ts-communication 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +246 -14
  3. package/dist/Invt/InvtModbusTcp.d.ts +88 -0
  4. package/dist/Invt/InvtModbusTcp.js +255 -0
  5. package/dist/XCommunication.d.ts +18 -0
  6. package/dist/XCommunication.js +29 -0
  7. package/dist/ab/AbEtherNetIp.d.ts +1 -0
  8. package/dist/ab/AbEtherNetIp.js +2 -0
  9. package/dist/core/CommunicationLock.d.ts +88 -0
  10. package/dist/core/CommunicationLock.js +156 -0
  11. package/dist/core/Core.d.ts +27 -1
  12. package/dist/core/Core.js +375 -71
  13. package/dist/core/DeviceSerialPort.d.ts +64 -0
  14. package/dist/core/DeviceSerialPort.js +283 -0
  15. package/dist/core/DeviceUdpNet.d.ts +56 -0
  16. package/dist/core/DeviceUdpNet.js +307 -0
  17. package/dist/core/INetMessage.d.ts +145 -0
  18. package/dist/core/INetMessage.js +240 -0
  19. package/dist/debug/PingTest.d.ts +11 -0
  20. package/dist/debug/PingTest.js +59 -0
  21. package/dist/debug/tcpClient.d.ts +11 -0
  22. package/dist/debug/tcpClient.js +116 -0
  23. package/dist/debug/tcpServer.d.ts +12 -0
  24. package/dist/debug/tcpServer.js +102 -0
  25. package/dist/delta/DeltaTcpNet.d.ts +65 -0
  26. package/dist/delta/DeltaTcpNet.js +346 -0
  27. package/dist/ge/GeSRTPNet.d.ts +49 -0
  28. package/dist/ge/GeSRTPNet.js +337 -0
  29. package/dist/inovance/InovanceEasyNet.d.ts +8 -0
  30. package/dist/inovance/InovanceEasyNet.js +103 -0
  31. package/dist/inovance/InovanceSerial.d.ts +55 -0
  32. package/dist/inovance/InovanceSerial.js +145 -0
  33. package/dist/inovance/InovanceSerialOverTcp.d.ts +39 -0
  34. package/dist/inovance/InovanceSerialOverTcp.js +172 -0
  35. package/dist/inovance/InovanceTcpNet.d.ts +2 -4
  36. package/dist/inovance/InovanceTcpNet.js +40 -13
  37. package/dist/melsec/MelsecMcAsciiNet.d.ts +49 -0
  38. package/dist/melsec/MelsecMcAsciiNet.js +196 -0
  39. package/dist/melsec/MelsecMcAsciiUdp.d.ts +54 -0
  40. package/dist/melsec/MelsecMcAsciiUdp.js +204 -0
  41. package/dist/melsec/MelsecMcNet.d.ts +1 -8
  42. package/dist/melsec/MelsecMcNet.js +4 -14
  43. package/dist/melsec/MelsecMcUdp.d.ts +52 -0
  44. package/dist/melsec/MelsecMcUdp.js +191 -0
  45. package/dist/modbus/ModbusAscii.d.ts +24 -3
  46. package/dist/modbus/ModbusAscii.js +109 -3
  47. package/dist/modbus/ModbusAsciiOverTcp.d.ts +21 -3
  48. package/dist/modbus/ModbusAsciiOverTcp.js +67 -3
  49. package/dist/modbus/ModbusRtu.d.ts +101 -13
  50. package/dist/modbus/ModbusRtu.js +355 -21
  51. package/dist/modbus/ModbusRtuOverTcp.d.ts +23 -3
  52. package/dist/modbus/ModbusRtuOverTcp.js +68 -3
  53. package/dist/modbus/ModbusTcpNet.d.ts +16 -3
  54. package/dist/modbus/ModbusTcpNet.js +192 -60
  55. package/dist/modbus/ModbusUdpNet.d.ts +26 -3
  56. package/dist/modbus/ModbusUdpNet.js +125 -2
  57. package/dist/omron/OmronFinsNet.d.ts +1 -2
  58. package/dist/omron/OmronFinsNet.js +210 -69
  59. package/dist/omron/OmronFinsUdp.d.ts +41 -0
  60. package/dist/omron/OmronFinsUdp.js +146 -0
  61. package/dist/siemens/SiemensS7Net.d.ts +6 -0
  62. package/dist/siemens/SiemensS7Net.js +45 -6
  63. package/dist/xinjie/XinJETcpNet.d.ts +18 -0
  64. package/dist/xinjie/XinJETcpNet.js +106 -0
  65. package/package.json +16 -3
