Skip to content

Instantly share code, notes, and snippets.

@defunctzombie
Last active January 25, 2022 18:49
Show Gist options
  • Save defunctzombie/83110a6314e4ccb8d278a5acc77effe1 to your computer and use it in GitHub Desktop.
Save defunctzombie/83110a6314e4ccb8d278a5acc77effe1 to your computer and use it in GitHub Desktop.
foxglove/studio/gh-2718
yarn
yarn start
import { FoxgloveServer } from "@foxglove/ws-protocol";
import Debug from "debug";
import { WebSocketServer } from "ws";
import { promises as fs } from "fs";
const log = Debug("foxglove:sysmon");
Debug.enable("foxglove:*");
// eslint-disable-next-line @typescript-eslint/promise-function-async
function delay(durationSec: number) {
return new Promise((resolve) => setTimeout(resolve, durationSec * 1000));
}
type Marker = {
header: {
frame_id: string;
stamp: { sec: number; nsec: number };
};
action: 0;
ns: string;
id: number;
type: 9;
scale: { x: number; y: number; z: number };
pose: {
position: { x: number; y: number; z: number };
orientation: { x: number; y: number; z: number; w: number };
};
lifetime: { sec: number; nsec: number };
text: string;
};
async function main() {
// read ws-dump file
const content = await fs.readFile("./ws-dump", "utf-8");
const lines = content.split("\n");
const messages: {
subscriptionId: number;
timestamp: bigint;
payload: Uint8Array;
}[] = [];
const subIdToChannelId = new Map<number, number>();
let channels = [];
for (const line of lines) {
if (line[0] === "{") {
const parsed = JSON.parse(line);
if (parsed.op === "advertise") {
channels = parsed.channels;
} else if (parsed.op === "subscribe") {
for (const subscription of parsed.subscriptions) {
subIdToChannelId.set(subscription.id, subscription.channelId);
}
}
} else if (line && line[0] !== "#") {
const buffer = new Uint8Array(Buffer.from(line, "base64"));
const view = new DataView(
buffer.buffer,
buffer.byteOffset,
buffer.byteLength
);
const opcode = view.getUint8(0);
const subscriptionId = view.getUint32(1, true);
const timestamp = view.getBigUint64(5, true);
const payload = buffer.slice(13);
console.log(opcode, subscriptionId, timestamp, payload);
messages.push({
subscriptionId,
timestamp,
payload,
});
}
}
const server = new FoxgloveServer({ name: "marker" });
const port = 8765;
const ws = new WebSocketServer({
port,
handleProtocols: (protocols) => server.handleProtocols(protocols),
});
ws.on("listening", () => {
console.log(`📡 Server listening on localhost:${port}`);
});
ws.on("connection", (conn, req) => {
const name = `${req.socket.remoteAddress!}:${req.socket.remotePort!}`;
server.handleConnection(conn, name);
});
const origToNewMap = new Map<number, number>();
for (const channel of channels) {
const id = server.addChannel({
topic: channel.topic,
encoding: channel.encoding,
schemaName: channel.schemaName,
schema: channel.schema,
});
origToNewMap.set(channel.id, id);
console.log(`${channel.topic} ${channel.id} -> ${id}`);
}
const textEncoder = new TextEncoder();
const INTERVAL_SEC = 2;
let controller: AbortController | undefined;
const nowSeconds = Math.floor(Date.now() / 1000);
server.on("subscribe", (_chanId) => {
if (controller) {
console.log("already running");
return;
}
controller = new AbortController();
void (async function (signal) {
await delay(INTERVAL_SEC);
let count = 0;
for (const message of messages) {
if (signal.aborted) {
console.log("aborted");
break;
}
console.log(message);
const origChannelId = subIdToChannelId.get(message.subscriptionId);
if (!origChannelId) {
console.log(`no channel for sub: ${message.subscriptionId}`);
await delay(INTERVAL_SEC);
continue;
}
const actualId = origToNewMap.get(origChannelId);
if (!actualId) {
console.log(`no new channel for ${origChannelId}`);
await delay(INTERVAL_SEC);
continue;
}
if (actualId === 2) {
continue;
}
console.log(count);
if (count > 3) {
break;
}
console.log(`sending to ${actualId}`);
server.sendMessage(actualId, message.timestamp, message.payload);
count++;
await delay(INTERVAL_SEC);
}
})(controller.signal);
});
server.on("unsubscribe", (_chanId) => {
log("stopping");
controller?.abort();
controller = undefined;
});
server.on("error", (err) => {
log("server error: %o", err);
});
}
void main();
{
"name": "@foxglove/ws-protocol-examples",
"version": "0.0.0",
"description": "Foxglove WebSocket protocol examples",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/foxglove/ws-protocol.git"
},
"author": {
"name": "Foxglove Technologies",
"email": "support@foxglove.dev"
},
"homepage": "https://foxglove.dev/",
"files": [
"dist",
"src"
],
"scripts": {
"start": "tsc -b ../*/tsconfig*.json && ts-node --project tsconfig.cjs.json --files index.ts"
},
"devDependencies": {
"@foxglove/tsconfig": "1.1.0",
"@types/debug": "4.1.7",
"@types/node": "16.11.12",
"@types/ws": "8.2.0",
"ts-node": "10.4.0",
"typescript": "4.4.4"
},
"dependencies": {
"@foxglove/ws-protocol": "^0.0.8",
"debug": "^4",
"tslib": "^2",
"ws": "8.2.3"
}
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/cjs",
"module": "commonjs"
}
}
{
"extends": "@foxglove/tsconfig/base",
"include": ["./**/*"],
"compilerOptions": {
"rootDir": "..",
"outDir": "./dist/esm",
"lib": ["es2020", "dom"],
"noUnusedLocals": false,
"paths": {
"@foxglove/ws-protocol": ["../ws-protocol/src"]
}
}
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
"@cspotcode/source-map-support@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"
"@foxglove/tsconfig@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@foxglove/tsconfig/-/tsconfig-1.1.0.tgz#48c37fffd6f349c3ee08a60fc62ccf636f3b59a6"
integrity sha512-qZU4MtXVgPhDBFazSEx7yDEuEg8cPHXFQVhBaUABZkCBdcnEE9sxlgEt0gSikF4fRtY6COGIJPVRflnPJXjJKA==
"@foxglove/ws-protocol@^0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@foxglove/ws-protocol/-/ws-protocol-0.0.8.tgz#c18aefa06746bb5df894e64778fc6d171c3ee1cd"
integrity sha512-wIME1ejWAAP/2TFOER+fMc8tdU9Ubph/TifO7FxFWxN5iDrj4ZyteEYFVpRpTz3SDGSZGLbhIcVROxAvR9EvPA==
dependencies:
debug "^4"
eventemitter3 "^4.0.7"
tslib "^2"
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
"@tsconfig/node16@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
"@types/debug@4.1.7":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==
dependencies:
"@types/ms" "*"
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node@*":
version "17.0.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab"
integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==
"@types/node@16.11.12":
version "16.11.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10"
integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==
"@types/ws@8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.0.tgz#75faefbe2328f3b833cb8dc640658328990d04f3"
integrity sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==
dependencies:
"@types/node" "*"
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^8.4.1:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
debug@^4:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ts-node@10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7"
integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==
dependencies:
"@cspotcode/source-map-support" "0.7.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
yn "3.1.1"
tslib@^2:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
typescript@4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
ws@8.2.3:
version "8.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment