Change Tag quality in script

Hi
I have a modbus device that return pressure sensor and status of wire break in two separate resisters:
Pressure_PV
Pressure_WB
In the case of wire break of sensor, I need the quality of Pressure_PV change to Bad in ignition so this value is not used in reporting as valid value, but the quality of pressure value follow by the modbus driver status not the value of wore break register.
I want some how to check wirebreak and force the quality of the Pressure_PV to Bad in script of tag value change event.
How can I force the quality of a tag?

I don’t think you can set the quality directly:
http://forum.inductiveautomation.com/t/forcing-the-quality-of-a-tag-using-python-script/5565/2?u=witman
Another option, though I don’t particularly like it, would be to create an expression tag linked to the value of Pressure_PV when Pressure_WB is good, but otherwise evaluating to something that results in bad quality.

What about enable and disabling tag according to wire break tag change event.
One think I’m not sure is if tag disabled, ignition ignore to log it’s value to historian?

You actually can write the quality and timestamp to a tag using our system.tag.write functions. You just need to send in a qualified value. You can use BasicQualifiedValue for that:

from com.inductiveautomation.ignition.common.model.values import BasicQualifiedValue
from com.inductiveautomation.ignition.common.sqltags.model.types import TagQuality
bqv = BasicQualifiedValue(value, TagQuality.GOOD, timestamp)
system.tag.write(tagPath, bqv)

The timestamp would be a proper date object (use system.date functions).

3 Likes

Thanks. Just one more question. If I define OPC tag and force the quality tag by script as you instruct, does OPC driver re-force and write it to good again?

Yes, and your write that includes quality will write the value back to the device, too. Writing value+quality by script is really only reliable for memory tags. Consider leaving your OPC tag alone and creating a derived tag with the adjusted quality.

3 Likes

Thanks. Do you believe enable and disabling tag is best solution here? I just want in some cases (wirebreak) historian doesn’t store data and ignore it.

Enable history on the derived tag, not the OPC tag.

1 Like

I tried this solution for setting the quality on a memory tag (I have a similar situation to the OP’s) but I get the following error:

AttributeError: type object ‘com.inductiveautomation.ignition.common.sqltags.mo’ has no attribute ‘BAD’

Here is my code for trying this:

from com.inductiveautomation.ignition.common.model.values import BasicQualifiedValue
from com.inductiveautomation.ignition.common.sqltags.model.types import TagQuality

path="[~]foo/bar/BAZ"

newQuality=TagQuality.BAD

value=system.tag.readBlocking([path])[0].value

bqv = BasicQualifiedValue(value, newQuality, system.date.now())
system.tag.write(path, bqv)

I’ve solved my own problem, this code needs to be updated to use QualityCode:

from com.inductiveautomation.ignition.common.model.values import BasicQualifiedValue, QualityCode

path="[default]foo/bar/BAZ"

newQuality=QualityCode.Bad

value=system.tag.readBlocking([path])[0].value

bqv = BasicQualifiedValue(value, newQuality, system.date.now())
system.tag.write(path, bqv)

1 Like

I have implemented a custom tag quality handler on read-only tags using the above described methods. However, how do I implement custom tag quality handler on read-write tags ?

By read-only tags I mean tags that whre their value is only updated by the PLC cannot be updated from the ignition gateway. Example the PV (process variable) variable of a PID controller block.
By read-write tags I mean, tags that could be updated from either PLC or the ignition gateway . Example: The OP (output) or SP (setpoint) variables of a PID controller block.

I am using Ignition 8.1.

I have a UDT called Ctrl_Anlg_PID with the following tags

  • PV [memory] updated by tag script in PVX or PVQ
  • PVX [OPC, read-only] address to PLC PID.PV proc value raw
  • PVQ [OPC, read-only] address to PLC PID.PV proc value quality (0=Good, 1=Bad)
  • SP [OPC, read-write] address to PLC PID.SP setpoint
  • OP [OPC, read-write] address to PLC PID.OP output

Here is the tag event script for PVQ.Value Changed:

	from com.inductiveautomation.ignition.common.model.values import BasicQualifiedValue, QualityCode

	logger = system.util.logger("tags.scripting") 
	logger.trace("Executing tag.script Ind_Anlg.PVQ valueChanged -- begin")
	logger.debug(tagPath + ".currentValue: " + str(currentValue.value))

	tp_PV = "[.]/PV"
	tp_PVX = "[.]/PVX"
	
	if currentValue.value == 0:
		logger.debug("Set Quality to Normal")
		qual = QualityCode.Good

	else:
		logger.debug("Set Quality to Bad")
		qual = QualityCode.Bad

	dbl_val = system.tag.readBlocking([tp_PVX])[0].value
	qval = BasicQualifiedValue(dbl_val, qual,system.date.now())
	system.tag.writeBlocking([tp_PV], [qval])

