Reading byte array through python script

Dear All,

I Have a tag that having 64byte array. I need to fetch this array value to table or power table with array index as something like below.

Slave Number (This will be array[0] to [63]) Slave Value (this will be array value)
1 ------> 10
2 ------> 5

It will be grateful if I get an any example of python script or any suggestion

Thank you in Advance

I am assuming you are using Vision since you mention the power table component, but I can also provide a perspective solution if needed.

  1. Add a string custom property to your power table called rawData and bind it to the value of your byte array tag.
    image
  2. add this code to the table's propertyChange script:
if event.propertyName == "rawData":
	stringList = event.newValue[1:-1].split(", ")
	nums = map(int, stringList)
	event.source.data = system.dataset.toDataSet(["Index", "Value"], list(enumerate(nums)))

There is no byte array type for custom properties in vision, so dumping the tag's value into a string and parsing the values from it was the best I could do. It is a lot easier in perspective where you can do a script transform on the binding to convert the byte array to the proper format.

In Vision, I would avoid converting the byteArray to a string.

I would instead use an expression binding with the following expression:

runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(" + {[default]New Tag} + ")])",0) 

This will work in either Vision or Perspective, you can put the binding directly on the power table, or on a Custom Dataset Property depending on what you are trying to with the table (If you are wanting to edit the values).

This can be made more performant with the Integration Toolkit Module if you can install it:

 unionAll(							\\returns a dataset give columnInfo, and row data
 	asList(
 		asList("Slave","Integer"),	\\Column 1 Header, Type
 		asList("Value","Byte")		\\Column 2 Header, Type
 	),
 	forEach(						\\results in a list of items from the iteration
 		{[default]New Tag},			\\Data to loop over
 		asList(
 			idx(),					\\item index each trip through the loop
 			it()					\\item value each trip through the loop
 		)
 	)
 )

Also works in vison or perspective, and will be more performant than a script transform in perspective as well.

2 Likes

Thank you all for the reply . much appreciate.

Just couple of QQ :

  1. How can I use UDT tag(Indirect tagpath of array) in this runscript expression instead of direct tag.

  2. I have to fetch the data if any of array value > 0. I am not interest to display if array value 0 .

Thank you in Advance

Well that's a little more complicated, as you can not parametrize a tag path in an expression, and you should not use the tag() expression binding in this instance. (You will kill your performance, there are many threads on the forum about this).

Nothing however, prevents you from building the tagPath and then using that in the script.

What I would do for vision is create a custom string property called tagPath, with an expression binding that you use to build the path with. Use other custom properties to hold the dynamic values of the path. So, for instance say your path varies by machine number, then I would create a custom property to hold the machine number and build the path in an expression:

"[default]" + {MachineNumber} + "/ByteArray"

Then I would use that path in the runScript expression and change the script to this:

 runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(system.tag.readBlocking(" + {event.source.tagPath} + ")[0].value)])",0)

To only return those values which are greater than 0 you can add a conditional to the comprehension. That would look like this:

runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(system.tag.readBlocking(" + {event.source.tagPath} + ")[0].value) if value > 0])",0)

Expression is working fine . I am figuring out dealing with indirect tag. I let you know once done . Thank you

Below is the my expression for reading indirect tag to update the array value in power table.

runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(system.tag.readBlocking(" + {DirectTag.tagPath} + ")[0].value) if value > 0])",0)

I am using template that having power table with custom property of template parameter string to read tag.

Tag Info

Problem is tag is not reading through above expression and not fetching data to table.

Can we use system.tag.readBlocking() in expression?

Any suggestion much appreciate.

The problem is you are not providing a valid TagPath.

  1. Either you need to provide quotes around the supplied path in the expression for your template property, or you need to add them into the script for the runScript expression.

  2. TableTag does not point at the data that you are interested in. Your tag path needs to look like this:

[default]TableTag/AMag

The quotes are required so even if you have the correct path, without the quotes it still won’t work, as it will see the path as an undefined identifier.

Your expression seems to be returning the wrong value.

