@foxglove/ros1
Version:
Standalone TypeScript implementation of the ROS 1 (Robot Operating System) protocol with a pluggable transport layer
96 lines (84 loc) • 3.42 kB
text/typescript
import net from "net";
import type { AddressInfo } from "net";
import { TcpConnection } from "./TcpConnection";
import { TcpSocket } from "./TcpTypes";
import { TcpSocketNode } from "./nodejs/TcpSocketNode";
////////////////////////////////////////////////////////////////////////////////
function hexToUint8Array(hex: string): Uint8Array {
const match = hex.match(/[\da-f]{2}/gi);
if (match == undefined) {
return new Uint8Array();
}
return new Uint8Array(
match.map((h) => {
return parseInt(h, 16);
}),
);
}
const CLIENT_HEADER = new Map<string, string>([
["topic", "/turtle1/color_sensor"],
["md5sum", "*"],
["callerid", "/test"],
["type", "turtlesim/Color"],
["tcp_nodelay", "1"],
]);
const HEADER = new Map<string, string>([
["callerid", "/turtlesim"],
["latching", "0"],
["md5sum", "353891e354491c51aabe32df673fb446"],
["message_definition", "uint8 r\nuint8 g\nuint8 b\n"],
["topic", "/turtle1/color_sensor"],
["type", "turtlesim/Color"],
]);
const HEADER_DATA = hexToUint8Array(
"1300000063616c6c657269643d2f747572746c6573696d0a0000006c61746368696e673d30270000006d643573756d3d33353338393165333534343931633531616162653332646636373366623434362b0000006d6573736167655f646566696e6974696f6e3d75696e743820720a75696e743820670a75696e743820620a1b000000746f7069633d2f747572746c65312f636f6c6f725f73656e736f7214000000747970653d747572746c6573696d2f436f6c6f72",
);
describe("TcpConnection", () => {
it("ParseHeader", () => {
const header = TcpConnection.ParseHeader(HEADER_DATA);
expect(header.size).toEqual(6);
expect(header).toEqual(HEADER);
});
it("SerializeHeader", () => {
const data = TcpConnection.SerializeHeader(HEADER);
expect(data).toEqual(HEADER_DATA);
});
it("Connects and reads a parsed message", async () => {
// Create the TCP listening socket
const server = net.createServer((client) => {
client.on("data", (fullData) => {
const data = fullData.subarray(4);
expect(data.byteLength).toEqual(102);
expect(TcpConnection.ParseHeader(data)).toEqual(CLIENT_HEADER);
client.write(new Uint8Array([0xb6, 0x00, 0x00, 0x00]));
client.write(HEADER_DATA);
client.write(new Uint8Array([0x03, 0x00, 0x00, 0x00]));
client.write(new Uint8Array([0x45, 0x56, 0xff]));
});
client.on("error", (err) => {
throw err;
});
});
await new Promise<void>((resolve) => server.listen(undefined, undefined, undefined, resolve));
const port = (server.address() as AddressInfo).port;
// Create the client socket
const tcpSocketCreate = async (options: { host: string; port: number }): Promise<TcpSocket> => {
return new TcpSocketNode(options.host, options.port, new net.Socket());
};
const socket = await tcpSocketCreate({ host: "localhost", port });
const connection = new TcpConnection(socket, "localhost", port, CLIENT_HEADER);
const p = new Promise((resolve, reject) => {
connection.on("message", (msg, data: Uint8Array) => {
expect(data.byteLength).toEqual(3);
resolve(msg);
});
connection.on("error", reject);
});
await socket.connect();
await expect(p).resolves.toEqual({ b: 255, g: 86, r: 69 });
connection.close();
await new Promise<void>((resolve, reject) =>
server.close((err) => (err != undefined ? reject(err) : resolve())),
);
});
});