Skip to content

Commit a7e5995

Browse files
committed
chore: refactor
1 parent 4c72f74 commit a7e5995

File tree

3 files changed

+169
-165
lines changed

3 files changed

+169
-165
lines changed

packages/mos-gateway/src/mosHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { MosGatewayConfig } from './generated/options'
3838
import { MosDeviceConfig } from './generated/devices'
3939
import { PeripheralDeviceForDevice } from '@sofie-automation/server-core-integration'
4040
import _ = require('underscore')
41-
import { MosStatusHandler } from './mosStatusHandler'
41+
import { MosStatusHandler } from './mosStatus/handler'
4242
import { isPromise } from 'util/types'
4343

4444
export interface MosConfig {
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { IMOSObjectStatus } from '@mos-connection/connector'
2+
import type { MosDeviceStatusesConfig } from '../generated/devices'
3+
import {
4+
IngestPartPlaybackStatus,
5+
type IngestPartStatus,
6+
type IngestRundownStatus,
7+
} from '@sofie-automation/shared-lib/dist/ingest/rundownStatus'
8+
9+
export const MOS_STATUS_UNKNOWN = '' as IMOSObjectStatus // Force the status to be empty, which isn't a valid state in the enum
10+
11+
export type SomeStatusEntry = StoryStatusEntry | ItemStatusEntry
12+
13+
export interface ItemStatusEntry {
14+
type: 'item'
15+
rundownExternalId: string
16+
storyId: string
17+
itemId: string
18+
mosStatus: IMOSObjectStatus
19+
}
20+
21+
export interface StoryStatusEntry {
22+
type: 'story'
23+
rundownExternalId: string
24+
storyId: string
25+
mosStatus: IMOSObjectStatus
26+
}
27+
28+
export function diffStatuses(
29+
config: MosDeviceStatusesConfig,
30+
previousStatuses: IngestRundownStatus | undefined,
31+
newStatuses: IngestRundownStatus | undefined
32+
): SomeStatusEntry[] {
33+
const rundownExternalId = previousStatuses?.externalId ?? newStatuses?.externalId
34+
35+
if ((!previousStatuses && !newStatuses) || !rundownExternalId) return []
36+
37+
const statuses: SomeStatusEntry[] = []
38+
39+
const previousStories = buildStoriesMap(previousStatuses)
40+
const newStories = buildStoriesMap(newStatuses)
41+
42+
// Process any removed stories first
43+
for (const [storyId, story] of previousStories) {
44+
if (!newStories.has(storyId)) {
45+
// The story has been removed
46+
statuses.push({
47+
type: 'story',
48+
rundownExternalId,
49+
storyId,
50+
mosStatus: MOS_STATUS_UNKNOWN,
51+
})
52+
53+
// Clear any items too
54+
for (const [itemId, isReady] of Object.entries<boolean | undefined>(story.itemsReady)) {
55+
if (isReady === undefined) continue
56+
57+
statuses.push({
58+
type: 'item',
59+
rundownExternalId,
60+
storyId,
61+
itemId: itemId,
62+
mosStatus: MOS_STATUS_UNKNOWN,
63+
})
64+
}
65+
}
66+
}
67+
68+
// Then any remaining stories in order
69+
for (const [storyId, status] of newStories) {
70+
const previousStatus = previousStories.get(storyId)
71+
72+
const newMosStatus = buildMosStatus(config, status.playbackStatus, status.isReady, newStatuses?.active)
73+
if (
74+
newMosStatus !== null &&
75+
(!previousStatus ||
76+
buildMosStatus(
77+
config,
78+
previousStatus.playbackStatus,
79+
previousStatus.isReady,
80+
previousStatuses?.active
81+
) !== newMosStatus)
82+
) {
83+
statuses.push({
84+
type: 'story',
85+
rundownExternalId,
86+
storyId,
87+
mosStatus: newMosStatus,
88+
})
89+
}
90+
91+
// Diff each item in the story
92+
const previousItemStatuses = previousStatus?.itemsReady ?? {}
93+
const allItemIds = new Set<string>([...Object.keys(status.itemsReady), ...Object.keys(previousItemStatuses)])
94+
95+
for (const itemId of allItemIds) {
96+
const newReady = status.itemsReady[itemId]
97+
const previousReady = previousItemStatuses[itemId]
98+
99+
const newMosStatus =
100+
newReady !== undefined
101+
? buildMosStatus(config, status.playbackStatus, newReady, newStatuses?.active)
102+
: null
103+
const previousMosStatus =
104+
previousReady !== undefined && previousStatus
105+
? buildMosStatus(config, previousStatus.playbackStatus, previousReady, previousStatuses?.active)
106+
: null
107+
108+
if (newMosStatus !== null && previousMosStatus !== newMosStatus) {
109+
statuses.push({
110+
type: 'item',
111+
rundownExternalId,
112+
storyId,
113+
itemId,
114+
mosStatus: newMosStatus,
115+
})
116+
}
117+
}
118+
}
119+
120+
return statuses
121+
}
122+
123+
function buildStoriesMap(state: IngestRundownStatus | undefined): Map<string, IngestPartStatus> {
124+
const stories = new Map<string, IngestPartStatus>()
125+
126+
if (state) {
127+
for (const segment of state.segments) {
128+
for (const part of segment.parts) {
129+
stories.set(part.externalId, part)
130+
}
131+
}
132+
}
133+
134+
return stories
135+
}
136+
137+
function buildMosStatus(
138+
config: MosDeviceStatusesConfig,
139+
playbackStatus: IngestPartPlaybackStatus,
140+
isReady: boolean | null | undefined,
141+
active: IngestRundownStatus['active'] | undefined
142+
): IMOSObjectStatus | null {
143+
if (active === 'inactive') return MOS_STATUS_UNKNOWN
144+
if (active === 'rehearsal' && !config.sendInRehearsal) return null
145+
146+
switch (playbackStatus) {
147+
case IngestPartPlaybackStatus.PLAY:
148+
return IMOSObjectStatus.PLAY
149+
case IngestPartPlaybackStatus.STOP:
150+
return IMOSObjectStatus.STOP
151+
default:
152+
switch (isReady) {
153+
case true:
154+
return IMOSObjectStatus.READY
155+
case false:
156+
return IMOSObjectStatus.NOT_READY
157+
default:
158+
return MOS_STATUS_UNKNOWN
159+
}
160+
}
161+
}
Lines changed: 7 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {
22
getMosTypes,
3-
IMOSItemStatus,
3+
type IMOSItemStatus,
44
IMOSObjectStatus,
5-
IMOSStoryStatus,
6-
MosTypes,
5+
type IMOSStoryStatus,
6+
type MosTypes,
77
type IMOSDevice,
88
} from '@mos-connection/connector'
9-
import type { MosDeviceStatusesConfig } from './generated/devices'
10-
import type { CoreMosDeviceHandler } from './CoreMosDeviceHandler'
9+
import type { MosDeviceStatusesConfig } from '../generated/devices'
10+
import type { CoreMosDeviceHandler } from '../CoreMosDeviceHandler'
1111
import {
1212
assertNever,
1313
type Observer,
@@ -16,16 +16,11 @@ import {
1616
stringifyError,
1717
SubscriptionId,
1818
} from '@sofie-automation/server-core-integration'
19-
import {
20-
IngestPartPlaybackStatus,
21-
type IngestPartStatus,
22-
type IngestRundownStatus,
23-
} from '@sofie-automation/shared-lib/dist/ingest/rundownStatus'
19+
import type { IngestRundownStatus } from '@sofie-automation/shared-lib/dist/ingest/rundownStatus'
2420
import type { RundownId } from '@sofie-automation/shared-lib/dist/core/model/Ids'
2521
import type winston = require('winston')
2622
import { Queue } from '@sofie-automation/server-core-integration/dist/lib/queue'
27-
28-
const MOS_STATUS_UNKNOWN = '' as IMOSObjectStatus // Force the status to be empty, which isn't a valid state in the enum
23+
import { diffStatuses } from './diff'
2924

3025
export class MosStatusHandler {
3126
readonly #logger: winston.Logger
@@ -166,155 +161,3 @@ export class MosStatusHandler {
166161
if (this.#subId) this.#coreMosHandler.core.unsubscribe(this.#subId)
167162
}
168163
}
169-
170-
type SomeStatusEntry = StoryStatusEntry | ItemStatusEntry
171-
172-
interface ItemStatusEntry {
173-
type: 'item'
174-
rundownExternalId: string
175-
storyId: string
176-
itemId: string
177-
mosStatus: IMOSObjectStatus
178-
}
179-
180-
interface StoryStatusEntry {
181-
type: 'story'
182-
rundownExternalId: string
183-
storyId: string
184-
mosStatus: IMOSObjectStatus
185-
}
186-
187-
function diffStatuses(
188-
config: MosDeviceStatusesConfig,
189-
previousStatuses: IngestRundownStatus | undefined,
190-
newStatuses: IngestRundownStatus | undefined
191-
): SomeStatusEntry[] {
192-
const rundownExternalId = previousStatuses?.externalId ?? newStatuses?.externalId
193-
194-
if ((!previousStatuses && !newStatuses) || !rundownExternalId) return []
195-
196-
const statuses: SomeStatusEntry[] = []
197-
198-
const previousStories = buildStoriesMap(previousStatuses)
199-
const newStories = buildStoriesMap(newStatuses)
200-
201-
// Process any removed stories first
202-
for (const [storyId, story] of previousStories) {
203-
if (!newStories.has(storyId)) {
204-
// The story has been removed
205-
statuses.push({
206-
type: 'story',
207-
rundownExternalId,
208-
storyId,
209-
mosStatus: MOS_STATUS_UNKNOWN,
210-
})
211-
212-
// Clear any items too
213-
for (const [itemId, isReady] of Object.entries<boolean | undefined>(story.itemsReady)) {
214-
if (isReady === undefined) continue
215-
216-
statuses.push({
217-
type: 'item',
218-
rundownExternalId,
219-
storyId,
220-
itemId: itemId,
221-
mosStatus: MOS_STATUS_UNKNOWN,
222-
})
223-
}
224-
}
225-
}
226-
227-
// Then any remaining stories in order
228-
for (const [storyId, status] of newStories) {
229-
const previousStatus = previousStories.get(storyId)
230-
231-
const newMosStatus = buildMosStatus(config, status.playbackStatus, status.isReady, newStatuses?.active)
232-
if (
233-
newMosStatus !== null &&
234-
(!previousStatus ||
235-
buildMosStatus(
236-
config,
237-
previousStatus.playbackStatus,
238-
previousStatus.isReady,
239-
previousStatuses?.active
240-
) !== newMosStatus)
241-
) {
242-
statuses.push({
243-
type: 'story',
244-
rundownExternalId,
245-
storyId,
246-
mosStatus: newMosStatus,
247-
})
248-
}
249-
250-
// Diff each item in the story
251-
const previousItemStatuses = previousStatus?.itemsReady ?? {}
252-
const allItemIds = new Set<string>([...Object.keys(status.itemsReady), ...Object.keys(previousItemStatuses)])
253-
254-
for (const itemId of allItemIds) {
255-
const newReady = status.itemsReady[itemId]
256-
const previousReady = previousItemStatuses[itemId]
257-
258-
const newMosStatus =
259-
newReady !== undefined
260-
? buildMosStatus(config, status.playbackStatus, newReady, newStatuses?.active)
261-
: null
262-
const previousMosStatus =
263-
previousReady !== undefined && previousStatus
264-
? buildMosStatus(config, previousStatus.playbackStatus, previousReady, previousStatuses?.active)
265-
: null
266-
267-
if (newMosStatus !== null && previousMosStatus !== newMosStatus) {
268-
statuses.push({
269-
type: 'item',
270-
rundownExternalId,
271-
storyId,
272-
itemId,
273-
mosStatus: newMosStatus,
274-
})
275-
}
276-
}
277-
}
278-
279-
return statuses
280-
}
281-
282-
function buildStoriesMap(state: IngestRundownStatus | undefined): Map<string, IngestPartStatus> {
283-
const stories = new Map<string, IngestPartStatus>()
284-
285-
if (state) {
286-
for (const segment of state.segments) {
287-
for (const part of segment.parts) {
288-
stories.set(part.externalId, part)
289-
}
290-
}
291-
}
292-
293-
return stories
294-
}
295-
296-
function buildMosStatus(
297-
config: MosDeviceStatusesConfig,
298-
playbackStatus: IngestPartPlaybackStatus,
299-
isReady: boolean | null | undefined,
300-
active: IngestRundownStatus['active'] | undefined
301-
): IMOSObjectStatus | null {
302-
if (active === 'inactive') return MOS_STATUS_UNKNOWN
303-
if (active === 'rehearsal' && !config.sendInRehearsal) return null
304-
305-
switch (playbackStatus) {
306-
case IngestPartPlaybackStatus.PLAY:
307-
return IMOSObjectStatus.PLAY
308-
case IngestPartPlaybackStatus.STOP:
309-
return IMOSObjectStatus.STOP
310-
default:
311-
switch (isReady) {
312-
case true:
313-
return IMOSObjectStatus.READY
314-
case false:
315-
return IMOSObjectStatus.NOT_READY
316-
default:
317-
return MOS_STATUS_UNKNOWN
318-
}
319-
}
320-
}

0 commit comments

Comments
 (0)