package/dist/core/Core.js CHANGED
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
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
37
  exports.wordsFor = wordsFor;
38
38
  const net = __importStar(require("node:net"));
39
+ const CommunicationLock_1 = require("./CommunicationLock");
39
40
  var DataFormat;
40
41
  (function (DataFormat) {
41
42
  DataFormat["ABCD"] = "ABCD";
@@ -55,54 +56,89 @@ class PlcValue {
55
56
  this.width = width;
56
57
  this.signed = signed;
57
58
  }
58
- valueOf() { return this.value; }
59
+ valueOf() {
60
+ return this.value;
61
+ }
59
62
  }
60
63
  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}`); }
64
+ function range(name, value, min, max) {
65
+ if (!Number.isInteger(value) || value < min || value > max)
66
+ throw new RangeError(`${name} value out of range: ${value}`);
67
+ }
63
68
  class Byte extends PlcValue {
64
- constructor(value) { range('byte', value, 0, 255); super(value, 'byte', 1, false); }
69
+ constructor(value) {
70
+ range('byte', value, 0, 255);
71
+ super(value, 'byte', 1, false);
72
+ }
65
73
  }
66
74
  exports.Byte = Byte;
67
75
  class Int16 extends PlcValue {
68
- constructor(value) { range('short', value, -32768, 32767); super(value, 'int16', 2, true); }
76
+ constructor(value) {
77
+ range('short', value, -32768, 32767);
78
+ super(value, 'int16', 2, true);
79
+ }
69
80
  }
70
81
  exports.Int16 = Int16;
71
82
  class UInt16 extends PlcValue {
72
- constructor(value) { range('ushort', value, 0, 65535); super(value, 'uint16', 2, false); }
83
+ constructor(value) {
84
+ range('ushort', value, 0, 65535);
85
+ super(value, 'uint16', 2, false);
86
+ }
73
87
  }
74
88
  exports.UInt16 = UInt16;
75
89
  class Int32 extends PlcValue {
76
- constructor(value) { range('int', value, -2147483648, 2147483647); super(value, 'int32', 4, true); }
90
+ constructor(value) {
91
+ range('int', value, -2147483648, 2147483647);
92
+ super(value, 'int32', 4, true);
93
+ }
77
94
  }
78
95
  exports.Int32 = Int32;
79
96
  class UInt32 extends PlcValue {
80
- constructor(value) { range('uint', value, 0, 4294967295); super(value, 'uint32', 4, false); }
97
+ constructor(value) {
98
+ range('uint', value, 0, 4294967295);
99
+ super(value, 'uint32', 4, false);
100
+ }
81
101
  }
82
102
  exports.UInt32 = UInt32;
83
103
  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); }
104
+ constructor(value) {
105
+ if (!Number.isFinite(value))
106
+ throw new RangeError(`float value is not finite: ${value}`);
107
+ super(value, 'float', 4, true);
108
+ }
86
109
  }
87
110
  exports.Float = Float;
88
111
  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); }
112
+ constructor(value) {
113
+ if (!Number.isFinite(value))
114
+ throw new RangeError(`double value is not finite: ${value}`);
115
+ super(value, 'double', 8, true);
116
+ }
91
117
  }
92
118
  exports.Double = Double;
93
119
  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); }
120
+ constructor(value) {
121
+ const n = BigInt(value);
122
+ if (n < -9223372036854775808n || n > 9223372036854775807n)
123
+ throw new RangeError(`long value out of range: ${value}`);
124
+ super(n, 'int64', 8, true);
125
+ }
96
126
  }
97
127
  exports.Int64 = Int64;
98
128
  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); }
129
+ constructor(value) {
130
+ const n = BigInt(value);
131
+ if (n < 0n || n > 18446744073709551615n)
132
+ throw new RangeError(`ulong value out of range: ${value}`);
133
+ super(n, 'uint64', 8, false);
134
+ }
101
135
  }
102
136
  exports.UInt64 = UInt64;
103
137
  class PlcString extends String {
104
138
  kind = 'string';
105
- constructor(value) { super(value); }
139
+ constructor(value) {
140
+ super(value);
141
+ }
106
142
  }
107
143
  exports.PlcString = PlcString;
108
144
  class OperateResult {
@@ -124,7 +160,9 @@ class OperateResult {
124
160
  this.ErrorCode = isSuccessOrErrorCode;
125
161
  }
126
162
  }
127
- static CreateSuccessResult(content) { return new OperateResult(content, true, '', 0); }
163
+ static CreateSuccessResult(content) {
164
+ return new OperateResult(content, true, '', 0);
165
+ }
128
166
  static CreateFailedResult(resultOrMessage, errorCode = 10000) {
129
167
  return typeof resultOrMessage === 'string'
130
168
  ? new OperateResult(undefined, false, resultOrMessage, errorCode)
@@ -135,48 +173,146 @@ exports.OperateResult = OperateResult;
135
173
  class ByteTransform {
136
174
  IsStringReverseByteWord = false;
137
175
  DataFormat;
138
- constructor(format = DataFormat.CDAB) { this.DataFormat = format; }
176
+ constructor(format = DataFormat.CDAB) {
177
+ this.DataFormat = format;
178
+ }
179
+ /**
180
+ * 按照 DataFormat 重新排序字节
181
+ * 完全匹配 C# RegularByteTransform 的 ByteTransDataFormat* 方法
182
+ * 转换后的字节将用小端序 DataView 读取
183
+ */
139
184
  order(bytes, width) {
140
- if (width === 2)
141
- return bytes;
142
185
  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
186
  for (let i = 0; i < bytes.length; i += width) {
149
187
  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;
188
+ if (width === 2) {
189
+ // 对应 C# ByteTransDataFormat2
190
+ switch (this.DataFormat) {
191
+ case DataFormat.ABCD:
192
+ case DataFormat.CDAB:
193
+ out[i] = chunk[1];
194
+ out[i + 1] = chunk[0];
195
+ break;
196
+ case DataFormat.BADC:
197
+ case DataFormat.DCBA:
198
+ out[i] = chunk[0];
199
+ out[i + 1] = chunk[1];
200
+ break;
201
+ }
202
+ }
203
+ else if (width === 4) {
204
+ // 对应 C# ByteTransDataFormat4
205
+ switch (this.DataFormat) {
206
+ case DataFormat.ABCD:
207
+ out[i] = chunk[3];
208
+ out[i + 1] = chunk[2];
209
+ out[i + 2] = chunk[1];
210
+ out[i + 3] = chunk[0];
211
+ break;
212
+ case DataFormat.BADC:
213
+ out[i] = chunk[2];
214
+ out[i + 1] = chunk[3];
215
+ out[i + 2] = chunk[0];
216
+ out[i + 3] = chunk[1];
217
+ break;
218
+ case DataFormat.CDAB:
219
+ out[i] = chunk[1];
220
+ out[i + 1] = chunk[0];
221
+ out[i + 2] = chunk[3];
222
+ out[i + 3] = chunk[2];
223
+ break;
224
+ case DataFormat.DCBA:
225
+ out[i] = chunk[0];
226
+ out[i + 1] = chunk[1];
227
+ out[i + 2] = chunk[2];
228
+ out[i + 3] = chunk[3];
229
+ break;
230
+ }
231
+ }
153
232
  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);
233
+ // 对应 C# ByteTransDataFormat8
234
+ switch (this.DataFormat) {
235
+ case DataFormat.ABCD:
236
+ out[i] = chunk[7];
237
+ out[i + 1] = chunk[6];
238
+ out[i + 2] = chunk[5];
239
+ out[i + 3] = chunk[4];
240
+ out[i + 4] = chunk[3];
241
+ out[i + 5] = chunk[2];
242
+ out[i + 6] = chunk[1];
243
+ out[i + 7] = chunk[0];
244
+ break;
245
+ case DataFormat.BADC:
246
+ out[i] = chunk[6];
247
+ out[i + 1] = chunk[7];
248
+ out[i + 2] = chunk[4];
249
+ out[i + 3] = chunk[5];
250
+ out[i + 4] = chunk[2];
251
+ out[i + 5] = chunk[3];
252
+ out[i + 6] = chunk[0];
253
+ out[i + 7] = chunk[1];
254
+ break;
255
+ case DataFormat.CDAB:
256
+ out[i] = chunk[1];
257
+ out[i + 1] = chunk[0];
258
+ out[i + 2] = chunk[3];
259
+ out[i + 3] = chunk[2];
260
+ out[i + 4] = chunk[5];
261
+ out[i + 5] = chunk[4];
262
+ out[i + 6] = chunk[7];
263
+ out[i + 7] = chunk[6];
264
+ break;
265
+ case DataFormat.DCBA:
266
+ out[i] = chunk[0];
267
+ out[i + 1] = chunk[1];
268
+ out[i + 2] = chunk[2];
269
+ out[i + 3] = chunk[3];
270
+ out[i + 4] = chunk[4];
271
+ out[i + 5] = chunk[5];
272
+ out[i + 6] = chunk[6];
273
+ out[i + 7] = chunk[7];
274
+ break;
275
+ }
160
276
  }
161
- else
277
+ else {
278
+ // 其他宽度,直接复制
162
279
  out.set(chunk, i);
280
+ }
163
281
  }
164
282
  return out;
165
283
  }
166
- view(data, offset, length) { return this.order(data.subarray(offset, offset + length), length); }
284
+ view(data, offset, length) {
285
+ return this.order(data.subarray(offset, offset + length), length);
286
+ }
167
287
  dataView(data, offset, length) {
168
288
  const view = this.view(data, offset, length);
169
289
  // Buffer.subarray shares an ArrayBuffer; preserve its byteOffset.
170
290
  return new DataView(view.buffer, view.byteOffset, view.byteLength);
171
291
  }
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); }
292
+ TransInt16(data, offset = 0) {
293
+ return this.dataView(data, offset, 2).getInt16(0, true);
294
+ }
295
+ TransUInt16(data, offset = 0) {
296
+ return this.dataView(data, offset, 2).getUint16(0, true);
297
+ }
298
+ TransInt32(data, offset = 0) {
299
+ return this.dataView(data, offset, 4).getInt32(0, true);
300
+ }
301
+ TransUInt32(data, offset = 0) {
302
+ return this.dataView(data, offset, 4).getUint32(0, true);
303
+ }
304
+ TransSingle(data, offset = 0) {
305
+ return this.dataView(data, offset, 4).getFloat32(0, true);
306
+ }
307
+ TransDouble(data, offset = 0) {
308
+ return this.dataView(data, offset, 8).getFloat64(0, true);
309
+ }
310
+ TransInt64(data, offset = 0) {
311
+ return this.dataView(data, offset, 8).getBigInt64(0, true);
312
+ }
313
+ TransUInt64(data, offset = 0) {
314
+ return this.dataView(data, offset, 8).getBigUint64(0, true);
315
+ }
180
316
  TransByte(value, width) {
181
317
  const values = Array.isArray(value) ? value : [value];
182
318
  const first = values[0];
@@ -189,16 +325,16 @@ class ByteTransform {
189
325
  if (w === 1)
190
326
  raw[0] = Number(scalar);
191
327
  else if (w === 2)
192
- (v instanceof UInt16 ? view.setUint16 : view.setInt16).call(view, 0, Number(scalar), false);
328
+ (v instanceof UInt16 ? view.setUint16 : view.setInt16).call(view, 0, Number(scalar), true);
193
329
  else if (w === 4)
194
- (v instanceof UInt32 ? view.setUint32 : v instanceof Float ? view.setFloat32 : view.setInt32).call(view, 0, Number(scalar), false);
330
+ (v instanceof UInt32 ? view.setUint32 : v instanceof Float ? view.setFloat32 : view.setInt32).call(view, 0, Number(scalar), true);
195
331
  else if (w === 8)
196
- (v instanceof UInt64 ? view.setBigUint64 : view.setBigInt64).call(view, 0, BigInt(scalar), false);
332
+ (v instanceof UInt64 ? view.setBigUint64 : view.setBigInt64).call(view, 0, BigInt(scalar), true);
197
333
  out.set(this.order(raw, w), i * w);
198
334
  });
199
335
  return out;
200
336
  }
201
- TransString(data, encoding = 'utf8') {
337
+ TransString(data, encoding = 'ascii') {
202
338
  let raw = Buffer.from(data);
203
339
  if (this.IsStringReverseByteWord)
204
340
  for (let i = 0; i + 1 < raw.length; i += 2)
@@ -217,9 +353,28 @@ class DeviceClient {
217
353
  ConnectionId = '';
218
354
  socket;
219
355
  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}`; }