Here is the tag event script for PVX.Value Changed:

	from com.inductiveautomation.ignition.common.model.values import BasicQualifiedValue, QualityCode


	logger = system.util.logger("tags.scripting") 
	logger.trace("Executing tag.script Ind_Anlg.PVX valueChanged -- begin")
	logger.debug(tagPath + ".currentValue: " + str(currentValue.value))

	tp_PV = "[.]/PV"
	tp_PVQ = "[.]/PVQ"
	
	int_qual = system.tag.readBlocking([tp_PVQ])[0].value

	if int_qual == 0:
		logger.debug("Set Quality to Normal")
		qual = QualityCode.Good

	else:
		logger.debug("Set Quality to Bad")
		qual = QualityCode.Bad

	dbl_val = currentValue.value
	qval = BasicQualifiedValue(dbl_val, qual,system.date.now())
	system.tag.writeBlocking([tp_PV], [qval])
	
			
	logger.trace("Executing tag.script Ind_Anlg.PVX valueChanged -- end")

@pturmel I am poking you as I am guessing you have solved this already.

Thank you in advance.

I just realized that quality doesn’t really apply to the PID case above as SP and OP exist in the block and are not generally associated with field wiring.

However, if you consider the case where the PID.OP is connected to a controlling element such as a valve, then field wiring does come into play.

So I believe there are still cases where you are writing a value to a PLC and need to be able to update the quality in some custom manner.

Perhaps this is a feature to be included in a future release of the gateway ?

Thank you,

-Mark

For any such activity, I would use a derived tag, as mentioned above. But I avoid using an architecture where writes to specific items can come from two directions, with limited exceptions.

You probably want to read this whole topic:

Phil,

Thank you for the tips. I read the thread and I see your comment segregating/grouping the various tags in a UDTs into 3 groups (xyz_fbk, xyz_cmd, xzy_cfg) according to the needed ignition scan/subscription rate.

Currently, I am communicating to a Honeywell HC900 PLC using the modbus driver so I am not sure how much the scan/subscription rate is an issue.

When you say derived tag, are you meaning a memory tag updated by another tag via a tag script or specifically a derived tag? I don’t see how you can update the tag quality of a derived tag.

Some background on my case:
The Honeywell HC900 uses simple analog and digital variables (registers) along with function blocks for programming. The simple variables and block variables can be mapped to a modbus map for reading and writing. Unfortunately, not all of the function block variables are accessible through input and output pins and consequently cannot be read or written by other function blocks or simple variables. However all of the block variables can be mapped to a modbus address.

This is the case with the PID block. When the PID block is in AUTO mode, the PLC will control the output. When the PLC block is MANUAL mode, the output can be written to via the modbus address.

I created an OPC tag that is addressed to the PID output variable and I can read it from ignition when the PID block is in AUTO and read/write to it from ignition when the PID block is in MANUAL.
That functionality works excellent.

The PID output is connected to an analog output channel that has a wiring bad quality detection. I would like to show a bad quality on the output tag when the quality is bad.

Is there a way and how can I set the quality of my read-write tag, the tag address to the PID output ?

Thank you,

-Mark

A derived tag may use the forceQuality expression function when generating the the derived tag’s value from the OPC tag’s value. This allows you to substitute some other tag’s quality for the original, or apply any other quality logic you like. Derived tags function a lot like Expression tags, but are writable–you are expected to supply an appropriate reverse transformation for that.

Your description of your PID data looked like you were describing an Allen Bradley PID instruction. Most of that topic wouldn’t apply to the Honeywell, though the general principle of not having writes from two directions would still apply.

Ahhh ! I didn’t realize the expression function forceQuality was exactly what I needed.

I have replaced the memory tag and tag scripts with derived tags.

This works on my read-only tags as well as my read-write tags.

For anyone interested, my UDT structure is as follows:

UDT: Ctrl_Anlg_PID

  • PV [Derived]
    source tag path: [.]PVX
    read expression: If({[.]PVQ}=0,{source},forceQuality({source},512))
    write expression: {value}

  • PVX [OPC] address points to PLC, PID Block, PV variable

  • PVQ [OPC] address points to PLC, input channel block, bad wire detection

  • SP [OPC] address points to PLC, PID Block, SP variable

  • OP [Derived]
    source tag path: [.]OPX
    read expression: If({[.]OPB}=0,{source},forceQuality({source},1))
    write expression: {value}

  • OPX [OPC] address points to PLC, PID Block, OP variable

  • OPB [OPC] address points to PLC, output channel block, bad wire detection

Although this works, for some reason using the codes according to the documentation did not work for me. Specifically code 512 kept producing an “Uncertain” quality. Using code 1 produced a “Bad” quality.
https://docs.inductiveautomation.com/display/DOC81/Tag+Quality+and+Overlays

I also looked in the SDK but it does not have the quality code associated with the quality description.
https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.0/com/inductiveautomation/ignition/common/model/values/QualityCode.html

Any ideas what I am missing?

Thank you. Phil.

That was like an early Christmas gift for me.

Bleh, the forceQuality function looks up the code passed in using the old DataQuality enum and then tries to convert that to the equivalent QualityCode. Probably to try to preserve backwards compatibility from versions before QualityCode was introduced. 512 was an unknown DataQuality and so becomes an unknown QualityCode.

We might need a forceQuality2 function or something…

Ah ha!

I think this is the intended replacement: https://docs.inductiveautomation.com/display/DOC81/qualifiedValue

3 Likes