Modbus Slave/Server to Generate Read Only Tags

I've create a generic "Modbus Publish" script that eliminates the need for a second set of tags. I've publicly posted it here:

https://www.automation-pros.com/ignition/modbusPublish.py

It requires the "Queue Utility" script posted here:

https://www.automation-pros.com/ignition/queueUtil.py

and described over here:

Both scripts rely on my Integration Toolkit's globalVarMap() script function, but you can rework that (not quite 1:1) to use system.util.getGlobals() if you must. (Not recommended nor supported by me.)

Do read all of the comments in that topic and in both scripts. They need to be in the hierarchy for your global scripting project, as they are called from tag events.

A leaf project needs a gateway timer event that contains this one-liner:

modbusPublish.drainSlavePublish()

That timer event should use a fixed rate and a dedicated thread, in most cases. Select an interval that makes sense for your workload. Increase the size of the blocking queue (modbusPublish.py line 45) if you run this with very many tags. (It must be large enough for a flood of startup events.)

Once all of that is in place, for any tag or UDT member you wish to publish via my module, set the tag's valueChange event to this script:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	modbusPublish.enqSlavePublish(tag, tagPath, previousValue, currentValue, initialChange, missedEvents)

Then create one of these string custom properties on the tag or UDT member:

  • publishOpcItem : Containing the complete OPC Item Path to receive value changes. As written, must be in IA's local OPC Server, but doesn't necessarily have to use any of my drivers. The source tag can be anything.

  • publishOpcPrefix : Containing an OPC Item Path prefix, of at least a bracketed device name, with optional unit address and memory area overrides. The source tag must have an OPC Item Path property containing a complete modbus address. The parts of the given prefix will be substituted to produce the complete target address.

Some examples for source OPC Item Path plus publish prefix:

[device1]HRUI2345 & [slave]2. => [slave]2.HRUI2345

[device1]HRUI2345 & [slave]2.IR => [slave]2.IRUI2345

[device1]HRUI2345 & [slave]IR => [slave]IRUI2345

(Remember that IA Modbus driver defaults to unit #0 when omitted, while mine defaults to unit #1.)

The scripts above have been lightly tested in my lab.

Thanks Phil, I will look into this!

I was able to come up with a different successful method for pairing tags in folders.
The script below is being held in the project library and called by a gateway event timer script. What are your thoughts on using this vs your method? Any Pros and cons?

# Define the source and destination tag paths
source_folder = "[default]_Gen 1_/UnitId 21/AllTags/Breaker Settings 2"
destination_folder = "[default]_Slave1_/UnitId 21/AllTags/Breaker Settings 2"

# Get all tags under the source folder
source_tags = system.tag.browseTags(parentPath=source_folder)

# Iterate through each tag and write its value to the corresponding tag in the destination folder
for tag in source_tags:
    source_tag_path = source_folder + "/" + tag.name
    destination_tag_path = destination_folder + "/" + tag.name
    source_tag_value = system.tag.read(source_tag_path).value
    system.tag.write(destination_tag_path, source_tag_value)

Browsing every time the timer fires is brutally inefficient. Using individual tag reads and writes is even worse.

The concept is fine, but the tag lists desperately need caching, and there should only be one multi-tag read and one multi-tag write per timer event.

Some contrasts:

  • Your method unconditionally reads every source tag and writes every destination tag on every timer event, while mine only writes values from tags that are starting/restarting and tags that actually change.

  • My method never explicitly reads tags, because the values are handed to the script from the event.

  • My method uses a single multi-value OPC write per timer event.

  • My method doesn't use Ignition OPC tags pointing into the Modbus Server at all, so no excess gateway load from that.

Great info, Thanks for your response!!