@mos-connection/connector
Version:
MOS compliant TCP/IP Socket connection.
325 lines • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NCSServerConnection = exports.DEFAULT_COMMAND_TIMEOUT = void 0;
const mosSocketClient_1 = require("../connection/mosSocketClient");
const helper_1 = require("@mos-connection/helper");
const eventemitter3_1 = require("eventemitter3");
exports.DEFAULT_COMMAND_TIMEOUT = 5000;
/** Handles connections to a NCS (server) */
class NCSServerConnection extends eventemitter3_1.EventEmitter {
constructor(id, host, mosID, timeout, heartbeatsInterval, debug, strict, isOpenMediaHotStandby) {
super();
this._strict = false;
this._disposed = false;
this._clients = {};
this._emittedConnected = false;
this._isOpenMediaHotStandby = false;
this._id = id;
this._host = host;
this._timeout = timeout ?? exports.DEFAULT_COMMAND_TIMEOUT;
this._heartBeatsInterval = Math.max(heartbeatsInterval ?? 0, this._timeout);
this._mosID = mosID;
this._connected = false;
this._debug = debug ?? false;
this._strict = strict ?? false;
this._isOpenMediaHotStandby = isOpenMediaHotStandby;
}
get timeout() {
return this._timeout;
}
/** Create a MOS client, which talks to */
createClient(clientID, port, clientDescription, useHeartbeats) {
const client = new mosSocketClient_1.MosSocketClient(this._host, port, clientDescription, this._timeout, this._debug, this._strict);
this.debugTrace('registerOutgoingConnection', clientID);
this._clients[clientID] = {
useHeartbeats: useHeartbeats,
heartbeatConnected: false,
client: client,
clientDescription: clientDescription,
};
client.on('rawMessage', (type, message) => {
this.emit('rawMessage', type, message);
});
client.on('warning', (str) => {
this.emit('warning', 'MosSocketClient: ' + str);
});
client.on('error', (err) => {
err.message = 'MosSocketClient: ' + err.message;
this.emit('error', err);
});
}
/** */
removeClient(clientID) {
this._clients[clientID].client.dispose();
delete this._clients[clientID];
}
/** */
disableHeartbeats() {
for (const i in this._clients) {
this._clients[i].useHeartbeats = false;
}
}
/** */
enableHeartbeats() {
for (const i in this._clients) {
this._clients[i].useHeartbeats = true;
}
}
/** */
isHearbeatEnabled() {
for (const i in this._clients) {
if (this._clients[i].useHeartbeats)
return true;
}
return false;
}
setAutoReconnectInterval(interval) {
for (const i in this._clients) {
this._clients[i].client.autoReconnectInterval = interval;
}
}
connect() {
for (const i in this._clients) {
// Connect client
this.emit('info', `Connect client ${i} on ${this._clients[i].clientDescription} on host ${this._host} (${this._clients[i].client.port})`);
this.debugTrace(`Connect client ${i} on ${this._clients[i].clientDescription} on host ${this._host} (${this._clients[i].client.port})`);
this._clients[i].client.connect();
}
this._connected = true;
// Send heartbeat and check connection
this._sendHeartBeats();
// Emit to _callbackOnConnectionChange
// if (this._callbackOnConnectionChange) this._callbackOnConnectionChange()
}
/**
* Sends a mos message.
* Returns a Promise which resolves when a MOS reply has been received.
*/
async executeCommand(message) {
// Fill with clients
let clients;
// Set mosID and ncsID
message.mosID = this._mosID;
message.ncsID = this._id;
// Example: Port based on message type
if (message.port === 'lower') {
clients = this.lowerPortClients;
}
else if (message.port === 'upper') {
clients = this.upperPortClients;
}
else if (message.port === 'query') {
clients = this.queryPortClients;
}
else {
throw Error(`No "${message.port}" ports found`);
}
return new Promise((resolve, reject) => {
if (clients?.length) {
clients[0].queueCommand(message, (response) => {
if ('error' in response) {
reject(response.error);
}
else {
resolve(response.reply);
}
});
}
else {
reject(new Error('executeCommand: No clients found for ' + message.port));
}
});
}
setDebug(debug) {
this._debug = debug;
Object.keys(this._clients).forEach((clientID) => {
const cd = this._clients[clientID];
if (cd) {
cd.client.setDebug(debug);
}
});
}
get connected() {
return this.getConnectedStatus().connected;
}
getConnectedStatus() {
if (!this._connected)
return {
connected: false,
status: 'Not connected',
};
// Check if we have any clients at all
const clientCount = Object.keys(this._clients).length;
if (clientCount === 0) {
return {
connected: false,
status: 'No clients available',
};
}
if (this._isOpenMediaHotStandby) {
// In OpenMedia Standby mode
let isConnectedToSomeDevice = false;
for (const client of Object.values(this._clients)) {
if (client.client.isConnected) {
isConnectedToSomeDevice = true;
}
}
if (isConnectedToSomeDevice) {
return {
connected: true,
status: 'Connected',
};
}
else {
return {
connected: false,
status: 'No heartbeats on any connected client',
};
}
}
else {
// In normal mode
let notConnectedStatus = undefined;
for (const client of Object.values(this._clients)) {
if (client.useHeartbeats && !client.heartbeatConnected) {
notConnectedStatus = `No heartbeats on port ${client.clientDescription}`;
}
}
if (!notConnectedStatus) {
return {
connected: true,
status: 'Connected',
};
}
else {
return {
connected: false,
status: notConnectedStatus,
};
}
}
}
_getClients(clientDescription) {
const clients = [];
for (const i in this._clients) {
if (this._clients[i].clientDescription === clientDescription) {
clients.push(this._clients[i].client);
}
}
return clients;
}
/** */
get lowerPortClients() {
return this._getClients('lower');
}
/** */
get upperPortClients() {
return this._getClients('upper');
}
/** */
get queryPortClients() {
return this._getClients('query');
}
get host() {
return this._host;
}
get id() {
return this._id;
}
handOverQueue(otherConnection) {
const cmds = {};
// this._clients.forEach((client, id) => {
// // cmds[id] = client.client.handOverQueue()
// })
this.debugTrace(this.id + ' ' + this.host + ' handOverQueue');
for (const id in this._clients) {
cmds[id] = this._clients[id].client.handOverQueue();
}
otherConnection.receiveQueue(cmds);
}
receiveQueue(queue) {
// @todo: keep order
// @todo: prevent callback-promise horror...
for (const clientId of Object.keys(queue)) {
for (const msg of queue[clientId].messages) {
this.executeCommand(msg.msg).then((data) => {
const cb = queue[clientId].callbacks[msg.msg.messageID];
if (cb)
cb({ reply: data });
}, (error) => {
const cb = queue[clientId].callbacks[msg.msg.messageID];
if (cb)
cb({ error });
});
}
}
}
async dispose() {
this._disposed = true;
for (const key in this._clients) {
this.removeClient(key);
}
if (this._heartBeatsTimer) {
global.clearTimeout(this._heartBeatsTimer);
delete this._heartBeatsTimer;
}
this._connected = false;
this.emit('connectionChanged');
}
_sendHeartBeats() {
if (this._heartBeatsTimer) {
clearTimeout(this._heartBeatsTimer);
delete this._heartBeatsTimer;
}
if (this._disposed)
return;
const triggerNextHeartBeat = () => {
if (this._disposed)
return;
this._heartBeatsTimer = global.setTimeout(() => {
if (!this._disposed)
this._sendHeartBeats();
}, this._heartBeatsInterval);
};
Promise.all(Object.keys(this._clients).map(async (key) => {
const client = this._clients[key];
if (client.useHeartbeats) {
const heartbeat = new helper_1.MosModel.HeartBeat(this._clients[key].clientDescription, undefined, true);
try {
await this.executeCommand(heartbeat);
client.heartbeatConnected = true;
}
catch (err0) {
// probably a timeout
client.heartbeatConnected = false;
const err = err0 instanceof Error ? err0 : new Error(`${err0}`);
err.message = `Heartbeat error on ${this._clients[key].clientDescription}: ${err.message}`;
this.emit('error', err);
this.debugTrace(`Heartbeat on ${this._clients[key].clientDescription}: ${err0}`);
}
}
}))
.catch((e) => {
triggerNextHeartBeat();
this.emit('error', e);
})
.then(() => {
const connected = this.connected;
if (connected !== this._emittedConnected) {
this._emittedConnected = connected;
this.emit('connectionChanged');
}
triggerNextHeartBeat();
})
.catch((e) => {
this.emit('error', e);
});
}
debugTrace(...strs) {
// eslint-disable-next-line no-console
if (this._debug)
console.log(...strs);
}
}
exports.NCSServerConnection = NCSServerConnection;
//# sourceMappingURL=NCSServerConnection.js.map