@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.
@@ -0,0 +1,7 @@
1
+ export type { ModbusTransportPort, ModbusMessage } from './transport.js';
2
+ export { createModbusGateway } from './gateway.js';
3
+ export type { ModbusGatewayConfig, ModbusGateway } from './gateway.js';
4
+ export { createModbusSensorBridge } from './sensor-bridge.js';
5
+ export type { ModbusSensorBinding, ModbusSensorBridgeConfig, ModbusSensorBridge } from './sensor-bridge.js';
6
+ export { NativeModbusTransport } from './native-transport.js';
7
+ export type { NativeModbusConfig } from './native-transport.js';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NativeModbusTransport = exports.createModbusSensorBridge = exports.createModbusGateway = void 0;
4
+ var gateway_js_1 = require("./gateway.js");
5
+ Object.defineProperty(exports, "createModbusGateway", { enumerable: true, get: function () { return gateway_js_1.createModbusGateway; } });
6
+ var sensor_bridge_js_1 = require("./sensor-bridge.js");
7
+ Object.defineProperty(exports, "createModbusSensorBridge", { enumerable: true, get: function () { return sensor_bridge_js_1.createModbusSensorBridge; } });
8
+ var native_transport_js_1 = require("./native-transport.js");
9
+ Object.defineProperty(exports, "NativeModbusTransport", { enumerable: true, get: function () { return native_transport_js_1.NativeModbusTransport; } });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * NativeModbusTransport — connects to the edge-modbus Go binary over TCP.
3
+ *
4
+ * The Go binary listens on a local TCP socket (default 127.0.0.1:15002)
5
+ * and accepts newline-delimited JSON requests. This class implements
6
+ * ModbusTransportPort by forwarding all calls to the Go process.
7
+ *
8
+ * Usage:
9
+ * const transport = new NativeModbusTransport({ host: '127.0.0.1', port: 15002 });
10
+ * await transport.connect();
11
+ * const response = await transport.sendFrame(requestFrame);
12
+ * transport.disconnect();
13
+ */
14
+ import type { ModbusTransportPort } from './transport.js';
15
+ export interface NativeModbusConfig {
16
+ host?: string;
17
+ port?: number;
18
+ /** Connection timeout in ms. Default 5000. */
19
+ connectTimeoutMs?: number;
20
+ /** Per-request timeout in ms. Default 10000. */
21
+ requestTimeoutMs?: number;
22
+ }
23
+ export declare class NativeModbusTransport implements ModbusTransportPort {
24
+ private socket;
25
+ private buffer;
26
+ private pending;
27
+ private frameHandlers;
28
+ private errorHandlers;
29
+ private readonly config;
30
+ constructor(config?: NativeModbusConfig);
31
+ connect(): Promise<void>;
32
+ disconnect(): Promise<void>;
33
+ sendFrame(frame: Uint8Array): Promise<Uint8Array>;
34
+ onFrame(handler: (frame: Uint8Array) => void): () => void;
35
+ onError(handler: (err: Error) => void): () => void;
36
+ }
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /**
3
+ * NativeModbusTransport — connects to the edge-modbus Go binary over TCP.
4
+ *
5
+ * The Go binary listens on a local TCP socket (default 127.0.0.1:15002)
6
+ * and accepts newline-delimited JSON requests. This class implements
7
+ * ModbusTransportPort by forwarding all calls to the Go process.
8
+ *
9
+ * Usage:
10
+ * const transport = new NativeModbusTransport({ host: '127.0.0.1', port: 15002 });
11
+ * await transport.connect();
12
+ * const response = await transport.sendFrame(requestFrame);
13
+ * transport.disconnect();
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.NativeModbusTransport = void 0;
50
+ let requestCounter = 0;
51
+ class NativeModbusTransport {
52
+ constructor(config = {}) {
53
+ this.socket = null;
54
+ this.buffer = '';
55
+ this.pending = new Map();
56
+ this.frameHandlers = [];
57
+ this.errorHandlers = [];
58
+ this.config = {
59
+ host: config.host ?? '127.0.0.1',
60
+ port: config.port ?? 15002,
61
+ connectTimeoutMs: config.connectTimeoutMs ?? 5000,
62
+ requestTimeoutMs: config.requestTimeoutMs ?? 10000,
63
+ };
64
+ }
65
+ async connect() {
66
+ const net = await Promise.resolve().then(() => __importStar(require('net')));
67
+ return new Promise((resolve, reject) => {
68
+ const timer = setTimeout(() => {
69
+ this.socket?.destroy();
70
+ reject(new Error(`NativeModbusTransport: connect timeout (${this.config.connectTimeoutMs}ms)`));
71
+ }, this.config.connectTimeoutMs);
72
+ this.socket = net.createConnection({ host: this.config.host, port: this.config.port }, () => {
73
+ clearTimeout(timer);
74
+ resolve();
75
+ });
76
+ this.socket.on('data', (chunk) => {
77
+ this.buffer += chunk.toString('utf-8');
78
+ const lines = this.buffer.split('\n');
79
+ this.buffer = lines.pop() ?? '';
80
+ for (const line of lines) {
81
+ if (!line.trim())
82
+ continue;
83
+ try {
84
+ const msg = JSON.parse(line);
85
+ if (msg.id && this.pending.has(msg.id)) {
86
+ const { resolve: res, reject: rej } = this.pending.get(msg.id);
87
+ this.pending.delete(msg.id);
88
+ if (msg.ok && msg.frame) {
89
+ res(hexToBytes(msg.frame));
90
+ }
91
+ else {
92
+ rej(new Error(msg.error ?? 'unknown error'));
93
+ }
94
+ }
95
+ }
96
+ catch {
97
+ // ignore parse errors on partial lines
98
+ }
99
+ }
100
+ });
101
+ this.socket.on('error', (err) => {
102
+ for (const handler of this.errorHandlers) {
103
+ try {
104
+ handler(err);
105
+ }
106
+ catch { }
107
+ }
108
+ });
109
+ this.socket.on('close', () => {
110
+ for (const [, pending] of this.pending) {
111
+ pending.reject(new Error('NativeModbusTransport: connection closed'));
112
+ }
113
+ this.pending.clear();
114
+ });
115
+ });
116
+ }
117
+ async disconnect() {
118
+ if (this.socket) {
119
+ this.socket.destroy();
120
+ this.socket = null;
121
+ }
122
+ }
123
+ async sendFrame(frame) {
124
+ if (!this.socket) {
125
+ throw new Error('NativeModbusTransport: not connected');
126
+ }
127
+ const id = `modbus-${++requestCounter}`;
128
+ const request = { id, frame: bytesToHex(frame) };
129
+ return new Promise((resolve, reject) => {
130
+ const timer = setTimeout(() => {
131
+ this.pending.delete(id);
132
+ reject(new Error(`NativeModbusTransport: request ${id} timed out (${this.config.requestTimeoutMs}ms)`));
133
+ }, this.config.requestTimeoutMs);
134
+ this.pending.set(id, {
135
+ resolve: (data) => { clearTimeout(timer); resolve(data); },
136
+ reject: (err) => { clearTimeout(timer); reject(err); },
137
+ });
138
+ this.socket.write(JSON.stringify(request) + '\n');
139
+ });
140
+ }
141
+ onFrame(handler) {
142
+ this.frameHandlers.push(handler);
143
+ return () => {
144
+ this.frameHandlers = this.frameHandlers.filter(h => h !== handler);
145
+ };
146
+ }
147
+ onError(handler) {
148
+ this.errorHandlers.push(handler);
149
+ return () => {
150
+ this.errorHandlers = this.errorHandlers.filter(h => h !== handler);
151
+ };
152
+ }
153
+ }
154
+ exports.NativeModbusTransport = NativeModbusTransport;
155
+ function bytesToHex(bytes) {
156
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
157
+ }
158
+ function hexToBytes(hex) {
159
+ if (hex.length % 2 !== 0)
160
+ throw new Error('Invalid hex string');
161
+ const arr = new Uint8Array(hex.length / 2);
162
+ for (let i = 0; i < arr.length; i++) {
163
+ arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
164
+ }
165
+ return arr;
166
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Modbus Sensor Bridge — maps Modbus register reads to Edge proof inputs.
3
+ *
4
+ * Shares gateway validation so malformed responses never become sensor proofs.
5
+ */
6
+ import type { EdgeRuntime } from '@totemsdk/edge';
7
+ import type { ModbusTransportPort } from './transport.js';
8
+ import type { ModbusGateway } from './gateway.js';
9
+ export interface ModbusSensorBinding {
10
+ sensorId: string;
11
+ unitId: number;
12
+ functionCode: number;
13
+ address: number;
14
+ count: number;
15
+ intervalMs: number;
16
+ dataType: 'coil' | 'register' | 'input';
17
+ unit?: string;
18
+ }
19
+ export interface ModbusSensorBridgeConfig {
20
+ runtime: EdgeRuntime;
21
+ transport: ModbusTransportPort;
22
+ gateway?: ModbusGateway;
23
+ bindings: ModbusSensorBinding[];
24
+ }
25
+ export interface ModbusSensorBridge {
26
+ start(): Promise<void>;
27
+ stop(): Promise<void>;
28
+ poll(): Promise<void>;
29
+ }
30
+ export declare function createModbusSensorBridge(config: ModbusSensorBridgeConfig): ModbusSensorBridge;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Modbus Sensor Bridge — maps Modbus register reads to Edge proof inputs.
4
+ *
5
+ * Shares gateway validation so malformed responses never become sensor proofs.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createModbusSensorBridge = createModbusSensorBridge;
9
+ const gateway_js_1 = require("./gateway.js");
10
+ function createModbusSensorBridge(config) {
11
+ const timers = [];
12
+ let running = false;
13
+ const gateway = config.gateway ?? (0, gateway_js_1.createModbusGateway)({ runtime: config.runtime, transport: config.transport });
14
+ async function pollBinding(binding) {
15
+ try {
16
+ const args = [binding.unitId, binding.address, binding.count];
17
+ const result = binding.functionCode === 1 ? await gateway.readCoils(...args)
18
+ : binding.functionCode === 2 ? await gateway.readDiscreteInputs(...args)
19
+ : binding.functionCode === 3 ? await gateway.readHoldingRegisters(...args)
20
+ : binding.functionCode === 4 ? await gateway.readInputRegisters(...args) : undefined;
21
+ if (!result?.ok || !result.data)
22
+ return;
23
+ const values = result.data.values;
24
+ if (config.runtime.ports.proof) {
25
+ await config.runtime.ports.proof.createProof({
26
+ subject: `sensor:${binding.sensorId}`,
27
+ claims: [{
28
+ sensorId: binding.sensorId,
29
+ unitId: binding.unitId,
30
+ address: binding.address,
31
+ values,
32
+ unit: binding.unit,
33
+ timestamp: Date.now(),
34
+ }],
35
+ });
36
+ }
37
+ }
38
+ catch {
39
+ // Poll failures are non-fatal — next cycle retries
40
+ }
41
+ }
42
+ return {
43
+ async start() {
44
+ if (running)
45
+ return;
46
+ for (const binding of config.bindings) {
47
+ await pollBinding(binding);
48
+ timers.push(setInterval(() => pollBinding(binding), binding.intervalMs));
49
+ }
50
+ running = true;
51
+ },
52
+ async stop() {
53
+ for (const t of timers)
54
+ clearInterval(t);
55
+ timers.length = 0;
56
+ running = false;
57
+ },
58
+ async poll() {
59
+ for (const binding of config.bindings) {
60
+ await pollBinding(binding);
61
+ }
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Modbus transport port — injected by the caller.
3
+ *
4
+ * Supports Modbus TCP (port 502) and Modbus RTU (serial).
5
+ * The caller provides the actual socket/serial implementation.
6
+ */
7
+ export interface ModbusTransportPort {
8
+ /** Open the connection. */
9
+ connect(): Promise<void>;
10
+ /** Close the connection. */
11
+ disconnect(): Promise<void>;
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
+ */
19
+ sendFrame(frame: Uint8Array): Promise<Uint8Array>;
20
+ /** Register a handler for unsolicited/inbound frames. */
21
+ onFrame(handler: (frame: Uint8Array) => void): () => void;
22
+ /** Register a handler for connection errors. */
23
+ onError(handler: (err: Error) => void): () => void;
24
+ }
25
+ export interface ModbusMessage {
26
+ /** Unit/slave ID (1-247). */
27
+ unitId: number;
28
+ /** Modbus function code (1-6, 15, 16). */
29
+ functionCode: number;
30
+ /** Starting address (0-based). */
31
+ address: number;
32
+ /** Register/coil count or value. */
33
+ value: number | number[];
34
+ /** Raw frame bytes. */
35
+ raw: Uint8Array;
36
+ /** Timestamp of receipt. */
37
+ receivedAt: number;
38
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Modbus transport port — injected by the caller.
4
+ *
5
+ * Supports Modbus TCP (port 502) and Modbus RTU (serial).
6
+ * The caller provides the actual socket/serial implementation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/gateway.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Modbus Edge Gateway — wires a ModbusTransportPort into an EdgeRuntime.
3
3
  *
4
- * Supports Modbus RTU frame format (no MBAP header) to match the Go native binary.
5
- * The Go binary parses frame[0] as unitId and frame[1] as functionCode, which
6
- * is correct for RTU but would misparse TCP frames.
4
+ * Uses a unit-prefixed PDU (no CRC or MBAP header) at the transport boundary.
5
+ * The binding owns TCP transaction framing or RTU CRC validation.
7
6
  */
8
7
  import type { EdgeRuntime, EdgeOperationResult } from '@totemsdk/edge';
9
8
  import type { ModbusTransportPort } from './transport.js';
package/dist/gateway.js CHANGED
@@ -1,14 +1,10 @@
1
- "use strict";
2
1
  /**
3
2
  * Modbus Edge Gateway — wires a ModbusTransportPort into an EdgeRuntime.
4
3
  *
5
- * Supports Modbus RTU frame format (no MBAP header) to match the Go native binary.
6
- * The Go binary parses frame[0] as unitId and frame[1] as functionCode, which
7
- * is correct for RTU but would misparse TCP frames.
4
+ * Uses a unit-prefixed PDU (no CRC or MBAP header) at the transport boundary.
5
+ * The binding owns TCP transaction framing or RTU CRC validation.
8
6
  */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.createModbusGateway = createModbusGateway;
11
- function createModbusGateway(config) {
7
+ export function createModbusGateway(config) {
12
8
  let status = 'stopped';
13
9
  let unsubFrame;
14
10
  let unsubError;
@@ -45,6 +41,8 @@ function createModbusGateway(config) {
45
41
  return readRegisters(config.transport, unitId, 4, address, count);
46
42
  },
47
43
  async writeSingleCoil(unitId, address, value) {
44
+ if (typeof value !== 'boolean')
45
+ return { ok: false, error: 'Coil value must be boolean', errorCode: 'MODBUS_INVALID_REQUEST' };
48
46
  return writeSingle(config.transport, unitId, 5, address, value ? 0xFF00 : 0x0000);
49
47
  },
50
48
  async writeSingleRegister(unitId, address, value) {
@@ -58,29 +56,48 @@ function createModbusGateway(config) {
58
56
  },
59
57
  };
60
58
  }
61
- // ── RTU frame builders (no MBAP header — matches Go binary) ──────────────
59
+ // ── Unit-prefixed PDU builders (no CRC or MBAP header) ───────────────────
60
+ class ModbusRequestError extends Error {
61
+ }
62
+ class ModbusExceptionError extends Error {
63
+ }
64
+ function validateRange(unitId, address, count, maximum) {
65
+ if (!Number.isInteger(unitId) || unitId < 1 || unitId > 247 ||
66
+ !Number.isInteger(address) || address < 0 || address > 0xffff ||
67
+ !Number.isInteger(count) || count < 1 || count > maximum || address + count > 0x10000) {
68
+ throw new ModbusRequestError('Invalid Modbus unit, address or quantity');
69
+ }
70
+ }
71
+ function validateRegister(value) {
72
+ if (!Number.isInteger(value) || value < 0 || value > 0xffff) {
73
+ throw new ModbusRequestError('Register value must be an unsigned 16-bit integer');
74
+ }
75
+ }
62
76
  function buildReadFrame(unitId, fc, address, count) {
63
- const buf = new Uint8Array(8);
77
+ validateRange(unitId, address, count, fc <= 2 ? 2000 : 125);
78
+ const buf = new Uint8Array(6);
64
79
  buf[0] = unitId;
65
80
  buf[1] = fc;
66
81
  new DataView(buf.buffer).setUint16(2, address, false);
67
82
  new DataView(buf.buffer).setUint16(4, count, false);
68
- // CRC placeholder (bytes 6-7) — Go library handles CRC internally
69
- buf[6] = 0;
70
- buf[7] = 0;
71
83
  return buf;
72
84
  }
73
85
  function buildWriteSingleFrame(unitId, fc, address, value) {
74
- const buf = new Uint8Array(8);
86
+ validateRange(unitId, address, 1, 1);
87
+ validateRegister(value);
88
+ const buf = new Uint8Array(6);
75
89
  buf[0] = unitId;
76
90
  buf[1] = fc;
77
91
  new DataView(buf.buffer).setUint16(2, address, false);
78
92
  new DataView(buf.buffer).setUint16(4, value, false);
79
- buf[6] = 0;
80
- buf[7] = 0;
81
93
  return buf;
82
94
  }
83
95
  function buildWriteMultipleCoilsFrame(unitId, address, values) {
96
+ validateRange(unitId, address, values.length, 1968);
97
+ for (const value of values) {
98
+ if (typeof value !== 'boolean')
99
+ throw new ModbusRequestError('Coil values must be boolean');
100
+ }
84
101
  const byteCount = Math.ceil(values.length / 8);
85
102
  const buf = new Uint8Array(7 + byteCount);
86
103
  buf[0] = unitId;
@@ -96,6 +113,9 @@ function buildWriteMultipleCoilsFrame(unitId, address, values) {
96
113
  return buf;
97
114
  }
98
115
  function buildWriteMultipleRegistersFrame(unitId, address, values) {
116
+ validateRange(unitId, address, values.length, 123);
117
+ for (const value of values)
118
+ validateRegister(value);
99
119
  const byteCount = values.length * 2;
100
120
  const buf = new Uint8Array(7 + byteCount);
101
121
  buf[0] = unitId;
@@ -109,26 +129,45 @@ function buildWriteMultipleRegistersFrame(unitId, address, values) {
109
129
  return buf;
110
130
  }
111
131
  // ── Response parsers (RTU format — no MBAP header) ────────────────────────
112
- function checkException(response) {
113
- if (response.length < 2)
114
- return;
132
+ function checkResponse(response, unitId, expectedFunction) {
133
+ if (response.length < 2 || response[0] !== unitId)
134
+ throw new Error('Modbus response unit/length mismatch');
115
135
  const fc = response[1];
116
- if (fc & 0x80) {
117
- const exceptionCode = response.length >= 3 ? response[2] : 0;
118
- throw new Error(`Modbus exception: function=${fc & 0x7F} code=${exceptionCode}`);
136
+ if (fc === (expectedFunction | 0x80)) {
137
+ if (response.length !== 3 || response[2] === 0)
138
+ throw new Error('Malformed Modbus exception response');
139
+ throw new ModbusExceptionError(`Modbus exception: function=${expectedFunction} code=${response[2]}`);
119
140
  }
141
+ if (fc !== expectedFunction)
142
+ throw new Error('Modbus response function mismatch');
143
+ }
144
+ function checkReadLength(response, byteCount) {
145
+ if (response.length !== 3 + byteCount || response[2] !== byteCount) {
146
+ throw new Error('Modbus response byte count/length mismatch');
147
+ }
148
+ }
149
+ function checkWriteResponse(response, request) {
150
+ checkResponse(response, request[0], request[1]);
151
+ if (response.length !== 6 || response.some((byte, index) => byte !== request[index])) {
152
+ throw new Error('Modbus write acknowledgement mismatch');
153
+ }
154
+ }
155
+ function writeFailure(error) {
156
+ return { ok: false, error: String(error), errorCode: error instanceof ModbusRequestError
157
+ ? 'MODBUS_INVALID_REQUEST' : error instanceof ModbusExceptionError
158
+ ? 'MODBUS_EXCEPTION' : 'MODBUS_WRITE_OUTCOME_UNKNOWN' };
120
159
  }
121
160
  function parseBitResponse(response, count) {
122
- checkException(response);
161
+ checkReadLength(response, Math.ceil(count / 8));
123
162
  const values = [];
124
163
  for (let i = 0; i < count; i++) {
125
- const byte = response[3 + Math.floor(i / 8)] ?? 0;
164
+ const byte = response[3 + Math.floor(i / 8)];
126
165
  values.push(((byte >> (i % 8)) & 1) === 1);
127
166
  }
128
167
  return values;
129
168
  }
130
169
  function parseRegisterResponse(response, count) {
131
- checkException(response);
170
+ checkReadLength(response, count * 2);
132
171
  const values = [];
133
172
  for (let i = 0; i < count; i++) {
134
173
  values.push(new DataView(response.buffer, response.byteOffset + 3 + i * 2, 2).getUint16(0, false));
@@ -140,6 +179,7 @@ async function readBits(transport, unitId, fc, address, count) {
140
179
  try {
141
180
  const request = buildReadFrame(unitId, fc, address, count);
142
181
  const response = await transport.sendFrame(request);
182
+ checkResponse(response, unitId, fc);
143
183
  return { ok: true, data: { values: parseBitResponse(response, count) } };
144
184
  }
145
185
  catch (e) {
@@ -150,6 +190,7 @@ async function readRegisters(transport, unitId, fc, address, count) {
150
190
  try {
151
191
  const request = buildReadFrame(unitId, fc, address, count);
152
192
  const response = await transport.sendFrame(request);
193
+ checkResponse(response, unitId, fc);
153
194
  return { ok: true, data: { values: parseRegisterResponse(response, count) } };
154
195
  }
155
196
  catch (e) {
@@ -160,32 +201,32 @@ async function writeSingle(transport, unitId, fc, address, value) {
160
201
  try {
161
202
  const request = buildWriteSingleFrame(unitId, fc, address, value);
162
203
  const response = await transport.sendFrame(request);
163
- checkException(response);
204
+ checkWriteResponse(response, request);
164
205
  return { ok: true };
165
206
  }
166
207
  catch (e) {
167
- return { ok: false, error: String(e) };
208
+ return writeFailure(e);
168
209
  }
169
210
  }
170
211
  async function writeMultipleCoilsImpl(transport, unitId, address, values) {
171
212
  try {
172
213
  const request = buildWriteMultipleCoilsFrame(unitId, address, values);
173
214
  const response = await transport.sendFrame(request);
174
- checkException(response);
215
+ checkWriteResponse(response, request);
175
216
  return { ok: true };
176
217
  }
177
218
  catch (e) {
178
- return { ok: false, error: String(e) };
219
+ return writeFailure(e);
179
220
  }
180
221
  }
181
222
  async function writeMultipleRegistersImpl(transport, unitId, address, values) {
182
223
  try {
183
224
  const request = buildWriteMultipleRegistersFrame(unitId, address, values);
184
225
  const response = await transport.sendFrame(request);
185
- checkException(response);
226
+ checkWriteResponse(response, request);
186
227
  return { ok: true };
187
228
  }
188
229
  catch (e) {
189
- return { ok: false, error: String(e) };
230
+ return writeFailure(e);
190
231
  }
191
232
  }
package/dist/index.js CHANGED
@@ -1,9 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NativeModbusTransport = exports.createModbusSensorBridge = exports.createModbusGateway = void 0;
4
- var gateway_js_1 = require("./gateway.js");
5
- Object.defineProperty(exports, "createModbusGateway", { enumerable: true, get: function () { return gateway_js_1.createModbusGateway; } });
6
- var sensor_bridge_js_1 = require("./sensor-bridge.js");
7
- Object.defineProperty(exports, "createModbusSensorBridge", { enumerable: true, get: function () { return sensor_bridge_js_1.createModbusSensorBridge; } });
8
- var native_transport_js_1 = require("./native-transport.js");
9
- Object.defineProperty(exports, "NativeModbusTransport", { enumerable: true, get: function () { return native_transport_js_1.NativeModbusTransport; } });
1
+ export { createModbusGateway } from './gateway.js';
2
+ export { createModbusSensorBridge } from './sensor-bridge.js';
3
+ export { NativeModbusTransport } from './native-transport.js';
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * NativeModbusTransport — connects to the edge-modbus Go binary over TCP.
4
3
  *
@@ -12,10 +11,8 @@
12
11
  * const response = await transport.sendFrame(requestFrame);
13
12
  * transport.disconnect();
14
13
  */
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.NativeModbusTransport = void 0;
17
14
  let requestCounter = 0;
18
- class NativeModbusTransport {
15
+ export class NativeModbusTransport {
19
16
  constructor(config = {}) {
20
17
  this.socket = null;
21
18
  this.buffer = '';
@@ -118,7 +115,6 @@ class NativeModbusTransport {
118
115
  };
119
116
  }
120
117
  }
121
- exports.NativeModbusTransport = NativeModbusTransport;
122
118
  function bytesToHex(bytes) {
123
119
  return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
124
120
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Modbus Sensor Bridge — maps Modbus register reads to Edge proof inputs.
3
3
  *
4
- * Uses RTU frame format (no MBAP header) to match the Go native binary.
4
+ * Shares gateway validation so malformed responses never become sensor proofs.
5
5
  */
6
6
  import type { EdgeRuntime } from '@totemsdk/edge';
7
7
  import type { ModbusTransportPort } from './transport.js';