@mos-connection/connector
Version:
MOS compliant TCP/IP Socket connection.
911 lines • 77.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MosDevice = void 0;
const net_1 = require("net");
const model_1 = require("@mos-connection/model");
const helper_1 = require("@mos-connection/helper");
const lib_1 = require("./lib");
const mosModel_1 = require("@mos-connection/helper/dist/mosModel");
const { ensureXMLObject, ensureArray, ensureSingular, ensureSingularArray } = helper_1.MosModel;
class MosDevice {
constructor(idPrimary, idSecondary, connectionConfig, primaryConnection, secondaryConnection, offSpecFailover, strict) {
this._debug = false;
this.supportedProfiles = {
deviceType: 'MOS',
profile0: false,
profile1: false,
profile2: false,
profile3: false,
profile4: false,
profile5: false,
profile6: false,
profile7: false,
}; // Use same names as IProfiles?
this._disposed = false;
this._scheduleCheckProfileValidnessTimeout = null;
this._primaryConnection = null;
this._secondaryConnection = null;
this._currentConnection = null;
// this._id = this.mosTypes.mosString128.create(connectionConfig.mosID).toString()
this._idPrimary = idPrimary;
this._idSecondary = idSecondary;
this.socket = new net_1.Socket();
this.strict = strict;
this.mosTypes = (0, model_1.getMosTypes)(this.strict);
this.parseMosTypes = helper_1.MosModel.getParseMosTypes(this.strict);
// Add params to this in MosConnection/MosDevice
this.manufacturer = this.mosTypes.mosString128.create('RadioVision, Ltd.');
this.model = this.mosTypes.mosString128.create('TCS6000');
this.hwRev = this.mosTypes.mosString128.create('0.1'); // empty string returnes <hwRev/>
this.swRev = this.mosTypes.mosString128.create('0.1');
this.DOM = this.mosTypes.mosTime.create(Date.now());
this.SN = this.mosTypes.mosString128.create('927748927');
this.ID = this.mosTypes.mosString128.create(connectionConfig ? connectionConfig.mosID : '');
this.time = this.mosTypes.mosTime.create(Date.now());
this.opTime = this.mosTypes.mosTime.create(Date.now());
this.mosRev = this.mosTypes.mosString128.create('2.8.5');
if (connectionConfig) {
if (connectionConfig.profiles['0'])
this.supportedProfiles.profile0 = true;
if (connectionConfig.profiles['1'])
this.supportedProfiles.profile1 = true;
if (connectionConfig.profiles['2'])
this.supportedProfiles.profile2 = true;
if (connectionConfig.profiles['3'])
this.supportedProfiles.profile3 = true;
if (connectionConfig.profiles['4'])
this.supportedProfiles.profile4 = true;
if (connectionConfig.profiles['5'])
this.supportedProfiles.profile5 = true;
if (connectionConfig.profiles['6'])
this.supportedProfiles.profile6 = true;
if (connectionConfig.profiles['7'])
this.supportedProfiles.profile7 = true;
if (connectionConfig.isNCS)
this.supportedProfiles.deviceType = 'NCS';
if (connectionConfig.debug)
this._debug = connectionConfig.debug;
}
if (primaryConnection) {
this._primaryConnection = primaryConnection;
this._primaryConnection.on('connectionChanged', () => {
this._emitConnectionChange();
if (offSpecFailover &&
this._currentConnection !== this._primaryConnection &&
this._primaryConnection?.connected) {
this.switchConnections(); // and hope no current message goes lost
}
});
}
if (secondaryConnection) {
this._secondaryConnection = secondaryConnection;
this._secondaryConnection.on('connectionChanged', () => this._emitConnectionChange());
}
this._currentConnection = this._primaryConnection ?? this._secondaryConnection ?? null;
if (this.strict) {
const orgStack = new Error();
this._scheduleCheckProfileValidness(orgStack);
}
}
/** True if MOS-device has connection to server (can send messages) */
get hasConnection() {
return !!this._currentConnection;
}
/** Primary ID (probably the NCS-ID) */
get idPrimary() {
return this._idPrimary;
}
/** Secondary / Buddy ID (probably the MOS-ID) */
get idSecondary() {
return this._idSecondary;
}
/** Host name (IP-address) of the primary server */
get primaryHost() {
return this._primaryConnection ? this._primaryConnection.host : null;
}
/** Name (ID) of the primary server */
get primaryId() {
return this._primaryConnection ? this._primaryConnection.id : null;
}
/** Host name (IP-address) of the secondary (buddy) server */
get secondaryHost() {
return this._secondaryConnection ? this._secondaryConnection.host : null;
}
/** Name (ID) of the secondary (buddy) server */
get secondaryId() {
return this._secondaryConnection ? this._secondaryConnection.id : null;
}
connect() {
if (this._primaryConnection)
this._primaryConnection.connect();
if (this._secondaryConnection)
this._secondaryConnection.connect();
}
async dispose() {
this._currentConnection = null;
const ps = [];
if (this._primaryConnection)
ps.push(this._primaryConnection.dispose());
if (this._secondaryConnection)
ps.push(this._secondaryConnection.dispose());
await Promise.all(ps);
}
async routeData(data, port) {
// Suppress console spam:
if (!(0, lib_1.has)(data, 'heartbeat')) {
this.debugTrace('parsedData', data);
// this.debugTrace('keys', Object.keys(data))
}
// Route and format data:
// Profile 0: -------------------------------------------------------------------------------------------------
if (data.heartbeat) {
// send immediate reply on the same port:
return new helper_1.MosModel.HeartBeat(port, undefined, this.strict);
}
else if (data.reqMachInfo && typeof this._callbackOnRequestMachineInfo === 'function') {
if (port === 'query')
throw new Error('message "reqMachInfo" is invalid on query port');
const m = await this._callbackOnRequestMachineInfo();
return new helper_1.MosModel.ListMachineInfo(m, port, this.strict);
}
// Profile 1: -------------------------------------------------------------------------------------------------
if (data.mosReqObj && typeof this._callbackOnRequestMOSOBject === 'function') {
const mosReqObj = ensureXMLObject(data.mosReqObj, this.strict);
const mosObj = await this._callbackOnRequestMOSOBject(this.parseMosTypes.string.createRequired(mosReqObj.objID, 'objID'));
if (!mosObj)
return null;
return new helper_1.MosModel.MosObj(mosObj, this.strict);
}
else if (data.mosReqAll && typeof this._callbackOnRequestAllMOSObjects === 'function') {
const mosReqAll = ensureXMLObject(data.mosReqAll, this.strict);
const pause = this.parseMosTypes.number.createOptional(mosReqAll.pause, 'pause') || 0;
const mosObjects = await this._callbackOnRequestAllMOSObjects();
setImmediate(() => {
// spec: Pause, when greater than zero, indicates the number of seconds to pause
// between individual mosObj messages.
// Pause of zero indicates that all objects will be sent using the mosListAll message..
// https://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS-Protocol-2.8.4-Current.htm#mosReqAll
if (pause > 0) {
if (mosObjects.length) {
// const firstObject = mosObjects.shift() as IMOSObject
// const resp = new MosObj(firstObject)
// resolve(resp)
const sendNextObject = () => {
if (this._disposed)
return;
const nextObject = mosObjects.shift();
if (nextObject) {
this.sendMOSObject(nextObject)
.then(() => {
setTimeout(sendNextObject, pause * 1000);
})
.catch((e) => {
// eslint-disable-next-line no-console
console.error('Error in async mosObj response to mosReqAll', e);
});
}
};
setTimeout(sendNextObject, pause * 1000);
}
}
else {
this._sendAllMOSObjects(mosObjects).catch((e) => {
// eslint-disable-next-line no-console
console.error('Error in async mosListAll response to mosReqAll', e);
});
}
});
// What this should contain isn't well defined in the protocol
return new helper_1.MosModel.MOSAck({
ID: this.mosTypes.mosString128.create('0'),
Revision: 0,
Description: this.mosTypes.mosString128.create(''),
Status: model_1.IMOSAckStatus.ACK,
}, this.strict);
}
else if (data.mosObj && typeof this._callbackOnMOSObjects === 'function') {
const obj = helper_1.MosModel.XMLMosObject.fromXML('mosObj', data.mosObj, this.strict);
const resp = await this._callbackOnMOSObjects([obj]);
return new helper_1.MosModel.MOSAck(resp, this.strict);
}
else if (data.mosListAll && typeof this._callbackOnMOSObjects === 'function') {
const objs = helper_1.MosModel.XMLMosObjects.fromXML('mosListAll', data.mosListAll, this.strict);
const resp = await this._callbackOnMOSObjects(objs);
return new helper_1.MosModel.MOSAck(resp, this.strict);
}
// Profile 2: -------------------------------------------------------------------------------------------------
// Translate deprecated messages into the functionally equivalent roElementActions:
if (data.roStoryAppend) {
// This is equivalent to inserting a story at the end of the running order
const roStoryAppend = ensureXMLObject(data.roStoryAppend, this.strict);
data.roElementAction = {
roID: roStoryAppend.roID,
operation: 'INSERT',
element_target: {
storyID: '',
},
element_source: {
story: roStoryAppend.story,
},
};
}
else if (data.roStoryInsert) {
const roStoryInsert = ensureXMLObject(data.roStoryInsert, this.strict);
data.roElementAction = {
roID: roStoryInsert.roID,
operation: 'INSERT',
element_target: {
storyID: roStoryInsert.storyID,
},
element_source: {
story: roStoryInsert.story,
},
};
}
else if (data.roStoryReplace) {
const roStoryReplace = ensureXMLObject(data.roStoryReplace, this.strict);
data.roElementAction = {
roID: roStoryReplace.roID,
operation: 'REPLACE',
element_target: {
storyID: roStoryReplace.storyID,
},
element_source: {
story: roStoryReplace.story,
},
};
}
else if (data.roStoryMove) {
// From documentation:
// **Note**: If the second <storyID> tag is blank move the story to the bottom of the Running Order.
const roStoryMove = ensureXMLObject(data.roStoryMove, this.strict);
let storyIDs;
if (Array.isArray(roStoryMove.storyID)) {
storyIDs = ensureSingularArray(roStoryMove.storyID, this.strict);
}
else {
if (this.strict) {
// The storyID is xml-converted to a string if the second tag is missing.
// The spec says that there must be two storyID tags, so we'll throw an error here:
return new helper_1.MosModel.ROAck({
ID: this.parseMosTypes.mosString128.createRequired(roStoryMove.roID, 'roID'),
Status: this.mosTypes.mosString128.create(`The second <storyID> tag is missing in <roStoryMove>.`),
Stories: [],
}, this.strict);
}
else {
// Non strict mode: This is technically out of spec, but it's a common mistake, so we'll handle it like so:
const storyID = ensureSingular(roStoryMove.storyID, this.strict);
storyIDs = [storyID, ''];
}
}
data.roElementAction = {
roID: roStoryMove.roID,
operation: 'MOVE',
element_target: {
storyID: storyIDs[1],
},
element_source: {
storyID: storyIDs[0],
},
};
}
else if (data.roStorySwap) {
const roStorySwap = ensureXMLObject(data.roStorySwap, this.strict);
data.roElementAction = {
roID: roStorySwap.roID,
operation: 'SWAP',
element_source: {
storyID: roStorySwap.storyID, // an array
},
};
}
else if (data.roStoryDelete) {
const roStoryDelete = ensureXMLObject(data.roStoryDelete, this.strict);
data.roElementAction = {
roID: roStoryDelete.roID,
operation: 'DELETE',
// element_target: {
// storyID: roStoryDelete.storyID[1],
// },
element_source: {
storyID: roStoryDelete.storyID,
},
};
}
else if ((0, mosModel_1.isXMLObject)(data.roStoryMoveMultiple) && data.roStoryMoveMultiple.storyID) {
const roStoryMoveMultiple = ensureXMLObject(data.roStoryMoveMultiple, this.strict);
const stories = (Array.isArray(roStoryMoveMultiple.storyID)
? ensureSingularArray(roStoryMoveMultiple.storyID, this.strict)
: [ensureSingular(roStoryMoveMultiple.storyID, this.strict)]).map((s) => this.parseMosTypes.string.createRequired(s, 'storyID'));
{
// From documentation:
// Validation: Duplicate storyIDs are not permitted with in the storyID list.
// This prevents the move from being ambiguous; if two IDs are the same, it is unclear
// where in the playlist the story with duplicate ID must be placed.
const uniqueStoryIds = new Set();
for (const storyId of stories) {
if (uniqueStoryIds.has(storyId))
return new helper_1.MosModel.ROAck({
ID: this.parseMosTypes.mosString128.createRequired(roStoryMoveMultiple.roID, 'roID'),
Status: this.mosTypes.mosString128.create(`Duplicate storyIDs are not permitted with in the storyID list.`),
Stories: [],
}, this.strict);
uniqueStoryIds.add(storyId);
}
}
let target;
let sources;
if (stories.length > 1) {
target = stories[stories.length - 1];
sources = stories.slice(0, stories.length - 1);
}
else {
if (this.strict) {
// Technically a no-op:
target = stories[0];
sources = [];
}
else {
// Handling of edge-case:
// If there is only a single storyId, we assume that the single mentioned story should be moved to the end of the playlist
// (ie that there is supposed to be a second, blank storyId that was just omitted by the sender)
target = '';
sources = stories;
}
}
data.roElementAction = {
roID: roStoryMoveMultiple.roID,
operation: 'MOVE',
element_target: {
storyID: target,
},
element_source: {
storyID: sources,
},
};
}
else if (data.roItemInsert) {
const roItemInsert = ensureXMLObject(data.roItemInsert, this.strict);
data.roElementAction = {
roID: roItemInsert.roID,
operation: 'INSERT',
element_target: {
storyID: roItemInsert.storyID,
itemID: roItemInsert.itemID,
},
element_source: {
item: roItemInsert.item,
},
};
}
else if (data.roItemReplace) {
const roItemReplace = ensureXMLObject(data.roItemReplace, this.strict);
data.roElementAction = {
roID: roItemReplace.roID,
operation: 'REPLACE',
element_target: {
storyID: roItemReplace.storyID,
itemID: roItemReplace.itemID,
},
element_source: {
item: roItemReplace.item,
},
};
}
else if (data.roItemDelete) {
const roItemDelete = ensureXMLObject(data.roItemDelete, this.strict);
data.roElementAction = {
roID: roItemDelete.roID,
operation: 'DELETE',
element_target: {
storyID: roItemDelete.storyID,
},
element_source: {
itemID: roItemDelete.itemID,
},
};
}
else if ((0, mosModel_1.isXMLObject)(data.roItemMoveMultiple) &&
data.roItemMoveMultiple?.itemID &&
data.roItemMoveMultiple?.storyID) {
const roItemMoveMultiple = ensureXMLObject(data.roItemMoveMultiple, this.strict);
const items = (Array.isArray(roItemMoveMultiple.itemID)
? ensureSingularArray(roItemMoveMultiple.itemID, this.strict)
: [ensureSingular(roItemMoveMultiple.itemID, this.strict)]).map((s) => this.parseMosTypes.string.createRequired(s, 'itemID'));
// An additional validation checking the length of items can be added
const l = items.length;
const target = items[l - 1];
const sources = items.slice(0, l - 1);
data.roElementAction = {
roID: roItemMoveMultiple.roID,
operation: 'MOVE',
element_target: {
storyID: roItemMoveMultiple.storyID,
itemID: l === 1 ? undefined : target,
},
element_source: {
itemID: l === 1 ? items : sources,
},
};
}
if (data.roCreate && typeof this._callbackOnCreateRunningOrder === 'function') {
const ro = helper_1.MosModel.XMLRunningOrder.fromXML('roCreate', data.roCreate, this.strict);
const resp = await this._callbackOnCreateRunningOrder(ro);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if (data.roReplace && typeof this._callbackOnReplaceRunningOrder === 'function') {
const ro = helper_1.MosModel.XMLRunningOrder.fromXML('roReplace', data.roReplace, this.strict);
const resp = await this._callbackOnReplaceRunningOrder(ro);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if (data.roDelete && typeof this._callbackOnDeleteRunningOrder === 'function') {
const roDelete = ensureXMLObject(data.roDelete, this.strict);
const resp = await this._callbackOnDeleteRunningOrder(this.parseMosTypes.mosString128.createRequired(roDelete.roID, 'roID'));
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if (data.roReq && typeof this._callbackOnRequestRunningOrder === 'function') {
const roReq = ensureXMLObject(data.roReq, this.strict);
const roId = this.parseMosTypes.mosString128.createRequired(roReq.roID, 'roID');
const ro = await this._callbackOnRequestRunningOrder(roId);
if (ro) {
return new helper_1.MosModel.ROList(ro, this.strict);
}
else {
// RO not found
return new helper_1.MosModel.ROAck({
ID: roId,
Status: this.mosTypes.mosString128.create(model_1.IMOSAckStatus.NACK),
Stories: [],
}, this.strict);
}
}
else if (data.roMetadataReplace && typeof this._callbackOnMetadataReplace === 'function') {
const ro = helper_1.MosModel.XMLRunningOrderBase.fromXML('roMetadataReplace', data.roMetadataReplace, this.strict);
const resp = await this._callbackOnMetadataReplace(ro);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementStat) &&
data.roElementStat.element === 'RO' &&
typeof this._callbackOnRunningOrderStatus === 'function') {
const status = {
ID: this.parseMosTypes.mosString128.createRequired(data.roElementStat.roID, 'roID'),
Status: this.parseMosTypes.stringEnum.createRequired({
value: data.roElementStat.status,
enum: model_1.IMOSObjectStatus,
}, 'status'),
Time: this.parseMosTypes.mosTime.createRequired(data.roElementStat.time, 'time'),
};
const resp = await this._callbackOnRunningOrderStatus(status);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementStat) &&
data.roElementStat.element === 'STORY' &&
typeof this._callbackOnStoryStatus === 'function') {
const status = {
RunningOrderId: this.parseMosTypes.mosString128.createRequired(data.roElementStat.roID, 'roID'),
ID: this.parseMosTypes.mosString128.createRequired(data.roElementStat.storyID, 'storyID'),
Status: this.parseMosTypes.stringEnum.createRequired({
value: data.roElementStat.status,
enum: model_1.IMOSObjectStatus,
}, 'status'),
Time: this.parseMosTypes.mosTime.createRequired(data.roElementStat.time, 'time'),
};
const resp = await this._callbackOnStoryStatus(status);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementStat) &&
data.roElementStat.element === 'ITEM' &&
typeof this._callbackOnItemStatus === 'function') {
const status = {
RunningOrderId: this.parseMosTypes.mosString128.createRequired(data.roElementStat.roID, 'roID'),
StoryId: this.parseMosTypes.mosString128.createRequired(data.roElementStat.storyID, 'storyID'),
ID: this.parseMosTypes.mosString128.createRequired(data.roElementStat.itemID, 'itemID'),
Status: this.parseMosTypes.stringEnum.createRequired({
value: data.roElementStat.status,
enum: model_1.IMOSObjectStatus,
}, 'status'),
Time: this.parseMosTypes.mosTime.createRequired(data.roElementStat.time, 'time'),
ObjectId: this.parseMosTypes.mosString128.createOptional(data.roElementStat.objID, 'objID'),
Channel: this.parseMosTypes.mosString128.createOptional(data.roElementStat.itemChannel, 'itemChannel'),
};
const resp = await this._callbackOnItemStatus(status);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if (data.roReadyToAir && typeof this._callbackOnReadyToAir === 'function') {
const roReadyToAir = ensureXMLObject(data.roReadyToAir, this.strict);
const resp = await this._callbackOnReadyToAir({
ID: this.parseMosTypes.mosString128.createRequired(roReadyToAir.roID, 'roID'),
Status: this.parseMosTypes.stringEnum.createRequired({
value: roReadyToAir.roAir,
enum: model_1.IMOSObjectAirStatus,
}, 'roAir'),
});
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'INSERT' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.story &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROInsertStories === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
};
const stories = helper_1.MosModel.XMLROStories.fromXML('element_source.story', data.roElementAction.element_source.story, this.strict);
const resp = await this._callbackOnROInsertStories(action, stories);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'INSERT' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.item &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROInsertItems === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
ItemID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.itemID, 'element_target.itemID'),
};
const items = helper_1.MosModel.XMLMosItems.fromXML('element_source.item', data.roElementAction.element_source.item, this.strict);
const resp = await this._callbackOnROInsertItems(action, items);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'REPLACE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.story &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROReplaceStories === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
};
const stories = helper_1.MosModel.XMLROStories.fromXML('element_source.story', data.roElementAction.element_source.story, this.strict);
const resp = await this._callbackOnROReplaceStories(action, stories);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'REPLACE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.item &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROReplaceItems === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
ItemID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.itemID, 'element_target.itemID'),
};
const items = helper_1.MosModel.XMLMosItems.fromXML('element_source.item', data.roElementAction.element_source.item, this.strict);
const resp = await this._callbackOnROReplaceItems(action, items);
resp.Stories = []; // dont return these (?)
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'MOVE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.storyID &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROMoveStories === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
};
const storyIDs = helper_1.MosModel.XMLMosIDs.fromXML('element_source.storyID', data.roElementAction.element_source.storyID, this.strict);
const resp = await this._callbackOnROMoveStories(action, storyIDs);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'MOVE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.itemID &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROMoveItems === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
ItemID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.itemID, 'element_target.itemID'),
};
const itemIDs = helper_1.MosModel.XMLMosIDs.fromXML('element_source.itemID', data.roElementAction.element_source.itemID, this.strict);
const resp = await this._callbackOnROMoveItems(action, itemIDs);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'DELETE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.storyID &&
typeof this._callbackOnRODeleteStories === 'function') {
const stories = helper_1.MosModel.XMLMosIDs.fromXML('element_source.storyID', data.roElementAction.element_source.storyID, this.strict);
const resp = await this._callbackOnRODeleteStories({
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
}, stories);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'DELETE' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.itemID &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnRODeleteItems === 'function') {
const action = {
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
};
const items = helper_1.MosModel.XMLMosIDs.fromXML('element_source.itemID', data.roElementAction.element_source.itemID, this.strict);
const resp = await this._callbackOnRODeleteItems(action, items);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'SWAP' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.storyID &&
data.roElementAction.element_source.storyID.length === 2 &&
typeof this._callbackOnROSwapStories === 'function') {
const stories = helper_1.MosModel.XMLMosIDs.fromXML('element_source.storyID', data.roElementAction.element_source.storyID, this.strict);
const resp = await this._callbackOnROSwapStories({
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
}, stories[0], stories[1]);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.roElementAction) &&
data.roElementAction.operation === 'SWAP' &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_source) &&
data.roElementAction.element_source.itemID &&
data.roElementAction.element_source.itemID.length === 2 &&
(0, mosModel_1.isXMLObject)(data.roElementAction.element_target) &&
typeof this._callbackOnROSwapItems === 'function') {
const items = helper_1.MosModel.XMLMosIDs.fromXML('element_source.itemID', data.roElementAction.element_source.itemID, this.strict);
const resp = await this._callbackOnROSwapItems({
RunningOrderID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.roID, 'roID'),
StoryID: this.parseMosTypes.mosString128.createRequired(data.roElementAction.element_target.storyID, 'element_target.storyID'),
}, items[0], items[1]);
return new helper_1.MosModel.ROAck(resp, this.strict);
}
// Profile 3: -------------------------------------------------------------------------------------------------
if (data.mosItemReplace && typeof this._callbackOnItemReplace === 'function') {
const mosItemReplace = ensureXMLObject(data.mosItemReplace, this.strict);
const resp = await this._callbackOnItemReplace(this.parseMosTypes.mosString128.createRequired(mosItemReplace.ID, 'ID'), this.parseMosTypes.mosString128.createRequired(mosItemReplace.itemID, 'itemID'), helper_1.MosModel.XMLMosItem.fromXML('item', mosItemReplace.item, this.strict));
return new helper_1.MosModel.ROAck(resp, this.strict);
}
else if (data.mosObjCreate && typeof this._callbackOnObjectCreate === 'function') {
const mosObjCreate = ensureXMLObject(data.mosObjCreate, this.strict);
const resp = await this._callbackOnObjectCreate(helper_1.MosModel.XMLMosObject.fromXML('mosObjCreate', mosObjCreate, this.strict));
const ack = new helper_1.MosModel.MOSAck(resp, this.strict);
return ack;
}
else if ((0, mosModel_1.isXMLObject)(data.mosReqObjAction) &&
data.mosReqObjAction.operation === 'NEW' &&
typeof this._callbackOnRequestObjectActionNew === 'function') {
const resp = await this._callbackOnRequestObjectActionNew(helper_1.MosModel.XMLMosObject.fromXML('mosReqObjAction', data.mosReqObjAction, this.strict));
return new helper_1.MosModel.MOSAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.mosReqObjAction) &&
data.mosReqObjAction.operation === 'UPDATE' &&
typeof this._callbackOnRequestObjectActionUpdate === 'function') {
const resp = await this._callbackOnRequestObjectActionUpdate(this.parseMosTypes.mosString128.createRequired(data.mosReqObjAction.objID, 'objID'), helper_1.MosModel.XMLMosObject.fromXML('mosReqObjAction', data.mosReqObjAction, this.strict));
return new helper_1.MosModel.MOSAck(resp, this.strict);
}
else if ((0, mosModel_1.isXMLObject)(data.mosReqObjAction) &&
data.mosReqObjAction.operation === 'DELETE' &&
typeof this._callbackOnRequestObjectActionDelete === 'function') {
const resp = await this._callbackOnRequestObjectActionDelete(this.parseMosTypes.mosString128.createRequired(data.mosReqObjAction.objID, 'objID'));
return new helper_1.MosModel.MOSAck(resp, this.strict);
}
else if (data.mosReqObjList && typeof this._callbackOnRequestObjectList === 'function') {
const resp = await this._callbackOnRequestObjectList(helper_1.MosModel.XMLMosRequestObjectList.fromXML('mosReqObjList', data.mosReqObjList, this.strict));
return new helper_1.MosModel.MosObjList(resp, this.strict);
}
else if (data.mosReqSearchableSchema && typeof this._callbackOnRequestSearchableSchema === 'function') {
const mosReqSearchableSchema = ensureXMLObject(data.mosReqSearchableSchema, this.strict);
const resp = await this._callbackOnRequestSearchableSchema(this.parseMosTypes.string.createRequired(mosReqSearchableSchema.username, 'username'));
return new helper_1.MosModel.MosListSearchableSchema(resp, this.strict);
}
// Profile 4: -------------------------------------------------------------------------------------------------
if (data.roReqAll && typeof this._callbackOnRequestAllRunningOrders === 'function') {
const list = await this._callbackOnRequestAllRunningOrders();
const roListAll = new helper_1.MosModel.ROListAll(list, this.strict);
return roListAll;
}
else if (data.roStorySend && typeof this._callbackOnRunningOrderStory === 'function') {
const story = helper_1.MosModel.XMLROFullStory.fromXML('roStorySend', data.roStorySend, this.strict);
const resp = await this._callbackOnRunningOrderStory(story);
return new helper_1.MosModel.ROAck(resp, this.strict);
// TODO: Use reject if function dont exists? Put Nack in ondata
}
// Unsupported messages: --------------------------------------------------------------------------------------
{
this.debugTrace('Unsupported function');
this.debugTrace(data);
const keys = Object.keys(data).filter((key) => ['ncsID', 'mosID', 'messageID'].indexOf(key) === -1);
return new helper_1.MosModel.MOSAck({
ID: this.mosTypes.mosString128.create('0'),
Revision: 0,
Description: this.mosTypes.mosString128.create(`Unsupported function: "${keys.join(', ')}"`),
Status: model_1.IMOSAckStatus.NACK,
}, this.strict);
}
}
// ============================================================================================================
// ========================== Profile 0 ===================================================================
// ============================================================================================================
async requestMachineInfo() {
const message = new helper_1.MosModel.ReqMachInfo(this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosListMachInfo.fromXML('listMachInfo', reply.mos.listMachInfo, strict));
}
onRequestMachineInfo(cb) {
this.checkProfile('onRequestMachineInfo', 'profile1');
this._callbackOnRequestMachineInfo = cb;
}
onConnectionChange(cb) {
this.checkProfile('onConnectionChange', 'profile1');
this._callbackOnConnectionChange = cb;
}
getConnectionStatus() {
const primary = this._primaryConnection?.getConnectedStatus();
const secondary = this._secondaryConnection?.getConnectedStatus();
return {
PrimaryConnected: primary ? primary.connected : false,
PrimaryStatus: primary ? `Primary: ${primary.status}` : 'No primary connection',
SecondaryConnected: secondary ? secondary.connected : false,
SecondaryStatus: secondary ? `Secondary: ${secondary.status}` : 'No secondary connection',
};
}
// Deprecated methods:
/** @deprecated getMachineInfo is deprecated, use requestMachineInfo instead */
async getMachineInfo() {
return this.requestMachineInfo();
}
/** @deprecated onGetMachineInfo is deprecated, use onRequestMachineInfo instead */
onGetMachineInfo(cb) {
return this.onRequestMachineInfo(cb);
}
// ============================================================================================================
// ========================== Profile 1 ===================================================================
// ============================================================================================================
async sendMOSObject(obj) {
const message = new helper_1.MosModel.MosObj(obj, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosAck.fromXML('mosAck', reply.mos.mosAck, strict));
}
onRequestMOSObject(cb) {
this.checkProfile('onRequestMOSObject', 'profile1');
this._callbackOnRequestMOSOBject = cb;
}
async sendRequestMOSObject(objID) {
const message = new helper_1.MosModel.ReqMosObj(objID, this.strict);
const reply = await this.executeCommand(message);
if (reply.mos.roAck) {
throw this.badRoAckReply(ensureXMLObject(reply.mos.roAck, this.strict));
}
else if (reply.mos.mosObj) {
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosObject.fromXML('mosObj', reply.mos.mosObj, strict));
}
else {
throw this.unknownReply(reply);
}
}
onRequestAllMOSObjects(cb) {
this.checkProfile('onRequestAllMOSObjects', 'profile1');
this._callbackOnRequestAllMOSObjects = cb;
}
onMOSObjects(cb) {
this.checkProfile('onMOSObjects', 'profile1');
this._callbackOnMOSObjects = cb;
}
async sendRequestAllMOSObjects(pause = 0) {
if (typeof this._callbackOnMOSObjects !== 'function') {
throw new Error('Cannot sent request, because callback onMOSObjects() is required');
}
const message = new helper_1.MosModel.ReqMosObjAll(pause, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosAck.fromXML('mosAck', reply.mos.mosAck, strict));
// Then we'll be sent mosListAll or mosObj messages separately,
// handled in the callback in this.onMOSObjects(cb)
}
/**
* https://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS-Protocol-2.8.4-Current.htm#mosListAll
*/
async _sendAllMOSObjects(objs) {
const message = new helper_1.MosModel.MosListAll(objs, this.strict);
const reply = await this.executeCommand(message);
if (reply.mos) {
const ack = helper_1.MosModel.XMLMosAck.fromXML('mosAck', reply.mos.mosAck, this.strict);
return ack;
}
else {
throw this.unknownReply(reply);
}
}
/**
* @deprecated getMOSObject is deprecated, use sendRequestMOSObject instead
*/
async getMOSObject(objId) {
return this.sendRequestMOSObject(objId);
}
/** @deprecated getAllMOSObjects is deprecated, use sendRequestAllMOSObjects instead */
async getAllMOSObjects() {
return this.sendRequestAllMOSObjects();
}
// ============================================================================================================
// ========================== Profile 2 ===================================================================
// ============================================================================================================
onCreateRunningOrder(cb) {
this.checkProfile('onCreateRunningOrder', 'profile2');
this._callbackOnCreateRunningOrder = cb;
}
async sendCreateRunningOrder(ro) {
const message = new helper_1.MosModel.ROCreate(ro, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosROAck.fromXML('roAck', reply.mos.roAck, strict));
}
onReplaceRunningOrder(cb) {
this.checkProfile('onReplaceRunningOrder', 'profile2');
this._callbackOnReplaceRunningOrder = cb;
}
async sendReplaceRunningOrder(ro) {
const message = new helper_1.MosModel.ROReplace(ro, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosROAck.fromXML('roAck', reply.mos.roAck, strict));
}
onDeleteRunningOrder(cb) {
this.checkProfile('onDeleteRunningOrder', 'profile2');
this._callbackOnDeleteRunningOrder = cb;
}
async sendDeleteRunningOrder(runningOrderId) {
const message = new helper_1.MosModel.RODelete(runningOrderId, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosROAck.fromXML('roAck', reply.mos.roAck, strict));
}
onRequestRunningOrder(cb) {
this.checkProfile('onRequestRunningOrder', 'profile2');
this._callbackOnRequestRunningOrder = cb;
}
async sendRequestRunningOrder(runningOrderId) {
const message = new helper_1.MosModel.ROReq(runningOrderId, this.strict);
const reply = await this.executeCommand(message);
if (reply.mos.roList) {
return this.handleParseReply((strict) => helper_1.MosModel.XMLRunningOrder.fromXML('roList', ensureXMLObject(reply.mos.roList, strict), strict));
}
else if (reply.mos.roAck) {
throw this.badRoAckReply(ensureXMLObject(reply.mos.roAck, this.strict));
}
throw this.unknownReply(reply);
}
/**
* @deprecated getRunningOrder is deprecated, use sendRequestRunningOrder instead
*/
async getRunningOrder(runningOrderId) {
return this.sendRequestRunningOrder(runningOrderId);
}
onMetadataReplace(cb) {
this.checkProfile('onMetadataReplace', 'profile2');
this._callbackOnMetadataReplace = cb;
}
async sendMetadataReplace(metadata) {
const message = new helper_1.MosModel.ROMetadataReplace(metadata, this.strict);
const reply = await this.executeCommand(message);
return this.handleParseReply((strict) => helper_1.MosModel.XMLMosROAck.fromXML('roAck', reply.mos.roAck, strict));
}
onRunningOrderStatus(cb) {
this.checkProfile('onRunningOrderStatus', 'profile2');
this._callbackOnRunningOrderStatus = cb;
}
onStoryStatus(cb) {
this.checkProfile('onStoryStatus', 'profile2');
this._callbackOnStoryStatus = cb;
}
onItemStatus(cb) {
this.checkProfile('onItemStatus', 'profile2');
this._callbackOnItemStatus = cb;
}
/** @deprecated setRunningOrderStatus is deprecated, use sendRunningOrderStatus instead */
async setRunningOrderStatus(status) {
return this.sendRunningOrderStatus(status);
}
/** @deprecated setStoryStatus is deprecated, use sendStoryStatus instead */
async setStoryStatus(status) {
return this.sendStoryStatus(status);
}
/** @deprecated setItemStatus is deprec