Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- **[Mahmoud Alghalayini](https://github.com/mahmoud-ghalayini)**
- JSON safe serialization improvements
- Promise-based service calls implementation
- Add ParameterClient for external parameter access

- **[Martins Mozeiko](https://github.com/martins-mozeiko)**
- QoS new/delete fix
Expand Down
124 changes: 119 additions & 5 deletions example/parameter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Parameters are ideal for:

## Parameter Examples

### 1. Parameter Declaration (`parameter-declaration-example.js`)
### Local Parameters (On Current Node)

#### 1. Parameter Declaration (`parameter-declaration-example.js`)

**Purpose**: Demonstrates how to declare and use parameters in a ROS 2 node.

Expand All @@ -41,7 +43,7 @@ Parameters are ideal for:
- **Default Value**: `"hello world"`
- **Run Command**: `node parameter-declaration-example.js`

### 2. Parameter Override (`parameter-override-example.js`)
#### 2. Parameter Override (`parameter-override-example.js`)

**Purpose**: Shows how to override parameter values using command-line arguments.

Expand All @@ -63,6 +65,74 @@ Parameters are ideal for:
- **Override Value**: `"hello ros2"` (via command line)
- **Run Command**: `node parameter-override-example.js`

### Remote Parameter Access (On Other Nodes)

#### 3. ParameterClient Basic (`parameter-client-basic-example.js`)

**Purpose**: Demonstrates accessing and modifying parameters on a remote node using `ParameterClient`.

- **Functionality**:
- Creates a client node and ParameterClient
- Connects to turtlesim node's parameters
- Lists all available parameters
- Gets and sets individual parameters
- Retrieves multiple parameters at once
- **Features**:
- Service availability checking with `waitForService()`
- Parameter listing with `listParameters()`
- Single parameter get/set operations
- Batch parameter retrieval
- Automatic type inference for parameter values
- **Target Node**: `turtlesim` (run: `ros2 run turtlesim turtlesim_node`)
- **Run Command**: `node parameter-client-basic-example.js`

#### 4. ParameterClient Advanced (`parameter-client-advanced-example.js`)

**Purpose**: Comprehensive example showing all ParameterClient features and capabilities.

- **Functionality**:
- Creates target and client nodes
- Declares parameters on target node
- Demonstrates all ParameterClient operations:
- List parameters
- Get single/multiple parameters
- Set single/multiple parameters
- Describe parameters (get metadata)
- Get parameter types
- Timeout handling
- Request cancellation with AbortController
- **Features**:
- Complete API coverage
- Error handling examples
- Timeout and cancellation patterns
- Automatic BigInt conversion for integers
- Type inference demonstrations
- Lifecycle management
- **Run Command**: `node parameter-client-advanced-example.js`

**ParameterClient Key Features**:

- **Remote Access**: Query/modify parameters on any node
- **Async/Await API**: Modern promise-based interface
- **Type-Safe**: Automatic type inference and BigInt conversion
- **Timeout Support**: Per-request or default timeout settings
- **Cancellation**: AbortController integration for request cancellation
- **Lifecycle Management**: Automatic cleanup when parent node is destroyed

**Public API**:

- `remoteNodeName` (getter) - Get the target node name
- `waitForService(timeout?)` - Wait for remote services to be available
- `getParameter(name, options?)` - Get a single parameter
- `getParameters(names, options?)` - Get multiple parameters
- `setParameter(name, value, options?)` - Set a single parameter
- `setParameters(parameters, options?)` - Set multiple parameters
- `listParameters(options?)` - List all parameters with optional filtering
- `describeParameters(names, options?)` - Get parameter descriptors
- `getParameterTypes(names, options?)` - Get parameter types
- `isDestroyed()` - Check if client has been destroyed
- `destroy()` - Clean up and destroy the client

## How to Run the Examples

### Prerequisites
Expand Down Expand Up @@ -128,6 +198,53 @@ ParameterDescriptor {

Notice how the value changed from "hello world" to "hello ros2" due to the command-line override.

### Running ParameterClient Basic Example

First, in one terminal, run turtlesim:

```bash
ros2 run turtlesim turtlesim_node
```

Then in another terminal:

```bash
cd example/parameter
node parameter-client-basic-example.js
```

**Expected Output**:

```
Current background_b: 255n
Updated background_b: 200n
```

### Running ParameterClient Advanced Example

```bash
cd example/parameter
node parameter-client-advanced-example.js
```

**Expected Output**:

```
Available parameters: [ 'use_sim_time', 'max_speed', 'debug_mode', 'retry_count' ]
max_speed = 10.5
Retrieved parameters: [ 'max_speed', 'debug_mode', 'retry_count' ]
max_speed descriptor: {
name: 'max_speed',
type: 3,
description: 'Maximum speed in m/s',
additional_constraints: '',
read_only: false,
dynamic_typing: false,
floating_point_range: [],
integer_range: []
}
```

## Using ROS 2 Parameter Tools

You can interact with these examples using standard ROS 2 parameter tools:
Expand Down Expand Up @@ -276,19 +393,16 @@ const doubleParam = new Parameter(
### Common Issues

1. **Parameter Not Found**:

- Ensure parameter is declared before accessing
- Check parameter name spelling
- Verify node has been properly initialized

2. **Type Mismatch**:

- Ensure parameter type matches declaration
- Check ParameterType constants are correct
- Verify value type matches parameter type

3. **Override Not Working**:

- Check command-line syntax: `--ros-args -p node_name:param_name:=value`
- Ensure node name matches exactly
- Verify rclnodejs.init() is called with argv
Expand Down
101 changes: 101 additions & 0 deletions example/parameter/parameter-client-advanced-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const rclnodejs = require('../../index.js');

const { ParameterType, Parameter, ParameterDescriptor } = rclnodejs;

async function main() {
await rclnodejs.init();

const targetNode = rclnodejs.createNode('target_node');
const clientNode = rclnodejs.createNode('client_node');

targetNode.declareParameter(
new Parameter('max_speed', ParameterType.PARAMETER_DOUBLE, 10.5),
new ParameterDescriptor(
'max_speed',
ParameterType.PARAMETER_DOUBLE,
'Maximum speed in m/s'
)
);

targetNode.declareParameter(
new Parameter('debug_mode', ParameterType.PARAMETER_BOOL, false),
new ParameterDescriptor(
'debug_mode',
ParameterType.PARAMETER_BOOL,
'Enable debug logging'
)
);

targetNode.declareParameter(
new Parameter('retry_count', ParameterType.PARAMETER_INTEGER, BigInt(3)),
new ParameterDescriptor(
'retry_count',
ParameterType.PARAMETER_INTEGER,
'Number of retries'
)
);

rclnodejs.spin(targetNode);
rclnodejs.spin(clientNode);

const paramClient = clientNode.createParameterClient('target_node');

try {
await paramClient.waitForService(10000);

const { names } = await paramClient.listParameters();
console.log('Available parameters:', names);

const maxSpeed = await paramClient.getParameter('max_speed');
console.log(`max_speed = ${maxSpeed.value}`);

const params = await paramClient.getParameters([
'max_speed',
'debug_mode',
'retry_count',
]);
console.log(
'Retrieved parameters:',
params.map((p) => p.name)
);

await paramClient.setParameter('max_speed', 15.0);
await paramClient.setParameters([
{ name: 'debug_mode', value: true },
{ name: 'retry_count', value: 5 },
]);

const descriptors = await paramClient.describeParameters(['max_speed']);
console.log(`max_speed descriptor:`, descriptors[0]);

try {
await paramClient.getParameter('max_speed', { timeout: 1 });
} catch (error) {
// Expected timeout with 1ms
}
} catch (error) {
console.error('Error:', error.message);
} finally {
clientNode.destroy();
targetNode.destroy();
rclnodejs.shutdown();
}
}

main();
51 changes: 51 additions & 0 deletions example/parameter/parameter-client-basic-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const rclnodejs = require('../../index.js');

async function main() {
await rclnodejs.init();

const node = rclnodejs.createNode('param_client_node');

const paramClient = node.createParameterClient('turtlesim', {
timeout: 5000,
});

try {
const available = await paramClient.waitForService(10000);

if (!available) {
console.log('Turtlesim node not available. Please run:');
console.log(' ros2 run turtlesim turtlesim_node');
return;
}

const param = await paramClient.getParameter('background_b');
console.log(`Current background_b: ${param.value}`);

await paramClient.setParameter('background_b', 200);
const updated = await paramClient.getParameter('background_b');
console.log(`Updated background_b: ${updated.value}`);
} catch (error) {
console.error('Error:', error.message);
} finally {
node.destroy();
rclnodejs.shutdown();
}
}

main();
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const {
serializeMessage,
deserializeMessage,
} = require('./lib/serialization.js');
const ParameterClient = require('./lib/parameter_client.js');
const { spawn } = require('child_process');

/**
Expand Down Expand Up @@ -217,6 +218,9 @@ let rcl = {
/** {@link ParameterType} */
ParameterType: ParameterType,

/** {@link ParameterClient} class */
ParameterClient: ParameterClient,

/** {@link QoS} class */
QoS: QoS,

Expand Down
Loading
Loading