356
+ // 通信锁和请求队列
357
+ communicationLock;
358
+ requestQueue;
359
+ // 是否启用通信锁(默认启用)
360
+ EnableCommunicationLock = true;
361
+ constructor(ipAddress, port) {
362
+ if (ipAddress !== undefined)
363
+ this.IpAddress = ipAddress;
364
+ if (port !== undefined)
365
+ this.Port = port;
366
+ this.ConnectionId = `${this.IpAddress}:${this.Port}`;
367
+ // 初始化通信锁和队列
368
+ this.communicationLock = new CommunicationLock_1.CommunicationLockSimple();
369
+ this.requestQueue = new CommunicationLock_1.RequestQueue();
370
+ }
371
+ /**
372
+ * 获取当前协议的消息解析器
373
+ * 子类应该重写此方法以提供特定协议的消息解析器
374
+ */
375
+ GetNewNetMessage() {
376
+ return undefined;
377
+ }
223
378
  async ConnectServer() {
224
379
  if (this.socket && !this.socket.destroyed)
225
380
  return OperateResult.CreateSuccessResult();
@@ -227,18 +382,131 @@ class DeviceClient {
227
382
  return this.connecting;
228
383
  this.connecting = new Promise(resolve => {
229
384
  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; });
385
+ const timer = setTimeout(() => {
386
+ socket.destroy();
387
+ resolve(new OperateResult('Connection timeout', -1));
388
+ }, this.ConnectTimeOut);
389
+ socket.once('connect', () => {
390
+ clearTimeout(timer);
391
+ this.socket = socket;
392
+ resolve(OperateResult.CreateSuccessResult());
393
+ });
394
+ socket.once('error', e => {
395
+ clearTimeout(timer);
396
+ resolve(new OperateResult(e.message, -1));
397
+ });
398
+ }).finally(() => {
399
+ this.connecting = undefined;
400
+ });
234
401
  return this.connecting;
