mos-connection
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -5,2 +5,23 @@ # Change Log | ||
<a name="0.5.0"></a> | ||
# [0.5.0](https://github.com/nrkno/tv-automation-mos-connection/compare/0.4.0...0.5.0) (2018-08-17) | ||
### Bug Fixes | ||
* 0 reconnects implies infinite reconnects ([7749dc3](https://github.com/nrkno/tv-automation-mos-connection/commit/7749dc3)) | ||
* extra guards in switching logic ([a66584e](https://github.com/nrkno/tv-automation-mos-connection/commit/a66584e)) | ||
* reconnect timer reset ([970da0d](https://github.com/nrkno/tv-automation-mos-connection/commit/970da0d)) | ||
* roAck may contain error regarding buddy server ([4ef32ff](https://github.com/nrkno/tv-automation-mos-connection/commit/4ef32ff)) | ||
* timeout messages even before being sent ([39ba705](https://github.com/nrkno/tv-automation-mos-connection/commit/39ba705)) | ||
### Features | ||
* failover prototype ([2f979e6](https://github.com/nrkno/tv-automation-mos-connection/commit/2f979e6)) | ||
* failover upon NACK: Main Available ([5bb64e4](https://github.com/nrkno/tv-automation-mos-connection/commit/5bb64e4)) | ||
* option to enable offspec failover behaviour ([86f7b0d](https://github.com/nrkno/tv-automation-mos-connection/commit/86f7b0d)) | ||
<a name="0.4.0"></a> | ||
@@ -7,0 +28,0 @@ # [0.4.0](https://github.com/nrkno/tv-automation-mos-connection/compare/0.3.17...0.4.0) (2018-08-03) |
@@ -9,2 +9,3 @@ /** */ | ||
openRelay?: boolean; | ||
offspecFailover?: boolean; | ||
} | ||
@@ -29,2 +30,3 @@ /** */ | ||
openRelay?: boolean; | ||
offspecFailover?: boolean; | ||
private _profiles; | ||
@@ -31,0 +33,0 @@ constructor(init: IConnectionConfig); |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { MosMessage } from '../mosModel/MosMessage'; | ||
import { HandedOverQueue } from './NCSServerConnection'; | ||
export declare type CallBackFunction = (err: any, data: object) => void; | ||
export interface QueueMessage { | ||
time: number; | ||
msg: MosMessage; | ||
} | ||
export declare class MosSocketClient extends EventEmitter { | ||
@@ -39,4 +44,5 @@ private _host; | ||
disconnect(): void; | ||
queueCommand(message: MosMessage, cb: CallBackFunction): void; | ||
queueCommand(message: MosMessage, cb: CallBackFunction, time?: number): void; | ||
processQueue(): void; | ||
handOverQueue(): HandedOverQueue; | ||
/** */ | ||
@@ -57,3 +63,3 @@ readonly host: string; | ||
/** */ | ||
private executeCommand(message); | ||
private executeCommand(message, isRetry?); | ||
/** */ | ||
@@ -60,0 +66,0 @@ private _autoReconnectionAttempt(); |
@@ -86,7 +86,7 @@ "use strict"; | ||
} | ||
queueCommand(message, cb) { | ||
queueCommand(message, cb, time) { | ||
message.prepare(); | ||
// console.log('queueing', message.messageID, message.constructor.name ) | ||
this._queueCallback[message.messageID + ''] = cb; | ||
this._queueMessages.push({ time: Date.now(), msg: message }); | ||
this._queueMessages.push({ time: time || Date.now(), msg: message }); | ||
this.processQueue(); | ||
@@ -109,9 +109,30 @@ } | ||
else { | ||
// Try again later: | ||
clearTimeout(this.processQueueTimeout); | ||
this.processQueueTimeout = setTimeout(() => { | ||
this.processQueue(); | ||
}, 200); | ||
if (!this._sentMessage && this._queueMessages.length > 0) { | ||
if (Date.now() - this._queueMessages[0].time > this._commandTimeout) { | ||
const msg = this._queueMessages.shift(); | ||
this._queueCallback[msg.msg.messageID]('Command timed out', {}); | ||
delete this._queueCallback[msg.msg.messageID]; | ||
this.processQueue(); | ||
} | ||
else { | ||
// Try again later: | ||
clearTimeout(this.processQueueTimeout); | ||
this.processQueueTimeout = setTimeout(() => { | ||
this.processQueue(); | ||
}, 200); | ||
} | ||
} | ||
} | ||
} | ||
handOverQueue() { | ||
const queue = { | ||
messages: this._queueMessages, | ||
callbacks: this._queueCallback | ||
}; | ||
this._queueMessages = []; | ||
this._queueCallback = {}; | ||
this._sentMessage = null; | ||
clearTimeout(this.processQueueTimeout); | ||
return queue; | ||
} | ||
/** */ | ||
@@ -172,3 +193,3 @@ get host() { | ||
/** */ | ||
executeCommand(message) { | ||
executeCommand(message, isRetry) { | ||
if (this._sentMessage) | ||
@@ -190,4 +211,10 @@ throw Error('executeCommand: there already is a sent Command!'); | ||
console.log('timeout ' + sentMessageId); | ||
this._sendReply(sentMessageId, Error('Command timed out'), null); | ||
this.processQueue(); | ||
if (isRetry) { | ||
this._sendReply(sentMessageId, Error('Command timed out'), null); | ||
this.processQueue(); | ||
} | ||
else { | ||
this._sentMessage = null; | ||
this.executeCommand(message, true); | ||
} | ||
} | ||
@@ -203,4 +230,4 @@ }, this._commandTimeout); | ||
if (this._autoReconnect) { | ||
if (this._reconnectAttempts > 0) { // no reconnection if no valid reconnectionAttemps is set | ||
if ((this._reconnectAttempt >= this._reconnectAttempts)) { // if current attempt is not less than max attempts | ||
if (this._reconnectAttempts > -1) { // no reconnection if no valid reconnectionAttemps is set | ||
if (this._reconnectAttempts > 0 && (this._reconnectAttempt >= this._reconnectAttempts)) { // if current attempt is not less than max attempts | ||
// reset reconnection behaviour | ||
@@ -234,4 +261,4 @@ this._clearConnectionAttemptTimer(); | ||
this._client.emit(socketConnection_1.SocketConnectionEvent.ALIVE); | ||
global.clearInterval(this._connectionAttemptTimer); | ||
// this._clearConnectionAttemptTimer() | ||
// global.clearInterval(this._connectionAttemptTimer) | ||
this._clearConnectionAttemptTimer(); | ||
this.connected = true; | ||
@@ -307,5 +334,10 @@ } | ||
if (parsedData.mos.mosAck && parsedData.mos.mosAck.status === 'NACK') { | ||
if (this._debug) | ||
console.log('Mos Error message:' + parsedData.mos.mosAck.statusDescription); | ||
this.emit('error', 'Error message: ' + parsedData.mos.mosAck.statusDescription); | ||
if (this._sentMessage && parsedData.mos.mosAck.statusDescription === 'Buddy server cannot respond because main server is available') { | ||
this._sendReply(this._sentMessage.msg.messageID, 'Main server available', parsedData); | ||
} | ||
else { | ||
if (this._debug) | ||
console.log('Mos Error message:' + parsedData.mos.mosAck.statusDescription); | ||
this.emit('error', 'Error message: ' + parsedData.mos.mosAck.statusDescription); | ||
} | ||
} | ||
@@ -312,0 +344,0 @@ else { |
/// <reference types="node" /> | ||
import { ConnectionType } from './socketConnection'; | ||
import { MosSocketClient } from '../connection/mosSocketClient'; | ||
import { MosSocketClient, CallBackFunction, QueueMessage } from '../connection/mosSocketClient'; | ||
import { MosMessage } from '../mosModel/MosMessage'; | ||
@@ -14,2 +14,8 @@ import { EventEmitter } from 'events'; | ||
} | ||
export interface HandedOverQueue { | ||
messages: QueueMessage[]; | ||
callbacks: { | ||
[messageId: string]: CallBackFunction; | ||
}; | ||
} | ||
/** */ | ||
@@ -44,4 +50,8 @@ export declare class NCSServerConnection extends EventEmitter implements INCSServerConnection { | ||
readonly id: string; | ||
handOverQueue(otherConnection: NCSServerConnection): void; | ||
receiveQueue(queue: { | ||
[clientId: string]: HandedOverQueue; | ||
}): void; | ||
dispose(): Promise<void>; | ||
private _sendHeartBeats(); | ||
} |
@@ -141,2 +141,31 @@ "use strict"; | ||
} | ||
handOverQueue(otherConnection) { | ||
const cmds = {}; | ||
// this._clients.forEach((client, id) => { | ||
// // cmds[id] = client.client.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(null, data); | ||
} | ||
}, (err) => { | ||
const cb = queue[clientId].callbacks[msg.msg.messageID]; | ||
if (cb) { | ||
cb(null, err); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
dispose() { | ||
@@ -143,0 +172,0 @@ this._disposed = true; |
@@ -188,3 +188,3 @@ "use strict"; | ||
let id1 = (theirMosId1 ? myMosID + '_' + theirMosId1 : null); | ||
let mosDevice = new MosDevice_1.MosDevice(id0, id1, this._conf, primary, secondary); | ||
let mosDevice = new MosDevice_1.MosDevice(id0, id1, this._conf, primary, secondary, this._conf.offspecFailover); | ||
// Add mosDevice to register: | ||
@@ -191,0 +191,0 @@ if (this._mosDevices[id0]) { |
@@ -55,3 +55,3 @@ /// <reference types="node" /> | ||
private _callbackOnROStory?; | ||
constructor(idPrimary: string, idSecondary: string | null, connectionConfig: IConnectionConfig, primaryConnection: NCSServerConnection | null, secondaryConnection: NCSServerConnection | null); | ||
constructor(idPrimary: string, idSecondary: string | null, connectionConfig: IConnectionConfig, primaryConnection: NCSServerConnection | null, secondaryConnection: NCSServerConnection | null, offSpecFailover?: boolean); | ||
readonly hasConnection: boolean; | ||
@@ -101,2 +101,4 @@ readonly idPrimary: string; | ||
onROStory(cb: (story: IMOSROFullStory) => Promise<IMOSROAck>): void; | ||
private executeCommand(message, resend?); | ||
private switchConnections(message?); | ||
} |
@@ -23,3 +23,3 @@ "use strict"; | ||
class MosDevice { | ||
constructor(idPrimary, idSecondary, connectionConfig, primaryConnection, secondaryConnection) { | ||
constructor(idPrimary, idSecondary, connectionConfig, primaryConnection, secondaryConnection, offSpecFailover) { | ||
this._debug = false; | ||
@@ -78,3 +78,8 @@ this.supportedProfiles = { | ||
this._primaryConnection = primaryConnection; | ||
this._primaryConnection.onConnectionChange(() => this.emitConnectionChange()); | ||
this._primaryConnection.onConnectionChange(() => { | ||
this.emitConnectionChange(); | ||
if (offSpecFailover && this._currentConnection !== this._primaryConnection && this._primaryConnection.connected) { | ||
this.switchConnections().catch(() => null); // and hope no current message goes lost | ||
} | ||
}); | ||
} | ||
@@ -509,3 +514,3 @@ if (secondaryConnection) { | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
let listMachInfo = data.mos.listMachInfo; | ||
@@ -530,5 +535,2 @@ let list = { | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -562,3 +564,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
if (data.mos.roAck) { | ||
@@ -576,5 +578,2 @@ reject(Parser_1.Parser.xml2ROAck(data.mos.roAck)); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -586,3 +585,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
if (data.mos.roAck) { | ||
@@ -600,5 +599,2 @@ reject(Parser_1.Parser.xml2ROAck(data.mos.roAck)); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -624,3 +620,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
if (data.mos.roAck) { | ||
@@ -639,5 +635,2 @@ reject(Parser_1.Parser.xml2ROAck(data.mos.roAck)); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -665,3 +658,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
let roAck = Parser_1.Parser.xml2ROAck(data.mos.roAck); | ||
@@ -671,5 +664,2 @@ resolve(roAck); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -686,3 +676,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
let roAck = Parser_1.Parser.xml2ROAck(data.mos.roAck); | ||
@@ -692,5 +682,2 @@ resolve(roAck); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -710,3 +697,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
let roAck = Parser_1.Parser.xml2ROAck(data.mos.roAck); | ||
@@ -716,5 +703,2 @@ resolve(roAck); | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -760,3 +744,3 @@ } | ||
if (this._currentConnection) { | ||
this._currentConnection.executeCommand(message).then((data) => { | ||
this.executeCommand(message).then((data) => { | ||
if (data.mos.hasOwnProperty('roListAll')) { | ||
@@ -780,5 +764,2 @@ let xmlRos = (data.mos.roListAll || {}).ro; | ||
} | ||
else { | ||
reject('No Connection'); | ||
} | ||
}); | ||
@@ -789,4 +770,55 @@ } | ||
} | ||
executeCommand(message, resend) { | ||
if (this._currentConnection) { | ||
console.log('exec command', message); | ||
if (!this._currentConnection.connected) { | ||
return this.switchConnections(message); | ||
} | ||
return this._currentConnection.executeCommand(message).then((res) => { | ||
if (res.mos.roAck && res.mos.roAck.roStatus === 'Buddy server cannot respond because main server is available') { | ||
return Promise.reject('Buddy server cannot respond because main server is available'); | ||
} | ||
return res; | ||
}).catch((e) => { | ||
console.log('errored', e); | ||
if (this._primaryConnection && this._secondaryConnection && !resend) { | ||
return this.switchConnections(message); | ||
} | ||
else { | ||
return Promise.reject(e); | ||
} | ||
}); | ||
} | ||
else { | ||
return Promise.reject('No connection'); | ||
} | ||
} | ||
switchConnections(message) { | ||
if (this._currentConnection && this._primaryConnection && this._secondaryConnection) { | ||
console.log('swithcing conn'); | ||
this._currentConnection = this._currentConnection === this._primaryConnection ? this._secondaryConnection : this._primaryConnection; | ||
if (!this._currentConnection.connected) | ||
return Promise.reject('No connection available for failover'); | ||
let p; | ||
if (message) { | ||
console.log('resending msg'); | ||
p = this.executeCommand(message, true).catch((e) => { | ||
if (e === 'Main server available') { | ||
// @todo: we may deadlock if primary is down for us, but up for buddy | ||
return this.switchConnections(message); | ||
} | ||
// @ts-ignore - following line will always resolve if called from here | ||
this.switchConnections().catch((e) => { | ||
throw Error('e'); | ||
}); | ||
return Promise.reject(e); | ||
}); | ||
} | ||
(this._currentConnection === this._primaryConnection ? this._secondaryConnection : this._primaryConnection).handOverQueue(this._currentConnection); | ||
return p || Promise.resolve(); | ||
} | ||
return Promise.reject('No connection available for failover'); | ||
} | ||
} | ||
exports.MosDevice = MosDevice; | ||
//# sourceMappingURL=MosDevice.js.map |
{ | ||
"name": "mos-connection", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "MOS compliant TCP/IP Socket connection.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
317040
4358