How do I read multiple coils using the modbus tcp driver into an array

I have almost no hair left trying to get this working. I keep getting either can’t read ‘false’ into *array or config error. I just want to read multiple coils into an array of either bools or into a string array as 1’s and 0’s. The coils are essentially meaningless, I need to work around the plc programmers terrible implementation. I’m sure I can do this by creating the individual tags concatenating them together for an expression tag to convert to integers to compare to magic numbers. It just seems very inefficient as I’m used to my my modbus server and client code that can do this simply with the modbus protocol. I have hundreds of existing devices and about a dozen parent udt’s with hundreds of udt’s derived from those parents. Any suggestions would be helpful. Thx

If you create all the tags and put them into the same scan class, Ignition’s drivers are smart enough to optimize the scheduled reads into bulk reads of adjacent addresses. Use wireshark to capture some of the resulting traffic and you’ll see.

I assumed the modbus request packets were optimized, but my issue is with the coils themselves. Like there’s the HRS to get a string of bytes, assuming byte per character, from holding registers. Is there anyway to to do the same thing with coils? The coils honestly are useless and being used as a workaround for the plc programmers implementation of the status of the machine, so I don’t care which coils are sprung, just need to know if any of them are so the user knows there’s a fault the machines interface is hiding from them.

If I understand what you are trying to do, could you use the ‘C’ command for getting the coil status?
https://docs.inductiveautomation.com/display/DOC79/Modbus+Addressing

About 1/2 way down on this page is a reference for it.
Would you be able to compare the coil status and if not equal to zero, a fault is present?

You’ll have to just make one tag per Coil, and if you need to somehow combine them, use an expression tag.

This absolutely will work, but very tedious. So with this solution I would need to add around 50 - 80 new tags per the 8 or so affected udts. I would then need to either have a giant nested if statement in another tag that would check each individual coil and if the ones that are real faults are sprung than that tag would be true, otherwise it would be false. So it’s not that ignition can’t do it, it’s more do I really want to:), and does my employer want to pay me to work around the plc programmers shortcomings, and would it be cheaper to update the plc code and update each machine instead.

I haven’t had to use any Modbus tags but I do something that might work for you.
I make extensive use of the system.opc.readValues call, I pass it an array of OPC addresses and then loop through the result set to check values. Could you build up an array of the addresses, use the above call and then check the status?

How do you pass it an array, the documentation doesn’t seem to talk about this? Thanks

You can either hard code it, or loop through a list or database query to add the values to a list.
You want to end up with something like

path=
path=[u'[ML1]Local:1:I.CH0Data', u'[ML1]Local:1:I.CH1Data', u'[ML1]Local:1:I.CH2Data', u'[ML1]Local:1:I.CH3Data', u'[ML1]Local:1:I.CH4Data', u'[ML1]Local:1:I.CH5Data', u'[ML1]Local:1:I.CH6Data', u'[ML1]Local:1:I.CH7Data', u'[ML1]Local:1:I.CH8Data', u'[ML1]Local:1:I.CH9Data', u'[ML1]Local:1:I.CH10Data', u'[ML1]Local:1:I.CH11Data', u'[ML1]Local:1:I.CH12Data', u'[ML1]Local:1:I.CH13Data', u'[ML1]Local:1:I.CH14Data', u'[ML1]Local:1:I.CH15Data', u'[ML1]Local:1:O.CH0Data', u'[ML1]Local:1:O.CH1Data', u'[ML1]Local:1:O.CH2Data', u'[ML1]Local:1:O.CH3Data', u'[ML1]Local:1:O.CH4Data', u'[ML1]Local:1:O.CH5Data', u'[ML1]Local:1:O.CH6Data', u'[ML1]Local:1:O.CH7Data', u'[ML1]Local:1:O.CH8Data', u'[ML1]Local:1:O.CH9Data', u'[ML1]Local:1:O.CH10Data', u'[ML1]Local:1:O.CH11Data', u'[ML1]Local:1:O.CH12Data', u'[ML1]Local:1:O.CH13Data', u'[ML1]Local:1:O.CH14Data', u'[ML1]Local:1:O.CH15Data']

Basically a list of the OPC Item Paths
Then you call it like

  server="Ignition OPC-UA Server"
  qualifiedValue = system.opc.readValues(server,path)
  just_values = map(lambda x: x.value, qualifiedValue)
  count=0
  table = event.source.parent.getComponent("Power Table")
  data = event.source.parent.getComponent('Power Table').data
  for values in just_values:
  	#print values
  	newData = system.dataset.setValue(table.data, count, 3, values)
  	table.data = newData
  	count=count+1

Ah, so use python tricks, I’ll check this out when I get a chance. So it seems like either way I’m looking at each coil as individual and no way to deal with them as an array tags.

I’m writing a bash script to generate the coil tags, what are the implications from importing a udt that already exists and lots of tags are derived from? Will it destroy the history, will it destroy those existing tags, will it destroy * :). Thanks

So here’s the function that expression tag will call:

def Faulted(unit):
path=[]
for fault in range(1,69):
path.append(’[]’ % unit % ‘/Faults/Fault’ % fault)
values = system.opc.readValues(‘Ignition OPC-UA Server’,path)
faults = map(lambda x: str(int(x.value == ‘True’)), values)
raw1 = int(faults[:31], 2)
raw2 = int(faults[32:63], 2)
raw3 = int(faults[64:67], 2)
if raw1 & 3271557119 and raw2 & 4110413567 and raw3 & 14:
return True
else:
return False

Well, some of them, there’s 2 other functions depending on the udt for that expression tag. How often will this run, like every time the scan class does it’s thang? This seems ridiculously inefficient, am I going to need to beef up the server now, if my assumption of how those expression tags work?

If you put that in a shared script, you could call it on load of the UDT and then on a timer at whatever rate you want it.
If these are alarms that have to be acted on as soon as they happen you are better off like Kevin said and make each one a tag. Once you have the tags in a list, generating XML for creating those tags is simple.
We generate most of our tags right out of a database table.

The above code I posted is used for Maintenance to check IO points, and to do IO documentation.

So if I export all my current tags, update the xml with new tags and then import those tags back into ignition it will just look at the differences to what is currently there and add the new tags to existing udts, opc, db and everything? If this is the case I think what I will do is generate the individual coil tags with the script. The tags will have the value changed event call the script to set a boolean tag that is in the history of if there was a true fault or not. Let me know about the import, it’s the one thing that makes me nervous. Thanks.

Yes, tags will be overwritten/updated on import.
Just be careful about paths.
If you right click a tag, export it, then edit it, make sure you right click import into the same folder you exported from.
If you do the entire tag structure that isn’t an issue since the root folder will be there.

If I was you, I would create a single coil tag, then export it.
Open it and see the XML structure, then with some simple scripting and looping you can generate the rest in XML format and import them.

PHP is really good at this. I often use this approach with PHP doing the intelligent replication.

1 Like

No doubt! We do almost all of our tag generation inside of Ignition using Python, and some other tag generation using Excel VBA. The XML structure of the tag database is a HUGE plus for Ignition.

My man, that’s exactly what I did with bash, the script was so simple and all I was doing is changing the tag name, Fault1, Fault2, … and the modbus address with the same type thing. Yeah, I exported the first one and I’ll need to do the same thing for the a tag with value change script. I’ll be careful to import and export from the same folder. I’m ready to make a snapshot, pray and destroy our scada. Thanks for all the help.