235
402
  }
236
- async ConnectClose() { this.socket?.destroy(); this.socket = undefined; return OperateResult.CreateSuccessResult(); }
403
+ async ConnectClose() {
404
+ this.socket?.destroy();
405
+ this.socket = undefined;
406
+ this.communicationLock.dispose();
407
+ return OperateResult.CreateSuccessResult();
408
+ }
409
+ /**
410
+ * 发送请求并接收响应(带通信锁和队列)
411
+ */
237
412
  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);
413
+ // 加入请求队列
414
+ return this.requestQueue.enqueue(async () => {
415
+ // 进入通信锁
416
+ if (this.EnableCommunicationLock) {
417
+ const lockResult = await this.communicationLock.EnterLock(this.ConnectTimeOut);
418
+ if (!lockResult.IsSuccess) {
419
+ return OperateResult.CreateFailedResult(lockResult);
420
+ }
421
+ }
422
+ try {
423
+ const connected = await this.ConnectServer();
424
+ if (!connected.IsSuccess) {
425
+ return OperateResult.CreateFailedResult(connected);
426
+ }
427
+ // 如果有消息解析器,使用它
428
+ const message = this.GetNewNetMessage();
429
+ if (message && !parser) {
430
+ return await this.requestWithMessage(frame, message);
431
+ }
432
+ // 否则使用传统的 parser 方式
433
+ return await this.requestConnected(frame, parser || ((data) => data));
434
+ }
435
+ finally {
436
+ // 离开通信锁
437
+ if (this.EnableCommunicationLock) {
438
+ this.communicationLock.LeaveLock();
439
+ }
440
+ }
441
+ });
442
+ }
443
+ /**
444
+ * 使用 INetMessage 解析器发送请求
445
+ */
446
+ async requestWithMessage(frame, message) {
447
+ return new Promise(resolve => {
448
+ const socket = this.socket;
449
+ let receiveBuffer = Buffer.alloc(0);
450
+ let done = false;
451
+ message.SendBytes = frame;
452
+ const finish = (result) => {
453
+ if (done)
454
+ return;
455
+ done = true;
456
+ clearTimeout(timer);
457
+ socket.off('data', onData);
458
+ socket.off('error', onError);
459
+ resolve(result);
460
+ };
461
+ const onError = (e) => finish(new OperateResult(e.message, -1));
462
+ const onData = (chunk) => {
463
+ receiveBuffer = Buffer.concat([receiveBuffer, chunk]);
464
+ try {
465
+ // 第一步:接收头部
466
+ if (message.HeadBytes.length < message.ProtocolHeadBytesLength) {
467
+ const headLength = message.ProtocolHeadBytesLength;
468
+ if (receiveBuffer.length >= headLength) {
469
+ message.HeadBytes = receiveBuffer.subarray(0, headLength);
470
+ receiveBuffer = receiveBuffer.subarray(headLength);
471
+ // 检查头部合法性
472
+ if (!message.CheckHeadBytesLegal()) {
473
+ finish(new OperateResult('Invalid message head', -3));
474
+ return;
475
+ }
476
+ }
477
+ else {
478
+ return; // 继续等待
479
+ }
480
+ }
481
+ // 第二步:获取内容长度
482
+ const contentLength = message.GetContentLengthByHeadBytes();
483
+ if (contentLength < 0) {
484
+ // 需要更多数据来确定长度
485
+ return;
486
+ }
487
+ // 第三步:接收内容
488
+ if (receiveBuffer.length >= contentLength) {
489
+ message.ContentBytes = receiveBuffer.subarray(0, contentLength);
490
+ // 检查消息匹配
491
+ const matchResult = message.CheckMessageMatch(frame, Buffer.concat([message.HeadBytes, message.ContentBytes]));
492
+ if (matchResult === 0) {
493
+ finish(new OperateResult('Message match failed', -4));
494
+ }
495
+ else if (matchResult === 1) {
496
+ finish(OperateResult.CreateSuccessResult(message.ContentBytes));
497
+ }
498
+ // matchResult === -1 继续等待
499
+ }
500
+ }
501
+ catch (e) {
502
+ finish(new OperateResult(`Parse error: ${e.message}`, -5));
503
+ }
504
+ };
505
+ const timer = setTimeout(() => finish(new OperateResult('Receive timeout', -2)), this.ReceiveTimeOut);
506
+ socket.on('data', onData);
507
+ socket.once('error', onError);
508
+ socket.write(frame);
509
+ });
242
510
  }
