Skip to content

Commit

Permalink
feat: refactor tricaster device SOFIE-2497 (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian authored Jun 26, 2024
1 parent 1d433f3 commit 4b4d2f6
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 160 deletions.
14 changes: 3 additions & 11 deletions packages/timeline-state-resolver/src/conductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
DeviceOptionsLawo,
DeviceOptionsSofieChef,
DeviceOptionsPharos,
DeviceOptionsTriCaster,
} from 'timeline-state-resolver-types'

import { DoOnTime } from './devices/doOnTime'
Expand All @@ -50,7 +51,6 @@ import { SingularLiveDevice, DeviceOptionsSingularLiveInternal } from './integra
import { VMixDevice, DeviceOptionsVMixInternal } from './integrations/vmix'
import { VizMSEDevice, DeviceOptionsVizMSEInternal } from './integrations/vizMSE'
import { TelemetricsDevice } from './integrations/telemetrics'
import { TriCasterDevice, DeviceOptionsTriCasterInternal } from './integrations/tricaster'
import { DeviceOptionsMultiOSCInternal, MultiOSCMessageDevice } from './integrations/multiOsc'
import { BaseRemoteDeviceIntegration, RemoteDeviceInstance } from './service/remoteDeviceInstance'
import type { ImplementedServiceDeviceTypes } from './service/devices'
Expand Down Expand Up @@ -555,15 +555,6 @@ export class Conductor extends EventEmitter<ConductorEvents> {
getCurrentTime,
threadedClassOptions
)
case DeviceType.TRICASTER:
return DeviceContainer.create<DeviceOptionsTriCasterInternal, typeof TriCasterDevice>(
'../../dist/integrations/tricaster/index.js',
'TriCasterDevice',
deviceId,
deviceOptions,
getCurrentTime,
threadedClassOptions
)
case DeviceType.MULTI_OSC:
return DeviceContainer.create<DeviceOptionsMultiOSC, typeof MultiOSCMessageDevice>(
'../../dist/integrations/multiOsc/index.js',
Expand All @@ -586,6 +577,7 @@ export class Conductor extends EventEmitter<ConductorEvents> {
case DeviceType.SHOTOKU:
case DeviceType.SOFIE_CHEF:
case DeviceType.TCPSEND:
case DeviceType.TRICASTER:
case DeviceType.QUANTEL: {
ensureIsImplementedAsService(deviceOptions.type)

Expand Down Expand Up @@ -1509,7 +1501,7 @@ export type DeviceOptionsAnyInternal =
| DeviceOptionsShotoku
| DeviceOptionsVizMSEInternal
| DeviceOptionsTelemetrics
| DeviceOptionsTriCasterInternal
| DeviceOptionsTriCaster
| DeviceOptionsMultiOSC

function removeParentFromState(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { EventEmitter } from 'eventemitter3'
import { MockTime } from '../../../__tests__/mockTime'
import {
DeviceType,
SomeMappingTricaster,
MappingTricasterType,
TimelineContentTypeTriCaster,
TimelineContentTriCasterME,
Mapping,
Timeline,
TSRTimelineContent,
} from 'timeline-state-resolver-types'
import { TriCasterDevice } from '..'
import { TriCasterConnectionEvents, TriCasterConnection } from '../triCasterConnection'
import { literal } from '../../../lib'
import { wrapIntoResolvedInstance } from './helpers'
import { getDeviceContext } from '../../__tests__/testlib'

const MOCK_CONNECT = jest.fn()
const MOCK_SEND = jest.fn(async () => Promise.resolve())
const MOCK_CLOSE = jest.fn()

jest.mock('../triCasterConnection', () => ({
Expand All @@ -24,17 +25,14 @@ jest.mock('../triCasterConnection', () => ({
}))

describe('TriCasterDevice', () => {
const mockTime = new MockTime()
beforeEach(() => {
mockTime.init()
;(TriCasterConnection as jest.Mock).mockClear()
MOCK_CONNECT.mockClear()
MOCK_SEND.mockClear()
MOCK_CLOSE.mockClear()
})

test('resolves init when connected', async () => {
const device = createTriCasterDevice(mockTime)
const device = createTriCasterDevice()

const initResult = await device.init({
host: 'testhost',
Expand All @@ -51,14 +49,13 @@ describe('TriCasterDevice', () => {
})

test('sends commands', async () => {
const device = createTriCasterDevice(mockTime)
const device = createTriCasterDevice()

await device.init({
host: 'testhost',
port: 56789,
})

expect(MOCK_SEND).not.toHaveBeenCalled()
const mappings = {
tc_me0_0: literal<Mapping<SomeMappingTricaster>>({
device: DeviceType.TRICASTER,
Expand All @@ -70,53 +67,39 @@ describe('TriCasterDevice', () => {
}),
}

device.handleState({ time: 11000, layers: {}, nextEvents: [] }, mappings)
await mockTime.advanceTimeToTicks(11010)

// check that initial commands are sent after connection
// the number of them is not that relevant, but they have to only affect the mapped resource
expect(MOCK_SEND).toHaveBeenCalled()
expect(MOCK_SEND.mock.calls.filter((call) => (call as any)[0].target.startsWith('main')).length).toEqual(
MOCK_SEND.mock.calls.length
)
MOCK_SEND.mockClear()

device.handleState(
{
time: 12000,
layers: {
tc_me0_0: wrapIntoResolvedInstance<TimelineContentTriCasterME>({
layer: 'tc_me0_0',
enable: { while: '1' },
id: 't0',
content: {
deviceType: DeviceType.TRICASTER,
type: TimelineContentTypeTriCaster.ME,
me: { programInput: 'input2', previewInput: 'input3', transitionEffect: 5, transitionDuration: 20 },
},
}),
},
nextEvents: [],
const state1: Timeline.TimelineState<TSRTimelineContent> = { time: 11000, layers: {}, nextEvents: [] }
const tricasterState1 = device.convertTimelineStateToDeviceState(state1, mappings)
const commands1 = device.diffStates(undefined, tricasterState1, mappings)
expect(commands1).not.toHaveLength(0)

const state2: Timeline.TimelineState<TSRTimelineContent> = {
time: 12000,
layers: {
tc_me0_0: wrapIntoResolvedInstance<TimelineContentTriCasterME>({
layer: 'tc_me0_0',
enable: { while: '1' },
id: 't0',
content: {
deviceType: DeviceType.TRICASTER,
type: TimelineContentTypeTriCaster.ME,
me: { programInput: 'input2', previewInput: 'input3', transitionEffect: 5, transitionDuration: 20 },
},
}),
},
mappings
)
await mockTime.advanceTimeToTicks(12010)
expect(MOCK_SEND).toHaveBeenCalledTimes(4)
expect(MOCK_SEND).toHaveBeenNthCalledWith(1, { target: 'main', name: '_set_mix_effect_bin_index', value: 5 })
expect(MOCK_SEND).toHaveBeenNthCalledWith(2, { target: 'main', name: '_speed', value: 20 })
expect(MOCK_SEND).toHaveBeenNthCalledWith(3, { target: 'main_b', name: '_row_named_input', value: 'input2' })
expect(MOCK_SEND).toHaveBeenNthCalledWith(4, { target: 'main', name: '_auto' })
nextEvents: [],
}
const tricasterState2 = device.convertTimelineStateToDeviceState(state2, mappings)
const commands2 = device.diffStates(tricasterState1, tricasterState2, mappings)
expect(commands2).toHaveLength(4)
expect(commands2[0].command).toMatchObject({ target: 'main', name: '_set_mix_effect_bin_index', value: 5 })
expect(commands2[1].command).toMatchObject({ target: 'main', name: '_speed', value: 20 })
expect(commands2[2].command).toMatchObject({ target: 'main_b', name: '_row_named_input', value: 'input2' })
expect(commands2[3].command).toMatchObject({ target: 'main', name: '_auto' })
})
})

function createTriCasterDevice(mockTime): TriCasterDevice {
return new TriCasterDevice(
'tc0',
{
type: DeviceType.TRICASTER,
},
mockTime.getCurrentTime
)
function createTriCasterDevice(): TriCasterDevice {
return new TriCasterDevice(getDeviceContext())
}

class TriCasterConnectionMock extends EventEmitter<TriCasterConnectionEvents> {
Expand All @@ -139,7 +122,5 @@ class TriCasterConnectionMock extends EventEmitter<TriCasterConnectionEvents> {
)
})

send = MOCK_SEND

close = MOCK_CLOSE
}
Original file line number Diff line number Diff line change
Expand Up @@ -731,11 +731,13 @@ describe('TriCasterStateDiffer.getCommandsToAchieveState', () => {
expect(commands[0]).toEqual({
command: { target: 'main_dsk2', name: '_select_named_input', value: 'input2' },
timelineObjId: 't3',
context: '',
temporalPriority: 0,
})
expect(commands[1]).toEqual({
command: { target: 'main_dsk2', name: '_value', value: 1 },
timelineObjId: 't2',
context: '',
temporalPriority: 0,
})
})
Expand Down
Loading

0 comments on commit 4b4d2f6

Please sign in to comment.