UNPKG

@mos-connection/connector

Version:
325 lines 11.4 kB
"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