modbus_rtu.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { Socket } from "node:net";
  2. import { Buffer } from "node:buffer";
  3. interface ModbusRTUTestOptions {
  4. ip: string;
  5. port: number;
  6. frameHexStrings?: string[];
  7. }
  8. interface ModbusRTUTestResult {
  9. connected: boolean;
  10. message: string;
  11. response?: string;
  12. error?: string;
  13. }
  14. /**
  15. * Modbus RTU设备连接
  16. * @param options 连接参数 {ip, port, frameHexStrings}
  17. * @returns 连接结果
  18. */
  19. export async function modbusRTUConnection(
  20. options: ModbusRTUTestOptions
  21. ): Promise<ModbusRTUTestResult> {
  22. try {
  23. const { ip, port, frameHexStrings } = options;
  24. if (!ip || !port) {
  25. throw new Error("缺少IP或端口参数");
  26. }
  27. const socket = new Socket();
  28. const timeout = 2000;
  29. // 默认测试帧: 设备地址2, 功能码4(读取输入寄存器), 起始地址0, 读取2个寄存器
  30. const defaultTestFrame = [0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xF8];
  31. let testFrame;
  32. if (frameHexStrings) {
  33. // 16进制字符串数组格式处理
  34. if (!Array.isArray(frameHexStrings)) {
  35. throw new Error("测试帧格式错误: 必须是16进制字符串数组");
  36. }
  37. try {
  38. testFrame = Buffer.from(frameHexStrings.map(s => parseInt(s, 16)));
  39. } catch (e) {
  40. throw new Error("测试帧格式错误: 16进制字符串转换失败");
  41. }
  42. } else {
  43. testFrame = Buffer.from(defaultTestFrame);
  44. }
  45. return await new Promise((resolve) => {
  46. socket.setTimeout(timeout);
  47. socket.on('connect', () => {
  48. socket.write(testFrame);
  49. });
  50. socket.on('data', (data) => {
  51. const hexResponse = data.toString('hex');
  52. socket.destroy();
  53. resolve({
  54. connected: true,
  55. message: "连接成功",
  56. response: hexResponse
  57. });
  58. });
  59. socket.on('timeout', () => {
  60. socket.destroy();
  61. resolve({
  62. connected: false,
  63. message: "连接超时"
  64. });
  65. });
  66. socket.on('error', (error) => {
  67. socket.destroy();
  68. resolve({
  69. connected: false,
  70. message: `连接失败: ${error.message}`
  71. });
  72. });
  73. socket.connect(Number(port), ip);
  74. });
  75. } catch (error: unknown) {
  76. console.error("Modbus RTU测试连接错误:", error);
  77. const message = error instanceof Error ? error.message : "未知错误";
  78. return {
  79. connected: false,
  80. message: `测试连接失败: ${message}`
  81. };
  82. }
  83. }
  84. /**
  85. * 读取Modbus RTU寄存器数据
  86. * @param address 设备地址
  87. * @param register 寄存器地址
  88. * @param length 读取长度
  89. * @returns 寄存器数据数组
  90. */
  91. export async function readModbusRTU(
  92. address: string,
  93. register: number,
  94. length: number
  95. ): Promise<number[] | null> {
  96. try {
  97. const [ip, portStr] = address.split(':');
  98. const port = parseInt(portStr);
  99. if (!ip || !port) {
  100. throw new Error("设备地址格式错误,应为IP:PORT");
  101. }
  102. // 构建读取输入寄存器命令帧
  103. const frame = [
  104. 0x02, // 设备地址
  105. 0x04, // 功能码(读取输入寄存器)
  106. (register >> 8) & 0xFF, // 寄存器地址高字节
  107. register & 0xFF, // 寄存器地址低字节
  108. (length >> 8) & 0xFF, // 长度高字节
  109. length & 0xFF, // 长度低字节
  110. 0x71, 0xF8 // CRC校验(示例值)
  111. ];
  112. const result = await modbusRTUConnection({
  113. ip,
  114. port,
  115. frameHexStrings: frame.map(b => b.toString(16))
  116. });
  117. if (result.connected && result.response) {
  118. // 解析返回数据(示例)
  119. const bytes = Buffer.from(result.response, 'hex');
  120. const data = [];
  121. for (let i = 3; i < bytes.length - 2; i++) {
  122. data.push(bytes[i]);
  123. }
  124. return data;
  125. }
  126. return null;
  127. } catch (error) {
  128. console.error("读取Modbus寄存器失败:", error);
  129. return null;
  130. }
  131. }