Considering Tag Value Quality in Cicode
- Last UpdatedSep 09, 2024
- 5 minute read
When using tag values in Cicode, it is important to consider the quality of the tag and how it is determined. Just as the quality of a displayed value should be presented to a client user, the quality of a value also needs to be reflected in Cicode calculations.
For example, the type of driver used to communicate with an I/O device will greatly impact how tag value quality is determined.
-
Tags from traditional drivers may be set to a bad quality when the I/O device is not contactable (or for other errors in the driver commands).
-
Tags from devices using the Driver Runtime Interface (DRI) can have their quality set individually and could be set to a bad quality for reasons specific to an I/O point.
For more information on DRI-based drivers, see Retrieving Time-stamped Data from I/O Devices.
Best practice is to treat quality handling like error handling, and code for scenarios where it is required.
I/O Quality
The quality of a tag value is essentially propagated from the quality of the I/O point from which it is sourced.
-
In traditional protocols, such as Modbus, quality is driven by a single error code provided in a command response or by the field device being offline. This type of protocol does not provide the ability to nominate a quality for individual I/O points. This still holds true for Modbus over TCP as well (as used by the Plant SCADA's MODNET driver).
As the response error codes are typically a result of an invalid request, these types of issues are normally addressed in commissioning and are usually not encountered during operation. However, it is still possible to lose contact with a field device at any time which will result in all tags related to that device going into bad quality.
-
In more advanced protocols, such as OPC, quality is independently applied to each specific I/O point. To accommodate this, and to also cater for other advanced functionality such as unsolicited responses from the field, a newer generation of Plant SCADA drivers was developed to use the Driver Runtime Interface (DRI).
When a DRI driver is notified from the field of an update (value, quality and timestamp), this is immediately reflected in the corresponding tag. An example of a change in quality due to operational issues could be a faulty sensor. This increases the chances of a tag not being in good quality and the need for this scenario to be covered when using tag values in Cicode.
A traditional driver uses a chained sequence of request/response transactions that result in a tag update waiting on the reply back from a field device. As an I/O device is only marked online when the corresponding field device can be contacted, reverting from standby back to primary will never result in intermediate bad quality values from the switch.
However, a DRI driver uses a chained sequence of subscribe/publish transactions allowing the driver to update a tag at any time. When reverting from standby to a primary that has just returned online, it will be up to the driver to immediately provide tag updates for a new subscription without waiting to receive values from the field. If so, these values will be marked with a quality to indicate as such and will be followed by a good quality update when the field value is received.
If the driver is receiving updates from an interim gateway style device, such as an OPC Server, it will also be able to provide similar updates.
Implications for Cicode
Your Cicode should handle values that are not good quality in the same way it handles other error cases.
Tag extensions can be used to select the quality component of a tag (for example, TagA.Q), and this can then be checked using the QualityGetPart Cicode function.
If the quality of a tag needs to be referenced multiple times in a Cicode function, it is best practice to assign it to a variable when first required and then keep using that variable. When a tag is assigned to a Cicode variable (for example, var = TagA), that variable will also take the quality of the tag.
Further, the quality will continue to propagate through to any additional variables that are assigned an expression containing the first variable. The quality component of these variables can be extracted using the VariableQuality Cicode function, and checked using QualityGetPart as described above.
Note, however, that if a Cicode variable is assigned the value extension of a tag (for example, var = TagA.V), then the variable will always have good quality.
Examples
Example 1
Your Cicode needs to handle the possibility of a tag update occurring after it has determined the quality of a tag. If quality changes due to a tag update, your Cicode may proceed using an altered quality value. This can occur because of the way the Cicode execution system operates in a multi-thread environment.
This could even happen when the tag quality check and tag usage are on the same line, for example:
IF QualityIsGood(MyTag.Q) AND MyTag > 100 THEN
The easiest way to avoid this is to only use the tag when assigning to a Cicode variable, and only use the Cicode variable from then on.
In the case of the example above, a better approach would be to assign "MyTag" to a local tag at the start of the function and use this instead.
Int localMyTag
IF QualityIsGood(VariableQuality(localMyTag) AND localMyTag > 100 THEN
// Following example calculates flow rate based on a PLC tag 'MyDiffPress'
// and then writes value to a memory mode Disk PLC tag 'MyFlowRate'
// Returns 1 for error when BQ on the PLC tag and forces MyFlowRate to Override mode.
INT
FUNCTION
UpdateMyFlowRate()
INT FlowFactor = 5;
INT SpecificGravity = 1;
//Copy the variable tag MyDiffPress to a local variable
INT DiffPress = MyDiffPress;
INT FlowRate = 0;
INT Error = 0;
IF QualityIsGood(VariableQuality(DiffPress)) THEN
//Do this instead of using QualityIsGood(VariableQuality(MyDiffPress))
FlowRate = FlowFactor * Sqrt(DiffPress/SpecificGravity);
MyFlowRate.OverrideMode = 0;
MyFlowRate = FlowRate;
ELSE
Error = 1;
MyFlowRate = 0;
MyFlowRate.OverrideMode = 4;
END
RETURN Error;
END
Example 2
The following example checks the threshold of a tag value.
INT
FUNCTION
IsThresholdExceeded(STRING Tag1, INT Threshold)
INT exceeded = FALSE;
INT myVar1 = TagReadEx(MyTag1);
IF QualityIsGood(VariableQuality(myVar1) AND myVar1 > Threshold THEN exceeded = TRUE;
END
RETURN exceeded;
END
Example 3
The following example logs tag values to a file.
FUNCTION
LogMyValue(STRING MyTag)
INT myVar1 = TagReadEx(MyTag);
IF QualityIsGood(VariableQuality(myVar) THEN
LogToMyFile(MyTag + " : " + IntToStr(MyTag));
ELSE
LogToMyFile(MyTag + " : "No Value");
END
END