1- import React from 'react'
2- import { Translated , useSubscription , useTracker } from '../../../lib/ReactMeteorData/react-meteor-data'
1+ import { useEffect , useMemo , useState } from 'react'
2+ import { useSubscription , useTracker } from '../../../lib/ReactMeteorData/react-meteor-data'
33import { PeripheralDevice , PeripheralDeviceType } from '@sofie-automation/corelib/dist/dataModel/PeripheralDevice'
4- import { withTranslation } from 'react-i18next'
4+ import { useTranslation } from 'react-i18next'
55import { protectString , unprotectString } from '../../../lib/tempLib'
66import * as _ from 'underscore'
77import { NotificationCenter , NoticeLevel , Notification } from '../../../lib/notifications/notifications'
8- import { ICoreSystem } from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
98import { StatusResponse } from '@sofie-automation/meteor-lib/dist/api/systemStatus'
109import { MeteorCall } from '../../../lib/meteorApi'
1110import { CoreSystem , PeripheralDevices } from '../../../collections'
@@ -16,75 +15,59 @@ import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyE
1615import { CoreItem } from './CoreItem'
1716import { DeviceItem } from './DeviceItem'
1817
19- interface ISystemStatusProps { }
20- interface ISystemStatusState {
21- systemStatus : StatusResponse | undefined
22- deviceDebugState : Map < PeripheralDeviceId , object >
23- }
24-
25- interface ISystemStatusTrackedProps {
26- coreSystem : ICoreSystem | undefined
27- devices : Array < PeripheralDevice >
28- }
29-
30- interface DeviceInHierarchy {
31- device : PeripheralDevice
32- children : Array < DeviceInHierarchy >
33- }
18+ export function SystemStatus ( ) : JSX . Element {
19+ const { t } = useTranslation ( )
3420
35- export default function SystemStatus ( props : Readonly < ISystemStatusProps > ) : JSX . Element {
3621 // Subscribe to data:
3722 useSubscription ( CorelibPubSub . peripheralDevices , null )
3823
3924 const coreSystem = useTracker ( ( ) => CoreSystem . findOne ( ) , [ ] )
4025 const devices = useTracker ( ( ) => PeripheralDevices . find ( { } , { sort : { lastConnected : - 1 } } ) . fetch ( ) , [ ] , [ ] )
4126
42- return < SystemStatusContent { ...props } coreSystem = { coreSystem } devices = { devices } />
27+ const systemStatus = useSystemStatus ( )
28+ const playoutDebugStates = usePlayoutDebugStates ( devices )
29+
30+ const devicesHeirarchy = convertDevicesIntoHeirarchy ( devices )
31+
32+ return (
33+ < div className = "mhl gutter system-status" >
34+ < header className = "mbs" >
35+ < h1 > { t ( 'System Status' ) } </ h1 >
36+ </ header >
37+ < div className = "mod mvl" >
38+ { coreSystem && < CoreItem coreSystem = { coreSystem } systemStatus = { systemStatus } /> }
39+
40+ { devicesHeirarchy . map ( ( d ) => (
41+ < DeviceItemWithChildren playoutDebugStates = { playoutDebugStates } parentDevice = { null } device = { d } />
42+ ) ) }
43+ </ div >
44+ </ div >
45+ )
4346}
4447
45- const SystemStatusContent = withTranslation ( ) (
46- class SystemStatusContent extends React . Component <
47- Translated < ISystemStatusProps & ISystemStatusTrackedProps > ,
48- ISystemStatusState
49- > {
50- private refreshInterval : NodeJS . Timer | undefined = undefined
51- private refreshDebugStatesInterval : NodeJS . Timer | undefined = undefined
52- private destroyed = false
53-
54- constructor ( props : Translated < ISystemStatusProps & ISystemStatusTrackedProps > ) {
55- super ( props )
56-
57- this . state = {
58- systemStatus : undefined ,
59- deviceDebugState : new Map ( ) ,
60- }
61- }
48+ interface DeviceInHierarchy {
49+ device : PeripheralDevice
50+ children : Array < DeviceInHierarchy >
51+ }
6252
63- componentDidMount ( ) : void {
64- this . refreshSystemStatus ( )
65- this . refreshInterval = setInterval ( this . refreshSystemStatus , 5000 )
66- this . refreshDebugStatesInterval = setInterval ( this . refreshDebugStates , 1000 )
67- }
53+ function useSystemStatus ( ) : StatusResponse | undefined {
54+ const { t } = useTranslation ( )
6855
69- componentWillUnmount ( ) : void {
70- if ( this . refreshInterval ) clearInterval ( this . refreshInterval )
71- if ( this . refreshDebugStatesInterval ) clearInterval ( this . refreshDebugStatesInterval )
72- this . destroyed = true
73- }
56+ const [ sytemStatus , setSystemStatus ] = useState < StatusResponse | undefined > ( )
7457
75- refreshSystemStatus = ( ) => {
76- const { t } = this . props
58+ useEffect ( ( ) => {
59+ let destroyed = false
60+
61+ const refreshSystemStatus = ( ) => {
7762 MeteorCall . systemStatus
7863 . getSystemStatus ( )
79- . then ( ( systemStatus : StatusResponse ) => {
80- if ( this . destroyed ) return
64+ . then ( ( newSystemStatus : StatusResponse ) => {
65+ if ( destroyed ) return
8166
82- this . setState ( {
83- systemStatus : systemStatus ,
84- } )
67+ setSystemStatus ( newSystemStatus )
8568 } )
8669 . catch ( ( err ) => {
87- if ( this . destroyed ) return
70+ if ( destroyed ) return
8871
8972 logger . error ( 'systemStatus.getSystemStatus' , err )
9073 NotificationCenter . push (
@@ -98,106 +81,131 @@ const SystemStatusContent = withTranslation()(
9881 } )
9982 }
10083
101- refreshDebugStates = ( ) => {
102- for ( const device of this . props . devices ) {
103- if ( device . type === PeripheralDeviceType . PLAYOUT && device . settings && ( device . settings as any ) [ 'debugState' ] ) {
104- MeteorCall . systemStatus
105- . getDebugStates ( device . _id )
106- . then ( ( res ) => {
107- const states : Map < PeripheralDeviceId , object > = new Map ( )
84+ refreshSystemStatus ( )
85+
86+ const interval = setInterval ( refreshSystemStatus , 5000 )
87+
88+ return ( ) => {
89+ clearInterval ( interval )
90+ destroyed = true
91+ }
92+ } , [ t ] )
93+
94+ return sytemStatus
95+ }
96+
97+ function usePlayoutDebugStates ( devices : PeripheralDevice [ ] ) : Map < PeripheralDeviceId , object > {
98+ const { t } = useTranslation ( )
99+
100+ const [ playoutDebugStates , setPlayoutDebugStates ] = useState < Map < PeripheralDeviceId , object > > ( new Map ( ) )
101+
102+ const playoutDeviceIds = useMemo ( ( ) => {
103+ const deviceIds : PeripheralDeviceId [ ] = [ ]
104+
105+ for ( const device of devices ) {
106+ if ( device . type === PeripheralDeviceType . PLAYOUT && device . settings && ( device . settings as any ) [ 'debugState' ] ) {
107+ deviceIds . push ( device . _id )
108+ }
109+ }
110+
111+ deviceIds . sort ( )
112+ return deviceIds
113+ } , [ devices ] )
114+
115+ useEffect ( ( ) => {
116+ let destroyed = false
117+
118+ const refreshDebugStates = ( ) => {
119+ for ( const deviceId of playoutDeviceIds ) {
120+ MeteorCall . systemStatus
121+ . getDebugStates ( deviceId )
122+ . then ( ( res ) => {
123+ if ( destroyed ) return
124+
125+ setPlayoutDebugStates ( ( oldState ) => {
126+ // Create a new map based on the old one
127+ const newStates = new Map ( oldState . entries ( ) )
108128 for ( const [ key , state ] of Object . entries < any > ( res ) ) {
109- states . set ( protectString ( key ) , state )
129+ newStates . set ( protectString ( key ) , state )
110130 }
111- this . setState ( {
112- deviceDebugState : states ,
113- } )
131+ return newStates
114132 } )
115- . catch ( ( err ) => console . log ( `Error fetching device states: ${ stringifyError ( err ) } ` ) )
116- }
133+ } )
134+ . catch ( ( err ) => console . log ( `Error fetching device states: ${ stringifyError ( err ) } ` ) )
117135 }
118136 }
119137
120- renderPeripheralDevices ( ) {
121- const devices : Array < DeviceInHierarchy > = [ ]
122- const refs : Record < string , DeviceInHierarchy | undefined > = { }
123- const devicesToAdd : Record < string , DeviceInHierarchy > = { }
124- // First, add all as references:
125- _ . each ( this . props . devices , ( device ) => {
126- const d : DeviceInHierarchy = {
127- device : device ,
128- children : [ ] ,
129- }
130- refs [ unprotectString ( device . _id ) ] = d
131- devicesToAdd [ unprotectString ( device . _id ) ] = d
132- } )
133- // Then, map and add devices:
134- _ . each ( devicesToAdd , ( d : DeviceInHierarchy ) => {
135- if ( d . device . parentDeviceId ) {
136- const parent = refs [ unprotectString ( d . device . parentDeviceId ) ]
137- if ( parent ) {
138- parent . children . push ( d )
139- } else {
140- // not found, add on top level then:
141- devices . push ( d )
142- }
143- } else {
144- devices . push ( d )
145- }
146- } )
147-
148- const getDeviceContent = ( parentDevice : DeviceInHierarchy | null , d : DeviceInHierarchy ) : JSX . Element => {
149- const content : JSX . Element [ ] = [
150- < DeviceItem
151- key = { 'device' + d . device . _id }
152- parentDevice = { parentDevice ?. device ?? null }
153- device = { d . device }
154- hasChildren = { d . children . length !== 0 }
155- debugState = { this . state . deviceDebugState . get ( d . device . _id ) }
156- /> ,
157- ]
158- if ( d . children . length ) {
159- const children : JSX . Element [ ] = [ ]
160- _ . each ( d . children , ( child : DeviceInHierarchy ) =>
161- children . push (
162- < li key = { 'childdevice' + child . device . _id } className = "child-device-li" >
163- { getDeviceContent ( d , child ) }
164- </ li >
165- )
166- )
167- content . push (
168- < div key = { d . device . _id + '_children' } className = "children" >
169- < ul className = "childlist" > { children } </ ul >
170- </ div >
171- )
172- }
173- return (
174- < div key = { d . device . _id + '_parent' } className = "device-item-container" >
175- { content }
176- </ div >
177- )
178- }
138+ const interval = setInterval ( refreshDebugStates , 1000 )
179139
180- return (
181- < React . Fragment >
182- { this . props . coreSystem && (
183- < CoreItem coreSystem = { this . props . coreSystem } systemStatus = { this . state . systemStatus } />
184- ) }
185- { _ . map ( devices , ( d ) => getDeviceContent ( null , d ) ) }
186- </ React . Fragment >
187- )
140+ return ( ) => {
141+ clearInterval ( interval )
142+ destroyed = true
188143 }
144+ } , [ t , JSON . stringify ( playoutDeviceIds ) ] )
189145
190- render ( ) : JSX . Element {
191- const { t } = this . props
146+ return playoutDebugStates
147+ }
192148
193- return (
194- < div className = "mhl gutter system-status" >
195- < header className = "mbs" >
196- < h1 > { t ( 'System Status' ) } </ h1 >
197- </ header >
198- < div className = "mod mvl" > { this . renderPeripheralDevices ( ) } </ div >
199- </ div >
200- )
149+ function convertDevicesIntoHeirarchy ( devices : PeripheralDevice [ ] ) : DeviceInHierarchy [ ] {
150+ const devicesMap = new Map < PeripheralDeviceId , DeviceInHierarchy > ( )
151+ const devicesToAdd : DeviceInHierarchy [ ] = [ ]
152+
153+ // First, add all as references:
154+ for ( const device of devices ) {
155+ const entry : DeviceInHierarchy = {
156+ device : device ,
157+ children : [ ] ,
201158 }
159+ devicesMap . set ( device . _id , entry )
160+ devicesToAdd . push ( entry )
202161 }
203- )
162+
163+ // Then, map and add devices:
164+ const devicesHeirarchy : Array < DeviceInHierarchy > = [ ]
165+ for ( const entry of devicesToAdd ) {
166+ if ( entry . device . parentDeviceId ) {
167+ const parent = devicesMap . get ( entry . device . parentDeviceId )
168+ if ( parent ) {
169+ parent . children . push ( entry )
170+ } else {
171+ // not found, add on top level then:
172+ devicesHeirarchy . push ( entry )
173+ }
174+ } else {
175+ devicesHeirarchy . push ( entry )
176+ }
177+ }
178+
179+ return devicesHeirarchy
180+ }
181+
182+ interface DeviceItemWithChildrenProps {
183+ playoutDebugStates : Map < PeripheralDeviceId , object >
184+ parentDevice : DeviceInHierarchy | null
185+ device : DeviceInHierarchy
186+ }
187+
188+ function DeviceItemWithChildren ( { playoutDebugStates, device, parentDevice } : DeviceItemWithChildrenProps ) {
189+ return (
190+ < div key = { device . device . _id + '_parent' } className = "device-item-container" >
191+ < DeviceItem
192+ parentDevice = { parentDevice ?. device ?? null }
193+ device = { device . device }
194+ hasChildren = { device . children . length !== 0 }
195+ debugState = { playoutDebugStates . get ( device . device . _id ) }
196+ />
197+
198+ { device . children . length > 0 && (
199+ < div className = "children" >
200+ < ul className = "childlist" >
201+ { device . children . map ( ( child ) => (
202+ < li key = { unprotectString ( child . device . _id ) } className = "child-device-li" >
203+ < DeviceItemWithChildren playoutDebugStates = { playoutDebugStates } parentDevice = { device } device = { child } />
204+ </ li >
205+ ) ) }
206+ </ ul >
207+ </ div >
208+ ) }
209+ </ div >
210+ )
211+ }
0 commit comments