remove binary-parsser lib dependency

This commit is contained in:
2023-09-14 19:47:16 -05:00
parent d443b313cf
commit 016239b3ad
32 changed files with 373 additions and 455 deletions
+1
View File
@@ -14,5 +14,6 @@ module.exports = {
'class-methods-use-this': 'off',
'import/extensions': 'off',
'import/no-relative-packages': 'off',
'no-param-reassign': 'off',
},
};
+12 -6
View File
@@ -13,15 +13,18 @@ js implementation of the [PosiStageNet protocol](https://github.com/vyv/psn-cpp/
const { Decoder } = require('@jwetzell/posistagenet')
const decoder = new Decoder()
const buffer = Buffer.from('source PSN packets from somewhere')
const infoPacketBuffer = Buffer.from('5667348000000c0001000000000000000203010101000b00536572766572204e616d650200118001000d8000000900547261636b65722031','hex')
const dataPacketBuffer = Buffer.from('5567288000000c00010000000000000002030101010014800100108000000c000000803f0000803f0000803f','hex')
decoder.decode(buffer)
decoder.decode(infoPacketBuffer)
decoder.decode(dataPacketBuffer)
// data from PSN Info packets
console.log(decoder.info)
// the system_name from info packets if available
console.log(decoder.system_name)
// data from PSN Data packets
console.log(decoder.data)
// map of trackers populated with any data that has been received
// this merges both info and data packet properties into one
console.log(decoder.trackers)
```
@@ -56,3 +59,6 @@ infoPackets.forEach((infoPacket)=>{
```
## Notes
- UInt64
- PosiStageNet uses 64 bit UInt's for timestamp values make sure to use [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) if using values over `Number.MAX_SAFE_INTEGER`
+14 -11
View File
@@ -1,3 +1,4 @@
// recreation of psn_client.cpp example from https://github.com/vyv/psn-cpp
const dgram = require('dgram');
const { Decoder } = require('..');
@@ -14,29 +15,29 @@ client.on('message', (buffer) => {
client.bind(56565, '0.0.0.0');
setInterval(() => {
if (decoder.info.system_name) {
console.log(`System Name: ${decoder.info.system_name}`);
if (decoder.system_name) {
console.log(`System Name: ${decoder.system_name}`);
}
if (Object.keys(decoder.info.trackers).length > 0) {
console.log(`Tracker Count: ${Object.keys(decoder.info.trackers).length}`);
if (Object.keys(decoder.trackers).length > 0) {
console.log(`Tracker Count: ${Object.keys(decoder.trackers).length}`);
}
Object.entries(decoder.data.trackers).forEach(([trackerId, tracker]) => {
Object.entries(decoder.trackers).forEach(([trackerId, tracker]) => {
console.log(
`Tracker - id: ${trackerId} | name: ${
decoder.info.trackers[trackerId]?.name ? decoder.info.trackers[trackerId]?.name : ''
decoder.trackers[trackerId]?.tracker_name ? decoder.trackers[trackerId]?.tracker_name : ''
}`
);
if (tracker.pos) {
console.log(`\tpos: ${tracker.pos.x}, ${tracker.pos.y}, ${tracker.pos.z}`);
console.log(`\tpos: ${tracker.pos.pos_x}, ${tracker.pos.pos_y}, ${tracker.pos.pos_z}`);
}
if (tracker.speed) {
console.log(`\tspeed: ${tracker.speed.x}, ${tracker.speed.y}, ${tracker.speed.z}`);
console.log(`\tspeed: ${tracker.speed.speed_x}, ${tracker.speed.speed_y}, ${tracker.speed.speed_z}`);
}
if (tracker.ori) {
console.log(`\tori: ${tracker.ori.x}, ${tracker.ori.y}, ${tracker.ori.z}`);
console.log(`\tori: ${tracker.ori.ori_x}, ${tracker.ori.ori_y}, ${tracker.ori.ori_z}`);
}
if (tracker.status) {
@@ -44,11 +45,13 @@ setInterval(() => {
}
if (tracker.accel) {
console.log(`\taccel: ${tracker.accel.x}, ${tracker.accel.y}, ${tracker.accel.z}`);
console.log(`\taccel: ${tracker.accel.accel_x}, ${tracker.accel.accel_y}, ${tracker.accel.accel_z}`);
}
if (tracker.trgtpos) {
console.log(`\ttrgtpos: ${tracker.trgtpos.x}, ${tracker.trgtpos.y}, ${tracker.trgtpos.z}`);
console.log(
`\ttrgtpos: ${tracker.trgtpos.trgtpos_x}, ${tracker.trgtpos.trgtpos_y}, ${tracker.trgtpos.trgtpos_z}`
);
}
if (tracker.timestamp) {
+18 -8
View File
@@ -1,8 +1,10 @@
// recreation of psn_server.cpp example from https://github.com/vyv/psn-cpp
const dgram = require('dgram');
const { Encoder, Tracker } = require('..');
const { Encoder, Tracker, Decoder } = require('..');
const client = dgram.createSocket('udp4');
const encoder = new Encoder('Test PSN Server', 2, 0);
const decoder = new Decoder();
const trackers = [];
@@ -22,7 +24,7 @@ const distFromSun = [0, 0.58, 1.08, 1.5, 2.28, 7.78, 14.29, 28.71, 45.04, 59.13]
let timestamp = 0;
setInterval(() => {
for (let index = 0; index < trackers.length; index += 1) {
orbits.forEach((orbit, index) => {
const a = 1.0 / orbits[index];
const b = distFromSun[index];
const x = timestamp;
@@ -36,19 +38,27 @@ setInterval(() => {
trackers[index].setTrgtPos(3, 14, 16);
trackers[index].setStatus(index / 10.0);
trackers[index].setTimestamp(timestamp);
}
});
const dataPackets = encoder.getDataPackets(timestamp, trackers);
dataPackets.forEach((packet) => {
dataPackets.forEach((packet, index) => {
const decodedPacket = decoder.decode(packet);
console.log(
`Sending PSN_DATA_PACKET : Frame Id = ${decodedPacket.packet_header.frame_id} , Packet Count = ${index + 1}`
);
client.send(packet, 56565, '236.10.10.10');
});
timestamp += 1;
}, 20);
}, 5);
setInterval(() => {
const infoPackets = encoder.getInfoPackets(timestamp, trackers);
infoPackets.forEach((packet) => {
infoPackets.forEach((packet, index) => {
const decodedPacket = decoder.decode(packet);
console.log(
`Sending PSN_INFO_PACKET : Frame Id = ${decodedPacket.packet_header.frame_id} , Packet Count = ${index + 1}`
);
client.send(packet, 56565, '236.10.10.10');
});
}, 1000);
}, 500);
-11
View File
@@ -8,9 +8,6 @@
"name": "@jwetzell/posistagenet",
"version": "0.2.1",
"license": "ISC",
"dependencies": {
"binary-parser": "^2.2.1"
},
"devDependencies": {
"eslint": "^8.48.0",
"eslint-config-airbnb": "^19.0.4",
@@ -429,14 +426,6 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/binary-parser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/binary-parser/-/binary-parser-2.2.1.tgz",
"integrity": "sha512-5ATpz/uPDgq5GgEDxTB4ouXCde7q2lqAQlSdBRQVl/AJnxmQmhIfyxJx+0MGu//D5rHQifkfGbWWlaysG0o9NA==",
"engines": {
"node": ">=12"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@jwetzell/posistagenet",
"version": "0.2.1",
"version": "0.3.0",
"description": "",
"main": "index.js",
"scripts": {
@@ -19,8 +19,5 @@
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.0.0",
"prettier": "^3.0.3"
},
"dependencies": {
"binary-parser": "^2.2.1"
}
}
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
PACKET_HEADER_SIZE: 16,
MAX_UDP_PACKET_SIZE: 1500,
CHUNK_HEADER_SIZE: 4,
};
+49 -71
View File
@@ -1,8 +1,7 @@
const PacketParser = require('./decoders/packet');
const InfoPacket = require('./models/packets/info-packet');
const DataPacket = require('./models/packets/data-packet');
const packetDecoder = require('./decoders/packet');
class Decoder {
// TODO(jwetzell): check if these last packet headers are necessary to hold on to
lastInfoPacketHeader;
lastDataPacketHeader;
@@ -12,74 +11,58 @@ class Decoder {
dataPacketFrames = {};
constructor() {
this.info = {
trackers: {},
};
this.system_name = '';
this.data = {
trackers: {},
};
this.trackers = {};
}
updateInfo(framePackets) {
framePackets.forEach((packet) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
// NOTE(jwetzell): system name subChunk
this.info.system_name = subChunk.system_name;
} else if (subChunk.id === 0x0002) {
subChunk.trackers?.forEach((tracker) => {
if (this.info.trackers[tracker.id] === undefined) {
this.info.trackers[tracker.id] = {};
}
this.info.trackers[tracker.id].name = tracker.tracker_name.tracker_name;
});
}
});
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) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
subChunk.trackers?.forEach((tracker) => {
if (this.data.trackers[tracker.id] === undefined) {
this.data.trackers[tracker.id] = {};
}
this.data.trackers[tracker.id] = {
pos: tracker.pos,
speed: tracker.speed,
ori: tracker.ori,
status: tracker.status,
accel: tracker.accel,
trgtpos: tracker.trgtpos,
timestamp: tracker.timestamp,
};
});
}
});
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 = PacketParser.parse(packetBuf);
const packet = packetDecoder(packetBuf);
if (packet.id === 0x6756) {
const infoPacket = new InfoPacket(packet);
const infoPacket = packet;
if (infoPacket) {
if (infoPacket.subChunks?.length > 0) {
const currentInfoPacketHeader = infoPacket.getHeaderPacket();
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?
return;
throw new Error('malformed packet');
}
const systemSubChunk = infoPacket.getSystemPacket();
const systemSubChunk = infoPacket.system_name;
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
return;
throw new Error('malformed packet');
}
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
@@ -99,32 +82,27 @@ class Decoder {
}
}
} else if (packet.id === 0x6755) {
const dataPacket = new DataPacket(packet);
if (dataPacket) {
if (dataPacket.subChunks?.length > 0) {
const currentDataPacketHeader = dataPacket.getHeaderPacket();
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
return;
}
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;
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;
}
}
+15 -19
View File
@@ -1,20 +1,16 @@
const { Parser } = require('binary-parser');
module.exports = (buffer, dataDecoder) => {
let offset = 0;
const chunk = {};
chunk.id = buffer.readUInt16LE(offset);
offset += 2;
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.buffer('chunk_data', {
length: 'data_len',
});
// NOTE(jwetzell): this data is split up as 1 bit for has_subchunks and 15 bits for the data_len
const combinedLengthAndFlag = buffer.readUInt16LE(offset);
chunk.has_subchunks = combinedLengthAndFlag > 32768;
chunk.data_len = chunk.has_subchunks ? combinedLengthAndFlag - 32768 : combinedLengthAndFlag;
offset += 2;
chunk.chunk_data = buffer.subarray(offset, offset + chunk.data_len);
dataDecoder(chunk);
return chunk;
};
+29
View File
@@ -0,0 +1,29 @@
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);
+41
View File
@@ -0,0 +1,41 @@
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);
+42 -28
View File
@@ -1,29 +1,43 @@
const Parser = require('binary-parser').Parser;
/* eslint-disable no-case-declarations */
const chunk = require('../chunk');
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.choice('data', {
tag: 'id',
choices: {
0x0000: new Parser().floatle('x').floatle('y').floatle('z'),
0x0001: new Parser().floatle('x').floatle('y').floatle('z'),
0x0002: new Parser().floatle('x').floatle('y').floatle('z'),
0x0003: new Parser().floatle('validity'),
0x0004: new Parser().floatle('x').floatle('y').floatle('z'),
0x0005: new Parser().floatle('x').floatle('y').floatle('z'),
0x0006: new Parser().uint64le('tracker_timestamp'),
},
});
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);
+12 -19
View File
@@ -1,24 +1,17 @@
module.exports = (buffer) => {
const trackerList = {
trackers: [],
};
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 < buffer.length) {
const trackerChunk = {};
trackerChunk.id = buffer.readUInt16LE(offset);
offset += 2;
// NOTE(jwetzell): this data is split up as 1 bit for has_subchunks and 15 bits for the data_len
const tempBytesAsBinary = buffer.readUInt16LE(offset).toString(2);
trackerChunk.has_subchunks = tempBytesAsBinary.charAt(0) === '0';
trackerChunk.data_len = parseInt(tempBytesAsBinary.substring(1), 2);
offset += 2;
trackerChunk.data = buffer.subarray(offset, offset + trackerChunk.data_len);
while (offset < dataTrackerListChunk.chunk_data.length) {
const trackerChunk = dataTrackerChunk(dataTrackerListChunk.chunk_data.subarray(offset));
offset += CHUNK_HEADER_SIZE;
offset += trackerChunk.data_len;
trackerList.trackers.push(trackerChunk);
dataTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
}
return trackerList;
};
}
module.exports = (buffer) => chunk(buffer, decodeDataTrackerListChunk);
-7
View File
@@ -1,7 +0,0 @@
const TrackerListChunk = require('./data-tracker-list-chunk');
const TrackerFieldChunk = require('./data-tracker-field-chunk');
module.exports = {
TrackerFieldChunk,
TrackerListChunk,
};
-17
View File
@@ -1,17 +0,0 @@
const Chunk = require('./chunk');
const PacketHeaderChunk = require('./packet-header-chunk');
const Packet = require('./packet');
const Info = require('./info');
const Data = require('./data');
module.exports = {
Chunk,
PacketHeaderChunk,
Packet,
Info: {
...Info,
},
Data: {
...Data,
},
};
-7
View File
@@ -1,7 +0,0 @@
const SystemNameChunk = require('./info-system-name-chunk');
const TrackerListChunk = require('./info-tracker-list-chunk');
module.exports = {
SystemNameChunk,
TrackerListChunk,
};
+35
View File
@@ -0,0 +1,35 @@
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);
+8 -2
View File
@@ -1,3 +1,9 @@
const Parser = require('binary-parser').Parser;
const chunk = require('../chunk');
module.exports = new Parser().string('system_name', { greedy: true });
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);
+24
View File
@@ -0,0 +1,24 @@
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);
+16 -38
View File
@@ -1,39 +1,17 @@
const Parser = require('binary-parser').Parser;
const { CHUNK_HEADER_SIZE } = require('../../constants');
const chunk = require('../chunk');
const infoTrackerChunk = require('./info-tracker-chunk');
const infoTrackerNameParser = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.string('tracker_name', { length: 'data_len' });
const infoTrackerParser = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.nest('tracker_name', {
type: infoTrackerNameParser,
});
module.exports = new Parser().array('trackers', { type: infoTrackerParser, readUntil: 'eof' });
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,8 @@
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);
+11 -8
View File
@@ -1,9 +1,12 @@
const Parser = require('binary-parser').Parser;
const chunk = require('./chunk');
module.exports = new Parser()
.uint64le('packet_timestamp')
.uint8('version_high')
.uint8('version_low')
.uint8('frame_id')
.uint8('frame_packet_count')
.seek(4);
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);
+13 -17
View File
@@ -1,18 +1,14 @@
const Parser = require('binary-parser').Parser;
const dataPacketChunk = require('./data/data-packet-chunk');
const infoPacketChunk = require('./info/info-packet-chunk');
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.buffer('chunk_data', { readUntil: 'eof' });
module.exports = (buffer) => {
const chunkId = buffer.readUInt16LE();
switch (chunkId) {
case 0x6756:
return infoPacketChunk(buffer);
case 0x6755:
return dataPacketChunk(buffer);
default:
return {};
}
};
+16 -16
View File
@@ -1,14 +1,13 @@
const dataChunk = require('./encoders/data/data-chunk');
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 infoChunk = require('./encoders/info/info-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');
const MAX_TRACKER_LIST_SIZE = 1000;
class Encoder {
constructor(systemName, versionHigh, versionLow) {
constructor(systemName, versionHigh = 2, versionLow = 3) {
this.systemName = systemName;
this.versionHigh = versionHigh;
this.versionLow = versionLow;
@@ -17,6 +16,8 @@ class Encoder {
}
getInfoPackets(timestamp, trackers) {
const systemNameChunk = infoSystemNameChunk(this.systemName);
const trackerChunks = trackers.map((tracker) => tracker.getInfoChunk());
const infoPackets = [];
@@ -24,16 +25,16 @@ class Encoder {
const trackerChunksLists = [];
let currentTrackerList = [];
let currentTrackerListSize = 0;
let currentInfoPacketSize = PACKET_HEADER_SIZE + systemNameChunk.length + CHUNK_HEADER_SIZE;
trackerChunks.forEach((trackerChunk) => {
if (currentTrackerListSize + trackerChunk.length > MAX_TRACKER_LIST_SIZE) {
if (currentInfoPacketSize + trackerChunk.length > MAX_UDP_PACKET_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentTrackerListSize = 0;
currentInfoPacketSize = 0;
}
currentTrackerList.push(trackerChunk);
currentTrackerListSize += trackerChunk.length;
currentInfoPacketSize += trackerChunk.length;
});
trackerChunksLists.push(currentTrackerList);
const header = packetHeaderChunk(
@@ -44,7 +45,7 @@ class Encoder {
trackerChunksLists.length
);
trackerChunksLists.forEach((trackerChunkList) => {
infoPackets.push(infoChunk(header, infoSystemNameChunk(this.systemName), infoTrackerListChunk(trackerChunkList)));
infoPackets.push(infoPacketChunk(header, systemNameChunk, infoTrackerListChunk(trackerChunkList)));
});
this.dataFrameId += 1;
if (this.dataFrameId > 255) {
@@ -55,22 +56,21 @@ class Encoder {
getDataPackets(timestamp, trackers) {
const allTrackerChunks = trackers.map((tracker) => tracker.getDataChunk());
const dataPackets = [];
const trackerChunksLists = [];
let currentTrackerList = [];
let currentTrackerListSize = 0;
let currentDataPacketSize = PACKET_HEADER_SIZE + CHUNK_HEADER_SIZE;
allTrackerChunks.forEach((trackerChunk) => {
if (currentTrackerListSize + trackerChunk.length > MAX_TRACKER_LIST_SIZE) {
if (currentDataPacketSize + trackerChunk.length > MAX_UDP_PACKET_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentTrackerListSize = 0;
currentDataPacketSize = 0;
}
currentTrackerList.push(trackerChunk);
currentTrackerListSize += trackerChunk.length;
currentDataPacketSize += trackerChunk.length;
});
trackerChunksLists.push(currentTrackerList);
@@ -82,7 +82,7 @@ class Encoder {
trackerChunksLists.length
);
trackerChunksLists.forEach((trackerChunks) => {
dataPackets.push(dataChunk(header, dataTrackerListChunk(trackerChunks)));
dataPackets.push(dataPacketChunk(header, dataTrackerListChunk(trackerChunks)));
});
this.dataFrameId += 1;
if (this.dataFrameId > 255) {
+1 -1
View File
@@ -1,3 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerId, fieldChunks) => chunk(trackerId, Buffer.concat(fieldChunks), true);
module.exports = (trackerId, fieldChunks) => chunk(trackerId, Buffer.concat(fieldChunks), fieldChunks.length > 0);
@@ -2,9 +2,6 @@ const chunk = require('../chunk');
module.exports = (timestamp) => {
const buf = Buffer.alloc(8);
const timestampHigh = timestamp.toString(2).padStart(64, '0').substring(0, 32);
const timestampLow = timestamp.toString(2).padStart(64, '0').substring(32);
buf.writeUInt32LE(parseInt(timestampLow, 2));
buf.writeUInt32LE(parseInt(timestampHigh, 2), 4);
buf.writeBigUInt64LE(BigInt(timestamp));
return chunk(0x0006, buf, false);
};
+1 -5
View File
@@ -5,11 +5,7 @@ module.exports = (timestamp, versionHigh, versionLow, frameId, framePacketCount)
throw new Error('frame id must be >= 0 and <= 255');
}
const packetHeader = Buffer.alloc(12);
const timestampHigh = timestamp.toString(2).padStart(64, '0').substring(0, 32);
const timestampLow = timestamp.toString(2).padStart(64, '0').substring(32);
packetHeader.writeUInt32LE(parseInt(timestampLow, 2));
packetHeader.writeUInt32LE(parseInt(timestampHigh, 2), 4);
packetHeader.writeBigUInt64LE(BigInt(timestamp));
packetHeader.writeUint8(versionHigh, 8);
packetHeader.writeUint8(versionLow, 9);
packetHeader.writeUint8(frameId, 10);
-93
View File
@@ -1,93 +0,0 @@
/* eslint-disable no-case-declarations */
/* eslint-disable no-param-reassign */
const { Parser } = require('binary-parser');
const Decoders = require('../../decoders');
class DataPacket {
constructor(packet) {
this.packet = packet;
this.subChunks = [];
// console.log(this.packet);
if (this.packet.has_subchunks) {
this.subChunkParser = new Parser().array('chunks', {
type: Decoders.Chunk,
lengthInBytes: this.packet.data_len,
});
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
this.subChunks = this.subChunks.map((subChunk) => {
let populatedSubChunk = {};
populatedSubChunk.data_len_valid = subChunk.chunk_data.length === subChunk.data_len;
switch (subChunk.id) {
case 0:
populatedSubChunk = {
...subChunk,
...Decoders.PacketHeaderChunk.parse(subChunk.chunk_data),
};
break;
case 1:
const dataTrackerList = Decoders.Data.TrackerListChunk(subChunk.chunk_data);
dataTrackerList.trackers?.forEach((tracker) => {
if (tracker.data && tracker.data_len > 0) {
const fields = new Parser()
.array('fields', {
type: Decoders.Data.TrackerFieldChunk,
lengthInBytes: tracker.data_len,
})
.parse(tracker.data);
if (fields.fields) {
fields.fields.forEach((field) => {
switch (field.id) {
case 0x0000:
tracker.pos = field.data;
break;
case 0x0001:
tracker.speed = field.data;
break;
case 0x0002:
tracker.ori = field.data;
break;
case 0x0003:
tracker.status = field.data;
break;
case 0x0004:
tracker.accel = field.data;
break;
case 0x0005:
tracker.trgtpos = field.data;
break;
case 0x0006:
tracker.timestamp = field.data;
break;
default:
break;
}
});
}
}
});
populatedSubChunk = {
...subChunk,
...dataTrackerList,
};
break;
default:
populatedSubChunk = {
...subChunk,
};
break;
}
return populatedSubChunk;
});
}
}
getHeaderPacket() {
return this.subChunks.find((subChunk) => subChunk.id === 0x0000);
}
getSystemPacket() {
return this.subChunks.find((subChunk) => subChunk.id === 0x0001);
}
}
module.exports = DataPacket;
-7
View File
@@ -1,7 +0,0 @@
const DataPacket = require('./data-packet');
const InfoPacket = require('./info-packet');
module.exports = {
DataPacket,
InfoPacket,
};
-56
View File
@@ -1,56 +0,0 @@
const { Parser } = require('binary-parser');
const Decoders = require('../../decoders');
class InfoPacket {
constructor(packet) {
this.packet = packet;
this.subChunks = [];
if (this.packet.has_subchunks) {
this.subChunkParser = new Parser().array('chunks', {
type: Decoders.Chunk,
lengthInBytes: this.packet.data_len,
});
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
this.subChunks = this.subChunks.map((subChunk) => {
let populatedSubChunk = {};
populatedSubChunk.data_len_valid = subChunk.chunk_data.length === subChunk.data_len;
switch (subChunk.id) {
case 0:
populatedSubChunk = {
...subChunk,
...Decoders.PacketHeaderChunk.parse(subChunk.chunk_data),
};
break;
case 1:
populatedSubChunk = {
...subChunk,
...Decoders.Info.SystemNameChunk.parse(subChunk.chunk_data),
};
break;
case 2:
populatedSubChunk = {
...subChunk,
...Decoders.Info.TrackerListChunk.parse(subChunk.chunk_data),
};
break;
default:
populatedSubChunk = {
...subChunk,
};
break;
}
return populatedSubChunk;
});
}
}
getHeaderPacket() {
return this.subChunks.find((subChunk) => subChunk.id === 0x0000);
}
getSystemPacket() {
return this.subChunks.find((subChunk) => subChunk.id === 0x0001);
}
}
module.exports = InfoPacket;