Skip to content

Commit

Permalink
Change time/duration deserialization to sec,nanosec by default (#32)
Browse files Browse the repository at this point in the history
### Changelog
- **[BREAKING]** `MessageReader` now deserializes Time and Duration
values as `{sec,nanosec}` rather than `{sec,nsec}`. The `{sec,nsec}`
behavior is still available via a new `timeType` constructor option.

### Description

Based on / supersedes / resolves #24

Bumps package version to 3.0.0 for breaking change.

```ts
export type MessageReaderOptions = {
   /**
    * Select the type for deserialized `time` and `duration` values. "sec" and "nanosec" are used
    * by default in ROS 2, whereas "sec" and "nsec" originates from ROS 1 and matches
    * `@foxglove/rostime`.
    *
    * @default "sec,nanosec"
    */
   timeType?: "sec,nanosec" | "sec,nsec";
 };

const reader = new MessageReader(messageDefinition, { timeFormat: "sec,nsec" });
```

---------

Co-authored-by: MatthewDHarrison <madoha5812@gmail.com>
  • Loading branch information
jtbandes and MatthewDHarrison authored Dec 4, 2024
1 parent e34de90 commit 00cfc61
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 148 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { MessageReader } from "@foxglove/rosmsg2-serialization";
// message definition comes from `parse()` in @foxglove/rosmsg
const reader = new MessageReader(messageDefinition);

// specify a different `timeType` for time objects compatible with ROS 1 and @foxglove/rostime
const reader = new MessageReader(messageDefinition, { timeType: "sec,nsec" });

// deserialize a buffer into an object
const message = reader.readMessage([0x00, 0x01, ...]);

Expand Down
3 changes: 3 additions & 0 deletions eslint.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ module.exports = tseslint.config(
project: "./tsconfig.json",
},
},
rules: {
"@typescript-eslint/explicit-member-accessibility": "error",
},
},
...foxglove.configs.base,
...foxglove.configs.jest,
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@foxglove/rosmsg2-serialization",
"version": "2.0.4",
"version": "3.0.0",
"description": "ROS 2 (Robot Operating System) message serialization, for reading and writing bags and network messages",
"license": "MIT",
"keywords": [
Expand Down Expand Up @@ -39,11 +39,12 @@
"test": "jest"
},
"engines": {
"node": ">= 14"
"node": ">= 20"
},
"devDependencies": {
"@foxglove/eslint-plugin": "2.0.0",
"@foxglove/rosmsg": "4.2.1",
"@foxglove/ros2idl-parser": "0.3.5",
"@foxglove/rosmsg": "5.0.4",
"@sounisi5011/jest-binary-data-matchers": "1.2.1",
"@types/jest": "^29.4.0",
"@types/prettier": "^3.0.0",
Expand All @@ -55,8 +56,8 @@
"typescript-eslint": "8.17.0"
},
"dependencies": {
"@foxglove/cdr": "^3.0.0",
"@foxglove/message-definition": "^0.2.0",
"@foxglove/cdr": "^3.3.0",
"@foxglove/message-definition": "^0.4.0",
"@foxglove/rostime": "^1.1.2"
},
"packageManager": "yarn@4.5.3"
Expand Down
148 changes: 88 additions & 60 deletions src/MessageReader.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { parse as parseMessageDefinition, parseRos2idl } from "@foxglove/rosmsg";
import { parseRos2idl } from "@foxglove/ros2idl-parser";
import { parse as parseMessageDefinition } from "@foxglove/rosmsg";

import { MessageReader } from "./MessageReader";

