Skip to content

Commit

Permalink
feat: macro data transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
mint-dewit committed Nov 16, 2021
1 parent 74ddb0e commit 3aaea78
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 9 deletions.
13 changes: 11 additions & 2 deletions src/commands/DataTransfer/DataTransferAckCommand.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DeserializedCommand } from '../CommandBase'
import { SymmetricalCommand } from '../CommandBase'

export interface DataTransferAckProps {
transferId: number
transferIndex: number
}

export class DataTransferAckCommand extends DeserializedCommand<DataTransferAckProps> {
export class DataTransferAckCommand extends SymmetricalCommand<DataTransferAckProps> {
public static readonly rawName = 'FTUA'

public static deserialize(rawCommand: Buffer): DataTransferAckCommand {
Expand All @@ -17,6 +17,15 @@ export class DataTransferAckCommand extends DeserializedCommand<DataTransferAckP
return new DataTransferAckCommand(properties)
}

public serialize () {
const buffer = Buffer.alloc(4)

buffer.writeUInt16BE(this.properties.transferId)
buffer.writeUInt8(this.properties.transferIndex, 2)

return buffer
}

public applyToState(): string[] {
// Nothing to do
return []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { DownloadRequestType } from '../../enums'
import { BasicWritableCommand } from '../CommandBase'

export interface DataTransferDownloadRequestProps {
transferId: number
transferStoreId: number
transferIndex: number
transferType: DownloadRequestType
}

export class DataTransferDownloadRequestCommand extends BasicWritableCommand<DataTransferDownloadRequestProps> {
Expand All @@ -13,10 +15,9 @@ export class DataTransferDownloadRequestCommand extends BasicWritableCommand<Dat
const buffer = Buffer.alloc(12)
buffer.writeUInt16BE(this.properties.transferId, 0)
buffer.writeUInt16BE(this.properties.transferStoreId, 2)
buffer.writeUInt8(this.properties.transferIndex, 7)
buffer.writeUInt16BE(this.properties.transferIndex, 6)

buffer.writeUInt16BE(0x00f9, 8)
buffer.writeUInt16BE(0x020f, 10)
buffer.writeUInt8(this.properties.transferType, 8)

return buffer
}
Expand Down
76 changes: 76 additions & 0 deletions src/dataTransfer/MacroLock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Commands, Enums } from '..'

import DataTransfer from './dataTransfer'

/**
* Macros don't have a lock so this is just a queue with the API of a lock.
*/
export default class MacroLock {
private taskQueue: Array<DataTransfer> = []

public activeTransfer: DataTransfer | undefined

private queueCommand: (cmd: Commands.ISerializableCommand) => void

constructor(queueCommand: (cmd: Commands.ISerializableCommand) => void) {
this.queueCommand = queueCommand
}

public enqueue(transfer: DataTransfer): Promise<DataTransfer> {
this.taskQueue.push(transfer)
if (!this.activeTransfer || this.activeTransfer.state === Enums.TransferState.Finished) {
this.dequeueAndRun()
}

return transfer.promise
}

private dequeueAndRun(): void {
if (
(this.activeTransfer === undefined || this.activeTransfer.state === Enums.TransferState.Finished) &&
this.taskQueue.length > 0
) {
this.activeTransfer = this.taskQueue.shift()
this.activeTransfer?.gotLock().forEach((cmd) => this.queueCommand(cmd))
}
}

public lockObtained(): void {
// I don't know what this means, let's hope it never happens
}

public lostLock(): void {
// can't lose a lock if you don't have a lock
}

public updateLock(): void {
//
}

public transferFinished(): void {
// great, no need to do anything, the transfer will have ack'ed already
}

public transferErrored(code: number): void {
if (this.activeTransfer) {
switch (code) {
case 1: // Probably means "retry".
this.activeTransfer.start().forEach((cmd) => this.queueCommand(cmd))
break
case 2: // Unknown: looks like macro not found
case 3: // Unknown.
case 4: // Unknown.
case 5: // Might mean "You don't have the lock"?
default:
// Abort the transfer.
// @todo: dequeue any old commands
this.activeTransfer.rejectPromise(new Error(`Code ${code}`))
this.activeTransfer = undefined
this.dequeueAndRun()
}
} else {
this.activeTransfer = undefined
this.dequeueAndRun()
}
}
}
2 changes: 1 addition & 1 deletion src/dataTransfer/dataTransferAudio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class DataTransferAudio extends DataTransferFrame {
transferStoreId: this.storeId,
transferIndex: 0,
size: this.data.length,
mode: Enums.TransferMode.WriteAudio,
mode: Enums.TransferMode.Write2,
})
return [command]
}
Expand Down
89 changes: 89 additions & 0 deletions src/dataTransfer/dataTransferMacro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { DownloadRequestType, TransferMode, TransferState } from '../enums'
import { Commands } from '..'

