node-red-contrib-my-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.txt +69 -0
  2. package/node-red-contrib-mysql-extended-test/mysql-extended.html +443 -0
  3. package/node-red-contrib-mysql-extended-test/mysql-extended.js +355 -0
  4. package/node-red-contrib-rfid-helper/rfid-lazy.html +153 -0
  5. package/node-red-contrib-rfid-helper/rfid-lazy.js +361 -0
  6. package/node-red-contrib-s7-plus/LICENSE +235 -0
  7. package/node-red-contrib-s7-plus/README.md +141 -0
  8. package/node-red-contrib-s7-plus/examples/read-multiple-values-flow.json +346 -0
  9. package/node-red-contrib-s7-plus/examples/read-write-test-flow.json +172 -0
  10. package/node-red-contrib-s7-plus/examples/write-single-values-flow.json +5583 -0
  11. package/node-red-contrib-s7-plus/lib/s7plus/browse/README.md +72 -0
  12. package/node-red-contrib-s7-plus/lib/s7plus/browse/areas.js +36 -0
  13. package/node-red-contrib-s7-plus/lib/s7plus/browse/datatypes.js +91 -0
  14. package/node-red-contrib-s7-plus/lib/s7plus/browse/flat-browser.js +433 -0
  15. package/node-red-contrib-s7-plus/lib/s7plus/browse/index.js +22 -0
  16. package/node-red-contrib-s7-plus/lib/s7plus/browse/lazy.js +397 -0
  17. package/node-red-contrib-s7-plus/lib/s7plus/browse/node-id.js +22 -0
  18. package/node-red-contrib-s7-plus/lib/s7plus/browse/resolve-symbolic.js +181 -0
  19. package/node-red-contrib-s7-plus/lib/s7plus/browse/vte-helpers.js +49 -0
  20. package/node-red-contrib-s7-plus/lib/s7plus/buffer-stream.js +96 -0
  21. package/node-red-contrib-s7-plus/lib/s7plus/client.js +1238 -0
  22. package/node-red-contrib-s7-plus/lib/s7plus/constants.js +155 -0
  23. package/node-red-contrib-s7-plus/lib/s7plus/crc/index.js +3 -0
  24. package/node-red-contrib-s7-plus/lib/s7plus/crc/s7-crc32.js +70 -0
  25. package/node-red-contrib-s7-plus/lib/s7plus/crc/symbol-crc.js +271 -0
  26. package/node-red-contrib-s7-plus/lib/s7plus/debug.js +88 -0
  27. package/node-red-contrib-s7-plus/lib/s7plus/explore-result.js +41 -0
  28. package/node-red-contrib-s7-plus/lib/s7plus/item-address.js +55 -0
  29. package/node-red-contrib-s7-plus/lib/s7plus/pdu-explore.js +77 -0
  30. package/node-red-contrib-s7-plus/lib/s7plus/pdu-messages.js +359 -0
  31. package/node-red-contrib-s7-plus/lib/s7plus/pobject.js +197 -0
  32. package/node-red-contrib-s7-plus/lib/s7plus/pvalue-codec.js +435 -0
  33. package/node-red-contrib-s7-plus/lib/s7plus/pvalue.js +653 -0
  34. package/node-red-contrib-s7-plus/lib/s7plus/pvar-lists.js +336 -0
  35. package/node-red-contrib-s7-plus/lib/s7plus/read-result.js +91 -0
  36. package/node-red-contrib-s7-plus/lib/s7plus/s7p.js +266 -0
  37. package/node-red-contrib-s7-plus/lib/s7plus/tag-routing.js +30 -0
  38. package/node-red-contrib-s7-plus/lib/s7plus/transport/cotp-stream.js +335 -0
  39. package/node-red-contrib-s7-plus/lib/s7plus/transport/s7-transport.js +335 -0
  40. package/node-red-contrib-s7-plus/nodes/icons/s7complus.png +0 -0
  41. package/node-red-contrib-s7-plus/nodes/icons/s7complus.svg +17 -0
  42. package/node-red-contrib-s7-plus/nodes/s7complus-endpoint.js +825 -0
  43. package/node-red-contrib-s7-plus/nodes/s7complus-explore.js +91 -0
  44. package/node-red-contrib-s7-plus/nodes/s7complus-in.js +255 -0
  45. package/node-red-contrib-s7-plus/nodes/s7complus-out.js +182 -0
  46. package/node-red-contrib-s7-plus/nodes/s7complus.html +1541 -0
  47. package/node-red-contrib-s7-plus/nodes/s7complus.js +10 -0
  48. package/node-red-contrib-s7-plus/package.json +50 -0
  49. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Arrays.db +17 -0
  50. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Binary.db +16 -0
  51. package/node-red-contrib-s7-plus/plc/s7-1500/DB_BitStrings.db +36 -0
  52. package/node-red-contrib-s7-plus/plc/s7-1500/DB_CharacterStrings.db +42 -0
  53. package/node-red-contrib-s7-plus/plc/s7-1500/DB_DateAndTime.db +70 -0
  54. package/node-red-contrib-s7-plus/plc/s7-1500/DB_FloatingPoint.db +42 -0
  55. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Integers.db +72 -0
  56. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Timers.db +40 -0
  57. package/node-red-contrib-s7-plus/scripts/example-flow-shared.js +39 -0
  58. package/node-red-contrib-s7-plus/scripts/generate-read-multiple-flow.js +331 -0
  59. package/node-red-contrib-s7-plus/scripts/generate-rw-test-flow.js +690 -0
  60. package/node-red-contrib-s7-plus/scripts/generate-write-flow.js +476 -0
  61. package/node-red-contrib-s7-plus/scripts/layout-write-flow.js +156 -0
  62. package/node-red-contrib-s7-plus/scripts/run-tests.js +16 -0
  63. package/node-red-flowgobal-change/variable-change.html +425 -0
  64. package/node-red-flowgobal-change/variable-change.js +92 -0
  65. package/node-red-ui_media_viewer/ui_media_viewer.html +87 -0
  66. package/node-red-ui_media_viewer/ui_media_viewer.js +136 -0
  67. package/package.json +12 -0