Expand Down Expand Up @@ -51,26 +52,6 @@ describe("MessageReader", () => {
// eslint-disable-next-line no-loss-of-precision
{ sample: 0.123456789121212121212 },
],
[
`time stamp`,
[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
{
stamp: {
sec: 0,
nsec: 1,
},
},
],
[
`duration stamp`,
[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
{
stamp: {
sec: 0,
nsec: 1,
},
},
],
[
`int32[] arr`,
[
Expand All @@ -79,26 +60,6 @@ describe("MessageReader", () => {
],
{ arr: Int32Array.from([3, 7]) },
],
[
`time[1] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00],
{ arr: [{ sec: 1, nsec: 2 }] },
],
[
`duration[1] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00],
{ arr: [{ sec: 1, nsec: 2 }] },
],
[
`time[] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
{ arr: [{ sec: 2, nsec: 3 }] },
],
[
`duration[] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
{ arr: [{ sec: 2, nsec: 3 }] },
],
// unaligned access
[
`uint8 blank\nint32[] arr`,
Expand All @@ -110,15 +71,6 @@ describe("MessageReader", () => {
],
{ blank: 0, arr: Int32Array.from([3, 7]) },
],
[
`uint8 blank\ntime[] arr`,
[
0x00,
...[0x00, 0x00, 0x00], // alignment
...[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
],
{ blank: 0, arr: [{ sec: 2, nsec: 3 }] },
],
[`float32[2] arr`, float32Buffer([5.5, 6.5]), { arr: Float32Array.from([5.5, 6.5]) }],
[
`uint8 blank\nfloat32[2] arr`,
Expand Down Expand Up @@ -275,6 +227,82 @@ describe("MessageReader", () => {
},
);

it.each([
[
`time stamp`,
[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
{ stamp: { sec: 0, nanosec: 1 } },
{ stamp: { sec: 0, nsec: 1 } },
],
[
`duration stamp`,
[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
{ stamp: { sec: 0, nanosec: 1 } },
{ stamp: { sec: 0, nsec: 1 } },
],
[
`time[1] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00],
{ arr: [{ sec: 1, nanosec: 2 }] },
{ arr: [{ sec: 1, nsec: 2 }] },
],
[
`duration[1] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00],
{ arr: [{ sec: 1, nanosec: 2 }] },
{ arr: [{ sec: 1, nsec: 2 }] },
],
[
`time[] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
{ arr: [{ sec: 2, nanosec: 3 }] },
{ arr: [{ sec: 2, nsec: 3 }] },
],
[
`duration[] arr`,
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
{ arr: [{ sec: 2, nanosec: 3 }] },
{ arr: [{ sec: 2, nsec: 3 }] },
],
// unaligned access
[
`uint8 blank\ntime[] arr`,
[
0x00,
...[0x00, 0x00, 0x00], // alignment
...[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00],
],
{ blank: 0, arr: [{ sec: 2, nanosec: 3 }] },
{ blank: 0, arr: [{ sec: 2, nsec: 3 }] },
],
])(
"should deserialize %s correctly based on specified time type",
(
msgDef: string,
arr: Iterable<number>,
expected: Record<string, unknown>,
ros1Expected: Record<string, unknown>,
) => {
const buffer = Uint8Array.from([0, 1, 0, 0, ...arr]);

expect(
new MessageReader(parseMessageDefinition(msgDef, { ros2: true })).readMessage(buffer),
).toEqual(expected);

expect(
new MessageReader(parseMessageDefinition(msgDef, { ros2: true }), {
timeType: "sec,nanosec",
}).readMessage(buffer),
).toEqual(expected);

expect(
new MessageReader(parseMessageDefinition(msgDef, { ros2: true }), {
timeType: "sec,nsec",
}).readMessage(buffer),
).toEqual(ros1Expected);
},
);

it("should deserialize a ROS 2 log message", () => {
const buffer = Uint8Array.from(
Buffer.from(
Expand Down Expand Up @@ -303,7 +331,7 @@ describe("MessageReader", () => {
const read = reader.readMessage(buffer);

expect(read).toEqual({
stamp: { sec: 1585866235, nsec: 112130688 },
stamp: { sec: 1585866235, nanosec: 112130688 },
level: 20,
name: "minimal_publisher",
msg: "Publishing: 'Hello, world! 0'",
Expand All @@ -321,27 +349,27 @@ describe("MessageReader", () => {
),
);
const msgDef = `
geometry_msgs/msg/TransformStamped[] transforms
geometry_msgs/TransformStamped[] transforms
================================================================================
MSG: geometry_msgs/msg/TransformStamped
MSG: geometry_msgs/TransformStamped
Header header
string child_frame_id # the frame id of the child frame
Transform transform
================================================================================
MSG: std_msgs/msg/Header
MSG: std_msgs/Header
time stamp
string frame_id
================================================================================
MSG: geometry_msgs/msg/Transform
MSG: geometry_msgs/Transform
Vector3 translation
Quaternion rotation
================================================================================
MSG: geometry_msgs/msg/Vector3
MSG: geometry_msgs/Vector3
float64 x
float64 y
float64 z
================================================================================
MSG: geometry_msgs/msg/Quaternion
MSG: geometry_msgs/Quaternion
float64 x
float64 y
float64 z
Expand All @@ -354,7 +382,7 @@ describe("MessageReader", () => {
transforms: [
{
header: {
stamp: { sec: 1638821672, nsec: 836230505 },
stamp: { sec: 1638821672, nanosec: 836230505 },
frame_id: "turtle1",
},
child_frame_id: "turtle1_ahead",
Expand Down Expand Up @@ -454,7 +482,7 @@ IDL: builtin_interfaces/Time
module builtin_interfaces {
struct Time {
int32 sec;
uint32 nanosec;
uint32 nsec;
};
};
`;
Expand All @@ -465,7 +493,7 @@ module builtin_interfaces {
transforms: [
{
header: {
stamp: { sec: 1638821672, nanosec: 836230505 },
stamp: { sec: 1638821672, nsec: 836230505 },
frame_id: "turtle1",
},
child_frame_id: "turtle1_ahead",
Expand Down
Loading

0 comments on commit 00cfc61

Please sign in to comment.