import { MosConnection , IMOSDevice , IMOSConnectionStatus , IMOSRunningOrder , IMOSROAck , MosString128 , IMOSRunningOrderBase , IMOSRunningOrderStatus , IMOSStoryStatus , IMOSItemStatus , IMOSStoryAction , IMOSROStory , IMOSROAction , IMOSItemAction , IMOSItem , IMOSROReadyToAir , IMOSROFullStory , IConnectionConfig , IMOSDeviceConnectionOptions , MosDevice , IMOSListMachInfo } from 'mos-connection' ; import * as _ from 'underscore' ; import * as Winston from 'winston' ; import { CoreHandler , CoreMosDeviceHandler } from './coreHandler' ; import { CollectionObj } from '@sofie-automation/server-core-integration' ; export interface MosConfig { self : IConnectionConfig ; } export interface MosDeviceSettings { mosId : string ; devices : { [ deviceId ] : MosDeviceSettingsDevice ; } ; debugLogging : boolean ; } export interface MosDeviceSettingsDevice { primary : MosDeviceSettingsDeviceOptions ; secondary ? : MosDeviceSettingsDeviceOptions ; } export interface MosDeviceSettingsDeviceOptions { id : string ; host : string ; timeout ? : number ; } export class MosHandler { public mos : MosConnection ; public mosOptions : MosConfig ; public debugLogging : boolean = false ; private allMosDevices : { [ id ] : { mosDevice : IMOSDevice ; coreMosHandler ? : CoreMosDeviceHandler ; } ; } = { } ; private _ownMosDevices : { [ deviceId ] : MosDevice ; } = { } ; private _logger : Winston . LoggerInstance ; private _disposed : boolean = false ; private _settings ? : MosDeviceSettings ; private _coreHandler : CoreHandler ; private _observers : Array < any > = [ ] ; private _triggerupdateDevicesTimeout : any = null ; constructor ( logger ) { this . _logger = logger ; } init ( config , coreHandler ) { this . mosOptions = config ; this . _coreHandler = coreHandler ; return coreHandler . core . getPeripheralDevice ( ) . then ( ( peripheralDevice ) => { this . _settings = peripheralDevice . settings || { } ; return this . _initMosConnection ( ) ; } ) . then ( ( ) => { this . _coreHandler . onConnected ( ( ) => { this . setupObservers ( ) ; this . sendStatusOfAllMosDevices ( ) ; } ) ; this . setupObservers ( ) ; return this . _updateDevices ( ) ; } ) . then ( ( ) => { } ) ; } dispose ( ) { this . _disposed = true ; if ( this . mos ) { return this . mos . dispose ( ) ; } else { return Promise . resolve ( ) ; } } setupObservers ( ) { if ( this . _observers . length ) { this . _observers . forEach ( ( obs ) => { obs . stop ( ) ; } ) ; this . _observers = [ ] ; } this . _logger . info ( 'Renewing observers' ) ; let deviceObserver = this . _coreHandler . core . observe ( 'peripheralDevices' ) ; deviceObserver . added = ( ) => { this . _deviceOptionsChanged ( ) ; } ; deviceObserver . changed = ( ) => { this . _deviceOptionsChanged ( ) ; } ; deviceObserver . removed = ( ) => { this . _deviceOptionsChanged ( ) ; } ; this . _observers . push ( deviceObserver ) ; this . _deviceOptionsChanged ( ) ; } debugLog ( msg , ... args ) { if ( this . debugLogging ) { this . _logger . debug ( msg , ... args ) ; } } private _deviceOptionsChanged ( ) { let peripheralDevice = this . getThisPeripheralDevice ( ) ; if ( peripheralDevice ) { let settings = peripheralDevice . settings || { } ; if ( this . debugLogging !== settings . debugLogging ) { this . _logger . info ( 'Changing debugLogging to ' + settings . debugLogging ) ; this . debugLogging = settings . debugLogging ; this . mos . setDebug ( settings . debugLogging ) ; if ( settings . debugLogging ) { this . _logger . level = 'debug' ; } else { this . _logger . level = 'info' ; } this . _logger . info ( 'log level ' + this . _logger . level ) ; this . _logger . info ( 'test log info' ) ; console . log ( 'test console.log' ) ; this . _logger . debug ( 'test log debug' ) ; } } if ( this . _triggerupdateDevicesTimeout ) { clearTimeout ( this . _triggerupdateDevicesTimeout ) ; } this . _triggerupdateDevicesTimeout = setTimeout ( ( ) => { this . _updateDevices ( ) . catch ( e => { this . _logger . error ( e ) ; } ) ; } , 20 ) ; } private _initMosConnection ( ) { if ( this . _disposed ) return Promise . resolve ( ) ; if ( ! this . _settings ) throw Error ( 'Mos-Settings are not set' ) ; this . _logger . info ( 'Initializing MosConnection...' ) ; let connectionConfig = this . mosOptions . self ; if ( ! this . _settings . mosId ) throw Error ( 'mosId missing in settings!' ) ; connectionConfig . mosID = this . _settings . mosId ; this . mos = new MosConnection ( connectionConfig ) ; this . mos . on ( 'rawMessage' , ( source , type , message ) => { if ( `${ message }` . indexOf ( '<heartbeat' ) >= 0 ) { return ; } this . debugLog ( 'rawMessage' , source , type , message ) ; } ) ; this . mos . on ( 'info' , ( message ) => { this . _logger . info ( message ) ; } ) ; this . mos . on ( 'error' , ( error ) => { this . _logger . error ( error ) ; } ) ; this . mos . on ( 'warning' , ( warning ) => { this . _logger . error ( warning ) ; } ) ; this . mos . onConnection ( ( mosDevice ) => { this . _logger . info ( 'new mosConnection established: ' + mosDevice . idPrimary + ', ' + mosDevice . idSecondary ) ; this . allMosDevices [ mosDevice . idPrimary ] = { mosDevice : mosDevice } ; return this . _coreHandler . registerMosDevice ( mosDevice , this ) . then ( ( coreMosHandler ) => { this . allMosDevices [ mosDevice . idPrimary ] . coreMosHandler = coreMosHandler ; let connectionStatus = mosDevice . getConnectionStatus ( ) ; coreMosHandler . onMosConnectionChanged ( connectionStatus ) ; mosDevice . onConnectionChange ( ( connectionStatus ) => { coreMosHandler . onMosConnectionChanged ( connectionStatus ) ; } ) ; coreMosHandler . onMosConnectionChanged ( mosDevice . getConnectionStatus ( ) ) ; mosDevice . onRequestMachineInfo ( ( ) => { return coreMosHandler . getMachineInfo ( ) ; } ) ; mosDevice . onCreateRunningOrder ( ( ro ) => { return this . _getROAck ( ro . ID , coreMosHandler . mosRoCreate ( ro ) ) ; } ) ; mosDevice . onReplaceRunningOrder ( ( ro ) => { return this . _getROAck ( ro . ID , coreMosHandler . mosRoReplace ( ro ) ) ; } ) ; mosDevice . onDeleteRunningOrder ( ( runningOrderId ) => { return this . _getROAck ( runningOrderId , coreMosHandler . mosRoDelete ( runningOrderId ) ) ; } ) ; mosDevice . onMetadataReplace ( ( ro ) => { return this . _getROAck ( ro . ID , coreMosHandler . mosRoMetadata ( ro ) ) ; } ) ; mosDevice . onRunningOrderStatus ( ( status ) => { return this . _getROAck ( status . ID , coreMosHandler . mosRoStatus ( status ) ) ; } ) ; mosDevice . onStoryStatus ( ( status ) => { return this . _getROAck ( status . RunningOrderId , coreMosHandler . mosRoStoryStatus ( status ) ) ; } ) ; mosDevice . onItemStatus ( ( status ) => { return this . _getROAck ( status . RunningOrderId , coreMosHandler . mosRoItemStatus ( status ) ) ; } ) ; mosDevice . onROInsertStories ( ( Action , Stories ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoStoryInsert ( Action , Stories ) ) ; } ) ; mosDevice . onROInsertItems ( ( Action , Items ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoItemInsert ( Action , Items ) ) ; } ) ; mosDevice . onROReplaceStories ( ( Action , Stories ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoStoryReplace ( Action , Stories ) ) ; } ) ; mosDevice . onROReplaceItems ( ( Action , Items ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoItemReplace ( Action , Items ) ) ; } ) ; mosDevice . onROMoveStories ( ( Action , Stories ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoStoryMove ( Action , Stories ) ) ; } ) ; mosDevice . onROMoveItems ( ( Action , Items ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoItemMove ( Action , Items ) ) ; } ) ; mosDevice . onRODeleteStories ( ( Action , Stories ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoStoryDelete ( Action , Stories ) ) ; } ) ; mosDevice . onRODeleteItems ( ( Action , Items ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoItemDelete ( Action , Items ) ) ; } ) ; mosDevice . onROSwapStories ( ( Action , StoryID0 , StoryID1 ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoStorySwap ( Action , StoryID0 , StoryID1 ) ) ; } ) ; mosDevice . onROSwapItems ( ( Action , ItemID0 , ItemID1 ) => { return this . _getROAck ( Action . RunningOrderID , coreMosHandler . mosRoItemSwap ( Action , ItemID0 , ItemID1 ) ) ; } ) ; mosDevice . onReadyToAir ( ( Action ) => { return this . _getROAck ( Action . ID , coreMosHandler . mosRoReadyToAir ( Action ) ) ; } ) ; mosDevice . onRunningOrderStory ( ( story ) => { return this . _getROAck ( story . RunningOrderId , coreMosHandler . mosRoFullStory ( story ) ) ; } ) ; } ) . catch ( ( e ) => { this . _logger . error ( 'Error:' , e ) ; } ) ; } ) ; return this . mos . init ( ) . then ( ( ) => { return ; } ) ; } private sendStatusOfAllMosDevices ( ) { for ( const handler of Object . values ( this . allMosDevices ) ) { if ( handler . coreMosHandler ) { handler . coreMosHandler . onMosConnectionChanged ( handler . mosDevice . getConnectionStatus ( ) ) ; } } } private getThisPeripheralDevice ( ) { let peripheralDevices = this . _coreHandler . core . getCollection ( 'peripheralDevices' ) ; return peripheralDevices . findOne ( this . _coreHandler . core . deviceId ) ; } private _updateDevices ( ) { if ( this . _disposed ) return Promise . resolve ( ) ; return ( ! this . mos ? this . _initMosConnection ( ) : Promise . resolve ( ) ) . then ( ( ) => { let peripheralDevice = this . getThisPeripheralDevice ( ) ; if ( peripheralDevice ) { let settings = peripheralDevice . settings || { } ; let devices = settings . devices ; let devicesToAdd = { } ; let devicesToRemove = { } ; _ . each ( devices , ( device , deviceId ) => { if ( device ) { if ( device . secondary ) { if ( ! device . secondary . host || ! device . secondary . id ) delete device . secondary ; } let oldDevice = this . _getDevice ( deviceId ) ; if ( ! oldDevice ) { this . _logger . info ( 'Initializing new device: ' + deviceId ) ; devicesToAdd [ deviceId ] = device ; } else { if ( ( oldDevice . primaryId || '' ) !== device . primary . id || ( oldDevice . primaryHost || '' ) !== device . primary . host || ( oldDevice . secondaryId || '' ) !== ( ( device . secondary || { id : '' } ) . id || '' ) || ( oldDevice . secondaryHost || '' ) !== ( ( device . secondary || { host : '' } ) . host || '' ) ) { this . _logger . info ( 'Re-initializing device: ' + deviceId ) ; devicesToRemove [ deviceId ] = true ; devicesToAdd [ deviceId ] = device ; } } } } ) ; _ . each ( this . _ownMosDevices , ( oldDevice , deviceId ) => { if ( oldDevice && ! devices [ deviceId ] ) { this . _logger . info ( 'Un-initializing device: ' + deviceId ) ; devicesToRemove [ deviceId ] = true ; } } ) ; return Promise . all ( _ . map ( devicesToRemove , ( _val , deviceId ) => { return this . _removeDevice ( deviceId ) ; } ) ) . then ( ( ) => { return Promise . all ( _ . map ( devicesToAdd , ( device , deviceId ) => { return this . _addDevice ( deviceId , device ) ; } ) ) ; } ) . then ( ( ) => { return ; } ) ; } return Promise . resolve ( ) ; } ) . then ( ( ) => { return ; } ) ; } private _addDevice ( deviceId , deviceOptions ) { if ( this . _getDevice ( deviceId ) ) { throw new Error ( 'Unable to add device "' + deviceId + '", because it already exists!' ) ; } return this . mos . connect ( deviceOptions ) . then ( ( mosDevice ) => { this . _ownMosDevices [ deviceId ] = mosDevice ; const getMachineInfoUntilConnected = ( ) => mosDevice . requestMachineInfo ( ) . catch ( ( e ) => { if ( e && ( ( e + '' ) . match ( /no connection available for failover/i ) || ( e + '' ) . match ( /failover connection/i ) ) ) { return new Promise ( ( resolve ) => { setTimeout ( ( ) => { resolve ( getMachineInfoUntilConnected ( ) ) ; } , 2000 ) ; } ) ; } else { throw e ; } } ) ; return getMachineInfoUntilConnected ( ) . then ( ( machInfo ) => { this . _logger . info ( 'Connected to Mos-device' , machInfo ) ; let machineId = machInfo . ID && machInfo . ID . toString ( ) ; if ( ! ( machineId === deviceOptions . primary . id || ( deviceOptions . secondary && machineId === deviceOptions . secondary . id ) ) ) { throw new Error ( 'Mos-device has ID "' + machineId + '" but specified ncs-id is "' + ( deviceOptions . primary . id || ( deviceOptions . secondary || { id : '' } ) . id ) + '"' ) ; } return mosDevice ; } ) . catch ( ( e ) => { this . mos . disposeMosDevice ( mosDevice ) . catch ( ( ) => { this . _logger . error ( e ) ; } ) ; throw e ; } ) ; } ) ; } private _removeDevice ( deviceId ) { let mosDevice = this . _getDevice ( deviceId ) as MosDevice ; delete this . _ownMosDevices [ deviceId ] ; if ( mosDevice ) { let mosDevice0 = ( this . mos . getDevice ( mosDevice . idPrimary ) || ( mosDevice . idSecondary && this . mos . getDevice ( mosDevice . idSecondary ) ) ) ; if ( mosDevice0 ) { return this . mos . disposeMosDevice ( mosDevice ) . then ( ( ) => { return this . _coreHandler . unRegisterMosDevice ( mosDevice ) ; } ) . then ( ( ) => { delete this . _ownMosDevices [ mosDevice . idPrimary ] ; if ( mosDevice . idSecondary ) delete this . _ownMosDevices [ mosDevice . idSecondary ] ; } ) . catch ( ( e ) => { throw new Error ( e ) ; } ) ; } else { } } else { } return Promise . resolve ( ) ; } private _getDevice ( deviceId ) { return this . _ownMosDevices [ deviceId ] || null ; } private _getROAck ( roId , p ) { return p . then ( ( ) => { let roAck = { ID : roId , Status : new MosString128 ( 'OK' ) , Stories : [ ] } ; return roAck ; } ) . catch ( ( err ) => { this . _logger . error ( 'ROAck error:' , err ) ; let roAck = { ID : roId , Status : new MosString128 ( 'Error: ' + err . toString ( ) ) , Stories : [ ] } ; return roAck ; } ) ; } }