UNPKG

modbus-serial

Version:

A pure JavaScript implemetation of MODBUS-RTU (Serial and TCP) for NodeJS.

169 lines (138 loc) 4.49 kB
"use strict"; const events = require("events"); const EventEmitter = events.EventEmitter || events; const dgram = require("dgram"); const modbusSerialDebug = require("debug")("modbus-serial"); const crc16 = require("../utils/crc16"); /* TODO: const should be set once, maybe */ const MODBUS_PORT = 502; // modbus port const MAX_TRANSACTIONS = 256; // maximum transaction to wait for const MIN_DATA_LENGTH = 6; const MIN_MBAP_LENGTH = 6; const CRC_LENGTH = 2; class ModbusUdpPort extends EventEmitter { /** * Simulate a modbus-RTU port using modbus-udp. * * @param ip * @param options * @constructor */ constructor(ip, options) { super(); const modbus = this; this.ip = ip; this.openFlag = false; this._transactionIdWrite = 1; this.port = options.port || MODBUS_PORT; // options if (typeof(options) === "undefined") options = {}; // create a socket this._client = dgram.createSocket("udp4"); // Bind to the same port as we're sending to this._client.bind(); // wait for answer const self = this; this._client.on("message", function(data, rinfo) { let buffer; let crc; let length; // Filter stuff not intended for us if(rinfo.address !== self.ip || rinfo.port !== self.port) { return; } // data received modbusSerialDebug({ action: "receive udp port strings", data: data }); // check data length while (data.length > MIN_MBAP_LENGTH) { // parse udp header length length = data.readUInt16BE(4); // cut 6 bytes of mbap and copy pdu buffer = Buffer.alloc(length + CRC_LENGTH); data.copy(buffer, 0, MIN_MBAP_LENGTH); // add crc to message crc = crc16(buffer.slice(0, -CRC_LENGTH)); buffer.writeUInt16LE(crc, buffer.length - CRC_LENGTH); // update transaction id and emit data modbus._transactionIdRead = data.readUInt16BE(0); modbus.emit("data", buffer); // debug modbusSerialDebug({ action: "parsed udp port", buffer: buffer, transactionId: modbus._transactionIdRead }); // reset data data = data.slice(length + MIN_MBAP_LENGTH); } }); this._client.on("listening", function() { modbus.openFlag = true; }); this._client.on("close", function() { modbus.openFlag = false; }); } /** * Check if port is open. * * @returns {boolean} */ get isOpen() { return this.openFlag; } /** * Simulate successful port open. * * @param callback */ // eslint-disable-next-line class-methods-use-this open(callback) { if (callback) callback(null); } /** * Simulate successful close port. * * @param callback */ close(callback) { this._client.close(); if (callback) callback(null); } /** * Send data to a modbus-udp slave. * * @param data */ write(data) { if(data.length < MIN_DATA_LENGTH) { modbusSerialDebug("expected length of data is too small - minimum is " + MIN_DATA_LENGTH); return; } // remember current unit and command this._id = data[0]; this._cmd = data[1]; // remove crc and add mbap const buffer = Buffer.alloc(data.length + MIN_MBAP_LENGTH - CRC_LENGTH); buffer.writeUInt16BE(this._transactionIdWrite, 0); buffer.writeUInt16BE(0, 2); buffer.writeUInt16BE(data.length - CRC_LENGTH, 4); data.copy(buffer, MIN_MBAP_LENGTH); modbusSerialDebug({ action: "send modbus udp port", data: data, buffer: buffer, unitid: this._id, functionCode: this._cmd }); // send buffer via udp this._client.send(buffer, 0, buffer.length, this.port, this.ip); // set next transaction id this._transactionIdWrite = (this._transactionIdWrite + 1) % MAX_TRANSACTIONS; } } /** * UDP port for Modbus. * * @type {ModbusUdpPort} */ module.exports = ModbusUdpPort;