Specific Change to system.opc.writeValue in 8.1.27

Does anyone know what specifically changed with the system.opc.writeValue function in 8.1.27?

We updated over the weekend from 8.1.26 to 8.1.28 at one of our facilities, and we are seeing some odd behavior when using this function. I am confident there were not code changes to the way we are using the function, and I know that system.opc.writeValue was around well before 8.1.26.

Ignition Release Notes for Version 8.1.27 | Inductive Automation

Thank you; I have looked at the release notes, but I'm not quite sure I understand how the change is affecting our code. I'm going to post our code here to see if anyone can help me identify out the change may have affected our code. I believe the line that is not working properly is line 87.

1 Like

print/log value before that call, see if it's a raw value or QualifiedValue

Also - what value does this function return now? Did it change?

The value is a raw value, but I think I know what is happening now: The write is failing whenever I try to write a string/unicode value to a tag on the PLC that is actually an integer/short/float tag. I suspect that the issue has something to do with converting the value to a QualifiedValue.

In the screen shot below, I printed the name of the tag, the value, and then the typefor the value. If the type is unicode and the actual PLC tag is short, a 0 gets written instead of the intended value.

Yeah, there was some unintentional behavior change here... I think we reverted some of it (looking for details), but in general you shouldn't rely on implicit conversion between string and integer types to be done for you.

details: previously, when coercion failed, we would just write the original raw value to the server. If it ever happened to work after that it was server-specific behavior that ignores OPC UA strict typing requirements. A change in 8.1.28 unintentionally changed this to write null or 0 when coercion failed. This was reverted in 8.1.32.

1 Like

Okay, thanks. This one is a bit of a challenge because we're pulling in the values from a 3rd party database, and all of the values are string values. We do a conversion on a couple of the values that we know should be float values, but in the past, we just sent all of the values as they were and let the conversion happen automatically.

I should also mention that our preference is to use system.tag.writeBlocking in this case instead of system.opc.writeValue (or system.opc.writeValues), but we were having a very odd timeout issue with this specific PLC. The support ticket for that issue is #73376. For now, we have reverted to using system.tag.writeBlocking, and I'm hoping the update may have fixed the timeout issue. :slightly_smiling_face:

Are you doing anything with the returned Quality? Have you considered using system.tag.writeAsync().

How quickly is this script likely to be called, and where is it most likely to be called from?

Yeah, tricky situation. You're relying on undocumented behavior in what I presume is one of the older AB drivers... it's not even something you can expect to happen with the newer drivers - they should return Bad_TypeMismatch as the spec requires.

In the ticket with IA (#73376), IA support indicated that writing asynchronously most likely would not fix the timeout issue we were seeing because it appeared to be an issue between the OPC server and the PLC. At the time, we captured a bunch of Wireshark data, and we were able to show that the PLC was responding, but Ignition was reporting a timeout error nonetheless.

Yeah, tricky situation. You're relying on undocumented behavior in what I presume is one of the older AB drivers... it's not even something you can expect to happen with the newer drivers - they should return Bad_TypeMismatch as the spec requires.

@Kevin.Herron I am wondering now if the timeout issue was related to the data type mismatch as well. I think we may need to add some code to try to change the data type to a number before we write to the PLC. We have a fairly generic function for this, which could be a challenge.

Tough to say...

I would expect that using an Ignition tag and system.tag functions would be the solution, because while the OPC client does only minimal implicit conversions to support writing OPC UA types that don't exist as Ignition tag types, the Ignition tag system does loads of automatic/implicit conversions.

The configuration should have the dataType of the tag in it. I would try something like this:

from java.lang import Throwable
from com.inductiveautomation.ignition.common import TypeUtilities

def write(tag,value):
        tagConfig = system.tag.getConfiguration(tag)
        coercedValue = TypeUtilities.coerce(value,tagConfig[0]["dataType"].javaType)
        return system.tag.writeBlocking([tag],[coercedValue])
    except Throwable, T:
        #Do something as a result of the error.  Coerce will throw a ClassCastException
        #if it fails to coerce the value. So you could catch that specifically to do one
        #thing and then catch Throwable separately for general errors.
        print T.message

Of course you could also read the dataType directly which would avoid the case that it isn't included in the configuration (which isn't guaranteed according to the manual).

Also, as an aside, I would update the comments in that script as they don't match what the function is actually doing anymore. Looks like a read function was copied and just the code was changed?


@lrose Nice catch that our comments are not up to date. :rofl:

After I wrote my last comment, I started thinking that I can probably grab the data type from the tag somehow so that I know what I'm trying to convert the data to. Thanks for the possible code to get that idea rolling!