From 9a8832fe90e3a3c50f4592c606e38c6b1544b3d7 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:01:50 -0500 Subject: [PATCH 1/7] add decoder for channel block --- src/decoders/index.ts | 2 ++ src/decoders/lighting/channel-block.ts | 20 ++++++++++++++++++++ src/models.ts | 6 ++++++ tests/decode.test.js | 10 ++++++++++ 4 files changed, 38 insertions(+) create mode 100644 src/decoders/lighting/channel-block.ts diff --git a/src/decoders/index.ts b/src/decoders/index.ts index e55d22a..ea9be20 100644 --- a/src/decoders/index.ts +++ b/src/decoders/index.ts @@ -1,3 +1,4 @@ +import ChannelBlock from './lighting/channel-block'; import CentroidAccelVelocity from './motion/centroid-accel-velocity'; import CentroidPosition from './motion/centroid-position'; import OrientationEuler from './motion/orientation-euler'; @@ -22,4 +23,5 @@ export const Decoders = { OrientationEuler, ZoneCollisionDetection, ZoneObject, + ChannelBlock, }; diff --git a/src/decoders/lighting/channel-block.ts b/src/decoders/lighting/channel-block.ts new file mode 100644 index 0000000..616d8c6 --- /dev/null +++ b/src/decoders/lighting/channel-block.ts @@ -0,0 +1,20 @@ +import { ChannelBlock } from '../../models'; + +export default (bytes: Uint8Array, isLittleEndian: boolean = false): ChannelBlock => { + if (bytes.length !== 5) { + throw new Error('Channel Block module must be 5 bytes'); + } + + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + + const offset = view.getUint16(0); + + const fade = view.getUint16(2, isLittleEndian); + + const value = view.getUint8(4); + return { + offset, + fade, + value, + }; +}; diff --git a/src/models.ts b/src/models.ts index 59b32e7..871cf42 100644 --- a/src/models.ts +++ b/src/models.ts @@ -114,3 +114,9 @@ export type ZoneObject = { nameLength: number; name: string; }; + +export type ChannelBlock = { + offset: number; + fade: number; + value: number; +}; diff --git a/tests/decode.test.js b/tests/decode.test.js index 380e59c..3d90dca 100644 --- a/tests/decode.test.js +++ b/tests/decode.test.js @@ -251,6 +251,16 @@ const goodTests = [ }, decoder: Decoders.RTTrPM, }, + { + description: 'Channel Block', + bytes: new Uint8Array([0x00, 0x08, 0x00, 0x06, 0x40]), + expected: { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + decoder: Decoders.ChannelBlock, + }, ]; describe('RTTrP Bytes Decoding', () => { From b0c050035baee052cf0d1433390ee9bc2351ef16 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:03:19 -0500 Subject: [PATCH 2/7] add decoder for spot --- src/decoders/index.ts | 2 ++ src/decoders/lighting/spot.ts | 44 +++++++++++++++++++++++++++++++++++ src/models.ts | 9 +++++++ tests/decode.test.js | 19 +++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 src/decoders/lighting/spot.ts diff --git a/src/decoders/index.ts b/src/decoders/index.ts index ea9be20..0b1d7dd 100644 --- a/src/decoders/index.ts +++ b/src/decoders/index.ts @@ -1,4 +1,5 @@ import ChannelBlock from './lighting/channel-block'; +import Spot from './lighting/spot'; import CentroidAccelVelocity from './motion/centroid-accel-velocity'; import CentroidPosition from './motion/centroid-position'; import OrientationEuler from './motion/orientation-euler'; @@ -23,5 +24,6 @@ export const Decoders = { OrientationEuler, ZoneCollisionDetection, ZoneObject, + Spot, ChannelBlock, }; diff --git a/src/decoders/lighting/spot.ts b/src/decoders/lighting/spot.ts new file mode 100644 index 0000000..3d9c0d2 --- /dev/null +++ b/src/decoders/lighting/spot.ts @@ -0,0 +1,44 @@ +import { Decoders } from '..'; +import { ChannelBlock, Spot } from '../../models'; + +export default (bytes: Uint8Array, isLittleEndian: boolean = false): Spot => { + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + + let dataOffset = 0; + const type = view.getUint8(dataOffset); + dataOffset += 1; + if (type !== 0x0a) { + throw new Error('Spot module must have a type of 0x0a'); + } + + const size = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + if (size !== bytes.byteLength) { + throw new Error('Spot module size mismatch'); + } + const id = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + const offset = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + const numberOfChannelBlocks = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + + const channelBlocks: ChannelBlock[] = []; + let zoneObjectOffset = dataOffset; + + for (let index = 0; index < numberOfChannelBlocks; index++) { + const channelBlockData = bytes.subarray(zoneObjectOffset, zoneObjectOffset + 5); + console.log(channelBlockData); + const channelBlock = Decoders.ChannelBlock(channelBlockData); + channelBlocks.push(channelBlock); + zoneObjectOffset += channelBlockData.byteLength; + } + return { + type, + size, + id, + offset, + numberOfChannelBlocks, + channelBlocks, + }; +}; diff --git a/src/models.ts b/src/models.ts index 871cf42..aaef772 100644 --- a/src/models.ts +++ b/src/models.ts @@ -115,6 +115,15 @@ export type ZoneObject = { name: string; }; +export type Spot = { + type: number; + size: number; + id: number; + offset: number; + numberOfChannelBlocks: number; + channelBlocks: ChannelBlock[]; +}; + export type ChannelBlock = { offset: number; fade: number; diff --git a/tests/decode.test.js b/tests/decode.test.js index 3d90dca..72fed4c 100644 --- a/tests/decode.test.js +++ b/tests/decode.test.js @@ -251,6 +251,25 @@ const goodTests = [ }, decoder: Decoders.RTTrPM, }, + { + description: 'Spot + Single Channel Block', + bytes: new Uint8Array([0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40]), + expected: { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + decoder: Decoders.Spot, + }, { description: 'Channel Block', bytes: new Uint8Array([0x00, 0x08, 0x00, 0x06, 0x40]), From c4ceaa427a63693b4a5593bbd7b71501dd933814 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:11:28 -0500 Subject: [PATCH 3/7] add decoder for universe --- src/decoders/index.ts | 2 ++ src/decoders/lighting/spot.ts | 2 +- src/decoders/lighting/universe.ts | 41 +++++++++++++++++++++++++++++++ src/models.ts | 8 ++++++ tests/decode.test.js | 30 ++++++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/decoders/lighting/universe.ts diff --git a/src/decoders/index.ts b/src/decoders/index.ts index 0b1d7dd..e8fcb4a 100644 --- a/src/decoders/index.ts +++ b/src/decoders/index.ts @@ -1,5 +1,6 @@ import ChannelBlock from './lighting/channel-block'; import Spot from './lighting/spot'; +import Universe from './lighting/universe'; import CentroidAccelVelocity from './motion/centroid-accel-velocity'; import CentroidPosition from './motion/centroid-position'; import OrientationEuler from './motion/orientation-euler'; @@ -26,4 +27,5 @@ export const Decoders = { ZoneObject, Spot, ChannelBlock, + Universe, }; diff --git a/src/decoders/lighting/spot.ts b/src/decoders/lighting/spot.ts index 3d9c0d2..d522c9f 100644 --- a/src/decoders/lighting/spot.ts +++ b/src/decoders/lighting/spot.ts @@ -13,7 +13,7 @@ export default (bytes: Uint8Array, isLittleEndian: boolean = false): Spot => { const size = view.getUint16(dataOffset, isLittleEndian); dataOffset += 2; - if (size !== bytes.byteLength) { + if (size < bytes.byteLength) { throw new Error('Spot module size mismatch'); } const id = view.getUint16(dataOffset, isLittleEndian); diff --git a/src/decoders/lighting/universe.ts b/src/decoders/lighting/universe.ts new file mode 100644 index 0000000..ec9cc7b --- /dev/null +++ b/src/decoders/lighting/universe.ts @@ -0,0 +1,41 @@ +import { Decoders } from '..'; +import { Spot, Universe } from '../../models'; + +export default (bytes: Uint8Array, isLittleEndian: boolean = false): Universe => { + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + + let dataOffset = 0; + const type = view.getUint8(dataOffset); + dataOffset += 1; + if (type !== 0x09) { + throw new Error('Universe module must have a type of 0x09'); + } + + const size = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + if (size !== bytes.byteLength) { + throw new Error('Universe module size mismatch'); + } + const id = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + const numberOfSpots = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + + const spots: Spot[] = []; + let spotOffset = dataOffset; + + for (let index = 0; index < numberOfSpots; index++) { + const spotData = bytes.subarray(spotOffset); + console.log(spotData); + const spot = Decoders.Spot(spotData); + spots.push(spot); + spotOffset += spot.size; + } + return { + type, + size, + id, + numberOfSpots, + spots, + }; +}; diff --git a/src/models.ts b/src/models.ts index aaef772..dc55bde 100644 --- a/src/models.ts +++ b/src/models.ts @@ -115,6 +115,14 @@ export type ZoneObject = { name: string; }; +export type Universe = { + type: number; + size: number; + id: number; + numberOfSpots: number; + spots: Spot[]; +}; + export type Spot = { type: number; size: number; diff --git a/tests/decode.test.js b/tests/decode.test.js index 72fed4c..6104c4d 100644 --- a/tests/decode.test.js +++ b/tests/decode.test.js @@ -251,6 +251,36 @@ const goodTests = [ }, decoder: Decoders.RTTrPM, }, + { + description: 'Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 0x09, 0x00, 0x15, 0x12, 0x34, 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, + 0x06, 0x40, + ]), + expected: { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + decoder: Decoders.Universe, + }, { description: 'Spot + Single Channel Block', bytes: new Uint8Array([0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40]), From 60dd1499186f0af50047e0fe1cb676abe455cd8f Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:23:40 -0500 Subject: [PATCH 4/7] add decoder for lighting output --- src/decoders/index.ts | 2 ++ src/decoders/lighting/lighting-output.ts | 46 ++++++++++++++++++++++++ src/decoders/lighting/universe.ts | 2 +- src/models.ts | 10 ++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/decoders/lighting/lighting-output.ts diff --git a/src/decoders/index.ts b/src/decoders/index.ts index e8fcb4a..21f1406 100644 --- a/src/decoders/index.ts +++ b/src/decoders/index.ts @@ -1,4 +1,5 @@ import ChannelBlock from './lighting/channel-block'; +import LightingOutput from './lighting/lighting-output'; import Spot from './lighting/spot'; import Universe from './lighting/universe'; import CentroidAccelVelocity from './motion/centroid-accel-velocity'; @@ -28,4 +29,5 @@ export const Decoders = { Spot, ChannelBlock, Universe, + LightingOutput, }; diff --git a/src/decoders/lighting/lighting-output.ts b/src/decoders/lighting/lighting-output.ts new file mode 100644 index 0000000..f0bd5c6 --- /dev/null +++ b/src/decoders/lighting/lighting-output.ts @@ -0,0 +1,46 @@ +import { Decoders } from '..'; +import { LightingOutput, Universe } from '../../models'; + +export default (bytes: Uint8Array, isLittleEndian: boolean = false): LightingOutput => { + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + + let dataOffset = 0; + const type = view.getUint8(dataOffset); + dataOffset += 1; + if (type !== 0x07) { + throw new Error('Lighting Output module must have a type of 0x09'); + } + + const size = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + if (size < bytes.byteLength) { + throw new Error('Lighting Output module size mismatch'); + } + const sequence = view.getUint32(dataOffset, isLittleEndian); + dataOffset += 4; + const action = view.getUint8(dataOffset); + dataOffset += 1; + const holdTime = view.getUint32(dataOffset, isLittleEndian); + dataOffset += 4; + const numberOfUniverses = view.getUint16(dataOffset, isLittleEndian); + dataOffset += 2; + + const universes: Universe[] = []; + let universeOffset = dataOffset; + + for (let index = 0; index < numberOfUniverses; index++) { + const universeData = bytes.subarray(universeOffset); + const universe = Decoders.Universe(universeData); + universes.push(universe); + universeOffset += universe.size; + } + return { + type, + size, + sequence, + action, + holdTime, + numberOfUniverses, + universes, + }; +}; diff --git a/src/decoders/lighting/universe.ts b/src/decoders/lighting/universe.ts index ec9cc7b..392dbe8 100644 --- a/src/decoders/lighting/universe.ts +++ b/src/decoders/lighting/universe.ts @@ -13,7 +13,7 @@ export default (bytes: Uint8Array, isLittleEndian: boolean = false): Universe => const size = view.getUint16(dataOffset, isLittleEndian); dataOffset += 2; - if (size !== bytes.byteLength) { + if (size < bytes.byteLength) { throw new Error('Universe module size mismatch'); } const id = view.getUint16(dataOffset, isLittleEndian); diff --git a/src/models.ts b/src/models.ts index dc55bde..949430b 100644 --- a/src/models.ts +++ b/src/models.ts @@ -115,6 +115,16 @@ export type ZoneObject = { name: string; }; +export type LightingOutput = { + type: number; + size: number; + sequence: number; + action: number; + holdTime: number; + numberOfUniverses: number; + universes: Universe[]; +}; + export type Universe = { type: number; size: number; From 1e605df6c8e139f17fc9a958d396803f80bf955c Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:23:49 -0500 Subject: [PATCH 5/7] cleanup logging --- src/decoders/lighting/spot.ts | 1 - src/decoders/lighting/universe.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/decoders/lighting/spot.ts b/src/decoders/lighting/spot.ts index d522c9f..9cf3d3b 100644 --- a/src/decoders/lighting/spot.ts +++ b/src/decoders/lighting/spot.ts @@ -28,7 +28,6 @@ export default (bytes: Uint8Array, isLittleEndian: boolean = false): Spot => { for (let index = 0; index < numberOfChannelBlocks; index++) { const channelBlockData = bytes.subarray(zoneObjectOffset, zoneObjectOffset + 5); - console.log(channelBlockData); const channelBlock = Decoders.ChannelBlock(channelBlockData); channelBlocks.push(channelBlock); zoneObjectOffset += channelBlockData.byteLength; diff --git a/src/decoders/lighting/universe.ts b/src/decoders/lighting/universe.ts index 392dbe8..eec9de5 100644 --- a/src/decoders/lighting/universe.ts +++ b/src/decoders/lighting/universe.ts @@ -26,7 +26,6 @@ export default (bytes: Uint8Array, isLittleEndian: boolean = false): Universe => for (let index = 0; index < numberOfSpots; index++) { const spotData = bytes.subarray(spotOffset); - console.log(spotData); const spot = Decoders.Spot(spotData); spots.push(spot); spotOffset += spot.size; From e11f16f427db8b32bc50b7918b710e669d5e9b40 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:32:13 -0500 Subject: [PATCH 6/7] add decoder for RTTrPL --- src/decoders/index.ts | 2 + src/decoders/lighting/rttrpl.ts | 37 +++++++++++++ src/models.ts | 5 ++ tests/decode.test.js | 96 +++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 src/decoders/lighting/rttrpl.ts diff --git a/src/decoders/index.ts b/src/decoders/index.ts index 21f1406..8218e0f 100644 --- a/src/decoders/index.ts +++ b/src/decoders/index.ts @@ -1,5 +1,6 @@ import ChannelBlock from './lighting/channel-block'; import LightingOutput from './lighting/lighting-output'; +import RTTrPL from './lighting/rttrpl'; import Spot from './lighting/spot'; import Universe from './lighting/universe'; import CentroidAccelVelocity from './motion/centroid-accel-velocity'; @@ -17,6 +18,7 @@ import RTTrPHeader from './rttrp-header'; export const Decoders = { RTTrPHeader, RTTrPM, + RTTrPL, Trackable, CentroidPosition, CentroidAccelVelocity, diff --git a/src/decoders/lighting/rttrpl.ts b/src/decoders/lighting/rttrpl.ts new file mode 100644 index 0000000..5ed142b --- /dev/null +++ b/src/decoders/lighting/rttrpl.ts @@ -0,0 +1,37 @@ +import { Decoders } from '..'; +import { LightingOutput, RTTrPL } from '../../models'; + +export default (bytes: Uint8Array): RTTrPL => { + if (bytes.byteLength < 18) { + throw new Error('RTTrPL packet must be at least 18 bytes long'); + } + const header = Decoders.RTTrPHeader(bytes.subarray(0, 18)); + + if (bytes.byteLength !== header.size) { + throw new Error('RTTrPL packet size mismatch'); + } + + const modules: LightingOutput[] = []; + let packetModuleOffset = 18; + + for (let index = 0; index < header.subModuleCount; index++) { + const moduleData = bytes.subarray(packetModuleOffset); + const moduleType = moduleData[0]; + switch (moduleType) { + case 0x07: { + const module = Decoders.LightingOutput(moduleData, header.isLittleEndian); + modules.push(module); + packetModuleOffset += module.size; + break; + } + default: + console.error(`unknown RTTrPL submodule type: ${moduleType}`); + break; + } + } + + return { + header, + modules, + }; +}; diff --git a/src/models.ts b/src/models.ts index 949430b..9dcde49 100644 --- a/src/models.ts +++ b/src/models.ts @@ -15,6 +15,11 @@ export type RTTrPM = { modules: Trackable[]; }; +export type RTTrPL = { + header: RTTrPHeader; + modules: LightingOutput[]; +}; + export type TrackableModule = | CentroidPosition | CentroidAccelVelocity diff --git a/tests/decode.test.js b/tests/decode.test.js index 6104c4d..ee47e54 100644 --- a/tests/decode.test.js +++ b/tests/decode.test.js @@ -251,6 +251,102 @@ const goodTests = [ }, decoder: Decoders.RTTrPM, }, + { + description: 'RTTrPL + Lighting Output + Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 65, 84, 0x44, 0x34, 0, 2, 0, 0, 0, 1, 0, 0, 53, 18, 52, 86, 120, 1, 0x07, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x01, 0x09, 0x00, 0x15, 0x12, 0x34, 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40, + ]), + expected: { + header: { + intHeader: 0x4154, + floatHeader: 0x4434, + version: 2, + packetID: 1, + packetFormat: 0, + size: 53, + context: 0x12345678, + subModuleCount: 1, + isLittleEndian: false, + }, + modules: [ + { + type: 0x07, + size: 35, + sequence: 1, + action: 1, + holdTime: 100, + numberOfUniverses: 1, + universes: [ + { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + ], + }, + ], + }, + decoder: Decoders.RTTrPL, + }, + { + description: 'Lighting Output + Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 0x07, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x01, 0x09, 0x00, 0x15, 0x12, 0x34, + 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40, + ]), + expected: { + type: 0x07, + size: 35, + sequence: 1, + action: 1, + holdTime: 100, + numberOfUniverses: 1, + universes: [ + { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + ], + }, + decoder: Decoders.LightingOutput, + }, { description: 'Universe + Spot + Single Channel Block', bytes: new Uint8Array([ From 928745c9a888e16b8c6eaf28f2d0724e354ae11a Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 15 Mar 2025 12:33:26 -0500 Subject: [PATCH 7/7] split lighting tests out --- tests/lighting-decode.test.js | 181 ++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 tests/lighting-decode.test.js diff --git a/tests/lighting-decode.test.js b/tests/lighting-decode.test.js new file mode 100644 index 0000000..9de2e13 --- /dev/null +++ b/tests/lighting-decode.test.js @@ -0,0 +1,181 @@ +const { deepEqual, throws } = require('assert'); +const { describe, it } = require('node:test'); +const { Decoders } = require('../dist/cjs'); +const goodTests = [ + { + description: 'RTTrPL + Lighting Output + Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 65, 84, 0x44, 0x34, 0, 2, 0, 0, 0, 1, 0, 0, 53, 18, 52, 86, 120, 1, 0x07, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x01, 0x09, 0x00, 0x15, 0x12, 0x34, 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40, + ]), + expected: { + header: { + intHeader: 0x4154, + floatHeader: 0x4434, + version: 2, + packetID: 1, + packetFormat: 0, + size: 53, + context: 0x12345678, + subModuleCount: 1, + isLittleEndian: false, + }, + modules: [ + { + type: 0x07, + size: 35, + sequence: 1, + action: 1, + holdTime: 100, + numberOfUniverses: 1, + universes: [ + { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + ], + }, + ], + }, + decoder: Decoders.RTTrPL, + }, + { + description: 'Lighting Output + Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 0x07, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x01, 0x09, 0x00, 0x15, 0x12, 0x34, + 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40, + ]), + expected: { + type: 0x07, + size: 35, + sequence: 1, + action: 1, + holdTime: 100, + numberOfUniverses: 1, + universes: [ + { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + ], + }, + decoder: Decoders.LightingOutput, + }, + { + description: 'Universe + Spot + Single Channel Block', + bytes: new Uint8Array([ + 0x09, 0x00, 0x15, 0x12, 0x34, 0x00, 0x01, 0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, + 0x06, 0x40, + ]), + expected: { + type: 0x09, + size: 21, + id: 0x1234, + numberOfSpots: 1, + spots: [ + { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + ], + }, + decoder: Decoders.Universe, + }, + { + description: 'Spot + Single Channel Block', + bytes: new Uint8Array([0x0a, 0x00, 0x0e, 0x12, 0x34, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, 0x40]), + expected: { + type: 0x0a, + size: 14, + id: 0x1234, + offset: 1, + numberOfChannelBlocks: 1, + channelBlocks: [ + { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + ], + }, + decoder: Decoders.Spot, + }, + { + description: 'Channel Block', + bytes: new Uint8Array([0x00, 0x08, 0x00, 0x06, 0x40]), + expected: { + offset: 0x08, + fade: 0x06, + value: 0x40, + }, + decoder: Decoders.ChannelBlock, + }, +]; + +describe('RTTrPL Bytes Decoding', () => { + goodTests.forEach((bytesTest) => { + it(bytesTest.description, () => { + const decoded = bytesTest.decoder(bytesTest.bytes); + deepEqual(decoded, bytesTest.expected); + }); + }); +}); + +// TODO(jwetzell): add error tests +const badTests = []; +describe('RTTrPL Bytes Decoding Throws', () => { + badTests.forEach((bytesTest) => { + it(bytesTest.description, () => { + throws(() => { + bytesTest.decoder(bytesTest.bytes); + }, bytesTest.throwsMessage); + }); + }); +});