UNPKG

jsmodbus

Version:

Implementation for the Serial/TCP Modbus protocol.

168 lines (138 loc) 4.26 kB
'use strict' const OUT_OF_SYNC = 'OutOfSync' const OFFLINE = 'Offline' const MODBUS_EXCEPTION = 'ModbusException' const debug = require('debug')('client-request-handler') const UserRequest = require('./user-request.js') const ExceptionResponseBody = require('./response/exception.js') /** Common Request Handler * @abstract */ class ModbusClientRequestHandler { /** Create a new Request handler for Client requests * @param {net.Socket} socket A net.Socket object. * @param {Number} timeout The request timeout value in ms. */ constructor (socket, timeout) { if (new.target === ModbusClientRequestHandler) { throw new TypeError('Cannot instantiate ModbusClientRequestHandler directly.') } this._socket = socket this._timeout = timeout this._requests = [] this._currentRequest = null this._state = 'offline' } _clearCurrentRequest () { if (!this._currentRequest) { return } this._currentRequest.done() this._currentRequest = null } _clearAllRequests () { this._clearCurrentRequest() while (this._requests.length > 0) { const req = this._requests.shift() req.reject({ 'err': OUT_OF_SYNC, 'message': 'rejecting because of earlier OutOfSync error' }) } } _onConnect () { this._state = 'online' } _onClose () { this._state = 'offline' this._clearAllRequests() } /** Register a new request. * @param {RequestBody} requestBody A request body to execute a modbus function. * @returns {Promise} A promise to handle the request outcome. */ register (request) { const userRequest = new UserRequest(request, this._timeout) this._requests.push(userRequest) this._flush() return userRequest.promise } /** Handle a ModbusTCPResponse object. * @param {ModbusTCPResponse} response A Modbus TCP Response. */ handle (response) { debug('incoming response') if (!response) { debug('well, sorry I was wrong, no response at all') return } const userRequest = this._currentRequest if (!userRequest) { debug('no current request, no idea where this came from') return } const request = userRequest.request /* check that response fc equals request id */ if (response.body.fc < 0x80 && response.body.fc !== request.body.fc) { debug('something is weird, request fc and response fc do not match.') /* clear all request, client must be reset */ userRequest.reject({ 'err': OUT_OF_SYNC, 'message': 'request fc and response fc does not match.' }) this._clearAllRequests() return } /* check if response is an exception */ if (response.body instanceof ExceptionResponseBody) { debug('response is a exception') userRequest.reject({ 'err': MODBUS_EXCEPTION, 'response': response }) this._clearCurrentRequest() this._flush() return } /* everything is fine, handle response */ debug('resolving request') userRequest.resolve(response) this._clearCurrentRequest() /* start next request */ this._flush() } /* execute next request */ _flush () { debug('flushing') if (this._currentRequest !== null) { debug('executing another request, come back later') return } if (this._requests.length === 0) { debug('no request to be executed') return } this._currentRequest = this._requests.shift() if (this._state === 'offline') { debug('rejecting request immediatly, client offline') this._currentRequest.reject({ 'err': OFFLINE, 'message': 'no connection to modbus server' }) this._clearCurrentRequest() /* start next request */ setTimeout(this._flush.bind(this), 0) return } const payload = this._currentRequest.createPayload() debug('flushing new request', payload) this._currentRequest.start(function () { this._clearCurrentRequest() this._flush() }.bind(this)) this._socket.write(payload, function (err, result) { debug('request fully flushed, ( error:', err, ')', result) }) } } module.exports = ModbusClientRequestHandler