243
511
  /** Send one frame on an already connected socket (used by protocol handshakes). */
244
512
  async requestConnected(frame, parser) {
@@ -246,22 +514,58 @@ class DeviceClient {
246
514
  const socket = this.socket;
247
515
  let chunks = [];
248
516
  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); };
517
+ const finish = (result) => {
518
+ if (done)
519
+ return;
520
+ done = true;
521
+ clearTimeout(timer);
522
+ socket.off('data', onData);
523
+ socket.off('error', onError);
524
+ resolve(result);
525
+ };
251
526
  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 */ } };
527
+ const onData = (chunk) => {
528
+ chunks.push(chunk);
529
+ try {
530
+ const body = parser(Buffer.concat(chunks));
531
+ if (body)
532
+ finish(OperateResult.CreateSuccessResult(body));
533
+ }
534
+ catch { /* wait for remaining frame */
535
+ }
536
+ };
258
537
  const timer = setTimeout(() => finish(new OperateResult('Receive timeout', -2)), this.ReceiveTimeOut);
259
538
  socket.on('data', onData);
260
539
  socket.once('error', onError);
261
540
  socket.write(frame);
262
541
  });
263
542
  }
264
- fromResult(result, fn) { return result.IsSuccess ? OperateResult.CreateSuccessResult(fn(result.Content)) : OperateResult.CreateFailedResult(result); }
543
+ /**
544
+ * 批量发送请求(自动合并结果)
545
+ */
546
+ async ReadFromCoreServer(commands) {
547
+ if (commands.length === 0) {
548
+ return OperateResult.CreateSuccessResult(Buffer.alloc(0));
549
+ }
550
+ if (commands.length === 1) {
551
+ return this.request(commands[0]);
552
+ }
553
+ // 批量发送
554
+ const results = [];
555
+ for (const cmd of commands) {
556
+ const result = await this.request(cmd);
557
+ if (!result.IsSuccess) {
558
+ return result;
559
+ }
560
+ results.push(result.Content);
561
+ }
562
+ return OperateResult.CreateSuccessResult(Buffer.concat(results));
563
+ }
564
+ fromResult(result, fn) {
565
+ return result.IsSuccess ? OperateResult.CreateSuccessResult(fn(result.Content)) : OperateResult.CreateFailedResult(result);
566
+ }
265
567
  }