system.tag.readBlocking(" + {DirectTag.tagPath} + ")[0].value)

Seems to be evaluated as:

system.tag.readBlocking(TableTag)[0].value)

And TableTag is not a valid tagPath, maybe try:

system.tag.readBlocking('" + {DirectTag.tagPath}+  "/AMag')[0].value)

Thanks for the reply.
Adjusted the code now no error, but tables are not updating until I save. whereas table are updating if I use direct tag in expression.

below is the indirect tag expression.
runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(system.tag.readBlocking('" +{IndirectTag.Power Table.tTagPath}+ "/PLCUDTArray')[0].value) if value > 0])",0)

The 0 at the end of the runScript expression is the pollRate. If that is set to 0 then it only executes at load. Set it to some amount of ms that you wish for it to update. Beware, the lower the number the higher the load you will put on the client and the gateway.

Hi All,
Morning

It’s starts to update now after changing the value from 0 to 1000ms .

Thanks to everyone for your support. Much appreciated

2 Likes

Hi
How can I add another column in same power table using runscript.
same tag path but different array as below.
(system.tag.readBlocking('" +{IndirectTag.Power Table.TagPath}+ "/Faultnode')

Below is the current runscript I have. In this script I have to add another column.

runScript("system.dataset.toDataSet(['Slave', 'Value'], [[i,value] for i,value in enumerate(system.tag.readBlocking('" +{IndirectTag.Power Table.TagPath}+ "/PLCUDTArray')[0].value) if value > 0])",3000)

Can you please advice in this.
Thank you.

This run script is starting to get convoluted, and it seems to be doing to much work. I would abstract this out to a function, put the driving value into a bound custom property, and call the function from the custom property's propertyChange change event.

There is really no "good" and clean way to do this as a string in a runScript expression. Instead, you should create the script in the project library script, then use runScript() to call that instead.

For instance you could write this script:

def getDataSet(values, faultNodes):
    headers = ['Slave', 'Value', 'Node']
    data = [[i, value, node] for i, (value, node) in enumerate(zip(values, falutNodes))]
    return system.dataset.toDataSet(headers, data)

Next create custom properties to hold your arrays, and use Indirect Tag Bindings to bind them to the tags.

Then your expression can be:

runScript("libraryName.getDataSet",1000, {Power Table.UDTArray}, {Power Table.faultNodes})

Hi,

Thanks for the script. Much appreciated.

I have implemented the script in project library as ReadingArray. Then created custom properties in the name of FaultNodes and PLCUDTArray as string data type and bind them with array value as you can see in the pic.

I can able to read the value on the table but two digit values are reading as single digit including comma.

Is there a way that only reading value please. Thanks in Advance.

Ahh, yes, I forgot the limitation that custom properties cant have an array type. Hopefully, that gets fixed in 8.3

Anyway, we will need to force the script to see the string as a list of integer values instead of a list of characters.

def getDataSet(values, faultNodes):
    headers = ['Slave', 'Value', 'Node']

    cnvValues = [int(iVal) for iVal in values[1:-1].split(',')]
    cnvNodes = [int(iVal) for iVal in falutNodes[1:-1].split(',')]

    data = [[i, value, node] for i, (value, node) in enumerate(zip(cnvValues, cnvNodes))]
    return system.dataset.toDataSet(headers, data)
1 Like

Legend. Working really nice.

Final question I believe.

  1. Can we add "break" if wrong tag name given.
  2. Skip if values are showing Zero on PLCUDTArray

Thank you in Advance.

I don't know what this means, nor do I know how you would get the wrong tag name.

Yep, just add the conditional back in to the comprehension.

def getDataSet(values, faultNodes):
    headers = ['Slave', 'Value', 'Node']

    cnvValues = [int(iVal) for iVal in values[1:-1].split(',')]
    cnvNodes = [int(iVal) for iVal in falutNodes[1:-1].split(',')]

    data = [[i, value, node] for i, (value, node) in enumerate(zip(cnvValues, cnvNodes)) if value > 0]
    return system.dataset.toDataSet(headers, data)
1 Like