Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 60 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@
"dependencies": {
"@xmldom/xmldom": "^0.9.8",
"bson": "^7.0.0",
"buffer": "^6.0.3",
"cbor-js": "^0.1.0",
"eventemitter3": "^5.0.1",
"pngparse": "^2.0.0",
"fast-png": "^7.0.1",
"uuid": "^13.0.0",
"ws": "^8.0.0"
},
Expand Down
25 changes: 5 additions & 20 deletions src/core/SocketAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "../types/protocol.js";
import { deserialize } from "bson";
import type { WebSocket as WsWebSocket } from "ws";
import decompressPng from "../util/decompressPng.js";

export type RequiredSocketInterface = Pick<
WebSocket | RTCDataChannel | WsWebSocket,
Expand Down Expand Up @@ -157,27 +158,11 @@ export default class SocketAdapter {
callback: (message: RosbridgeMessage) => void,
) {
if (isRosbridgePngMessage(message)) {
const pngCallback = (data: unknown) => {
if (isRosbridgeMessage(data)) {
callback(data);
} else {
throw new Error("Decompressed PNG data was invalid!");
}
};
// If in Node.js..
if (typeof window === "undefined") {
import("../util/decompressPng.js")
.then(({ default: decompressPng }) => {
decompressPng(message.data, pngCallback);
})
.catch(console.error);
const decoded = decompressPng(message.data);
if (isRosbridgeMessage(decoded)) {
callback(decoded);
} else {
// if in browser..
import("../util/shim/decompressPng.js")
.then(({ default: decompressPng }) => {
decompressPng(message.data, pngCallback);
})
.catch(console.error);
throw new Error("Received invalid message in PNG data!");
}
} else {
callback(message);
Expand Down
3 changes: 0 additions & 3 deletions src/types/pngparse.d.ts

This file was deleted.

34 changes: 20 additions & 14 deletions src/util/decompressPng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@
* @author Ramon Wijnands - rayman747@hotmail.com
*/

import pngparse from "pngparse";
import { decode } from "fast-png";
import { Buffer } from "buffer";

const textDecoder = new TextDecoder();

/**
* If a message was compressed as a PNG image (a compression hack since
* gzipping over WebSockets * is not supported yet), this function decodes
* the "image" as a Base64 string.
*
* @param data - An object containing the PNG data.
* @param callback - Function with the following params:
*/
export default function decompressPng(
data: string,
callback: (data: unknown) => void,
) {
export default function decompressPng(data: string): unknown {
const buffer = Buffer.from(data, "base64");

pngparse.parse(buffer, function (err, data) {
if (err || !(data instanceof Object) || !("data" in data)) {
throw new Error("Cannot process PNG encoded message ");
} else {
const jsonData = String(data.data);
callback(JSON.parse(jsonData));
}
});
const decoded = tryDecodeBuffer(buffer);

try {
return JSON.parse(textDecoder.decode(decoded.data));
} catch (error) {
throw new Error("Error parsing PNG JSON contents", { cause: error });
}
}

function tryDecodeBuffer(buffer: Buffer) {
try {
return decode(buffer);
} catch (error) {
throw new Error("Error decoding buffer", { cause: error });
}
}
61 changes: 0 additions & 61 deletions src/util/shim/decompressPng.ts

This file was deleted.

22 changes: 20 additions & 2 deletions test/examples/topic-listener.example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect } from "vitest";
import { describe, it, expect, vi } from "vitest";
import * as ROSLIB from "../../src/RosLib.js";

const ros = new ROSLIB.Ros({
Expand Down Expand Up @@ -45,4 +45,22 @@ describe("Topics Example", function () {

topic.on("unsubscribe", done);
}));
}, 1000);

// TODO: reenable when rosbridge is fixed in ROS 2
it.skip("Listening to a PNG-compressed topic", async () => {
const topic = ros.Topic<{ data: string }>({
name: "/png_test",
messageType: "std_msgs/String",
compression: "png",
});
const callback = vi.fn();
topic.subscribe(callback);

topic.publish({ data: "some message that will be PNG-compressed" });
await vi.waitFor(() => {
expect(callback).toHaveBeenCalledWith({
data: "some message that will be PNG-compressed",
});
});
});
});