Open ALeman123 opened 4 years ago
duplicate of #15 ?
Since I saw no response i created this new topic to verify if others might have found a solution.
I made a way to read strings in order to get data from a plc to a .csv but it's time consuming. I have a flow that i could post but due to the number of SINT types needed to make a full string it's a rather large bit of text. (let me know if that isn't an issue and i'll post it here)
Basically, i followed these steps
~"all tag" E/ip node that that's scanning every SINT in the desired STRING tag (string.DATA[#]) ~Split/join-nodes the data so that it's in the proper order and now in an array ~function node that translates the array from dec to ascii ~node-red-contrib-stoptimer2 to make sure the STRING data scan is complete ~change node to format/organize payload
PLC side in order for this to work every desired time, i had to populate and clear the SINT data for every "scan".
I'm just going to post this, if it's too much just let me know and i'll remove.
This is a test sample of how i was able to get STRING and DINT data into a csv file
[{"id":"5822d7d0.88c7f","type":"tab","label":"Data_Trace_Test","disabled":false,"info":""},{"id":"93642354.47a498","type":"eth-ip in","z":"5822d7d0.88c7f","endpoint":"d455c786.24a808","mode":"all","variable":"Z1_DataTest_DINTArray[0]","program":"","name":"DINT_0","x":577.5,"y":234.3333282470703,"wires":[["be765ca9.b1933"]]},{"id":"6ee1c408.f0df34","type":"csv","z":"5822d7d0.88c7f","name":"CSV Format","sep":",","hdrin":false,"hdrout":true,"multi":"one","ret":"\\n","temp":"StringBoi,Z1_DataTest_DINTArray[0],Z1_DataTest_DINTArray[1],Z1_DataTest_DINTArray[2],Z1_DataTest_DINTArray[3],Z1_DataTest_DINTArray[4],Z1_DataTest_DINTArray[5],Z1_Real[0],Z1_Real[1],Z1_Real[2],Z1_Real[3],Z1_Real[4],Z1_Real[5]","skip":"3","strings":false,"x":864.949951171875,"y":299.3665771484375,"wires":[["886635ea.278a5"]]},{"id":"886635ea.278a5","type":"file","z":"5822d7d0.88c7f","name":"Data.CSV","filename":"","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"none","x":962.949951171875,"y":345.13323974609375,"wires":[[]]},{"id":"f9123854.a69f3","type":"eth-ip out","z":"5822d7d0.88c7f","endpoint":"1d08c74f.e59be9","variable":"Z1_DataTest_PB.1","program":"","name":"Comp_Bool","x":724.949951171875,"y":638,"wires":[]},{"id":"8a8047ec.9baa08","type":"complete","z":"5822d7d0.88c7f","name":"","scope":["886635ea.278a5"],"uncaught":false,"x":408.94996643066406,"y":644.7333374023438,"wires":[["901076c6.5db6e"]]},{"id":"901076c6.5db6e","type":"trigger","z":"5822d7d0.88c7f","op1":"true","op2":"false","op1type":"bool","op2type":"bool","duration":"250","extend":false,"units":"ms","reset":"","bytopic":"all","name":"","x":566.566650390625,"y":608.1666870117188,"wires":[["f9123854.a69f3"]]},{"id":"824ad270.9b1f4","type":"eth-ip in","z":"5822d7d0.88c7f","endpoint":"9182120d.6c825","mode":"all","variable":"","program":"","name":"String_0","x":131.566650390625,"y":267.16668701171875,"wires":[["246c8281.37b4a6"]]},{"id":"61f7f3f.065cf8c","type":"function","z":"5822d7d0.88c7f","name":"Dec to ASCII Convert","func":"function decimalToText(unicodeArray){\n return String.fromCharCode.apply(null, unicodeArray);\n}\n\nvar surething = decimalToText(msg.payload)\n\nmsg.payload = surething\n\nreturn msg;","outputs":1,"noerr":0,"x":384.5665588378906,"y":376.16661071777344,"wires":[["667b905f.b1739"]]},{"id":"246c8281.37b4a6","type":"split","z":"5822d7d0.88c7f","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":215.94996643066406,"y":303.6666259765625,"wires":[["fa6ac3ae.950598"]]},{"id":"be765ca9.b1933","type":"join","z":"5822d7d0.88c7f","name":"","mode":"custom","build":"merged","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":710.949951171875,"y":287.5333251953125,"wires":[["6ee1c408.f0df34"]]},{"id":"fa6ac3ae.950598","type":"join","z":"5822d7d0.88c7f","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"82","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":286.949951171875,"y":340.5,"wires":[["61f7f3f.065cf8c"]]},{"id":"667b905f.b1739","type":"stoptimer2","z":"5822d7d0.88c7f","duration":"1","durationType":"num","units":"Second","payloadtype":"num","payloadval":"0","name":"1 sec delay","x":492.949951171875,"y":414.89996337890625,"wires":[["cf44cc82.1cdaa"],[]]},{"id":"cf44cc82.1cdaa","type":"change","z":"5822d7d0.88c7f","name":"StringForm","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload[\"StringBoi\"]","tot":"msg"},{"t":"delete","p":"_msgid","pt":"msg"},{"t":"delete","p":"_timerpass","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":662.949951171875,"y":387.5999755859375,"wires":[["be765ca9.b1933"]]},{"id":"d5882a7c.fcf44","type":"comment","z":"5822d7d0.88c7f","name":"This E/IP node","info":"this is were we get our string data from the default 82 individual SINTs in the PLC","x":115.94999694824219,"y":217.06666564941406,"wires":[]},{"id":"82667fee.11851","type":"comment","z":"5822d7d0.88c7f","name":"split / join","info":"the split and join nodes are needed to organize the SINT data in the proper order.","x":336.566650390625,"y":278.566650390625,"wires":[]},{"id":"f08ad395.0b2bb","type":"comment","z":"5822d7d0.88c7f","name":"function note","info":"this is a very usefull function node that translates an array of decimal values into ascii format","x":277.566650390625,"y":412.566650390625,"wires":[]},{"id":"9c8e15e2.50d92","type":"comment","z":"5822d7d0.88c7f","name":"delay","info":"since the E/IP node scans STRINGS in a strange manner, the 1 sec delay node is nessasary to make sure that we only use the final scan of the PLC STRING array.","x":496.566650390625,"y":456.566650390625,"wires":[]},{"id":"9e9f688b.ec982","type":"comment","z":"5822d7d0.88c7f","name":"change","info":"formating the STRING data into an object for later packaging","x":683.566650390625,"y":430.566650390625,"wires":[]},{"id":"55d66f18.a5a728","type":"comment","z":"5822d7d0.88c7f","name":"E/IP Number data","info":"this node monitors the DINT/REAL datatypes. in the future it may be fruitfull to organize the station data in this fashion.","x":574.566650390625,"y":199.56666564941406,"wires":[]},{"id":"46208dc4.24ae5c","type":"comment","z":"5822d7d0.88c7f","name":"final tail","info":"in this final tail, we combine all object data and create a .CSV format.\n\nfinally we write to a CSV file as a designated location","x":977.566650390625,"y":257.566650390625,"wires":[]},{"id":"bf02a6ec.41d5f","type":"comment","z":"5822d7d0.88c7f","name":"Ack Finalize","info":"this little flow is simply a flag telling the PLC that our data has been saved","x":565.566650390625,"y":679.566650390625,"wires":[]},{"id":"d455c786.24a808","type":"eth-ip endpoint","z":"","address":"192.168.1.10","slot":"0","cycletime":"150","name":"DINTS_Test","vartable":{"":{"Z1_DataTest_DINTArray[0]":{"type":"DINT"},"Z1_DataTest_DINTArray[1]":{"type":"DINT"},"Z1_DataTest_DINTArray[2]":{"type":"DINT"},"Z1_DataTest_DINTArray[3]":{"type":"DINT"},"Z1_DataTest_DINTArray[4]":{"type":"DINT"},"Z1_DataTest_DINTArray[5]":{"type":"DINT"},"Z1_Real[0]":{"type":"REAL"},"Z1_Real[1]":{"type":"REAL"},"Z1_Real[2]":{"type":"REAL"},"Z1_Real[3]":{"type":"REAL"},"Z1_Real[4]":{"type":"REAL"},"Z1_Real[5]":{"type":"REAL"}}}},{"id":"1d08c74f.e59be9","type":"eth-ip endpoint","z":"","address":"192.168.1.10","slot":"0","cycletime":"25","name":"_PB_Test","vartable":{"":{"Z1_DataTest_PB.0":{"type":"BOOL"},"Z1_DataTest_PB.1":{"type":"BOOL"}}}},{"id":"9182120d.6c825","type":"eth-ip endpoint","z":"","address":"192.168.1.10","slot":"0","cycletime":"100","name":"StringPayloadTest","vartable":{"":{"Z1_DataTest_STRINGS[0].DATA[0]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[1]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[2]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[3]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[4]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[5]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[6]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[7]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[8]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[9]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[10]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[11]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[12]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[13]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[14]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[15]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[16]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[17]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[18]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[19]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[20]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[21]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[22]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[23]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[24]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[25]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[26]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[27]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[28]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[29]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[30]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[31]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[32]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[33]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[34]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[35]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[36]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[37]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[38]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[39]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[40]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[41]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[42]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[43]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[44]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[45]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[46]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[47]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[48]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[49]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[50]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[51]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[52]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[53]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[54]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[55]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[56]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[57]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[58]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[59]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[60]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[61]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[62]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[63]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[64]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[65]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[66]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[67]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[68]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[69]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[70]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[71]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[72]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[73]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[74]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[75]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[76]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[77]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[78]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[79]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[80]":{"type":"SINT"},"Z1_DataTest_STRINGS[0].DATA[81]":{"type":"SINT"}}}}]
That sounds like a reasonable solution. Thanks for sharing! I tried it with ethernet/ip messages, I ended up doing a simular thing.
@Archymedys do you know if it is possible to have a variable tagname as ethernet/ip output? I also want to send a string to the PLC but instead of selecting all tags manualy I could run a loop of an array to place the data. But so far i did not succeed.
I don't yet have the answer to that one.
It would be simpler if the nodes could handle tag arrays though.
I suppose, one could setup an indirect addressing "handshake loop" with the PLC, basically populating a STRING one SINT at a time.
The library this node is built upon (node-ethernet-ip) has had very few updates lately but one of the forks for it has integrated LINT and STRING types. The link is https://github.com/SerafinTech/node-ethernet-ip but use it as your own risk as the author has stated they have not completed all the necessary testing. If possible, someone might be able to use this fork to read string types.
Somebody has published this node-red pallete supporting String
https://www.npmjs.com/package/node-red-contrib-cip-ethernet-ip-strings-mfgx
I have made my own work-around for dealing with strings, since I only need to read about a half dozen.
I have the eth-ip node read every value of the .Data array, as well as the .LEN value.
Then I use the built-in javascript "String.fromCharCode()" function to do the ASCII conversion for me, and just concatenate the string together. I'm sure it's not super efficient, but it does work. And if the strings are shorter, the .LEN helps make it a litter better instead of running a bunch of code on empty values.
If someone was struggling to deal with just a few strings, so they can move on with the rest of their project, here is what I did, just using some globals to store the string values for me so I can do what I wish to with them later in the flow.
`var oldRecipe = global.get('Recipe'); var length = flow.get('RecipeLength');
var recipe = "";
for (var i = 0; i < length; i++) { var arrayLocation = "Recipe.Data[" + i + "]"; recipe += String.fromCharCode(msg.payload[arrayLocation]); }
msg.payload = {};
if (oldRecipe !== recipe) { msg.payload = recipe; global.set('Recipe',recipe); } else { msg.payload = oldRecipe; }`
Hi @msienkow could you paste a flow for your solution?
@TehSemni
Here is an example flow. I have moved on from using this. I kept getting unhandled promises and timeouts after running for a couple of days. I needed my application to run non-stop, regardless if it can communicate with the PLC or not. I ended up move on to pycomm3 and python and have had much better luck.
However here is a working example of what I was doing.
[ { "id": "9cf91256fe26e874", "type": "tab", "label": "String Example", "disabled": false, "info": "", "env": [] }, { "id": "c264e3623ba7a5d4", "type": "eth-ip endpoint", "address": "10.10.135.202", "slot": "0", "cycletime": "500", "name": "test", "vartable": { "": { "test_string.len": { "type": "DINT" }, "test_string.data[0]": { "type": "SINT" }, "test_string.data[1]": { "type": "SINT" }, "test_string.data[2]": { "type": "SINT" }, "test_string.data[3]": { "type": "SINT" }, "test_string.data[4]": { "type": "SINT" }, "test_string.data[5]": { "type": "SINT" }, "test_string.data[6]": { "type": "SINT" }, "test_string.data[7]": { "type": "SINT" }, "test_string.data[8]": { "type": "SINT" }, "test_string.data[9]": { "type": "SINT" }, "test_string.data[10]": { "type": "SINT" }, "test_string.data[11]": { "type": "SINT" }, "test_string.data[12]": { "type": "SINT" }, "test_string.data[13]": { "type": "SINT" }, "test_string.data[14]": { "type": "SINT" }, "test_string.data[15]": { "type": "SINT" }, "test_string.data[16]": { "type": "SINT" }, "test_string.data[17]": { "type": "SINT" }, "test_string.data[18]": { "type": "SINT" }, "test_string.data[19]": { "type": "SINT" }, "test_string.data[20]": { "type": "SINT" }, "test_string.data[21]": { "type": "SINT" }, "test_string.data[22]": { "type": "SINT" }, "test_string.data[23]": { "type": "SINT" }, "test_string.data[24]": { "type": "SINT" }, "test_string.data[25]": { "type": "SINT" }, "test_string.data[26]": { "type": "SINT" }, "test_string.data[27]": { "type": "SINT" }, "test_string.data[28]": { "type": "SINT" }, "test_string.data[29]": { "type": "SINT" }, "test_string.data[30]": { "type": "SINT" }, "test_string.data[31]": { "type": "SINT" }, "test_string.data[32]": { "type": "SINT" }, "test_string.data[33]": { "type": "SINT" }, "test_string.data[34]": { "type": "SINT" }, "test_string.data[35]": { "type": "SINT" }, "test_string.data[36]": { "type": "SINT" }, "test_string.data[37]": { "type": "SINT" }, "test_string.data[38]": { "type": "SINT" }, "test_string.data[39]": { "type": "SINT" }, "test_string.data[40]": { "type": "SINT" }, "test_string.data[41]": { "type": "SINT" }, "test_string.data[42]": { "type": "SINT" }, "test_string.data[43]": { "type": "SINT" }, "test_string.data[44]": { "type": "SINT" }, "test_string.data[45]": { "type": "SINT" }, "test_string.data[46]": { "type": "SINT" }, "test_string.data[47]": { "type": "SINT" }, "test_string.data[48]": { "type": "SINT" }, "test_string.data[49]": { "type": "SINT" }, "test_string.data[50]": { "type": "SINT" }, "test_string.data[51]": { "type": "SINT" }, "test_string.data[52]": { "type": "SINT" }, "test_string.data[53]": { "type": "SINT" }, "test_string.data[54]": { "type": "SINT" }, "test_string.data[55]": { "type": "SINT" }, "test_string.data[56]": { "type": "SINT" }, "test_string.data[57]": { "type": "SINT" }, "test_string.data[58]": { "type": "SINT" }, "test_string.data[59]": { "type": "SINT" }, "test_string.data[60]": { "type": "SINT" }, "test_string.data[61]": { "type": "SINT" }, "test_string.data[62]": { "type": "SINT" }, "test_string.data[63]": { "type": "SINT" }, "test_string.data[64]": { "type": "SINT" }, "test_string.data[65]": { "type": "SINT" }, "test_string.data[66]": { "type": "SINT" }, "test_string.data[67]": { "type": "SINT" }, "test_string.data[68]": { "type": "SINT" }, "test_string.data[69]": { "type": "SINT" }, "test_string.data[70]": { "type": "SINT" }, "test_string.data[71]": { "type": "SINT" }, "test_string.data[72]": { "type": "SINT" }, "test_string.data[73]": { "type": "SINT" }, "test_string.data[74]": { "type": "SINT" }, "test_string.data[75]": { "type": "SINT" }, "test_string.data[76]": { "type": "SINT" }, "test_string.data[77]": { "type": "SINT" }, "test_string.data[78]": { "type": "SINT" }, "test_string.data[79]": { "type": "SINT" }, "test_string.data[80]": { "type": "SINT" }, "test_string.data[81]": { "type": "SINT" } } } }, { "id": "3042f73fc1785d2e", "type": "eth-ip in", "z": "9cf91256fe26e874", "endpoint": "c264e3623ba7a5d4", "mode": "all", "variable": "", "program": "", "name": "", "x": 190, "y": 340, "wires": [ [ "ad15ece1450e791f" ] ] }, { "id": "2a221bc4aae05dd0", "type": "debug", "z": "9cf91256fe26e874", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 730, "y": 340, "wires": [] }, { "id": "ad15ece1450e791f", "type": "function", "z": "9cf91256fe26e874", "name": "", "func": "let length = msg.payload[\"test_string.len\"];\nlet string = \"\";\nfor (let i = 0; i < length; i++) {\n let tag = \"test_string.data[\" + i + \"]\";\n string += String.fromCharCode(msg.payload[tag]);\n}\n\nmsg.payload = string;\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 460, "y": 340, "wires": [ [ "2a221bc4aae05dd0" ] ] } ]
@msienkow
Thank you for replying and providing the samples I really appreciate that.
+1 this should be supported
Is there any news on when the string data type will be supported?
Any others who have found different ways to send and receive strings?