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.
- package/LICENSE +15 -0
- package/README.md +246 -14
- package/dist/Invt/InvtModbusTcp.d.ts +88 -0
- package/dist/Invt/InvtModbusTcp.js +255 -0
- package/dist/XCommunication.d.ts +18 -0
- package/dist/XCommunication.js +29 -0
- package/dist/ab/AbEtherNetIp.d.ts +1 -0
- package/dist/ab/AbEtherNetIp.js +2 -0
- package/dist/core/CommunicationLock.d.ts +88 -0
- package/dist/core/CommunicationLock.js +156 -0
- package/dist/core/Core.d.ts +27 -1
- package/dist/core/Core.js +375 -71
- package/dist/core/DeviceSerialPort.d.ts +64 -0
- package/dist/core/DeviceSerialPort.js +283 -0
- package/dist/core/DeviceUdpNet.d.ts +56 -0
- package/dist/core/DeviceUdpNet.js +307 -0
- package/dist/core/INetMessage.d.ts +145 -0
- package/dist/core/INetMessage.js +240 -0
- package/dist/debug/PingTest.d.ts +11 -0
- package/dist/debug/PingTest.js +59 -0
- package/dist/debug/tcpClient.d.ts +11 -0
- package/dist/debug/tcpClient.js +116 -0
- package/dist/debug/tcpServer.d.ts +12 -0
- package/dist/debug/tcpServer.js +102 -0
- package/dist/delta/DeltaTcpNet.d.ts +65 -0
- package/dist/delta/DeltaTcpNet.js +346 -0
- package/dist/ge/GeSRTPNet.d.ts +49 -0
- package/dist/ge/GeSRTPNet.js +337 -0
- package/dist/inovance/InovanceEasyNet.d.ts +8 -0
- package/dist/inovance/InovanceEasyNet.js +103 -0
- package/dist/inovance/InovanceSerial.d.ts +55 -0
- package/dist/inovance/InovanceSerial.js +145 -0
- package/dist/inovance/InovanceSerialOverTcp.d.ts +39 -0
- package/dist/inovance/InovanceSerialOverTcp.js +172 -0
- package/dist/inovance/InovanceTcpNet.d.ts +2 -4
- package/dist/inovance/InovanceTcpNet.js +40 -13
- package/dist/melsec/MelsecMcAsciiNet.d.ts +49 -0
- package/dist/melsec/MelsecMcAsciiNet.js +196 -0
- package/dist/melsec/MelsecMcAsciiUdp.d.ts +54 -0
- package/dist/melsec/MelsecMcAsciiUdp.js +204 -0
- package/dist/melsec/MelsecMcNet.d.ts +1 -8
- package/dist/melsec/MelsecMcNet.js +4 -14
- package/dist/melsec/MelsecMcUdp.d.ts +52 -0
- package/dist/melsec/MelsecMcUdp.js +191 -0
- package/dist/modbus/ModbusAscii.d.ts +24 -3
- package/dist/modbus/ModbusAscii.js +109 -3
- package/dist/modbus/ModbusAsciiOverTcp.d.ts +21 -3
- package/dist/modbus/ModbusAsciiOverTcp.js +67 -3
- package/dist/modbus/ModbusRtu.d.ts +101 -13
- package/dist/modbus/ModbusRtu.js +355 -21
- package/dist/modbus/ModbusRtuOverTcp.d.ts +23 -3
- package/dist/modbus/ModbusRtuOverTcp.js +68 -3
- package/dist/modbus/ModbusTcpNet.d.ts +16 -3
- package/dist/modbus/ModbusTcpNet.js +192 -60
- package/dist/modbus/ModbusUdpNet.d.ts +26 -3
- package/dist/modbus/ModbusUdpNet.js +125 -2
- package/dist/omron/OmronFinsNet.d.ts +1 -2
- package/dist/omron/OmronFinsNet.js +210 -69
- package/dist/omron/OmronFinsUdp.d.ts +41 -0
- package/dist/omron/OmronFinsUdp.js +146 -0
- package/dist/siemens/SiemensS7Net.d.ts +6 -0
- package/dist/siemens/SiemensS7Net.js +45 -6
- package/dist/xinjie/XinJETcpNet.d.ts +18 -0
- package/dist/xinjie/XinJETcpNet.js +106 -0
- package/package.json +16 -3
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { DeviceClient, DataFormat } from '../core/Core';
|
|
2
|
+
import { OperateResult } from '../core/Core';
|
|
3
|
+
import { INetMessage } from '../core/INetMessage';
|
|
4
|
+
/**
|
|
5
|
+
* Modbus ASCII over TCP 客户端
|
|
6
|
+
* TCP 传输 ASCII 帧
|
|
7
|
+
*/
|
|
8
|
+
export declare class ModbusAsciiOverTcp extends DeviceClient {
|
|
9
|
+
Station: number;
|
|
10
|
+
AddressStartWithZero: boolean;
|
|
11
|
+
BroadcastStation: number;
|
|
12
|
+
get DataFormat(): DataFormat;
|
|
13
|
+
set DataFormat(value: DataFormat);
|
|
14
|
+
constructor(ipAddress?: string, port?: number, station?: number);
|
|
15
|
+
protected GetNewNetMessage(): INetMessage;
|
|
16
|
+
private packCommand;
|
|
17
|
+
private unpackResponse;
|
|
18
|
+
protected sendRequest(command: Buffer): Promise<OperateResult<Buffer>>;
|
|
19
|
+
Read(address: string, length: number): Promise<OperateResult<Buffer>>;
|
|
20
|
+
ReadInt16(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
21
|
+
ReadFloat(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
4
22
|
}
|
|
@@ -1,8 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ModbusAsciiOverTcp = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const Core_1 = require("../core/Core");
|
|
5
|
+
const Core_2 = require("../core/Core");
|
|
6
|
+
const INetMessage_1 = require("../core/INetMessage");
|
|
7
|
+
const ModbusAscii_1 = require("./ModbusAscii");
|
|
8
|
+
/**
|
|
9
|
+
* Modbus ASCII over TCP 客户端
|
|
10
|
+
* TCP 传输 ASCII 帧
|
|
11
|
+
*/
|
|
12
|
+
class ModbusAsciiOverTcp extends Core_1.DeviceClient {
|
|
13
|
+
Station = 1;
|
|
14
|
+
AddressStartWithZero = true;
|
|
15
|
+
BroadcastStation = -1;
|
|
16
|
+
get DataFormat() {
|
|
17
|
+
return this.ByteTransform.DataFormat;
|
|
18
|
+
}
|
|
19
|
+
set DataFormat(value) {
|
|
20
|
+
this.ByteTransform.DataFormat = value;
|
|
21
|
+
}
|
|
22
|
+
constructor(ipAddress, port = 502, station = 1) {
|
|
23
|
+
super(ipAddress, port);
|
|
24
|
+
this.Station = station;
|
|
25
|
+
this.ByteTransform.DataFormat = Core_1.DataFormat.CDAB;
|
|
26
|
+
}
|
|
27
|
+
GetNewNetMessage() {
|
|
28
|
+
return new INetMessage_1.ModbusAsciiMessage();
|
|
29
|
+
}
|
|
30
|
+
packCommand(command) {
|
|
31
|
+
const core = Buffer.concat([Buffer.from([this.Station]), command]);
|
|
32
|
+
const lrc = ModbusAscii_1.ModbusAscii.calculateLrc(core);
|
|
33
|
+
const hex = Buffer.concat([core, Buffer.from([lrc])]).toString('hex').toUpperCase();
|
|
34
|
+
return Buffer.from(':' + hex + '\r\n', 'ascii');
|
|
35
|
+
}
|
|
36
|
+
unpackResponse(send, response) {
|
|
37
|
+
return ModbusAscii_1.ModbusAscii.ExtraAsciiResponseContent(send, response, this.BroadcastStation);
|
|
38
|
+
}
|
|
39
|
+
async sendRequest(command) {
|
|
40
|
+
const packed = this.packCommand(command);
|
|
41
|
+
const result = await this.request(packed);
|
|
42
|
+
if (!result.IsSuccess)
|
|
43
|
+
return result;
|
|
44
|
+
return this.unpackResponse(packed, result.Content);
|
|
45
|
+
}
|
|
46
|
+
async Read(address, length) {
|
|
47
|
+
const cmd = Buffer.alloc(5);
|
|
48
|
+
cmd[0] = 3;
|
|
49
|
+
cmd.writeUInt16BE(parseInt(address), 1);
|
|
50
|
+
cmd.writeUInt16BE(length, 3);
|
|
51
|
+
return this.sendRequest(cmd);
|
|
52
|
+
}
|
|
53
|
+
async ReadInt16(address, length = 1) {
|
|
54
|
+
const result = await this.Read(address, length);
|
|
55
|
+
if (!result.IsSuccess)
|
|
56
|
+
return Core_2.OperateResult.CreateFailedResult(result);
|
|
57
|
+
const values = [];
|
|
58
|
+
for (let i = 0; i < length; i++)
|
|
59
|
+
values.push(this.ByteTransform.TransInt16(result.Content, i * 2));
|
|
60
|
+
return Core_2.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
61
|
+
}
|
|
62
|
+
async ReadFloat(address, length = 1) {
|
|
63
|
+
const result = await this.Read(address, length * 2);
|
|
64
|
+
if (!result.IsSuccess)
|
|
65
|
+
return Core_2.OperateResult.CreateFailedResult(result);
|
|
66
|
+
const values = [];
|
|
67
|
+
for (let i = 0; i < length; i++)
|
|
68
|
+
values.push(this.ByteTransform.TransSingle(result.Content, i * 4));
|
|
69
|
+
return Core_2.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
70
|
+
}
|
|
7
71
|
}
|
|
8
72
|
exports.ModbusAsciiOverTcp = ModbusAsciiOverTcp;
|
|
@@ -1,15 +1,103 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { OperateResult } from '../core/Core';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { DeviceSerialPort } from '../core/DeviceSerialPort';
|
|
2
|
+
import { OperateResult, DataFormat } from '../core/Core';
|
|
3
|
+
import { INetMessage } from '../core/INetMessage';
|
|
4
|
+
/**
|
|
5
|
+
* Modbus RTU 串口客户端
|
|
6
|
+
* Modbus-Rtu serial client with CRC16 check
|
|
7
|
+
*/
|
|
8
|
+
export declare class ModbusRtu extends DeviceSerialPort {
|
|
9
|
+
Station: number;
|
|
10
|
+
AddressStartWithZero: boolean;
|
|
11
|
+
get IsStringReverse(): boolean;
|
|
12
|
+
set IsStringReverse(value: boolean);
|
|
13
|
+
StationCheckMatch: boolean;
|
|
14
|
+
Crc16CheckEnable: boolean;
|
|
15
|
+
BroadcastStation: number;
|
|
16
|
+
WordReadBatchLength: number;
|
|
17
|
+
DisableFunctionCode06: boolean;
|
|
18
|
+
EnableWriteMaskCode: boolean;
|
|
19
|
+
get DataFormat(): DataFormat;
|
|
20
|
+
set DataFormat(value: DataFormat);
|
|
11
21
|
constructor(station?: number);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
protected GetNewNetMessage(): INetMessage;
|
|
23
|
+
/**
|
|
24
|
+
* 打包命令:添加站号和 CRC16 校验
|
|
25
|
+
*/
|
|
26
|
+
protected PackCommandWithHeader(command: Buffer): Buffer;
|
|
27
|
+
/**
|
|
28
|
+
* 解包响应:验证 CRC16 和站号
|
|
29
|
+
*/
|
|
30
|
+
protected UnpackResponseContent(send: Buffer, response: Buffer): OperateResult<Buffer>;
|
|
31
|
+
/**
|
|
32
|
+
* CRC16 校验(多项式 0xA001)
|
|
33
|
+
*/
|
|
34
|
+
static crc16(data: Buffer): Buffer;
|
|
35
|
+
/**
|
|
36
|
+
* 验证 CRC16
|
|
37
|
+
*/
|
|
38
|
+
static checkCrc16(data: Buffer): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* 提取 RTU 响应内容
|
|
41
|
+
*/
|
|
42
|
+
static ExtraRtuResponseContent(send: Buffer, response: Buffer, crcCheck?: boolean, broadcastStation?: number): OperateResult<Buffer>;
|
|
43
|
+
/**
|
|
44
|
+
* 提取实际数据
|
|
45
|
+
*/
|
|
46
|
+
static ExtractActualData(core: Buffer): OperateResult<Buffer>;
|
|
47
|
+
/**
|
|
48
|
+
* 获取 Modbus 异常描述
|
|
49
|
+
*/
|
|
50
|
+
static GetModbusExceptionDescription(code: number): string;
|
|
51
|
+
/**
|
|
52
|
+
* 地址解析
|
|
53
|
+
*/
|
|
54
|
+
private addr;
|
|
55
|
+
/**
|
|
56
|
+
* 构建读取命令
|
|
57
|
+
*/
|
|
58
|
+
private buildReadCommand;
|
|
59
|
+
/**
|
|
60
|
+
* 读取字节数据
|
|
61
|
+
*/
|
|
62
|
+
Read(address: string, length: number): Promise<OperateResult<Buffer>>;
|
|
63
|
+
/**
|
|
64
|
+
* 读取线圈
|
|
65
|
+
*/
|
|
66
|
+
ReadCoil(address: string, length?: number): Promise<OperateResult<boolean | boolean[]>>;
|
|
67
|
+
/**
|
|
68
|
+
* 读取离散输入
|
|
69
|
+
*/
|
|
70
|
+
ReadDiscrete(address: string, length?: number): Promise<OperateResult<boolean | boolean[]>>;
|
|
71
|
+
/**
|
|
72
|
+
* 写入单个线圈
|
|
73
|
+
*/
|
|
74
|
+
WriteCoil(address: string, value: boolean): Promise<OperateResult<void>>;
|
|
75
|
+
/**
|
|
76
|
+
* 写入多个线圈
|
|
77
|
+
*/
|
|
78
|
+
WriteCoils(address: string, values: boolean[]): Promise<OperateResult<void>>;
|
|
79
|
+
/**
|
|
80
|
+
* 写入单个寄存器
|
|
81
|
+
*/
|
|
82
|
+
WriteRegister(address: string, value: number): Promise<OperateResult<void>>;
|
|
83
|
+
/**
|
|
84
|
+
* 写入多个寄存器
|
|
85
|
+
*/
|
|
86
|
+
WriteRegisters(address: string, values: number[]): Promise<OperateResult<void>>;
|
|
87
|
+
/**
|
|
88
|
+
* 读取 Int16
|
|
89
|
+
*/
|
|
90
|
+
ReadInt16(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
91
|
+
/**
|
|
92
|
+
* 读取 UInt16
|
|
93
|
+
*/
|
|
94
|
+
ReadUInt16(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
95
|
+
/**
|
|
96
|
+
* 读取 Int32
|
|
97
|
+
*/
|
|
98
|
+
ReadInt32(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
99
|
+
/**
|
|
100
|
+
* 读取 Float
|
|
101
|
+
*/
|
|
102
|
+
ReadFloat(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
15
103
|
}
|
package/dist/modbus/ModbusRtu.js
CHANGED
|
@@ -1,27 +1,361 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ModbusRtu = void 0;
|
|
4
|
-
const
|
|
4
|
+
const DeviceSerialPort_1 = require("../core/DeviceSerialPort");
|
|
5
5
|
const Core_1 = require("../core/Core");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
const INetMessage_1 = require("../core/INetMessage");
|
|
7
|
+
const ModbusTcpNet_1 = require("./ModbusTcpNet");
|
|
8
|
+
/**
|
|
9
|
+
* Modbus RTU 串口客户端
|
|
10
|
+
* Modbus-Rtu serial client with CRC16 check
|
|
11
|
+
*/
|
|
12
|
+
class ModbusRtu extends DeviceSerialPort_1.DeviceSerialPort {
|
|
13
|
+
Station = 1;
|
|
14
|
+
AddressStartWithZero = true;
|
|
15
|
+
get IsStringReverse() {
|
|
16
|
+
return this.ByteTransform.IsStringReverseByteWord;
|
|
17
|
+
}
|
|
18
|
+
set IsStringReverse(value) {
|
|
19
|
+
this.ByteTransform.IsStringReverseByteWord = value;
|
|
20
|
+
}
|
|
21
|
+
StationCheckMatch = true;
|
|
22
|
+
Crc16CheckEnable = true;
|
|
23
|
+
BroadcastStation = -1;
|
|
24
|
+
WordReadBatchLength = 120;
|
|
25
|
+
DisableFunctionCode06 = false;
|
|
26
|
+
EnableWriteMaskCode = true;
|
|
27
|
+
get DataFormat() {
|
|
28
|
+
return this.ByteTransform.DataFormat;
|
|
29
|
+
}
|
|
30
|
+
set DataFormat(value) {
|
|
31
|
+
this.ByteTransform.DataFormat = value;
|
|
32
|
+
}
|
|
33
|
+
constructor(station = 1) {
|
|
34
|
+
super();
|
|
35
|
+
this.Station = station;
|
|
36
|
+
this.ByteTransform.DataFormat = Core_1.DataFormat.CDAB;
|
|
37
|
+
this.ReceiveEmptyDataCount = 8; // RTU 帧间隔检测
|
|
38
|
+
}
|
|
39
|
+
GetNewNetMessage() {
|
|
40
|
+
return new INetMessage_1.ModbusRtuMessage(this.StationCheckMatch);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 打包命令:添加站号和 CRC16 校验
|
|
44
|
+
*/
|
|
45
|
+
PackCommandWithHeader(command) {
|
|
46
|
+
const frame = Buffer.concat([Buffer.from([this.Station]), command]);
|
|
47
|
+
const crc = ModbusRtu.crc16(frame);
|
|
48
|
+
return Buffer.concat([frame, crc]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 解包响应:验证 CRC16 和站号
|
|
52
|
+
*/
|
|
53
|
+
UnpackResponseContent(send, response) {
|
|
54
|
+
return ModbusRtu.ExtraRtuResponseContent(send, response, this.Crc16CheckEnable, this.BroadcastStation);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* CRC16 校验(多项式 0xA001)
|
|
58
|
+
*/
|
|
59
|
+
static crc16(data) {
|
|
60
|
+
let crc = 0xFFFF;
|
|
61
|
+
for (const byte of data) {
|
|
62
|
+
crc ^= byte;
|
|
63
|
+
for (let i = 0; i < 8; i++) {
|
|
64
|
+
if (crc & 1) {
|
|
65
|
+
crc = (crc >>> 1) ^ 0xA001;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
crc = crc >>> 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return Buffer.from([crc & 0xFF, (crc >>> 8) & 0xFF]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 验证 CRC16
|
|
76
|
+
*/
|
|
77
|
+
static checkCrc16(data) {
|
|
78
|
+
if (data.length < 2)
|
|
79
|
+
return false;
|
|
80
|
+
const received = data.subarray(0, data.length - 2);
|
|
81
|
+
const crcReceived = data.subarray(data.length - 2);
|
|
82
|
+
const crcCalculated = ModbusRtu.crc16(received);
|
|
83
|
+
return crcReceived[0] === crcCalculated[0] && crcReceived[1] === crcCalculated[1];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 提取 RTU 响应内容
|
|
87
|
+
*/
|
|
88
|
+
static ExtraRtuResponseContent(send, response, crcCheck = true, broadcastStation = -1) {
|
|
89
|
+
// 广播站不需要响应
|
|
90
|
+
if (broadcastStation >= 0 && send[0] === broadcastStation) {
|
|
91
|
+
return Core_1.OperateResult.CreateSuccessResult(Buffer.alloc(0));
|
|
92
|
+
}
|
|
93
|
+
if (response.length < 5) {
|
|
94
|
+
return new Core_1.OperateResult(`Receive data length too short: ${response.length}`, -1);
|
|
95
|
+
}
|
|
96
|
+
let frame = response;
|
|
97
|
+
// CRC 校验
|
|
98
|
+
if (crcCheck && !ModbusRtu.checkCrc16(frame)) {
|
|
99
|
+
// 尝试去掉第一个字节后再验证(可能有干扰)
|
|
100
|
+
if (frame.length > 1) {
|
|
101
|
+
frame = frame.subarray(1);
|
|
102
|
+
if (!ModbusRtu.checkCrc16(frame)) {
|
|
103
|
+
return new Core_1.OperateResult(`Modbus CRC check failed: ${response.toString('hex')}`, -2);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return new Core_1.OperateResult(`Modbus CRC check failed: ${response.toString('hex')}`, -2);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 去除 CRC
|
|
111
|
+
const dataWithoutCrc = frame.subarray(0, frame.length - 2);
|
|
112
|
+
// 检查站号
|
|
113
|
+
if (send[0] !== dataWithoutCrc[0]) {
|
|
114
|
+
return new Core_1.OperateResult(`Station not match, request: ${send[0]}, response: ${dataWithoutCrc[0]}`, -3);
|
|
115
|
+
}
|
|
116
|
+
// 检查功能码
|
|
117
|
+
const sendFunctionCode = send[1];
|
|
118
|
+
const receiveFunctionCode = dataWithoutCrc[1];
|
|
119
|
+
// 异常响应
|
|
120
|
+
if (receiveFunctionCode === (sendFunctionCode | 0x80)) {
|
|
121
|
+
const exceptionCode = dataWithoutCrc[2];
|
|
122
|
+
return new Core_1.OperateResult(ModbusRtu.GetModbusExceptionDescription(exceptionCode), exceptionCode);
|
|
123
|
+
}
|
|
124
|
+
// 功能码不匹配
|
|
125
|
+
if (sendFunctionCode !== receiveFunctionCode) {
|
|
126
|
+
return new Core_1.OperateResult(`Function code not match: ${sendFunctionCode} != ${receiveFunctionCode}`, -4);
|
|
127
|
+
}
|
|
128
|
+
// 提取数据部分
|
|
129
|
+
return ModbusRtu.ExtractActualData(dataWithoutCrc);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 提取实际数据
|
|
133
|
+
*/
|
|
134
|
+
static ExtractActualData(core) {
|
|
135
|
+
const functionCode = core[1];
|
|
136
|
+
// 读取响应:01/02/03/04
|
|
137
|
+
if (functionCode === 0x01 || functionCode === 0x02 || functionCode === 0x03 || functionCode === 0x04 || functionCode === 0x17) {
|
|
138
|
+
const length = core[2];
|
|
139
|
+
return Core_1.OperateResult.CreateSuccessResult(core.subarray(3, 3 + length));
|
|
140
|
+
}
|
|
141
|
+
// 写入响应:05/06/0F/10/16
|
|
142
|
+
if (functionCode === 0x05 || functionCode === 0x06 || functionCode === 0x0F || functionCode === 0x10 || functionCode === 0x16) {
|
|
143
|
+
return Core_1.OperateResult.CreateSuccessResult(core.subarray(2));
|
|
144
|
+
}
|
|
145
|
+
return Core_1.OperateResult.CreateSuccessResult(core.subarray(2));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 获取 Modbus 异常描述
|
|
149
|
+
*/
|
|
150
|
+
static GetModbusExceptionDescription(code) {
|
|
151
|
+
const descriptions = {
|
|
152
|
+
0x01: 'Illegal Function',
|
|
153
|
+
0x02: 'Illegal Data Address',
|
|
154
|
+
0x03: 'Illegal Data Value',
|
|
155
|
+
0x04: 'Slave Device Failure',
|
|
156
|
+
0x05: 'Acknowledge',
|
|
157
|
+
0x06: 'Slave Device Busy',
|
|
158
|
+
0x08: 'Memory Parity Error',
|
|
159
|
+
0x0A: 'Gateway Path Unavailable',
|
|
160
|
+
0x0B: 'Gateway Target Device Failed to Respond'
|
|
161
|
+
};
|
|
162
|
+
return descriptions[code] || `Unknown Exception: 0x${code.toString(16).toUpperCase()}`;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 地址解析
|
|
166
|
+
*/
|
|
167
|
+
addr(input, code) {
|
|
168
|
+
const p = (0, ModbusTcpNet_1.parseModbusAddress)(input, this.Station, code);
|
|
169
|
+
if (p.IsSuccess && !this.AddressStartWithZero) {
|
|
170
|
+
p.Content.address--;
|
|
171
|
+
}
|
|
172
|
+
return p;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 构建读取命令
|
|
176
|
+
*/
|
|
177
|
+
buildReadCommand(address, length, functionCode) {
|
|
178
|
+
const a = this.addr(address, functionCode);
|
|
179
|
+
if (!a.IsSuccess)
|
|
180
|
+
return Core_1.OperateResult.CreateFailedResult(a);
|
|
181
|
+
const cmd = Buffer.alloc(5);
|
|
182
|
+
cmd[0] = a.Content.code;
|
|
183
|
+
cmd.writeUInt16BE(a.Content.address, 1);
|
|
184
|
+
cmd.writeUInt16BE(length, 3);
|
|
185
|
+
return Core_1.OperateResult.CreateSuccessResult(cmd);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 读取字节数据
|
|
189
|
+
*/
|
|
190
|
+
async Read(address, length) {
|
|
191
|
+
const cmdResult = this.buildReadCommand(address, length, 3);
|
|
192
|
+
if (!cmdResult.IsSuccess)
|
|
193
|
+
return cmdResult;
|
|
194
|
+
return this.ReadFromCoreServer(cmdResult.Content);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 读取线圈
|
|
198
|
+
*/
|
|
199
|
+
async ReadCoil(address, length = 1) {
|
|
200
|
+
const cmdResult = this.buildReadCommand(address, length, 1);
|
|
201
|
+
if (!cmdResult.IsSuccess)
|
|
202
|
+
return Core_1.OperateResult.CreateFailedResult(cmdResult);
|
|
203
|
+
const result = await this.ReadFromCoreServer(cmdResult.Content);
|
|
204
|
+
if (!result.IsSuccess)
|
|
205
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
206
|
+
const bools = [];
|
|
207
|
+
for (let i = 0; i < length; i++) {
|
|
208
|
+
const byteIndex = Math.floor(i / 8);
|
|
209
|
+
const bitIndex = i % 8;
|
|
210
|
+
bools.push((result.Content[byteIndex] & (1 << bitIndex)) !== 0);
|
|
211
|
+
}
|
|
212
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? bools[0] : bools);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 读取离散输入
|
|
216
|
+
*/
|
|
217
|
+
async ReadDiscrete(address, length = 1) {
|
|
218
|
+
const cmdResult = this.buildReadCommand(address, length, 2);
|
|
219
|
+
if (!cmdResult.IsSuccess)
|
|
220
|
+
return Core_1.OperateResult.CreateFailedResult(cmdResult);
|
|
221
|
+
const result = await this.ReadFromCoreServer(cmdResult.Content);
|
|
222
|
+
if (!result.IsSuccess)
|
|
223
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
224
|
+
const bools = [];
|
|
225
|
+
for (let i = 0; i < length; i++) {
|
|
226
|
+
const byteIndex = Math.floor(i / 8);
|
|
227
|
+
const bitIndex = i % 8;
|
|
228
|
+
bools.push((result.Content[byteIndex] & (1 << bitIndex)) !== 0);
|
|
229
|
+
}
|
|
230
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? bools[0] : bools);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 写入单个线圈
|
|
234
|
+
*/
|
|
235
|
+
async WriteCoil(address, value) {
|
|
236
|
+
const a = this.addr(address, 5);
|
|
237
|
+
if (!a.IsSuccess)
|
|
238
|
+
return Core_1.OperateResult.CreateFailedResult(a);
|
|
239
|
+
const cmd = Buffer.alloc(5);
|
|
240
|
+
cmd[0] = 5;
|
|
241
|
+
cmd.writeUInt16BE(a.Content.address, 1);
|
|
242
|
+
cmd.writeUInt16BE(value ? 0xFF00 : 0x0000, 3);
|
|
243
|
+
const result = await this.ReadFromCoreServer(cmd);
|
|
244
|
+
return result.IsSuccess ? Core_1.OperateResult.CreateSuccessResult() : Core_1.OperateResult.CreateFailedResult(result);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* 写入多个线圈
|
|
248
|
+
*/
|
|
249
|
+
async WriteCoils(address, values) {
|
|
250
|
+
const a = this.addr(address, 15);
|
|
251
|
+
if (!a.IsSuccess)
|
|
252
|
+
return Core_1.OperateResult.CreateFailedResult(a);
|
|
253
|
+
const byteCount = Math.ceil(values.length / 8);
|
|
254
|
+
const data = Buffer.alloc(byteCount);
|
|
255
|
+
for (let i = 0; i < values.length; i++) {
|
|
256
|
+
if (values[i]) {
|
|
257
|
+
const byteIndex = Math.floor(i / 8);
|
|
258
|
+
const bitIndex = i % 8;
|
|
259
|
+
data[byteIndex] |= (1 << bitIndex);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const cmd = Buffer.alloc(6 + byteCount);
|
|
263
|
+
cmd[0] = 15;
|
|
264
|
+
cmd.writeUInt16BE(a.Content.address, 1);
|
|
265
|
+
cmd.writeUInt16BE(values.length, 3);
|
|
266
|
+
cmd[5] = byteCount;
|
|
267
|
+
data.copy(cmd, 6);
|
|
268
|
+
const result = await this.ReadFromCoreServer(cmd);
|
|
269
|
+
return result.IsSuccess ? Core_1.OperateResult.CreateSuccessResult() : Core_1.OperateResult.CreateFailedResult(result);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 写入单个寄存器
|
|
273
|
+
*/
|
|
274
|
+
async WriteRegister(address, value) {
|
|
275
|
+
if (this.DisableFunctionCode06) {
|
|
276
|
+
return this.WriteRegisters(address, [value]);
|
|
277
|
+
}
|
|
278
|
+
const a = this.addr(address, 6);
|
|
279
|
+
if (!a.IsSuccess)
|
|
280
|
+
return Core_1.OperateResult.CreateFailedResult(a);
|
|
281
|
+
const cmd = Buffer.alloc(5);
|
|
282
|
+
cmd[0] = 6;
|
|
283
|
+
cmd.writeUInt16BE(a.Content.address, 1);
|
|
284
|
+
cmd.writeUInt16BE(value & 0xFFFF, 3);
|
|
285
|
+
const result = await this.ReadFromCoreServer(cmd);
|
|
286
|
+
return result.IsSuccess ? Core_1.OperateResult.CreateSuccessResult() : Core_1.OperateResult.CreateFailedResult(result);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 写入多个寄存器
|
|
290
|
+
*/
|
|
291
|
+
async WriteRegisters(address, values) {
|
|
292
|
+
const a = this.addr(address, 16);
|
|
293
|
+
if (!a.IsSuccess)
|
|
294
|
+
return Core_1.OperateResult.CreateFailedResult(a);
|
|
295
|
+
const data = Buffer.alloc(values.length * 2);
|
|
296
|
+
for (let i = 0; i < values.length; i++) {
|
|
297
|
+
data.writeUInt16BE(values[i] & 0xFFFF, i * 2);
|
|
298
|
+
}
|
|
299
|
+
const cmd = Buffer.alloc(6 + data.length);
|
|
300
|
+
cmd[0] = 16;
|
|
301
|
+
cmd.writeUInt16BE(a.Content.address, 1);
|
|
302
|
+
cmd.writeUInt16BE(values.length, 3);
|
|
303
|
+
cmd[5] = data.length;
|
|
304
|
+
data.copy(cmd, 6);
|
|
305
|
+
const result = await this.ReadFromCoreServer(cmd);
|
|
306
|
+
return result.IsSuccess ? Core_1.OperateResult.CreateSuccessResult() : Core_1.OperateResult.CreateFailedResult(result);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* 读取 Int16
|
|
310
|
+
*/
|
|
311
|
+
async ReadInt16(address, length = 1) {
|
|
312
|
+
const result = await this.Read(address, length);
|
|
313
|
+
if (!result.IsSuccess)
|
|
314
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
315
|
+
const values = [];
|
|
316
|
+
for (let i = 0; i < length; i++) {
|
|
317
|
+
values.push(this.ByteTransform.TransInt16(result.Content, i * 2));
|
|
318
|
+
}
|
|
319
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* 读取 UInt16
|
|
323
|
+
*/
|
|
324
|
+
async ReadUInt16(address, length = 1) {
|
|
325
|
+
const result = await this.Read(address, length);
|
|
326
|
+
if (!result.IsSuccess)
|
|
327
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
328
|
+
const values = [];
|
|
329
|
+
for (let i = 0; i < length; i++) {
|
|
330
|
+
values.push(this.ByteTransform.TransUInt16(result.Content, i * 2));
|
|
331
|
+
}
|
|
332
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 读取 Int32
|
|
336
|
+
*/
|
|
337
|
+
async ReadInt32(address, length = 1) {
|
|
338
|
+
const result = await this.Read(address, length * 2);
|
|
339
|
+
if (!result.IsSuccess)
|
|
340
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
341
|
+
const values = [];
|
|
342
|
+
for (let i = 0; i < length; i++) {
|
|
343
|
+
values.push(this.ByteTransform.TransInt32(result.Content, i * 4));
|
|
344
|
+
}
|
|
345
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* 读取 Float
|
|
349
|
+
*/
|
|
350
|
+
async ReadFloat(address, length = 1) {
|
|
351
|
+
const result = await this.Read(address, length * 2);
|
|
352
|
+
if (!result.IsSuccess)
|
|
353
|
+
return Core_1.OperateResult.CreateFailedResult(result);
|
|
354
|
+
const values = [];
|
|
355
|
+
for (let i = 0; i < length; i++) {
|
|
356
|
+
values.push(this.ByteTransform.TransSingle(result.Content, i * 4));
|
|
357
|
+
}
|
|
358
|
+
return Core_1.OperateResult.CreateSuccessResult(length === 1 ? values[0] : values);
|
|
359
|
+
}
|
|
26
360
|
}
|
|
27
361
|
exports.ModbusRtu = ModbusRtu;
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { DeviceClient, DataFormat } from '../core/Core';
|
|
2
|
+
import { OperateResult } from '../core/Core';
|
|
3
|
+
import { INetMessage } from '../core/INetMessage';
|
|
4
|
+
/**
|
|
5
|
+
* Modbus RTU over TCP 客户端
|
|
6
|
+
* TCP 传输 RTU 帧(无 MBAP 头)
|
|
7
|
+
*/
|
|
8
|
+
export declare class ModbusRtuOverTcp extends DeviceClient {
|
|
9
|
+
Station: number;
|
|
10
|
+
AddressStartWithZero: boolean;
|
|
11
|
+
StationCheckMatch: boolean;
|
|
12
|
+
Crc16CheckEnable: boolean;
|
|
13
|
+
BroadcastStation: number;
|
|
14
|
+
get DataFormat(): DataFormat;
|
|
15
|
+
set DataFormat(value: DataFormat);
|
|
16
|
+
constructor(ipAddress?: string, port?: number, station?: number);
|
|
17
|
+
protected GetNewNetMessage(): INetMessage;
|
|
18
|
+
private packCommand;
|
|
19
|
+
private unpackResponse;
|
|
20
|
+
protected sendRequest(command: Buffer): Promise<OperateResult<Buffer>>;
|
|
21
|
+
Read(address: string, length: number): Promise<OperateResult<Buffer>>;
|
|
22
|
+
ReadInt16(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
23
|
+
ReadFloat(address: string, length?: number): Promise<OperateResult<number | number[]>>;
|
|
4
24
|
}
|