Two ways to loop dynamic write objects for Opto nodes

If you check the help information for groov or PAC nodes, you see that there are specific JavaScript object properties you can define to dynamically write values to your Opto tags. In this post I’ll use groov View nodes, but the technique applies to PAC nodes as well.
Here is an example object that the groov View nodes can write from:

msg.payload = {
  tagName         : 'MyTable',
  tableStartIndex : 5,
  value           : 10
};

Sometimes the tag name will always be the same and you can just set the static property in the node itself, but other times the tag name will change by a simple count value, or sometimes you’ll have totally different names you want to write for. (I’ve already covered writing to groov tables in a previous post here: Writing a groov table with a single node)

To use either of the methods I’ll be going over in this post you’ll need to change the groov write node “value” property to be msg.payload.value since that is the property that we’ll be defining and sending from the function node. The rest is handled by the for loop and node.send() function.

image


For the first example I’ll be writing to groov View tags test_value01 through test_value16 with some msg.payload array that could come from any Node-RED data source – in this case an inject node:

image

Here is the contents of the for loop function node:

for (i = 0; i < msg.payload.length; i++) {
    twoDigit = ('0'+(i+1)).slice(-2);
    
    node.send({ payload : {
        tagName         : 'test_value' + twoDigit,
        value           : msg.payload[i]
    }})
}

Here the incoming data is held in the individual msg.payload[i] array slots. You could also have it on a sub-property, in which case you could just change the node.send() value property to be msg.payload[i].subProperty, or wherever it happens to be stored.

The main part of this example is the 'test_value' + twoDigit I’m using for the tagName. Since each tag name consists of the string ‘test_value’ and then a two-digit number '01' through '16' we need to add a leading '0' to all single-digit values, but avoid having adding it to two-digit values so we don’t end up trying to reference test_value011, which is not one of our tags.
To do this we simply add 1 to the index i (since the for loop starts at the integer 0 but our first test_value is 01), then append the character '0' to the front for our leading 0, and finish by taking only the last two values to prevent creating any triple-digit values once we pass i+1=10.
This is how we get the line twoDigit = ('0'+(i+1)).slice(-2);

Note: Make sure you change the msg.payload[i] value to wherever your data source is, and the msg.payload.length to the data table length in the for loop definition.

Here’s the flow import text for you to try out for yourself:

[{"id":"d3ecf84d.34d258","type":"inject","z":"6dc3f358.26a64c","name":"data source","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"alphabet","payload":"[\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\"]","payloadType":"json","x":550,"y":680,"wires":[["d1c4231a.de17b"]]},{"id":"cbfaf03d.2cec3","type":"debug","z":"6dc3f358.26a64c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":930,"y":640,"wires":[]},{"id":"d1c4231a.de17b","type":"function","z":"6dc3f358.26a64c","name":"for loop","func":"for (i = 0; i < msg.payload.length; i++) {\n    twoDigit = ('0'+(i+1)).slice(-2);\n    \n    node.send({ payload : {\n        tagName         : 'test_value' + twoDigit,\n        value           : msg.payload[i]\n    }})\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":720,"y":680,"wires":[["cbfaf03d.2cec3","13ae212e.9ff38f"]]},{"id":"13ae212e.9ff38f","type":"groov-write-ds","z":"6dc3f358.26a64c","dataStore":"b1060423.ddf178","tagName":"","tableStartIndex":"","value":"payload.value","valueType":"msg","name":"","x":910,"y":680,"wires":[[]]},{"id":"b1060423.ddf178","type":"groov-data-store","z":"","project":"dc976ab5.a5fcc8","dsName":"nodered"},{"id":"dc976ab5.a5fcc8","type":"groov-project","z":"","address":"localhost"}]

The other scenario you might have is that there is no sequential way to reference each of the tag names, for example you might have unique names for each and every tag you want to write, but still want to fill them with a table of values. For example your tags could be:

In this case we cannot dynamically create the tag name by adding a number to a string, we need to reference the string names individually. To do this, just create an additional array to hold each tag name, then reference each array index just like you do with the incoming data values:

// List out the tagnames in a reference array:
tagNames = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth"];

for (i = 0; i < msg.payload.length; i++) {
    node.send({ payload : {
        tagName         : tagNames[i],
        value           : msg.payload[i]
    }})
}

Here we just create a tagNames array with the unique, individual names, then reference them with tagName : tagNames[i], in the node.send() function.

Again, you will need to set the for loop size based on whatever your data array is, in this case msg.payload.length, and make sure that your tagNames list has the same number of items as that data array, and you should be good to go.

Here is the flow import text for this second example:

[{"id":"e1e14225.fe757","type":"function","z":"6dc3f358.26a64c","name":"for loop","func":"// List out the tagnames in a reference array:\ntagNames = [\"first\", \"second\", \"third\", \"fourth\", \"fifth\", \"sixth\", \"seventh\", \"eighth\"];\n\nfor (i = 0; i < msg.payload.length; i++) {\n    node.send({ payload : {\n        tagName         : tagNames[i],\n        value           : msg.payload[i]\n    }})\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":720,"y":860,"wires":[["cd40743f.ee5978","b21c333d.c80a6"]]},{"id":"3e5164fc.8e1c1c","type":"inject","z":"6dc3f358.26a64c","name":"data source","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"values","payload":"[\"value1\",\"value2\",\"value3\",\"value4\",\"value5\",\"value6\",\"value7\",\"value8\"]","payloadType":"json","x":550,"y":860,"wires":[["e1e14225.fe757"]]},{"id":"cd40743f.ee5978","type":"debug","z":"6dc3f358.26a64c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":930,"y":820,"wires":[]},{"id":"b21c333d.c80a6","type":"groov-write-ds","z":"6dc3f358.26a64c","dataStore":"b1060423.ddf178","tagName":"","tableStartIndex":"","value":"payload.value","valueType":"msg","name":"","x":910,"y":860,"wires":[[]]},{"id":"b1060423.ddf178","type":"groov-data-store","z":"","project":"dc976ab5.a5fcc8","dsName":"nodered"},{"id":"dc976ab5.a5fcc8","type":"groov-project","z":"","address":"localhost"}]

If you have any questions about these flows or the methods used, or if you use these in your own project and modify them in some way, please feel free to ask questions and share your projects in the thread below!

Happy coding!

1 Like