jsmodebus
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
'use strict';
|
||||
|
||||
const Stampit = require('stampit');
|
||||
const StateMachine = require('stampit-state-machine');
|
||||
const EventBus = require('stampit-event-bus');
|
||||
|
||||
const ExceptionMessage = {
|
||||
0x01: 'ILLEGAL FUNCTION',
|
||||
0x02: 'ILLEGAL DATA ADDRESS',
|
||||
0x03: 'ILLEGAL DATA VALUE',
|
||||
0x04: 'SLAVE DEVICE FAILURE',
|
||||
0x05: 'ACKNOWLEDGE',
|
||||
0x06: 'SLAVE DEVICE BUSY',
|
||||
0x08: 'MEMORY PARITY ERROR',
|
||||
0x0A: 'GATEWAY PATH UNAVAILABLE',
|
||||
0x0B: 'GATEWAY TARGET DEVICE FAILED TO RESPOND'
|
||||
};
|
||||
|
||||
module.exports = Stampit()
|
||||
.compose(StateMachine, EventBus)
|
||||
.init(function () {
|
||||
this.log = this.options && this.options.log;
|
||||
|
||||
if (!this.log) {
|
||||
this.log = {
|
||||
log: () => console.log.apply(console, arguments),
|
||||
error: () => console.error.apply(console, arguments),
|
||||
warn: () => console.warn.apply(console, arguments),
|
||||
debug: () => console.log.apply(console, arguments)
|
||||
};
|
||||
}
|
||||
|
||||
let responseHandler = {};
|
||||
let currentRequest = null;
|
||||
|
||||
let reqFifo = [];
|
||||
|
||||
let init = () => {
|
||||
this.options.timeout = this.options.timeout || (5 * 1000); // 5s
|
||||
|
||||
this.on('data', onData);
|
||||
this.on('newState_ready', flush);
|
||||
this.on('newState_closed', onClosed);
|
||||
};
|
||||
|
||||
let flush = () => {
|
||||
if (reqFifo.length) {
|
||||
currentRequest = reqFifo.shift();
|
||||
|
||||
currentRequest.timeout = setTimeout(() => {
|
||||
currentRequest.cb && currentRequest.cb({err: 'timeout'});
|
||||
this.emit('trashCurrentRequest');
|
||||
this.log.error('Request timed out.');
|
||||
this.setState('error');
|
||||
}, this.options.timeout);
|
||||
|
||||
this.setState('waiting');
|
||||
this.emit('send', currentRequest.pdu, currentRequest.unitId);
|
||||
}
|
||||
};
|
||||
|
||||
let onClosed = () => {
|
||||
if (currentRequest) {
|
||||
this.log.debug('Clearing timeout of the current request.');
|
||||
clearTimeout(currentRequest.timeout);
|
||||
}
|
||||
|
||||
this.log.debug('Cleaning up request fifo.');
|
||||
reqFifo = [];
|
||||
};
|
||||
|
||||
let handleErrorPDU = (pdu) => {
|
||||
const errorCode = pdu.readUInt8(0);
|
||||
|
||||
// if error code is smaller than 0x80
|
||||
// ths pdu describes no error
|
||||
|
||||
if (errorCode < 0x80) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// pdu describes an error
|
||||
const exceptionCode = pdu.readUInt8(1);
|
||||
const message = ExceptionMessage[exceptionCode];
|
||||
|
||||
const err = {
|
||||
errorCode: errorCode,
|
||||
exceptionCode: exceptionCode,
|
||||
message: message
|
||||
};
|
||||
|
||||
// call the desired deferred
|
||||
currentRequest.cb && currentRequest.cb(err);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the incoming data, cut out the mbap
|
||||
* packet and send the pdu to the listener
|
||||
*/
|
||||
let onData = (pdu, unitId) => {
|
||||
if (!currentRequest) {
|
||||
this.log.debug('No current request.');
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(currentRequest.timeout);
|
||||
|
||||
// check pdu for error
|
||||
if (handleErrorPDU(pdu)) {
|
||||
this.log.debug('Received pdu describes an error.');
|
||||
currentRequest = null;
|
||||
this.setState('ready');
|
||||
return;
|
||||
}
|
||||
|
||||
// handle pdu
|
||||
|
||||
const handler = responseHandler[currentRequest.fc];
|
||||
if (!handler) {
|
||||
this.log.debug(`Found no handler for fc ${currentRequest.fc}`);
|
||||
throw new Error(`No handler implemented for fc ${currentRequest.fc}`);
|
||||
}
|
||||
|
||||
handler(unitId, pdu, currentRequest.cb);
|
||||
|
||||
this.setState('ready');
|
||||
};
|
||||
|
||||
this.addResponseHandler = (fc, handler) => {
|
||||
responseHandler[fc] = handler;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.queueRequest = (unitId, fc, pdu, cb) => {
|
||||
reqFifo.push({unitId, fc, pdu, cb});
|
||||
|
||||
if (this.inState('ready')) {
|
||||
flush();
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
});
|
||||
Reference in New Issue
Block a user