Skip to content

Commit

Permalink
service introspection implementation (#914)
Browse files Browse the repository at this point in the history
* service introspection implementation

* fixes undefined skip() function
  • Loading branch information
wayneparrott authored Aug 28, 2023
1 parent 39ea18a commit b743356
Show file tree
Hide file tree
Showing 20 changed files with 899 additions and 61 deletions.
71 changes: 41 additions & 30 deletions example/client-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,46 @@

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

rclnodejs
.init()
.then(() => {
const node = rclnodejs.createNode('client_example_node');

const client = node.createClient(
'example_interfaces/srv/AddTwoInts',
'add_two_ints'
async function main() {
await rclnodejs.init();
const node = rclnodejs.createNode('client_example_node');
const client = node.createClient(
'example_interfaces/srv/AddTwoInts',
'add_two_ints'
);

if (
rclnodejs.DistroUtils.getDistroId() >
rclnodejs.DistroUtils.getDistroId('humble')
) {
// To view service events use the following command:
// ros2 topic echo "/add_two_ints/_service_event"
client.configureIntrospection(
node.getClock(),
rclnodejs.QoS.profileSystemDefault,
rclnodejs.ServiceIntrospectionStates.METADATA
);
const request = {
a: Math.floor(Math.random() * 100),
b: Math.floor(Math.random() * 100),
};

client.waitForService(1000).then((result) => {
if (!result) {
console.log('Error: service not available');
rclnodejs.shutdown();
return;
}
console.log(`Sending: ${typeof request}`, request);
client.sendRequest(request, (response) => {
console.log(`Result: ${typeof response}`, response);
rclnodejs.shutdown();
});
});

rclnodejs.spin(node);
})
.catch((e) => {
console.log(`Error: ${e}`);
}

const request = {
a: Math.floor(Math.random() * 100),
b: Math.floor(Math.random() * 100),
};

let result = await client.waitForService(1000);
if (!result) {
console.log('Error: service not available');
rclnodejs.shutdown();
return;
}

console.log(`Sending: ${typeof request}`, request);
client.sendRequest(request, (response) => {
console.log(`Result: ${typeof response}`, response);
rclnodejs.shutdown();
});

rclnodejs.spin(node);
}

main();
16 changes: 15 additions & 1 deletion example/service-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rclnodejs
.then(() => {
let node = rclnodejs.createNode('service_example_node');

node.createService(
let service = node.createService(
'example_interfaces/srv/AddTwoInts',
'add_two_ints',
(request, response) => {
Expand All @@ -33,6 +33,20 @@ rclnodejs
}
);

if (
rclnodejs.DistroUtils.getDistroId() >
rclnodejs.DistroUtils.getDistroId('humble')
) {
console.log('Introspection configured');
// To view service events use the following command:
// ros2 topic echo "/add_two_ints/_service_event"
service.configureIntrospection(
node.getClock(),
rclnodejs.QoS.profileSystemDefault,
rclnodejs.ServiceIntrospectionStates.CONTENTS
);
}

rclnodejs.spin(node);
})
.catch((e) => {
Expand Down
35 changes: 35 additions & 0 deletions example/subscription-service-event-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2023 Wayne Parrott. 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 = new rclnodejs.Node('subscription_service_event_example_node');
let count = 0;

node.createSubscription(
'example_interfaces/srv/AddTwoInts_Event',
'/add_two_ints/_service_event',
(event) => {
console.log(`Received event No. ${++count}: `, event);
}
);

node.spin();
}

main();
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const {
getActionServerNamesAndTypesByNode,
getActionNamesAndTypes,
} = require('./lib/action/graph.js');
const ServiceIntrospectionStates = require('./lib/service_introspection.js');

/**
* Get the version of the generator that was used for the currently present interfaces.
Expand Down Expand Up @@ -143,6 +144,9 @@ let rcl = {
/** {@link ROSClock} class */
ROSClock: ROSClock,

/** {@link ServiceIntrospectionStates} */
ServiceIntrospectionStates: ServiceIntrospectionStates,

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

Expand Down
28 changes: 28 additions & 0 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'use strict';

const rclnodejs = require('bindings')('rclnodejs');
const DistroUtils = require('./distro.js');
const Entity = require('./entity.js');
const debug = require('debug')('rclnodejs:client');

Expand Down Expand Up @@ -129,6 +130,33 @@ class Client extends Entity {
get serviceName() {
return this._serviceName;
}

/**
* Configure introspection.
* @param {Clock} clock - Clock to use for service event timestamps
* @param {QoS} qos - QoSProfile for the service event publisher
* @param {ServiceIntrospectionState} introspectionState - State to set introspection to
*/
configureIntrospection(clock, qos, introspectionState) {
if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
console.warn(
'Service introspection is not supported by this versionof ROS 2'
);
return;
}

let type = this.typeClass.type();
rclnodejs.configureServiceIntrospection(
this.handle,
this._nodeHandle,
clock.handle,
type.interfaceName,
type.pkgName,
qos,
introspectionState,
false
);
}
}

module.exports = Client;
5 changes: 4 additions & 1 deletion lib/lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class LifecycleNode extends Node {
'srv_get_state'
);
let service = new Service(
this.handle,
srvHandleObj.handle,
srvHandleObj.name,
loader.loadInterface('lifecycle_msgs/srv/GetState'),
Expand All @@ -321,6 +322,7 @@ class LifecycleNode extends Node {
'srv_get_available_states'
);
service = new Service(
this.handle,
srvHandleObj.handle,
srvHandleObj.name,
loader.loadInterface('lifecycle_msgs/srv/GetAvailableStates'),
Expand All @@ -334,6 +336,7 @@ class LifecycleNode extends Node {
'srv_get_available_transitions'
);
service = new Service(
this.handle,
srvHandleObj.handle,
srvHandleObj.name,
loader.loadInterface('lifecycle_msgs/srv/GetAvailableTransitions'),
Expand All @@ -347,14 +350,14 @@ class LifecycleNode extends Node {
'srv_change_state'
);
service = new Service(
this.handle,
srvHandleObj.handle,
srvHandleObj.name,
loader.loadInterface('lifecycle_msgs/srv/ChangeState'),
this._validateOptions(undefined),
(request, response) => this._onChangeState(request, response)
);
this._services.push(service);

this.syncHandles();
}

Expand Down
2 changes: 1 addition & 1 deletion lib/node_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class NodeOptions {
/**
* Get the automaticallyDeclareParametersFromOverrides.
*
* @returns {boolean} - True indicates that a node shold declare declare parameters from
* @returns {boolean} - True indicates that a node should declare parameters from
* it's parameter-overrides
*/
get automaticallyDeclareParametersFromOverrides() {
Expand Down
40 changes: 38 additions & 2 deletions lib/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'use strict';

const rclnodejs = require('bindings')('rclnodejs');
const DistroUtils = require('./distro.js');
const Entity = require('./entity.js');
const debug = require('debug')('rclnodejs:service');

Expand Down Expand Up @@ -63,8 +64,9 @@ class Response {
*/

class Service extends Entity {
constructor(handle, serviceName, typeClass, options, callback) {
constructor(nodeHandle, handle, serviceName, typeClass, options, callback) {
super(handle, typeClass, options);
this._nodeHandle = nodeHandle;
this._callback = callback;
}

Expand Down Expand Up @@ -95,7 +97,14 @@ class Service extends Entity {
type.pkgName,
options.qos
);
return new Service(handle, serviceName, typeClass, options, callback);
return new Service(
nodeHandle,
handle,
serviceName,
typeClass,
options,
callback
);
}

/**
Expand All @@ -105,6 +114,33 @@ class Service extends Entity {
const fullServiceName = rclnodejs.getServiceName(this._handle); // returns /my_node/myservice
return fullServiceName.split('/').pop();
}

/**
* Configure introspection.
* @param {Clock} clock - Clock to use for service event timestamps
* @param {QoS} qos - QoSProfile for the service event publisher
* @param {ServiceIntrospectionState} introspectionState - State to set introspection to
*/
configureIntrospection(clock, qos, introspectionState) {
if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
console.warn(
'Service introspection is not supported by this versionof ROS 2'
);
return;
}

let type = this.typeClass.type();
rclnodejs.configureServiceIntrospection(
this.handle,
this._nodeHandle,
clock.handle,
type.interfaceName,
type.pkgName,
qos,
introspectionState,
true
);
}
}

module.exports = Service;
30 changes: 30 additions & 0 deletions lib/service_introspection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2023 Wayne Parrott. 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.

/**
* @typedef {number} ServiceIntrospectionState
**/

/**
* Enum for service introspection states.
* @readonly
* @enum {ServiceIntrospectionState}
*/
const ServiceIntrospectionStates = {
OFF: 0,
METADATA: 1,
CONTENTS: 2,
};

module.exports = ServiceIntrospectionStates;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"rimraf": "^3.0.2",
"sinon": "^9.0.2",
"tree-kill": "^1.2.2",
"typescript": "^4.0.3"
"typescript": "^5.0.3"
},
"dependencies": {
"@rclnodejs/ref-array-di": "^1.2.2",
Expand Down
Loading

0 comments on commit b743356

Please sign in to comment.