Array Manipulation

Hello community!

Hopefully a simple and fast one here, I'm reading four registers using a Modbus read and need to convert this to date/time using epoch (a great discussion already posted in this forum on how to do that conversion).

When I do the Modbus read I am getting an array returned as follows:
image

What I need to do is manipulate this array to sort the values in a way that I can arrange 5, 58851, 51851, 2688 in that sequence, then convert each to its binary equivalent and output a single sequence of 1/0s and then use those 64 bits to convert to date/time (again, the date/time conversion has already been addressed so no help needed on that).

Any suggestions to point me in the right direction ?

A function containing this should do what you want, assuming that the incoming array is in msg.payload

msg.payload = (((((msg.payload[3] * 65536) + 
                  msg.payload[2]) * 65536) +
                  msg.payload[1]) * 65536) + msg.payload[0]
return msg;

Test flow:

[{"id":"fa9a77ed282b4368","type":"inject","z":"bdd7be38.d3b55","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[2688,51851,58851,5]","payloadType":"json","x":140,"y":4660,"wires":[["dc7483f785d25b67"]]},{"id":"dc7483f785d25b67","type":"function","z":"bdd7be38.d3b55","name":"Build answer","func":"msg.payload = (((((msg.payload[3] * 65536) + \n                  msg.payload[2]) * 65536) +\n                  msg.payload[1]) * 65536) + msg.payload[0]\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":4660,"wires":[["38086ddbb12e2bbb"]]},{"id":"38086ddbb12e2bbb","type":"debug","z":"bdd7be38.d3b55","name":"debug 16","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":4660,"wires":[]}]

Thank you so much for this ,, this does exactly what I am looking for.

I don't understand the magic behind it, but it does exactly what I need. Many thanks

Please spend a bit of time understanding it. You should never use code like this that you do not, at least vaguely, understand. Otherwise I might have planted some malicious code there (difficult in such a short sequence I know, but the principle is correct).
Start with Javascript Arrays, then once you basically see how they work the rest should be ok.

You are correct Colin :slight_smile:

I did follow the array organization, what I didn't get was the reason for multiplication by 65536.

65536 is 2^16 or 1 followed by 16 zeros. So multiplying by that effectively adds 16 zeros at the right hand side.
The number you are looking for is
5 followed by 48 zeros +
58851 followed by 32 zeros +
51851 followed by 16 zeros +
2668

Which is what the code implements.

Magic demystified :D!

While what colin showed you works, you should be aware that regular Numbers in JavaScript are only really good for about 10^53

e.g. Number.MAX_SAFE_INTEGER results in 9007199254740991

fortunately, the number you are calculating is 1660141402000000

The safe way to do this in a function node is...

const buf = new Buffer.alloc(8)
buf.writeUInt16BE(msg.payload[3],0)
buf.writeUInt16BE(msg.payload[2],2)
buf.writeUInt16BE(msg.payload[1],4)
buf.writeUInt16BE(msg.payload[0],6)
msg.payload = buf.readBigInt64BE() // note this will be of type BigInt64
// or msg.payload = buf.readBigInt64BE().toString() // for a string: 1660141402000000
// or msg.payload = parseInt(buf.readBigInt64BE()) // to convert to regular Number
return msg;

Alternatively, without any functions, use Buffer-parser - designed specifically for making sense of PLC/modbus/buffers permitting you to create 1, 2, 3 (as many as you can) string/int/uint/32 bit, 64 bit, BCD etc etc - in one node (all at once).
Reading many registers and converting them all at once has the benefit of being consistent data ...

[{"id":"445423eb33b15f74","type":"buffer-parser","z":"b851e68919625d02","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"bigint64le","name":"time","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"swap16","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"return","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":2430,"y":160,"wires":[["17ba904498e1ef93"]]},{"id":"5f0316999f769032","type":"function","z":"b851e68919625d02","name":"modbus (get 4 registers)","func":"msg.payload = [\n    2688,51851,58851,5\n]\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2380,"y":100,"wires":[["445423eb33b15f74","526814b3820d55a3"]]},{"id":"6a0f9d393e9817de","type":"inject","z":"b851e68919625d02","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":2190,"y":100,"wires":[["5f0316999f769032"]]},{"id":"526814b3820d55a3","type":"debug","z":"b851e68919625d02","name":"debug 57","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2620,"y":100,"wires":[]},{"id":"17ba904498e1ef93","type":"debug","z":"b851e68919625d02","name":"debug 58","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2620,"y":160,"wires":[]}]
1 Like

That is quite correct in the general case. However since @Jaime_G knows that this is a microsecond timestamp (10 August 2022 14:23:22 UTC) in practice, if it fails then there is a more fundamental problem as the data would be at least 230 years in the future, if I have done my sums correctly. If a date greater than that were provided then the accuracy of the timestamp returned would suffer, but it would still be in the right ballpark.

Node-RED V233.0.0 launches then. I think EMCA has also fixed the Number limitation :wink:

Excellent. So I will be able to rest in peace knowing that my contribution to open source code will continue to be useful to the AI beings that will no doubt still be going long after mankind has fried due to climate change.

3 Likes

Hi Steve !

I've been testing the buffer parser and it is a pretty slick tool, thanks for the suggestion.

A quick question if I may on this, using the recommended solution from Colin I'm able to scale the result easily through a function (if you recall the result was in milliseconds and to convert to epoch time it needed to be done in seconds).

When I use the buffer parser output (using the example that you provided) if I try to do a function to divide by 1000 or multiply by 10^(-3) I keep getting results that either say NaN or "TypeError: Cannot mix BigInt and other types, use explicit conversions"

Could you help teach a man how to fish ?

Its a common issue with 64 bit numbers. But as already ascertained, you are not hitting Number.MAX_SAFE_INTEGER so you can simply cast it to a Number using Number(msg.payload.time) / 1000

OR

Better still, use BigInt devisor e.g. msg.payload.time\ 1000n (note the n means BigInt)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.