146 lines
7.2 KiB
JavaScript
146 lines
7.2 KiB
JavaScript
import { log } from "./logger.js";
|
|
import net from "net";
|
|
export var E_FN_CODE;
|
|
(function (E_FN_CODE) {
|
|
E_FN_CODE[E_FN_CODE["FN_ANY_FUNCTION_CODE"] = 0] = "FN_ANY_FUNCTION_CODE";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_COIL"] = 1] = "FN_READ_COIL";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_DISCR_INPUT"] = 2] = "FN_READ_DISCR_INPUT";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_HOLD_REGISTER"] = 3] = "FN_READ_HOLD_REGISTER";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_INPUT_REGISTER"] = 4] = "FN_READ_INPUT_REGISTER";
|
|
E_FN_CODE[E_FN_CODE["FN_WRITE_COIL"] = 5] = "FN_WRITE_COIL";
|
|
E_FN_CODE[E_FN_CODE["FN_WRITE_HOLD_REGISTER"] = 6] = "FN_WRITE_HOLD_REGISTER";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_EXCEPTION_SERIAL"] = 7] = "FN_READ_EXCEPTION_SERIAL";
|
|
E_FN_CODE[E_FN_CODE["FN_DIAGNOSTICS_SERIAL"] = 8] = "FN_DIAGNOSTICS_SERIAL";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_COMM_CNT_SERIAL"] = 11] = "FN_READ_COMM_CNT_SERIAL";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_COMM_LOG_SERIAL"] = 12] = "FN_READ_COMM_LOG_SERIAL";
|
|
E_FN_CODE[E_FN_CODE["FN_WRITE_MULT_COILS"] = 15] = "FN_WRITE_MULT_COILS";
|
|
E_FN_CODE[E_FN_CODE["FN_WRITE_MULT_REGISTERS"] = 16] = "FN_WRITE_MULT_REGISTERS";
|
|
E_FN_CODE[E_FN_CODE["FN_REPORT_SERVER_ID_SERIAL"] = 17] = "FN_REPORT_SERVER_ID_SERIAL";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_FILE_RECORD"] = 20] = "FN_READ_FILE_RECORD";
|
|
E_FN_CODE[E_FN_CODE["FN_WRITE_FILE_RECORD"] = 21] = "FN_WRITE_FILE_RECORD";
|
|
E_FN_CODE[E_FN_CODE["FN_MASK_WRITE_REGISTER"] = 22] = "FN_MASK_WRITE_REGISTER";
|
|
E_FN_CODE[E_FN_CODE["FN_R_W_MULT_REGISTERS"] = 23] = "FN_R_W_MULT_REGISTERS";
|
|
E_FN_CODE[E_FN_CODE["FN_READ_FIFO_QUEUE"] = 24] = "FN_READ_FIFO_QUEUE";
|
|
E_FN_CODE[E_FN_CODE["FN_ENCAPSULATED_INTERFACE"] = 43] = "FN_ENCAPSULATED_INTERFACE";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_41"] = 65] = "FN_USER_DEFINED_41";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_42"] = 66] = "FN_USER_DEFINED_42";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_43"] = 67] = "FN_USER_DEFINED_43";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_44"] = 68] = "FN_USER_DEFINED_44";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_45"] = 69] = "FN_USER_DEFINED_45";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_46"] = 70] = "FN_USER_DEFINED_46";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_47"] = 71] = "FN_USER_DEFINED_47";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_48"] = 72] = "FN_USER_DEFINED_48";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_64"] = 100] = "FN_USER_DEFINED_64";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_65"] = 101] = "FN_USER_DEFINED_65";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_66"] = 102] = "FN_USER_DEFINED_66";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_67"] = 103] = "FN_USER_DEFINED_67";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_68"] = 104] = "FN_USER_DEFINED_68";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_69"] = 105] = "FN_USER_DEFINED_69";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_6A"] = 106] = "FN_USER_DEFINED_6A";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_6B"] = 107] = "FN_USER_DEFINED_6B";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_6C"] = 108] = "FN_USER_DEFINED_6C";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_6D"] = 109] = "FN_USER_DEFINED_6D";
|
|
E_FN_CODE[E_FN_CODE["FN_USER_DEFINED_6E"] = 110] = "FN_USER_DEFINED_6E";
|
|
E_FN_CODE[E_FN_CODE["FN_NONE"] = 255] = "FN_NONE";
|
|
})(E_FN_CODE || (E_FN_CODE = {}));
|
|
export var E_ModbusAccess;
|
|
(function (E_ModbusAccess) {
|
|
E_ModbusAccess[E_ModbusAccess["MB_ACCESS_NONE"] = 0] = "MB_ACCESS_NONE";
|
|
E_ModbusAccess[E_ModbusAccess["MB_ACCESS_READ_ONLY"] = 1] = "MB_ACCESS_READ_ONLY";
|
|
E_ModbusAccess[E_ModbusAccess["MB_ACCESS_WRITE_ONLY"] = 2] = "MB_ACCESS_WRITE_ONLY";
|
|
E_ModbusAccess[E_ModbusAccess["MB_ACCESS_READ_WRITE"] = 3] = "MB_ACCESS_READ_WRITE";
|
|
})(E_ModbusAccess || (E_ModbusAccess = {}));
|
|
export class ModbusClient {
|
|
host;
|
|
port;
|
|
client;
|
|
constructor(host, port) {
|
|
this.host = host;
|
|
this.port = port;
|
|
this.client = new net.Socket();
|
|
}
|
|
connect() {
|
|
return new Promise((resolve, reject) => {
|
|
this.client.connect(this.port, this.host, () => {
|
|
log.info("Connected to Modbus server");
|
|
resolve();
|
|
});
|
|
this.client.on('error', (err) => {
|
|
log.error("Connection error:", err);
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
disconnect() {
|
|
this.client.end();
|
|
log.info("Disconnected from Modbus server");
|
|
}
|
|
sendRequest(unitId, functionCode, data) {
|
|
return new Promise((resolve, reject) => {
|
|
const transactionId = Math.floor(Math.random() * 65535);
|
|
const protocolId = 0; // Modbus protocol
|
|
const pdu = Buffer.concat([Buffer.from([functionCode]), data]);
|
|
const length = 1 + pdu.length;
|
|
const header = Buffer.alloc(7);
|
|
header.writeUInt16BE(transactionId, 0);
|
|
header.writeUInt16BE(protocolId, 2);
|
|
header.writeUInt16BE(length, 4);
|
|
header.writeUInt8(unitId, 6);
|
|
const request = Buffer.concat([header, pdu]);
|
|
this.client.once('data', (response) => {
|
|
const responseTransactionId = response.readUInt16BE(0);
|
|
if (responseTransactionId !== transactionId) {
|
|
return reject(new Error("Transaction ID mismatch"));
|
|
}
|
|
const responseFunctionCode = response.readUInt8(7);
|
|
if (responseFunctionCode > 0x80) {
|
|
const errorCode = response.readUInt8(8);
|
|
return reject(new Error(`Modbus Exception (FC: ${functionCode}): ${errorCode}`));
|
|
}
|
|
resolve(response.subarray(8));
|
|
});
|
|
this.client.write(request);
|
|
});
|
|
}
|
|
async writeHoldingRegister(address, value, unitId = 1) {
|
|
const data = Buffer.alloc(4);
|
|
data.writeUInt16BE(address, 0);
|
|
data.writeUInt16BE(value, 2);
|
|
await this.sendRequest(unitId, E_FN_CODE.FN_WRITE_HOLD_REGISTER, data);
|
|
}
|
|
async writeCoil(address, value, unitId = 1) {
|
|
const data = Buffer.alloc(4);
|
|
data.writeUInt16BE(address, 0);
|
|
data.writeUInt16BE(value ? 0xFF00 : 0x0000, 2);
|
|
await this.sendRequest(unitId, E_FN_CODE.FN_WRITE_COIL, data);
|
|
}
|
|
async readHoldingRegisters(address, count, unitId = 1) {
|
|
const data = Buffer.alloc(4);
|
|
data.writeUInt16BE(address, 0);
|
|
data.writeUInt16BE(count, 2);
|
|
const response = await this.sendRequest(unitId, E_FN_CODE.FN_READ_HOLD_REGISTER, data);
|
|
const byteCount = response[0];
|
|
const values = [];
|
|
for (let i = 0; i < byteCount / 2; i++) {
|
|
values.push(response.readUInt16BE(1 + i * 2));
|
|
}
|
|
return values;
|
|
}
|
|
async readCoils(address, count, unitId = 1) {
|
|
const data = Buffer.alloc(4);
|
|
data.writeUInt16BE(address, 0);
|
|
data.writeUInt16BE(count, 2);
|
|
const response = await this.sendRequest(unitId, E_FN_CODE.FN_READ_COIL, data);
|
|
const byteCount = response[0];
|
|
const values = [];
|
|
for (let i = 0; i < byteCount; i++) {
|
|
for (let j = 0; j < 8; j++) {
|
|
if (values.length < count) {
|
|
values.push((response[1 + i] & (1 << j)) !== 0);
|
|
}
|
|
}
|
|
}
|
|
return values;
|
|
}
|
|
}
|
|
//# sourceMappingURL=modbus-client.js.map
|