From 4c54234e153698ffe8c05d41d4c565c1282584e4 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Thu, 27 Jun 2024 12:46:56 +0100 Subject: [PATCH 01/14] Create a test driver This accepts addresses specifying fixed functions. We need to supply a `version` in the address config. --- acs-edge/Makefile | 1 + acs-edge/docs/TODO.md | 2 + acs-edge/lib/devices/driver.ts | 1 + edge-test/.gitignore | 1 + edge-test/bin/driver.js | 120 +++++ edge-test/package-lock.json | 797 +++++++++++++++++++++++++++++++++ edge-test/package.json | 17 + 7 files changed, 939 insertions(+) create mode 100644 edge-test/.gitignore create mode 100644 edge-test/bin/driver.js create mode 100644 edge-test/package-lock.json create mode 100644 edge-test/package.json diff --git a/acs-edge/Makefile b/acs-edge/Makefile index a1e06c11..979616cf 100644 --- a/acs-edge/Makefile +++ b/acs-edge/Makefile @@ -7,6 +7,7 @@ repo?=acs-edge include ${mk}/acs.js.mk local.build: + npm install npx tsc --project tsconfig.json local.run: local.build diff --git a/acs-edge/docs/TODO.md b/acs-edge/docs/TODO.md index 8c5844e6..759b762a 100644 --- a/acs-edge/docs/TODO.md +++ b/acs-edge/docs/TODO.md @@ -35,3 +35,5 @@ * Supply data topic names to a Device in return for addresses when it subscribes to the connection manifold. * Poll the manifold using connection/datatopic pairs. + * Possibly the Device should always assume we are using a 'smart' + driver, and polling should be handled by the manifold? diff --git a/acs-edge/lib/devices/driver.ts b/acs-edge/lib/devices/driver.ts index 9a02e931..5ef943b4 100644 --- a/acs-edge/lib/devices/driver.ts +++ b/acs-edge/lib/devices/driver.ts @@ -175,6 +175,7 @@ export class DriverConnection extends DeviceConnection { #send_addrs () { const addrs = { + version: 1, addrs: Object.fromEntries(this.addrs), groups: Object.fromEntries( [...this.groups.entries()] diff --git a/edge-test/.gitignore b/edge-test/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/edge-test/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/edge-test/bin/driver.js b/edge-test/bin/driver.js new file mode 100644 index 00000000..0da40e99 --- /dev/null +++ b/edge-test/bin/driver.js @@ -0,0 +1,120 @@ +/* AMRC Connectivity Stack + * Modbus Edge Agent driver + * Copyright 2024 AMRC + */ + +import asyncjs from "async"; +import MQTT from "mqtt"; + +const id = process.env.EDGE_USERNAME; + +const topic = (msg, data) => `fpEdge1/${id}/${msg}` + (data ? `/${data}` : ""); + +const mqtt = MQTT.connect({ + url: process.env.EDGE_MQTT, + clientId: id, + username: id, + password: process.env.EDGE_PASSWORD, + will: { + topic: topic("status"), + payload: "DOWN", + }, + manualConnect: true, + resubscribe: false, +}); + +let status = "DOWN"; +function setStatus (st) { + status = st; + if (mqtt.connected) + mqtt.publish(topic("status"), status); +} +let conf; +let addrs; + +const funcs = { + const: (p, a) => t => a, + sin: (p, a) => t => a * Math.sin(2 * Math.PI * (t / p)), + saw: (p, a) => t => (a / p) * (t % p), +} + +function parseAddr (addr) { + const parts = addr.split(":"); + if (parts.length != 3) return; + + const func = funcs[parts[0]]; + if (!func) return; + const period = Number.parseFloat(parts[1]); + if (Number.isNaN(period)) return; + const amplitude = Number.parseFloat(parts[2]); + if (Number.isNaN(amplitude)) return; + + return func(period, amplitude); +} + +function setAddrs (pkt) { + if (pkt.version != 1) { + console.log("Bad addr config version: %s", pkt.version); + return false; + } + + const parsed = Object.entries(pkt.addrs) + .map(([t, a]) => [t, parseAddr(a)]); + if (parsed.some(([, f]) => !f)) { + console.log("BAD ADDRS: %O", pkt.addrs); + return false; + } + + addrs = new Map( + parsed.map(([t, f]) => [t, { topic: t, func: f }])); + console.log("Set addrs: %O", addrs); + return true; +} + +const polling = asyncjs.queue(async addr => { + const val = addr.func(performance.now()); + const buf = Buffer.alloc(8); + const top = topic("data", addr.topic); + + buf.writeDoubleBE(val); + await mqtt.publishAsync(top, buf); +}); + +mqtt.on("connect", async () => { + await mqtt.subscribeAsync( + "active conf addr poll" + .split(" ") + .map(t => topic(t))); + setStatus("READY"); +}); +mqtt.on("message", (t, p) => { + const [, , msg, data] = t.split("/"); + switch (msg) { + case "active": + if (p.toString() == "ONLINE") + setStatus("READY"); + break; + case "conf": + conf = JSON.parse(p.toString()); + console.log("CONF: %O", conf); + setStatus("UP"); + break; + case "addr": + const a = JSON.parse(p.toString()); + if (!setAddrs(a)) + setStatus("CONF"); + break; + case "poll": + const poll = p.toString().split("\n"); + console.log("POLL %O", poll); + if (addrs) + poll.map(t => addrs.get(t)) + .filter(a => a) + .forEach(a => polling.push(a)); + else + console.log("Can't poll, no addrs!"); + break; + } +}); + +mqtt.connect(); diff --git a/edge-test/package-lock.json b/edge-test/package-lock.json new file mode 100644 index 00000000..b553adfa --- /dev/null +++ b/edge-test/package-lock.json @@ -0,0 +1,797 @@ +{ + "name": "edge-modbus", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "edge-modbus", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "async": "^3.2.5", + "mqtt": "^5.7.2" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@types/node": { + "version": "20.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", + "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", + "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.12.tgz", + "integrity": "sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w==", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mqtt": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.7.2.tgz", + "integrity": "sha512-b5xIA9J/K1LTubSWKaNYYLxYIusQdip6o9/8bRWad2TelRr8xLifjQt+SnamDAwMp3O6NdvR9E8ae7VMuN02kg==", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@types/node": { + "version": "20.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", + "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/readable-stream": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", + "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "requires": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "requires": { + "@types/node": "*" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.12.tgz", + "integrity": "sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w==", + "requires": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "requires": { + "ms": "2.1.2" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "requires": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + } + }, + "help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" + }, + "lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==" + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mqtt": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.7.2.tgz", + "integrity": "sha512-b5xIA9J/K1LTubSWKaNYYLxYIusQdip6o9/8bRWad2TelRr8xLifjQt+SnamDAwMp3O6NdvR9E8ae7VMuN02kg==", + "requires": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + } + }, + "mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "requires": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "requires": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, + "rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "requires": {} + } + } +} diff --git a/edge-test/package.json b/edge-test/package.json new file mode 100644 index 00000000..c03bd6db --- /dev/null +++ b/edge-test/package.json @@ -0,0 +1,17 @@ +{ + "name": "edge-modbus", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "async": "^3.2.5", + "mqtt": "^5.7.2" + } +} From 4a4351fc1ebd23f716a318dadfea6b9d52a41338 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Thu, 11 Jul 2024 10:41:00 +0100 Subject: [PATCH 02/14] Support variable data packing --- edge-test/bin/driver.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/edge-test/bin/driver.js b/edge-test/bin/driver.js index 0da40e99..c431e1ce 100644 --- a/edge-test/bin/driver.js +++ b/edge-test/bin/driver.js @@ -36,11 +36,17 @@ const funcs = { const: (p, a) => t => a, sin: (p, a) => t => a * Math.sin(2 * Math.PI * (t / p)), saw: (p, a) => t => (a / p) * (t % p), -} +}; +const packing = { + bd: [8, (b, v) => b.writeDoubleBE(v)], + ld: [8, (b, v) => b.writeDoubleLE(v)], + bf: [4, (b, v) => b.writeFloatBE(v)], + lf: [4, (b, v) => b.writeFloatLE(v)], +}; function parseAddr (addr) { const parts = addr.split(":"); - if (parts.length != 3) return; + if (parts.length != 4) return; const func = funcs[parts[0]]; if (!func) return; @@ -48,8 +54,10 @@ function parseAddr (addr) { if (Number.isNaN(period)) return; const amplitude = Number.parseFloat(parts[2]); if (Number.isNaN(amplitude)) return; + const pack = packing[parts[3]]; + if (!pack) return; - return func(period, amplitude); + return [func(period, amplitude), ...pack]; } function setAddrs (pkt) { @@ -66,17 +74,18 @@ function setAddrs (pkt) { } addrs = new Map( - parsed.map(([t, f]) => [t, { topic: t, func: f }])); + parsed.map(([t, [f, s, p]]) => + [t, { topic: t, func: f, size: s, pack: p }])); console.log("Set addrs: %O", addrs); return true; } const polling = asyncjs.queue(async addr => { const val = addr.func(performance.now()); - const buf = Buffer.alloc(8); + const buf = Buffer.alloc(addr.size); const top = topic("data", addr.topic); - buf.writeDoubleBE(val); + addr.pack(buf, val); await mqtt.publishAsync(top, buf); }); From 7b5e4d53b22a4233f43c6bd2766b2458f6948741 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Thu, 11 Jul 2024 14:32:09 +0100 Subject: [PATCH 03/14] Create a Modbus driver This doesn't handle errors entirely properly yet, but it works. --- edge-modbus/.gitignore | 1 + edge-modbus/bin/driver.js | 152 ++++ edge-modbus/package-lock.json | 1254 +++++++++++++++++++++++++++++++++ edge-modbus/package.json | 21 + 4 files changed, 1428 insertions(+) create mode 100644 edge-modbus/.gitignore create mode 100644 edge-modbus/bin/driver.js create mode 100644 edge-modbus/package-lock.json create mode 100644 edge-modbus/package.json diff --git a/edge-modbus/.gitignore b/edge-modbus/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/edge-modbus/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/edge-modbus/bin/driver.js b/edge-modbus/bin/driver.js new file mode 100644 index 00000000..b49936f5 --- /dev/null +++ b/edge-modbus/bin/driver.js @@ -0,0 +1,152 @@ +/* AMRC Connectivity Stack + * Modbus Edge Agent driver + * Copyright 2024 AMRC + */ + +import asyncjs from "async"; +import MQTT from "mqtt"; +import ModbusRTU from "modbus-serial"; + +const id = process.env.EDGE_USERNAME; + +const topic = (msg, data) => `fpEdge1/${id}/${msg}` + (data ? `/${data}` : ""); + +const mqtt = MQTT.connect({ + url: process.env.EDGE_MQTT, + clientId: id, + username: id, + password: process.env.EDGE_PASSWORD, + will: { + topic: topic("status"), + payload: "DOWN", + }, + manualConnect: true, + resubscribe: false, +}); + +let status = "DOWN"; +function setStatus (st) { + status = st; + if (mqtt.connected) + mqtt.publish(topic("status"), status); +} + +let client; +async function setConf (conf) { + if (conf.protocol != "tcp") + return "CONF"; + + client = new ModbusRTU(); + return client.connectTCP(conf.host, { port: conf.port }) + .then(() => "UP", () => "CONN"); +} + +let addrs; + +const funcs = { + input: { + read: (c, a, l) => c.readInputRegisters(a, l), + }, + holding: { + read: (c, a, l) => c.readHoldingRegisters(a, l), + }, + coil: { + read: (c, a, l) => c.readCoils(a, l), + }, + discrete: { + read: (c, a, l) => c.readDiscreteInputs(a, l), + }, +}; + +function parseAddr (spec) { + const parts = spec.split(","); + if (parts.length != 4) return; + + const id = Number.parseInt(parts[0]); + if (Number.isNaN(id) || id < 0) return; + const func = funcs[parts[1]]; + if (!func) return; + const addr = Number.parseInt(parts[2]); + if (Number.isNaN(addr) || addr < 0) return; + const len = Number.parseInt(parts[3]); + if (Number.isNaN(len) || len < 1) return; + + return { id, func, addr, len }; +} + +const polling = asyncjs.queue(async ({ data, addr }) => { + console.log("READ %O", addr); + if (!client) { + console.log("Can't poll, no client!"); + return; + } + if (!client.isOpen) + await new Promise((r, j) => + client.open(e => e ? j(e) : r())); + + client.setID(addr.id); + const val = await addr.func.read(client, addr.addr, addr.len); + console.log("DATA %O", val); + + await mqtt.publishAsync(data, val.buffer); +}); + +function setAddrs (pkt) { + if (pkt.version != 1) { + console.log("Bad addr config version: %s", pkt.version); + return false; + } + + const parsed = Object.entries(pkt.addrs) + .map(([t, a]) => [t, parseAddr(a)]); + if (parsed.some(([, f]) => !f)) { + console.log("BAD ADDRS: %O", pkt.addrs); + return false; + } + + addrs = new Map(parsed); + console.log("Set addrs: %O", addrs); + return true; +} + +mqtt.on("connect", async () => { + await mqtt.subscribeAsync( + "active conf addr poll" + .split(" ") + .map(t => topic(t))); + setStatus("READY"); +}); +mqtt.on("message", async (t, p) => { + const [, , msg, data] = t.split("/"); + switch (msg) { + case "active": + if (p.toString() == "ONLINE") + setStatus("READY"); + break; + case "conf": + const conf = JSON.parse(p.toString()); + console.log("CONF: %O", conf); + setStatus(await setConf(conf)); + break; + case "addr": + const a = JSON.parse(p.toString()); + if (!setAddrs(a)) + setStatus("CONF"); + break; + case "poll": + const poll = p.toString().split("\n"); + console.log("POLL %s %O", polling.length(), poll); + if (addrs) + poll.map(t => ({ + data: topic("data", t), + addr: addrs.get(t), + })) + .filter(({ addr }) => addr) + .forEach(a => polling.push(a)); + else + console.log("Can't poll, no addrs!"); + break; + } +}); + +mqtt.connect(); diff --git a/edge-modbus/package-lock.json b/edge-modbus/package-lock.json new file mode 100644 index 00000000..a0734d48 --- /dev/null +++ b/edge-modbus/package-lock.json @@ -0,0 +1,1254 @@ +{ + "name": "amrc-connectivity-stack", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "amrc-connectivity-stack", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "async": "^3.2.5", + "modbus-serial": "^8.0.17", + "mqtt": "^5.8.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-12.0.1.tgz", + "integrity": "sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==", + "hasInstallScript": true, + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "11.0.0", + "debug": "4.3.4", + "node-addon-api": "7.0.0", + "node-gyp-build": "4.6.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", + "integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", + "integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", + "dependencies": { + "@serialport/parser-delimiter": "11.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-12.0.0.tgz", + "integrity": "sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-12.0.0.tgz", + "integrity": "sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-12.0.0.tgz", + "integrity": "sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-12.0.0.tgz", + "integrity": "sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-12.0.0.tgz", + "integrity": "sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-12.0.0.tgz", + "integrity": "sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-12.0.0.tgz", + "integrity": "sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-12.0.0.tgz", + "integrity": "sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-12.0.0.tgz", + "integrity": "sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", + "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/modbus-serial": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/modbus-serial/-/modbus-serial-8.0.17.tgz", + "integrity": "sha512-1lCNpCY72wTgGnnBzv9O3ZfbKeHl9zZTgVuMgp2mEqFErEZC7p3I0CGpzfQ4XPHT0Aqz+qXhpkGew8WK57SwvQ==", + "dependencies": { + "debug": "^4.3.1", + "serialport": "^12.0.0" + } + }, + "node_modules/mqtt": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.8.0.tgz", + "integrity": "sha512-/+H04mv6goy6K5gHMNH3uS0icBzXapS+4uUf4yZyQWXi72APPZNb81bQhvkm99poEQettXVT8XETB0mPxl5Wjg==", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-addon-api": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/serialport": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-12.0.0.tgz", + "integrity": "sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "12.0.1", + "@serialport/parser-byte-length": "12.0.0", + "@serialport/parser-cctalk": "12.0.0", + "@serialport/parser-delimiter": "12.0.0", + "@serialport/parser-inter-byte-timeout": "12.0.0", + "@serialport/parser-packet-length": "12.0.0", + "@serialport/parser-readline": "12.0.0", + "@serialport/parser-ready": "12.0.0", + "@serialport/parser-regex": "12.0.0", + "@serialport/parser-slip-encoder": "12.0.0", + "@serialport/parser-spacepacket": "12.0.0", + "@serialport/stream": "12.0.0", + "debug": "4.3.4" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "requires": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + } + }, + "@serialport/bindings-cpp": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-12.0.1.tgz", + "integrity": "sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==", + "requires": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "11.0.0", + "debug": "4.3.4", + "node-addon-api": "7.0.0", + "node-gyp-build": "4.6.0" + }, + "dependencies": { + "@serialport/parser-delimiter": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", + "integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==" + }, + "@serialport/parser-readline": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", + "integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", + "requires": { + "@serialport/parser-delimiter": "11.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==" + }, + "@serialport/parser-byte-length": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-12.0.0.tgz", + "integrity": "sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==" + }, + "@serialport/parser-cctalk": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-12.0.0.tgz", + "integrity": "sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==" + }, + "@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==" + }, + "@serialport/parser-inter-byte-timeout": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-12.0.0.tgz", + "integrity": "sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==" + }, + "@serialport/parser-packet-length": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-12.0.0.tgz", + "integrity": "sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==" + }, + "@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "requires": { + "@serialport/parser-delimiter": "12.0.0" + } + }, + "@serialport/parser-ready": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-12.0.0.tgz", + "integrity": "sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==" + }, + "@serialport/parser-regex": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-12.0.0.tgz", + "integrity": "sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==" + }, + "@serialport/parser-slip-encoder": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-12.0.0.tgz", + "integrity": "sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==" + }, + "@serialport/parser-spacepacket": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-12.0.0.tgz", + "integrity": "sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==" + }, + "@serialport/stream": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-12.0.0.tgz", + "integrity": "sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==", + "requires": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/readable-stream": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "requires": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "requires": { + "@types/node": "*" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", + "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", + "requires": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "requires": { + "ms": "2.1.2" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "requires": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + } + }, + "help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "modbus-serial": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/modbus-serial/-/modbus-serial-8.0.17.tgz", + "integrity": "sha512-1lCNpCY72wTgGnnBzv9O3ZfbKeHl9zZTgVuMgp2mEqFErEZC7p3I0CGpzfQ4XPHT0Aqz+qXhpkGew8WK57SwvQ==", + "requires": { + "debug": "^4.3.1", + "serialport": "^12.0.0" + } + }, + "mqtt": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.8.0.tgz", + "integrity": "sha512-/+H04mv6goy6K5gHMNH3uS0icBzXapS+4uUf4yZyQWXi72APPZNb81bQhvkm99poEQettXVT8XETB0mPxl5Wjg==", + "requires": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + } + }, + "mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "requires": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-addon-api": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" + }, + "node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" + }, + "number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "requires": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, + "rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "serialport": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-12.0.0.tgz", + "integrity": "sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==", + "requires": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "12.0.1", + "@serialport/parser-byte-length": "12.0.0", + "@serialport/parser-cctalk": "12.0.0", + "@serialport/parser-delimiter": "12.0.0", + "@serialport/parser-inter-byte-timeout": "12.0.0", + "@serialport/parser-packet-length": "12.0.0", + "@serialport/parser-readline": "12.0.0", + "@serialport/parser-ready": "12.0.0", + "@serialport/parser-regex": "12.0.0", + "@serialport/parser-slip-encoder": "12.0.0", + "@serialport/parser-spacepacket": "12.0.0", + "@serialport/stream": "12.0.0", + "debug": "4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} + } + } +} diff --git a/edge-modbus/package.json b/edge-modbus/package.json new file mode 100644 index 00000000..5692f866 --- /dev/null +++ b/edge-modbus/package.json @@ -0,0 +1,21 @@ +{ + "name": "amrc-connectivity-stack", + "version": "1.0.0", + "description": "The AMRC Connectivity Stack (ACS) is a comprehensive collection of open-source services developed by the AMRC that represents a complete end-to-end implementation of the [Factory+](https://factoryplus.app.amrc.co.uk) framework. It is distributed as a Kubernetes Helm chart an can be deployed onto any Kubernetes cluster.", + "main": "index.js", + "type": "module", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "async": "^3.2.5", + "modbus-serial": "^8.0.17", + "mqtt": "^5.8.0" + } +} From e50fae6aa3f9b289835ee9f2b51626e45ff82f85 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Thu, 11 Jul 2024 14:48:43 +0100 Subject: [PATCH 04/14] Remove some logging --- acs-edge/lib/device.ts | 7 ------- acs-edge/lib/devices/driver.ts | 4 ++-- acs-edge/lib/driverBroker.ts | 6 +++--- acs-edge/lib/helpers/typeHandler.ts | 4 ---- acs-edge/lib/translator.ts | 10 +++++----- 5 files changed, 10 insertions(+), 21 deletions(-) diff --git a/acs-edge/lib/device.ts b/acs-edge/lib/device.ts index bd9d74cd..4b4e60da 100644 --- a/acs-edge/lib/device.ts +++ b/acs-edge/lib/device.ts @@ -286,19 +286,16 @@ export class Device { } _handleData(obj: { [p: string]: any }, parseVals: boolean) { - logf("_handleData %s (%s) %O", this._name, parseVals, obj); // Array to keep track of values that changed let changedMetrics: sparkplugMetric[] = []; // Iterate through each key in obj for (let addr in obj) { // Get all payload paths registered for this address const paths = this._metrics.getPathsForAddr(addr); - logf("paths for %s: %s", addr, paths); // Iterate through each path paths.forEach((path) => { // Get the complete metric according to its address and path const metric = this._metrics.getByAddrPath(addr, path); - logf("metric for %s:%s: %O", addr, path, metric); // If the metric can be read i.e. GET method if (typeof metric.properties !== "undefined" && (metric.properties.method.value as string).search( /^GET/g) > -1) { @@ -313,8 +310,6 @@ export class Device { this._delimiter ) : obj[addr]; - logf("parsed new val: %O", newVal); - // Test if the value is a bigint and convert it to a Long. This is a hack to ensure that the // Tahu library works - it only accepts Longs, not bigints. if (typeof newVal === "bigint") { @@ -334,8 +329,6 @@ export class Device { this._payloadFormat, this._delimiter ); - logf("updating metric %s:%s ts %s val %O", - addr, path, timestamp, newVal); // Update the metric value and push it to the array of changed metrics changedMetrics.push({...(this._metrics.setValueByAddrPath(addr, diff --git a/acs-edge/lib/devices/driver.ts b/acs-edge/lib/devices/driver.ts index 5ef943b4..0fe8f125 100644 --- a/acs-edge/lib/devices/driver.ts +++ b/acs-edge/lib/devices/driver.ts @@ -135,7 +135,7 @@ export class DriverConnection extends DeviceConnection { const { id, msg, data, payload } = message; if (id != this.id) return; - log(util.format("DRIVER message: %s %s", id, msg)); + //log(util.format("DRIVER message: %s %s", id, msg)); switch (msg) { case "status": return this.#msg_status(payload.toString()); case "data": return this.#msg_data(data, payload); @@ -193,7 +193,7 @@ export class DriverConnection extends DeviceConnection { #msg_data (data: string, payload: Buffer) { const addr = this.addrs.get(data); - log(`Driver [${this.id}]: data ${data} ${addr}`); + //log(`Driver [${this.id}]: data ${data} ${addr}`); if (addr) this.emit("data", { [addr]: payload }); } diff --git a/acs-edge/lib/driverBroker.ts b/acs-edge/lib/driverBroker.ts index fb3b0219..1276b763 100644 --- a/acs-edge/lib/driverBroker.ts +++ b/acs-edge/lib/driverBroker.ts @@ -106,7 +106,7 @@ export class DriverBroker extends EventEmitter { const { id } = client; const { topic } = packet; - log("PUBLISH: %s %s", id, topic); + //log("PUBLISH: %s %s", id, topic); if (packet.retain) return callback(new Error("Retained PUBLISH forbidden")); if (!this.acl.get(id)!.publish.test(topic)) @@ -125,7 +125,7 @@ export class DriverBroker extends EventEmitter { } message (topic, payload) { - log("PACKET: %s %o", topic, payload); + //log("PACKET: %s %o", topic, payload); const match = topic.match(topicrx); if (!match) { @@ -142,7 +142,7 @@ export class DriverBroker extends EventEmitter { const topic = `${prefix}/${id}/${msg}` + (data ? `/${data}` : ""); - log("Publishing %s: %O", topic, packet); + //log("Publishing %s: %O", topic, packet); return new Promise((resolve, reject) => this.broker.publish({ cmd: "publish", diff --git a/acs-edge/lib/helpers/typeHandler.ts b/acs-edge/lib/helpers/typeHandler.ts index 9fe48d7f..18dc10b8 100644 --- a/acs-edge/lib/helpers/typeHandler.ts +++ b/acs-edge/lib/helpers/typeHandler.ts @@ -356,8 +356,6 @@ export class Metrics { export function parseValueFromPayload(msg: any, metric: sparkplugMetric, payloadFormat: serialisationType | string, delimiter?: string) { let payload: any; const path = (typeof metric.properties !== "undefined" && typeof metric.properties.path !== "undefined" ? metric.properties.path.value as string : ""); - logf("parseValueFP: path [%s], fmt [%s], type [%s], msg [%o]", - path, payloadFormat, metric.type, msg); switch (payloadFormat) { case serialisationType.delimited: // Handle no delimiter @@ -598,8 +596,6 @@ export function typeLens(type: string): number { */ export function parseValFromBuffer(type: sparkplugDataType, endianness: byteOrder, byteAddr: number, buf: Buffer, bit?: number): any { - logf("parseValFB: addr %s, bit %s, end %s, typ %s, buf: %O", - byteAddr, bit, endianness, type, buf); switch (type) { case sparkplugDataType.boolean: if (bit != null) { diff --git a/acs-edge/lib/translator.ts b/acs-edge/lib/translator.ts index 32da38a8..ed703602 100644 --- a/acs-edge/lib/translator.ts +++ b/acs-edge/lib/translator.ts @@ -94,8 +94,8 @@ export class Translator extends EventEmitter { log(`Created Sparkplug node "${ids.sparkplug!}".`); log("Starting driver broker..."); - this.broker.on("message", msg => - log(util.format("Driver message: %O", msg))); + //this.broker.on("message", msg => + // log(util.format("Driver message: %O", msg))); await this.broker.start(); // Create a new device connection for each type listed in config file @@ -212,8 +212,8 @@ export class Translator extends EventEmitter { // What to do when the device connection has new data from a device newConn.on('data', (obj: { [index: string]: any }, parseVals = true) => { - log(util.format("Received data for %s: (%s) %O", - connection.name, parseVals, obj)); + //log(util.format("Received data for %s: (%s) %O", + // connection.name, parseVals, obj)); connection.devices?.forEach((devConf: deviceOptions) => { this.devices[devConf.deviceId]?._handleData(obj, parseVals); }) @@ -375,4 +375,4 @@ Trying again in ${interval} seconds...`); await timers.setTimeout(interval * 1000); } } -} \ No newline at end of file +} From 547d870145b7e780b89fdd2f85feae18b682b41c Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Fri, 12 Jul 2024 08:55:01 +0100 Subject: [PATCH 05/14] Handle driver connection status properly We need to emit "open" and "close" on the Driver connection, but only if this is a change of status. --- acs-edge/lib/devices/driver.ts | 14 +++++++++++--- edge-modbus/bin/driver.js | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/acs-edge/lib/devices/driver.ts b/acs-edge/lib/devices/driver.ts index 0fe8f125..944ae821 100644 --- a/acs-edge/lib/devices/driver.ts +++ b/acs-edge/lib/devices/driver.ts @@ -153,8 +153,10 @@ export class DriverConnection extends DeviceConnection { } #msg_status (status: string) { + const ost = this.status; this.status = status; - log(`DRIVER [${this.id}]: status ${status}`); + log(`DRIVER [${this.id}]: status ${ost} -> ${status}`); + switch (status) { case "READY": this.broker.publish({ @@ -165,10 +167,16 @@ export class DriverConnection extends DeviceConnection { this.#send_addrs(); break; case "UP": - this.emit("open"); + if (ost != "UP") + this.emit("open"); break; case "DOWN": - this.emit("close"); + case "CONF": + case "CONN": + case "AUTH": + case "ERR": + if (ost == "UP") + this.emit("close"); break; } } diff --git a/edge-modbus/bin/driver.js b/edge-modbus/bin/driver.js index b49936f5..1ebe83f1 100644 --- a/edge-modbus/bin/driver.js +++ b/edge-modbus/bin/driver.js @@ -74,22 +74,30 @@ function parseAddr (spec) { return { id, func, addr, len }; } -const polling = asyncjs.queue(async ({ data, addr }) => { +async function poll ({ data, addr }) { console.log("READ %O", addr); if (!client) { console.log("Can't poll, no client!"); return; } - if (!client.isOpen) - await new Promise((r, j) => - client.open(e => e ? j(e) : r())); + if (!client.isOpen) { + const st = await new Promise((r, j) => + client.open(e => { + console.log("Modbus open: %s", e); + r(e ? "CONN" : "UP"); + })); + setStatus(st); + if (status != "UP") return; + } client.setID(addr.id); const val = await addr.func.read(client, addr.addr, addr.len); console.log("DATA %O", val); await mqtt.publishAsync(data, val.buffer); -}); +} +const polling = asyncjs.queue(asyncjs.timeout(poll, 10000)); +polling.error(e => console.log("POLL ERR: %o", e)); function setAddrs (pkt) { if (pkt.version != 1) { From 9690b026489c7816bdb1921baff04f74279e00d0 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Fri, 12 Jul 2024 11:12:36 +0100 Subject: [PATCH 06/14] Refactor into a general driver class This class will only handle drivers which can't do their own polling, and ignore the address groups. Drivers like OPCUA will need a different implementation. This driver is also careful to always poll serially. Some drivers might be able to handle parallel polls, and we should allow this. (Modbus can't.) --- edge-modbus/.gitignore | 1 + edge-modbus/bin/driver.js | 158 +-------------------------------- edge-modbus/lib/edge-driver.js | 147 ++++++++++++++++++++++++++++++ edge-modbus/lib/modbus.js | 78 ++++++++++++++++ 4 files changed, 230 insertions(+), 154 deletions(-) create mode 100644 edge-modbus/lib/edge-driver.js create mode 100644 edge-modbus/lib/modbus.js diff --git a/edge-modbus/.gitignore b/edge-modbus/.gitignore index 3c3629e6..6cb772be 100644 --- a/edge-modbus/.gitignore +++ b/edge-modbus/.gitignore @@ -1 +1,2 @@ node_modules +tmp diff --git a/edge-modbus/bin/driver.js b/edge-modbus/bin/driver.js index 1ebe83f1..2ff87dca 100644 --- a/edge-modbus/bin/driver.js +++ b/edge-modbus/bin/driver.js @@ -3,158 +3,8 @@ * Copyright 2024 AMRC */ -import asyncjs from "async"; -import MQTT from "mqtt"; -import ModbusRTU from "modbus-serial"; +import { PolledDriver } from "../lib/edge-driver.js"; +import { modbusHandler } from "../lib/modbus.js"; -const id = process.env.EDGE_USERNAME; - -const topic = (msg, data) => `fpEdge1/${id}/${msg}` + (data ? `/${data}` : ""); - -const mqtt = MQTT.connect({ - url: process.env.EDGE_MQTT, - clientId: id, - username: id, - password: process.env.EDGE_PASSWORD, - will: { - topic: topic("status"), - payload: "DOWN", - }, - manualConnect: true, - resubscribe: false, -}); - -let status = "DOWN"; -function setStatus (st) { - status = st; - if (mqtt.connected) - mqtt.publish(topic("status"), status); -} - -let client; -async function setConf (conf) { - if (conf.protocol != "tcp") - return "CONF"; - - client = new ModbusRTU(); - return client.connectTCP(conf.host, { port: conf.port }) - .then(() => "UP", () => "CONN"); -} - -let addrs; - -const funcs = { - input: { - read: (c, a, l) => c.readInputRegisters(a, l), - }, - holding: { - read: (c, a, l) => c.readHoldingRegisters(a, l), - }, - coil: { - read: (c, a, l) => c.readCoils(a, l), - }, - discrete: { - read: (c, a, l) => c.readDiscreteInputs(a, l), - }, -}; - -function parseAddr (spec) { - const parts = spec.split(","); - if (parts.length != 4) return; - - const id = Number.parseInt(parts[0]); - if (Number.isNaN(id) || id < 0) return; - const func = funcs[parts[1]]; - if (!func) return; - const addr = Number.parseInt(parts[2]); - if (Number.isNaN(addr) || addr < 0) return; - const len = Number.parseInt(parts[3]); - if (Number.isNaN(len) || len < 1) return; - - return { id, func, addr, len }; -} - -async function poll ({ data, addr }) { - console.log("READ %O", addr); - if (!client) { - console.log("Can't poll, no client!"); - return; - } - if (!client.isOpen) { - const st = await new Promise((r, j) => - client.open(e => { - console.log("Modbus open: %s", e); - r(e ? "CONN" : "UP"); - })); - setStatus(st); - if (status != "UP") return; - } - - client.setID(addr.id); - const val = await addr.func.read(client, addr.addr, addr.len); - console.log("DATA %O", val); - - await mqtt.publishAsync(data, val.buffer); -} -const polling = asyncjs.queue(asyncjs.timeout(poll, 10000)); -polling.error(e => console.log("POLL ERR: %o", e)); - -function setAddrs (pkt) { - if (pkt.version != 1) { - console.log("Bad addr config version: %s", pkt.version); - return false; - } - - const parsed = Object.entries(pkt.addrs) - .map(([t, a]) => [t, parseAddr(a)]); - if (parsed.some(([, f]) => !f)) { - console.log("BAD ADDRS: %O", pkt.addrs); - return false; - } - - addrs = new Map(parsed); - console.log("Set addrs: %O", addrs); - return true; -} - -mqtt.on("connect", async () => { - await mqtt.subscribeAsync( - "active conf addr poll" - .split(" ") - .map(t => topic(t))); - setStatus("READY"); -}); -mqtt.on("message", async (t, p) => { - const [, , msg, data] = t.split("/"); - switch (msg) { - case "active": - if (p.toString() == "ONLINE") - setStatus("READY"); - break; - case "conf": - const conf = JSON.parse(p.toString()); - console.log("CONF: %O", conf); - setStatus(await setConf(conf)); - break; - case "addr": - const a = JSON.parse(p.toString()); - if (!setAddrs(a)) - setStatus("CONF"); - break; - case "poll": - const poll = p.toString().split("\n"); - console.log("POLL %s %O", polling.length(), poll); - if (addrs) - poll.map(t => ({ - data: topic("data", t), - addr: addrs.get(t), - })) - .filter(({ addr }) => addr) - .forEach(a => polling.push(a)); - else - console.log("Can't poll, no addrs!"); - break; - } -}); - -mqtt.connect(); +const drv = new PolledDriver(process.env, modbusHandler); +drv.run(); diff --git a/edge-modbus/lib/edge-driver.js b/edge-modbus/lib/edge-driver.js new file mode 100644 index 00000000..7917a98f --- /dev/null +++ b/edge-modbus/lib/edge-driver.js @@ -0,0 +1,147 @@ +/* AMRC Connectivity Stack + * Edge Agent driver library + * Copyright 2024 AMRC + */ + +import asyncjs from "async"; +import MQTT from "mqtt"; + +const Q_TIMEOUT = 30000; +const Q_MAX = 20; + +export class PolledDriver { + constructor (env, createHandler) { + this.createHandler = createHandler; + + this.status = "DOWN"; + this.addrs = null; + + this.id = env.EDGE_USERNAME; + this.mqtt = this.createMqttClient(env.EDGE_MQTT, env.EDGE_PASSWORD); + this.polling = this.createPollQueue(); + } + + run () { + this.mqtt.connect(); + } + + topic (msg, data) { + return `fpEdge1/${this.id}/${msg}` + (data ? `/${data}` : ""); + } + + createMqttClient (broker, password) { + const mqtt = MQTT.connect({ + url: process.env.EDGE_MQTT, + clientId: this.id, + username: this.id, + password: process.env.EDGE_PASSWORD, + will: { + topic: this.topic("status"), + payload: "DOWN", + }, + manualConnect: true, + resubscribe: false, + }); + + mqtt.on("connect", () => this.connected()); + mqtt.on("message", (t, p) => this.handleMessage(t, p)); + + return mqtt; + } + + createPollQueue () { + const poll = this.poll.bind(this); + const q = asyncjs.queue(asyncjs.timeout(poll, Q_TIMEOUT)); + q.error(e => console.log("POLL ERR: %o", e)); + + return q; + } + + setStatus (st) { + this.status = st; + if (this.mqtt.connected) + this.mqtt.publish(this.topic("status"), st); + } + + setAddrs (pkt) { + if (!this.handler) { + console.log("Received addrs without handler"); + return false; + } + + if (pkt.version != 1) { + console.log("Bad addr config version: %s", pkt.version); + return false; + } + + const parsed = Object.entries(pkt.addrs) + .map(([t, a]) => [t, this.handler.parseAddr(a)]); + + if (parsed.some(([, f]) => !f)) { + console.log("BAD ADDRS: %O", pkt.addrs); + return false; + } + + this.addrs = new Map(parsed); + console.log("Set addrs: %O", this.addrs); + return true; + } + + async poll ({ data, spec }) { + console.log("READ %O", spec); + const buf = await this.handler.poll(spec); + + console.log("DATA %O", buf); + if (buf) + await this.mqtt.publishAsync(data, buf); + } + + async connected () { + await this.mqtt.subscribeAsync( + "active conf addr poll" + .split(" ") + .map(t => this.topic(t))); + this.setStatus("READY"); + } + + async handleMessage (topic, p) { + const [, , msg, data] = topic.split("/"); + switch (msg) { + case "active": + if (p.toString() == "ONLINE") + this.setStatus("READY"); + break; + + case "conf": + const conf = JSON.parse(p.toString()); + console.log("CONF: %O", conf); + this.addrs = null; + this.handler = this.createHandler(this, conf); + if (this.handler) + this.handler.run(); + else + this.setStatus("CONF"); + break; + + case "addr": + const a = JSON.parse(p.toString()); + if (!this.setAddrs(a)) + this.setStatus("CONF"); + break; + + case "poll": + const poll = p.toString().split("\n"); + console.log("POLL %s %O", this.polling.length(), poll); + if (this.addrs) + poll.map(t => ({ + data: this.topic("data", t), + spec: this.addrs.get(t), + })) + .filter(v => v.spec) + .forEach(a => this.polling.push(a)); + else + console.log("Can't poll, no addrs!"); + break; + } + } +} diff --git a/edge-modbus/lib/modbus.js b/edge-modbus/lib/modbus.js new file mode 100644 index 00000000..15ca39e5 --- /dev/null +++ b/edge-modbus/lib/modbus.js @@ -0,0 +1,78 @@ +/* AMRC Connectivity Stack + * Modbus Edge Agent driver + * Copyright 2024 AMRC + */ + +import ModbusRTU from "modbus-serial"; + +const funcs = { + input: { + read: (c, a, l) => c.readInputRegisters(a, l), + }, + holding: { + read: (c, a, l) => c.readHoldingRegisters(a, l), + }, + coil: { + read: (c, a, l) => c.readCoils(a, l), + }, + discrete: { + read: (c, a, l) => c.readDiscreteInputs(a, l), + }, +}; + +class ModbusHandler { + constructor (driver, conf) { + this.driver = driver; + this.conf = conf; + this.client = new ModbusRTU(); + } + + run () { + const { client, driver } = this; + const { host, port } = this.conf; + + client.connectTCP(host, { port }) + .then(() => driver.setStatus("UP")) + .catch(() => driver.setStatus("CONN")); + } + + parseAddr (spec) { + const parts = spec.split(","); + if (parts.length != 4) return; + + const id = Number.parseInt(parts[0]); + if (Number.isNaN(id) || id < 0) return; + const func = funcs[parts[1]]; + if (!func) return; + const addr = Number.parseInt(parts[2]); + if (Number.isNaN(addr) || addr < 0) return; + const len = Number.parseInt(parts[3]); + if (Number.isNaN(len) || len < 1) return; + + return { id, func, addr, len }; + } + + async poll (addr) { + const { client } = this; + + if (!client.isOpen) { + const st = await new Promise((r, j) => + client.open(e => { + console.log("Modbus open: %s", e); + r(e ? "CONN" : "UP"); + })); + this.driver.setStatus(st); + if (st != "UP") return; + } + + client.setID(addr.id); + const val = await addr.func.read(client, addr.addr, addr.len); + return val.buffer; + } +} + +export function modbusHandler (driver, conf) { + if (conf.protocol != "tcp") + return; + return new ModbusHandler(driver, conf); +} From bd6b8437c54d10c7f8b2b77f770e967c38e30aeb Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Fri, 12 Jul 2024 12:17:54 +0100 Subject: [PATCH 07/14] Handle reconnection better We want to maintain a continuous connection to the modbus device. We also want to alert the edge agent promptly if we lose our connection. Handle reconnection via connection events rather than waiting for a poll. Delay 5s before attempting to reconnect, otherwise we may enter a fast loop. --- edge-modbus/lib/modbus.js | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/edge-modbus/lib/modbus.js b/edge-modbus/lib/modbus.js index 15ca39e5..aab4d529 100644 --- a/edge-modbus/lib/modbus.js +++ b/edge-modbus/lib/modbus.js @@ -5,6 +5,8 @@ import ModbusRTU from "modbus-serial"; +const RECONNECT = 5000; + const funcs = { input: { read: (c, a, l) => c.readInputRegisters(a, l), @@ -24,16 +26,35 @@ class ModbusHandler { constructor (driver, conf) { this.driver = driver; this.conf = conf; + this.client = new ModbusRTU(); } run () { - const { client, driver } = this; + const { driver, client, sock } = this; const { host, port } = this.conf; + client.on("close", () => this.reconnect()); + client.connectTCP(host, { port }) .then(() => driver.setStatus("UP")) - .catch(() => driver.setStatus("CONN")); + .catch(() => this.reconnect()); + } + + async reconnect () { + const { client, driver } = this; + + console.log("Modbus connection closed"); + setTimeout(() => { + console.log("Reconnecting to modbus"); + client.open(e => { + driver.setStatus(e ? "CONN" : "UP"); + if (e) { + console.log("Failed to connect to modbus: %s", e); + this.reconnect(); + } + }); + }, RECONNECT); } parseAddr (spec) { @@ -55,15 +76,7 @@ class ModbusHandler { async poll (addr) { const { client } = this; - if (!client.isOpen) { - const st = await new Promise((r, j) => - client.open(e => { - console.log("Modbus open: %s", e); - r(e ? "CONN" : "UP"); - })); - this.driver.setStatus(st); - if (st != "UP") return; - } + if (!client.isOpen) return; client.setID(addr.id); const val = await addr.func.read(client, addr.addr, addr.len); From bf69e0e654f0313cade3bc1205ce75448c70919d Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Fri, 12 Jul 2024 13:49:27 +0100 Subject: [PATCH 08/14] Add a Dockerfile These drivers do not need the GSSAPI code, so should not need our customised base images. --- edge-modbus/Dockerfile | 33 +++++++++++++++++++++++++++++++++ edge-modbus/Makefile | 6 ++++++ 2 files changed, 39 insertions(+) create mode 100644 edge-modbus/Dockerfile create mode 100644 edge-modbus/Makefile diff --git a/edge-modbus/Dockerfile b/edge-modbus/Dockerfile new file mode 100644 index 00000000..7163f915 --- /dev/null +++ b/edge-modbus/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1 + +FROM node:22-alpine AS build +ARG acs_npm=NO +ARG revision=unknown + +USER root +RUN <<'SHELL' + install -d -o node -g node /home/node/app +SHELL +WORKDIR /home/node/app +USER node +COPY package*.json ./ +RUN <<'SHELL' + touch /home/node/.npmrc + if [ "${acs_npm}" != NO ] + then + npm config set @amrc-factoryplus:registry "${acs_npm}" + fi + + npm install --save=false +SHELL +COPY --chown=node . . +RUN <<'SHELL' + echo "export const GIT_VERSION=\"$revision\";" > ./lib/git-version.js +SHELL + +FROM node:22-alpine AS run +# Copy across from the build container. +WORKDIR /home/node/app +COPY --from=build --chown=root:root /home/node/app ./ +USER node +CMD node bin/driver.js diff --git a/edge-modbus/Makefile b/edge-modbus/Makefile new file mode 100644 index 00000000..fc81e800 --- /dev/null +++ b/edge-modbus/Makefile @@ -0,0 +1,6 @@ +top=.. +include ${top}/mk/acs.init.mk + +repo?=edge-modbus + +include ${mk}/acs.js.mk From 3ab39b79683ffbec919257d29575cd8b277610ef Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Mon, 15 Jul 2024 12:03:00 +0100 Subject: [PATCH 09/14] Migrate to js-edge-driver library --- edge-modbus/bin/driver.js | 8 +- edge-modbus/lib/edge-driver.js | 147 --------------------------------- edge-modbus/package-lock.json | 28 +++++-- edge-modbus/package.json | 9 +- 4 files changed, 33 insertions(+), 159 deletions(-) delete mode 100644 edge-modbus/lib/edge-driver.js diff --git a/edge-modbus/bin/driver.js b/edge-modbus/bin/driver.js index 2ff87dca..e41eb62c 100644 --- a/edge-modbus/bin/driver.js +++ b/edge-modbus/bin/driver.js @@ -3,8 +3,12 @@ * Copyright 2024 AMRC */ -import { PolledDriver } from "../lib/edge-driver.js"; +import { PolledDriver } from "@amrc-factoryplus/edge-driver"; import { modbusHandler } from "../lib/modbus.js"; -const drv = new PolledDriver(process.env, modbusHandler); +const drv = new PolledDriver({ + env: process.env, + handler: modbusHandler, + serial: true, +}); drv.run(); diff --git a/edge-modbus/lib/edge-driver.js b/edge-modbus/lib/edge-driver.js deleted file mode 100644 index 7917a98f..00000000 --- a/edge-modbus/lib/edge-driver.js +++ /dev/null @@ -1,147 +0,0 @@ -/* AMRC Connectivity Stack - * Edge Agent driver library - * Copyright 2024 AMRC - */ - -import asyncjs from "async"; -import MQTT from "mqtt"; - -const Q_TIMEOUT = 30000; -const Q_MAX = 20; - -export class PolledDriver { - constructor (env, createHandler) { - this.createHandler = createHandler; - - this.status = "DOWN"; - this.addrs = null; - - this.id = env.EDGE_USERNAME; - this.mqtt = this.createMqttClient(env.EDGE_MQTT, env.EDGE_PASSWORD); - this.polling = this.createPollQueue(); - } - - run () { - this.mqtt.connect(); - } - - topic (msg, data) { - return `fpEdge1/${this.id}/${msg}` + (data ? `/${data}` : ""); - } - - createMqttClient (broker, password) { - const mqtt = MQTT.connect({ - url: process.env.EDGE_MQTT, - clientId: this.id, - username: this.id, - password: process.env.EDGE_PASSWORD, - will: { - topic: this.topic("status"), - payload: "DOWN", - }, - manualConnect: true, - resubscribe: false, - }); - - mqtt.on("connect", () => this.connected()); - mqtt.on("message", (t, p) => this.handleMessage(t, p)); - - return mqtt; - } - - createPollQueue () { - const poll = this.poll.bind(this); - const q = asyncjs.queue(asyncjs.timeout(poll, Q_TIMEOUT)); - q.error(e => console.log("POLL ERR: %o", e)); - - return q; - } - - setStatus (st) { - this.status = st; - if (this.mqtt.connected) - this.mqtt.publish(this.topic("status"), st); - } - - setAddrs (pkt) { - if (!this.handler) { - console.log("Received addrs without handler"); - return false; - } - - if (pkt.version != 1) { - console.log("Bad addr config version: %s", pkt.version); - return false; - } - - const parsed = Object.entries(pkt.addrs) - .map(([t, a]) => [t, this.handler.parseAddr(a)]); - - if (parsed.some(([, f]) => !f)) { - console.log("BAD ADDRS: %O", pkt.addrs); - return false; - } - - this.addrs = new Map(parsed); - console.log("Set addrs: %O", this.addrs); - return true; - } - - async poll ({ data, spec }) { - console.log("READ %O", spec); - const buf = await this.handler.poll(spec); - - console.log("DATA %O", buf); - if (buf) - await this.mqtt.publishAsync(data, buf); - } - - async connected () { - await this.mqtt.subscribeAsync( - "active conf addr poll" - .split(" ") - .map(t => this.topic(t))); - this.setStatus("READY"); - } - - async handleMessage (topic, p) { - const [, , msg, data] = topic.split("/"); - switch (msg) { - case "active": - if (p.toString() == "ONLINE") - this.setStatus("READY"); - break; - - case "conf": - const conf = JSON.parse(p.toString()); - console.log("CONF: %O", conf); - this.addrs = null; - this.handler = this.createHandler(this, conf); - if (this.handler) - this.handler.run(); - else - this.setStatus("CONF"); - break; - - case "addr": - const a = JSON.parse(p.toString()); - if (!this.setAddrs(a)) - this.setStatus("CONF"); - break; - - case "poll": - const poll = p.toString().split("\n"); - console.log("POLL %s %O", this.polling.length(), poll); - if (this.addrs) - poll.map(t => ({ - data: this.topic("data", t), - spec: this.addrs.get(t), - })) - .filter(v => v.spec) - .forEach(a => this.polling.push(a)); - else - console.log("Can't poll, no addrs!"); - break; - } - } -} diff --git a/edge-modbus/package-lock.json b/edge-modbus/package-lock.json index a0734d48..d97196bb 100644 --- a/edge-modbus/package-lock.json +++ b/edge-modbus/package-lock.json @@ -1,16 +1,25 @@ { - "name": "amrc-connectivity-stack", - "version": "1.0.0", + "name": "edge-modbus", + "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "amrc-connectivity-stack", - "version": "1.0.0", + "name": "edge-modbus", + "version": "0.0.0", + "license": "ISC", + "dependencies": { + "@amrc-factoryplus/edge-driver": "0.0.1-bmz2", + "modbus-serial": "^8.0.17" + } + }, + "node_modules/@amrc-factoryplus/edge-driver": { + "version": "0.0.1-bmz2", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", + "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", "license": "ISC", "dependencies": { "async": "^3.2.5", - "modbus-serial": "^8.0.17", "mqtt": "^5.8.0" } }, @@ -751,6 +760,15 @@ } }, "dependencies": { + "@amrc-factoryplus/edge-driver": { + "version": "0.0.1-bmz2", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", + "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "requires": { + "async": "^3.2.5", + "mqtt": "^5.8.0" + } + }, "@babel/runtime": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", diff --git a/edge-modbus/package.json b/edge-modbus/package.json index 5692f866..379a68e0 100644 --- a/edge-modbus/package.json +++ b/edge-modbus/package.json @@ -1,7 +1,7 @@ { - "name": "amrc-connectivity-stack", - "version": "1.0.0", - "description": "The AMRC Connectivity Stack (ACS) is a comprehensive collection of open-source services developed by the AMRC that represents a complete end-to-end implementation of the [Factory+](https://factoryplus.app.amrc.co.uk) framework. It is distributed as a Kubernetes Helm chart an can be deployed onto any Kubernetes cluster.", + "name": "edge-modbus", + "version": "0.0.0", + "description": "", "main": "index.js", "type": "module", "directories": { @@ -14,8 +14,7 @@ "author": "", "license": "ISC", "dependencies": { - "async": "^3.2.5", "modbus-serial": "^8.0.17", - "mqtt": "^5.8.0" + "@amrc-factoryplus/edge-driver": "0.0.1-bmz2" } } From bf32dbeb0c2c3097fae68277ee5212b8de1f0361 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Mon, 15 Jul 2024 12:19:31 +0100 Subject: [PATCH 10/14] Refactor using @amrc-factoryplus/edge-driver --- edge-test/Dockerfile | 33 +++++++++ edge-test/Makefile | 6 ++ edge-test/bin/driver.js | 131 +++--------------------------------- edge-test/lib/test.js | 58 ++++++++++++++++ edge-test/package-lock.json | 32 +++++++-- edge-test/package.json | 1 + 6 files changed, 132 insertions(+), 129 deletions(-) create mode 100644 edge-test/Dockerfile create mode 100644 edge-test/Makefile create mode 100644 edge-test/lib/test.js diff --git a/edge-test/Dockerfile b/edge-test/Dockerfile new file mode 100644 index 00000000..7163f915 --- /dev/null +++ b/edge-test/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1 + +FROM node:22-alpine AS build +ARG acs_npm=NO +ARG revision=unknown + +USER root +RUN <<'SHELL' + install -d -o node -g node /home/node/app +SHELL +WORKDIR /home/node/app +USER node +COPY package*.json ./ +RUN <<'SHELL' + touch /home/node/.npmrc + if [ "${acs_npm}" != NO ] + then + npm config set @amrc-factoryplus:registry "${acs_npm}" + fi + + npm install --save=false +SHELL +COPY --chown=node . . +RUN <<'SHELL' + echo "export const GIT_VERSION=\"$revision\";" > ./lib/git-version.js +SHELL + +FROM node:22-alpine AS run +# Copy across from the build container. +WORKDIR /home/node/app +COPY --from=build --chown=root:root /home/node/app ./ +USER node +CMD node bin/driver.js diff --git a/edge-test/Makefile b/edge-test/Makefile new file mode 100644 index 00000000..85f1a1ef --- /dev/null +++ b/edge-test/Makefile @@ -0,0 +1,6 @@ +top=.. +include ${top}/mk/acs.init.mk + +repo?=edge-test + +include ${mk}/acs.js.mk diff --git a/edge-test/bin/driver.js b/edge-test/bin/driver.js index c431e1ce..dc93af92 100644 --- a/edge-test/bin/driver.js +++ b/edge-test/bin/driver.js @@ -1,129 +1,14 @@ /* AMRC Connectivity Stack - * Modbus Edge Agent driver + * Testing Edge Agent driver * Copyright 2024 AMRC */ -import asyncjs from "async"; -import MQTT from "mqtt"; +import { PolledDriver } from "@amrc-factoryplus/edge-driver"; +import { handleTest } from "../lib/test.js"; -const id = process.env.EDGE_USERNAME; - -const topic = (msg, data) => `fpEdge1/${id}/${msg}` + (data ? `/${data}` : ""); - -const mqtt = MQTT.connect({ - url: process.env.EDGE_MQTT, - clientId: id, - username: id, - password: process.env.EDGE_PASSWORD, - will: { - topic: topic("status"), - payload: "DOWN", - }, - manualConnect: true, - resubscribe: false, +const drv = new PolledDriver({ + env: process.env, + handler: handleTest, + serial: false, }); - -let status = "DOWN"; -function setStatus (st) { - status = st; - if (mqtt.connected) - mqtt.publish(topic("status"), status); -} -let conf; -let addrs; - -const funcs = { - const: (p, a) => t => a, - sin: (p, a) => t => a * Math.sin(2 * Math.PI * (t / p)), - saw: (p, a) => t => (a / p) * (t % p), -}; -const packing = { - bd: [8, (b, v) => b.writeDoubleBE(v)], - ld: [8, (b, v) => b.writeDoubleLE(v)], - bf: [4, (b, v) => b.writeFloatBE(v)], - lf: [4, (b, v) => b.writeFloatLE(v)], -}; - -function parseAddr (addr) { - const parts = addr.split(":"); - if (parts.length != 4) return; - - const func = funcs[parts[0]]; - if (!func) return; - const period = Number.parseFloat(parts[1]); - if (Number.isNaN(period)) return; - const amplitude = Number.parseFloat(parts[2]); - if (Number.isNaN(amplitude)) return; - const pack = packing[parts[3]]; - if (!pack) return; - - return [func(period, amplitude), ...pack]; -} - -function setAddrs (pkt) { - if (pkt.version != 1) { - console.log("Bad addr config version: %s", pkt.version); - return false; - } - - const parsed = Object.entries(pkt.addrs) - .map(([t, a]) => [t, parseAddr(a)]); - if (parsed.some(([, f]) => !f)) { - console.log("BAD ADDRS: %O", pkt.addrs); - return false; - } - - addrs = new Map( - parsed.map(([t, [f, s, p]]) => - [t, { topic: t, func: f, size: s, pack: p }])); - console.log("Set addrs: %O", addrs); - return true; -} - -const polling = asyncjs.queue(async addr => { - const val = addr.func(performance.now()); - const buf = Buffer.alloc(addr.size); - const top = topic("data", addr.topic); - - addr.pack(buf, val); - await mqtt.publishAsync(top, buf); -}); - -mqtt.on("connect", async () => { - await mqtt.subscribeAsync( - "active conf addr poll" - .split(" ") - .map(t => topic(t))); - setStatus("READY"); -}); -mqtt.on("message", (t, p) => { - const [, , msg, data] = t.split("/"); - switch (msg) { - case "active": - if (p.toString() == "ONLINE") - setStatus("READY"); - break; - case "conf": - conf = JSON.parse(p.toString()); - console.log("CONF: %O", conf); - setStatus("UP"); - break; - case "addr": - const a = JSON.parse(p.toString()); - if (!setAddrs(a)) - setStatus("CONF"); - break; - case "poll": - const poll = p.toString().split("\n"); - console.log("POLL %O", poll); - if (addrs) - poll.map(t => addrs.get(t)) - .filter(a => a) - .forEach(a => polling.push(a)); - else - console.log("Can't poll, no addrs!"); - break; - } -}); - -mqtt.connect(); +drv.run(); diff --git a/edge-test/lib/test.js b/edge-test/lib/test.js new file mode 100644 index 00000000..1cd529dd --- /dev/null +++ b/edge-test/lib/test.js @@ -0,0 +1,58 @@ +/* + * Edge Agent testing driver + * Copyright 2024 AMRC + */ + +const funcs = { + const: (p, a) => t => a, + sin: (p, a) => t => a * Math.sin(2 * Math.PI * (t / p)), + saw: (p, a) => t => (a / p) * (t % p), +}; +const packing = { + bd: [8, (b, v) => b.writeDoubleBE(v)], + ld: [8, (b, v) => b.writeDoubleLE(v)], + bf: [4, (b, v) => b.writeFloatBE(v)], + lf: [4, (b, v) => b.writeFloatLE(v)], +}; + +class TestHandler { + constructor (driver) { + this.driver = driver; + } + + run () { + this.driver.setStatus("UP"); + } + + parseAddr (spec) { + const parts = spec.split(":"); + if (parts.length != 4) return; + + const func = funcs[parts[0]]; + if (!func) return; + const period = Number.parseFloat(parts[1]); + if (Number.isNaN(period)) return; + const amplitude = Number.parseFloat(parts[2]); + if (Number.isNaN(amplitude)) return; + const pack = packing[parts[3]]; + if (!pack) return; + + return { + func: func(period, amplitude), + size: pack[0], + pack: pack[1], + }; + } + + async poll (addr) { + const val = addr.func(performance.now()); + const buf = Buffer.alloc(addr.size); + addr.pack(buf, val); + + return buf; + } +} + +export function handleTest (driver, conf) { + return new TestHandler(driver); +} diff --git a/edge-test/package-lock.json b/edge-test/package-lock.json index b553adfa..7d971948 100644 --- a/edge-test/package-lock.json +++ b/edge-test/package-lock.json @@ -9,10 +9,21 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz2", "async": "^3.2.5", "mqtt": "^5.7.2" } }, + "node_modules/@amrc-factoryplus/edge-driver": { + "version": "0.0.1-bmz2", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", + "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "license": "ISC", + "dependencies": { + "async": "^3.2.5", + "mqtt": "^5.8.0" + } + }, "node_modules/@babel/runtime": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", @@ -254,9 +265,9 @@ } }, "node_modules/mqtt": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.7.2.tgz", - "integrity": "sha512-b5xIA9J/K1LTubSWKaNYYLxYIusQdip6o9/8bRWad2TelRr8xLifjQt+SnamDAwMp3O6NdvR9E8ae7VMuN02kg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.8.0.tgz", + "integrity": "sha512-/+H04mv6goy6K5gHMNH3uS0icBzXapS+4uUf4yZyQWXi72APPZNb81bQhvkm99poEQettXVT8XETB0mPxl5Wjg==", "dependencies": { "@types/readable-stream": "^4.0.5", "@types/ws": "^8.5.9", @@ -465,6 +476,15 @@ } }, "dependencies": { + "@amrc-factoryplus/edge-driver": { + "version": "0.0.1-bmz2", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", + "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "requires": { + "async": "^3.2.5", + "mqtt": "^5.8.0" + } + }, "@babel/runtime": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", @@ -627,9 +647,9 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "mqtt": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.7.2.tgz", - "integrity": "sha512-b5xIA9J/K1LTubSWKaNYYLxYIusQdip6o9/8bRWad2TelRr8xLifjQt+SnamDAwMp3O6NdvR9E8ae7VMuN02kg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.8.0.tgz", + "integrity": "sha512-/+H04mv6goy6K5gHMNH3uS0icBzXapS+4uUf4yZyQWXi72APPZNb81bQhvkm99poEQettXVT8XETB0mPxl5Wjg==", "requires": { "@types/readable-stream": "^4.0.5", "@types/ws": "^8.5.9", diff --git a/edge-test/package.json b/edge-test/package.json index c03bd6db..b28f7929 100644 --- a/edge-test/package.json +++ b/edge-test/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz2", "async": "^3.2.5", "mqtt": "^5.7.2" } From 7682bf135bdb95fa1a25a48b96e16b9af123b97d Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Tue, 16 Jul 2024 11:48:06 +0100 Subject: [PATCH 11/14] Update edge drivers to the new API --- edge-modbus/lib/modbus.js | 18 ++++++++++++++---- edge-modbus/package-lock.json | 14 +++++++------- edge-modbus/package.json | 2 +- edge-test/lib/test.js | 12 +++--------- edge-test/package-lock.json | 14 +++++++------- edge-test/package.json | 2 +- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/edge-modbus/lib/modbus.js b/edge-modbus/lib/modbus.js index aab4d529..7122d2af 100644 --- a/edge-modbus/lib/modbus.js +++ b/edge-modbus/lib/modbus.js @@ -28,17 +28,27 @@ class ModbusHandler { this.conf = conf; this.client = new ModbusRTU(); + this.on_close = () => this.reconnect(); } run () { - const { driver, client, sock } = this; + const { driver, client } = this; const { host, port } = this.conf; - client.on("close", () => this.reconnect()); + client.on("close", this.on_close); client.connectTCP(host, { port }) .then(() => driver.setStatus("UP")) - .catch(() => this.reconnect()); + .catch(this.on_close); + + return this; + } + + close () { + const { client } = this; + + client.off("close", this.on_close); + return new Promise(r => client.close(r)); } async reconnect () { @@ -87,5 +97,5 @@ class ModbusHandler { export function modbusHandler (driver, conf) { if (conf.protocol != "tcp") return; - return new ModbusHandler(driver, conf); + return new ModbusHandler(driver, conf).run(); } diff --git a/edge-modbus/package-lock.json b/edge-modbus/package-lock.json index d97196bb..252ecbf4 100644 --- a/edge-modbus/package-lock.json +++ b/edge-modbus/package-lock.json @@ -9,14 +9,14 @@ "version": "0.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "0.0.1-bmz2", + "@amrc-factoryplus/edge-driver": "0.0.1-bmz3", "modbus-serial": "^8.0.17" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz2", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", - "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "version": "0.0.1-bmz3", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", + "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", "license": "ISC", "dependencies": { "async": "^3.2.5", @@ -761,9 +761,9 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz2", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", - "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "version": "0.0.1-bmz3", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", + "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" diff --git a/edge-modbus/package.json b/edge-modbus/package.json index 379a68e0..5fd9bd8b 100644 --- a/edge-modbus/package.json +++ b/edge-modbus/package.json @@ -15,6 +15,6 @@ "license": "ISC", "dependencies": { "modbus-serial": "^8.0.17", - "@amrc-factoryplus/edge-driver": "0.0.1-bmz2" + "@amrc-factoryplus/edge-driver": "0.0.1-bmz3" } } diff --git a/edge-test/lib/test.js b/edge-test/lib/test.js index 1cd529dd..7081a332 100644 --- a/edge-test/lib/test.js +++ b/edge-test/lib/test.js @@ -16,14 +16,6 @@ const packing = { }; class TestHandler { - constructor (driver) { - this.driver = driver; - } - - run () { - this.driver.setStatus("UP"); - } - parseAddr (spec) { const parts = spec.split(":"); if (parts.length != 4) return; @@ -54,5 +46,7 @@ class TestHandler { } export function handleTest (driver, conf) { - return new TestHandler(driver); + const h = new TestHandler(); + driver.setStatus("UP"); + return h; } diff --git a/edge-test/package-lock.json b/edge-test/package-lock.json index 7d971948..12d73b87 100644 --- a/edge-test/package-lock.json +++ b/edge-test/package-lock.json @@ -9,15 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz2", + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz3", "async": "^3.2.5", "mqtt": "^5.7.2" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz2", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", - "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "version": "0.0.1-bmz3", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", + "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", "license": "ISC", "dependencies": { "async": "^3.2.5", @@ -477,9 +477,9 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz2", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz2.tgz", - "integrity": "sha512-ZpMyfNWxtboGRYl/B4oqG1a3VX00Pw0yILTJXTN/1SB9GTsFj5zJ+qkJOGL7G9XMM3QFjV7h9YtZH36wrokJcw==", + "version": "0.0.1-bmz3", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", + "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" diff --git a/edge-test/package.json b/edge-test/package.json index b28f7929..b78218df 100644 --- a/edge-test/package.json +++ b/edge-test/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz2", + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz3", "async": "^3.2.5", "mqtt": "^5.7.2" } From b68700e501db2e0b3ea5a23982e7e721ee6881a2 Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Tue, 16 Jul 2024 14:32:56 +0100 Subject: [PATCH 12/14] Use the Debug object from the driver class --- edge-modbus/lib/modbus.js | 8 +++++--- edge-modbus/package-lock.json | 14 +++++++------- edge-modbus/package.json | 2 +- edge-test/package-lock.json | 14 +++++++------- edge-test/package.json | 2 +- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/edge-modbus/lib/modbus.js b/edge-modbus/lib/modbus.js index 7122d2af..3ea4cd40 100644 --- a/edge-modbus/lib/modbus.js +++ b/edge-modbus/lib/modbus.js @@ -27,6 +27,8 @@ class ModbusHandler { this.driver = driver; this.conf = conf; + this.log = driver.debug.bound("modbus"); + this.client = new ModbusRTU(); this.on_close = () => this.reconnect(); } @@ -54,13 +56,13 @@ class ModbusHandler { async reconnect () { const { client, driver } = this; - console.log("Modbus connection closed"); + this.log("Modbus connection closed"); setTimeout(() => { - console.log("Reconnecting to modbus"); + this.log("Reconnecting to modbus"); client.open(e => { driver.setStatus(e ? "CONN" : "UP"); if (e) { - console.log("Failed to connect to modbus: %s", e); + this.log("Failed to connect to modbus: %s", e); this.reconnect(); } }); diff --git a/edge-modbus/package-lock.json b/edge-modbus/package-lock.json index 252ecbf4..eced3f47 100644 --- a/edge-modbus/package-lock.json +++ b/edge-modbus/package-lock.json @@ -9,14 +9,14 @@ "version": "0.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "0.0.1-bmz3", + "@amrc-factoryplus/edge-driver": "0.0.1-bmz4", "modbus-serial": "^8.0.17" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz3", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", - "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", + "version": "0.0.1-bmz4", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", + "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", "license": "ISC", "dependencies": { "async": "^3.2.5", @@ -761,9 +761,9 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz3", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", - "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", + "version": "0.0.1-bmz4", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", + "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" diff --git a/edge-modbus/package.json b/edge-modbus/package.json index 5fd9bd8b..c9be45ce 100644 --- a/edge-modbus/package.json +++ b/edge-modbus/package.json @@ -15,6 +15,6 @@ "license": "ISC", "dependencies": { "modbus-serial": "^8.0.17", - "@amrc-factoryplus/edge-driver": "0.0.1-bmz3" + "@amrc-factoryplus/edge-driver": "0.0.1-bmz4" } } diff --git a/edge-test/package-lock.json b/edge-test/package-lock.json index 12d73b87..b8d518a6 100644 --- a/edge-test/package-lock.json +++ b/edge-test/package-lock.json @@ -9,15 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz3", + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz4", "async": "^3.2.5", "mqtt": "^5.7.2" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz3", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", - "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", + "version": "0.0.1-bmz4", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", + "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", "license": "ISC", "dependencies": { "async": "^3.2.5", @@ -477,9 +477,9 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz3", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz3.tgz", - "integrity": "sha512-ScEmjMZ8vUH+prjCqEYCfVGbQdA+e7o2lvNZI9pJRah3ULNDthymTmOAB6CEnhd1jwlkdcgmLmbR9GPBok+yGg==", + "version": "0.0.1-bmz4", + "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", + "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" diff --git a/edge-test/package.json b/edge-test/package.json index b78218df..9af286c0 100644 --- a/edge-test/package.json +++ b/edge-test/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz3", + "@amrc-factoryplus/edge-driver": "^0.0.1-bmz4", "async": "^3.2.5", "mqtt": "^5.7.2" } From 6821f87796a4ceaddac159b5a55d2390576f441c Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Wed, 17 Jul 2024 10:37:06 +0100 Subject: [PATCH 13/14] Add an update target --- mk/acs.js.mk | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mk/acs.js.mk b/mk/acs.js.mk index f7c257dd..c715e92f 100644 --- a/mk/acs.js.mk +++ b/mk/acs.js.mk @@ -11,7 +11,7 @@ ifdef acs_npm build_args+= --build-arg acs_npm="${acs_npm}" endif -.PHONY: lint +.PHONY: lint update build: lint @@ -25,6 +25,14 @@ js.eslint: npx eslint ${eslint} endif +update: js.update + @: + +js.update: git.check-committed + npm update + git add . + git commit -m "npm update $$(git rev-parse --show-prefix)" + include ${mk}/acs.docker.mk endif From cbfcd5551abfc0459ad5215c2355297d721bafbe Mon Sep 17 00:00:00 2001 From: Ben Morrow Date: Tue, 16 Jul 2024 14:45:14 +0100 Subject: [PATCH 14/14] npm update --- edge-modbus/package-lock.json | 51 +++++++++-------- edge-modbus/package.json | 2 +- edge-test/package-lock.json | 102 ++++++++++++++++------------------ edge-test/package.json | 2 +- 4 files changed, 76 insertions(+), 81 deletions(-) diff --git a/edge-modbus/package-lock.json b/edge-modbus/package-lock.json index eced3f47..287249c4 100644 --- a/edge-modbus/package-lock.json +++ b/edge-modbus/package-lock.json @@ -9,24 +9,23 @@ "version": "0.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "0.0.1-bmz4", + "@amrc-factoryplus/edge-driver": "^0.0.2", "modbus-serial": "^8.0.17" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz4", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", - "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", - "license": "ISC", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@amrc-factoryplus/edge-driver/-/edge-driver-0.0.2.tgz", + "integrity": "sha512-Uhcou4R1IRusqKRO7ae8gwkiTv0UfcTbissi9RN74+C3O9NqHPPYOScGCRjmYwDyKhJUOHsVkoB1BNcfnsLzpg==", "dependencies": { "async": "^3.2.5", "mqtt": "^5.8.0" } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -256,9 +255,9 @@ } }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dependencies": { "undici-types": "~5.26.4" } @@ -273,9 +272,9 @@ } }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", "dependencies": { "@types/node": "*" } @@ -761,18 +760,18 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz4", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", - "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@amrc-factoryplus/edge-driver/-/edge-driver-0.0.2.tgz", + "integrity": "sha512-Uhcou4R1IRusqKRO7ae8gwkiTv0UfcTbissi9RN74+C3O9NqHPPYOScGCRjmYwDyKhJUOHsVkoB1BNcfnsLzpg==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" } }, "@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "requires": { "regenerator-runtime": "^0.14.0" } @@ -899,9 +898,9 @@ } }, "@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "requires": { "undici-types": "~5.26.4" } @@ -916,9 +915,9 @@ } }, "@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", "requires": { "@types/node": "*" } diff --git a/edge-modbus/package.json b/edge-modbus/package.json index c9be45ce..1dccdd9a 100644 --- a/edge-modbus/package.json +++ b/edge-modbus/package.json @@ -15,6 +15,6 @@ "license": "ISC", "dependencies": { "modbus-serial": "^8.0.17", - "@amrc-factoryplus/edge-driver": "0.0.1-bmz4" + "@amrc-factoryplus/edge-driver": "^0.0.2" } } diff --git a/edge-test/package-lock.json b/edge-test/package-lock.json index b8d518a6..d67012a5 100644 --- a/edge-test/package-lock.json +++ b/edge-test/package-lock.json @@ -9,25 +9,24 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz4", + "@amrc-factoryplus/edge-driver": "^0.0.2", "async": "^3.2.5", "mqtt": "^5.7.2" } }, "node_modules/@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz4", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", - "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", - "license": "ISC", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@amrc-factoryplus/edge-driver/-/edge-driver-0.0.2.tgz", + "integrity": "sha512-Uhcou4R1IRusqKRO7ae8gwkiTv0UfcTbissi9RN74+C3O9NqHPPYOScGCRjmYwDyKhJUOHsVkoB1BNcfnsLzpg==", "dependencies": { "async": "^3.2.5", "mqtt": "^5.8.0" } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -36,26 +35,26 @@ } }, "node_modules/@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/readable-stream": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", - "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", "dependencies": { "@types/node": "*", "safe-buffer": "~5.1.1" } }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", "dependencies": { "@types/node": "*" } @@ -96,9 +95,9 @@ ] }, "node_modules/bl": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.12.tgz", - "integrity": "sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w==", + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", + "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", @@ -249,12 +248,9 @@ } }, "node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/minimist": { "version": "1.2.8", @@ -455,9 +451,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -477,43 +473,43 @@ }, "dependencies": { "@amrc-factoryplus/edge-driver": { - "version": "0.0.1-bmz4", - "resolved": "https://npm.amrc-factoryplus-dev.shef.ac.uk/@amrc-factoryplus%2fedge-driver/-/edge-driver-0.0.1-bmz4.tgz", - "integrity": "sha512-niiX/PpbliUvVlICRKhly/F6irYu5UxbDN6qxAeJZbJyqVMauTc3z41eO66fNLStppgwxWvgBn0sWkFjhGn8TQ==", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@amrc-factoryplus/edge-driver/-/edge-driver-0.0.2.tgz", + "integrity": "sha512-Uhcou4R1IRusqKRO7ae8gwkiTv0UfcTbissi9RN74+C3O9NqHPPYOScGCRjmYwDyKhJUOHsVkoB1BNcfnsLzpg==", "requires": { "async": "^3.2.5", "mqtt": "^5.8.0" } }, "@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "requires": { "regenerator-runtime": "^0.14.0" } }, "@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "requires": { "undici-types": "~5.26.4" } }, "@types/readable-stream": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", - "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", "requires": { "@types/node": "*", "safe-buffer": "~5.1.1" } }, "@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", "requires": { "@types/node": "*" } @@ -537,9 +533,9 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bl": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.12.tgz", - "integrity": "sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w==", + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", + "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", "requires": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", @@ -637,9 +633,9 @@ "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" }, "lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "minimist": { "version": "1.2.8", @@ -808,9 +804,9 @@ } }, "ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "requires": {} } } diff --git a/edge-test/package.json b/edge-test/package.json index 9af286c0..3706d6ad 100644 --- a/edge-test/package.json +++ b/edge-test/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "@amrc-factoryplus/edge-driver": "^0.0.1-bmz4", + "@amrc-factoryplus/edge-driver": "^0.0.2", "async": "^3.2.5", "mqtt": "^5.7.2" }