Skip to content

Raw (isRaw) subscriptions never receive MessageInfo, although the types and docs say they do #1617

Description

@Matt95

Summary

Since #1440, a subscription callback that declares two parameters receives a MessageInfo as its second argument. This works for deserialising subscriptions but silently does nothing for { isRaw: true } subscriptions: the second argument is always undefined.

The typings promise it:

// types/subscription.d.ts
type SubscriptionWithRawMessageCallback =
  (message: Buffer, messageInfo?: MessageInfo) => void;

and Subscription.processResponse in lib/subscription.js is ready to forward it for the raw case (if (this._isRaw) { if (this._wantsMessageInfo && messageInfo) ... }), but the raw take path never produces one:

  • lib/node.js takes raw samples with rclnodejs.rclTakeRaw(subscription.handle) and calls subscription.processResponse(rawMessage) with no info, regardless of subscription.wantsMessageInfo.
  • src/rcl_subscription_bindings.cpp RclTakeRaw calls rcl_take_serialized_message(subscription, &msg, nullptr, nullptr), so the message info is never requested from rcl in the first place.

Only the deserialising path (RclTakeWithInfo, which calls rcl_take(..., &message_info, nullptr)) builds the info object.

Reproduce

const rclnodejs = require('rclnodejs');
(async () => {
  await rclnodejs.init();
  const node = new rclnodejs.Node('raw_info_repro');
  const pub = node.createPublisher('std_msgs/msg/String', 'chatter');
  let seen = 0;
  node.createSubscription('std_msgs/msg/String', 'chatter', { isRaw: true },
    (buffer, info) => {
      console.log('raw buffer:', buffer.length, 'bytes; info:', info); // info is always undefined
      if (++seen >= 3) process.exit(0);
    });
  node.spin();
  setInterval(() => pub.publish({ data: 'hi' }), 100);
})();

Observed on rclnodejs 2.1.1 (ROS 2 Jazzy, rmw_fastrtps_cpp); the code on develop is unchanged, so 2.2.0 behaves the same.

Why it matters

Raw subscriptions are the natural choice for bridges that forward CDR bytes without deserialising (web/WebRTC relays, recorders). Those bridges need publisherGid most: when several publishers share one topic, which is normal for joint_states and /tf, a latest-sample-per-topic throttle overwrites the slow publisher with the fast one. We hit exactly this: a PTZ adapter publishing two joints at 17 Hz next to a joint_state_broadcaster at 170 Hz on one joint_states topic reached the browser only ~9% of the time. Keying by publisherGid is the clean fix, and the typings suggested it was available, but on the raw path it never is. Without it, a bridge has to partially decode every message to tell publishers apart.

Proposed fix

Keep rclTakeRaw as is for callbacks that do not want info, and add a sibling rclTakeRawWithInfo that calls rcl_take_serialized_message(subscription, &msg, &message_info, nullptr) and returns { buffer, info }, sharing the info-object construction with RclTakeWithInfo. In lib/node.js, the raw branch uses it when subscription.wantsMessageInfo and wraps the result in MessageInfo, mirroring the deserialising branch. Plus a test alongside test/test-message-info.js for a raw subscription with a two-parameter callback.

I have this change ready and would be happy to open a PR if you agree with the approach.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions