Siemens reading from PLC with nodeS7 npm

Hello,

I needed dynamic addressing of Siemens PLC, so standard NR nodes are not a case. I've imported GitHub - plcpeople/nodeS7: Node.JS library for communication to Siemens S7 PLCs as external module.

Here is the simple flow which closes connection after data is read. It is necessary because S7 connections accumulate until ECONNRESET is thrown and NR needs to be restarted.

My end goal was to read identical structures of data with offset for each module.
Simple flow is here, PLC IP is static.

[{"id":"96b881a14dbfa4a0","type":"subflow","name":"S7 read offset","info":"","category":"","in":[{"x":40,"y":80,"wires":[{"id":"2b484e68cee9678b"}]}],"out":[{"x":620,"y":80,"wires":[{"id":"c75e08ca6ed6f92e","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"c75e08ca6ed6f92e","type":"function","z":"96b881a14dbfa4a0","name":"S7 read","func":"//var nodes7 = require('nodes7'); // This is the package name, if the repository is cloned you may need to require 'nodeS7' with uppercase S\nlet conn = new nodes7;\nlet doneReading = false;\nlet doneWriting = false;\n\nlet variables=msg.payload;\nlet var_names = Object.keys(variables);\n\nconn.initiateConnection({ port: 102, host: '11.200.2.63', rack: 0, slot: 1, debug: true }, connected); // slot 2 for 300/400, slot 1 for 1200/1500, change debug to true to get more info\n// conn.initiateConnection({port: 102, host: '192.168.0.2', localTSAP: 0x0100, remoteTSAP: 0x0200, timeout: 8000, doNotOptimize: true}, connected);\n// local and remote TSAP can also be directly specified instead. The timeout option specifies the TCP timeout.\n\nfunction connected(err) {\n  if (typeof(err) !== \"undefined\") {\n    console.log(err);\n  }\n  conn.setTranslationCB(function(tag) { return variables[tag]; }); // This sets the \"translation\" to allow us to work with object names\n  conn.addItems(var_names);\n  console.log(\"-----------------Init done-------------\")\n  conn.readAllItems(valuesReady);\n\n}\n\nfunction valuesReady(anythingBad, values) {\n  if (anythingBad) { \n      //console.log(\"SOMETHING WENT WRONG READING VALUES!!!!\"); \n\n      node.warn(\"SOMETHING WENT WRONG READING VALUES!!!!\");\n      conn.dropConnection(); \n      conn.connectionCleanup();\n  }\n  console.log(values);\n  msg.payload = values;\n  node.send(msg);\n\n  conn.dropConnection();\n  conn.connectionCleanup();\n  \n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"nodes7","module":"nodes7"}],"x":440,"y":80,"wires":[[]]},{"id":"2b484e68cee9678b","type":"function","z":"96b881a14dbfa4a0","name":"Create Offset variables","func":"let offset = msg.payload;\nvar new_Obj = {};\n\nlet variables = {\n    \"var0\": \"DB40,X0.1\",\n    \"var1\": \"DB40,INT2\",\n    \"var2\": \"DB40,INT4\",\n    \"var3\": \"DB40,INT6\",\n    \"var4\": \"DB40,INT8\",\n    \"var5\": \"DB599,INT0\"\n}\n\nlet new_variables = {};\n\nlet offset_addr = [];\n\nlet init_addr = Object.values(variables); //extract variables addresses\n\ninit_addr.forEach((element)=>{\n    let AddrArray = element.split(\",\"); //Split string into memory and address location\n    const regex = /[+-]?\\d+(\\.\\d+)?/g; //extract numbers from string\n    let address = AddrArray[1].match(regex);  // array with found numbers\n    let new_addr_nr = parseFloat(address[0])+offset; //number with offset address\n    \n    const vartype = AddrArray[1].substring(0, AddrArray[1].indexOf(address)); //extract variable type\n    \n    //new variable string\n    let new_addr_str = AddrArray[0]+\",\"+vartype+new_addr_nr.toString();\n    \n    offset_addr.push(new_addr_str)\n});\n\n//Re-map existing keys to new offset values\nObject.keys(variables).forEach(function(key, index) {\n  new_variables[key] = offset_addr[index];\n});\n\n// new_Obj.old = variables;\n// new_Obj.new = new_variables;\n\nmsg.payload = new_variables;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":200,"y":80,"wires":[["c75e08ca6ed6f92e"]]},{"id":"245f1f869343b198","type":"function","z":"d3c43d3a26c0723a","name":"Offset","func":"msg.payload = 0;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":180,"wires":[["51b3830442babab7"]]},{"id":"51b3830442babab7","type":"subflow:96b881a14dbfa4a0","z":"d3c43d3a26c0723a","name":"","env":[{"name":"flexdash_grid","value":"","type":"str"}],"x":700,"y":180,"wires":[["7b900b1547e43189"]]},{"id":"5e30148055bbfb34","type":"function","z":"d3c43d3a26c0723a","name":"Offset","func":"msg.payload = 6;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":220,"wires":[["5077f67b700bdd30"]]},{"id":"5077f67b700bdd30","type":"subflow:96b881a14dbfa4a0","z":"d3c43d3a26c0723a","name":"","env":[{"name":"flexdash_grid","value":"","type":"str"}],"x":700,"y":220,"wires":[[]]},{"id":"7abc152169fece82","type":"function","z":"d3c43d3a26c0723a","name":"Offset","func":"msg.payload = 12;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":260,"wires":[["fd9ec24f906792d4"]]},{"id":"fd9ec24f906792d4","type":"subflow:96b881a14dbfa4a0","z":"d3c43d3a26c0723a","name":"","env":[{"name":"flexdash_grid","value":"","type":"str"}],"x":700,"y":260,"wires":[[]]},{"id":"c23d4592c282ac06","type":"function","z":"d3c43d3a26c0723a","name":"function 4","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":180,"wires":[["245f1f869343b198","5e30148055bbfb34","7abc152169fece82","eaaa13f228d86b98","50a0620b653b09d7","a9f5409ca25514f6","5c01cae7b6ebcf8a","49cc52096270c735","5e74bc5a714bc42d","3a5b1d305b15e429","96cc978ccea9bdb1","33031e3fab92dc1e","b735761ba3e62f45","360e0e1cc13cd591","b2bd7df7871b56b0","662588ee5d8927a9","d908d8a9ef633bf5","800f0e98a5ac5352","93ccd25073baa3f8","a9a269f1bbd2f9ab","d3f6e5e2681df604","17435b0e66cfdd4b","62a71ca610d15162","5c99531f7d0a823a","2e720f298f243a4a","7cdfcd1dd25e62b4","9d2278fa9c01606d","3bf6d8442e6b0ec6","447f76cf07dc6579","113797b26badedab","24424e1d05994c1f","ae4212a166a01681","5243d40c2171bffe","436010cd92bae826","f9155f31df7d7c9f","e276d6d265dcf5ad","095f3b20d857c07c","ffd43eafe7020d56","e2aab48cfac801d6","006f5cacbb04ef8a","01cf673d15ce4f11","c4622982c553387a"]]},{"id":"6e6ec1629192ecc7","type":"inject","z":"d3c43d3a26c0723a","name":"1s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":180,"wires":[["c23d4592c282ac06"]]}]

What I found out is that with this setup I was able to monitor ~40 modules at most, otherwise BAD 255 values were given at random "S7 read offset" nodes

What I've also tried is to keep connection open and read values in separate function. Beta version without control when connection is missing.

[{"id":"57d868d90281a1cc","type":"inject","z":"d3c43d3a26c0723a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":290,"y":60,"wires":[["11acc26bcd04e936"]]},{"id":"fc2bbd968a22cf7b","type":"function","z":"d3c43d3a26c0723a","name":"S7 init connection","func":"\nlet conn = new nodes7;\nlet doneReading = false;\nlet doneWriting = false;\n\nlet variables=msg.payload;\nlet var_names = Object.keys(variables);\n\nconn.initiateConnection({ port: 102, host: '11.200.2.63', rack: 0, slot: 1, debug: true }, connected); // slot 2 for 300/400, slot 1 for 1200/1500, change debug to true to get more info\n// conn.initiateConnection({port: 102, host: '192.168.0.2', localTSAP: 0x0100, remoteTSAP: 0x0200, timeout: 8000, doNotOptimize: true}, connected);\n// local and remote TSAP can also be directly specified instead. The timeout option specifies the TCP timeout.\n\nfunction connected(err) {\n  if (typeof(err) !== \"undefined\") {\n    // We have an error. Maybe the PLC is not reachable.\n    console.log(err);\n    //process.exit();\n  }\n  conn.setTranslationCB(function(tag) { return variables[tag]; }); // This sets the \"translation\" to allow us to work with object names\n  conn.addItems(var_names);\n  console.log(\"-----------------Init done-------------\")\n   const newobj = conn;\n   msg.payload = conn;\n   node.send(newobj)\n   node.send(msg)\n   global.set(\"conn\",newobj)\n}\n\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"nodes7","module":"nodes7"}],"x":690,"y":60,"wires":[["bed6097f77a5f058"]]},{"id":"11acc26bcd04e936","type":"function","z":"d3c43d3a26c0723a","name":"Create Init variables","func":"let variables = {\n    \"var0\": \"DB40,X0.1\",\n    \"var1\": \"DB40,INT2\",\n    \"var2\": \"DB40,INT4\",\n    \"var3\": \"DB40,INT6\",\n    \"var4\": \"DB40,INT8\"\n}\n\nmsg.payload = variables;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":60,"wires":[["fc2bbd968a22cf7b"]]},{"id":"bed6097f77a5f058","type":"debug","z":"d3c43d3a26c0723a","name":"S7conn","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":860,"y":60,"wires":[]},{"id":"58e1c947191fc4db","type":"inject","z":"d3c43d3a26c0723a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"6","payloadType":"num","x":290,"y":100,"wires":[["aa33038330a3649b"]]},{"id":"fbfecaa4fe8ab1d0","type":"debug","z":"d3c43d3a26c0723a","name":"ReadValues","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":100,"wires":[]},{"id":"f769be1c469c325f","type":"function","z":"d3c43d3a26c0723a","name":"S7 read","func":"\nlet conn = global.get(\"conn\")\nlet doneReading = false;\nlet doneWriting = false;\n\nlet variables=msg.payload;\nlet var_names = Object.keys(variables);\n\nconn.setTranslationCB(function(tag) { return variables[tag]; }); // This sets the \"translation\" to allow us to work with object names\nconn.addItems(var_names);\n//global.set()\nconn.readAllItems(valuesReady);\n\nfunction valuesReady(anythingBad, values) {\n  if (anythingBad) { \n      //console.log(\"SOMETHING WENT WRONG READING VALUES!!!!\"); \n\n      node.warn(\"SOMETHING WENT WRONG READING VALUES!!!!\");\n  }\n  console.log(values);\n  msg.payload = values;\n  node.send(msg)\n}\n\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":100,"wires":[["fbfecaa4fe8ab1d0"]]},{"id":"aa33038330a3649b","type":"function","z":"d3c43d3a26c0723a","name":"Create Offset variables","func":"let offset = msg.payload;\nvar new_Obj = {};\n\nlet variables = {\n    \"var0\": \"DB40,X0.1\",\n    \"var1\": \"DB40,INT2\",\n    \"var2\": \"DB40,INT4\",\n    \"var3\": \"DB40,INT6\",\n    \"var4\": \"DB40,INT8\",\n    \"var5\": \"DB40,INT10\",\n    \"var6\": \"DB40,INT12\"    \n}\n\nlet new_variables = {};\n\nlet offset_addr = [];\n\nlet init_addr = Object.values(variables); //extract variables addresses\n\ninit_addr.forEach((element)=>{\n    let AddrArray = element.split(\",\"); //Split string into memory and address location\n    const regex = /[+-]?\\d+(\\.\\d+)?/g; //extract numbers from string\n    let address = AddrArray[1].match(regex);  // array with found numbers\n    let new_addr_nr = parseFloat(address[0])+offset; //number with offset address\n    \n    const vartype = AddrArray[1].substring(0, AddrArray[1].indexOf(address)); //extract variable type\n    \n    //new variable string\n    let new_addr_str = AddrArray[0]+\",\"+vartype+new_addr_nr.toString();\n    \n    offset_addr.push(new_addr_str)\n});\n\n//Re-map existing keys to new offset values\nObject.keys(variables).forEach(function(key, index) {\n  new_variables[key] = offset_addr[index];\n});\n\n// new_Obj.old = variables;\n// new_Obj.new = new_variables;\n\nmsg.payload = new_variables;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":100,"wires":[["f769be1c469c325f"]]}]

Hope it will save somebody some time and help in the future