Tag Value Delta Calculation

#1

Hello all,

I am attempting to create a tag that will represent the change in an OPC tags value over the previous 60 seconds. I have flirted with a few ideas on how to do this, but have run into some potential issues with each version i tried.

First, I attempted to just create an expression tag that is on a much slower scan class that takes the value of the OPC tag every 60 seconds (scan class execution). That way, there would be a tag that represents the current count value (OPC tag) as well as a tag that represents its value 60 seconds ago. This tag would then have a tag change event script that would calculate the delta and write its “previousValue” to a 3rd memory tag. The issue I ran into here was that when the machine was off, or not running, the delta value would stay what it was previously (since the tag change event script would never trigger), when it ideally would change to 0.

Next, I tried to create a gateway script that could be launched from an expression tags runScript() function. Essentially, the expression tag on the 60 second scan class would pass the tag paths of the two tags it needs a delta calculation on (the OPC tag and itself [the slower 60 second scan class tag]), do a system.tag.read() on them both and then return the difference. This resulted in slightly inaccurate delta calculations when compared to the OPC tag totalized. I believe this is due to the small time difference between the scan class execution and the script running which would result in small discrepancies since the OPC tag is pretty high-speed.

A last thought I had would be to utilize the historian and every 60 seconds query the tag history and compare it to the current value. But if this is something that will be done for multiple tags in the future, a bunch of tag historian scripts executing every minute seems pretty heavy.

Does anyone have better ideas than the few I have came up with? We would really like to get an accurate, light, and simplistic solution to this. But if it does not exist then perhaps we should look at changing the way we report the counts.

Thank you,

Chris

0 Likes

#2

Sounds like a job for the recorder() expression function from Simulation Aids.

You’d make an expression tag of dataset type, and use recorder() to sample the tag of interest at the desired fast rate, with the row limit set to yield sixty seconds of samples. Something like this:

recorder(1000, 60, "SomeColumnName", {[default]path/to/tag})

Then make another expression that uses the value in the first row of that dataset to produce your delta. Something like this:

{[default]path/to/tag} - try({[default]path/to/recordingDS}[0, 'SomeColumnName'], {[default]path/to/tag})

The try() suppresses an error when starting up (no row recorded yet).

Note that a single recording dataset expression can handle multiple values of interest. The output expression is a rolling 60-second delta.

0 Likes

#3

Thank you for your reply!

However, I am unable to get this to work. The dataset from the expression tag is not building.

recorder(1000,60,“Column”,{[default]Chicago/DWPC/B1/Former 37/PROD_COUNT_1})

0 Likes

#4

Make sure the tag is set to evaluate every second (scan class in v7.9, tag group in v8.0). (And you did install Simulation Aids, I assume?)

0 Likes

#5

Correct, on both points.
The expression tag is on the default 1 second scan class and Simulation Aids was successfully installed and is Running.

image

0 Likes

#7

What version of Ignition?

0 Likes

#8

Ignition gateway version 7.9.10

Simulation Aids version 1.7.8

0 Likes

#9

I’ve reproduced this. It works in v7.9.8. Doesn’t work (in an expression tag) in v7.9.10. I’ll update this thread when it is resolved.

0 Likes

#10

Candidate fixed module.

There’s a bit of a visual issue remaining that I don’t plan to fix. The output of the recorder() function (and the view() function, too) is a TransientDataset, not a normal Dataset. This type of dataset serializes to zero rows. It is intended to prevent bloated project resources when datasets are part of a component. This means that recorder() output will always show zero rows in the designer, even though they really are there in the gateway. Here’s an export of a set of tags testing and demonstrating this behavior. The Recording tag shows in the tag browser with zero rows, even though it ends up with twenty rows.

<Tags>
   <Tag name="SimRecord" path="" type="Folder"/>
   <Tag name="Delta" path="SimRecord" type="DB">
      <Property name="Value">0.0</Property>
      <Property name="DataType">4</Property>
      <Property name="ExpressionType">1</Property>
      <Property name="Expression">{[.]Source}-try({[.]Recording}[0, &apos;Source&apos;], {[.]Source})</Property>
   </Tag>
   <Tag name="Recording" path="SimRecord" type="DB">
      <Property name="Value">
         <Dataset>
            <columns>
               <col name="t_stamp" datatype="DateTime"/>
               <col name="Source" datatype="Float4"/>
            </columns>
            <rows>
            </rows>
         </Dataset>
      </Property>
      <Property name="DataType">9</Property>
      <Property name="ExpressionType">1</Property>
      <Property name="Expression">recorder(1000, 20,  &quot;Source&quot;, {[.]Source})</Property>
   </Tag>
   <Tag name="RecordingLen" path="SimRecord" type="DB">
      <Property name="Value">20</Property>
      <Property name="DataType">2</Property>
      <Property name="ExpressionType">1</Property>
      <Property name="Expression">len({[.]Recording})</Property>
   </Tag>
   <Tag name="Source" path="SimRecord" type="DB">
      <Property name="Value">6.0</Property>
      <Property name="DataType">4</Property>
   </Tag>
</Tags>

For some reason, the rows do display in earlier Ignition versions, in spite of the TransientDataset return.

0 Likes