How to locate the number 'value' of a Tag's Data Quality

https://docs.inductiveautomation.com/display/DOC79/Tag+Quality+and+Overlays

Says that :

The following table outlines the primary data qualities. The most important is Good , and that has a value of 192 . There are more values, but these represent the most common:

We are currently working to correct a problem in our network introduced by our security team segmenting the network. The Ignition computer is able to reach out to a PLC via Kepware, and displays points that are monitored from the PLC , however, when the tags are created, they are created with Bad Quality.

It might help things if I could find out exactly what the ‘value’ is (as listed on the table in the linked page) but I don’t seem to be able to find it. Any help?

Answered:

The table is incomplete. ‘Bad Quality’ (among who knows what else) is not a quality that appears on the table of qualities. It ought to but does not. ‘Bad Quality’ represents a tag that is experiencing an unknown error. If your error can be diagnosed, you might get lucky and get a value that appears on the table, which will assist with troubleshooting. That’s why you’d be looking at a tag’s quality.

Where are trying to look for the quality value?

If you drill into the tag in the tag browser of the designer it shows Quality in there but gives you a string to tell you the quality.

If you are in an expression you can put in the tag path followed by .quality to bring the quality number into an expression.

If you are in a script when you do system.tag.read(“tagpath”).quality will return the quality number of the tag.

Hi,

So, the exact code I’m using in the script console is here:

print(system.tag.read("Cryo/ADPFlowMeter").quality)
print(system.tag.read("Cryo/ADPFlowMeter.Quality"))

And it returns:

Bad Quality
[Bad Quality, Good, Wed Jul 24 11:12:05 PDT 2019]
>>>
1 Like

Why? Genuine question; the integer codes used are basically arbitrary. If you want to know what they mean, then the human-readable representation you get by string-converting a qualified value or quality object is good enough. If, instead, you're trying to drive logic, you absolutely should not care about specific quality codes - instead, use the quality member's isGood() method, or the dedicated isGood() expression function; eg:
if system.tag.read("path/to/myTag").quality.isGood():
isGood({path/to/myTag})

https://docs.inductiveautomation.com/display/DOC80/isGood

The lack of a ‘Bad Quality’ entry in the table in the documentation has me thinking that ‘Bad Quality’ doesn’t correspond to any of the integer codes, and is a general ‘bad state’. The fact that integer codes are given in the documentation leads me to believe they are both accessible and useful to the user. I have a communication problem, but I have not managed to figure out its parameters. If I could get a code, that would be a start as to where to begin looking/who to call for help. One of these assumptions is apparently wrong, but I don’t know what.

I string converted a qv, and got nothing, since it’s a list in python, I’m not sure what I should have expected.

print(str(system.tag.read("Cryo/ADPFlowMeter")))
'[0.0, Good, Wed Jul 24 12:00:34 PDT 2019]'

I’m not trying to drive logic. I have a network issue between my PLC and my ignition gateway that is interfering with communication, and I’d like Ignition to tell me anything it can about how the info is or isn’t coming in.

The codes may be arbitrary, but they each correspond to a specific problem with the link, which could be debugged, correct?

I don’t care if isGood. I know isBad. I want howIsBad. Ignition has codes for that, I’d like to know how to access them, or to know why they are in the public documentation?

