Checksum/Hash on a group of OPC UA Tags

TLDR:
I have a UDT that I am using to write/read recipes for a process we run. Each part we make has its own recipe, and they have been manually confirming that the recipe matches our documented recipes. This has led to errors and scraped out parts. And it is surprisingly difficulted to maintain change management across 7 machines.

I would love to have some kind of checksum to compare rather than just checking by a bunch of if-then statements.

I know that Ignition has a lot of java libs built into it and callable in a script. Could one of them have a checksum or hashing ability?

import org.apache.commons.codec.digest.DigestUtils.sha256Hex as sha256Hex

data = "example-data-structure"
print(sha256Hex(data))

ChatGPT for the win
But it only works on strings

If the tags are all either strings or numbers, they should be hashable.

So in theory you should be able to iterate through the master recipe, and the selected recipe, and compare the results of the python builtin hash() function. Something like this:

def chunker(seq,size):
	return (seq[pos:pos + size] for pos in xrange(0,len(seq),size))
	
def confirmRecipe():
	documnetedPaths = ["list.of.paths.to.documented.values"]
	selectedPaths = ["list.of.paths.to.selected.values"]
	tagPaths =  documentedPaths + selectedPaths
	tagValues = system.tag.readBlocking(tagPaths)
	
	documentedValues, selectedValues = [[qv.value for qv in chunk] for chunk in chunker(tagValues,len(documentedPaths))]
	
	for i, value in enumerate(documentedValues):
		if hash(value) != hash(selectedValues[i]):
			#flag as unconfirmed
			return False
	
	#if execution makes it here then recipes match
	return True

This will work with str,int,bool, float

Or any other type that is hashable.

import org.apache.commons.codec.digest.DigestUtils.sha256Hex as sha256Hex

segmentTagPath = ["[default]Folder/Tag_A","[default]Folder/Tag_B"]
values = system.tag.readBlocking(segmentTagPath)
valueList = ''
for x in values:
    valueList + str(x.value)
print(sha256Hex(valueList))

I ended up doing something like this. The main thing is the sha256Hex function only works on strings.

So, I index into the returning array of data and pulled only the value of the tag, and nothing else as that will affect the hash. And stringify the value.

Concatenate all the values into a string and run the sha256Hex function on that.

Note: Any change of the order of the tags read will affect the hash amount.

We use DigestBuilder for this sort of task internally.

So your snippet becomes:

from com.inductiveautomation.ignition.common.util import DigestBuilder

segmentTagPath = ["[default]Folder/Tag_A","[default]Folder/Tag_B"]
values = system.tag.readBlocking(segmentTagPath)
digest = DigestBuilder.sha256()
for x in values:
    digest.update(x.value)
print(digest.buildHexString())

The big advantage being the update function has overloads for common numeric types as well as direct byte arrays.

4 Likes

I think that PDriffith solution will work and thus he gets the solution point. Altho i might just use 16 bits to keep it short.

But then I started to wonder if sha256 was the absolute best function to use.

I went down a rabbit hole. Not knowing too much about cryptography, it took me some time to describe the things I want in a function:

Avalanche Effect: I would like it to have a drastic change if even one datapoint is off . That way operators can very easily tell if the settings are off

Short: I would like it to be less than 32 characters so we can have operators check, by hand, it to our documented procedure

Ease of Implementation: Something someone already has a lib for would be great

Reversible: I would like it to be uncompressible to see the data inside if possible

Can Be Insecure: No need for keys or security

I don't expect there to be a function/algorithm that matches all these criteria but you never know untill you ask

Short and reversible are literally mutually incompatible.

Also, short increases the risk of collisions significantly.

You may be interested in sidestepping the problem:

1 Like

unfortunately, Pigeonhole principle - Wikipedia causes you suffering.

What you could do is 'reversible compression, but operators check the first 6 bits of the hash', git style