-
Notifications
You must be signed in to change notification settings - Fork 160
feat(cli): type-free topic echo via /topic#pkg.Msg inference, this mi… #988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,12 +12,16 @@ | |
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import importlib | ||
| import re | ||
| import time | ||
|
|
||
| import typer | ||
|
|
||
| from dimos.core.transport import LCMTransport, pLCMTransport | ||
| from dimos.protocol.pubsub.lcmpubsub import LCMPubSubBase | ||
|
|
||
| _modules_to_try = [ | ||
| "dimos.msgs.geometry_msgs", | ||
|
|
@@ -42,24 +46,53 @@ def _resolve_type(type_name: str) -> type: | |
| raise ValueError(f"Could not find type '{type_name}' in any known message modules") | ||
|
|
||
|
|
||
| def topic_echo(topic: str, type_name: str) -> None: | ||
| msg_type = _resolve_type(type_name) | ||
| use_pickled = getattr(msg_type, "lcm_encode", None) is None | ||
| transport: pLCMTransport[object] | LCMTransport[object] = ( | ||
| pLCMTransport(topic) if use_pickled else LCMTransport(topic, msg_type) | ||
| ) | ||
|
|
||
| def _on_message(msg: object) -> None: | ||
| print(msg) | ||
| def topic_echo(topic: str, type_name: str | None) -> None: | ||
| # Explicit mode (legacy): unchanged. | ||
| if type_name is not None: | ||
| msg_type = _resolve_type(type_name) | ||
| use_pickled = getattr(msg_type, "lcm_encode", None) is None | ||
| transport: pLCMTransport[object] | LCMTransport[object] = ( | ||
| pLCMTransport(topic) if use_pickled else LCMTransport(topic, msg_type) | ||
| ) | ||
|
|
||
| transport.subscribe(_on_message) | ||
| def _on_message(msg: object) -> None: | ||
| print(msg) | ||
|
|
||
| typer.echo(f"Listening on {topic} for {type_name} messages... (Ctrl+C to stop)") | ||
| transport.subscribe(_on_message) | ||
| typer.echo(f"Listening on {topic} for {type_name} messages... (Ctrl+C to stop)") | ||
| try: | ||
| while True: | ||
| time.sleep(0.1) | ||
| except KeyboardInterrupt: | ||
| typer.echo("\nStopped.") | ||
| return | ||
|
|
||
| # Inferred typed mode: listen on /topic#pkg.Msg and decode from the msg_name suffix. | ||
| bus = LCMPubSubBase(autoconf=True) | ||
| bus.start() # starts threaded handle loop | ||
|
|
||
| typed_pattern = rf"^{re.escape(topic)}#.*" | ||
|
|
||
| def on_msg(channel: str, data: bytes) -> None: | ||
| _, msg_name = channel.split("#", 1) # e.g. "nav_msgs.Odometry" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] If channel doesn't contain |
||
| pkg, cls_name = msg_name.split(".", 1) # "nav_msgs", "Odometry" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] If msg_name doesn't contain |
||
| module = importlib.import_module(f"dimos.msgs.{pkg}") | ||
| cls = getattr(module, cls_name) | ||
|
Comment on lines
+79
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] No error handling for import failures or missing attributes. If the package doesn't exist in |
||
| print(cls.lcm_decode(data)) | ||
|
Comment on lines
+77
to
+81
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P0] Missing error handling for channel parsing and message decoding. If a channel doesn't contain Triggering conditions: malformed channel names, missing message packages, or corrupted message data. |
||
|
|
||
| assert bus.l is not None | ||
| bus.l.subscribe(typed_pattern, on_msg) | ||
|
|
||
| typer.echo( | ||
| f"Listening on {topic} (inferring from typed LCM channels like '{topic}#pkg.Msg')... " | ||
| "(Ctrl+C to stop)" | ||
| ) | ||
|
|
||
| try: | ||
| while True: | ||
| time.sleep(0.1) | ||
| except KeyboardInterrupt: | ||
| bus.stop() | ||
| typer.echo("\nStopped.") | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P3] The regex pattern
^{topic}#.*will match channels that start with the topic but could match unintended channels if the topic is a prefix of another topic. For example, if topic is/odom, it will also match/odom_extended#pkg.Msg. Consider using^{topic}#[^#]+$to be more precise.