Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: strict tsconfig and use threadedClass #60

Merged
merged 70 commits into from
Jan 17, 2020
Merged
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
d1e99aa
wip: enable strict tsconfig
Julusian Oct 7, 2019
e9a4c2f
wip: replace AbstractCommand with better typed abstract classes
Julusian Oct 7, 2019
6c5ec03
fix some bad typings
Julusian Oct 7, 2019
e681623
feat: some changes for atem-state
Julusian Oct 22, 2019
416a520
fix: TransitionMixCommand using wrong base class
Julusian Oct 22, 2019
7d48ff6
feat: updateProps only updates the valid properties (according to the…
Julusian Oct 22, 2019
bf688ba
feat: add test to ensure all serialized properties are covered by Mas…
Julusian Oct 22, 2019
0e2317e
Merge branch 'develop' into feat/strict-tsconfig
Julusian Dec 7, 2019
3b76248
fix: failing tests
Julusian Nov 29, 2019
458666b
wip: fix: still media uploading
Julusian Nov 29, 2019
42a99c9
feat: some tidying of socket-child and parsing of previously unknown …
Julusian Nov 30, 2019
b261ee2
fix: incorrectly handling acks for sent packets around the wrap point
Julusian Dec 1, 2019
14dda0b
feat: unlock media pool as part of transfer. this avoids race conditi…
Julusian Dec 1, 2019
b26293a
fix: tslint member-access rule
Julusian Dec 1, 2019
cb255fb
fix: tidying
Julusian Dec 1, 2019
b136ac0
fix: packet management incorrect if one times out
Julusian Dec 1, 2019
1b32e35
fix: not resending packets when id has wrapped and retransmit is need…
Julusian Dec 2, 2019
022918c
fix: handle udp message errors gracefully
Julusian Dec 2, 2019
22ff629
chore: rename CommandTimeout event to be more generic
Julusian Dec 2, 2019
537d907
feat: retransmit on demand and handle impossible retransmits by reset…
Julusian Dec 2, 2019
35a85a5
feat: individual commands cannot be rejected
Julusian Dec 2, 2019
447f1d1
fix: ensure the retransmit fromId is within range
Julusian Dec 2, 2019
570a6f1
wip: atemSocketChild takes callbacks instead of being an eventEmitter
Julusian Dec 2, 2019
234284e
fix: wrap transferIndex once it reaches maximum
Julusian Dec 4, 2019
c31fc1a
fix: tidy up multiviewer state object
Julusian Dec 4, 2019
a451a90
fix: create new state object on connection start
Julusian Dec 4, 2019
a9f5269
fix: move exit-hook inside dataTransferManager to ensure it gets run …
Julusian Dec 4, 2019
d20b185
feat: Replace usage of fork with threadedClass
Julusian Oct 18, 2019
c0a2471
feat(tests): Tests for basic atem methods
Julusian Oct 19, 2019
caa9ac5
feat(tests): For atemsocket class (child process wrapper)
Julusian Dec 1, 2019
69c7d32
fix: use dev threadedclass and remove some hacks
Julusian Dec 4, 2019
3b07d0a
fix: replace IPCMessageType with stricter basic strings
Julusian Dec 4, 2019
8b840f4
fix: make cleanup of connections better
Julusian Dec 4, 2019
bf27947
fix: typedoc
Julusian Dec 4, 2019
1ac7da8
feat: batch commands
Julusian Dec 5, 2019
0086eee
fix: some command tests
Julusian Dec 6, 2019
d1cf174
feat: tests for audio and clips uploading
Julusian Dec 6, 2019
0f005ec
chore: refactor convertRGBAToYUV422
Julusian Dec 6, 2019
24eb1da
feat: ensure state update after deserialize is successful
Julusian Dec 6, 2019
29e61bd
feat: don't validate deserialized commands values are in range
Julusian Dec 6, 2019
062e977
feat: attempt to handle out of range features safely
Julusian Dec 6, 2019
c52a3e8
chore: use Buffer.readUInt8 instead of direct index operator
Julusian Dec 6, 2019
698c19e
fix: gracefully emit error if applyToState fails
Julusian Dec 6, 2019
aa9cea6
fix: ignore dve commands if not supported
Julusian Dec 6, 2019
e07fb62
chore: tidy enums
Julusian Dec 7, 2019
652603d
chore: more socket-child tests
Julusian Dec 7, 2019
5a89910
chore: enable test
Julusian Dec 7, 2019
093c2b7
chore: bump version as this is a breaking change
Julusian Dec 7, 2019
ad6c523
chore: update readme and some TODOs
Julusian Dec 7, 2019
17b1fdb
fix: export keyer state types
Julusian Dec 9, 2019
115b6d0
chore: tidy datatransfer command rejection
Julusian Dec 9, 2019
85ac91f
fix: some defaults
Julusian Dec 9, 2019
d6e02d8
feat: split Atem into a BasicAtem base class, as an easier to mock al…
Julusian Dec 9, 2019
f646e92
feat: emit more events in batches, rather than for individual commands
Julusian Dec 9, 2019
137d135
feat: parse some more deviceprofile commands
Julusian Dec 10, 2019
d884eb1
fix: upstream keyers using missing field to check validity
Julusian Dec 14, 2019
611d12c
fix: format received buffers as hex in debug messages
Julusian Dec 14, 2019
e57d44c
fix: tests
Julusian Dec 14, 2019
1f7ef94
feat: Add tests to ensure device connection is successful
Julusian Dec 16, 2019
06ec5a5
feat: parse multiviewerConfigCommand
Julusian Dec 16, 2019
6048802
fix: tests
Julusian Dec 16, 2019
1f8e5fd
chore: add atem-mini connection test case
Julusian Dec 20, 2019
6be1024
fix: add atem-mini id
Julusian Jan 10, 2020
97d0522
feat: use objects instead of sparse arrays
Julusian Jan 14, 2020
9530d79
chore: update dependencies
Julusian Jan 14, 2020
2f1dd1a
feat: refactor state to be purely interfaces, to allow for better clo…
Julusian Jan 15, 2020
d4f26ee
feat: throw InvalidIdError when trying to applyToState. These are onl…
Julusian Jan 15, 2020
77ef0d1
fix: do a todo
Julusian Jan 15, 2020
76cadfb
Merge branch 'develop' into feat/strict-tsconfig
Julusian Jan 15, 2020
cdad157
chore: add test case for TVS HD 8.1
Jan 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix some bad typings
  • Loading branch information
Julusian committed Dec 7, 2019
commit 6c5ec03d56f2f1856c68c870e7ed577198782502
33 changes: 20 additions & 13 deletions src/atem.ts
Original file line number Diff line number Diff line change
@@ -34,6 +34,12 @@ export interface AtemOptions {
externalLog?: (arg0?: any,arg1?: any,arg2?: any,arg3?: any) => void
}

interface SentCommand {
command: ISerializableCommand
resolve: (cmd: ISerializableCommand) => void
reject: (cmd: ISerializableCommand) => void
}

export class Atem extends EventEmitter {
DEFAULT_PORT = 9910
RECONNECT_INTERVAL = 5000
@@ -45,12 +51,9 @@ export class Atem extends EventEmitter {
private socket: AtemSocket
private dataTransferManager: DT.DataTransferManager
private _log: (...args: any[]) => void
private _sentQueue: {[packetId: string]: ISerializableCommand } = {}
private _sentQueue: {[packetId: string]: SentCommand } = {}

on: ((event: 'error', listener: (message: any) => void) => this) &
((event: 'connected', listener: () => void) => this) &
((event: 'disconnected', listener: () => void) => this) &
((event: 'stateChanged', listener: (state: AtemState, path: string) => void) => this)
on!: ((event: 'error', listener: (message: any) => void) => this) & ((event: 'connected', listener: () => void) => this) & ((event: 'disconnected', listener: () => void) => this) & ((event: 'stateChanged', listener: (state: AtemState, path: string) => void) => this)

constructor (options?: AtemOptions) {
super()
@@ -103,12 +106,14 @@ export class Atem extends EventEmitter {
})
}

sendCommand (command: ISerializableCommand): Promise<any> {
sendCommand (command: ISerializableCommand): Promise<ISerializableCommand> {
const nextPacketId = this.socket.nextPacketId
this._sentQueue[nextPacketId] = command
return new Promise((resolve, reject) => {
command.resolve = resolve
command.reject = reject
this._sentQueue[nextPacketId] = {
command,
resolve,
reject
}
this.socket._sendCommand(command, nextPacketId).catch(reject)
})
}
@@ -465,15 +470,17 @@ export class Atem extends EventEmitter {
}

private _resolveCommand (trackingId: number) {
if (this._sentQueue[trackingId]) {
this._sentQueue[trackingId].resolve(this._sentQueue[trackingId])
const sent = this._sentQueue[trackingId]
if (sent) {
sent.resolve(sent.command)
delete this._sentQueue[trackingId]
}
}

private _rejectCommand (trackingId: number) {
if (this._sentQueue[trackingId]) {
this._sentQueue[trackingId].reject(this._sentQueue[trackingId])
const sent = this._sentQueue[trackingId]
if (sent) {
sent.reject(sent.command)
delete this._sentQueue[trackingId]
}
}
1 change: 0 additions & 1 deletion src/commands/Audio/AudioMixerInputCommand.ts
Original file line number Diff line number Diff line change
@@ -56,7 +56,6 @@ export class AudioMixerInputUpdateCommand extends DeserializedCommand<AudioChann
}

applyToState (state: AtemState) {
// const channel = state.audio.getChannel(this.index)
state.audio.channels[this.index] = {
...state.audio.channels[this.index],
...this.properties
3 changes: 3 additions & 0 deletions src/commands/MixEffects/FadeToBlack/FadeToBlackRateCommand.ts
Original file line number Diff line number Diff line change
@@ -44,6 +44,9 @@ export class FadeToBlackRateUpdateCommand extends DeserializedCommand<{ rate: nu
applyToState (state: AtemState) {
const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.fadeToBlack = {
isFullyBlack: false,
inTransition: false,
remainingFrames: 0,
...mixEffect.fadeToBlack,
rate: this.properties.rate
}
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ export class FadeToBlackStateCommand extends DeserializedCommand<FadeToBlackProp
applyToState (state: AtemState) {
const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.fadeToBlack = {
rate: 0,
...mixEffect.fadeToBlack,
...this.properties
}
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ export class MixEffectKeyPropertiesGetCommand extends DeserializedCommand<Upstre
applyToState (state: AtemState) {
const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.upstreamKeyers[this.properties.upstreamKeyerId] = {
...mixEffect.upstreamKeyers[this.properties.upstreamKeyerId],
...mixEffect.getUpstreamKeyer(this.properties.upstreamKeyerId),
...this.properties
}
return `video.ME.${this.mixEffect}.upstreamKeyers.${this.properties.upstreamKeyerId}`
5 changes: 1 addition & 4 deletions src/commands/MixEffects/ProgramInputCommand.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { BasicWritableCommand, DeserializedCommand } from '../CommandBase'
import { AtemState } from '../../state'
import { Util } from '../..'

export interface InputSource {
source: number
}
import { InputSource } from './PreviewInputCommand'

export class ProgramInputCommand extends BasicWritableCommand<InputSource> {
static readonly rawName = 'CPgI'
2 changes: 1 addition & 1 deletion src/commands/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ const commandConverters: CommandTestConverterSet = {
'_pin': {
idAliases: {},
propertyAliases: {
'name': (val: any) => ({ val, name: 'deviceName' })
'name': (val: any) => ({ val, name: 'productIdentifier' })
}
},
'SSBP': {
81 changes: 36 additions & 45 deletions src/dataTransfer/dataLock.ts
Original file line number Diff line number Diff line change
@@ -4,82 +4,83 @@ import DataTransfer from './dataTransfer'
import DataTransferClip from './dataTransferClip'

export default class DataLock {
storeId: number
state: number
transfer: DataTransfer | undefined
queue: Array<DataTransfer> = []
private storeId: number
private isLocked: boolean
private taskQueue: Array<DataTransfer> = []

commandQueue: Array<Commands.ISerializableCommand> = []
public activeTransfer: DataTransfer | undefined

constructor (storeId: number, commandQueue: Array<Commands.ISerializableCommand>) {
private queueCommand: (cmd: Commands.ISerializableCommand) => void

constructor (storeId: number, queueCommand: (cmd: Commands.ISerializableCommand) => void) {
this.storeId = storeId
this.commandQueue = commandQueue
this.queueCommand = queueCommand
this.isLocked = false
}

enqueue (transfer: DataTransfer) {
this.queue.push(transfer)
if (!this.transfer) {
this.taskQueue.push(transfer)
if (!this.activeTransfer) {
this.dequeueAndRun()
}
}

dequeueAndRun () {
if ((this.transfer === undefined || this.transfer.state === Enums.TransferState.Finished) && this.queue.length > 0) {
this.transfer = this.queue.shift()
private dequeueAndRun () {
if ((this.activeTransfer === undefined || this.activeTransfer.state === Enums.TransferState.Finished) && this.taskQueue.length > 0) {
this.activeTransfer = this.taskQueue.shift()

if (this.state === 1) {
if (this.isLocked) {
this.lockObtained()
} else {
this._getLock()
this.queueCommand(new Commands.LockStateCommand(this.storeId, true))
}
} else if (this.transfer) {
this.transfer.fail(new Error('Tried to run next transfer, but one was still in-progress'))
}
}

lockObtained () {
this.state = 1
if (this.transfer && this.transfer.state === Enums.TransferState.Queued) {
this.transfer.gotLock()
this.isLocked = true
if (this.activeTransfer && this.activeTransfer.state === Enums.TransferState.Queued) {
this.activeTransfer.gotLock().forEach(cmd => this.queueCommand(cmd))
}
}

lostLock () {
this.state = 0
if (this.transfer && this.transfer.state !== Enums.TransferState.Finished) {
this.isLocked = false
if (this.activeTransfer && this.activeTransfer.state !== Enums.TransferState.Finished) {
// @todo: dequeue any old commands
this.transfer.fail(new Error('Lost lock mid-transfer'))
this.activeTransfer.rejectPromise(new Error('Lost lock mid-transfer'))
}
this.transfer = undefined
this.activeTransfer = undefined
this.dequeueAndRun()
}

updateLock (locked: boolean) {
this.state = locked ? 1 : 0
this.isLocked = locked
}

transferFinished () {
if (this.transfer && this.transfer.state === Enums.TransferState.Finished) {
this.transfer.resolvePromise(this.transfer)
if (this.activeTransfer && this.activeTransfer.state === Enums.TransferState.Finished) {
this.activeTransfer.resolvePromise(this.activeTransfer)
this.activeTransfer = undefined
}

if (this.queue.length > 0) {
if (this.taskQueue.length > 0) {
this.dequeueAndRun()
} else { // unlock
this._releaseLock()
this.queueCommand(new Commands.LockStateCommand(this.storeId, false))
}
}

transferErrored (code: number) {
if (this.transfer) {
if (this.activeTransfer) {
switch (code) {
case 1: // Probably means "retry".
if (this.transfer instanceof DataTransferClip) {
if (this.activeTransfer instanceof DataTransferClip) {
// Retry the last frame.
this.transfer.frames[this.transfer.curFrame].start()
this.activeTransfer.frames[this.activeTransfer.curFrame].start().forEach(cmd => this.queueCommand(cmd))
} else {
// Retry the entire transfer.
this.transfer.start()
this.activeTransfer.start().forEach(cmd => this.queueCommand(cmd))
}
break
case 2: // Unknown.
@@ -89,23 +90,13 @@ export default class DataLock {
default:
// Abort the transfer.
// @todo: dequeue any old commands
this.transfer.fail(new Error(`Code ${code}`))
this.transfer = undefined
this.activeTransfer.rejectPromise(new Error(`Code ${code}`))
this.activeTransfer = undefined
this.dequeueAndRun()
}
} else {
this.transfer = undefined
this.activeTransfer = undefined
this.dequeueAndRun()
}
}

_getLock () {
const command = new Commands.LockStateCommand(this.storeId, true)
this.commandQueue.push(command)
}

_releaseLock () {
const command = new Commands.LockStateCommand(this.storeId, false)
this.commandQueue.push(command)
}
}
10 changes: 4 additions & 6 deletions src/dataTransfer/dataTransfer.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ export default abstract class DataTransfer {
readonly transferId: number
readonly storeId: number

commandQueue: Array<Commands.ISerializableCommand>
// commandQueue: Array<Commands.ISerializableCommand>

private readonly completionPromise: Promise<DataTransfer>
resolvePromise: (value?: DataTransfer | PromiseLike<DataTransfer> | undefined) => void
@@ -31,10 +31,8 @@ export default abstract class DataTransfer {
return this.completionPromise
}

fail (..._args: any[]): void {} // TODO remove
abstract start (): Commands.ISerializableCommand[]

abstract start (): void

abstract handleCommand (command: Commands.IDeserializedCommand): void
abstract gotLock (): void
abstract handleCommand (command: Commands.IDeserializedCommand): Commands.ISerializableCommand[]
abstract gotLock (): Commands.ISerializableCommand[]
}
13 changes: 5 additions & 8 deletions src/dataTransfer/dataTransferAudio.ts
Original file line number Diff line number Diff line change
@@ -11,21 +11,18 @@ export default class DataTransferAudio extends DataTransferFrame {
this.name = name
}

start () {
const command = new Commands.DataTransferUploadRequestCommand()
command.updateProps({
public start () {
const command = new Commands.DataTransferUploadRequestCommand({
transferId: this.transferId,
transferStoreId: this.storeId,
transferIndex: 0,
size: this.data.length,
mode: Enums.TransferMode.WriteAudio
})
this.commandQueue.push(command)
return [ command ]
}

sendDescription () {
const command = new Commands.DataTransferFileDescriptionCommand()
command.updateProps({ name: this.name, fileHash: this.hash, transferId: this.transferId })
this.commandQueue.push(command)
public sendDescription (): Commands.ISerializableCommand {
return new Commands.DataTransferFileDescriptionCommand({ name: this.name, fileHash: this.hash, transferId: this.transferId })
}
}
31 changes: 16 additions & 15 deletions src/dataTransfer/dataTransferClip.ts
Original file line number Diff line number Diff line change
@@ -16,43 +16,44 @@ export default class DataTransferClip extends DataTransfer {
this.name = name
}

start () {
const clearMediaCommand = new Commands.MediaPoolClearClipCommand(this.clipIndex)
clearMediaCommand.updateProps({
index: this.clipIndex
})
this.commandQueue.push(clearMediaCommand)
public start () {
const commands: Commands.ISerializableCommand[] = []
commands.push(new Commands.MediaPoolClearClipCommand(this.clipIndex))
this.frames[this.curFrame].state = Enums.TransferState.Locked
this.frames[this.curFrame].start()
commands.push(...this.frames[this.curFrame].start())
return commands
}

handleCommand (command: Commands.IDeserializedCommand) {
this.frames[this.curFrame].handleCommand(command)
public handleCommand (command: Commands.IDeserializedCommand): Commands.ISerializableCommand[] {
const commands: Commands.ISerializableCommand[] = []

commands.push(...this.frames[this.curFrame].handleCommand(command))
if (this.state !== Enums.TransferState.Transferring) this.state = Enums.TransferState.Transferring
if (this.frames[this.curFrame].state === Enums.TransferState.Finished) {
this.curFrame++
if (this.curFrame < this.frames.length) {
this.frames[this.curFrame].state = Enums.TransferState.Locked
this.frames[this.curFrame].start()
commands.push(...this.frames[this.curFrame].start())
} else {
const command = new Commands.MediaPoolSetClipCommand()
command.updateProps({
const command = new Commands.MediaPoolSetClipCommand({
index: this.clipIndex,
name: this.name,
frames: this.frames.length
})
this.commandQueue.push(command)
commands.push(command)
this.state = Enums.TransferState.Finished
}
}

return commands
}

get transferId () {
return this.frames[this.curFrame].transferId
}

gotLock () {
public gotLock (): Commands.ISerializableCommand[] {
this.state = Enums.TransferState.Locked
this.start()
return this.start()
}
}
Loading