That's not a list - that is the string representation of the Qualified Value object. 0.0 is the value, 'Good' is the quality, and the last is the timestamp of that value.
Codes used specific integer values in 7.9 for compatibility reasons across Vision (in the backend, 8.0 uses an entirely different mechanism that's mostly compatible). In all cases, casting to string should give you as much level information as the system has...which is not much. The most common codes (for 7.9) are listed here:
https://docs.inductiveautomation.com/display/DOC79/Tag+Quality+and+Overlays#TagQualityandOverlays-PrimaryDataQualities

It's in the manual because it can be relevant, it just isn't in 99% of situations. Also, the isGood expression function is pretty new, so there's lots of older code that relies on direct integer comparisons...which caused a lot of problems when people upgraded from 7.9 to 8.0 and there was an issue with the new quality codes used.

Right.

Except the string I was getting, on the table you linked to, which I had also linked to at the top of this page, after having read it, was not on that table. There’s a fundamental disconnect in the information there. You get that right? If there’s a table telling me ‘These are the strings and values’ and I’m not getting a string from that table, I have to assume it’s not to be referenced in the table.

‘Bad Quality’ provides no more information than I should already have by the time I’m checking the quality. It’s useless. Tautological.

Expression_Eval_Error 310
The expression in the tag generated an error during execution. The error log should provide more information on the error.

This is a useful error code and string and explanation. It tells me what is up and where to go to begin solving the problem.

I’m going to do my best to sum up what I think the answer is, but no one seems to be at all sheepish or embarrassed about giving it so I’m not sure I’m getting it. (I would be):

There are a number of codes. There’s a table of codes. It’s existence implies it should be consulted.
The table is incomplete. A very common result is not in the table. The very common result does not provide any information that is not provided by the tag not working.

I got the very common result, which could be made more informative simply along the lines of ‘unknown error’ and spent the day on a wild goose chase trying to reference it off an incomplete table based on an assumption that information provided in a manual would be complete, and got a wild goose chase with people telling me to typecast a string I’d already pulled and pasted into a string and to consult a table I’d already consulted?

‘Bad Quality’ is a part of the table, but is not listed. It has a value, but no qualities’ value can be found outside the table, which this one does not appear on. Unlike most all other entries in the table, it does not provide any more granular information and if a tag’s quality is ‘bad quality’ it is on the user to locate the error because it’s outside Ignition’s diagnostic capability, yes?

If I seem frustrated it’s because I am. That penultimate paragraph could have been given and saved everyone time.

2 Likes

As a follow up to this, I am currently running into an issue where system.tag.read("TagPath").quality returns “Good” as a string instead of 192 as an integer when I call it in the script console. I’m running v8.0.15. In general, there is not a lot of clarity in the documentation on when to expect a string vs an integer, and I would generally prefer to do integer compares vs relying on magic strings to check tag quality.

You shouldn’t use string comparison or integer comparison. In the vast majority of cases, it’s preferable to use the dedicated methods on QualityCode - isGood(), isBad(), etc directly:

system.tag.read("TagPath").quality.isGood()
Or, thanks to Jython:
system.tag.read("TagPath").quality.good

If you actually need to check for a specific quality code, then you should use is against a static member of the QualityCode class:

from com.inductiveautomation.ignition.common.model.values import QualityCode
system.tag.read("TagPath").quality.is(QualityCode.Bad_Failure)
2 Likes

It would be nice if there was an ‘exists’ method to check if the quality is ‘not found’, unless there already is something to do this? I use this a lot with templates to check if certain tags exist for the particular device so I can make more generic templates

1 Like

Use is with the Bad_NotFound static constant:

QualityCode.Bad_NotFound

Cheers. Is there something similar for the expression language?

Not outside the isGood() function…
Thinking about it, I’m not sure there is a good way to do it for expressions that won’t end up with the same ‘magic strings’ problem. I’ll file a ticket and see what we can come up with.

3 Likes

I’m not sure if this has been added to the SDK since this thread was started, but the QualityCode object now has a getCode method that returns the integer value.

You can do things like this in the scripting console:

>>> from com.inductiveautomation.ignition.common.model.values import QualityCode
>>> QualityCode.Error_TimeoutExpired.getCode()
-1073741049
>>> QualityCode.Error_Exception.getCode()
-1073741048

Very handy to us as developers of a custom SparkPlug edge device since we can only push quality codes in integer form and want them to be meaningful in Ignition.

In addition to an expression for tag exists, an expression for isDisabled would be great. We use the same UDTs on a lot of different ancient and modern PLCs (thank you for tag property overrides and scripted tag creation!) and disable unused UDT members that don’t exist in the older PLCs. Templates adapt based on whether members are disabled or not. Unfortunately we seem to be stuck with magic strings to make this happen.

Alternatively, we could use the isGood expression if it weren’t for the fact that disabling quality overlays on internal template properties bound with indirect tag bindings using a tagPath template parameter causes the isGood expression to evaluate to true on those template properties regardless of the quality of the binding. This means we can’t use isGood without showing bad quality overlays for disabled features (disabled via disabled UDT members). Is this a bug or intended behavior (observed in Vision 8.0.16)?

Either way, exists() and isDisabled() tag quality expressions would be useful. Or maybe something like isEnabled() could tell us it both exists and is enabled–would be perfect for our use case.

1 Like

Reviving this thread because of changes that landed in the latest nightly build/8.1.10:

The gist: In addition to isGood, we've added new expression functions to test each different level (isBad, isUncertain, isError).

As a further mechanism to allow direct comparison, e.g. if you want to find out if something is disabled, you can use the new qualityOf expression; this will return the QualityCode object from the input. The equality operator has been 'overloaded' for QualityCodes in expressions, so you can directly compare against the (modern) string constants or integer values:
qualityOf({tag}) = 515
or
qualityOf({tag}) = "Bad_Disabled"

Note that any value outside of the 'reserved' 0-1024 range for IA codes will need to be the somewhat opaque 32-bit integer code. It's a mask on the most-significant 2 bits of a 32 bit value (representing the level; Good(0b00), Uncertain(0b01), Bad(0b10), Error(0b11)) and the 'user code', which can be any other integer value. This is unlikely to matter to anyone unless they're using a module that implements their own custom QualityCodes.

Scripting is not affected by these changes, so the getCode() mechanism above detailed higher in the thread is still the 'best' way to store modern codes into a database.

3 Likes