Best Practices for Handling Unusual Modbus Data Types

I’m a new developer on the Ignition platform. I’ve been playing around with the demo for some time, but I finally have my first real-world project. I want to make sure I’m using best practices to so that project modification and up-keep is easy and less likely for me to make mistakes.

My question is, are there any best practice guidelines regarding the handling of custom Modbus data types? For example, I am working with multiple power meters that all support ModbusTCP. All the meters have the same values that I need to monitor (e.g. Voltage, Current, Power Factor, etc.), but since each meter has a different manufacturer the Modbus addresses and datatypes will be different. It seems to me that I should use some UDTs because I will be pulling the same set of information from each meter and this will allow me to make sure all the common properties are identical (e.g. history, alarming, and metadata settings) across all meters and can be updated on all instances from one location. As long as all the variations in the data structures can be handled by specifying a different Modbus register via the OPC item path, different source data type, or different scaling settings, this works great.

However, one of the values in one of the manufacturer’s meters is stored in a non-standard manner (as is common when dealing with Modbus). The manufacturer decided to store one value in sign-magnitude notation instead of 2s complement (the standard way signed integers are represented by all modern computers). Fortunately, the manufacturers manual gives a handy conversion (if (value < 0) value -= 32768). I am able to read the “raw” sign-magnitude data into an “XYZ_Raw” tag and then perform the required calculation by using an expression tag.

However, this new expression tag cannot be referenced from the OPC tag in my UDT (as far as I can tell). This means that for this value on this device, I cannot use my UDT to help standardize settings and increases the probability that I will make a mistake when I update an alarm setting and forget to also modify this special case.

Is there any way to get the OPC tag to read it’s value from an Ignition tag? Should I be writing the value back to an OPC item? If this were the case, I would need to be able to define a memory only value in the OPC server or use a virtual device to store the data if this is even possible. Should I be using derived tags in the UDT instead of OPC tags. This seems to be the most flexible option, but will result in creating at least two Ignition tags for every piece of data I need to monitor (one OPC tag to pull in the value; a second derived tag to collate the data into my UDT structure and apply common metadata, alarming, and history; and possibly a third expression tag to handle special case data types). Or do I just need to live with having some special cases.

Hopefully, you will have some valuable experience you can share with me. Thank you for your help.

I might be misunderstanding you, but it seems like you have an expression tag which reads a value from an OPC tag and modifies it. But, you’re using the OPC tag in your data structure as the primary value-carrying item for your HMI.

OPC tags are exclusively used to get data from an OPC path. What I’ve done in the past is made the OPC tag connect to the modbus register directly, and then made the expression tag contain just a direct reference to the value of the OPC tag by default. Then, whenever I’m using a tag which breaks the norm (such as the tags with an odd datatype like what you’ve encountered) I change the expression for all applicable tags. This way, all my items on the HMI can read from the same “value” tag, which is the expression tag.

If you have a significant number of tags with that odd datatype, then I would consider making a separate UDT just for that type, or making a flag in your UDT so that the expression conditionally applies whatever calculations you need, depending on the value of the flag.

This is why derived tags were created. Bring in the abnormal value, let’s call it source. Then make your value tag as a derived tag, and translate back and forth there.