import DataTransfer from './dataTransfer'
import DataTransferFrame from './dataTransferFrame'

export class DataDownloadMacro extends DataTransfer {
public data = Buffer.from([])
public curFrame = 0

constructor(transferId: number, public readonly macroIndex: number) {
super(transferId, 255)
}

public start(): Commands.ISerializableCommand[] {
const commands: Commands.ISerializableCommand[] = [
new Commands.DataTransferDownloadRequestCommand({
transferId: this.transferId,
transferStoreId: 0xffff,
transferIndex: this.macroIndex,
transferType: DownloadRequestType.Macro,
})
]
return commands
}

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

if (command.constructor.name === Commands.DataTransferDataCommand.name) {
this.data = (command as Commands.DataTransferDataCommand).properties.body

// todo - have we received all data? maybe check if the command.body < max_len

commands.push(new Commands.DataTransferAckCommand({
transferId: this.transferId,
transferIndex: this.macroIndex
}))
this.state = TransferState.Finished
this.resolvePromise(this)
}

return commands
}

public gotLock(): Commands.ISerializableCommand[] {
// yeah so locks don't exist here...
return this.start()
}
}


export class DataUploadMacro extends DataTransferFrame {

constructor(transferId: number, public readonly macroIndex: number, public readonly data: Buffer, private name: string) {
super(transferId, 255, 0, data)
}

public start(): Commands.ISerializableCommand[] {
const commands: Commands.ISerializableCommand[] = [
new Commands.DataTransferUploadRequestCommand({
transferId: this.transferId,
transferStoreId: 0xffff,
transferIndex: this.macroIndex,
size: this.data.length,
mode: TransferMode.Write2 | TransferMode.Clear2,
})
]
return commands
}

public handleCommand (command: Commands.IDeserializedCommand): Commands.ISerializableCommand[] {
const res = super.handleCommand(command)

if (this.state === TransferState.Finished) {
this.resolvePromise(this)
}

return res
}

public sendDescription(): Commands.ISerializableCommand {
return new Commands.DataTransferFileDescriptionCommand({
name: this.name,
fileHash: '',
transferId: this.transferId,
})
}
}
19 changes: 17 additions & 2 deletions src/dataTransfer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import DataTransferClip from './dataTransferClip'
import DataTransferAudio from './dataTransferAudio'
import { ISerializableCommand } from '../commands/CommandBase'
import DataTransfer from './dataTransfer'
import MacroLock from './MacroLock'
import {DataDownloadMacro, DataUploadMacro} from './dataTransferMacro'

const MAX_PACKETS_TO_SEND_PER_TICK = 10
const MAX_TRANSFER_INDEX = (1 << 16) - 1 // Inclusive maximum
Expand All @@ -20,6 +22,7 @@ export class DataTransferManager {
new DataLock(1, (cmd) => this.commandQueue.push(cmd)),
new DataLock(2, (cmd) => this.commandQueue.push(cmd)),
]
private readonly macroLock = new MacroLock((cmd) => this.commandQueue.push(cmd))

private interval?: NodeJS.Timer
private exitUnsubscribe?: () => void
Expand Down Expand Up @@ -62,10 +65,10 @@ export class DataTransferManager {
}

public handleCommand(command: Commands.IDeserializedCommand): void {
const allLocks = [this.stillsLock, ...this.clipLocks]
const allLocks = [this.stillsLock, ...this.clipLocks, this.macroLock]

// try to establish the associated DataLock:
let lock: DataLock | undefined
let lock: DataLock | MacroLock | undefined
if (
command.constructor.name === Commands.LockObtainedCommand.name ||
command.constructor.name === Commands.LockStateUpdateCommand.name
Expand Down Expand Up @@ -133,6 +136,18 @@ export class DataTransferManager {
return lock.enqueue(transfer)
}

public downloadMacro (index: number): Promise<Buffer> {
const transfer = new DataDownloadMacro(this.nextTransferIndex, index)

return this.macroLock.enqueue(transfer).then(transfer => (transfer as DataDownloadMacro).data)
}

public uploadMacro(index: number, data: Buffer, name: string): Promise<DataTransfer> {
const transfer = new DataUploadMacro(this.nextTransferIndex, index, data, name)

return this.macroLock.enqueue(transfer)
}

private get nextTransferIndex(): number {
const index = this.transferIndex++
if (this.transferIndex > MAX_TRANSFER_INDEX) this.transferIndex = 0
Expand Down
8 changes: 7 additions & 1 deletion src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,13 @@ export enum TransferMode {
NoOp,
Write,
Clear,
WriteAudio = 256,
Write2 = 256,
Clear2 = 512,
}

export enum DownloadRequestType {
Still,
Macro = 3,
}

export enum VideoMode {
Expand Down

0 comments on commit 3aaea78

Please sign in to comment.