149 lines
5.0 KiB
JavaScript
149 lines
5.0 KiB
JavaScript
'use strict';
|
|
const stampit = require('stampit');
|
|
const crc = require('crc');
|
|
const Put = require('put');
|
|
const ModbusCore = require('../modbus-client-core.js');
|
|
|
|
module.exports = stampit()
|
|
.compose(ModbusCore)
|
|
.init(function () {
|
|
const SerialPort = require('serialport');//.SerialPort,
|
|
let serialport;
|
|
let buffer = new Buffer(0);
|
|
|
|
let init = () => {
|
|
this.setState('init');
|
|
let serial = this.options.serial;
|
|
|
|
if (!serial.portName) {
|
|
throw new Error('No portname.');
|
|
}
|
|
|
|
serial.baudRate = serial.baudRate || 9600; // the most are working with 9600
|
|
serial.dataBits = serial.dataBits || 8;
|
|
serial.stopBits = serial.stopBits || 1;
|
|
serial.parity = serial.parity || 'none';
|
|
// TODO: flowControl - ['xon', 'xoff', 'xany', 'rtscts']
|
|
|
|
// TODO: settings - ['brk', 'cts', 'dtr', 'dts', 'rts']
|
|
|
|
this.log.debug('connect to serial ' + serial.portName + ' with ' + serial.baudRate);
|
|
|
|
serialport = new SerialPort(serial.portName, {
|
|
baudRate: serial.baudRate,
|
|
parity: serial.parity,
|
|
dataBits: serial.dataBits,
|
|
stopBits: serial.stopBits
|
|
});
|
|
|
|
serialport.on('open', onOpen);
|
|
serialport.on('close', onClose);
|
|
serialport.on('data', onData);
|
|
serialport.on('error', onError);
|
|
|
|
this.on('send', onSend);
|
|
|
|
};
|
|
|
|
let onOpen = () => {
|
|
this.emit('connect');
|
|
this.setState('ready');
|
|
};
|
|
|
|
let onClose = () => this.setState('closed');
|
|
|
|
function toStrArray(buf) {
|
|
if (!buf || !buf.length) return '';
|
|
let text = '';
|
|
for (let i = 0; i < buf.length; i++) {
|
|
text += (text ? ',' : '') + buf[i];
|
|
}
|
|
return text;
|
|
}
|
|
|
|
let onData = data => {
|
|
buffer = Buffer.concat([buffer, data]);
|
|
|
|
while (buffer.length > 4) {
|
|
|
|
// 1. there is no mbap
|
|
// 2. extract pdu
|
|
|
|
// 0 - device ID
|
|
// 1 - Function CODE
|
|
// 2 - Bytes length
|
|
// 3.. Data
|
|
// checksum.(2 bytes
|
|
let len;
|
|
let pdu;
|
|
// if response for write
|
|
if (buffer[1] === 5 || buffer[1] === 6 || buffer[1] === 15 || buffer[1] === 16) {
|
|
if (buffer.length < 8) {
|
|
break;
|
|
}
|
|
pdu = buffer.slice(0, 8); // 1 byte device ID + 1 byte FC + 2 bytes address + 2 bytes value + 2 bytes CRC
|
|
} else if (buffer[1] > 0 && buffer[1] < 5){
|
|
len = buffer[2];
|
|
|
|
if (buffer.length < len + 5) {
|
|
break;
|
|
}
|
|
|
|
pdu = buffer.slice(0, len + 5); // 1 byte deviceID + 1 byte FC + 1 byte length + 2 bytes CRC
|
|
} else {
|
|
// unknown function code
|
|
this.log.error('unknown function code: ' + buffer[1]);
|
|
// reset buffer and try again
|
|
buffer = new Buffer(0);
|
|
break;
|
|
}
|
|
|
|
if (crc.crc16modbus(pdu) === 0) { /* PDU is valid if CRC across whole PDU equals 0, else ignore and do nothing */
|
|
if (this.options.unitId !== undefined && pdu[0] !== this.options.unitId) {
|
|
// answer for wrong device
|
|
this.log.debug('received answer for wrong ID ' + buffer[0] + ', expected ' + this.options.unitId);
|
|
}
|
|
// emit data event and let the
|
|
// listener handle the pdu
|
|
this.emit('data', pdu.slice(1, pdu.length - 2), pdu[0]);
|
|
} else {
|
|
this.log.error('Wrong CRC for frame: ' + toStrArray(pdu));
|
|
// reset buffer and try again
|
|
buffer = new Buffer(0);
|
|
break;
|
|
}
|
|
buffer = buffer.slice(pdu.length, buffer.length);
|
|
}
|
|
};
|
|
|
|
let onError = err => this.emit('error', err);
|
|
|
|
let onSend = (pdu, unitId) => {
|
|
let pkt = Put().word8((unitId === undefined ? this.options.unitId : unitId) || 0).put(pdu);
|
|
let buf = pkt.buffer();
|
|
let crc16 = crc.crc16modbus(buf);
|
|
pkt = pkt.word16le(crc16).buffer();
|
|
|
|
if (!serialport) {
|
|
init();
|
|
}
|
|
|
|
serialport.write(pkt, err => err && this.emit('error', err));
|
|
};
|
|
|
|
this.connect = () => {
|
|
if (!serialport) {
|
|
init();
|
|
}
|
|
};
|
|
|
|
this.close = () => {
|
|
if (serialport) {
|
|
serialport.close();
|
|
serialport = null;
|
|
}
|
|
};
|
|
|
|
init();
|
|
});
|