ts-communication 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # ts-communication
2
+
3
+ 面向 Node.js 和 TypeScript 的工业 PLC 通讯库,提供统一的异步连接、读取、写入和在线监测 API。
4
+
5
+ ## 支持的设备与协议
6
+
7
+ - Siemens S7:S7-200、S7-200 Smart、S7-300、S7-400、S7-1200、S7-1500
8
+ - Melsec MC:MC 二进制 TCP/UDP 与 ASCII 兼容客户端
9
+ - Omron FINS:FINS TCP/UDP 客户端
10
+ - Inovance:AM、H3U、H5U、Easy、EVO 系列
11
+ - Modbus:Modbus TCP、RTU、RTU over TCP、ASCII、UDP
12
+
13
+ 各协议客户端均按独立类提供,不需要在业务代码中处理底层报文。
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install ts-communication
19
+ ```
20
+
21
+ 使用 Modbus RTU 或串口型客户端时,另外安装串口驱动:
22
+
23
+ ```bash
24
+ npm install serialport
25
+ ```
26
+
27
+ ## 快速开始
28
+
29
+ ### Siemens S7
30
+
31
+ ```ts
32
+ import { SiemensPLCS, SiemensS7Net } from 'ts-communication';
33
+
34
+ const plc = new SiemensS7Net(SiemensPLCS.S1200, '192.168.1.10');
35
+ const connected = await plc.ConnectServer();
36
+
37
+ if (!connected.IsSuccess) {
38
+ throw new Error(connected.Message);
39
+ }
40
+
41
+ const temperature = await plc.ReadFloat('DB1.0');
42
+ if (temperature.IsSuccess) {
43
+ console.log(temperature.Content);
44
+ }
45
+
46
+ const written = await plc.WriteFloat('DB1.0', 25.5);
47
+ console.log(written.IsSuccess, written.Message);
48
+
49
+ await plc.ConnectClose();
50
+ ```
51
+
52
+ 常用 Siemens 地址:`M100`、`M10.2`、`I0.0`、`Q0.0`、`DB1.0`、`DB1.DBD100`、`V100`。
53
+
54
+ ### Melsec MC
55
+
56
+ ```ts
57
+ import { MelsecMcNet } from 'ts-communication';
58
+
59
+ const plc = new MelsecMcNet('192.168.1.20', 6000);
60
+ const connected = await plc.ConnectServer();
61
+ if (!connected.IsSuccess) throw new Error(connected.Message);
62
+
63
+ const result = await plc.ReadInt16('D100');
64
+ if (result.IsSuccess) console.log(result.Content);
65
+
66
+ await plc.WriteInt16('D100', 1234);
67
+ await plc.ConnectClose();
68
+ ```
69
+
70
+ ### Omron FINS
71
+
72
+ ```ts
73
+ import { OmronFinsNet } from 'ts-communication';
74
+
75
+ const plc = new OmronFinsNet('192.168.1.30', 9600);
76
+ const connected = await plc.ConnectServer();
77
+ if (!connected.IsSuccess) throw new Error(connected.Message);
78
+
79
+ const result = await plc.ReadUInt16('D100');
80
+ if (result.IsSuccess) console.log(result.Content);
81
+
82
+ await plc.ConnectClose();
83
+ ```
84
+
85
+ ### Inovance
86
+
87
+ ```ts
88
+ import { InovanceSeries, InovanceTcpNet } from 'ts-communication';
89
+
90
+ const plc = new InovanceTcpNet('192.168.1.40', 502, 1);
91
+ plc.Series = InovanceSeries.AM;
92
+
93
+ const connected = await plc.ConnectServer();
94
+ if (!connected.IsSuccess) throw new Error(connected.Message);
95
+
96
+ const result = await plc.ReadInt32('MD0');
97
+ if (result.IsSuccess) console.log(result.Content);
98
+
99
+ await plc.WriteInt32('MD0', 100);
100
+ await plc.ConnectClose();
101
+ ```
102
+
103
+ 常用汇川地址:`M0`、`MX0.0`、`MW0`、`MD0`、`MB0`、`QX0.0`、`IX0.0`。地址格式会根据 `Series` 自动转换为对应的 Modbus 地址。
104
+
105
+ ### Modbus TCP
106
+
107
+ ```ts
108
+ import { ModbusTcpNet } from 'ts-communication';
109
+
110
+ const plc = new ModbusTcpNet('192.168.1.50', 502, 1);
111
+ const connected = await plc.ConnectServer();
112
+ if (!connected.IsSuccess) throw new Error(connected.Message);
113
+
114
+ const value = await plc.ReadUInt16('100');
115
+ if (value.IsSuccess) console.log(value.Content);
116
+
117
+ await plc.WriteUInt16('100', 88);
118
+ await plc.ConnectClose();
119
+ ```
120
+
121
+ Modbus 地址支持站号和功能码前缀,例如:`s=2;100`、`x=4;100`、`format=ABCD;100`。
122
+
123
+ ## C# 数值类型
124
+
125
+ JavaScript 的 `number` 不区分整数宽度。库提供与 PLC 常用类型对应的显式类型类,写入时可以保留 `byte`、`short`、`ushort`、`int`、`uint`、`float`、`double`、`long` 和 `ulong` 的范围与字节宽度。
126
+
127
+ ```ts
128
+ import { Byte, Float, Int16, UInt16, Int64 } from 'ts-communication';
129
+
130
+ const byteValue = new Byte(255);
131
+ const shortValue = new Int16(-12);
132
+ const unsignedValue = new UInt16(60000);
133
+ const floatValue = new Float(12.5);
134
+ const longValue = new Int64(9007199254740993n);
135
+ ```
136
+
137
+ 客户端也提供直接的强类型读写方法:
138
+
139
+ ```ts
140
+ await plc.ReadInt16('D100');
141
+ await plc.ReadUInt32('D102');
142
+ await plc.ReadFloat('D104');
143
+ await plc.ReadDouble('D106');
144
+ await plc.ReadInt64('D110');
145
+ await plc.WriteFloat('D104', 12.5);
146
+ ```
147
+
148
+ 当传入 `length` 时,读取方法返回数组;不传入时返回单个值。
149
+
150
+ ## 统一结果处理
151
+
152
+ 所有连接、读取和写入操作都返回 `OperateResult`。访问 `Content` 前应先检查 `IsSuccess`:
153
+
154
+ ```ts
155
+ const result = await plc.ReadInt16('D100');
156
+ if (result.IsSuccess) {
157
+ console.log(result.Content);
158
+ } else {
159
+ console.error(result.Message);
160
+ }
161
+ ```
162
+
163
+ ## 长任务与轮询
164
+
165
+ 库客户端可以在 Node.js Worker、Electron Worker 或其他后台线程中长期复用。建议保持一个客户端实例由一个工作线程独占,并在循环中复用连接:
166
+
167
+ ```ts
168
+ while (running) {
169
+ const result = await plc.ReadInt16('D100');
170
+ if (result.IsSuccess) {
171
+ console.log(new Date().toISOString(), result.Content);
172
+ }
173
+ await new Promise(resolve => setTimeout(resolve, 1000));
174
+ }
175
+ ```
176
+
177
+ ## 字节序
178
+
179
+ 客户端公开 `ByteTransform` 和 `DataFormat`,可根据 PLC 配置选择 `ABCD`、`BADC`、`CDAB` 或 `DCBA`。不同协议有各自的默认字节序,数值异常时应先确认 PLC 的字节排列方式。
180
+
181
+ ## TypeScript
182
+
183
+ 库内置 TypeScript 类型声明,可直接用于 TypeScript 项目:
184
+
185
+ ```ts
186
+ import { SiemensS7Net } from 'ts-communication';
187
+ ```
188
+
189
+ ## 注意事项
190
+
191
+ - 使用真实设备前,请确认 PLC IP、端口、站号、机架/槽号以及访问权限。
192
+ - Siemens S7-1200/S7-1500 通常使用 TCP `102` 端口,并需要允许 PUT/GET 访问。
193
+ - 串口协议需要操作系统具有对应串口权限,并正确配置波特率、数据位、停止位和校验位。
194
+ - 所有示例都应检查 `IsSuccess` 后再读取 `Content`。
195
+
196
+ ## License
197
+
198
+ ISC
@@ -0,0 +1,11 @@
1
+ export * from './core/Core';
2
+ export * from './modbus/ModbusTcpNet';
3
+ export * from './modbus/ModbusRtu';
4
+ export * from './modbus/ModbusRtuOverTcp';
5
+ export * from './modbus/ModbusAscii';
6
+ export * from './modbus/ModbusAsciiOverTcp';
7
+ export * from './modbus/ModbusUdpNet';
8
+ export * from './siemens/SiemensS7Net';
9
+ export * from './melsec/MelsecMcNet';
10
+ export * from './omron/OmronFinsNet';
11
+ export * from './inovance/InovanceTcpNet';
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./core/Core"), exports);
18
+ __exportStar(require("./modbus/ModbusTcpNet"), exports);
19
+ __exportStar(require("./modbus/ModbusRtu"), exports);
20
+ __exportStar(require("./modbus/ModbusRtuOverTcp"), exports);
21
+ __exportStar(require("./modbus/ModbusAscii"), exports);
22
+ __exportStar(require("./modbus/ModbusAsciiOverTcp"), exports);
23
+ __exportStar(require("./modbus/ModbusUdpNet"), exports);
24
+ __exportStar(require("./siemens/SiemensS7Net"), exports);
25
+ __exportStar(require("./melsec/MelsecMcNet"), exports);
26
+ __exportStar(require("./omron/OmronFinsNet"), exports);
27
+ __exportStar(require("./inovance/InovanceTcpNet"), exports);
@@ -0,0 +1,95 @@
1
+ import * as net from 'node:net';
2
+ export declare enum DataFormat {
3
+ ABCD = "ABCD",
4
+ BADC = "BADC",
5
+ CDAB = "CDAB",
6
+ DCBA = "DCBA"
7
+ }
8
+ /** Explicit PLC scalar types. JavaScript numbers do not carry the C# width/sign information. */
9
+ export declare abstract class PlcValue<T extends number | bigint> {
10
+ readonly value: T;
11
+ readonly kind: string;
12
+ readonly width: number;
13
+ readonly signed: boolean;
14
+ protected constructor(value: T, kind: string, width: number, signed?: boolean);
15
+ valueOf(): any;
16
+ }
17
+ export declare class Byte extends PlcValue<number> {
18
+ constructor(value: number);
19
+ }
20
+ export declare class Int16 extends PlcValue<number> {
21
+ constructor(value: number);
22
+ }
23
+ export declare class UInt16 extends PlcValue<number> {
24
+ constructor(value: number);
25
+ }
26
+ export declare class Int32 extends PlcValue<number> {
27
+ constructor(value: number);
28
+ }
29
+ export declare class UInt32 extends PlcValue<number> {
30
+ constructor(value: number);
31
+ }
32
+ export declare class Float extends PlcValue<number> {
33
+ constructor(value: number);
34
+ }
35
+ export declare class Double extends PlcValue<number> {
36
+ constructor(value: number);
37
+ }
38
+ export declare class Int64 extends PlcValue<bigint> {
39
+ constructor(value: bigint | number);
40
+ }
41
+ export declare class UInt64 extends PlcValue<bigint> {
42
+ constructor(value: bigint | number);
43
+ }
44
+ export declare class PlcString extends String {
45
+ readonly kind = "string";
46
+ constructor(value: string);
47
+ }
48
+ export declare class OperateResult<T = void> {
49
+ readonly IsSuccess: boolean;
50
+ readonly Content: T;
51
+ readonly Message: string;
52
+ readonly ErrorCode: number;
53
+ constructor(content: T, isSuccess?: boolean, message?: string, errorCode?: number);
54
+ constructor(message: string, errorCode?: number);
55
+ static CreateSuccessResult<T>(content: T): OperateResult<T>;
56
+ static CreateSuccessResult(): OperateResult<void>;
57
+ static CreateFailedResult<T>(resultOrMessage: OperateResult<any> | string, errorCode?: number): OperateResult<T>;
58
+ }
59
+ export declare class ByteTransform {
60
+ IsStringReverseByteWord: boolean;
61
+ DataFormat: DataFormat;
62
+ constructor(format?: DataFormat);
63
+ private order;
64
+ private view;
65
+ private dataView;
66
+ TransInt16(data: Uint8Array, offset?: number): number;
67
+ TransUInt16(data: Uint8Array, offset?: number): number;
68
+ TransInt32(data: Uint8Array, offset?: number): number;
69
+ TransUInt32(data: Uint8Array, offset?: number): number;
70
+ TransSingle(data: Uint8Array, offset?: number): number;
71
+ TransDouble(data: Uint8Array, offset?: number): number;
72
+ TransInt64(data: Uint8Array, offset?: number): bigint;
73
+ TransUInt64(data: Uint8Array, offset?: number): bigint;
74
+ TransByte(value: number | bigint | PlcValue<any> | number[] | bigint[] | PlcValue<any>[], width?: number): Buffer;
75
+ TransString(data: Uint8Array, encoding?: BufferEncoding): string;
76
+ }
77
+ export declare abstract class DeviceClient {
78
+ IpAddress: string;
79
+ Port: number;
80
+ ConnectTimeOut: number;
81
+ ReceiveTimeOut: number;
82
+ AutoReConnect: boolean;
83
+ ByteTransform: ByteTransform;
84
+ ConnectionId: string;
85
+ protected socket?: net.Socket;
86
+ private connecting?;
87
+ constructor(ipAddress?: string, port?: number);
88
+ ConnectServer(): Promise<OperateResult<any>>;
89
+ ConnectClose(): Promise<OperateResult<any>>;
90
+ protected request(frame: Buffer, parser: (data: Buffer) => Buffer): Promise<OperateResult<Buffer>>;
91
+ /** Send one frame on an already connected socket (used by protocol handshakes). */
92
+ protected requestConnected(frame: Buffer, parser: (data: Buffer) => Buffer): Promise<OperateResult<Buffer>>;
93
+ protected fromResult<T>(result: OperateResult<Buffer>, fn: (data: Buffer) => T): OperateResult<T>;
94
+ }
95
+ export declare function wordsFor(type: string, length: number): number;
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DeviceClient = exports.ByteTransform = exports.OperateResult = exports.PlcString = exports.UInt64 = exports.Int64 = exports.Double = exports.Float = exports.UInt32 = exports.Int32 = exports.UInt16 = exports.Int16 = exports.Byte = exports.PlcValue = exports.DataFormat = void 0;
37
+ exports.wordsFor = wordsFor;
38
+ const net = __importStar(require("node:net"));
39
+ var DataFormat;
40
+ (function (DataFormat) {
41
+ DataFormat["ABCD"] = "ABCD";
42
+ DataFormat["BADC"] = "BADC";
43
+ DataFormat["CDAB"] = "CDAB";
44
+ DataFormat["DCBA"] = "DCBA";
45
+ })(DataFormat || (exports.DataFormat = DataFormat = {}));
46
+ /** Explicit PLC scalar types. JavaScript numbers do not carry the C# width/sign information. */
47
+ class PlcValue {
48
+ value;
49
+ kind;
50
+ width;
51
+ signed;
52
+ constructor(value, kind, width, signed = true) {
53
+ this.value = value;
54
+ this.kind = kind;
55
+ this.width = width;
56
+ this.signed = signed;
57
+ }
58
+ valueOf() { return this.value; }
59
+ }
60
+ exports.PlcValue = PlcValue;
61
+ function range(name, value, min, max) { if (!Number.isInteger(value) || value < min || value > max)
62
+ throw new RangeError(`${name} value out of range: ${value}`); }
63
+ class Byte extends PlcValue {
64
+ constructor(value) { range('byte', value, 0, 255); super(value, 'byte', 1, false); }
65
+ }
66
+ exports.Byte = Byte;
67
+ class Int16 extends PlcValue {
68
+ constructor(value) { range('short', value, -32768, 32767); super(value, 'int16', 2, true); }
69
+ }
70
+ exports.Int16 = Int16;
71
+ class UInt16 extends PlcValue {
72
+ constructor(value) { range('ushort', value, 0, 65535); super(value, 'uint16', 2, false); }
73
+ }
74
+ exports.UInt16 = UInt16;
75
+ class Int32 extends PlcValue {
76
+ constructor(value) { range('int', value, -2147483648, 2147483647); super(value, 'int32', 4, true); }
77
+ }
78
+ exports.Int32 = Int32;
79
+ class UInt32 extends PlcValue {
80
+ constructor(value) { range('uint', value, 0, 4294967295); super(value, 'uint32', 4, false); }
81
+ }
82
+ exports.UInt32 = UInt32;
83
+ class Float extends PlcValue {
84
+ constructor(value) { if (!Number.isFinite(value))
85
+ throw new RangeError(`float value is not finite: ${value}`); super(value, 'float', 4, true); }
86
+ }
87
+ exports.Float = Float;
88
+ class Double extends PlcValue {
89
+ constructor(value) { if (!Number.isFinite(value))
90
+ throw new RangeError(`double value is not finite: ${value}`); super(value, 'double', 8, true); }
91
+ }
92
+ exports.Double = Double;
93
+ class Int64 extends PlcValue {
94
+ constructor(value) { const n = BigInt(value); if (n < -9223372036854775808n || n > 9223372036854775807n)
95
+ throw new RangeError(`long value out of range: ${value}`); super(n, 'int64', 8, true); }
96
+ }
97
+ exports.Int64 = Int64;
98
+ class UInt64 extends PlcValue {
99
+ constructor(value) { const n = BigInt(value); if (n < 0n || n > 18446744073709551615n)
100
+ throw new RangeError(`ulong value out of range: ${value}`); super(n, 'uint64', 8, false); }
101
+ }
102
+ exports.UInt64 = UInt64;
103
+ class PlcString extends String {
104
+ kind = 'string';
105
+ constructor(value) { super(value); }
106
+ }
107
+ exports.PlcString = PlcString;
108
+ class OperateResult {
109
+ IsSuccess;
110
+ Content;
111
+ Message;
112
+ ErrorCode;
113
+ constructor(contentOrMessage, isSuccessOrErrorCode = true, message = '', errorCode = 0) {
114
+ if (typeof isSuccessOrErrorCode === 'boolean') {
115
+ this.IsSuccess = isSuccessOrErrorCode;
116
+ this.Content = contentOrMessage;
117
+ this.Message = message;
118
+ this.ErrorCode = errorCode;
119
+ }
120
+ else {
121
+ this.IsSuccess = false;
122
+ this.Content = undefined;
123
+ this.Message = contentOrMessage;
124
+ this.ErrorCode = isSuccessOrErrorCode;
125
+ }
126
+ }
127
+ static CreateSuccessResult(content) { return new OperateResult(content, true, '', 0); }
128
+ static CreateFailedResult(resultOrMessage, errorCode = 10000) {
129
+ return typeof resultOrMessage === 'string'
130
+ ? new OperateResult(undefined, false, resultOrMessage, errorCode)
131
+ : new OperateResult(resultOrMessage.Message, resultOrMessage.ErrorCode);
132
+ }
133
+ }
134
+ exports.OperateResult = OperateResult;
135
+ class ByteTransform {
136
+ IsStringReverseByteWord = false;
137
+ DataFormat;
138
+ constructor(format = DataFormat.CDAB) { this.DataFormat = format; }
139
+ order(bytes, width) {
140
+ if (width === 2)
141
+ return bytes;
142
+ const out = new Uint8Array(bytes.length);
143
+ const map = {
144
+ [DataFormat.ABCD]: [0, 1, 2, 3], [DataFormat.BADC]: [1, 0, 3, 2],
145
+ [DataFormat.CDAB]: [2, 3, 0, 1], [DataFormat.DCBA]: [3, 2, 1, 0]
146
+ };
147
+ const m = map[this.DataFormat];
148
+ for (let i = 0; i < bytes.length; i += width) {
149
+ const chunk = bytes.subarray(i, i + width);
150
+ if (width === 4)
151
+ for (let j = 0; j < 4; j++)
152
+ out[i + j] = chunk[m[j]] ?? 0;
153
+ else if (width === 8) {
154
+ const words = [chunk.subarray(0, 4), chunk.subarray(4, 8)];
155
+ const first = this.DataFormat === 'CDAB' || this.DataFormat === 'DCBA' ? words[1] : words[0];
156
+ const second = first === words[0] ? words[1] : words[0];
157
+ const a = this.order(first, 4), b = this.order(second, 4);
158
+ out.set(a, i);
159
+ out.set(b, i + 4);
160
+ }
161
+ else
162
+ out.set(chunk, i);
163
+ }
164
+ return out;
165
+ }
166
+ view(data, offset, length) { return this.order(data.subarray(offset, offset + length), length); }
167
+ dataView(data, offset, length) {
168
+ const view = this.view(data, offset, length);
169
+ // Buffer.subarray shares an ArrayBuffer; preserve its byteOffset.
170
+ return new DataView(view.buffer, view.byteOffset, view.byteLength);
171
+ }
172
+ TransInt16(data, offset = 0) { return this.dataView(data, offset, 2).getInt16(0, false); }
173
+ TransUInt16(data, offset = 0) { return this.dataView(data, offset, 2).getUint16(0, false); }
174
+ TransInt32(data, offset = 0) { return this.dataView(data, offset, 4).getInt32(0, false); }
175
+ TransUInt32(data, offset = 0) { return this.dataView(data, offset, 4).getUint32(0, false); }
176
+ TransSingle(data, offset = 0) { return this.dataView(data, offset, 4).getFloat32(0, false); }
177
+ TransDouble(data, offset = 0) { return this.dataView(data, offset, 8).getFloat64(0, false); }
178
+ TransInt64(data, offset = 0) { return this.dataView(data, offset, 8).getBigInt64(0, false); }
179
+ TransUInt64(data, offset = 0) { return this.dataView(data, offset, 8).getBigUint64(0, false); }
180
+ TransByte(value, width) {
181
+ const values = Array.isArray(value) ? value : [value];
182
+ const first = values[0];
183
+ const w = width ?? (first instanceof PlcValue ? first.width : typeof first === 'bigint' ? 8 : 2);
184
+ const out = Buffer.alloc(values.length * w);
185
+ values.forEach((v, i) => {
186
+ const scalar = v instanceof PlcValue ? v.value : v;
187
+ const raw = Buffer.alloc(w);
188
+ const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
189
+ if (w === 1)
190
+ raw[0] = Number(scalar);
191
+ else if (w === 2)
192
+ (v instanceof UInt16 ? view.setUint16 : view.setInt16).call(view, 0, Number(scalar), false);
193
+ else if (w === 4)
194
+ (v instanceof UInt32 ? view.setUint32 : v instanceof Float ? view.setFloat32 : view.setInt32).call(view, 0, Number(scalar), false);
195
+ else if (w === 8)
196
+ (v instanceof UInt64 ? view.setBigUint64 : view.setBigInt64).call(view, 0, BigInt(scalar), false);
197
+ out.set(this.order(raw, w), i * w);
198
+ });
199
+ return out;
200
+ }
201
+ TransString(data, encoding = 'utf8') {
202
+ let raw = Buffer.from(data);
203
+ if (this.IsStringReverseByteWord)
204
+ for (let i = 0; i + 1 < raw.length; i += 2)
205
+ [raw[i], raw[i + 1]] = [raw[i + 1], raw[i]];
206
+ return raw.toString(encoding).replace(/\0+$/, '');
207
+ }
208
+ }
209
+ exports.ByteTransform = ByteTransform;
210
+ class DeviceClient {
211
+ IpAddress = '127.0.0.1';
212
+ Port = 5000;
213
+ ConnectTimeOut = 5000;
214
+ ReceiveTimeOut = 5000;
215
+ AutoReConnect = true;
216
+ ByteTransform = new ByteTransform();
217
+ ConnectionId = '';
218
+ socket;
219
+ connecting;
220
+ constructor(ipAddress, port) { if (ipAddress !== undefined)
221
+ this.IpAddress = ipAddress; if (port !== undefined)
222
+ this.Port = port; this.ConnectionId = `${this.IpAddress}:${this.Port}`; }
223
+ async ConnectServer() {
224
+ if (this.socket && !this.socket.destroyed)
225
+ return OperateResult.CreateSuccessResult();
226
+ if (this.connecting)
227
+ return this.connecting;
228
+ this.connecting = new Promise(resolve => {
229
+ const socket = net.createConnection({ host: this.IpAddress, port: this.Port });
230
+ const timer = setTimeout(() => { socket.destroy(); resolve(new OperateResult('Connection timeout', -1)); }, this.ConnectTimeOut);
231
+ socket.once('connect', () => { clearTimeout(timer); this.socket = socket; resolve(OperateResult.CreateSuccessResult()); });
232
+ socket.once('error', e => { clearTimeout(timer); resolve(new OperateResult(e.message, -1)); });
233
+ }).finally(() => { this.connecting = undefined; });
234
+ return this.connecting;
235
+ }
236
+ async ConnectClose() { this.socket?.destroy(); this.socket = undefined; return OperateResult.CreateSuccessResult(); }
237
+ async request(frame, parser) {
238
+ const connected = await this.ConnectServer();
239
+ if (!connected.IsSuccess)
240
+ return OperateResult.CreateFailedResult(connected);
241
+ return this.requestConnected(frame, parser);
242
+ }
243
+ /** Send one frame on an already connected socket (used by protocol handshakes). */
244
+ async requestConnected(frame, parser) {
245
+ return new Promise(resolve => {
246
+ const socket = this.socket;
247
+ let chunks = [];
248
+ let done = false;
249
+ const finish = (result) => { if (done)
250
+ return; done = true; clearTimeout(timer); socket.off('data', onData); socket.off('error', onError); resolve(result); };
251
+ const onError = (e) => finish(new OperateResult(e.message, -1));
252
+ const onData = (chunk) => { chunks.push(chunk); try {
253
+ const body = parser(Buffer.concat(chunks));
254
+ if (body)
255
+ finish(OperateResult.CreateSuccessResult(body));
256
+ }
257
+ catch { /* wait for remaining frame */ } };
258
+ const timer = setTimeout(() => finish(new OperateResult('Receive timeout', -2)), this.ReceiveTimeOut);
259
+ socket.on('data', onData);
260
+ socket.once('error', onError);
261
+ socket.write(frame);
262
+ });
263
+ }
264
+ fromResult(result, fn) { return result.IsSuccess ? OperateResult.CreateSuccessResult(fn(result.Content)) : OperateResult.CreateFailedResult(result); }
265
+ }
266
+ exports.DeviceClient = DeviceClient;
267
+ function wordsFor(type, length) { return type === 'int32' || type === 'uint32' || type === 'float' ? length * 2 : type === 'int64' || type === 'uint64' || type === 'double' ? length * 4 : length; }
@@ -0,0 +1,23 @@
1
+ import { ModbusTcpNet } from '../modbus/ModbusTcpNet';
2
+ import { OperateResult } from '../core/Core';
3
+ export declare enum InovanceSeries {
4
+ AM = "AM",
5
+ H3U = "H3U",
6
+ H5U = "H5U",
7
+ Easy = "Easy",
8
+ EVO = "EVO"
9
+ }
10
+ export declare function parseInovanceAddress(series: InovanceSeries, address: string, modbusCode: number): OperateResult<string>;
11
+ export declare class InovanceTcpNet extends ModbusTcpNet {
12
+ Series: InovanceSeries;
13
+ constructor(ipAddress?: string, port?: number, station?: number);
14
+ TranslateToModbusAddress(address: string, modbusCode: number): OperateResult<string>;
15
+ Read(address: string, length: number): Promise<any>;
16
+ ReadBool(address: string, length?: number): Promise<any>;
17
+ Write(address: string, value: any): Promise<any>;
18
+ ReadByte(address: string): Promise<OperateResult<any>>;
19
+ }
20
+ export declare class InovanceSerial extends InovanceTcpNet {
21
+ }
22
+ export declare class InovanceSerialOverTcp extends InovanceTcpNet {
23
+ }