266
568
  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; }
569
+ function wordsFor(type, length) {
570
+ return type === 'int32' || type === 'uint32' || type === 'float' ? length * 2 : type === 'int64' || type === 'uint64' || type === 'double' ? length * 4 : length;
571
+ }
@@ -0,0 +1,64 @@
1
+ import { OperateResult, ByteTransform } from './Core';
2
+ import { ICommunicationLock, RequestQueue } from './CommunicationLock';
3
+ import { INetMessage } from './INetMessage';
4
+ /**
5
+ * 串口设备基类
6
+ * Base class for serial port devices
7
+ */
8
+ export declare abstract class DeviceSerialPort {
9
+ PortName: string;
10
+ BaudRate: number;
11
+ DataBits: 7 | 8;
12
+ StopBits: 1 | 2;
13
+ Parity: 'none' | 'even' | 'odd' | 'mark' | 'space';
14
+ ReceiveTimeOut: number;
15
+ ConnectTimeOut: number;
16
+ ByteTransform: ByteTransform;
17
+ ConnectionId: string;
18
+ protected communicationLock: ICommunicationLock;
19
+ protected requestQueue: RequestQueue;
20
+ EnableCommunicationLock: boolean;
21
+ private serial?;
22
+ private isOpen;
23
+ ReceiveEmptyDataCount: number;
24
+ constructor();
25
+ /**
26
+ * 获取当前协议的消息解析器
27
+ * 子类应该重写此方法以提供特定协议的消息解析器
28
+ */
29
+ protected abstract GetNewNetMessage(): INetMessage | undefined;
30
+ /**
31
+ * 打包命令(在发送前添加协议头、CRC等)
32
+ * 子类可以重写此方法
33
+ */
34
+ protected PackCommandWithHeader(command: Buffer): Buffer;
35
+ /**
36
+ * 解包响应(验证CRC、去除协议头等)
37
+ * 子类可以重写此方法
38
+ */
39
+ protected UnpackResponseContent(send: Buffer, response: Buffer): OperateResult<Buffer>;
40
+ /**
41
+ * 初始化串口配置
42
+ */
43
+ SerialPortInni(portName: string, baudRate?: number, dataBits?: 7 | 8, stopBits?: 1 | 2, parity?: 'none' | 'even' | 'odd' | 'mark' | 'space'): this;
44
+ /**
45
+ * 打开串口
46
+ */
47
+ Open(): Promise<OperateResult<void>>;
48
+ /**
49
+ * 关闭串口
50
+ */
51
+ Close(): Promise<OperateResult<void>>;
52
+ /**
53
+ * 发送请求并接收响应
54
+ */
55
+ protected ReadFromCoreServer(send: Buffer, hasResponseData?: boolean, usePackAndUnpack?: boolean): Promise<OperateResult<Buffer>>;
56
+ /**
57
+ * 使用 INetMessage 解析器读取数据
58
+ */
59
+ private readWithMessage;
60
+ /**
61
+ * 释放资源
62
+ */
63
+ Dispose(): Promise<void>;
64
+ }