convert to typescript

This commit is contained in:
2023-09-14 22:43:25 -05:00
parent 4c19f5f7b9
commit f21bef263d
64 changed files with 1109 additions and 605 deletions
-5
View File
@@ -1,5 +0,0 @@
module.exports = {
PACKET_HEADER_SIZE: 16,
MAX_UDP_PACKET_SIZE: 1500,
CHUNK_HEADER_SIZE: 4,
};
+5
View File
@@ -0,0 +1,5 @@
export class Constants {
static readonly PACKET_HEADER_SIZE = 16;
static readonly MAX_UDP_PACKET_SIZE = 1500;
static readonly CHUNK_HEADER_SIZE = 4;
}
-109
View File
@@ -1,109 +0,0 @@
const packetDecoder = require('./decoders/packet');
class Decoder {
// TODO(jwetzell): check if these last packet headers are necessary to hold on to
lastInfoPacketHeader;
lastDataPacketHeader;
infoPacketFrames = {};
dataPacketFrames = {};
constructor() {
this.system_name = '';
this.trackers = {};
}
updateInfo(framePackets) {
framePackets.forEach((packet) => {
if (packet.system_name) {
this.system_name = packet.system_name.system_name;
}
if (packet.tracker_list?.trackers) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
this.trackers[trackerId] = {
...this.trackers[trackerId],
...tracker.tracker_name,
};
});
}
});
}
updateData(framePackets) {
framePackets.forEach((packet) => {
if (packet.tracker_list) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
this.trackers[trackerId] = {
...this.trackers[trackerId],
...tracker,
};
});
}
});
}
// TODO(jwetzell): add invalid frame id decoding. Scenario where a frame id is reused before one is complete
decode(packetBuf) {
const packet = packetDecoder(packetBuf);
if (packet.id === 0x6756) {
const infoPacket = packet;
if (infoPacket) {
if (infoPacket.has_subchunks > 0) {
const currentInfoPacketHeader = infoPacket.packet_header;
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
const systemSubChunk = infoPacket.system_name;
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
throw new Error('malformed packet');
}
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
if (
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
currentInfoPacketHeader.frame_packet_count
) {
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
}
this.lastInfoPacketHeader = currentInfoPacketHeader;
}
}
} else if (packet.id === 0x6755) {
const dataPacket = packet;
if (dataPacket.has_subchunks) {
const currentDataPacketHeader = dataPacket.packet_header;
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
}
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
if (
this.dataPacketFrames[currentDataPacketHeader.frame_id].length === currentDataPacketHeader.frame_packet_count
) {
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
}
this.lastDataPacketHeader = currentDataPacketHeader;
}
}
return packet;
}
}
module.exports = Decoder;
+117
View File
@@ -0,0 +1,117 @@
import { DataPacketChunk } from './decoders/data/data-packet-chunk';
import { InfoPacketChunk } from './decoders/info/info-packet-chunk';
import { InfoTrackerChunk } from './decoders/info/info-tracker-chunk';
import packetDecoder from './decoders/packet';
import { PacketHeaderChunk } from './decoders/packet-header-chunk';
export class Decoder {
// TODO(jwetzell): check if these last packet headers are necessary to hold on to
lastInfoPacketHeader?: PacketHeaderChunk;
lastDataPacketHeader?: PacketHeaderChunk;
infoPacketFrames: { [key: number]: InfoPacketChunk[] } = {};
dataPacketFrames: { [key: number]: DataPacketChunk[] } = {};
trackers: { [key: string]: InfoTrackerChunk } = {};
system_name: string = '';
constructor() {}
updateInfo(framePackets: InfoPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.system_name) {
this.system_name = packet.system_name.system_name;
}
if (packet.tracker_list?.trackers) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
if (!this.trackers[trackerId]) {
this.trackers[trackerId] = {};
}
this.trackers[trackerId].tracker_name = tracker.tracker_name;
});
}
});
}
updateData(framePackets: DataPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.tracker_list) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
this.trackers[trackerId] = {
...this.trackers[trackerId],
...tracker,
};
});
}
});
}
// TODO(jwetzell): add invalid frame id decoding. Scenario where a frame id is reused before one is complete
decode(packetBuf: Buffer): DataPacketChunk | InfoPacketChunk | undefined {
const packet = packetDecoder(packetBuf);
if (packet === undefined) {
return packet;
}
if (packet.id === 0x6756) {
const infoPacket = packet as InfoPacketChunk;
if (infoPacket && infoPacket.has_subchunks) {
const currentInfoPacketHeader = infoPacket.packet_header;
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
const systemSubChunk = infoPacket.system_name;
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
throw new Error('malformed packet');
}
if (currentInfoPacketHeader.frame_id) {
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
if (
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
currentInfoPacketHeader.frame_packet_count
) {
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
}
}
this.lastInfoPacketHeader = currentInfoPacketHeader;
}
} else if (packet.id === 0x6755) {
const dataPacket = packet as DataPacketChunk;
if (dataPacket.has_subchunks) {
const currentDataPacketHeader = dataPacket.packet_header;
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
if (currentDataPacketHeader.frame_id) {
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
}
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
if (
this.dataPacketFrames[currentDataPacketHeader.frame_id].length ===
currentDataPacketHeader.frame_packet_count
) {
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
}
}
this.lastDataPacketHeader = currentDataPacketHeader;
}
}
return packet;
}
}
@@ -1,6 +1,13 @@
module.exports = (buffer, dataDecoder) => {
export interface Chunk {
id?: number;
has_subchunks?: boolean;
data_len?: number;
chunk_data?: Buffer;
}
export default (buffer: Buffer, dataDecoder: Function): Chunk => {
let offset = 0;
const chunk = {};
const chunk: Chunk = {};
chunk.id = buffer.readUInt16LE(offset);
offset += 2;
-29
View File
@@ -1,29 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const packetHeaderChunk = require('../packet-header-chunk');
const dataTrackerListChunk = require('./data-tracker-list-chunk');
function decodeDataPacketChunk(dataPacketChunk) {
if (dataPacketChunk.has_subchunks && dataPacketChunk.chunk_data) {
let offset = 0;
while (offset < dataPacketChunk.data_len) {
const chunkId = dataPacketChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
dataPacketChunk.packet_header = packetHeaderChunk(dataPacketChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += dataPacketChunk.packet_header.data_len;
break;
case 0x0001:
dataPacketChunk.tracker_list = dataTrackerListChunk(dataPacketChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += dataPacketChunk.tracker_list.data_len;
break;
default:
break;
}
}
}
}
module.exports = (buffer) => chunk(buffer, decodeDataPacketChunk);
+38
View File
@@ -0,0 +1,38 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import packetHeaderChunk, { PacketHeaderChunk } from '../packet-header-chunk';
import dataTrackerListChunk, { DataTrackerListChunk } from './data-tracker-list-chunk';
export interface DataPacketChunk extends Chunk {
packet_header: PacketHeaderChunk;
tracker_list: DataTrackerListChunk;
}
function decodeDataPacketChunk(dataPacketChunk: DataPacketChunk) {
if (dataPacketChunk.has_subchunks && dataPacketChunk.chunk_data && dataPacketChunk.data_len) {
let offset = 0;
while (offset < dataPacketChunk.data_len) {
const chunkId = dataPacketChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
dataPacketChunk.packet_header = packetHeaderChunk(dataPacketChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (dataPacketChunk.packet_header?.data_len) {
offset += dataPacketChunk.packet_header.data_len;
}
break;
case 0x0001:
dataPacketChunk.tracker_list = dataTrackerListChunk(dataPacketChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (dataPacketChunk.tracker_list.data_len) {
offset += dataPacketChunk.tracker_list.data_len;
}
break;
default:
break;
}
}
}
}
export default (buffer: Buffer): DataPacketChunk => chunk(buffer, decodeDataPacketChunk) as DataPacketChunk;
-41
View File
@@ -1,41 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const dataTrackerFieldChunk = require('./data-tracker-field-chunk');
function decodeTrackerChunk(trackerChunk) {
if (trackerChunk.chunk_data && trackerChunk.data_len > 0) {
let offset = 0;
while (offset < trackerChunk.data_len) {
const trackerFieldChunk = dataTrackerFieldChunk(trackerChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += trackerFieldChunk.data_len;
switch (trackerFieldChunk.id) {
case 0x0000:
trackerChunk.pos = trackerFieldChunk;
break;
case 0x0001:
trackerChunk.speed = trackerFieldChunk;
break;
case 0x0002:
trackerChunk.ori = trackerFieldChunk;
break;
case 0x0003:
trackerChunk.status = trackerFieldChunk;
break;
case 0x0004:
trackerChunk.accel = trackerFieldChunk;
break;
case 0x0005:
trackerChunk.trgtpos = trackerFieldChunk;
break;
case 0x0006:
trackerChunk.timestamp = trackerFieldChunk;
break;
default:
break;
}
}
}
}
module.exports = (buffer) => chunk(buffer, decodeTrackerChunk);
+53
View File
@@ -0,0 +1,53 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import dataTrackerFieldChunk, { DataTrackerFieldChunk } from './data-tracker-field-chunk';
export interface DataTrackerChunk extends Chunk {
pos: DataTrackerFieldChunk;
speed: DataTrackerFieldChunk;
ori: DataTrackerFieldChunk;
status: DataTrackerFieldChunk;
accel: DataTrackerFieldChunk;
trgtpos: DataTrackerFieldChunk;
timestamp: DataTrackerFieldChunk;
}
function decodeTrackerChunk(trackerChunk: DataTrackerChunk) {
if (trackerChunk.chunk_data && trackerChunk.data_len) {
let offset = 0;
while (offset < trackerChunk.data_len) {
const trackerFieldChunk = dataTrackerFieldChunk(trackerChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerFieldChunk.data_len) {
offset += trackerFieldChunk.data_len;
switch (trackerFieldChunk.id) {
case 0x0000:
trackerChunk.pos = trackerFieldChunk;
break;
case 0x0001:
trackerChunk.speed = trackerFieldChunk;
break;
case 0x0002:
trackerChunk.ori = trackerFieldChunk;
break;
case 0x0003:
trackerChunk.status = trackerFieldChunk;
break;
case 0x0004:
trackerChunk.accel = trackerFieldChunk;
break;
case 0x0005:
trackerChunk.trgtpos = trackerFieldChunk;
break;
case 0x0006:
trackerChunk.timestamp = trackerFieldChunk;
break;
default:
break;
}
}
}
}
}
export default (buffer: Buffer): DataTrackerChunk => chunk(buffer, decodeTrackerChunk) as DataTrackerChunk;
@@ -1,43 +0,0 @@
/* eslint-disable no-case-declarations */
const chunk = require('../chunk');
function readXYZ(trackerFieldChunk, prefix) {
if (prefix === undefined) {
prefix = '';
}
trackerFieldChunk[`${prefix}x`] = trackerFieldChunk.chunk_data.readFloatLE(0);
trackerFieldChunk[`${prefix}y`] = trackerFieldChunk.chunk_data.readFloatLE(4);
trackerFieldChunk[`${prefix}z`] = trackerFieldChunk.chunk_data.readFloatLE(8);
}
function decodeTrackerFieldChunk(trackerFieldChunk) {
if (trackerFieldChunk.id !== undefined) {
switch (trackerFieldChunk.id) {
case 0x0000:
readXYZ(trackerFieldChunk, 'pos_');
break;
case 0x0001:
readXYZ(trackerFieldChunk, 'speed_');
break;
case 0x0002:
readXYZ(trackerFieldChunk, 'ori_');
break;
case 0x0003:
trackerFieldChunk.validity = trackerFieldChunk.chunk_data.readFloatLE();
break;
case 0x0004:
readXYZ(trackerFieldChunk, 'accel_');
break;
case 0x0005:
readXYZ(trackerFieldChunk, 'trgtpos_');
break;
case 0x0006:
trackerFieldChunk.tracker_timestamp = trackerFieldChunk.chunk_data.readBigUInt64LE();
break;
default:
break;
}
}
}
module.exports = (buffer) => chunk(buffer, decodeTrackerFieldChunk);
@@ -0,0 +1,50 @@
/* eslint-disable no-case-declarations */
import chunk, { Chunk } from '../chunk';
export interface DataTrackerFieldChunk extends Chunk {
[key: string]: number | bigint | number[] | Buffer | undefined | boolean;
}
function readXYZ(trackerFieldChunk: DataTrackerFieldChunk, prefix: string = '') {
if (trackerFieldChunk.chunk_data) {
trackerFieldChunk[`${prefix}x`] = trackerFieldChunk.chunk_data.readFloatLE(0);
trackerFieldChunk[`${prefix}y`] = trackerFieldChunk.chunk_data.readFloatLE(4);
trackerFieldChunk[`${prefix}z`] = trackerFieldChunk.chunk_data.readFloatLE(8);
}
}
function decodeTrackerFieldChunk(trackerFieldChunk: DataTrackerFieldChunk) {
if (trackerFieldChunk.id !== undefined) {
switch (trackerFieldChunk.id) {
case 0x0000:
readXYZ(trackerFieldChunk, 'pos_');
break;
case 0x0001:
readXYZ(trackerFieldChunk, 'speed_');
break;
case 0x0002:
readXYZ(trackerFieldChunk, 'ori_');
break;
case 0x0003:
if (trackerFieldChunk.chunk_data) {
trackerFieldChunk.validity = trackerFieldChunk.chunk_data.readFloatLE();
}
break;
case 0x0004:
readXYZ(trackerFieldChunk, 'accel_');
break;
case 0x0005:
readXYZ(trackerFieldChunk, 'trgtpos_');
break;
case 0x0006:
if (trackerFieldChunk.chunk_data) {
trackerFieldChunk.tracker_timestamp = trackerFieldChunk.chunk_data.readBigUInt64LE();
}
break;
default:
break;
}
}
}
export default (buffer: Buffer): DataTrackerFieldChunk =>
chunk(buffer, decodeTrackerFieldChunk) as DataTrackerFieldChunk;
@@ -1,17 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const dataTrackerChunk = require('./data-tracker-chunk');
function decodeDataTrackerListChunk(dataTrackerListChunk) {
dataTrackerListChunk.trackers = {};
// TODO(jwetzell): add error handling
let offset = 0;
while (offset < dataTrackerListChunk.chunk_data.length) {
const trackerChunk = dataTrackerChunk(dataTrackerListChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += trackerChunk.data_len;
dataTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
}
}
module.exports = (buffer) => chunk(buffer, decodeDataTrackerListChunk);
@@ -0,0 +1,30 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import dataTrackerChunk, { DataTrackerChunk } from './data-tracker-chunk';
export interface DataTrackerListChunk extends Chunk {
trackers: {
[key: number]: DataTrackerChunk;
};
}
function decodeDataTrackerListChunk(dataTrackerListChunk: DataTrackerListChunk) {
dataTrackerListChunk.trackers = {};
// TODO(jwetzell): add error handling
let offset = 0;
if (dataTrackerListChunk.chunk_data) {
while (offset < dataTrackerListChunk.chunk_data.length) {
const trackerChunk = dataTrackerChunk(dataTrackerListChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerChunk.data_len) {
offset += trackerChunk.data_len;
}
if (trackerChunk.id) {
dataTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
}
}
}
}
export default (buffer: Buffer): DataTrackerListChunk =>
chunk(buffer, decodeDataTrackerListChunk) as DataTrackerListChunk;
-35
View File
@@ -1,35 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const packetHeaderChunk = require('../packet-header-chunk');
const infoSystemNameChunk = require('./info-system-name-chunk');
const infoTrackerListChunk = require('./info-tracker-list-chunk');
function decodeInfoPacketChunk(infoPacketChunk) {
if (infoPacketChunk.has_subchunks && infoPacketChunk.chunk_data) {
let offset = 0;
while (offset < infoPacketChunk.data_len) {
const chunkId = infoPacketChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
infoPacketChunk.packet_header = packetHeaderChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += infoPacketChunk.packet_header.data_len;
break;
case 0x0001:
infoPacketChunk.system_name = infoSystemNameChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += infoPacketChunk.system_name.data_len;
break;
case 0x0002:
infoPacketChunk.tracker_list = infoTrackerListChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += infoPacketChunk.tracker_list.data_len;
break;
default:
break;
}
}
}
}
module.exports = (buffer) => chunk(buffer, decodeInfoPacketChunk);
+47
View File
@@ -0,0 +1,47 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import packetHeaderChunk, { PacketHeaderChunk } from '../packet-header-chunk';
import infoSystemNameChunk, { InfoSystemNameChunk } from './info-system-name-chunk';
import infoTrackerListChunk, { InfoTrackerListChunk } from './info-tracker-list-chunk';
export interface InfoPacketChunk extends Chunk {
packet_header: PacketHeaderChunk;
system_name: InfoSystemNameChunk;
tracker_list: InfoTrackerListChunk;
}
function decodeInfoPacketChunk(infoPacketChunk: InfoPacketChunk) {
if (infoPacketChunk.has_subchunks && infoPacketChunk.chunk_data && infoPacketChunk.data_len) {
let offset = 0;
while (offset < infoPacketChunk.data_len) {
const chunkId = infoPacketChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
infoPacketChunk.packet_header = packetHeaderChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.packet_header?.data_len) {
offset += infoPacketChunk.packet_header.data_len;
}
break;
case 0x0001:
infoPacketChunk.system_name = infoSystemNameChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.system_name?.data_len) {
offset += infoPacketChunk.system_name.data_len;
}
break;
case 0x0002:
infoPacketChunk.tracker_list = infoTrackerListChunk(infoPacketChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.tracker_list?.data_len) {
offset += infoPacketChunk.tracker_list.data_len;
}
break;
default:
break;
}
}
}
}
export default (buffer: Buffer): InfoPacketChunk => chunk(buffer, decodeInfoPacketChunk) as InfoPacketChunk;
@@ -1,9 +0,0 @@
const chunk = require('../chunk');
function decodeSystemNameChunk(systemNameChunk) {
if (systemNameChunk.chunk_data !== undefined && systemNameChunk.data_len !== undefined) {
systemNameChunk.system_name = systemNameChunk.chunk_data.subarray(0, systemNameChunk.data_len).toString();
}
}
module.exports = (buffer) => chunk(buffer, decodeSystemNameChunk);
@@ -0,0 +1,13 @@
import chunk, { Chunk } from '../chunk';
export interface InfoSystemNameChunk extends Chunk {
system_name: string;
}
function decodeSystemNameChunk(systemNameChunk: InfoSystemNameChunk) {
if (systemNameChunk.chunk_data !== undefined && systemNameChunk.data_len !== undefined) {
systemNameChunk.system_name = systemNameChunk.chunk_data.subarray(0, systemNameChunk.data_len).toString();
}
}
export default (buffer: Buffer): InfoSystemNameChunk => chunk(buffer, decodeSystemNameChunk) as InfoSystemNameChunk;
-24
View File
@@ -1,24 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const infoTrackerNameChunk = require('./info-tracker-name-chunk');
function decodeInfoTrackerChunk(infoTrackerChunk) {
if (infoTrackerChunk.has_subchunks && infoTrackerChunk.chunk_data) {
let offset = 0;
while (offset < infoTrackerChunk.data_len) {
const chunkId = infoTrackerChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
infoTrackerChunk.tracker_name = infoTrackerNameChunk(infoTrackerChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += infoTrackerChunk.tracker_name.data_len;
break;
default:
break;
}
}
}
}
module.exports = (buffer) => chunk(buffer, decodeInfoTrackerChunk);
+30
View File
@@ -0,0 +1,30 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import infoTrackerNameChunk, { InfoTrackerNameChunk } from './info-tracker-name-chunk';
export interface InfoTrackerChunk extends Chunk {
tracker_name?: InfoTrackerNameChunk;
}
function decodeInfoTrackerChunk(infoTrackerChunk: InfoTrackerChunk) {
if (infoTrackerChunk.has_subchunks && infoTrackerChunk.chunk_data && infoTrackerChunk.data_len) {
let offset = 0;
while (offset < infoTrackerChunk.data_len) {
const chunkId = infoTrackerChunk.chunk_data.readUInt16LE(offset);
switch (chunkId) {
case 0x0000:
infoTrackerChunk.tracker_name = infoTrackerNameChunk(infoTrackerChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoTrackerChunk.tracker_name?.data_len) {
offset += infoTrackerChunk?.tracker_name?.data_len;
}
break;
default:
break;
}
}
}
}
export default (buffer: Buffer): InfoTrackerChunk => chunk(buffer, decodeInfoTrackerChunk);
@@ -1,17 +0,0 @@
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const infoTrackerChunk = require('./info-tracker-chunk');
function decodeInfoTrackerListChunk(infoTrackerListChunk) {
infoTrackerListChunk.trackers = {};
if (infoTrackerListChunk.has_subchunks && infoTrackerListChunk.chunk_data) {
let offset = 0;
while (offset < infoTrackerListChunk.data_len) {
const trackerChunk = infoTrackerChunk(infoTrackerListChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += trackerChunk.data_len;
infoTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
}
}
}
module.exports = (buffer) => chunk(buffer, decodeInfoTrackerListChunk);
@@ -0,0 +1,29 @@
import { Constants } from '../../constants';
import chunk, { Chunk } from '../chunk';
import infoTrackerChunk, { InfoTrackerChunk } from './info-tracker-chunk';
export interface InfoTrackerListChunk extends Chunk {
trackers: {
[key: number]: InfoTrackerChunk;
};
}
function decodeInfoTrackerListChunk(infoTrackerListChunk: InfoTrackerListChunk) {
infoTrackerListChunk.trackers = {};
if (infoTrackerListChunk.has_subchunks && infoTrackerListChunk.chunk_data) {
let offset = 0;
if (infoTrackerListChunk.data_len) {
while (offset < infoTrackerListChunk.data_len) {
const trackerChunk = infoTrackerChunk(infoTrackerListChunk.chunk_data.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerChunk.data_len) {
offset += trackerChunk.data_len;
}
if (trackerChunk.id) {
infoTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
}
}
}
}
}
export default (buffer: Buffer): InfoTrackerListChunk =>
chunk(buffer, decodeInfoTrackerListChunk) as InfoTrackerListChunk;
@@ -1,8 +0,0 @@
const chunk = require('../chunk');
function decodeTrackerNameChunk(trackerNameChunk) {
if (trackerNameChunk.chunk_data !== undefined && trackerNameChunk.data_len !== undefined) {
trackerNameChunk.tracker_name = trackerNameChunk.chunk_data.subarray(0, trackerNameChunk.data_len).toString();
}
}
module.exports = (buffer) => chunk(buffer, decodeTrackerNameChunk);
@@ -0,0 +1,11 @@
import chunk, { Chunk } from '../chunk';
export interface InfoTrackerNameChunk extends Chunk {
tracker_name: string;
}
function decodeTrackerNameChunk(trackerNameChunk: InfoTrackerNameChunk) {
if (trackerNameChunk.chunk_data !== undefined && trackerNameChunk.data_len !== undefined) {
trackerNameChunk.tracker_name = trackerNameChunk.chunk_data.subarray(0, trackerNameChunk.data_len).toString();
}
}
export default (buffer: Buffer): InfoTrackerNameChunk => chunk(buffer, decodeTrackerNameChunk) as InfoTrackerNameChunk;
-12
View File
@@ -1,12 +0,0 @@
const chunk = require('./chunk');
function decodePacketHeaderChunk(packetHeaderChunk) {
// TODO(jwetzell): remove the string conversion
packetHeaderChunk.packet_timestamp = packetHeaderChunk.chunk_data.readBigUInt64LE(0);
packetHeaderChunk.version_high = packetHeaderChunk.chunk_data.readUInt8(8);
packetHeaderChunk.version_low = packetHeaderChunk.chunk_data.readUInt8(9);
packetHeaderChunk.frame_id = packetHeaderChunk.chunk_data.readUInt8(10);
packetHeaderChunk.frame_packet_count = packetHeaderChunk.chunk_data.readUInt8(11);
}
module.exports = (buffer) => chunk(buffer, decodePacketHeaderChunk);
+21
View File
@@ -0,0 +1,21 @@
import chunk, { Chunk } from './chunk';
export interface PacketHeaderChunk extends Chunk {
packet_timestamp?: bigint;
version_high?: number;
version_low?: number;
frame_id?: number;
frame_packet_count?: number;
}
function decodePacketHeaderChunk(packetHeaderChunk: PacketHeaderChunk) {
// TODO(jwetzell): remove the string conversion
if (packetHeaderChunk.chunk_data) {
packetHeaderChunk.packet_timestamp = packetHeaderChunk.chunk_data.readBigUInt64LE(0);
packetHeaderChunk.version_high = packetHeaderChunk.chunk_data.readUInt8(8);
packetHeaderChunk.version_low = packetHeaderChunk.chunk_data.readUInt8(9);
packetHeaderChunk.frame_id = packetHeaderChunk.chunk_data.readUInt8(10);
packetHeaderChunk.frame_packet_count = packetHeaderChunk.chunk_data.readUInt8(11);
}
}
export default (buffer: Buffer): PacketHeaderChunk => chunk(buffer, decodePacketHeaderChunk);
-14
View File
@@ -1,14 +0,0 @@
const dataPacketChunk = require('./data/data-packet-chunk');
const infoPacketChunk = require('./info/info-packet-chunk');
module.exports = (buffer) => {
const chunkId = buffer.readUInt16LE();
switch (chunkId) {
case 0x6756:
return infoPacketChunk(buffer);
case 0x6755:
return dataPacketChunk(buffer);
default:
return {};
}
};
+15
View File
@@ -0,0 +1,15 @@
import infoPacketChunk, { InfoPacketChunk } from './info/info-packet-chunk';
import dataPacketChunk, { DataPacketChunk } from './data/data-packet-chunk';
export default (buffer: Buffer): InfoPacketChunk | DataPacketChunk | undefined => {
const chunkId = buffer.readUInt16LE();
switch (chunkId) {
case 0x6756:
return infoPacketChunk(buffer);
case 0x6755:
return dataPacketChunk(buffer);
default:
return undefined;
}
};
+30 -27
View File
@@ -1,34 +1,39 @@
const { MAX_UDP_PACKET_SIZE, PACKET_HEADER_SIZE, CHUNK_HEADER_SIZE } = require('./constants');
const dataPacketChunk = require('./encoders/data/data-packet-chunk');
const dataTrackerListChunk = require('./encoders/data/data-tracker-list-chunk');
const infoPacketChunk = require('./encoders/info/info-packet-chunk');
const infoSystemNameChunk = require('./encoders/info/info-system-name-chunk');
const infoTrackerListChunk = require('./encoders/info/info-tracker-list-chunk');
const packetHeaderChunk = require('./encoders/packet-header-chunk');
import { Tracker } from './models/tracker';
class Encoder {
constructor(systemName, versionHigh = 2, versionLow = 3) {
import { Constants } from './constants';
import dataPacketChunk from './encoders/data/data-packet-chunk';
import dataTrackerListChunk from './encoders/data/data-tracker-list-chunk';
import infoPacketChunk from './encoders/info/info-packet-chunk';
import infoSystemNameChunk from './encoders/info/info-system-name-chunk';
import infoTrackerListChunk from './encoders/info/info-tracker-list-chunk';
import packetHeaderChunk from './encoders/packet-header-chunk';
export class Encoder {
private systemName: string;
private versionHigh: number;
private versionLow: number;
dataFrameId: number = 1;
infoFrameId: number = 1;
constructor(systemName: string, versionHigh: number = 2, versionLow: number = 3) {
this.systemName = systemName;
this.versionHigh = versionHigh;
this.versionLow = versionLow;
this.dataFrameId = 1;
this.infoFrameId = 1;
}
getInfoPackets(timestamp, trackers) {
getInfoPackets(timestamp: bigint, trackers: Tracker[]) {
const systemNameChunk = infoSystemNameChunk(this.systemName);
const trackerChunks = trackers.map((tracker) => tracker.getInfoChunk());
const trackerChunks = trackers.map((tracker: Tracker) => tracker.getInfoChunk());
const infoPackets = [];
const infoPackets: Buffer[] = [];
const trackerChunksLists = [];
let currentTrackerList = [];
const trackerChunksLists: Buffer[][] = [];
let currentTrackerList: Buffer[] = [];
let currentInfoPacketSize = PACKET_HEADER_SIZE + systemNameChunk.length + CHUNK_HEADER_SIZE;
let currentInfoPacketSize = Constants.MAX_UDP_PACKET_SIZE + systemNameChunk.length + Constants.CHUNK_HEADER_SIZE;
trackerChunks.forEach((trackerChunk) => {
if (currentInfoPacketSize + trackerChunk.length > MAX_UDP_PACKET_SIZE) {
trackerChunks.forEach((trackerChunk: Buffer) => {
if (currentInfoPacketSize + trackerChunk.length > Constants.MAX_UDP_PACKET_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentInfoPacketSize = 0;
@@ -54,17 +59,17 @@ class Encoder {
return infoPackets;
}
getDataPackets(timestamp, trackers) {
getDataPackets(timestamp: bigint, trackers: Tracker[]) {
const allTrackerChunks = trackers.map((tracker) => tracker.getDataChunk());
const dataPackets = [];
const dataPackets: Buffer[] = [];
const trackerChunksLists = [];
let currentTrackerList = [];
const trackerChunksLists: Buffer[][] = [];
let currentTrackerList: Buffer[] = [];
let currentDataPacketSize = PACKET_HEADER_SIZE + CHUNK_HEADER_SIZE;
let currentDataPacketSize = Constants.PACKET_HEADER_SIZE + Constants.CHUNK_HEADER_SIZE;
allTrackerChunks.forEach((trackerChunk) => {
if (currentDataPacketSize + trackerChunk.length > MAX_UDP_PACKET_SIZE) {
if (currentDataPacketSize + trackerChunk.length > Constants.MAX_UDP_PACKET_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentDataPacketSize = 0;
@@ -91,5 +96,3 @@ class Encoder {
return dataPackets;
}
}
module.exports = Encoder;
@@ -1,4 +1,4 @@
module.exports = (id, chunkData, hasSubchunks) => {
export default (id: number, chunkData: Buffer, hasSubchunks: boolean): Buffer => {
if (chunkData.length > 0x7fff) {
throw new Error('chunkData can not be greater than 32767 bytes');
}
-4
View File
@@ -1,4 +0,0 @@
const chunk = require('../chunk');
module.exports = (packetHeaderChunk, trackerListChunk) =>
chunk(0x6755, Buffer.concat([packetHeaderChunk, trackerListChunk]), true);
+3
View File
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (packetHeaderChunk: Buffer, trackerListChunk: Buffer): Buffer =>
chunk(0x6755, Buffer.concat([packetHeaderChunk, trackerListChunk]), true);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (x, y, z) => {
export default (x: number, y: number, z: number): Buffer => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
-3
View File
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (trackerId, fieldChunks) => chunk(trackerId, Buffer.concat(fieldChunks), fieldChunks.length > 0);
+4
View File
@@ -0,0 +1,4 @@
import chunk from '../chunk';
export default (trackerId: number, fieldChunks: Buffer[]): Buffer =>
chunk(trackerId, Buffer.concat(fieldChunks), fieldChunks.length > 0);
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (trackerChunks) => chunk(0x0001, Buffer.concat(trackerChunks), true);
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (trackerChunks: Buffer[]) => chunk(0x0001, Buffer.concat(trackerChunks), true);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (x, y, z) => {
export default (x: number, y: number, z: number): Buffer => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (x, y, z) => {
export default (x: number, y: number, z: number): Buffer => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (x, y, z) => {
export default (x: number, y: number, z: number): Buffer => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (validity) => {
export default (validity: number): Buffer => {
const buf = Buffer.alloc(4);
buf.writeFloatLE(validity);
return chunk(0x0003, buf, false);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (timestamp) => {
export default (timestamp: bigint): Buffer => {
const buf = Buffer.alloc(8);
buf.writeBigUInt64LE(BigInt(timestamp));
return chunk(0x0006, buf, false);
@@ -1,6 +1,6 @@
const chunk = require('../chunk');
import chunk from '../chunk';
module.exports = (x, y, z) => {
export default (x: number, y: number, z: number): Buffer => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
-4
View File
@@ -1,4 +0,0 @@
const chunk = require('../chunk');
module.exports = (packetHeaderChunk, systemNameChunk, trackerListChunk) =>
chunk(0x6756, Buffer.concat([packetHeaderChunk, systemNameChunk, trackerListChunk]), true);
+4
View File
@@ -0,0 +1,4 @@
import chunk from '../chunk';
export default (packetHeaderChunk: Buffer, systemNameChunk: Buffer, trackerListChunk: Buffer): Buffer =>
chunk(0x6756, Buffer.concat([packetHeaderChunk, systemNameChunk, trackerListChunk]), true);
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (systemName) => chunk(0x0001, Buffer.from(systemName), false);
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (systemName: string): Buffer => chunk(0x0001, Buffer.from(systemName), false);
-3
View File
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (trackerId, trackerNameChunk) => chunk(trackerId, trackerNameChunk, true);
+3
View File
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (trackerId: number, trackerNameChunk: Buffer): Buffer => chunk(trackerId, trackerNameChunk, true);
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (trackerChunks) => chunk(0x0002, Buffer.concat(trackerChunks), true);
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (trackerChunks: Buffer[]): Buffer => chunk(0x0002, Buffer.concat(trackerChunks), true);
@@ -1,3 +0,0 @@
const chunk = require('../chunk');
module.exports = (trackerName) => chunk(0x0000, Buffer.from(trackerName), false);
@@ -0,0 +1,3 @@
import chunk from '../chunk';
export default (trackerName: string) => chunk(0x0000, Buffer.from(trackerName), false);
@@ -1,6 +1,12 @@
const chunk = require('./chunk');
import chunk from './chunk';
module.exports = (timestamp, versionHigh, versionLow, frameId, framePacketCount) => {
export default (
timestamp: bigint,
versionHigh: number,
versionLow: number,
frameId: number,
framePacketCount: number
): Buffer => {
if (frameId > 255) {
throw new Error('frame id must be >= 0 and <= 255');
}
+3
View File
@@ -0,0 +1,3 @@
export { Decoder } from './decoder';
export { Encoder } from './encoder';
export { Tracker } from './models/tracker';
-128
View File
@@ -1,128 +0,0 @@
const dataTrackerAccelChunk = require('../encoders/data/data-tracker-accel-chunk');
const dataTrackerChunk = require('../encoders/data/data-tracker-chunk');
const dataTrackerOriChunk = require('../encoders/data/data-tracker-ori-chunk');
const dataTrackerPosChunk = require('../encoders/data/data-tracker-pos-chunk');
const dataTrackerSpeedChunk = require('../encoders/data/data-tracker-speed-chunk');
const dataTrackerStatusChunk = require('../encoders/data/data-tracker-status-chunk');
const dataTrackerTimestampChunk = require('../encoders/data/data-tracker-timestamp-chunk');
const dataTrackerTrgtposChunk = require('../encoders/data/data-tracker-trgtpos-chunk');
const infoTrackerChunk = require('../encoders/info/info-tracker-chunk');
const infoTrackerNameChunk = require('../encoders/info/info-tracker-name-chunk');
class Tracker {
/**
* @constructor
* @param {number} id - tracker id
* @param {string} name - tracker name
*/
constructor(id, name) {
this.id = id;
this.name = name;
this.pos = undefined;
this.speed = undefined;
this.ori = undefined;
this.validity = undefined;
this.accel = undefined;
this.trgtpos = undefined;
this.timestamp = undefined;
}
/**
* @param {float} x
* @param {float} y
* @param {float} z
*/
setPos(x, y, z) {
this.pos = [x, y, z];
}
/**
* @param {float} x
* @param {float} y
* @param {float} z
*/
setSpeed(x, y, z) {
this.speed = [x, y, z];
}
/**
* @param {float} x
* @param {float} y
* @param {float} z
*/
setOri(x, y, z) {
this.ori = [x, y, z];
}
/**
* @param {float} validity
*/
setStatus(validity) {
this.validity = validity;
}
/**
* @param {float} x
* @param {float} y
* @param {float} z
*/
setAccel(x, y, z) {
this.accel = [x, y, z];
}
/**
* @param {float} x
* @param {float} y
* @param {float} z
*/
setTrgtPos(x, y, z) {
this.trgtpos = [x, y, z];
}
/**
* @param {BigInt} timestamp
*/
setTimestamp(timestamp) {
this.timestamp = timestamp;
}
getDataChunk() {
const fieldChunks = [];
if (this.pos) {
fieldChunks.push(dataTrackerPosChunk(...this.pos));
}
if (this.speed) {
fieldChunks.push(dataTrackerSpeedChunk(...this.speed));
}
if (this.ori) {
fieldChunks.push(dataTrackerOriChunk(...this.ori));
}
if (this.validity) {
fieldChunks.push(dataTrackerStatusChunk(this.validity));
}
if (this.accel) {
fieldChunks.push(dataTrackerAccelChunk(...this.accel));
}
if (this.trgtpos) {
fieldChunks.push(dataTrackerTrgtposChunk(...this.trgtpos));
}
if (this.timestamp) {
fieldChunks.push(dataTrackerTimestampChunk(this.timestamp));
}
return dataTrackerChunk(this.id, fieldChunks);
}
getInfoChunk() {
return infoTrackerChunk(this.id, infoTrackerNameChunk(this.name));
}
}
module.exports = Tracker;
+100
View File
@@ -0,0 +1,100 @@
import dataTrackerAccelChunk from '../encoders/data/data-tracker-accel-chunk';
import dataTrackerChunk from '../encoders/data/data-tracker-chunk';
import dataTrackerOriChunk from '../encoders/data/data-tracker-ori-chunk';
import dataTrackerPosChunk from '../encoders/data/data-tracker-pos-chunk';
import dataTrackerSpeedChunk from '../encoders/data/data-tracker-speed-chunk';
import dataTrackerStatusChunk from '../encoders/data/data-tracker-status-chunk';
import dataTrackerTimestampChunk from '../encoders/data/data-tracker-timestamp-chunk';
import dataTrackerTrgtposChunk from '../encoders/data/data-tracker-trgtpos-chunk';
import infoTrackerChunk from '../encoders/info/info-tracker-chunk';
import infoTrackerNameChunk from '../encoders/info/info-tracker-name-chunk';
export class Tracker {
id: number;
name: string;
pos?: [number, number, number];
speed?: [number, number, number];
ori?: [number, number, number];
validity?: number;
accel?: [number, number, number];
trgtpos?: [number, number, number];
timestamp?: bigint;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
this.pos = undefined;
this.speed = undefined;
this.ori = undefined;
this.validity = undefined;
this.accel = undefined;
this.trgtpos = undefined;
this.timestamp = undefined;
}
setPos(x: number, y: number, z: number) {
this.pos = [x, y, z];
}
setSpeed(x: number, y: number, z: number) {
this.speed = [x, y, z];
}
setOri(x: number, y: number, z: number) {
this.ori = [x, y, z];
}
setStatus(validity: number) {
this.validity = validity;
}
setAccel(x: number, y: number, z: number) {
this.accel = [x, y, z];
}
setTrgtPos(x: number, y: number, z: number) {
this.trgtpos = [x, y, z];
}
setTimestamp(timestamp: bigint) {
this.timestamp = timestamp;
}
getDataChunk() {
const fieldChunks: Buffer[] = [];
if (this.pos) {
fieldChunks.push(dataTrackerPosChunk(...this.pos));
}
if (this.speed) {
fieldChunks.push(dataTrackerSpeedChunk(...this.speed));
}
if (this.ori) {
fieldChunks.push(dataTrackerOriChunk(...this.ori));
}
if (this.validity) {
fieldChunks.push(dataTrackerStatusChunk(this.validity));
}
if (this.accel) {
fieldChunks.push(dataTrackerAccelChunk(...this.accel));
}
if (this.trgtpos) {
fieldChunks.push(dataTrackerTrgtposChunk(...this.trgtpos));
}
if (this.timestamp) {
fieldChunks.push(dataTrackerTimestampChunk(this.timestamp));
}
return dataTrackerChunk(this.id, fieldChunks);
}
getInfoChunk() {
return infoTrackerChunk(this.id, infoTrackerNameChunk(this.name));
}
}