Skip to content

Commit 9cb7abb

Browse files
feat: add ParameterClient for external parameter access
1 parent 7c306aa commit 9cb7abb

File tree

12 files changed

+1757
-25
lines changed

12 files changed

+1757
-25
lines changed

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
- **[Mahmoud Alghalayini](https://github.com/mahmoud-ghalayini)**
4242
- JSON safe serialization improvements
4343
- Promise-based service calls implementation
44+
- Add ParameterClient for external parameter access
4445

4546
- **[Martins Mozeiko](https://github.com/martins-mozeiko)**
4647
- QoS new/delete fix

example/parameter/README.md

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ Parameters are ideal for:
2020

2121
## Parameter Examples
2222

23-
### 1. Parameter Declaration (`parameter-declaration-example.js`)
23+
### Local Parameters (On Current Node)
24+
25+
#### 1. Parameter Declaration (`parameter-declaration-example.js`)
2426

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

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

44-
### 2. Parameter Override (`parameter-override-example.js`)
46+
#### 2. Parameter Override (`parameter-override-example.js`)
4547

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

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

68+
### Remote Parameter Access (On Other Nodes)
69+
70+
#### 3. ParameterClient Basic (`parameter-client-basic-example.js`)
71+
72+
**Purpose**: Demonstrates accessing and modifying parameters on a remote node using `ParameterClient`.
73+
74+
- **Functionality**:
75+
- Creates a client node and ParameterClient
76+
- Connects to turtlesim node's parameters
77+
- Lists all available parameters
78+
- Gets and sets individual parameters
79+
- Retrieves multiple parameters at once
80+
- **Features**:
81+
- Service availability checking with `waitForService()`
82+
- Parameter listing with `listParameters()`
83+
- Single parameter get/set operations
84+
- Batch parameter retrieval
85+
- Automatic type inference for parameter values
86+
- **Target Node**: `turtlesim` (run: `ros2 run turtlesim turtlesim_node`)
87+
- **Run Command**: `node parameter-client-basic-example.js`
88+
89+
#### 4. ParameterClient Advanced (`parameter-client-advanced-example.js`)
90+
91+
**Purpose**: Comprehensive example showing all ParameterClient features and capabilities.
92+
93+
- **Functionality**:
94+
- Creates target and client nodes
95+
- Declares parameters on target node
96+
- Demonstrates all ParameterClient operations:
97+
- List parameters
98+
- Get single/multiple parameters
99+
- Set single/multiple parameters
100+
- Describe parameters (get metadata)
101+
- Get parameter types
102+
- Timeout handling
103+
- Request cancellation with AbortController
104+
- **Features**:
105+
- Complete API coverage
106+
- Error handling examples
107+
- Timeout and cancellation patterns
108+
- Automatic BigInt conversion for integers
109+
- Type inference demonstrations
110+
- Lifecycle management
111+
- **Run Command**: `node parameter-client-advanced-example.js`
112+
113+
**ParameterClient Key Features**:
114+
115+
- **Remote Access**: Query/modify parameters on any node
116+
- **Async/Await API**: Modern promise-based interface
117+
- **Type-Safe**: Automatic type inference and BigInt conversion
118+
- **Timeout Support**: Per-request or default timeout settings
119+
- **Cancellation**: AbortController integration for request cancellation
120+
- **Lifecycle Management**: Automatic cleanup when parent node is destroyed
121+
122+
**Public API**:
123+
124+
- `remoteNodeName` (getter) - Get the target node name
125+
- `waitForService(timeout?)` - Wait for remote services to be available
126+
- `getParameter(name, options?)` - Get a single parameter
127+
- `getParameters(names, options?)` - Get multiple parameters
128+
- `setParameter(name, value, options?)` - Set a single parameter
129+
- `setParameters(parameters, options?)` - Set multiple parameters
130+
- `listParameters(options?)` - List all parameters with optional filtering
131+
- `describeParameters(names, options?)` - Get parameter descriptors
132+
- `getParameterTypes(names, options?)` - Get parameter types
133+
- `isDestroyed()` - Check if client has been destroyed
134+
- `destroy()` - Clean up and destroy the client
135+
66136
## How to Run the Examples
67137

68138
### Prerequisites
@@ -128,6 +198,53 @@ ParameterDescriptor {
128198

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

201+
### Running ParameterClient Basic Example
202+
203+
First, in one terminal, run turtlesim:
204+
205+
```bash
206+
ros2 run turtlesim turtlesim_node
207+
```
208+
209+
Then in another terminal:
210+
211+
```bash
212+
cd example/parameter
213+
node parameter-client-basic-example.js
214+
```
215+
216+
**Expected Output**:
217+
218+
```
219+
Current background_b: 255n
220+
Updated background_b: 200n
221+
```
222+
223+
### Running ParameterClient Advanced Example
224+
225+
```bash
226+
cd example/parameter
227+
node parameter-client-advanced-example.js
228+
```
229+
230+
**Expected Output**:
231+
232+
```
233+
Available parameters: [ 'use_sim_time', 'max_speed', 'debug_mode', 'retry_count' ]
234+
max_speed = 10.5
235+
Retrieved parameters: [ 'max_speed', 'debug_mode', 'retry_count' ]
236+
max_speed descriptor: {
237+
name: 'max_speed',
238+
type: 3,
239+
description: 'Maximum speed in m/s',
240+
additional_constraints: '',
241+
read_only: false,
242+
dynamic_typing: false,
243+
floating_point_range: [],
244+
integer_range: []
245+
}
246+
```
247+
131248
## Using ROS 2 Parameter Tools
132249

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

278395
1. **Parameter Not Found**:
279-
280396
- Ensure parameter is declared before accessing
281397
- Check parameter name spelling
282398
- Verify node has been properly initialized
283399

284400
2. **Type Mismatch**:
285-
286401
- Ensure parameter type matches declaration
287402
- Check ParameterType constants are correct
288403
- Verify value type matches parameter type
289404

290405
3. **Override Not Working**:
291-
292406
- Check command-line syntax: `--ros-args -p node_name:param_name:=value`
293407
- Ensure node name matches exactly
294408
- Verify rclnodejs.init() is called with argv
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const rclnodejs = require('../../index.js');
18+
19+
const { ParameterType, Parameter, ParameterDescriptor } = rclnodejs;
20+
21+
async function main() {
22+
await rclnodejs.init();
23+
24+
const targetNode = rclnodejs.createNode('target_node');
25+
const clientNode = rclnodejs.createNode('client_node');
26+
27+
targetNode.declareParameter(
28+
new Parameter('max_speed', ParameterType.PARAMETER_DOUBLE, 10.5),
29+
new ParameterDescriptor(
30+
'max_speed',
31+
ParameterType.PARAMETER_DOUBLE,
32+
'Maximum speed in m/s'
33+
)
34+
);
35+
36+
targetNode.declareParameter(
37+
new Parameter('debug_mode', ParameterType.PARAMETER_BOOL, false),
38+
new ParameterDescriptor(
39+
'debug_mode',
40+
ParameterType.PARAMETER_BOOL,
41+
'Enable debug logging'
42+
)
43+
);
44+
45+
targetNode.declareParameter(
46+
new Parameter('retry_count', ParameterType.PARAMETER_INTEGER, BigInt(3)),
47+
new ParameterDescriptor(
48+
'retry_count',
49+
ParameterType.PARAMETER_INTEGER,
50+
'Number of retries'
51+
)
52+
);
53+
54+
rclnodejs.spin(targetNode);
55+
rclnodejs.spin(clientNode);
56+
57+
const paramClient = clientNode.createParameterClient('target_node');
58+
59+
try {
60+
await paramClient.waitForService(10000);
61+
62+
const { names } = await paramClient.listParameters();
63+
console.log('Available parameters:', names);
64+
65+
const maxSpeed = await paramClient.getParameter('max_speed');
66+
console.log(`max_speed = ${maxSpeed.value}`);
67+
68+
const params = await paramClient.getParameters([
69+
'max_speed',
70+
'debug_mode',
71+
'retry_count',
72+
]);
73+
console.log(
74+
'Retrieved parameters:',
75+
params.map((p) => p.name)
76+
);
77+
78+
await paramClient.setParameter('max_speed', 15.0);
79+
await paramClient.setParameters([
80+
{ name: 'debug_mode', value: true },
81+
{ name: 'retry_count', value: 5 },
82+
]);
83+
84+
const descriptors = await paramClient.describeParameters(['max_speed']);
85+
console.log(`max_speed descriptor:`, descriptors[0]);
86+
87+
try {
88+
await paramClient.getParameter('max_speed', { timeout: 1 });
89+
} catch (error) {
90+
// Expected timeout with 1ms
91+
}
92+
} catch (error) {
93+
console.error('Error:', error.message);
94+
} finally {
95+
clientNode.destroy();
96+
targetNode.destroy();
97+
rclnodejs.shutdown();
98+
}
99+
}
100+
101+
main();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const rclnodejs = require('../../index.js');
18+
19+
async function main() {
20+
await rclnodejs.init();
21+
22+
const node = rclnodejs.createNode('param_client_node');
23+
24+
const paramClient = node.createParameterClient('turtlesim', {
25+
timeout: 5000,
26+
});
27+
28+
try {
29+
const available = await paramClient.waitForService(10000);
30+
31+
if (!available) {
32+
console.log('Turtlesim node not available. Please run:');
33+
console.log(' ros2 run turtlesim turtlesim_node');
34+
return;
35+
}
36+
37+
const param = await paramClient.getParameter('background_b');
38+
console.log(`Current background_b: ${param.value}`);
39+
40+
await paramClient.setParameter('background_b', 200);
41+
const updated = await paramClient.getParameter('background_b');
42+
console.log(`Updated background_b: ${updated.value}`);
43+
} catch (error) {
44+
console.error('Error:', error.message);
45+
} finally {
46+
node.destroy();
47+
rclnodejs.shutdown();
48+
}
49+
}
50+
51+
main();

index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const {
5858
serializeMessage,
5959
deserializeMessage,
6060
} = require('./lib/serialization.js');
61+
const ParameterClient = require('./lib/parameter_client.js');
6162
const { spawn } = require('child_process');
6263

6364
/**
@@ -217,6 +218,9 @@ let rcl = {
217218
/** {@link ParameterType} */
218219
ParameterType: ParameterType,
219220

221+
/** {@link ParameterClient} class */
222+
ParameterClient: ParameterClient,
223+
220224
/** {@link QoS} class */
221225
QoS: QoS,
222226

0 commit comments

Comments
 (0)