@totemsdk/edge-modbus 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/NOTICE +60 -0
- package/README.md +76 -6
- package/dist/cjs/gateway.d.ts +35 -0
- package/dist/cjs/gateway.js +235 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.js +9 -0
- package/dist/cjs/native-transport.d.ts +36 -0
- package/dist/cjs/native-transport.js +166 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/sensor-bridge.d.ts +30 -0
- package/dist/cjs/sensor-bridge.js +64 -0
- package/dist/cjs/transport.d.ts +38 -0
- package/dist/cjs/transport.js +8 -0
- package/dist/gateway.d.ts +2 -3
- package/dist/gateway.js +71 -30
- package/dist/index.js +3 -9
- package/dist/native-transport.js +1 -5
- package/dist/sensor-bridge.d.ts +1 -1
- package/dist/sensor-bridge.js +12 -41
- package/dist/transport.d.ts +7 -1
- package/dist/transport.js +1 -2
- package/package.json +25 -15
- package/sidecar.json +34 -0
package/dist/sensor-bridge.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Modbus Sensor Bridge — maps Modbus register reads to Edge proof inputs.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* Shares gateway validation so malformed responses never become sensor proofs.
|
|
6
5
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function createModbusSensorBridge(config) {
|
|
6
|
+
import { createModbusGateway } from './gateway.js';
|
|
7
|
+
export function createModbusSensorBridge(config) {
|
|
10
8
|
const timers = [];
|
|
11
9
|
let running = false;
|
|
10
|
+
const gateway = config.gateway ?? createModbusGateway({ runtime: config.runtime, transport: config.transport });
|
|
12
11
|
async function pollBinding(binding) {
|
|
13
12
|
try {
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
13
|
+
const args = [binding.unitId, binding.address, binding.count];
|
|
14
|
+
const result = binding.functionCode === 1 ? await gateway.readCoils(...args)
|
|
15
|
+
: binding.functionCode === 2 ? await gateway.readDiscreteInputs(...args)
|
|
16
|
+
: binding.functionCode === 3 ? await gateway.readHoldingRegisters(...args)
|
|
17
|
+
: binding.functionCode === 4 ? await gateway.readInputRegisters(...args) : undefined;
|
|
18
|
+
if (!result?.ok || !result.data)
|
|
19
|
+
return;
|
|
20
|
+
const values = result.data.values;
|
|
23
21
|
if (config.runtime.ports.proof) {
|
|
24
22
|
await config.runtime.ports.proof.createProof({
|
|
25
23
|
subject: `sensor:${binding.sensorId}`,
|
|
@@ -61,30 +59,3 @@ function createModbusSensorBridge(config) {
|
|
|
61
59
|
},
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
|
-
// ── RTU frame builder (no MBAP header — matches Go binary) ────────────────
|
|
65
|
-
function buildReadFrame(unitId, fc, address, count) {
|
|
66
|
-
const buf = new Uint8Array(8);
|
|
67
|
-
buf[0] = unitId;
|
|
68
|
-
buf[1] = fc;
|
|
69
|
-
new DataView(buf.buffer).setUint16(2, address, false);
|
|
70
|
-
new DataView(buf.buffer).setUint16(4, count, false);
|
|
71
|
-
buf[6] = 0;
|
|
72
|
-
buf[7] = 0;
|
|
73
|
-
return buf;
|
|
74
|
-
}
|
|
75
|
-
// ── Response parsers (RTU format — no MBAP header) ────────────────────────
|
|
76
|
-
function parseCoils(response, count) {
|
|
77
|
-
const result = [];
|
|
78
|
-
for (let i = 0; i < count; i++) {
|
|
79
|
-
const byte = response[3 + Math.floor(i / 8)] ?? 0;
|
|
80
|
-
result.push(((byte >> (i % 8)) & 1) === 1);
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
function parseRegisters(response, count) {
|
|
85
|
-
const result = [];
|
|
86
|
-
for (let i = 0; i < count; i++) {
|
|
87
|
-
result.push(new DataView(response.buffer, response.byteOffset + 3 + i * 2, 2).getUint16(0, false));
|
|
88
|
-
}
|
|
89
|
-
return result;
|
|
90
|
-
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -9,7 +9,13 @@ export interface ModbusTransportPort {
|
|
|
9
9
|
connect(): Promise<void>;
|
|
10
10
|
/** Close the connection. */
|
|
11
11
|
disconnect(): Promise<void>;
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Exchange [unitId, functionCode, ...PDU data], with no CRC or MBAP header.
|
|
14
|
+
* Reads return [unitId, functionCode, byteCount, ...data]; successful writes
|
|
15
|
+
* return [unitId, functionCode, addressHi, addressLo, valueOrCountHi, valueOrCountLo].
|
|
16
|
+
* The binding must validate wire CRC/MBAP transaction identity and must not
|
|
17
|
+
* retry writes automatically when the physical outcome is uncertain.
|
|
18
|
+
*/
|
|
13
19
|
sendFrame(frame: Uint8Array): Promise<Uint8Array>;
|
|
14
20
|
/** Register a handler for unsolicited/inbound frames. */
|
|
15
21
|
onFrame(handler: (frame: Uint8Array) => void): () => void;
|
package/dist/transport.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Modbus transport port — injected by the caller.
|
|
4
3
|
*
|
|
5
4
|
* Supports Modbus TCP (port 502) and Modbus RTU (serial).
|
|
6
5
|
* The caller provides the actual socket/serial implementation.
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/edge-modbus",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Edge runtime adapter for Modbus — PLCs, RTUs, industrial sensors over serial/TCP",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"types": "dist/cjs/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"require": {
|
|
14
|
+
"types": "./dist/cjs/index.d.ts",
|
|
15
|
+
"default": "./dist/cjs/index.js"
|
|
16
|
+
}
|
|
12
17
|
}
|
|
13
18
|
},
|
|
14
19
|
"files": [
|
|
15
20
|
"dist",
|
|
21
|
+
"sidecar.json",
|
|
16
22
|
"README.md",
|
|
17
|
-
"LICENSE"
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"NOTICE"
|
|
18
25
|
],
|
|
19
26
|
"dependencies": {
|
|
20
|
-
"@totemsdk/edge": "^1.
|
|
27
|
+
"@totemsdk/edge": "^1.5.1"
|
|
21
28
|
},
|
|
22
29
|
"devDependencies": {
|
|
23
|
-
"@types/jest": "^30.0.0",
|
|
24
30
|
"@types/node": "^20.0.0",
|
|
25
|
-
"jest": "^30.4.2",
|
|
26
|
-
"ts-jest": "^29.0.0",
|
|
27
31
|
"typescript": "^7.0.2"
|
|
28
32
|
},
|
|
29
33
|
"engines": {
|
|
@@ -32,9 +36,9 @@
|
|
|
32
36
|
"publishConfig": {
|
|
33
37
|
"access": "public"
|
|
34
38
|
},
|
|
35
|
-
"license": "
|
|
39
|
+
"license": "Apache-2.0",
|
|
36
40
|
"author": "Totem SDK",
|
|
37
|
-
"homepage": "https://totem.ing",
|
|
41
|
+
"homepage": "https://docs.totem.ing",
|
|
38
42
|
"repository": {
|
|
39
43
|
"type": "git",
|
|
40
44
|
"url": "git+https://github.com/Totem-Edge/totem-sdk.git",
|
|
@@ -52,10 +56,16 @@
|
|
|
52
56
|
"bugs": {
|
|
53
57
|
"url": "https://github.com/Totem-Edge/totem-sdk/issues"
|
|
54
58
|
},
|
|
59
|
+
"type": "module",
|
|
55
60
|
"scripts": {
|
|
56
|
-
"build": "tsc",
|
|
61
|
+
"build": "tsc && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({type:'commonjs'})+'\\n')\"",
|
|
57
62
|
"build:go": "cd go && go build -o ../bin/edge-modbus .",
|
|
58
63
|
"clean": "rm -rf dist bin",
|
|
59
|
-
"test": "jest"
|
|
64
|
+
"test": "jest --config ../../tests/jest/edge-modbus.cjs",
|
|
65
|
+
"test:go": "cd go && go test -race ./... && go vet ./...",
|
|
66
|
+
"test:integration": "node --test ../../tests/qualification/edge-modbus/test/sidecar.integration.mjs",
|
|
67
|
+
"test:conformance": "cd go && go test -race ./...",
|
|
68
|
+
"test:recovery": "node --test ../../tests/qualification/edge-modbus/test/sidecar.integration.mjs",
|
|
69
|
+
"test:consumer": "node --test ../../tests/qualification/edge-modbus/test/packed-consumer.test.mjs"
|
|
60
70
|
}
|
|
61
71
|
}
|
package/sidecar.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "edge-modbus-sidecar",
|
|
3
|
+
"protocolVersion": 1,
|
|
4
|
+
"distribution": {
|
|
5
|
+
"mode": "source-build",
|
|
6
|
+
"bundledInNpmPackage": false,
|
|
7
|
+
"binary": "bin/edge-modbus",
|
|
8
|
+
"build": "pnpm --filter @totemsdk/edge-modbus build:go",
|
|
9
|
+
"autoDownload": false
|
|
10
|
+
},
|
|
11
|
+
"compatibility": {
|
|
12
|
+
"protocolMajor": 1,
|
|
13
|
+
"healthCheck": "edge-modbus --version",
|
|
14
|
+
"rule": "Rebuild the sidecar from the matching package source. The npm package does not bundle platform binaries."
|
|
15
|
+
},
|
|
16
|
+
"configuration": {
|
|
17
|
+
"mode": "MODBUS_MODE=tcp|rtu",
|
|
18
|
+
"listen": "MODBUS_LISTEN_ADDR=127.0.0.1:15002",
|
|
19
|
+
"tcpTarget": "MODBUS_TCP_ADDR=host:502",
|
|
20
|
+
"serial": "MODBUS_SERIAL_PORT=/dev/ttyUSB0",
|
|
21
|
+
"timeout": "MODBUS_TIMEOUT_MS=2000"
|
|
22
|
+
},
|
|
23
|
+
"qualified": [
|
|
24
|
+
"darwin-arm64 source build",
|
|
25
|
+
"loopback Modbus TCP simulator",
|
|
26
|
+
"RTU codec/CRC unit coverage"
|
|
27
|
+
],
|
|
28
|
+
"unqualified": [
|
|
29
|
+
"modbus serial hardware",
|
|
30
|
+
"Windows and Linux prebuilt binaries",
|
|
31
|
+
"broker/PLC vendor matrix",
|
|
32
|
+
"hot upgrade or rolling sidecar replacement"
|
|
33
|
+
]
|
|
34
|
+
}
|