Expression Tag Questions

Howdy,

I am pretty new to using Expression tags. I’ve been playing around with them for the past week. I am using them to write values to a few devices via Kepware (Modbus) and Ignition OPC (Ethernet) servers. The things I am writing are:

  1. Communication Heartbeat
  2. Process Variable Setpoint

I’ll describe what I am doing with the communication heartbeat. If I can figure out my problems with that, I’ll be able to fix my process setpoint writes as well. I am trying to write to a communication timeout period register. This register essentially just counts down in minutes. So if I don’t write a value in x minutes, the communication timeout alarm occurs. I tried to do this with an expression tag and I noticed that I wasn’t always writing to the device. My code was something like:

if({[~]Device/Timeout Period}<5,5,6)

And I am writing back to the same OPC register that {[~]Device/Timeout Period} is reading from. The expression tag is correctly writing to the device register initially, but then the Timeout Period will get down to 0 and the expression tag will stop writing. I’ve been able to get this to work by modifying my expression to something like this:

{[~]Device/Timeout Period}+5

The Timeout Period register will saturate at 120. However, I’d like to be able to accomplish this task by being able to use a global timeout period and write a constant value (5-10 minutes).

So my high level question is this: How can I make sure that the expression tag is writing to the OPC address at an (approximately) near constant rate? Best I can guess is that the OPC writes happen when the expression tag has a new value than the last write. Maybe part of my problem was related to reading and writing to the same device register.

Now I did just start experimenting with using scripting in expression tags via the runScript expression function. I was able to correctly call scripts (project vs. shared scope tripped me up at first). Is this the solution to my problems? I’m still worried that I’m only going to do an OPC write on the expression value change. Is there some way to guarantee I can write at a certain rate?

Thanks

Hi Jeff,

OPC will not write data unless it sees the value is different. This is to cut down on unnecessary traffic, and just the way OPC works.

But, if you are wanting to be sure of a constant-- or very nearly so-- rate of execution, I recommend either a gateway timer script or a transaction group. I personally use the latter.

For heartbeats, I recommend two registers. One controlled by the device, one controlled by Ignition. I set mine on a transaction group as well, but that’s just personal preference. The group reads the register controlled by the device and writes it back on the other register, giving a full handshake. That’s the bare bones of it.

The timeout tag uses the difference between the LastChange property of the device controlled tag and now():

minutesBetween({[.]path/to/heartbeatTag.LastChange},now())

If you want it to count down from 5 minutes:

5 - minutesBetween({[.]N7/N7:0.LastChange},now())

Jordan,

Thanks for the reply. It makes sense to me that we’d only do an OPC write on value change. I’m very interested in using a Transaction Group to achieve this. I don’t have much experience with Transaction Groups. Do I always need to do a database read/write in a specific Transaction Group? Would you mind posting a screenshot (if it’s not too much trouble) showing a device heartbeat exchange? I tried doing this with Transaction Groups but was getting errors with my group. I really appreciate your replies.

Thanks.

No database required. :slight_smile:

PLC side. This timer here controls the alarm (elsewhere in the program) on the device.

Ignition side.

1 Like

Oops. Didn’t show the transaction scheduling. I use a table called heartbeat, but it’s rather irrelevant here. I do be sure to set the Table action to update first or last row, because it would still insert empty rows to the database. Just to avoid a potential gotcha.

Jordan,

Thanks for the super quick reply. I have made some progress with my transaction groups. However, if I remove a database write on a Standard Transaction Group, it just gives me an error (either “Write target does not exist…” or “Group state changed to errored”). I do have a database that I can write to, but I want to know if this is required by Transaction Groups. I know you said it’s not required, but all of your examples are using one.

Thanks again for all your help.

Hi Jeff,

Did you set all your opc items to ‘read only’?

Sorry, I may have misunderstood your post. Although the read only thing still stands! :wink:

I think I’ve always specified a table, even if it’s a throw-away and nothing ever gets written to it. Force of habit prevented me from trying it without…

If you set the group up to not actually write to the table (read only items, no ‘automatically create table’, no index column/timestamp) then it will check that the table exists, but not actually do anything with it - so you’re “safe” to point it at a dummy table. Mirroring OPC->OPC is something we’re planning to improve with a future overhaul of SQLBridge, so there are a few hiccups with the current setup.

1 Like