diff --git a/.github/scripts/check_sdist.py b/.github/scripts/check_sdist.py index 248e62baae5..f2099cf0817 100755 --- a/.github/scripts/check_sdist.py +++ b/.github/scripts/check_sdist.py @@ -44,6 +44,13 @@ def main(sdist: str) -> None: generated = sorted(n for n in names if "node_modules" in n.split("/")) if generated: problems.append(f"{len(generated)} node_modules entries, e.g. {generated[0]}") + stale_builds = sorted( + n + for n in names + if n.startswith(("dimos/message_codegen/build/", "dimos/message_codegen/dist/")) + ) + if stale_builds: + problems.append(f"{len(stale_builds)} nested message build entries, e.g. {stale_builds[0]}") total_mb = sum(m.size for m in members) / 1e6 if total_mb > MAX_UNCOMPRESSED_MB: problems.append(f"{total_mb:.1f} MB uncompressed exceeds {MAX_UNCOMPRESSED_MB} MB") diff --git a/dimos/experimental/security_demo/security_module.py b/dimos/experimental/security_demo/security_module.py index 7853fd4cfa8..f40c40810da 100644 --- a/dimos/experimental/security_demo/security_module.py +++ b/dimos/experimental/security_demo/security_module.py @@ -48,6 +48,7 @@ from dimos_generated.geometry_msgs.msg import Vector3 + if TYPE_CHECKING: from dimos.perception.detection.type.detection2d.bbox import Detection2DBBox diff --git a/docs/development/message-alignment.md b/docs/development/message-alignment.md index 442582ddad1..5a309a750b3 100644 --- a/docs/development/message-alignment.md +++ b/docs/development/message-alignment.md @@ -1,12 +1,11 @@ # Verify prepared built-in inputs and artifacts -PR1 owns the generic source-package hooks, dependency ownership and pinned source -preparation. PR2 owns `packages/dimos-generated/custom`, `catalog.json`, +The published `dimos-cdrgen` package owns generic source-package hooks, dependency +ownership and source preparation. DimOS owns `packages/dimos-generated/custom`, `catalog.json`, `schema-sources.json`, package metadata and thin preparation/release entrypoints. There is no committed generated snapshot or bulk upstream catalog. ```sh skip -python scripts/prepare_message_parser.py python -m scripts.prepare_builtin_sources python -m scripts.generate_builtin_messages python -m scripts.generate_builtin_messages --check diff --git a/examples/language-interop/cpp/main.cpp b/examples/language-interop/cpp/main.cpp index 9b50937e852..56574c11d55 100644 --- a/examples/language-interop/cpp/main.cpp +++ b/examples/language-interop/cpp/main.cpp @@ -1,3 +1,6 @@ +#include +#include +#include // C++ robot control example // Subscribes to robot pose and publishes twist commands @@ -8,7 +11,7 @@ #include #include -#include + class RobotController { public: @@ -16,7 +19,7 @@ class RobotController { void onPose(const lcm::ReceiveBuffer* buffer, const std::string&) { try { - const auto decoded = dimos::cdr::decode( + const auto decoded = dimos::native::cdr_decode( static_cast(buffer->data), buffer->data_size); const auto* msg = &decoded; const auto& pos = msg->pose.position; @@ -47,7 +50,7 @@ class RobotController { twist.angular.y = 0; twist.angular.z = std::sin(t) * 0.3; - const auto bytes = dimos::cdr::encode(twist); + const auto bytes = dimos::native::cdr_encode(twist); lcm_.publish("/cmd_vel#geometry_msgs.Twist", bytes.data(), bytes.size()); printf("[twist] linear=%.2f angular=%.2f\n", twist.linear.x, twist.angular.z); t += 0.1; diff --git a/examples/language-interop/lua/.gitignore b/examples/language-interop/lua/.gitignore deleted file mode 100644 index f87dd2d1251..00000000000 --- a/examples/language-interop/lua/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -lcm/ -dimos-lcm/ -msgs/ diff --git a/examples/language-interop/lua/main.lua b/examples/language-interop/lua/main.lua deleted file mode 100644 index f6d2dccca17..00000000000 --- a/examples/language-interop/lua/main.lua +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env lua - --- Lua robot control example --- Subscribes to robot pose and publishes twist commands - --- Add local msgs folder to path -local script_dir = arg[0]:match("(.*/)") or "./" -package.path = script_dir .. "msgs/?.lua;" .. package.path -package.path = script_dir .. "msgs/?/init.lua;" .. package.path - -local lcm = require("lcm") -local PoseStamped = require("geometry_msgs.PoseStamped") -local Twist = require("geometry_msgs.Twist") -local Vector3 = require("geometry_msgs.Vector3") - -local lc = lcm.lcm.new() - -print("Robot control started") -print("Subscribing to /odom, publishing to /cmd_vel") -print("Press Ctrl+C to stop.\n") - --- Subscribe to pose -lc:subscribe("/odom#geometry_msgs.PoseStamped", function(channel, msg) - msg = PoseStamped.decode(msg) - local pos = msg.pose.position - local ori = msg.pose.orientation - print(string.format("[pose] x=%.2f y=%.2f z=%.2f | qw=%.2f", - pos.x, pos.y, pos.z, ori.w)) -end) - --- Publisher loop -local t = 0 -local socket = require("socket") -local last_pub = socket.gettime() - -while true do - -- Handle incoming messages - lc:handle() - - -- Publish at ~10 Hz - local now = socket.gettime() - if now - last_pub >= 0.1 then - local twist = Twist:new() - twist.linear = Vector3:new() - twist.linear.x = 0.5 - twist.linear.y = 0 - twist.linear.z = 0 - twist.angular = Vector3:new() - twist.angular.x = 0 - twist.angular.y = 0 - twist.angular.z = math.sin(t) * 0.3 - - lc:publish("/cmd_vel#geometry_msgs.Twist", twist:encode()) - print(string.format("[twist] linear=%.2f angular=%.2f", twist.linear.x, twist.angular.z)) - - t = t + 0.1 - last_pub = now - end -end diff --git a/examples/language-interop/lua/setup.sh b/examples/language-interop/lua/setup.sh deleted file mode 100755 index 682d676183e..00000000000 --- a/examples/language-interop/lua/setup.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -# Setup script for LCM Lua bindings -# Clones official LCM repo and builds Lua bindings -# -# Tested on: Arch Linux, Ubuntu, macOS (with Homebrew) - -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -LCM_DIR="$SCRIPT_DIR/lcm" - -echo "=== LCM Lua Setup ===" - -# Detect Lua version -if command -v lua &>/dev/null; then - LUA_VERSION=$(lua -v 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1) - echo "Detected Lua version: $LUA_VERSION" -else - echo "Error: lua not found in PATH" - exit 1 -fi - -# Detect Lua paths using pkg-config if available -if command -v pkg-config &>/dev/null && pkg-config --exists "lua$LUA_VERSION" 2>/dev/null; then - LUA_INCLUDE_DIR=$(pkg-config --variable=includedir "lua$LUA_VERSION") - LUA_LIBRARY=$(pkg-config --libs "lua$LUA_VERSION" | grep -oE '/[^ ]+\.so' | head -1 || echo "") -elif command -v pkg-config &>/dev/null && pkg-config --exists lua 2>/dev/null; then - LUA_INCLUDE_DIR=$(pkg-config --variable=includedir lua) - LUA_LIBRARY=$(pkg-config --libs lua | grep -oE '/[^ ]+\.so' | head -1 || echo "") -fi - -# Platform-specific defaults -if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS with Homebrew - LUA_INCLUDE_DIR="${LUA_INCLUDE_DIR:-$(brew --prefix lua 2>/dev/null)/include/lua}" - LUA_LIBRARY="${LUA_LIBRARY:-$(brew --prefix lua 2>/dev/null)/lib/liblua.dylib}" - LUA_CPATH_BASE="${LUA_CPATH_BASE:-/usr/local/lib/lua}" -else - # Linux defaults - LUA_INCLUDE_DIR="${LUA_INCLUDE_DIR:-/usr/include}" - LUA_LIBRARY="${LUA_LIBRARY:-/usr/lib/liblua.so}" - LUA_CPATH_BASE="${LUA_CPATH_BASE:-/usr/local/lib/lua}" -fi - -echo "Lua include: $LUA_INCLUDE_DIR" -echo "Lua library: $LUA_LIBRARY" - -# Clone LCM if not present -if [ ! -d "$LCM_DIR" ]; then - echo "Cloning LCM..." - git clone --depth 1 https://github.com/lcm-proj/lcm.git "$LCM_DIR" -else - echo "LCM already cloned" -fi - -# Build Lua bindings using cmake -echo "Building LCM Lua bindings..." -cd "$LCM_DIR" -mkdir -p build && cd build - -# Configure with Lua support -cmake .. \ - -DLCM_ENABLE_LUA=ON \ - -DLCM_ENABLE_PYTHON=OFF \ - -DLCM_ENABLE_JAVA=OFF \ - -DLCM_ENABLE_TESTS=OFF \ - -DLCM_ENABLE_EXAMPLES=OFF \ - -DLUA_INCLUDE_DIR="$LUA_INCLUDE_DIR" \ - -DLUA_LIBRARY="$LUA_LIBRARY" - -# Build just the lua target -make lcm-lua -j$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) - -# Install the lua module -LUA_CPATH_DIR="$LUA_CPATH_BASE/$LUA_VERSION" -echo "Installing lcm.so to $LUA_CPATH_DIR" -sudo mkdir -p "$LUA_CPATH_DIR" -sudo cp lcm-lua/lcm.so "$LUA_CPATH_DIR/" - -# Get dimos-lcm message definitions -DIMOS_LCM_DIR="$SCRIPT_DIR/dimos-lcm" -MSGS_DST="$SCRIPT_DIR/msgs" - -echo "Getting message definitions..." -if [ -d "$DIMOS_LCM_DIR" ]; then - echo "Updating dimos-lcm..." - cd "$DIMOS_LCM_DIR" && git pull -else - echo "Cloning dimos-lcm..." - git clone --depth 1 https://github.com/dimensionalOS/dimos-lcm.git "$DIMOS_LCM_DIR" -fi - -# Link/copy messages -rm -rf "$MSGS_DST" -cp -r "$DIMOS_LCM_DIR/generated/lua_lcm_msgs" "$MSGS_DST" -echo "Messages installed to $MSGS_DST" - -echo "" -echo "=== Setup complete ===" -echo "Run: lua main.lua" diff --git a/examples/language-interop/ts/deno.json b/examples/language-interop/ts/deno.json deleted file mode 100644 index b64363a64a7..00000000000 --- a/examples/language-interop/ts/deno.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "imports": { - "@dimos/lcm": "jsr:@dimos/lcm", - "@dimos/msgs": "jsr:@dimos/msgs" - }, - "tasks": { - "start": "deno run --allow-net --unstable-net main.ts" - } -} diff --git a/examples/language-interop/ts/deno.lock b/examples/language-interop/ts/deno.lock deleted file mode 100644 index 9529ebdb342..00000000000 --- a/examples/language-interop/ts/deno.lock +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "5", - "specifiers": { - "jsr:@dimos/lcm@*": "0.2.0", - "jsr:@dimos/msgs@*": "0.1.4" - }, - "jsr": { - "@dimos/lcm@0.2.0": { - "integrity": "03399f5e4800f28a0c294981e0210d784232fc65a57707de19052ad805bd5fea" - }, - "@dimos/msgs@0.1.4": { - "integrity": "564bc30b4bc41a562c296c257a15055283ca0cbd66d0627991ede5295832d0c4" - } - }, - "workspace": { - "dependencies": [ - "jsr:@dimos/lcm@*", - "jsr:@dimos/msgs@*" - ] - } -} diff --git a/examples/language-interop/ts/main.ts b/examples/language-interop/ts/main.ts deleted file mode 100644 index 9026304f2c6..00000000000 --- a/examples/language-interop/ts/main.ts +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env -S deno run --allow-net --unstable-net - -// TypeScript robot control example -// Subscribes to robot odometry and publishes twist commands - -import { LCM } from "@dimos/lcm"; -import { geometry_msgs } from "@dimos/msgs"; - -const lcm = new LCM(); -await lcm.start(); - -console.log("Robot control started"); -console.log("Subscribing to /odom, publishing to /cmd_vel"); -console.log("Press Ctrl+C to stop.\n"); - -// Subscribe to pose - prints robot position -lcm.subscribe("/odom", geometry_msgs.PoseStamped, (msg) => { - const pos = msg.data.pose.position; - const ori = msg.data.pose.orientation; - console.log( - `[pose] x=${pos.x.toFixed(2)} y=${pos.y.toFixed(2)} z=${pos.z.toFixed(2)} | qw=${ori.w.toFixed(2)}` - ); -}); - -// Publish twist commands at 10 Hz - simple forward motion -let t = 0; -const interval = setInterval(async () => { - if (!lcm.isRunning()) { - clearInterval(interval); - return; - } - - const twist = new geometry_msgs.Twist({ - linear: new geometry_msgs.Vector3({ x: 0.5, y: 0, z: 0 }), - angular: new geometry_msgs.Vector3({ x: 0, y: 0, z: Math.sin(t) * 0.3 }), - }); - - await lcm.publish("/cmd_vel", twist); - console.log(`[twist] linear=${twist.linear.x.toFixed(2)} angular=${twist.angular.z.toFixed(2)}`); - t += 0.1; -}, 100); - -await lcm.run(); diff --git a/examples/language-interop/ts/web/index.html b/examples/language-interop/ts/web/index.html deleted file mode 100644 index 1ce89604b6b..00000000000 --- a/examples/language-interop/ts/web/index.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - Robot Control - - - -

Robot Control

-
Connecting...
- -
-
-

Pose

-
X--
-
Y--
-
Z--
-
Qw--
-
- -
-

Controls

-
-
- -
- - - -
- -
-
-
- -
-

Log

-
-
- - - - diff --git a/examples/language-interop/ts/web/server.ts b/examples/language-interop/ts/web/server.ts deleted file mode 100644 index 5b8ec035bef..00000000000 --- a/examples/language-interop/ts/web/server.ts +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env -S deno run --allow-net --allow-read --unstable-net - -// LCM to WebSocket Bridge for Robot Control -// Forwards robot pose to browser, receives twist commands from browser - -import { LCM } from "jsr:@dimos/lcm"; -import { decodePacket, geometry_msgs } from "jsr:@dimos/msgs"; - -const PORT = 8080; -const clients = new Set(); - -Deno.serve({ port: PORT }, async (req) => { - const url = new URL(req.url); - - if (req.headers.get("upgrade") === "websocket") { - const { socket, response } = Deno.upgradeWebSocket(req); - socket.onopen = () => { console.log("Client connected"); clients.add(socket); }; - socket.onclose = () => { console.log("Client disconnected"); clients.delete(socket); }; - socket.onerror = () => clients.delete(socket); - - // Forward binary LCM packets from browser directly to UDP - socket.binaryType = "arraybuffer"; - socket.onmessage = async (event) => { - if (event.data instanceof ArrayBuffer) { - const packet = new Uint8Array(event.data); - try { - // we don't need to decode, just showing we can - const { channel, data } = decodePacket(packet); - console.log(`[ws->lcm] ${channel}`, data); - await lcm.publishPacket(packet); - } catch (e) { - console.error("Forward error:", e); - } - } - }; - - return response; - } - - if (url.pathname === "/" || url.pathname === "/index.html") { - const html = await Deno.readTextFile(new URL("./index.html", import.meta.url)); - return new Response(html, { headers: { "content-type": "text/html" } }); - } - - return new Response("Not found", { status: 404 }); -}); - -console.log(`Server: http://localhost:${PORT}`); - -const lcm = new LCM(); -await lcm.start(); - -// Subscribe to pose and just log to show how server can decode messages for itself -lcm.subscribe("/odom", geometry_msgs.PoseStamped, (msg) => { - const pos = msg.data.pose.position; - const ori = msg.data.pose.orientation; - console.log(`[pose] x=${pos.x.toFixed(2)} y=${pos.y.toFixed(2)} z=${pos.z.toFixed(2)}`); -}); - -// Forward all raw packets to browser (we are decoding LCM directly in the browser) -lcm.subscribePacket((packet) => { - for (const client of clients) { - if (client.readyState === WebSocket.OPEN) { - client.send(packet); - } - } -}); - -await lcm.run(); diff --git a/examples/message-codegen/user-story/demo_module.py b/examples/message-codegen/user-story/demo_module.py index b4eb4bf68cd..c51495724f1 100644 --- a/examples/message-codegen/user-story/demo_module.py +++ b/examples/message-codegen/user-story/demo_module.py @@ -15,6 +15,7 @@ """Use an application-local generated message in a Python module and file relay.""" import argparse +import os from pathlib import Path import subprocess @@ -80,7 +81,10 @@ def main() -> None: ) subprocess.run( [ - str(args.build / "rust/target/debug/story-consumer"), + str( + Path(os.environ.get("CARGO_TARGET_DIR", args.build / "rust/target")) + / "debug/story-consumer" + ), str(evidence / "cpp.cdr"), str(evidence / "rust.cdr"), ], diff --git a/examples/simplerobot/simplerobot.py b/examples/simplerobot/simplerobot.py index 74098974c2d..33563b451d5 100644 --- a/examples/simplerobot/simplerobot.py +++ b/examples/simplerobot/simplerobot.py @@ -25,7 +25,7 @@ import math import time -from dimos_generated.geometry_msgs.msg import Point, Pose, PoseStamped, Twist, Vector3 +from dimos_generated.geometry_msgs.msg import Point, Pose, PoseStamped, Quaternion, Twist, Vector3 from dimos_generated.std_msgs.msg import Header import reactivex as rx @@ -61,8 +61,10 @@ class SimpleRobot(Module): config: SimpleRobotConfig cmd_vel: In[Twist] pose: Out[PoseStamped] - _pose = Pose() - _vel = Twist() + _pose = Pose( + position=Point(x=0.0, y=0.0, z=0.0), orientation=Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) + ) + _vel = Twist(linear=Vector3(x=0.0, y=0.0, z=0.0), angular=Vector3(x=0.0, y=0.0, z=0.0)) _vel_time = 0.0 @rpc @@ -84,7 +86,11 @@ def _update(self) -> None: now = time.time() dt = 1.0 / self.config.update_rate - vel = self._vel if now - self._vel_time < self.config.cmd_timeout else Twist() + vel = ( + self._vel + if now - self._vel_time < self.config.cmd_timeout + else Twist(linear=Vector3(x=0.0, y=0.0, z=0.0), angular=Vector3(x=0.0, y=0.0, z=0.0)) + ) self._pose = apply_twist(self._pose, vel, dt) @@ -128,14 +134,20 @@ def _update(self) -> None: time.sleep(1) print("Forward...") for _ in range(8): - robot._on_twist(Twist(linear=Vector3(x=1.0))) + robot._on_twist( + Twist(linear=Vector3(x=1.0, y=0.0, z=0.0), angular=Vector3(x=0.0, y=0.0, z=0.0)) + ) time.sleep(0.25) print("Turn...") for _ in range(12): - robot._on_twist(Twist(linear=Vector3(x=0.5), angular=Vector3(z=0.5))) + robot._on_twist( + Twist(linear=Vector3(x=0.5, y=0.0, z=0.0), angular=Vector3(z=0.5, x=0.0, y=0.0)) + ) time.sleep(0.25) print("Stop") - robot._on_twist(Twist()) + robot._on_twist( + Twist(linear=Vector3(x=0.0, y=0.0, z=0.0), angular=Vector3(x=0.0, y=0.0, z=0.0)) + ) time.sleep(1) else: while True: diff --git a/scripts/test_external_native_messages.sh b/scripts/test_external_native_messages.sh index e033384ab7d..d66ea16137d 100644 --- a/scripts/test_external_native_messages.sh +++ b/scripts/test_external_native_messages.sh @@ -1,30 +1,13 @@ #!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")/.." -# Run test_message_packages.sh first: it adds application_note locally and installs packages. -application="$PWD/build/message-codegen/external-app" -native_demo="$PWD/build/message-codegen/external-native" -mkdir -p "$native_demo/rust/src" "$native_demo/evidence" -cp examples/message-codegen/external-native/relay.rs "$native_demo/rust/src/main.rs" -cat > "$native_demo/rust/Cargo.toml" <