From 0a7a8cb0f69fbc5c4428f688d0e60b8ed1083c39 Mon Sep 17 00:00:00 2001 From: Minggang Wang Date: Thu, 18 Jun 2026 15:11:50 +0800 Subject: [PATCH 1/4] [Demo][Electron] Load prebuilt binaries at runtime --- demo/electron/car/README.md | 24 ++++++----- demo/electron/car/package.json | 6 ++- demo/electron/manipulator/README.md | 22 +++++++--- demo/electron/manipulator/package.json | 6 ++- demo/electron/topics/README.md | 11 +++-- demo/electron/topics/package.json | 6 ++- demo/electron/turtle_tf2/README.md | 20 ++++----- demo/electron/turtle_tf2/main.js | 6 +++ demo/electron/turtle_tf2/package.json | 6 ++- demo/electron/turtle_tf2/renderer.js | 22 ++++++++-- lib/native_loader.js | 58 +++++++++++++++++++++----- 11 files changed, 139 insertions(+), 48 deletions(-) diff --git a/demo/electron/car/README.md b/demo/electron/car/README.md index f2b1061f..8a9c1f5d 100644 --- a/demo/electron/car/README.md +++ b/demo/electron/car/README.md @@ -66,11 +66,14 @@ npm run build npm install ``` -3. **Rebuild native modules for Electron:** +3. **Native modules โ€” no manual rebuild needed:** - ```bash - npm run rebuild - ``` + rclnodejs ships prebuilt binaries for Electron and selects the matching one at + runtime from `ROS_DISTRO` + Linux codename + architecture, so just make sure + ROS 2 is sourced (next step) before launching. Do not run `electron-rebuild` + against rclnodejs โ€” it recompiles from source and bypasses the prebuilt binary. + The Forge `rebuildConfig` in `package.json` already excludes `rclnodejs` from + the automatic rebuild step. 4. **Start the demo:** ```bash @@ -291,13 +294,12 @@ Extend the joystick commands by modifying the switch statement in `main.js` and - Ensure ROS2 is properly sourced before running npm start - Check that rclnodejs is built correctly -2. **Native Module Rebuild Issues** - - ```bash - npm run rebuild - # or manually: - ./node_modules/.bin/electron-rebuild - ``` +2. **Native module fails to load** + - Ensure ROS 2 is sourced so rclnodejs can match its prebuilt binary + (`ROS_DISTRO` must be set; prebuilt binaries are provided for Ubuntu). + - As a last resort, force a from-source rebuild by setting + `RCLNODEJS_FORCE_BUILD=1` before `npm start` (needs a compiler toolchain and + network access). 3. **Topic Not Appearing** - Verify ROS2 daemon is running: `ros2 daemon status` diff --git a/demo/electron/car/package.json b/demo/electron/car/package.json index 72b96d72..fd962c2d 100644 --- a/demo/electron/car/package.json +++ b/demo/electron/car/package.json @@ -5,7 +5,6 @@ "main": "main.js", "scripts": { "start": "electron-forge start", - "rebuild": "electron-rebuild", "package": "electron-forge package", "make": "electron-forge make" }, @@ -39,6 +38,11 @@ "unpack": "**/node_modules/rclnodejs/**" } }, + "rebuildConfig": { + "ignoreModules": [ + "rclnodejs" + ] + }, "makers": [ { "name": "@electron-forge/maker-squirrel", diff --git a/demo/electron/manipulator/README.md b/demo/electron/manipulator/README.md index efa7f88f..14f06bad 100644 --- a/demo/electron/manipulator/README.md +++ b/demo/electron/manipulator/README.md @@ -34,17 +34,27 @@ An interactive Electron application demonstrating a two-joint robotic manipulato npm install ``` -3. **Rebuild native modules for Electron**: +3. **Source your ROS 2 environment** (required so the matching prebuilt binary is selected): + ```bash - npm run rebuild + source /opt/ros/$ROS_DISTRO/setup.bash # or your ROS 2 installation path ``` + rclnodejs ships prebuilt native binaries for Electron, so no compilation is needed. + The binary is selected at runtime from `ROS_DISTRO`, the Linux distro codename, and + the CPU architecture, so `ROS_DISTRO` must be set before launching the app. If no + matching prebuilt binary is available for your platform, rclnodejs falls back to + building from source. + + > Note: do not run `electron-rebuild` against rclnodejs โ€” it recompiles the addon + > from source and bypasses the prebuilt binary. The Electron Forge `rebuildConfig` + > in `package.json` already excludes `rclnodejs` from the automatic rebuild step. + ## ๐Ÿ“œ Available Scripts - **`npm start`** - Launch the application in development mode - **`npm run package`** - Package the application into a standalone executable folder - **`npm run make`** - Create platform-specific installers (requires system tools like `zip`, `dpkg`) -- **`npm run rebuild`** - Rebuild native modules after dependency changes ## ๐Ÿš€ Quick Start @@ -54,9 +64,9 @@ An interactive Electron application demonstrating a two-joint robotic manipulato npm start ``` -- โœ… **No ROS2 environment required** -- โœ… **Works without external setup** +- โœ… **No ROS2 topic publishing required** - โœ… **Pure visualization and manual control** +- โš ๏ธ ROS 2 must still be sourced so the prebuilt native binary is selected (otherwise rclnodejs builds from source) - โš ๏ธ No ROS2 topic publishing (local mode only) ### Option 2: Manual ROS2 Setup (Recommended for ROS2 Integration) @@ -308,7 +318,7 @@ manipulator/ 2. **Build errors with Electron** - This demo currently uses Electron 40.1.0 -- If you change Electron or other native-module dependencies, rerun `npm run rebuild` +- rclnodejs loads its prebuilt Electron binary at runtime; if it fails to load, ensure ROS 2 is sourced so the matching prebuild is selected. To force a from-source rebuild of rclnodejs, set `RCLNODEJS_FORCE_BUILD=1` - The versions recorded in `package.json` and `package-lock.json` are the tested baseline for this demo 3. **No ROS2 messages received** diff --git a/demo/electron/manipulator/package.json b/demo/electron/manipulator/package.json index ef081f28..421d0612 100644 --- a/demo/electron/manipulator/package.json +++ b/demo/electron/manipulator/package.json @@ -5,7 +5,6 @@ "main": "main.js", "scripts": { "start": "electron-forge start", - "rebuild": "electron-rebuild", "package": "electron-forge package", "make": "electron-forge make" }, @@ -43,6 +42,11 @@ "unpack": "**/node_modules/rclnodejs/**" } }, + "rebuildConfig": { + "ignoreModules": [ + "rclnodejs" + ] + }, "makers": [ { "name": "@electron-forge/maker-squirrel", diff --git a/demo/electron/topics/README.md b/demo/electron/topics/README.md index e09e762a..8c9e8117 100644 --- a/demo/electron/topics/README.md +++ b/demo/electron/topics/README.md @@ -45,10 +45,13 @@ A minimal Electron application demonstrating basic ROS2 topic communication usin npm install ``` -4. **Rebuild rclnodejs for Electron**: - ```bash - ./node_modules/.bin/electron-rebuild - ``` +4. **Native modules โ€” no manual rebuild needed**: + + rclnodejs ships prebuilt binaries for Electron and picks the matching one at + runtime from `ROS_DISTRO` + Linux codename + architecture (ROS 2 was sourced in + step 2). Do not run `electron-rebuild` against rclnodejs โ€” it recompiles from + source and bypasses the prebuilt binary; the Forge `rebuildConfig` already + excludes `rclnodejs`. ## ๐Ÿš€ Running the Demo diff --git a/demo/electron/topics/package.json b/demo/electron/topics/package.json index 6851887f..c43465af 100644 --- a/demo/electron/topics/package.json +++ b/demo/electron/topics/package.json @@ -5,7 +5,6 @@ "main": "main.js", "scripts": { "start": "electron-forge start", - "rebuild": "electron-rebuild", "package": "electron-forge package", "make": "electron-forge make" }, @@ -36,6 +35,11 @@ "unpack": "**/node_modules/rclnodejs/**" } }, + "rebuildConfig": { + "ignoreModules": [ + "rclnodejs" + ] + }, "makers": [ { "name": "@electron-forge/maker-squirrel", diff --git a/demo/electron/turtle_tf2/README.md b/demo/electron/turtle_tf2/README.md index be11f2dc..c8f6bf64 100644 --- a/demo/electron/turtle_tf2/README.md +++ b/demo/electron/turtle_tf2/README.md @@ -86,13 +86,13 @@ The demo uses the following key dependencies: npm install ``` -3. **Rebuild native modules**: +3. **Native modules โ€” no manual rebuild needed**: - ```bash - npm run rebuild - ``` - - This step is crucial for ensuring that rclnodejs and other native dependencies are properly compiled for your system. + rclnodejs ships prebuilt binaries for Electron and selects the matching one at + runtime from `ROS_DISTRO` + Linux codename + architecture, so make sure ROS 2 is + sourced (next step) before launching. Do not run `electron-rebuild` against + rclnodejs โ€” it recompiles from source and bypasses the prebuilt binary; the Forge + `rebuildConfig` in `package.json` already excludes `rclnodejs`. 4. **Source ROS2 environment**: @@ -111,7 +111,7 @@ The demo uses the following key dependencies: ### Method 1: Complete Demo -Start the full demo with all components (ensure you have run `npm install && npm run rebuild` first): +Start the full demo with all components (ensure you have run `npm install` first): ```bash # Source ROS2 first @@ -345,14 +345,14 @@ You can observe the following behavior by: - Try restarting the Electron application 6. **"electron: not found" or native module errors** - - Make sure you ran `npm run rebuild` after `npm install` + - Make sure ROS 2 is sourced so rclnodejs can match its prebuilt binary (`ROS_DISTRO` must be set; prebuilt binaries are provided for Ubuntu) - Ensure Node.js version is compatible (20.20.2 or higher) - - Try deleting `node_modules` and running `npm install && npm run rebuild` again + - Try deleting `node_modules` and running `npm install` again; to force a from-source rebuild of rclnodejs set `RCLNODEJS_FORCE_BUILD=1` 7. **"THREE is not defined" or script loading errors** - Ensure Three.js is properly installed: `npm install three@0.155.0` - Check that `node_modules/three/build/three.min.js` exists - - If issues persist, try reinstalling: `rm -rf node_modules && npm install && npm run rebuild` + - If issues persist, try reinstalling: `rm -rf node_modules && npm install` 8. **WSL (Windows Subsystem for Linux) specific issues** - Install audio libraries: `sudo apt install libasound2t64 libasound2-dev` diff --git a/demo/electron/turtle_tf2/main.js b/demo/electron/turtle_tf2/main.js index 181d5a56..943d2274 100644 --- a/demo/electron/turtle_tf2/main.js +++ b/demo/electron/turtle_tf2/main.js @@ -13,6 +13,12 @@ const { app, BrowserWindow, ipcMain } = require('electron'); const rclnodejs = require('rclnodejs'); +// Allow WebGL to work on systems without a usable GPU (e.g. WSL2, headless, +// or VMs) where Chromium blocklists hardware WebGL. Without this, the 3D +// renderer fails to create a WebGL context and the visualization cannot start. +app.commandLine.appendSwitch('ignore-gpu-blocklist'); +app.commandLine.appendSwitch('enable-unsafe-swiftshader'); + let mainWindow; let turtleTf2Nodes = {}; diff --git a/demo/electron/turtle_tf2/package.json b/demo/electron/turtle_tf2/package.json index d6385bbd..80941337 100644 --- a/demo/electron/turtle_tf2/package.json +++ b/demo/electron/turtle_tf2/package.json @@ -5,7 +5,6 @@ "main": "main.js", "scripts": { "start": "electron-forge start", - "rebuild": "electron-rebuild", "package": "electron-forge package", "make": "electron-forge make" }, @@ -42,6 +41,11 @@ "unpack": "**/node_modules/rclnodejs/**" } }, + "rebuildConfig": { + "ignoreModules": [ + "rclnodejs" + ] + }, "makers": [ { "name": "@electron-forge/maker-squirrel", diff --git a/demo/electron/turtle_tf2/renderer.js b/demo/electron/turtle_tf2/renderer.js index 0b9c440d..aea6465c 100644 --- a/demo/electron/turtle_tf2/renderer.js +++ b/demo/electron/turtle_tf2/renderer.js @@ -55,11 +55,27 @@ document.addEventListener('DOMContentLoaded', function () { versionDiv.innerText = 'Electron: ' + process.versions.electron; document.body.appendChild(versionDiv); - initializeScene(); - setupEventListeners(); - setupKeyboardControls(); + // Register ROS2 status listeners first so the loading screen always reflects + // initialization progress, even if 3D scene setup fails below. setupROSListeners(); + try { + initializeScene(); + setupEventListeners(); + setupKeyboardControls(); + } catch (err) { + console.error('Failed to initialize 3D scene:', err); + const loadingScreen = document.getElementById('loading-screen'); + if (loadingScreen) { + const loadingText = loadingScreen.querySelector('div:last-child'); + if (loadingText) { + loadingText.textContent = + 'Failed to initialize 3D view (WebGL unavailable): ' + err.message; + loadingText.style.color = '#ff4444'; + } + } + } + updateStatus(); // Don't automatically hide loading screen - wait for ROS2 initialization diff --git a/lib/native_loader.js b/lib/native_loader.js index 0e00a2d5..e7d8a66b 100644 --- a/lib/native_loader.js +++ b/lib/native_loader.js @@ -34,6 +34,51 @@ const debug = createDebug('rclnodejs'); let nativeModule = null; +// Copy a matched prebuilt binary into build/Release so that standard binary +// resolution (and packaged Electron apps where ROS_DISTRO may be unset at +// runtime) can still locate it. Non-destructive: never overwrites an existing +// build output, so local source builds are preserved. +function mirrorPrebuiltToBuildRelease(srcPath) { + try { + const destDir = path.join(__dirname, '..', 'build', 'Release'); + const destPath = path.join(destDir, 'rclnodejs.node'); + if (fs.existsSync(destPath)) { + return; + } + fs.mkdirSync(destDir, { recursive: true }); + fs.copyFileSync(srcPath, destPath); + debug(`Mirrored prebuilt binary to ${destPath}`); + } catch (e) { + debug('Could not mirror prebuilt binary to build/Release:', e.message); + } +} + +// Build the native addon from source. When running under Electron, target +// Electron's headers/ABI so the resulting binary matches the runtime instead +// of producing a Node-targeted binary (the addon links libuv directly, so the +// host runtime matters). +function buildFromSource() { + const env = { ...process.env }; + + if (detectPrebuildRuntime() === 'electron' && process.versions.electron) { + // node-gyp targets Electron via --target (Electron version) + --dist-url + // (Electron headers); it reads these from npm_config_* env vars. + env.npm_config_target = process.versions.electron; + env.npm_config_dist_url = 'https://electronjs.org/headers'; + env.npm_config_arch = process.arch; + debug(`Building from source for Electron ${process.versions.electron}`); + } else { + debug('Building from source for Node.js'); + } + + childProcess.execSync('npm run rebuild', { + stdio: 'inherit', + cwd: path.join(__dirname, '..'), + timeout: 300000, // 5 minute timeout + env, + }); +} + // Simplified loader: only use prebuilt binaries with exact Ubuntu/ROS2/arch match // Note: Prebuilt binaries are only supported on Linux (Ubuntu) platform function customFallbackLoader() { @@ -79,6 +124,7 @@ function customFallbackLoader() { if (fs.existsSync(candidatePath)) { debug(`Found ${runtime} prebuilt binary: ${candidate}`); + mirrorPrebuiltToBuildRelease(candidatePath); return require(candidatePath); } @@ -106,11 +152,7 @@ function loadNativeAddon() { // Trigger actual compilation try { debug('Running forced node-gyp rebuild...'); - childProcess.execSync('npm run rebuild', { - stdio: 'inherit', - cwd: path.join(__dirname, '..'), - timeout: 300000, // 5 minute timeout - }); + buildFromSource(); // Load the newly built binary nativeModule = bindings('rclnodejs'); @@ -172,11 +214,7 @@ function loadNativeAddon() { // Trigger actual compilation try { debug('Running node-gyp rebuild...'); - childProcess.execSync('npm run rebuild', { - stdio: 'inherit', - cwd: path.join(__dirname, '..'), - timeout: 300000, // 5 minute timeout - }); + buildFromSource(); // Try to load the newly built binary nativeModule = bindings('rclnodejs'); From fb9c4eb42ec7a6911d09156e618bdd2fb3108ef9 Mon Sep 17 00:00:00 2001 From: Minggang Wang Date: Thu, 18 Jun 2026 15:21:43 +0800 Subject: [PATCH 2/4] Address comments --- demo/electron/car/README.md | 354 ++++------------------- demo/electron/manipulator/README.md | 373 ++++-------------------- demo/electron/topics/README.md | 144 +++------ demo/electron/turtle_tf2/README.md | 433 ++++------------------------ 4 files changed, 203 insertions(+), 1101 deletions(-) diff --git a/demo/electron/car/README.md b/demo/electron/car/README.md index 8a9c1f5d..eba74893 100644 --- a/demo/electron/car/README.md +++ b/demo/electron/car/README.md @@ -1,334 +1,88 @@ -# ROS2 Car Control Demo with Electron and rclnodejs +# ROS 2 Car Control Demo (Electron + rclnodejs) -This demo showcases how to use **rclnodejs** with **Electron** to create an interactive car control application. The demo features a virtual joystick that publishes ROS2 velocity commands and a car visualization that responds to those commands in real-time. +Interactive car-control app: a virtual joystick publishes `geometry_msgs/Twist` +velocity commands on `/cmd_vel`, and a visualization subscribes and moves a car +in real time. -## ๐Ÿš— Features - -### Joystick Control Panel - -- **Directional Controls**: Up/Down/Left/Right buttons for car movement -- **Stop Button**: Emergency stop functionality -- **Keyboard Support**: WASD and arrow keys for control -- **Real-time Status**: Display current command and velocity values - -### Car Visualization - -- **Real-time Movement**: Car moves and rotates based on received commands -- **Position Tracking**: Shows current position (North/South/East/West/Center) -- **Visual Feedback**: Color-coded movement indicators -- **Command Counter**: Tracks total number of commands received - -### ROS2 Integration - -- **Topic**: `cmd_vel` (geometry_msgs/Twist) -- **Publisher**: Sends velocity commands -- **Subscriber**: Receives and displays velocity commands -- **Node Name**: `car_control_node` - -## ๐Ÿ”ง Prerequisites - -Before running this demo, ensure you have: - -1. **ROS2 installed** (Humble, Jazzy, Kilted, Lyrical, or Rolling) -2. **Node.js** (version 20.20.2 or higher) -3. **rclnodejs built** and working +![demo screenshot](./car-control-electron.gif) -### ROS2 Setup +## Features -```bash -# Source your ROS2 installation -source /opt/ros//setup.bash +- Joystick plus WASD / arrow-key control with an emergency stop +- Real-time car movement, position tracking, and command counter +- ROS 2 node `car_control_node` publishing and subscribing on `cmd_vel` -# Verify ROS2 is working -ros2 topic list -``` +## Prerequisites -### Build rclnodejs +- ROS 2 (Humble, Jazzy, Kilted, Lyrical, or Rolling), sourced +- Node.js >= 20.20.2 +- Linux (prebuilt rclnodejs binaries are provided for Ubuntu) -From the main rclnodejs directory: +## Install & Run ```bash +cd demo/electron/car npm install -npm run build -``` - -## ๐Ÿš€ Installation & Running - -1. **Navigate to the demo directory:** - - ```bash - cd electron_car_demo - ``` - -2. **Install dependencies:** - - ```bash - npm install - ``` - -3. **Native modules โ€” no manual rebuild needed:** - - rclnodejs ships prebuilt binaries for Electron and selects the matching one at - runtime from `ROS_DISTRO` + Linux codename + architecture, so just make sure - ROS 2 is sourced (next step) before launching. Do not run `electron-rebuild` - against rclnodejs โ€” it recompiles from source and bypasses the prebuilt binary. - The Forge `rebuildConfig` in `package.json` already excludes `rclnodejs` from - the automatic rebuild step. - -4. **Start the demo:** - ```bash - # Make sure ROS2 is sourced first - source /opt/ros//setup.bash - npm start - ``` - -## ๐Ÿ“ฆ Packaging for Distribution - -You can package the application into a standalone folder using **Electron Forge**. - -### 1. Build the Package - -Run the following command to create a distributable executable: - -```bash -npm run package +source /opt/ros//setup.bash # required before launch +npm start ``` -The output will be located in the `out/` directory. +rclnodejs ships prebuilt Electron binaries and selects the matching one at +runtime from `ROS_DISTRO` + Linux codename + architecture, so no compilation is +needed. Do not run `electron-rebuild` against rclnodejs โ€” it rebuilds from +source and bypasses the prebuilt binary (the Forge `rebuildConfig` in +`package.json` already excludes `rclnodejs`). -**Technical Note on ASAR:** We enable ASAR but configure it to **unpack** the `rclnodejs` module. `rclnodejs` (v1.8.1+) requires file system access to generated code and native bindings, so we use the `asar.unpack` configuration in `package.json` to keep `rclnodejs` files accessible on disk while packing the rest of the application. - -```json -"config": { - "forge": { - "packagerConfig": { - "asar": { - "unpack": "**/node_modules/rclnodejs/**" - } - } - } -} -``` +## Controls -### 2. Create Installers (Optional) +- Mouse: directional buttons (โ†‘ โ†“ โ† โ†’) and the red STOP button +- Keyboard: W/S forward/back, A/D turn, Space or Esc to stop -To create a `.zip` file or other platform-specific installers (deb/rpm), run: - -```bash -npm run make -``` +## Message Format & Mapping -**Note**: Creating DEB/RPM installers requires system tools like `dpkg` and `fakeroot`. For ZIP files, you need `zip`. +`geometry_msgs/Twist` on `/cmd_vel`: -### 3. Running the Standalone Application +| Command | linear.x | angular.z | +| -------- | -------- | --------- | +| Forward | +1.0 | 0.0 | +| Backward | -1.0 | 0.0 | +| Left | 0.0 | +1.0 | +| Right | 0.0 | -1.0 | +| Stop | 0.0 | 0.0 | -Even as a standalone application, **ROS 2 must be installed and sourced on the target machine** because `rclnodejs` links dynamically to the ROS 2 shared libraries. +## Test from the CLI ```bash -# Source ROS2 environment -source /opt/ros//setup.bash - -# Run the packaged executable -./out/rclnodejs-electron-car-demo-linux-x64/rclnodejs-electron-car-demo -``` - -![demo screenshot](./car-control-electron.gif) - -## ๐ŸŽฎ How to Use - -### Control Methods - -#### Mouse Controls - -- Click the directional buttons (โ†‘โ†“โ†โ†’) on the joystick -- Click the red **STOP** button to halt movement - -#### Keyboard Controls - -- **W** or **โ†‘**: Move forward -- **S** or **โ†“**: Move backward -- **A** or **โ†**: Turn left -- **D** or **โ†’**: Turn right -- **Space** or **Esc**: Stop - -### Understanding the Interface - -#### Left Panel - Joystick Control - -- **Command**: Shows the current command being sent -- **Linear X**: Forward/backward velocity (m/s) -- **Angular Z**: Rotation velocity (rad/s) -- **Topic**: ROS2 topic name (`cmd_vel`) - -#### Right Panel - Car Visualization - -- **Received Commands**: Total count of commands received -- **Last Command**: Most recent command processed -- **Car Position**: Current position in the visualization area - -## ๐Ÿ” Technical Details - -### ROS2 Message Format - -The demo uses `geometry_msgs/Twist` messages with the following structure: - -```javascript -{ - linear: { - x: 1.0, // Forward/backward velocity (m/s) - y: 0.0, // Left/right velocity (typically 0 for cars) - z: 0.0 // Up/down velocity (typically 0 for ground vehicles) - }, - angular: { - x: 0.0, // Roll rate (typically 0 for cars) - y: 0.0, // Pitch rate (typically 0 for cars) - z: 1.0 // Yaw rate (turning left/right in rad/s) - } -} -``` - -### Command Mapping - -| Command | Linear X | Angular Z | Description | -| -------- | -------- | --------- | ---------------------- | -| Forward | +1.0 | 0.0 | Move forward at 1 m/s | -| Backward | -1.0 | 0.0 | Move backward at 1 m/s | -| Left | 0.0 | +1.0 | Turn left at 1 rad/s | -| Right | 0.0 | -1.0 | Turn right at 1 rad/s | -| Stop | 0.0 | 0.0 | Stop all movement | - -### Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Renderer โ”‚ โ”‚ Main Process โ”‚ โ”‚ ROS2 Network โ”‚ -โ”‚ (UI/Browser) โ”‚ โ”‚ (Node.js) โ”‚ โ”‚ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ€ข Joystick UI โ”‚โ”€โ”€โ”€โ–ถโ”‚ โ€ข rclnodejs Node โ”‚โ”€โ”€โ”€โ–ถโ”‚ โ€ข cmd_vel Topic โ”‚ -โ”‚ โ€ข Car Display โ”‚โ—€โ”€โ”€โ”€โ”‚ โ€ข Publisher โ”‚ โ”‚ โ€ข Other Nodes โ”‚ -โ”‚ โ€ข Status Panel โ”‚ โ”‚ โ€ข Subscriber โ”‚โ—€โ”€โ”€โ”€โ”‚ โ€ข Robot/Sim โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## ๐Ÿงช Testing with ROS2 Tools - -You can test the demo using standard ROS2 command-line tools: - -### Listen to Published Commands - -```bash -# In a new terminal (with ROS2 sourced) ros2 topic echo /cmd_vel +ros2 topic pub /cmd_vel geometry_msgs/Twist "{linear: {x: 1.0}, angular: {z: 0.0}}" ``` -### Send Commands from Command Line - -```bash -# Send a forward command -ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}" - -# Send a turn left command -ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.0}" - -# Stop command -ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}" -``` - -### Check Active Topics +## Packaging ```bash -ros2 topic list -ros2 topic info /cmd_vel -ros2 topic hz /cmd_vel +npm run package # standalone app in out/ +npm run make # zip / deb / rpm installers (needs zip, dpkg, fakeroot) ``` -## ๐Ÿค– Integration with Real Robots - -This demo can be easily connected to real robots or simulators: - -### TurtleBot3 (Example) +ASAR is enabled but `rclnodejs` is unpacked (`asar.unpack` in `package.json`) +because it needs filesystem access to its generated code and native bindings. +The target machine must still have ROS 2 installed and sourced โ€” rclnodejs links +dynamically to ROS 2 shared libraries: ```bash -# Launch TurtleBot3 simulation -ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py - -# The demo will automatically control the robot via cmd_vel topic -``` - -### Custom Robot - -Ensure your robot subscribes to the `/cmd_vel` topic with `geometry_msgs/Twist` messages. - -## ๐Ÿ› ๏ธ Customization - -### Modify Velocity Values - -Edit the `speed` and `turnSpeed` constants in `main.js`: - -```javascript -const speed = 1.0; // Linear velocity (m/s) -const turnSpeed = 1.0; // Angular velocity (rad/s) -``` - -### Change Topic Name - -Modify the topic name in `main.js`: - -```javascript -// Change 'cmd_vel' to your desired topic name -carControlPublisher = carControlNode.createPublisher( - 'geometry_msgs/msg/Twist', - 'your_topic_name' -); -``` - -### Add More Commands - -Extend the joystick commands by modifying the switch statement in `main.js` and adding corresponding UI elements. - -## ๐Ÿ› Troubleshooting - -### Common Issues - -1. **"Failed to initialize ROS2" Error** - - Ensure ROS2 is properly sourced before running npm start - - Check that rclnodejs is built correctly - -2. **Native module fails to load** - - Ensure ROS 2 is sourced so rclnodejs can match its prebuilt binary - (`ROS_DISTRO` must be set; prebuilt binaries are provided for Ubuntu). - - As a last resort, force a from-source rebuild by setting - `RCLNODEJS_FORCE_BUILD=1` before `npm start` (needs a compiler toolchain and - network access). - -3. **Topic Not Appearing** - - Verify ROS2 daemon is running: `ros2 daemon status` - - Check topic list: `ros2 topic list` - -4. **Car Not Moving in UI** - - Check browser console for JavaScript errors - - Verify IPC communication between main and renderer processes - -### Debug Mode - -Add debug logging by modifying `main.js`: - -```javascript -// Enable debug logging -console.log('Publishing command:', command, twist); +source /opt/ros//setup.bash +./out/rclnodejs-electron-car-demo-linux-x64/rclnodejs-electron-car-demo ``` -## ๐Ÿ“š Learning Resources - -- [rclnodejs Documentation](https://github.com/RobotWebTools/rclnodejs) -- [ROS2 Tutorials](https://docs.ros.org/en/lyrical/Tutorials.html) -- [Electron Documentation](https://www.electronjs.org/docs) -- [geometry_msgs/Twist Documentation](https://docs.ros.org/en/lyrical/p/geometry_msgs/interfaces/msg/Twist.html) - -## ๐Ÿ“„ License +## Troubleshooting -This demo is licensed under the Apache License 2.0, same as the main rclnodejs project. +- **"Failed to initialize ROS2"** โ€” source ROS 2 before `npm start`. +- **Native module fails to load** โ€” ensure `ROS_DISTRO` is set so the prebuilt + binary matches; as a last resort set `RCLNODEJS_FORCE_BUILD=1` (needs a + compiler toolchain and network access). +- **Topic missing** โ€” check `ros2 daemon status` and `ros2 topic list`. +- **Car not moving** โ€” open the DevTools console and check for errors. -## ๐Ÿค Contributing +## License -Feel free to submit issues and enhancement requests! This demo serves as both a functional example and a starting point for more complex ROS2 Electron applications. +Apache License 2.0, same as rclnodejs. diff --git a/demo/electron/manipulator/README.md b/demo/electron/manipulator/README.md index 14f06bad..ed3f2171 100644 --- a/demo/electron/manipulator/README.md +++ b/demo/electron/manipulator/README.md @@ -1,358 +1,91 @@ -# Two-Joint Manipulator Demo +# Two-Joint Manipulator Demo (Electron + rclnodejs) -An interactive Electron application demonstrating a two-joint robotic manipulator visualization using rclnodejs and Three.js. +Interactive 3D visualization of a two-joint robotic arm using rclnodejs and +Three.js. It publishes and subscribes to `sensor_msgs/msg/JointState` on +`/joint_states`. -![Manipulator Demo](./manipulator-demo.gif) +![demo screenshot](./manipulator-demo.gif) -## ๐Ÿš€ Features +## Features -- **Real-time 3D Visualization**: Interactive two-joint robotic arm with Three.js -- **ROS2 Integration**: Publishes and subscribes to `sensor_msgs/msg/JointState` topics -- **Interactive Control**: Manual joint control with sliders -- **Automatic Animation**: Smooth sinusoidal motion patterns -- **Live Feedback**: Real-time joint position display and ROS2 message frequency -- **Visual Movement Markers**: Color-coded rings, arrows, and labels to identify joint movements -- **Modern UI**: Clean, responsive interface with 3D orbit controls +- Real-time Three.js 3D arm with orbit controls +- Manual joint sliders and automatic sinusoidal animation +- Publishes `/joint_states` at 10 Hz; subscribes and visualizes incoming states +- Color-coded joints/markers with live position and message-rate display -## ๐Ÿ“‹ Prerequisites +## Prerequisites -- **Node.js** (>= 20.20.2) - JavaScript runtime -- **ROS 2** (Humble, Jazzy, Kilted, Lyrical, or Rolling) - Robot Operating System 2 -- **rclnodejs compatible environment** - Linux recommended (tested on Ubuntu/WSL) +- ROS 2 (Humble, Jazzy, Kilted, Lyrical, or Rolling), sourced +- Node.js >= 20.20.2 +- Linux recommended (prebuilt rclnodejs binaries are provided for Ubuntu) -## ๐Ÿ› ๏ธ Installation - -1. **Navigate to the demo directory**: - - ```bash - cd rclnodejs/demo/electron/manipulator - ``` - -2. **Install dependencies**: - - ```bash - npm install - ``` - -3. **Source your ROS 2 environment** (required so the matching prebuilt binary is selected): - - ```bash - source /opt/ros/$ROS_DISTRO/setup.bash # or your ROS 2 installation path - ``` - - rclnodejs ships prebuilt native binaries for Electron, so no compilation is needed. - The binary is selected at runtime from `ROS_DISTRO`, the Linux distro codename, and - the CPU architecture, so `ROS_DISTRO` must be set before launching the app. If no - matching prebuilt binary is available for your platform, rclnodejs falls back to - building from source. - - > Note: do not run `electron-rebuild` against rclnodejs โ€” it recompiles the addon - > from source and bypasses the prebuilt binary. The Electron Forge `rebuildConfig` - > in `package.json` already excludes `rclnodejs` from the automatic rebuild step. - -## ๐Ÿ“œ Available Scripts - -- **`npm start`** - Launch the application in development mode -- **`npm run package`** - Package the application into a standalone executable folder -- **`npm run make`** - Create platform-specific installers (requires system tools like `zip`, `dpkg`) - -## ๐Ÿš€ Quick Start - -### Option 1: Simple Demo (Works Everywhere) +## Install & Run ```bash +cd demo/electron/manipulator +npm install +source /opt/ros/$ROS_DISTRO/setup.bash # required before launch npm start ``` -- โœ… **No ROS2 topic publishing required** -- โœ… **Pure visualization and manual control** -- โš ๏ธ ROS 2 must still be sourced so the prebuilt native binary is selected (otherwise rclnodejs builds from source) -- โš ๏ธ No ROS2 topic publishing (local mode only) - -### Option 2: Manual ROS2 Setup (Recommended for ROS2 Integration) - -1. **Source your ROS2 environment**: - - ```bash - source /opt/ros/$ROS_DISTRO/setup.bash # or your ROS2 installation path - ``` - -2. **Run the demo**: - ```bash - npm start - ``` - -- โœ… **Publishes to `/joint_states` topic** -- โœ… **Full ROS2 ecosystem integration** -- โœ… **Real-time ROS2 message monitoring** - -## ๐ŸŽฎ Usage - -### Interactive Controls - -- **Joint Sliders**: Use the sliders in the control panel to manually adjust joint angles - - **Joint 1 (Base)**: Rotates the entire arm around the vertical axis (ยฑ180ยฐ) - - **Joint 2 (Elbow)**: Bends the upper arm segment (ยฑ135ยฐ) - -- **Animation**: Click "Start Animation" for automatic smooth motion -- **Reset**: Click "Reset Position" to return to zero configuration - -### 3D Visualization Controls +rclnodejs ships prebuilt Electron binaries and selects the matching one at +runtime from `ROS_DISTRO` + Linux codename + architecture, so no compilation is +needed. Do not run `electron-rebuild` against rclnodejs โ€” it rebuilds from +source and bypasses the prebuilt binary (the Forge `rebuildConfig` in +`package.json` already excludes `rclnodejs`). If no matching prebuilt binary +exists for your platform, rclnodejs falls back to building from source. -- **Orbit**: Click and drag to rotate the camera around the manipulator -- **Zoom**: Use mouse wheel to zoom in/out -- **View**: The manipulator is shown with color-coded components: - - **Gray Base**: Fixed mounting base - - **Red Joint 1**: Base rotation joint - - **Blue Link 1**: Upper arm segment - - **Green Joint 2**: Elbow rotation joint - - **Yellow Link 2**: Forearm segment - - **Purple End Effector**: Tool attachment point +> ROS 2 must be sourced even for local-only visualization, so the prebuilt +> binary can be selected. -### Visual Movement Markers +## Usage -The demo includes visual indicators to help identify joint movements: +- **Joint 1 (base)**: ยฑ180ยฐ rotation; **Joint 2 (elbow)**: ยฑ135ยฐ bend +- Sliders for manual control; "Start Animation" for automatic motion; + "Reset Position" to return to zero +- 3D view: drag to orbit, mouse wheel to zoom -- **๐Ÿ”ด Red Markers**: Joint 1 (Base rotation) - - Red ring around the base joint - - Red arrow showing rotation direction - - "Joint1" text label +## ROS 2 Topics -- **๐ŸŸข Green Markers**: Joint 2 (Elbow) - - Green ring around the elbow joint - - Green arrow showing rotation direction - - "Joint2" text label - -- **โšช White Reference Ring**: Fixed base reference point - -These markers make it easy to visually confirm which joints are moving during manual control or animation. - -## ๐Ÿ”ง ROS2 Topics - -### Published Topics - -- **`/joint_states`** (`sensor_msgs/msg/JointState`) - - Joint names: `['joint1', 'joint2']` - - Positions: Current joint angles in radians - - Published at 10 Hz - - Velocity and effort fields included (set to zero) - -### Subscribed Topics - -- **`/joint_states`** (`sensor_msgs/msg/JointState`) - - Receives external joint commands - - Updates visualization in real-time - - Displays message frequency and count - -### Monitoring Topics - -To verify the demo is working correctly, you can monitor the published topics: +`/joint_states` (`sensor_msgs/msg/JointState`) โ€” joints `['joint1', 'joint2']`, +positions in radians, published at 10 Hz. Monitor with: ```bash -# In a separate terminal, source ROS2 environment -source /opt/ros/$ROS_DISTRO/setup.bash # or your ROS2 installation - -# List all available topics -ros2 topic list - -# Monitor joint state messages ros2 topic echo /joint_states - -# Check publishing frequency ros2 topic hz /joint_states - -# View topic info -ros2 topic info /joint_states ``` -## ๐Ÿ“ฆ Packaging for Distribution - -You can package the application into a standalone folder using **Electron Forge**. - -### 1. Build the Package - -Run the following command to create a distributable executable: +## Packaging ```bash -npm run package +npm run package # standalone app in out/rclnodejs-manipulator-demo-linux-x64/ +npm run make # zip installer ``` -The output will be located in the `out/` directory, for example: `out/rclnodejs-manipulator-demo-linux-x64/`. - -**Technical Note on ASAR:** We enable ASAR but configure it to **unpack** the `rclnodejs` module. `rclnodejs` (v1.8.1+) requires file system access to generated code and native bindings, so we use the `asar.unpack` configuration in `package.json` to keep `rclnodejs` files accessible on disk while packing the rest of the application. - -```json -"config": { - "forge": { - "packagerConfig": { - "asar": { - "unpack": "**/node_modules/rclnodejs/**" - } - } - } -} -``` - -### 2. Create Installers (Optional) - -To create a `.zip` file for distribution, run: +ASAR is enabled but `rclnodejs` is unpacked (`asar.unpack` in `package.json`) +because it needs filesystem access to its generated code and native bindings. +The target machine must still have ROS 2 installed and sourced: ```bash -npm run make -``` - -**Note**: This will generate a ZIP archive containing the packaged application. - -### 3. Running the Standalone Application - -Even as a standalone application, **ROS 2 must be installed and sourced on the target machine** because `rclnodejs` links dynamically to the ROS 2 shared libraries. - -```bash -# Source ROS2 environment source /opt/ros/$ROS_DISTRO/setup.bash - -# Run the packaged executable ./out/rclnodejs-manipulator-demo-linux-x64/rclnodejs-manipulator-demo ``` -## ๐Ÿ—๏ธ Architecture - -### Main Process (`main.js`) - -- **ROS2 Node Management**: Creates and manages the manipulator node -- **Joint State Publishing**: Publishes current joint positions at 10 Hz -- **Animation Control**: Handles smooth motion generation -- **IPC Communication**: Bridges ROS2 data with renderer process - -### Renderer Process (`renderer.js`) - -- **3D Visualization**: Three.js scene with interactive manipulator model -- **Visual Markers**: Color-coded rings, arrows, and labels for joint identification -- **User Interface**: Control panels and status displays -- **Real-time Updates**: Smooth joint motion and camera controls -- **Message Handling**: Processes ROS2 data and user interactions - -### Component Hierarchy - -``` -Scene -โ”œโ”€โ”€ Lighting (Ambient + Directional + Point) -โ”œโ”€โ”€ Ground Plane -โ”œโ”€โ”€ Coordinate Axes -โ”œโ”€โ”€ Base (Fixed) -โ”œโ”€โ”€ Joint Markers (Red/Green Rings, Arrows, Labels) -โ””โ”€โ”€ Joint1 Group (Rotates around Y-axis) - โ”œโ”€โ”€ Joint1 Sphere - โ”œโ”€โ”€ Link1 Cylinder - โ””โ”€โ”€ Joint2 Group (Rotates around Z-axis) - โ”œโ”€โ”€ Joint2 Sphere - โ”œโ”€โ”€ Link2 Cylinder - โ””โ”€โ”€ End Effector Cube -``` - -## ๐ŸŽฏ Technical Details - -### Joint Configuration - -- **Joint 1 (Base)**: Revolute joint, Y-axis rotation, ยฑ180ยฐ range -- **Joint 2 (Elbow)**: Revolute joint, Z-axis rotation, ยฑ135ยฐ range -- **Forward Kinematics**: Hierarchical transformation chains -- **Coordinate System**: Right-handed, Z-up convention - -### Animation Patterns - -```javascript -// Sinusoidal motion with different frequencies (from main.js) -joint1_angle = (sin(time * 1.0) * ฯ€) / 3; // ยฑ60ยฐ at 1.0x speed -joint2_angle = (sin(time * 1.5) * ฯ€) / 4; // ยฑ45ยฐ at 1.5x speed -``` - -### Performance - -- **Rendering**: 60 FPS with requestAnimationFrame -- **ROS2 Publishing**: 10 Hz joint state updates -- **Animation**: 20 Hz smooth motion updates -- **UI Updates**: Real-time slider and display synchronization - -## ๐Ÿ› ๏ธ Development - -### File Structure - -``` -manipulator/ -โ”œโ”€โ”€ package.json # Dependencies and scripts -โ”œโ”€โ”€ main.js # Electron main process + ROS2 integration -โ”œโ”€โ”€ renderer.js # Three.js visualization + UI controls -โ”œโ”€โ”€ index.html # Application UI layout and styling -โ”œโ”€โ”€ start-demo.sh # Convenient ROS2 startup script -โ”œโ”€โ”€ README.md # This documentation -โ”œโ”€โ”€ VERSION_UPGRADE.md # rclnodejs upgrade details -โ””โ”€โ”€ COMPLETION_SUMMARY.md # Implementation summary -``` - -### Key Dependencies - -- **electron**: `^40.1.0` - Desktop application framework -- **rclnodejs**: `^1.8.1` - ROS2 JavaScript client library -- **@electron/rebuild**: `^4.0.3` - Native module rebuilding tool -- **three**: `^0.182.0` - 3D graphics library - -### Debugging - -- Press `F12` to open Developer Tools -- Console logs show ROS2 initialization and message flow -- Check ROS2 topics with: `ros2 topic list` and `ros2 topic echo /joint_states` - -## ๐Ÿ” Troubleshooting - -### Common Issues - -1. **"librcl.so not found"** - - ```bash - # Source ROS2 environment manually - source /opt/ros/$ROS_DISTRO/setup.bash # or your ROS2 installation path - npm start - ``` - -2. **Build errors with Electron** - -- This demo currently uses Electron 40.1.0 -- rclnodejs loads its prebuilt Electron binary at runtime; if it fails to load, ensure ROS 2 is sourced so the matching prebuild is selected. To force a from-source rebuild of rclnodejs, set `RCLNODEJS_FORCE_BUILD=1` -- The versions recorded in `package.json` and `package-lock.json` are the tested baseline for this demo - -3. **No ROS2 messages received** - - Check if ROS2 daemon is running: `ros2 daemon start` - - Verify topic exists: `ros2 topic list` - - Check message flow: `ros2 topic echo /joint_states` - -4. **Blank 3D scene** - - Check browser console for Three.js errors - - Ensure internet connection for Three.js CDN - - Press F12 to open Developer Tools for debugging - -### Performance Tips - -- Reduce animation frequency for slower systems -- Disable shadows in Three.js for better performance -- Adjust rendering quality in renderer settings - -## ๐Ÿ“š Learning Resources - -- **ROS2 Concepts**: [ROS2 Documentation](https://docs.ros.org/en/lyrical/) -- **Three.js Guide**: [Three.js Documentation](https://threejs.org/docs/) -- **Electron Tutorials**: [Electron Documentation](https://electronjs.org/docs) -- **rclnodejs API**: [rclnodejs Repository](https://github.com/RobotWebTools/rclnodejs) +## Files -## ๐Ÿค Contributing +- `main.js` โ€” Electron main process + ROS 2 node (joint publishing / animation) +- `renderer.js` โ€” Three.js scene, markers, and UI controls +- `index.html` โ€” UI layout -This demo is part of the rclnodejs project. Contributions welcome! +## Troubleshooting -1. Fork the repository -2. Create your feature branch -3. Add tests for new functionality -4. Submit a pull request +- **"librcl.so not found"** โ€” source ROS 2, then `npm start`. +- **Native module fails to load** โ€” ensure `ROS_DISTRO` is set so the prebuilt + binary matches; to force a from-source rebuild set `RCLNODEJS_FORCE_BUILD=1`. +- **No messages** โ€” run `ros2 daemon start`, then check `ros2 topic list` and + `ros2 topic echo /joint_states`. +- **Blank 3D scene** โ€” open DevTools (F12) and check for Three.js / WebGL errors. -## ๐Ÿ“„ License +## License -Licensed under the Apache License, Version 2.0. See the main rclnodejs repository for details. +Apache License 2.0, same as rclnodejs. diff --git a/demo/electron/topics/README.md b/demo/electron/topics/README.md index 8c9e8117..84bac495 100644 --- a/demo/electron/topics/README.md +++ b/demo/electron/topics/README.md @@ -1,132 +1,64 @@ -# Topics Demo - Electron and rclnodejs +# Topics Demo (Electron + rclnodejs) -A minimal Electron application demonstrating basic ROS2 topic communication using rclnodejs. This demo provides a simple interface for publishing and subscribing to string messages, making it the perfect starting point for learning rclnodejs with Electron. +A minimal Electron app for ROS 2 topic communication: publish and subscribe to +`std_msgs/String` messages on `ts_demo`. A good starting point for rclnodejs +with Electron. -![Demo Screenshot](./electron-demo.gif) +![demo screenshot](./electron-demo.gif) -## ๐Ÿ“จ Features +## Features -- **Simple Publisher**: Text input interface for publishing string messages -- **Real-time Subscriber**: Live display of received messages -- **Message Counter**: Tracks published and received message counts -- **Clean UI**: Minimal, user-friendly interface -- **Educational**: Perfect introduction to ROS2 topics with Electron +- Text input to publish string messages on `ts_demo` +- Live display of received messages +- Published / received message counters +- ROS 2 node `electron_demo_node` -### ROS2 Integration +## Prerequisites -- **Topic**: `ts_demo` (std_msgs/String) -- **Publisher**: Sends user-input text messages -- **Subscriber**: Receives and displays messages in real-time -- **Node Name**: `electron_demo_node` +- ROS 2 (Humble, Jazzy, Kilted, Lyrical, or Rolling), sourced +- Node.js >= 20.20.2 +- Linux (prebuilt rclnodejs binaries are provided for Ubuntu) -## ๐Ÿ“‹ Prerequisites - -- **Node.js** (>= 20.20.2) - JavaScript runtime -- **ROS 2** (Humble, Jazzy, Kilted, Lyrical, or Rolling) - Robot Operating System 2 -- **rclnodejs compatible environment** - Linux recommended (tested on Ubuntu/WSL) - -## ๐Ÿ› ๏ธ Installation - -1. **Navigate to the demo directory**: - - ```bash - cd rclnodejs/demo/electron/topics - ``` - -2. **Source your ROS 2 environment**: - - ```bash - source /opt/ros/jazzy/setup.bash # or your ROS 2 distro - ``` - -3. **Install dependencies**: - - ```bash - npm install - ``` - -4. **Native modules โ€” no manual rebuild needed**: - - rclnodejs ships prebuilt binaries for Electron and picks the matching one at - runtime from `ROS_DISTRO` + Linux codename + architecture (ROS 2 was sourced in - step 2). Do not run `electron-rebuild` against rclnodejs โ€” it recompiles from - source and bypasses the prebuilt binary; the Forge `rebuildConfig` already - excludes `rclnodejs`. - -## ๐Ÿš€ Running the Demo - -Start the application: +## Install & Run ```bash +cd demo/electron/topics +source /opt/ros//setup.bash # required before launch +npm install npm start ``` -The demo window will open with: - -- **Text input field** for typing messages to publish -- **Send button** to publish messages to the `ts_demo` topic -- **Message display area** showing received messages -- **Counters** for published and received messages - -## ๐Ÿ“ฆ Packaging for Distribution +rclnodejs ships prebuilt Electron binaries and picks the matching one at runtime +from `ROS_DISTRO` + Linux codename + architecture, so no compilation is needed. +Do not run `electron-rebuild` against rclnodejs โ€” it rebuilds from source and +bypasses the prebuilt binary (the Forge `rebuildConfig` in `package.json` +already excludes `rclnodejs`). -You can package the application into a standalone folder using **Electron Forge**. +The window provides a text field, a Send button (publishes to `ts_demo`), a +message display, and published / received counters. -### 1. Build the Package - -Run the following command to create a distributable executable: +## Packaging ```bash -npm run package +npm run package # standalone app in out/ +npm run make # zip / deb / rpm installers (needs zip, dpkg, fakeroot) ``` -The output will be located in the `out/` directory. - -**Technical Note on ASAR:** We enable ASAR but configure it to **unpack** the `rclnodejs` module. `rclnodejs` (v1.8.1+) requires file system access to generated code and native bindings, so we use the `asar.unpack` configuration in `package.json` to keep `rclnodejs` files accessible on disk while packing the rest of the application. - -```json -"config": { - "forge": { - "packagerConfig": { - "asar": { - "unpack": "**/node_modules/rclnodejs/**" - } - } - } -} -``` - -### 2. Create Installers (Optional) - -To create a `.zip` file or other platform-specific installers (deb/rpm), run: +ASAR is enabled but `rclnodejs` is unpacked (`asar.unpack` in `package.json`) +because it needs filesystem access to its generated code and native bindings. +The target machine must still have ROS 2 installed and sourced: ```bash -npm run make -``` - -**Note**: Creating DEB/RPM installers requires system tools like `dpkg` and `fakeroot`. For ZIP files, you need `zip`. - -### 3. Running the Standalone Application - -Even as a standalone application, **ROS 2 must be installed and sourced on the target machine** because `rclnodejs` links dynamically to the ROS 2 shared libraries. - -```bash -# Source ROS2 environment -source /opt/ros//setup.bash - -# Run the packaged executable +source /opt/ros//setup.bash ./out/rclnodejs-electron-demo-linux-x64/rclnodejs-electron-demo ``` -## ๐Ÿ“ Project Structure +## Files -- **`package.json`** - Project configuration and dependencies -- **`main.js`** - Electron main process with rclnodejs integration -- **`index.html`** - Application interface and layout -- **`renderer.js`** - Renderer process handling UI interactions and ROS2 communication +- `main.js` โ€” Electron main process + rclnodejs integration +- `renderer.js` โ€” UI and ROS 2 communication +- `index.html` โ€” interface layout -## ๐Ÿ”— Related Resources +## License -- [Electron Documentation](https://electronjs.org/docs) - Complete Electron framework guide -- [Native Node Modules](https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules) - Using native modules with Electron -- [rclnodejs Documentation](../../README.md) - Core rclnodejs API reference +Apache License 2.0, same as rclnodejs. diff --git a/demo/electron/turtle_tf2/README.md b/demo/electron/turtle_tf2/README.md index c8f6bf64..fdd73597 100644 --- a/demo/electron/turtle_tf2/README.md +++ b/demo/electron/turtle_tf2/README.md @@ -1,414 +1,97 @@ -# Turtle TF2 Electron Demo +# Turtle TF2 Demo (Electron + rclnodejs) -This demo replicates the functionality of the ROS2 `turtle_tf2_py` package using rclnodejs and provides a modern web-based 3D visualization interface. It demonstrates coordinate frame transformations, turtle simulation, and real-time TF2 data visualization. - -## Overview - -The turtle_tf2 demo showcases: - -- **Transform Broadcasting**: Multiple TF2 broadcasters publishing coordinate frame relationships -- **Transform Listening**: Real-time monitoring and visualization of coordinate transformations -- **Turtle Simulation**: Integration with turtlesim for turtle pose tracking and control -- **Turtle Following**: Intelligent turtle2 behavior that automatically follows turtle1 using TF2 transforms -- **3D Visualization**: WebGL-based rendering using Three.js for immersive coordinate frame visualization -- **Interactive Controls**: Web interface for spawning turtles, controlling motion, and managing transforms +A rclnodejs port of ROS 2's `turtle_tf2_py`, with a Three.js 3D view of +coordinate-frame transforms. It broadcasts and listens to TF2 transforms, drives +turtlesim, and makes turtle2 follow turtle1. ![demo screenshot](./turtle-tf2-demo.gif) ## Features -### TF2 Broadcasters - -- **Static Frame Broadcaster**: Publishes fixed coordinate frame relationships -- **Dynamic Frame Broadcaster**: Creates time-varying transforms with circular motion patterns -- **Fixed Frame Broadcaster**: Maintains constant offset transforms -- **Turtle Transform Broadcaster**: Converts turtle poses to TF2 transforms - -### Turtle Following System - -- **Real-time Following**: turtle2 automatically follows turtle1 using distance and angle calculations -- **Smart Movement**: Proportional velocity control based on distance to target -- **Collision Avoidance**: turtle2 stops when within optimal following distance (0.5 units) -- **Transform Integration**: Following logic uses turtle pose data from TF2 coordinate frames - -### Visualization - -- **3D Scene**: Interactive Three.js environment with orbit controls -- **Coordinate Frames**: Visual representation of X, Y, Z axes with color coding -- **Turtle Models**: 3D turtle representations with real-time pose updates -- **Transform Monitoring**: Live display of active transforms and their parameters -- **Frame Toggles**: Show/hide specific coordinate frames - -### Control Interface - -- **Turtle Spawning**: Create turtle2 instance (turtle1 spawns automatically with turtlesim) -- **Motion Control**: WASD keyboard controls for turtle1 movement -- **Demo Management**: Initialize and reset demo state -- **Transform Filtering**: Toggle visibility of different frame types +- TF2 broadcasters: static, dynamic (circular motion), fixed-offset, and + turtle-pose +- TF2 listener that drives turtle2 to follow turtle1 +- 3D Three.js scene with orbit controls, colored frame axes, and a live + transform list +- Controls to spawn turtle2, drive turtle1 (WASD), and toggle frame visibility ## Prerequisites -### System Requirements +- ROS 2 (Humble, Jazzy, Kilted, Lyrical, or Rolling), sourced +- Node.js >= 20.20.2 +- turtlesim: `sudo apt install ros-$ROS_DISTRO-turtlesim` +- Linux (prebuilt rclnodejs binaries are provided for Ubuntu) -- **ROS2**: Humble, Jazzy, Kilted, Lyrical, or Rolling distribution -- **Node.js**: Version 20.20.2 or higher -- **turtlesim**: ROS2 turtle simulation package -- **Electron**: For desktop application framework - -### ROS2 Packages +## Install & Run ```bash -# Install required ROS2 packages -sudo apt install ros-$ROS_DISTRO-turtlesim -sudo apt install ros-$ROS_DISTRO-tf2-tools -sudo apt install ros-$ROS_DISTRO-tf2-ros -``` - -### Node.js Dependencies - -The demo uses the following key dependencies: - -- `rclnodejs`: ROS2 bindings for Node.js -- `electron`: Cross-platform desktop app framework -- `three`: 3D graphics library for WebGL rendering - -## Installation - -1. **Navigate to the demo directory**: - - ```bash - cd demo/electron/turtle_tf2 - ``` - -2. **Install dependencies**: - - ```bash - npm install - ``` - -3. **Native modules โ€” no manual rebuild needed**: - - rclnodejs ships prebuilt binaries for Electron and selects the matching one at - runtime from `ROS_DISTRO` + Linux codename + architecture, so make sure ROS 2 is - sourced (next step) before launching. Do not run `electron-rebuild` against - rclnodejs โ€” it recompiles from source and bypasses the prebuilt binary; the Forge - `rebuildConfig` in `package.json` already excludes `rclnodejs`. - -4. **Source ROS2 environment**: - - ```bash - source /opt/ros/$ROS_DISTRO/setup.bash - ``` - - **Important**: The ROS2 environment must be sourced in the same terminal session where you run `npm start`. - -## Running the Demo - -**โš ๏ธ Important Setup Note**: Before running the demo, make sure to: - -1. Source the ROS2 environment in your terminal: `source /opt/ros/$ROS_DISTRO/setup.bash` -2. Keep this terminal session active for the entire demo run - -### Method 1: Complete Demo - -Start the full demo with all components (ensure you have run `npm install` first): - -```bash -# Source ROS2 first -source /opt/ros/$ROS_DISTRO/setup.bash - -# Then start the demo +cd demo/electron/turtle_tf2 +npm install +source /opt/ros/$ROS_DISTRO/setup.bash # required, in the same terminal as npm start +ros2 run turtlesim turtlesim_node # in another sourced terminal npm start ``` -### Method 2: Step-by-Step Launch - -1. **Source ROS2 environment**: - - ```bash - source /opt/ros/$ROS_DISTRO/setup.bash - ``` - -2. **Start turtlesim (in separate terminal, also sourced)**: - - ```bash - source /opt/ros/$ROS_DISTRO/setup.bash - ros2 run turtlesim turtlesim_node - ``` - -3. **Launch the Electron application**: - - ```bash - npm start - ``` - -4. **Use the web interface to**: - - Click "Start Demo" to initialize all TF2 broadcasters - - Click "Spawn Turtle2" to create the second turtle (turtle1 spawns automatically) - - Use WASD keys to control turtle1 movement - - Watch turtle2 automatically follow turtle1 - - Use frame toggle buttons to show/hide specific transforms - -**โš ๏ธ Important**: The dynamic frame (`carrot1_dynamic`) orbits around the static frame (`carrot1_static`) in a circular pattern with a 2-unit radius, regardless of turtle positions. - -## ๐Ÿ“ฆ Packaging for Distribution - -You can package the application into a standalone folder using **Electron Forge**. - -### 1. Build the Package - -Run the following command to create a distributable executable: - -```bash -npm run package -``` - -The output will be located in the `out/` directory. +rclnodejs ships prebuilt Electron binaries and selects the matching one at +runtime from `ROS_DISTRO` + Linux codename + architecture, so no compilation is +needed. Do not run `electron-rebuild` against rclnodejs โ€” it rebuilds from +source and bypasses the prebuilt binary (the Forge `rebuildConfig` in +`package.json` already excludes `rclnodejs`). -### 2. Create Installers (Optional) +In the app: click "Start Demo" to launch the broadcasters, "Spawn Turtle2" to +add the follower, then drive turtle1 with WASD and watch turtle2 follow. -To create a `.zip` file or other platform-specific installers (deb/rpm), run: +## Coordinate Frames -```bash -npm run make ``` - -**Note**: Creating DEB/RPM installers requires system tools like `dpkg` and `fakeroot`. For ZIP files, you need `zip`. - -### 3. Running the Standalone Application - -Even as a standalone application, **ROS 2 must be installed and sourced on the target machine** because `rclnodejs` links dynamically to the ROS 2 shared libraries. - -```bash -# Source ROS2 environment -source /opt/ros/$ROS_DISTRO/setup.bash - -# Run the packaged executable -./out/rclnodejs-turtle-tf2-demo-linux-x64/rclnodejs-turtle-tf2-demo -``` - -## Demo Components - -### Main Process (main.js) - -- **TF2 Static Broadcaster**: Publishes `world โ†’ carrot1_static` transform -- **TF2 Dynamic Broadcaster**: Publishes time-varying `carrot1_static โ†’ carrot1_dynamic` transform -- **Fixed Frame Broadcaster**: Publishes constant offset `turtle1 โ†’ carrot1_fixed` transform -- **Turtle TF2 Broadcaster**: Converts turtle poses to `world โ†’ turtle1/turtle2` transforms -- **Turtle TF2 Listener**: Monitors turtle poses and controls turtle2 following behavior using real-time transform data - -### Renderer Process (renderer.js) - -- **3D Scene Management**: Three.js scene setup with lighting and camera controls -- **Coordinate Frame Visualization**: Colored axes representation (X=red, Y=green, Z=blue) -- **Turtle Rendering**: 3D turtle models with real-time pose updates -- **Following Logic**: Calculates distance, angle, and velocity commands for turtle2 following behavior -- **Transform Monitoring**: Live display of transform data and frame relationships -- **User Interaction**: Control buttons, keyboard handling, and visual feedback systems - -### HTML Interface (index.html) - -- **Control Panel**: Buttons for turtle spawning and demo management -- **Status Display**: Real-time connection and system status -- **Transform List**: Active transforms with position and rotation data -- **3D Viewport**: WebGL canvas for Three.js rendering - -## Understanding TF2 Concepts - -### Coordinate Frames - -- **world**: Global reference frame (origin) -- **turtle1/turtle2**: Turtle body frames following turtlesim poses -- **carrot1_static**: Static frame with fixed relationship to world -- **carrot1_dynamic**: Dynamic frame with circular motion around carrot1_static (2-unit radius) -- **carrot1_fixed**: Fixed offset frame relative to turtle1 - -### Transform Chain - -The demo creates the following transform chain: - -``` -world โ†’ carrot1_static โ†’ carrot1_dynamic +world โ†’ carrot1_static โ†’ carrot1_dynamic (dynamic orbits static, 2-unit radius) world โ†’ turtle1 โ†’ carrot1_fixed world โ†’ turtle2 ``` -### Real-time Visualization - -- Coordinate frames are rendered as colored axes (X=red, Y=green, Z=blue) -- Transforms update in real-time as turtles move and frames change -- The transform list shows current position and rotation values -- Interactive 3D camera allows inspection from different angles +Axes are colored X=red, Y=green, Z=blue, and the transform list shows live +positions and rotations. ## Controls -### Keyboard Controls (NEW!) - -**Turtle1 Movement**: - -- **W**: Move forward -- **S**: Move backward -- **A**: Turn left -- **D**: Turn right - -๐Ÿ’ก **Tip**: Click on the 3D visualization area first to ensure keyboard focus, then use WASD keys to drive turtle1 around the turtlesim environment. turtle2 will automatically follow turtle1! - -**Camera Controls**: - -- **Arrow Keys**: Move camera view -- **Mouse Drag**: Rotate camera around scene -- **Mouse Wheel**: Zoom in/out -- **Right Click + Drag**: Pan camera view - -**Note**: Arrow keys are reserved for 3D camera navigation. Use WASD keys exclusively for turtle control to avoid conflicts. - -### Turtle Management - -- **Spawn Turtle2**: Creates turtle2 at position (4.0, 2.0) - turtle1 is automatically spawned by turtlesim -- **Stop All**: Halts all turtle motion commands - -### Turtle Following Behavior - -Once turtle2 is spawned, it will automatically follow turtle1 with the following intelligent behaviors: - -- **Distance-based Speed**: turtle2 moves faster when far from turtle1, slower when close -- **Angle Correction**: turtle2 continuously adjusts its heading to face turtle1 -- **Smart Stopping**: turtle2 stops moving when within 0.5 units of turtle1 to avoid collision -- **Real-time Updates**: Following commands are sent every second based on current turtle positions - -**Following Algorithm Details**: - -- **Linear Velocity**: Proportional to distance (max speed: 2.0 units/sec) -- **Angular Velocity**: Proportional to angle difference (4.0 ร— angle error) -- **Minimum Following Distance**: 0.5 units (prevents excessive oscillation) - -You can observe the following behavior by: - -1. Spawning turtle2 using the "Spawn Turtle2" button -2. Using WASD keys to move turtle1 around -3. Watching turtle2 chase turtle1 in both the turtlesim window and 3D visualization - -### Demo Control +- **Turtle1**: W/S forward/back, A/D turn (click the 3D view first for keyboard + focus) +- **Camera**: mouse drag to orbit, wheel to zoom, right-drag to pan +- **Frames**: toggle static (red), dynamic (orange), and fixed (purple) markers -- **Start Demo**: Initializes all TF2 broadcasters and systems -- **Reset Demo**: Clears all turtles and transforms, resets to initial state +turtle2 follows turtle1 proportionally (max ~2.0 units/s), correcting heading +and stopping within 0.5 units. -### Frame Visibility - -- **Toggle Static**: Show/hide carrot1_static frame (red sphere, fixed position) -- **Toggle Dynamic**: Show/hide carrot1_dynamic frame (orange sphere, orbits around carrot1_static) -- **Toggle Fixed**: Show/hide carrot1_fixed frame (purple sphere, fixed offset from turtle1) - -**Visual Guide**: - -- **Static Frame**: Red sphere at fixed world coordinates (2.0, 3.0, 0.0) -- **Dynamic Frame**: Orange sphere that moves in a circular pattern around the static frame (2-unit radius) -- **Fixed Frame**: Purple sphere that maintains a constant offset relative to turtle1 -- **Turtle Frames**: Coordinate axes (X=red, Y=green, Z=blue) attached to each turtle - -### 3D Navigation - -- **Mouse Drag**: Rotate camera around scene -- **Mouse Wheel**: Zoom in/out -- **Right Click + Drag**: Pan camera view - -## Troubleshooting - -### Common Issues - -1. **"Cannot connect to ROS2" or "librcl.so: cannot open shared object file"** - - Ensure ROS2 is sourced: `source /opt/ros/$ROS_DISTRO/setup.bash` - - **Critical**: Source ROS2 in the SAME terminal where you run `npm start` - - Check if ROS2 daemon is running: `ros2 daemon status` - - Verify ROS2 installation: `ros2 --version` - -2. **"Turtlesim not responding" or "Failed to spawn turtle2"** - - Verify turtlesim is running: `ros2 run turtlesim turtlesim_node` - - Check available topics: `ros2 topic list` - - Ensure spawn service is available: `ros2 service list | grep spawn` - - Try restarting turtlesim_node if spawn calls fail - -3. **"No transforms detected"** - - Ensure demo is started: Click "Start Demo" button - - Check TF2 tree: `ros2 run tf2_tools view_frames` - -4. **"Dynamic frame not visible when toggling"** - - **Check if the demo is started**: Click "Start Demo" button first to initialize all broadcasters - - **Look for an orange sphere near coordinates (2,3)**: The dynamic frame appears as an orange sphere orbiting around the red static frame - - **Wait for circular motion**: The dynamic frame moves in a 2-unit radius circle, taking about 6 seconds for a full rotation - - **The orange sphere is now bigger**: The dynamic frame has been made 3x larger for better visibility - - **Check the transform list**: The dynamic frame should appear in the left panel's transform list with changing coordinates around (2ยฑ2, 3ยฑ2, 0) - -5. **"3D visualization not loading"** - - Check browser console for WebGL errors - - Ensure hardware acceleration is enabled - - Try restarting the Electron application - -6. **"electron: not found" or native module errors** - - Make sure ROS 2 is sourced so rclnodejs can match its prebuilt binary (`ROS_DISTRO` must be set; prebuilt binaries are provided for Ubuntu) - - Ensure Node.js version is compatible (20.20.2 or higher) - - Try deleting `node_modules` and running `npm install` again; to force a from-source rebuild of rclnodejs set `RCLNODEJS_FORCE_BUILD=1` - -7. **"THREE is not defined" or script loading errors** - - Ensure Three.js is properly installed: `npm install three@0.155.0` - - Check that `node_modules/three/build/three.min.js` exists - - If issues persist, try reinstalling: `rm -rf node_modules && npm install` - -8. **WSL (Windows Subsystem for Linux) specific issues** - - Install audio libraries: `sudo apt install libasound2t64 libasound2-dev` - - Enable X11 forwarding for GUI: Install VcXsrv or similar X server - - Some GUI features may be limited in WSL environment - -### Debugging Commands +## Packaging ```bash -# Check TF2 transforms -ros2 run tf2_tools view_frames - -# Monitor TF2 topic -ros2 topic echo /tf - -# List active nodes -ros2 node list - -# Check turtle poses -ros2 topic echo /turtle1/pose +npm run package # standalone app in out/ +npm run make # zip / deb / rpm installers (needs zip, dpkg, fakeroot) ``` -## Development - -### Project Structure +The target machine must still have ROS 2 installed and sourced โ€” rclnodejs links +dynamically to ROS 2 shared libraries: +```bash +source /opt/ros/$ROS_DISTRO/setup.bash +./out/rclnodejs-turtle-tf2-demo-linux-x64/rclnodejs-turtle-tf2-demo ``` -turtle_tf2/ -โ”œโ”€โ”€ package.json # Node.js dependencies and scripts -โ”œโ”€โ”€ main.js # Electron main process with ROS2 nodes -โ”œโ”€โ”€ renderer.js # Three.js renderer and UI logic -โ”œโ”€โ”€ index.html # Web interface and controls -โ””โ”€โ”€ README.md # This documentation -``` - -### Key Technologies - -- **rclnodejs**: Provides ROS2 integration for Node.js -- **Electron**: Enables desktop application with web technologies -- **Three.js**: Handles 3D graphics and WebGL rendering -- **TF2**: ROS2 transform library for coordinate frame management -### Extending the Demo - -- Add custom coordinate frames by modifying broadcaster nodes -- Implement additional turtle behaviors in the listener logic -- Enhance 3D visualization with trails, grids, or measurement tools -- Create custom transform visualizations for specific use cases - -## Related Resources +## Troubleshooting -- [ROS2 TF2 Tutorials](https://docs.ros.org/en/lyrical/Tutorials/Intermediate/Tf2/Tf2-Main.html) -- [turtle_tf2_py Source](https://github.com/ros/geometry_tutorials/tree/ros2/turtle_tf2_py) -- [Three.js Documentation](https://threejs.org/docs/) -- [rclnodejs GitHub](https://github.com/RobotWebTools/rclnodejs) +- **"librcl.so: cannot open shared object file"** โ€” source ROS 2 in the same + terminal as `npm start`. +- **Can't spawn turtle2** โ€” ensure `turtlesim_node` is running and + `ros2 service list | grep spawn` shows the service. +- **No transforms** โ€” click "Start Demo"; inspect with + `ros2 run tf2_tools view_frames` or `ros2 topic echo /tf`. +- **Stuck on "Initializing ROS2 TF2 Demo..." or blank 3D view** โ€” a WebGL + failure. The app enables software WebGL (SwiftShader) for GPU-less + environments like WSL2; if it still fails, the loading screen shows the error. + Make sure your environment can create a WebGL context. +- **Native module errors** โ€” ensure `ROS_DISTRO` is set so the prebuilt binary + matches; to force a from-source rebuild set `RCLNODEJS_FORCE_BUILD=1`. ## License -Licensed under the Apache License, Version 2.0. See the original rclnodejs LICENSE file for details. +Apache License 2.0, same as rclnodejs. From 693cfb5ef387af498ebb0de480070d6206ecfa0b Mon Sep 17 00:00:00 2001 From: Minggang Wang Date: Thu, 18 Jun 2026 15:27:13 +0800 Subject: [PATCH 3/4] Address comments --- demo/electron/turtle_tf2/renderer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/electron/turtle_tf2/renderer.js b/demo/electron/turtle_tf2/renderer.js index aea6465c..247e6f22 100644 --- a/demo/electron/turtle_tf2/renderer.js +++ b/demo/electron/turtle_tf2/renderer.js @@ -69,8 +69,9 @@ document.addEventListener('DOMContentLoaded', function () { if (loadingScreen) { const loadingText = loadingScreen.querySelector('div:last-child'); if (loadingText) { + const reason = String((err && err.message) || err); loadingText.textContent = - 'Failed to initialize 3D view (WebGL unavailable): ' + err.message; + 'Failed to initialize 3D view (WebGL unavailable): ' + reason; loadingText.style.color = '#ff4444'; } } From ad8fab483307f21e7cde9116d45eb3893787303e Mon Sep 17 00:00:00 2001 From: Minggang Wang Date: Thu, 18 Jun 2026 17:07:53 +0800 Subject: [PATCH 4/4] Fix test failures --- test/test-native-loader.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/test-native-loader.js b/test/test-native-loader.js index 9c745a68..8cc54859 100644 --- a/test/test-native-loader.js +++ b/test/test-native-loader.js @@ -65,12 +65,17 @@ describe('NativeLoader testing', function () { const existsSync = sandbox.stub(fs, 'existsSync').returns(true); assert.strictEqual(loader.customFallbackLoader(), null); - // Verify it checked for the file + // Verify it checked the exact-match prebuild candidate. The loader may + // probe additional paths (e.g. the build/Release mirror target), so find + // the prebuild candidate check rather than relying on call order. assert.ok(existsSync.called); - const args = existsSync.lastCall.args[0]; + const args = existsSync + .getCalls() + .map((call) => call.args[0]) + .find((p) => p.includes('prebuilds') && p.includes('rclnodejs.node')); + assert.ok(args, 'expected a prebuild candidate path to be checked'); assert.ok(args.includes('humble')); assert.ok(args.includes('-node-')); - assert.ok(args.includes('rclnodejs.node')); }); it('customFallbackLoader includes electron runtime in exact match path', function () { @@ -87,10 +92,13 @@ describe('NativeLoader testing', function () { assert.strictEqual(loader.customFallbackLoader(), null); assert.ok(existsSync.called); - const args = existsSync.lastCall.args[0]; + const args = existsSync + .getCalls() + .map((call) => call.args[0]) + .find((p) => p.includes('prebuilds') && p.includes('rclnodejs.node')); + assert.ok(args, 'expected a prebuild candidate path to be checked'); assert.ok(args.includes('humble')); assert.ok(args.includes('-electron-')); - assert.ok(args.includes('rclnodejs.node')); }); it('loadNativeAddon force build triggers rebuild', async function () {