@@ -0,0 +1,172 @@
1
+ [
2
+ {
3
+ "id": "rw_test_tab",
4
+ "type": "tab",
5
+ "label": "R/W Verification Test",
6
+ "disabled": false,
7
+ "info": "Automated read/write verification for all scalar datatypes in plc/s7-1500.\nPress the \"Run all R/W tests\" inject. The Test Runner writes every constant value of every datatype into the matching *_write tag, reads it back and compares write vs. read-back.\nThe run never aborts on a failure: it completes fully and emits one result per datatype (\"Datatype Result\") plus a final \"Summary\".\nDTL round-trips as Date (ms resolution).\nRegenerate with: node scripts/generate-rw-test-flow.js"
8
+ },
9
+ {
10
+ "id": "rw_test_hdr",
11
+ "type": "comment",
12
+ "z": "rw_test_tab",
13
+ "name": "R/W verification — write, read back and compare every scalar constant",
14
+ "info": "",
15
+ "x": 320,
16
+ "y": 40,
17
+ "wires": []
18
+ },
19
+ {
20
+ "id": "rw_test_start",
21
+ "type": "inject",
22
+ "z": "rw_test_tab",
23
+ "name": "Run all R/W tests",
24
+ "props": [
25
+ {
26
+ "p": "payload"
27
+ }
28
+ ],
29
+ "repeat": "",
30
+ "crontab": "",
31
+ "once": false,
32
+ "onceDelay": 0.1,
33
+ "topic": "",
34
+ "payload": "",
35
+ "payloadType": "date",
36
+ "x": 150,
37
+ "y": 140,
38
+ "wires": [
39
+ [
40
+ "rw_test_runner"
41
+ ]
42
+ ]
43
+ },
44
+ {
45
+ "id": "rw_test_runner",
46
+ "type": "function",
47
+ "z": "rw_test_tab",
48
+ "name": "Test Runner",
49
+ "func": "const TESTS = [\n {\n group: \"Binary\",\n datatype: \"Bool\",\n symbol: \"DB_Binary.Bool_write\",\n cases: [\n { name: \"Bool_False\", value: false },\n { name: \"Bool_True\", value: true }\n ]\n },\n {\n group: \"BitStrings\",\n datatype: \"Byte\",\n symbol: \"DB_BitStrings.Byte_write\",\n cases: [\n { name: \"Byte_Min_0x00\", value: 0 },\n { name: \"Byte_Max_0xFF\", value: 255 },\n { name: \"Byte_0xAB\", value: 171 }\n ]\n },\n {\n group: \"BitStrings\",\n datatype: \"Word\",\n symbol: \"DB_BitStrings.Word_write\",\n cases: [\n { name: \"Word_Min_0x0000\", value: 0 },\n { name: \"Word_Max_0xFFFF\", value: 65535 },\n { name: \"Word_0xCAFE\", value: 51966 }\n ]\n },\n {\n group: \"BitStrings\",\n datatype: \"DWord\",\n symbol: \"DB_BitStrings.DWord_write\",\n cases: [\n { name: \"DWord_Min_0x00000000\", value: 0 },\n { name: \"DWord_Max_0xFFFFFFFF\", value: 4294967295 },\n { name: \"DWord_0xCAFEBABE\", value: 3405691582 }\n ]\n },\n {\n group: \"BitStrings\",\n datatype: \"LWord\",\n symbol: \"DB_BitStrings.LWord_write\",\n cases: [\n { name: \"LWord_Min_0x0000000000000000\", value: 0n },\n { name: \"LWord_Max_0xFFFFFFFFFFFFFFFF\", value: 0xFFFFFFFFFFFFFFFFn },\n { name: \"LWord_0x12345678ABCDEFAB\", value: 0x12345678ABCDEFABn }\n ]\n },\n {\n group: \"CharacterStrings\",\n datatype: \"Char\",\n symbol: \"DB_CharacterStrings.Char_write\",\n cases: [\n { name: \"Char_Space\", value: \" \" },\n { name: \"Char_A\", value: \"A\" },\n { name: \"Char_auml\", value: \"ä\" }\n ]\n },\n {\n group: \"CharacterStrings\",\n datatype: \"String\",\n symbol: \"DB_CharacterStrings.String_write\",\n cases: [\n { name: \"String_Empty\", value: \"\" },\n { name: \"String_Hello_World\", value: \"Hello World\" },\n { name: \"String_Moumltoumlrhead\", value: \"Mötörhead\" },\n { name: \"String_Len4_Test\", value: \"Test\" }\n ]\n },\n {\n group: \"CharacterStrings\",\n datatype: \"WChar\",\n symbol: \"DB_CharacterStrings.WChar_write\",\n cases: [\n { name: \"WChar_Space\", value: \" \" },\n { name: \"WChar_auml\", value: \"ä\" },\n { name: \"WChar_X\", value: \"X\" },\n { name: \"WChar_GreekSigmaBig\", value: \"Ʃ\" }\n ]\n },\n {\n group: \"CharacterStrings\",\n datatype: \"WString\",\n symbol: \"DB_CharacterStrings.WString_write\",\n cases: [\n { name: \"WString_Empty\", value: \"\" },\n { name: \"WString_Moumltoumlrhead\", value: \"Mötörhead\" },\n { name: \"WString_Greek1\", value: \"Test Greek ΣΛΔ end.\" },\n { name: \"WString_Len4_Test\", value: \"Test\" }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"SInt\",\n symbol: \"DB_Integers.SInt_write\",\n cases: [\n { name: \"SInt_Min_n128\", value: -128 },\n { name: \"SInt_Max_127\", value: 127 },\n { name: \"SInt_123\", value: 123 },\n { name: \"SInt_n123\", value: -123 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"Int\",\n symbol: \"DB_Integers.Int_write\",\n cases: [\n { name: \"Int_Min_n32768\", value: -32768 },\n { name: \"Int_Max_32767\", value: 32767 },\n { name: \"Int_12345\", value: 12345 },\n { name: \"Int_n9876\", value: -9876 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"DInt\",\n symbol: \"DB_Integers.DInt_write\",\n cases: [\n { name: \"DInt_Min_n2147483648\", value: -2147483648 },\n { name: \"DInt_Max_2147483647\", value: 2147483647 },\n { name: \"DInt_1234567890\", value: 1234567890 },\n { name: \"DInt_n987654321\", value: -987654321 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"USInt\",\n symbol: \"DB_Integers.USInt_write\",\n cases: [\n { name: \"USint_Min_0\", value: 0 },\n { name: \"USInt_Max_255\", value: 255 },\n { name: \"USInt_123\", value: 123 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"UInt\",\n symbol: \"DB_Integers.UInt_write\",\n cases: [\n { name: \"UInt_Min_0\", value: 0 },\n { name: \"UInt_Max_65535\", value: 65535 },\n { name: \"UInt_12345\", value: 12345 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"UDInt\",\n symbol: \"DB_Integers.UDInt_write\",\n cases: [\n { name: \"UDInt_Min_0\", value: 0 },\n { name: \"UDInt_Max_4294967295\", value: 4294967295 },\n { name: \"UDint_1234567890\", value: 1234567890 }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"LInt\",\n symbol: \"DB_Integers.LInt_write\",\n cases: [\n { name: \"LInt_Min_n9223372036854775808\", value: -9223372036854775808n },\n { name: \"LInt_Max_9223372036854775807\", value: 9223372036854775807n },\n { name: \"LInt_1234567890123456789\", value: 1234567890123456789n },\n { name: \"LInt_n987654321098765432\", value: -987654321098765432n }\n ]\n },\n {\n group: \"Integers\",\n datatype: \"ULInt\",\n symbol: \"DB_Integers.ULInt_write\",\n cases: [\n { name: \"ULInt_Min_0\", value: 0n },\n { name: \"ULInt_Max_18446744073709551615\", value: 18446744073709551615n },\n { name: \"ULInt_12345678901234567890\", value: 12345678901234567890n }\n ]\n },\n {\n group: \"FloatingPoint\",\n datatype: \"Real\",\n symbol: \"DB_FloatingPoint.Real_write\",\n cases: [\n { name: \"Real_MinNeg_n3d402823e38\", value: -3.402823E+38 },\n { name: \"Real_MaxNeg_n1d175495en38\", value: -1.175495E-38 },\n { name: \"Real_MinPos_1d175495en38\", value: 1.175495E-38 },\n { name: \"Real_MaxPos_3d402823e38\", value: 3.402823E+38 },\n { name: \"Real_0d0\", value: 0 },\n { name: \"Real_987d125\", value: 987.125 },\n { name: \"Real_n123d5\", value: -123.5 }\n ]\n },\n {\n group: \"FloatingPoint\",\n datatype: \"LReal\",\n symbol: \"DB_FloatingPoint.LReal_write\",\n cases: [\n { name: \"LReal_MinNeg_n1d7976931348623157e308\", value: -1.7976931348623157E+308 },\n { name: \"LReal_MaxNeg_n2d2250738585072014en308\", value: -2.2250738585072014E-308 },\n { name: \"LReal_MinPos_2d2250738585072014en308\", value: 2.2250738585072014E-308 },\n { name: \"LReal_MaxPos_1d7976931348623157e308\", value: 1.7976931348623157E+308 },\n { name: \"LReal_0d0\", value: 0 },\n { name: \"LReal_987d125\", value: 987.125 },\n { name: \"LReal_n123d5\", value: -123.5 },\n { name: \"LReal_1d23456789012346e18\", value: 1.23456789012346E+18 }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"Date\",\n symbol: \"DB_DateAndTime.Date_write\",\n cases: [\n { name: \"Date_Min_1990_01_01\", value: new Date(1990, 0, 1) },\n { name: \"Date_Max_2169_06_06\", value: new Date(2169, 5, 6) },\n { name: \"Date_2023_10_28\", value: new Date(2023, 9, 28) },\n { name: \"Date_1990_02_03\", value: new Date(1990, 1, 3) }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"TimeOfDay\",\n symbol: \"DB_DateAndTime.Time_Of_Day_write\",\n cases: [\n { name: \"Time_Of_Day_Min_00_00_00d000\", value: 0 },\n { name: \"Time_Of_Day_Max_23_59_59d999\", value: 86399999 },\n { name: \"Time_Of_Day_01_02_03\", value: 3723000 },\n { name: \"Time_Of_Day_23_58_59\", value: 86339000 },\n { name: \"Time_Of_Day_12_13_14d123\", value: 43994123 }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"DateAndTime\",\n symbol: \"DB_DateAndTime.Date_And_Time_write\",\n cases: [\n { name: \"Date_And_Time_Min_1990_01_01_00_00_00\", value: new Date(Date.UTC(1990, 0, 1, 0, 0, 0, 0)) },\n { name: \"Date_And_Time_Max_2089_12_31_23_59_59d999\", value: new Date(Date.UTC(2089, 11, 31, 23, 59, 59, 999)) },\n { name: \"Date_And_Time_2023_10_28_23_58_59d123\", value: new Date(Date.UTC(2023, 9, 28, 23, 58, 59, 123)) }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"LTod\",\n symbol: \"DB_DateAndTime.LTOD_write\",\n cases: [\n { name: \"LTOD_Min_00_00_00d000000000\", value: 0n },\n { name: \"LTOD_Max_23_59_59d999999999\", value: 86399999999999n },\n { name: \"LTOD_10_20_30d400365215\", value: 37230400365215n }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"Ldt\",\n symbol: \"DB_DateAndTime.LDT_write\",\n cases: [\n { name: \"LDT_Min_1970_01_01_00_00_00d000000000\", value: new Date(0) },\n { name: \"LDT_Max_2262_04_11_23_47_16d854775807\", value: new Date(Date.UTC(2262, 3, 11, 23, 47, 16, 854)) },\n { name: \"LDT_2008_10_25_08_12_34d567\", value: new Date(Date.UTC(2008, 9, 25, 8, 12, 34, 567)) }\n ]\n },\n {\n group: \"DateAndTime\",\n datatype: \"Dtl\",\n symbol: \"DB_DateAndTime.DTL_write\",\n cases: [\n { name: \"DTL_Min_1970_01_01_00_00_00d000000000\", value: new Date(0) },\n { name: \"DTL_Max_2262_04_11_23_47_16d854775807\", value: new Date(Date.UTC(2262, 3, 11, 23, 47, 16, 854)) },\n { name: \"DTL_2008_10_25_08_12_34d567\", value: new Date(Date.UTC(2008, 9, 25, 8, 12, 34, 567)) }\n ]\n },\n {\n group: \"Timers\",\n datatype: \"Time\",\n symbol: \"DB_Timers.Time_write\",\n cases: [\n { name: \"Time_Min_n24D_20H_31M_23S_648MS\", value: -2147483648 },\n { name: \"Time_Max_24D_20H_31M_23S_647MS\", value: 2147483647 },\n { name: \"Time_12S_345MS\", value: 12345 },\n { name: \"Time_n20M_34S\", value: -1234000 }\n ]\n },\n {\n group: \"Timers\",\n datatype: \"S5Time\",\n symbol: \"DB_Timers.S5Time_write\",\n cases: [\n { name: \"S5Time_Min_0MS\", value: 0 },\n { name: \"S5Time_Max_2H46M30S\", value: 9990000 },\n { name: \"S5Time_120MS\", value: 120 },\n { name: \"S5Time_12S300MS\", value: 12300 }\n ]\n },\n {\n group: \"Timers\",\n datatype: \"LTime\",\n symbol: \"DB_Timers.LTime_write\",\n cases: [\n { name: \"LTime_Min_n106751D_23H_47M_16S_854MS_775US_808NS\", value: -9223372036854775808n },\n { name: \"LTime_Max_106751D_23H_47M_16S_854MS_775US_807NS\", value: 9223372036854775807n },\n { name: \"LTime_12S345NS\", value: 12000000345n },\n { name: \"LTime_n12H_345US\", value: -43200000345000n },\n { name: \"LTime_23H_456US\", value: 82800000456000n }\n ]\n }\n];\n\n// ---- comparators (read-back value vs. expected) ----\nvar eq = function (e, r) { return r === e; };\nvar bigeq = function (e, r) {\n try { return (typeof r === 'bigint' ? r : BigInt(r)) === (typeof e === 'bigint' ? e : BigInt(e)); }\n catch (_) { return false; }\n};\nvar realeq = function (e, r) { return typeof r === 'number' && r === Math.fround(e); };\nvar lrealeq = function (e, r) { return r === e || (Number.isNaN(e) && Number.isNaN(r)); };\n// Duck-type the Date (getTime) instead of using instanceof, so the comparison\n// is robust even if the read-back Date crosses a vm realm boundary into the\n// function-node sandbox (where instanceof Date can be false).\nvar dateeq = function (e, r) {\n var te = e && typeof e.getTime === 'function' ? e.getTime() : Date.parse(e);\n var tr = r && typeof r.getTime === 'function' ? r.getTime() : Date.parse(r);\n return !isNaN(te) && te === tr;\n};\n\nvar COMPARATORS = {\n Bool: eq,\n Byte: eq, Word: eq, DWord: eq, LWord: bigeq,\n SInt: eq, Int: eq, DInt: eq, USInt: eq, UInt: eq, UDInt: eq,\n LInt: bigeq, ULInt: bigeq,\n Real: realeq, LReal: lrealeq,\n Char: eq, WChar: eq, String: eq, WString: eq,\n Date: dateeq, DateAndTime: dateeq, Ldt: dateeq, Dtl: dateeq,\n TimeOfDay: eq, Time: eq, S5Time: eq,\n LTod: bigeq, LTime: bigeq\n};\n\nfunction show(v) {\n if (typeof v === 'bigint') return v.toString();\n if (v && typeof v.toISOString === 'function') return v.toISOString();\n return v;\n}\n\nfunction tagResult(payload, symbol) {\n if (!payload || typeof payload !== 'object') return null;\n if (payload[symbol]) return payload[symbol];\n var keys = Object.keys(payload);\n return keys.length ? payload[keys[0]] : null;\n}\n\nfunction newAcc() { return { passed: 0, failed: 0, cases: [] }; }\n\nfunction buildWriteMsg(S) {\n var t = TESTS[S.di];\n var c = t.cases[S.ci];\n return {\n _t: { phase: 'write', di: S.di, ci: S.ci },\n symbols: [t.symbol],\n datatype: t.datatype,\n payload: c.value\n };\n}\n\nfunction buildReadMsg(S) {\n var t = TESTS[S.di];\n return {\n _t: { phase: 'read', di: S.di, ci: S.ci },\n symbols: [t.symbol],\n payload: ''\n };\n}\n\nfunction record(S, t, c, ok, wrote, read, error) {\n S.cur.cases.push({\n name: c.name,\n ok: ok,\n wrote: show(wrote),\n read: read === null || read === undefined ? null : show(read),\n error: error || ''\n });\n if (ok) { S.cur.passed++; S.totalPassed++; } else { S.cur.failed++; S.totalFailed++; }\n S.totalCases++;\n}\n\n// Advance to the next case; emit a per-datatype result when a datatype is\n// finished and a final summary when all datatypes are done.\nfunction advance(S) {\n var t = TESTS[S.di];\n S.ci++;\n if (S.ci < t.cases.length) {\n context.set('state', S);\n return [buildWriteMsg(S), null, null, null];\n }\n\n var result = {\n topic: t.group + '/' + t.datatype,\n payload: {\n group: t.group,\n datatype: t.datatype,\n symbol: t.symbol,\n total: t.cases.length,\n passed: S.cur.passed,\n failed: S.cur.failed,\n cases: S.cur.cases\n }\n };\n S.results.push({ group: t.group, datatype: t.datatype, total: t.cases.length, passed: S.cur.passed, failed: S.cur.failed });\n node.status({ fill: S.cur.failed ? 'yellow' : 'green', shape: 'dot', text: t.datatype + ': ' + S.cur.passed + '/' + t.cases.length + ' ok' });\n\n S.di++;\n S.ci = 0;\n if (S.di < TESTS.length) {\n S.cur = newAcc();\n context.set('state', S);\n return [buildWriteMsg(S), null, result, null];\n }\n\n S.running = false;\n context.set('state', S);\n var summary = {\n topic: 'SUMMARY',\n payload: {\n totalDatatypes: TESTS.length,\n totalCases: S.totalCases,\n passed: S.totalPassed,\n failed: S.totalFailed,\n failures: S.results.filter(function (r) { return r.failed > 0; })\n }\n };\n node.status({ fill: S.totalFailed ? 'red' : 'green', shape: 'dot', text: 'done: ' + S.totalPassed + '/' + S.totalCases + ' ok' });\n return [null, null, result, summary];\n}\n\nvar S = context.get('state');\n\n// Start trigger (inject): no _t marker.\nif (!msg || !msg._t) {\n if (S && S.running) {\n node.warn('R/W test already running, ignoring start trigger');\n return null;\n }\n S = {\n running: true,\n di: 0,\n ci: 0,\n cur: newAcc(),\n results: [],\n totalCases: 0,\n totalPassed: 0,\n totalFailed: 0\n };\n context.set('state', S);\n node.status({ fill: 'blue', shape: 'dot', text: 'running...' });\n return [buildWriteMsg(S), null, null, null];\n}\n\n// Stray feedback (e.g. after a completed run) is ignored.\nif (!S || !S.running) {\n return null;\n}\n\nvar t = TESTS[S.di];\nvar c = t.cases[S.ci];\nvar phase = msg._t.phase;\nvar errText = msg.error && msg.error.message ? msg.error.message : null;\n\nif (phase === 'write') {\n var wr = tagResult(msg.payload, t.symbol);\n if (errText || !wr || wr.status !== 'ok') {\n record(S, t, c, false, c.value, null, errText || (wr && wr.error) || 'write failed');\n return advance(S);\n }\n // write ok -> read the same tag back\n return [null, buildReadMsg(S), null, null];\n}\n\nif (phase === 'read') {\n var rd = tagResult(msg.payload, t.symbol);\n if (errText || !rd || rd.status !== 'ok') {\n record(S, t, c, false, c.value, null, errText || (rd && rd.error) || 'read failed');\n return advance(S);\n }\n var cmp = COMPARATORS[t.datatype] || eq;\n var ok = cmp(c.value, rd.value);\n var note = ok ? '' : 'value mismatch';\n record(S, t, c, ok, c.value, rd.value, note);\n return advance(S);\n}\n\nreturn null;\n",
50
+ "outputs": 4,
51
+ "timeout": 0,
52
+ "noerr": 0,
53
+ "initialize": "",
54
+ "finalize": "",
55
+ "libs": [],
56
+ "x": 410,
57
+ "y": 140,
58
+ "wires": [
59
+ [
60
+ "rw_test_write"
61
+ ],
62
+ [
63
+ "rw_test_read"
64
+ ],
65
+ [
66
+ "rw_test_dbg_result"
67
+ ],
68
+ [
69
+ "rw_test_dbg_summary"
70
+ ]
71
+ ]
72
+ },
73
+ {
74
+ "id": "rw_test_write",
75
+ "type": "s7-plus write",
76
+ "z": "rw_test_tab",
77
+ "name": "write *_write",
78
+ "endpoint": "example_plc_ep",
79
+ "symbols": [],
80
+ "outputFormat": "object",
81
+ "x": 690,
82
+ "y": 100,
83
+ "wires": [
84
+ [
85
+ "rw_test_runner"
86
+ ]
87
+ ]
88
+ },
89
+ {
90
+ "id": "rw_test_read",
91
+ "type": "s7-plus read",
92
+ "z": "rw_test_tab",
93
+ "name": "read *_write",
94
+ "endpoint": "example_plc_ep",
95
+ "symbols": [],
96
+ "outputFormat": "object",
97
+ "x": 690,
98
+ "y": 180,
99
+ "wires": [
100
+ [
101
+ "rw_test_runner"
102
+ ]
103
+ ]
104
+ },
105
+ {
106
+ "id": "rw_test_catch",
107
+ "type": "catch",
108
+ "z": "rw_test_tab",
109
+ "name": "write/read errors",
110
+ "scope": [
111
+ "rw_test_write",
112
+ "rw_test_read"
113
+ ],
114
+ "uncaught": false,
115
+ "x": 410,
116
+ "y": 260,
117
+ "wires": [
118
+ [
119
+ "rw_test_runner"
120
+ ]
121
+ ]
122
+ },
123
+ {
124
+ "id": "rw_test_dbg_result",
125
+ "type": "debug",
126
+ "z": "rw_test_tab",
127
+ "name": "Datatype Result",
128
+ "active": true,
129
+ "tosidebar": true,
130
+ "console": false,
131
+ "tostatus": false,
132
+ "complete": "payload",
133
+ "targetType": "msg",
134
+ "statusVal": "",
135
+ "statusType": "auto",
136
+ "x": 720,
137
+ "y": 280,
138
+ "wires": []
139
+ },
140
+ {
141
+ "id": "rw_test_dbg_summary",
142
+ "type": "debug",
143
+ "z": "rw_test_tab",
144
+ "name": "Summary",
145
+ "active": true,
146
+ "tosidebar": true,
147
+ "console": false,
148
+ "tostatus": false,
149
+ "complete": "payload",
150
+ "targetType": "msg",
151
+ "statusVal": "",
152
+ "statusType": "auto",
153
+ "x": 700,
154
+ "y": 340,
155
+ "wires": []
156
+ },
157
+ {
158
+ "id": "example_plc_ep",
159
+ "type": "s7-plus endpoint",
160
+ "name": "PLC",
161
+ "address": "192.168.0.1",
162
+ "timeout": 10000
163
+ },
164
+ {
165
+ "id": "69768f9f5a51add5",
166
+ "type": "global-config",
167
+ "env": [],
168
+ "modules": {
169
+ "node-red-contrib-s7-plus": "0.0.1"
170
+ }
171
+ }
172
+ ]