Skip to content

Instantly share code, notes, and snippets.

@morkai
Last active July 16, 2020 11:38
Show Gist options
  • Save morkai/c82c820591d12ea8ed7d464827564d2e to your computer and use it in GitHub Desktop.
Save morkai/c82c820591d12ea8ed7d464827564d2e to your computer and use it in GitHub Desktop.
Balluff RFID processor controller
'use strict';
const logger = require('h5.logger');
const {BufferQueueReader} = require('h5.buffers');
const TcpConnection = require('./TcpConnection');
const MIN_ID_OCCURENCES = 3;
const STX = 0x02;
const EOT = 0x04;
const ACK = 0x06;
const NAK = 0x15;
class BalluffProcessorController
{
constructor(options)
{
this.options = options;
this.callback = null;
this.connection = null;
this.timers = {};
this.stationI = -1;
this.reader = new BufferQueueReader();
this.stations = options.stations.map(station =>
{
return {
no: station.stationNo,
current: {
id: '',
count: 0,
time: 0
},
candidate: {
id: '',
count: 0,
time: 0
}
};
});
this.lastRead = [];
this.logger = logger.create({
module: 'ct-balluff',
submodule: `${options.processorIp}:${options.headPort}`
});
}
destroy()
{
this.logger.info('Destroying...');
this.callback = null;
Object.values(this.timers).forEach(timer => clearTimeout(timer));
this.timers = {};
if (this.connection)
{
this.connection.close();
this.connection = null;
}
this.reader.skip(this.reader.length);
}
start(callback)
{
this.logger.info('Connecting...');
this.callback = callback;
this.connection = new TcpConnection({
socketOptions: {
host: this.options.processorIp,
port: this.options.headPort
},
autoOpen: true,
autoReconnect: true,
minConnectTime: 2500,
maxReconnectTime: 5000,
noActivityTime: 5000,
closeOnDestroy: true,
suppressErrorsAfterDestroy: true
});
this.connection.on('open', this.onOpen.bind(this));
this.connection.on('close', this.onClose.bind(this));
this.connection.on('error', this.onError.bind(this));
this.connection.on('data', this.onData.bind(this));
this.connection.on('write', this.onWrite.bind(this));
}
onError(err)
{
this.logger.error(err, 'Connection error.');
}
onClose()
{
this.logger.warn('Disconnected.');
this.stationI = -1;
this.responseHandler = null;
}
onOpen()
{
this.logger.warn('Connected.');
this.stationI = -1;
this.responseHandler = null;
this.setKeepAlive();
}
onData(data)
{
if (0 && this.options.debug)
{
this.printFrame('RX: ', data);
}
if (this.responseHandler)
{
this.reader.push(data);
this.responseHandler.call(this);
}
}
onWrite(data)
{
if (0 && this.options.debug)
{
this.printFrame('TX: ', data);
}
}
setKeepAlive()
{
this.request('%1500000', this.handleKeepAliveAck);
}
handleKeepAliveAck()
{
if (this.reader.length === 2 && this.reader.readByte(0) === ACK && this.reader.readByte(1) === 0x30)
{
this.request(Buffer.from([STX]), this.readNext);
}
else
{
this.readNext();
}
}
readNext()
{
clearTimeout(this.timers.readNext);
const {stations} = this.options;
this.lastRead = [];
this.stationI += 1;
if (this.stationI === this.options.stations.length)
{
this.stationI = 0;
}
const station = stations[this.stationI];
if (this.options.debug)
{
this.logger.debug('Reading code...', {
stationNo: station.stationNo,
headNo: station.headNo
})
}
this.request(`M${station.headNo}T0030`, this.handleDataAck);
}
request(frame, responseHandler)
{
if (!this.connection || !this.connection.isOpen())
{
return;
}
this.responseHandler = responseHandler;
this.reader.skip(this.reader.length);
this.connection.write(Buffer.isBuffer(frame) ? frame : this.frame(frame));
}
frame(ascii)
{
const buf = Buffer.from(ascii + '0');
buf[buf.length - 1] = this.bcc(buf);
return buf;
}
bcc(buf)
{
let bcc = 0;
if (buf instanceof BufferQueueReader)
{
for (let i = 0; i < buf.length - 1; ++i)
{
bcc = bcc ^ buf.readByte(i);
}
}
else
{
for (let i = 0; i < buf.length - 1; ++i)
{
bcc = bcc ^ buf[i];
}
}
return bcc;
}
handleDataAck()
{
const length = this.reader.length;
if (length < 1)
{
return;
}
const status = this.reader.readByte(0);
if (length === 2 && status === NAK)
{
if (this.options.debug)
{
this.logger.debug('Read no codes.', {
stationNo: this.stations[this.stationI].no
})
}
this.timers.readNext = setTimeout(this.readNext.bind(this), 100);
return;
}
if (length === 9 && status === ACK)
{
this.request(Buffer.from([STX]), this.handleDataChunk);
}
}
handleDataChunk()
{
const status = this.reader.readByte(0);
if ((status === ACK || status === EOT) && ((this.reader.length - 15) % 66 === 0))
{
const expectedChecksum = this.reader.readByte(this.reader.length - 1);
const actualChecksum = this.bcc(this.reader);
if (actualChecksum !== expectedChecksum)
{
if (this.options.debug)
{
this.logger.debug('Invalid checksum.', {
stationNo: this.stations[this.stationI].no,
expectedChecksum,
actualChecksum,
buffer: this.reader.shiftBuffer(this.reader.length)
});
}
this.timers.readNext = setTimeout(this.readNext.bind(this), 100);
return;
}
this.reader.skip(14);
while (this.reader.length > 66)
{
this.reader.skip(2);
const dataLength = this.reader.shiftByte();
this.reader.skip(1);
const data = this.reader.shiftBuffer(62);
const id = [];
for (let i = 1; i <= dataLength; ++i)
{
id.push(data[62 - i].toString(16).toUpperCase().padStart(2, '0'));
}
this.lastRead.push(id.join(''));
}
}
if (status === ACK)
{
this.request(Buffer.from([STX]), this.handleDataChunk);
return;
}
this.handleData();
this.readNext();
}
handleData()
{
const station = this.stations[this.stationI];
if (!this.lastRead.length)
{
if (this.options.debug)
{
this.logger.debug('Read no codes.', {
stationNo: station.no
});
}
return;
}
this.lastRead.sort();
if (this.lastRead.includes(station.current.id))
{
if (this.options.debug)
{
this.logger.debug('Read the same code.', {
stationNo: station.no,
lastRead: this.lastRead
});
}
station.current.count += 1;
return;
}
if (this.lastRead.length > 1)
{
if (this.options.debug)
{
this.logger.debug('Read multiple codes.', {
stationNo: station.no,
lastRead: this.lastRead
});
}
return;
}
if (this.lastRead[0] === station.candidate.id || this.lastRead.includes(station.candidate.id))
{
station.candidate.count += 1;
if (station.candidate.count === MIN_ID_OCCURENCES)
{
station.current = {...station.candidate};
if (this.options.debug)
{
this.logger.debug('Read new code.', {
station,
lastRead: this.lastRead
});
}
if (this.callback)
{
this.callback(station.no, station.current);
}
}
else if (this.options.debug)
{
this.logger.debug('Read matching candidate code.', {
stationNo: station.no,
lastRead: this.lastRead
});
}
}
else if (this.lastRead.length === 1)
{
station.candidate.id = this.lastRead[0];
station.candidate.count = 1;
station.candidate.time = Date.now();
if (this.options.debug)
{
this.logger.debug('Read new candidate code.', {
station,
lastRead: this.lastRead
});
}
}
else if (this.options.debug)
{
this.logger.debug('Read ignored.', {
stationNo: station.no,
lastRead: this.lastRead
});
}
}
printReader(prefix)
{
this.printFrame(prefix, this.reader.readBuffer(0, this.reader.length));
}
printFrame(prefix, frame)
{
const ascii = [];
const hex = [];
const buffer = Buffer.isBuffer(frame);
for (let i = 0; i < frame.length; ++i)
{
const b = buffer ? frame[i] : frame.readByte(i);
if (b >= 0x20 && b <= 0x7E)
{
ascii.push(String.fromCharCode(b).padStart(2, ' '));
}
else
{
ascii.push(b.toString(16).padStart(2, '0'));
}
hex.push(b.toString(16).padStart(2, '0'));
}
if (prefix)
{
console.log(prefix);
}
console.log(`${ascii.join(' ')}\n${hex.join(' ')}`);
}
}
module.exports = BalluffProcessorController;
>>> warn 2020-07-14 19:33:34.425 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 19:33:34.427 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 19:57:21.699 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 19:57:21.699 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 19:57:32.466 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 19:57:32.468 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 20:04:46.166 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 20:04:46.166 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 20:04:57.249 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 20:04:57.251 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 20:14:41.410 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 20:14:41.410 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 20:14:52.860 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 20:14:52.862 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 20:21:53.654 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 20:21:53.655 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 20:22:05.410 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 20:22:05.412 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 20:47:47.987 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 20:47:47.988 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 21:17:37.863 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-14 21:17:37.866 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 22:47:50.604 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-14 22:47:50.643 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-14 22:47:50.645 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-14 23:17:39.286 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 00:47:52.050 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 00:47:52.053 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 00:47:53.478 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 01:17:42.153 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 02:47:54.876 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 02:47:54.878 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 02:47:54.899 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 03:17:43.545 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 04:47:56.308 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 04:47:56.310 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 04:47:57.715 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 04:47:57.718 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 06:47:59.132 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 06:48:00.545 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 07:17:44.663 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 07:17:48.065 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 08:47:55.808 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 08:47:57.384 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 09:17:44.973 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 09:17:46.409 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 10:47:57.717 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 10:47:59.157 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 11:17:47.308 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 11:17:48.042 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 12:48:00.066 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 12:48:00.778 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 13:17:49.804 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 13:17:49.811 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 14:47:57.575 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 14:47:57.578 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 14:47:57.599 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 14:47:57.600 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 16:48:00.444 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 16:48:00.446 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 16:48:00.455 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 16:48:00.458 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 18:48:03.297 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 18:48:03.298 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 19:17:53.756 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 19:17:53.757 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 20:48:01.527 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 20:48:01.527 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 20:48:01.530 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-15 21:17:50.192 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 22:48:02.923 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-15 22:48:02.926 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-15 22:48:04.328 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-15 22:48:04.330 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 00:48:02.143 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 00:48:02.145 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 00:48:05.728 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 00:48:05.730 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 02:48:03.545 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 02:48:03.548 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 02:48:04.942 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 02:48:04.944 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 04:48:06.340 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 04:48:07.744 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 04:48:07.746 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 05:17:54.999 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 06:48:07.741 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 06:48:10.539 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 06:48:10.542 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 07:17:56.378 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 08:48:08.382 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 08:48:08.385 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 08:48:09.142 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 09:17:57.686 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 10:48:10.417 [ct-balluff] [192.168.21.58:10001] Disconnected.
>>> warn 2020-07-16 10:48:11.234 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 10:48:12.744 [ct-balluff] [192.168.21.57:10001] Connected.
>>> warn 2020-07-16 10:48:12.938 [ct-balluff] [192.168.21.58:10001] Connected.
>>> warn 2020-07-16 10:48:25.248 [ct-balluff] [192.168.21.57:10001] Disconnected.
>>> warn 2020-07-16 10:48:25.441 [ct-balluff] [192.168.21.58:10001] Disconnected.
// Part of <http://miracle.systems/p/h5.modbus> licensed under <MIT>
'use strict';
const {EventEmitter} = require('events');
const {Socket} = require('net');
class TcpConnection extends EventEmitter
{
/**
* @param {TcpConnectionOptions} [options]
*/
constructor(options)
{
super();
/**
* @private
* @type {function(this:TcpConnection)}
*/
this.onSocketConnect = this.onSocketConnect.bind(this);
/**
* @private
* @type {function(this:TcpConnection)}
*/
this.onSocketClose = this.onSocketClose.bind(this);
/**
* @private
* @type {function(this:TcpConnection, Error)}
*/
this.onSocketError = this.onSocketError.bind(this);
/**
* @private
* @type {function(this:TcpConnection)}
*/
this.onSocketReadable = this.onSocketReadable.bind(this);
if (!options)
{
options = {};
}
/**
* @private
* @type {TcpSocketOptions}
*/
this.socketOptions = Object.assign({port: 502, host: 'localhost'}, options.socketOptions);
/**
* @private
* @type {?Socket}
*/
this.socket = this.setUpSocket(options.socket);
/**
* @private
* @type {boolean}
*/
this.autoReconnect = options.autoReconnect !== false;
/**
* @private
* @type {number}
*/
this.minConnectTime = options.minConnectTime || 2500;
/**
* @private
* @type {number}
*/
this.maxReconnectTime = options.maxReconnectTime || 5000;
/**
* @private
* @type {number}
*/
this.noActivityTime = options.noActivityTime || 0;
/**
* @private
* @type {boolean}
*/
this.closeOnDestroy = options.closeOnDestroy !== false;
/**
* @private
* @type {boolean}
*/
this.suppressErrorsAfterDestroy = options.suppressErrorsAfterDestroy !== false;
/**
* @private
* @type {boolean}
*/
this.connected = this.socket.readyState === 'open';
/**
* @private
* @type {boolean}
*/
this.connecting = this.socket.readyState === 'opening';
/**
* @private
* @type {boolean}
*/
this.shouldReconnect = this.autoReconnect;
/**
* @private
* @type {number}
*/
this.connectionAttempts = 0;
/**
* @private
* @type {number}
*/
this.lastDataEventTime = 0;
/**
* @private
* @type {?number}
*/
this.reconnectTimer = null;
/**
* @private
* @type {?number}
*/
this.minConnectTimeTimer = null;
/**
* @private
* @type {?number}
*/
this.noActivityTimer = null;
if (this.isOpen())
{
this.onSocketConnect(true);
}
else if (this.isOpening())
{
this.connectionAttempts += 1;
}
else if (options.autoOpen !== false)
{
this.open();
}
}
destroy()
{
this.removeAllListeners();
this.destroySocket();
if (this.reconnectTimer !== null)
{
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.minConnectTimeTimer !== null)
{
clearTimeout(this.minConnectTimeTimer);
this.minConnectTimeTimer = null;
}
if (this.noActivityTimer !== null)
{
clearInterval(this.noActivityTimer);
this.noActivityTimer = null;
}
}
/**
* @returns {boolean}
*/
isOpen()
{
return this.connected;
}
/**
* @returns {boolean}
*/
isOpening()
{
return this.connecting;
}
open()
{
if (this.isOpen() || this.isOpening())
{
return;
}
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
this.connecting = true;
this.shouldReconnect = this.autoReconnect;
this.connectionAttempts += 1;
if (this.socket === null)
{
this.socket = this.setUpSocket(null);
}
this.socket.connect(this.socketOptions);
}
close()
{
this.doClose(false);
}
/**
* @param {Buffer} data
*/
write(data)
{
this.emit('write', data);
if (!this.isOpen())
{
return;
}
try
{
this.socket.write(data);
}
catch (err)
{
this.emit('error', err);
}
}
/**
* @private
* @param {Socket} [socket]
* @returns {Socket}
*/
setUpSocket(socket)
{
if (!socket)
{
socket = new Socket();
}
socket.on('connect', this.onSocketConnect);
socket.on('close', this.onSocketClose);
socket.on('error', this.onSocketError);
socket.on('readable', this.onSocketReadable);
socket.setNoDelay(this.socketOptions.noDelay !== false);
return socket;
}
/**
* @private
*/
destroySocket()
{
const socket = this.socket;
if (socket === null)
{
return;
}
this.socket = null;
socket.removeListener('connect', this.onSocketConnect);
socket.removeListener('close', this.onSocketClose);
socket.removeListener('error', this.onSocketError);
socket.removeListener('readable', this.onSocketReadable);
if (this.suppressErrorsAfterDestroy)
{
socket.on('error', () => {});
}
if (this.closeOnDestroy)
{
socket.destroy();
}
}
/**
* @private
* @param {boolean} [doNotEmit]
*/
onSocketConnect(doNotEmit)
{
this.connecting = false;
this.connected = true;
clearTimeout(this.minConnectTimeTimer);
this.minConnectTimeTimer = setTimeout(this.onAfterMinConnectTime.bind(this), this.minConnectTime);
if (!doNotEmit)
{
this.emit('open');
}
this.onSocketReadable();
}
/**
* @private
*/
onSocketClose()
{
if (this.minConnectTimeTimer !== null)
{
clearTimeout(this.minConnectTimeTimer);
this.minConnectTimeTimer = null;
}
if (this.noActivityTimer !== null)
{
clearInterval(this.noActivityTimer);
this.noActivityTimer = null;
}
this.connecting = false;
this.connected = false;
this.reconnect();
this.emit('close');
}
/**
* @private
* @param {Error} err
*/
onSocketError(err)
{
this.emit('error', err);
}
/**
* @private
*/
onSocketReadable()
{
while (true) // eslint-disable-line no-constant-condition
{
const data = this.socket.read();
if (data === null)
{
this.lastDataEventTime = Date.now();
break;
}
this.emit('data', data);
}
}
/**
* @private
*/
onAfterMinConnectTime()
{
this.connectionAttempts = 0;
this.minConnectTimeTimer = null;
this.setUpNoActivityTimer();
}
/**
* @private
*/
setUpNoActivityTimer()
{
if (this.noActivityTime > 0 && this.noActivityTimer === null)
{
this.noActivityTimer = setInterval(
this.checkActivity.bind(this),
this.noActivityTime
);
}
}
/**
* @private
*/
checkActivity()
{
const lastActivityTime = Date.now() - this.lastDataEventTime;
if (lastActivityTime > this.noActivityTime)
{
this.connected = false;
this.doClose(true);
}
}
/**
* @private
*/
reconnect()
{
if (!this.shouldReconnect)
{
return;
}
let reconnectTime = 250 * this.connectionAttempts;
if (reconnectTime > this.maxReconnectTime)
{
reconnectTime = this.maxReconnectTime;
}
this.reconnectTimer = setTimeout(this.open.bind(this), reconnectTime);
}
/**
* @private
* @param {boolean} shouldReconnect
*/
doClose(shouldReconnect)
{
this.shouldReconnect = shouldReconnect;
if (this.reconnectTimer !== null)
{
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.socket !== null)
{
this.socket.destroy();
}
}
}
module.exports = TcpConnection;
/**
* @typedef {Object} TcpConnectionOptions
* @property {Socket} [socket]
* @property {TcpSocketOptions} [socketOptions={host: 'localhost', port: 502}]
* @property {boolean} [autoOpen=true]
* @property {boolean} [autoReconnect=true]
* @property {number} [minConnectTime=2500]
* @property {number} [maxReconnectTime=5000]
* @property {number} [noActivityTime=0]
* @property {boolean} [closeOnDestroy=true]
* @property {boolean} [suppressErrorsAfterDestroy=true]
*/
/**
* @typedef {Object} TcpSocketOptions
* @property {string} [host=localhost]
* @property {number} [port=502]
* @property {string} [localAddress]
* @property {number} [localPort]
* @property {number} [family=4]
* @property {function(string, object, function(Error, string, number): void): void} [lookup]
* @property {string} [path]
* @property {boolean} [noDelay=true]
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment