rclnodejs 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/binding.gyp +5 -0
- package/index.js +26 -3
- package/lib/context.js +4 -2
- package/lib/event_handler.js +474 -0
- package/lib/lifecycle.js +9 -0
- package/lib/lifecycle_publisher.js +2 -2
- package/lib/node.js +163 -28
- package/lib/publisher.js +31 -4
- package/lib/serialization.js +60 -0
- package/lib/subscription.js +48 -4
- package/lib/type_description_service.js +27 -1
- package/package.json +1 -1
- package/rosidl_gen/templates/message.dot +1 -1
- package/scripts/npmjs-readme.md +4 -4
- package/src/addon.cpp +4 -0
- package/src/executor.cpp +3 -4
- package/src/handle_manager.cpp +13 -2
- package/src/handle_manager.h +4 -1
- package/src/macros.h +17 -1
- package/src/rcl_action_client_bindings.cpp +3 -2
- package/src/rcl_action_goal_bindings.cpp +3 -2
- package/src/rcl_action_server_bindings.cpp +7 -5
- package/src/rcl_client_bindings.cpp +4 -3
- package/src/rcl_context_bindings.cpp +29 -19
- package/src/rcl_event_handle_bindings.cpp +294 -0
- package/src/rcl_event_handle_bindings.h +26 -0
- package/src/rcl_guard_condition_bindings.cpp +3 -2
- package/src/rcl_lifecycle_bindings.cpp +18 -4
- package/src/rcl_node_bindings.cpp +111 -3
- package/src/rcl_publisher_bindings.cpp +4 -3
- package/src/rcl_serialization_bindings.cpp +116 -0
- package/src/rcl_serialization_bindings.h +26 -0
- package/src/rcl_service_bindings.cpp +4 -3
- package/src/rcl_subscription_bindings.cpp +3 -2
- package/src/rcl_time_point_bindings.cpp +3 -2
- package/src/rcl_timer_bindings.cpp +8 -6
- package/src/rcl_utilities.cpp +31 -0
- package/src/rcl_utilities.h +7 -0
- package/tsconfig.json +2 -2
- package/types/context.d.ts +3 -2
- package/types/index.d.ts +26 -1
- package/types/lifecycle.d.ts +7 -0
- package/types/node.d.ts +60 -10
package/lib/node.js
CHANGED
|
@@ -41,6 +41,9 @@ const TimeSource = require('./time_source.js');
|
|
|
41
41
|
const Timer = require('./timer.js');
|
|
42
42
|
const TypeDescriptionService = require('./type_description_service.js');
|
|
43
43
|
const Entity = require('./entity.js');
|
|
44
|
+
const { SubscriptionEventCallbacks } = require('../lib/event_handler.js');
|
|
45
|
+
const { PublisherEventCallbacks } = require('../lib/event_handler.js');
|
|
46
|
+
const { validateFullTopicName } = require('./validator.js');
|
|
44
47
|
|
|
45
48
|
// Parameter event publisher constants
|
|
46
49
|
const PARAMETER_EVENT_MSG_TYPE = 'rcl_interfaces/msg/ParameterEvent';
|
|
@@ -64,7 +67,9 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
64
67
|
nodeName,
|
|
65
68
|
namespace = '',
|
|
66
69
|
context = Context.defaultContext(),
|
|
67
|
-
options = NodeOptions.defaultOptions
|
|
70
|
+
options = NodeOptions.defaultOptions,
|
|
71
|
+
args = [],
|
|
72
|
+
useGlobalArguments = true
|
|
68
73
|
) {
|
|
69
74
|
super();
|
|
70
75
|
|
|
@@ -72,7 +77,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
72
77
|
throw new TypeError('Invalid argument.');
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
this._init(nodeName, namespace, options, context);
|
|
80
|
+
this._init(nodeName, namespace, options, context, args, useGlobalArguments);
|
|
76
81
|
debug(
|
|
77
82
|
'Finish initializing node, name = %s and namespace = %s.',
|
|
78
83
|
nodeName,
|
|
@@ -80,8 +85,14 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
80
85
|
);
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
_init(name, namespace, options, context) {
|
|
84
|
-
this.handle = rclnodejs.createNode(
|
|
88
|
+
_init(name, namespace, options, context, args, useGlobalArguments) {
|
|
89
|
+
this.handle = rclnodejs.createNode(
|
|
90
|
+
name,
|
|
91
|
+
namespace,
|
|
92
|
+
context.handle,
|
|
93
|
+
args,
|
|
94
|
+
useGlobalArguments
|
|
95
|
+
);
|
|
85
96
|
Object.defineProperty(this, 'handle', {
|
|
86
97
|
configurable: false,
|
|
87
98
|
writable: false,
|
|
@@ -96,6 +107,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
96
107
|
this._services = [];
|
|
97
108
|
this._timers = [];
|
|
98
109
|
this._guards = [];
|
|
110
|
+
this._events = [];
|
|
99
111
|
this._actionClients = [];
|
|
100
112
|
this._actionServers = [];
|
|
101
113
|
this._rateTimerServer = null;
|
|
@@ -181,6 +193,9 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
181
193
|
let actionServersReady = this._actionServers.filter((actionServer) =>
|
|
182
194
|
handles.includes(actionServer.handle)
|
|
183
195
|
);
|
|
196
|
+
let eventsReady = this._events.filter((event) =>
|
|
197
|
+
handles.includes(event.handle)
|
|
198
|
+
);
|
|
184
199
|
|
|
185
200
|
timersReady.forEach((timer) => {
|
|
186
201
|
if (timer.isReady()) {
|
|
@@ -189,6 +204,10 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
189
204
|
}
|
|
190
205
|
});
|
|
191
206
|
|
|
207
|
+
eventsReady.forEach((event) => {
|
|
208
|
+
event.takeData();
|
|
209
|
+
});
|
|
210
|
+
|
|
192
211
|
for (const subscription of subscriptionsReady) {
|
|
193
212
|
if (subscription.isDestroyed()) continue;
|
|
194
213
|
if (subscription.isRaw) {
|
|
@@ -588,27 +607,39 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
588
607
|
* @param {object} options - The options argument used to parameterize the publisher.
|
|
589
608
|
* @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
|
|
590
609
|
* @param {QoS} options.qos - ROS Middleware "quality of service" settings for the publisher, default: QoS.profileDefault.
|
|
610
|
+
* @param {PublisherEventCallbacks} eventCallbacks - The event callbacks for the publisher.
|
|
591
611
|
* @return {Publisher} - An instance of Publisher.
|
|
592
612
|
*/
|
|
593
|
-
createPublisher(typeClass, topic, options) {
|
|
594
|
-
return this._createPublisher(
|
|
613
|
+
createPublisher(typeClass, topic, options, eventCallbacks) {
|
|
614
|
+
return this._createPublisher(
|
|
615
|
+
typeClass,
|
|
616
|
+
topic,
|
|
617
|
+
options,
|
|
618
|
+
Publisher,
|
|
619
|
+
eventCallbacks
|
|
620
|
+
);
|
|
595
621
|
}
|
|
596
622
|
|
|
597
|
-
_createPublisher(typeClass, topic, options, publisherClass) {
|
|
623
|
+
_createPublisher(typeClass, topic, options, publisherClass, eventCallbacks) {
|
|
598
624
|
if (typeof typeClass === 'string' || typeof typeClass === 'object') {
|
|
599
625
|
typeClass = loader.loadInterface(typeClass);
|
|
600
626
|
}
|
|
601
627
|
options = this._validateOptions(options);
|
|
602
628
|
|
|
603
|
-
if (
|
|
629
|
+
if (
|
|
630
|
+
typeof typeClass !== 'function' ||
|
|
631
|
+
typeof topic !== 'string' ||
|
|
632
|
+
(eventCallbacks && !(eventCallbacks instanceof PublisherEventCallbacks))
|
|
633
|
+
) {
|
|
604
634
|
throw new TypeError('Invalid argument');
|
|
605
635
|
}
|
|
606
636
|
|
|
607
637
|
let publisher = publisherClass.createPublisher(
|
|
608
|
-
this
|
|
638
|
+
this,
|
|
609
639
|
typeClass,
|
|
610
640
|
topic,
|
|
611
|
-
options
|
|
641
|
+
options,
|
|
642
|
+
eventCallbacks
|
|
612
643
|
);
|
|
613
644
|
debug('Finish creating publisher, topic = %s.', topic);
|
|
614
645
|
this._publishers.push(publisher);
|
|
@@ -643,12 +674,13 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
643
674
|
* the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
|
|
644
675
|
* fit with the requested values in the filter_expression (i.e., the number of %n tokens). default: undefined.
|
|
645
676
|
* @param {SubscriptionCallback} callback - The callback to be call when receiving the topic subscribed. The topic will be an instance of null-terminated Buffer when options.isRaw is true.
|
|
677
|
+
* @param {SubscriptionEventCallbacks} eventCallbacks - The event callbacks for the subscription.
|
|
646
678
|
* @return {Subscription} - An instance of Subscription.
|
|
647
679
|
* @throws {ERROR} - May throw an RMW error if content-filter is malformed.
|
|
648
680
|
* @see {@link SubscriptionCallback}
|
|
649
681
|
* @see {@link https://www.omg.org/spec/DDS/1.4/PDF|Content-filter details at DDS 1.4 specification, Annex B}
|
|
650
682
|
*/
|
|
651
|
-
createSubscription(typeClass, topic, options, callback) {
|
|
683
|
+
createSubscription(typeClass, topic, options, callback, eventCallbacks) {
|
|
652
684
|
if (typeof typeClass === 'string' || typeof typeClass === 'object') {
|
|
653
685
|
typeClass = loader.loadInterface(typeClass);
|
|
654
686
|
}
|
|
@@ -662,17 +694,20 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
662
694
|
if (
|
|
663
695
|
typeof typeClass !== 'function' ||
|
|
664
696
|
typeof topic !== 'string' ||
|
|
665
|
-
typeof callback !== 'function'
|
|
697
|
+
typeof callback !== 'function' ||
|
|
698
|
+
(eventCallbacks &&
|
|
699
|
+
!(eventCallbacks instanceof SubscriptionEventCallbacks))
|
|
666
700
|
) {
|
|
667
701
|
throw new TypeError('Invalid argument');
|
|
668
702
|
}
|
|
669
703
|
|
|
670
704
|
let subscription = Subscription.createSubscription(
|
|
671
|
-
this
|
|
705
|
+
this,
|
|
672
706
|
typeClass,
|
|
673
707
|
topic,
|
|
674
708
|
options,
|
|
675
|
-
callback
|
|
709
|
+
callback,
|
|
710
|
+
eventCallbacks
|
|
676
711
|
);
|
|
677
712
|
debug('Finish creating subscription, topic = %s.', topic);
|
|
678
713
|
this._subscriptions.push(subscription);
|
|
@@ -836,6 +871,12 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
836
871
|
if (!(publisher instanceof Publisher)) {
|
|
837
872
|
throw new TypeError('Invalid argument');
|
|
838
873
|
}
|
|
874
|
+
if (publisher.events) {
|
|
875
|
+
publisher.events.forEach((event) => {
|
|
876
|
+
this._destroyEntity(event, this._events);
|
|
877
|
+
});
|
|
878
|
+
publisher.events = [];
|
|
879
|
+
}
|
|
839
880
|
this._destroyEntity(publisher, this._publishers, false);
|
|
840
881
|
}
|
|
841
882
|
|
|
@@ -848,6 +889,13 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
848
889
|
if (!(subscription instanceof Subscription)) {
|
|
849
890
|
throw new TypeError('Invalid argument');
|
|
850
891
|
}
|
|
892
|
+
if (subscription.events) {
|
|
893
|
+
subscription.events.forEach((event) => {
|
|
894
|
+
this._destroyEntity(event, this._events);
|
|
895
|
+
});
|
|
896
|
+
subscription.events = [];
|
|
897
|
+
}
|
|
898
|
+
|
|
851
899
|
this._destroyEntity(subscription, this._subscriptions);
|
|
852
900
|
}
|
|
853
901
|
|
|
@@ -1025,27 +1073,57 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1025
1073
|
}
|
|
1026
1074
|
|
|
1027
1075
|
/**
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1076
|
+
* Return a list of publishers on a given topic.
|
|
1077
|
+
*
|
|
1078
|
+
* The returned parameter is a list of TopicEndpointInfo objects, where each will contain
|
|
1079
|
+
* the node name, node namespace, topic type, topic endpoint's GID, and its QoS profile.
|
|
1080
|
+
*
|
|
1081
|
+
* When the `no_mangle` parameter is `true`, the provided `topic` should be a valid
|
|
1082
|
+
* topic name for the middleware (useful when combining ROS with native middleware (e.g. DDS)
|
|
1083
|
+
* apps). When the `no_mangle` parameter is `false`, the provided `topic` should
|
|
1084
|
+
* follow ROS topic name conventions.
|
|
1085
|
+
*
|
|
1086
|
+
* `topic` may be a relative, private, or fully qualified topic name.
|
|
1087
|
+
* A relative or private topic will be expanded using this node's namespace and name.
|
|
1088
|
+
* The queried `topic` is not remapped.
|
|
1089
|
+
*
|
|
1090
|
+
* @param {string} topic - The topic on which to find the publishers.
|
|
1091
|
+
* @param {boolean} [noDemangle=false] - If `true`, `topic` needs to be a valid middleware topic
|
|
1092
|
+
* name, otherwise it should be a valid ROS topic name. Defaults to `false`.
|
|
1032
1093
|
* @returns {Array} - list of publishers
|
|
1033
1094
|
*/
|
|
1034
|
-
getPublishersInfoByTopic(topic, noDemangle) {
|
|
1035
|
-
return rclnodejs.getPublishersInfoByTopic(
|
|
1095
|
+
getPublishersInfoByTopic(topic, noDemangle = false) {
|
|
1096
|
+
return rclnodejs.getPublishersInfoByTopic(
|
|
1097
|
+
this.handle,
|
|
1098
|
+
this._getValidatedTopic(topic, noDemangle),
|
|
1099
|
+
noDemangle
|
|
1100
|
+
);
|
|
1036
1101
|
}
|
|
1037
1102
|
|
|
1038
1103
|
/**
|
|
1039
|
-
*
|
|
1040
|
-
*
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1104
|
+
* Return a list of subscriptions on a given topic.
|
|
1105
|
+
*
|
|
1106
|
+
* The returned parameter is a list of TopicEndpointInfo objects, where each will contain
|
|
1107
|
+
* the node name, node namespace, topic type, topic endpoint's GID, and its QoS profile.
|
|
1108
|
+
*
|
|
1109
|
+
* When the `no_mangle` parameter is `true`, the provided `topic` should be a valid
|
|
1110
|
+
* topic name for the middleware (useful when combining ROS with native middleware (e.g. DDS)
|
|
1111
|
+
* apps). When the `no_mangle` parameter is `false`, the provided `topic` should
|
|
1112
|
+
* follow ROS topic name conventions.
|
|
1113
|
+
*
|
|
1114
|
+
* `topic` may be a relative, private, or fully qualified topic name.
|
|
1115
|
+
* A relative or private topic will be expanded using this node's namespace and name.
|
|
1116
|
+
* The queried `topic` is not remapped.
|
|
1117
|
+
*
|
|
1118
|
+
* @param {string} topic - The topic on which to find the subscriptions.
|
|
1119
|
+
* @param {boolean} [noDemangle=false] - If `true`, `topic` needs to be a valid middleware topic
|
|
1120
|
+
name, otherwise it should be a valid ROS topic name. Defaults to `false`.
|
|
1043
1121
|
* @returns {Array} - list of subscriptions
|
|
1044
1122
|
*/
|
|
1045
|
-
getSubscriptionsInfoByTopic(topic, noDemangle) {
|
|
1123
|
+
getSubscriptionsInfoByTopic(topic, noDemangle = false) {
|
|
1046
1124
|
return rclnodejs.getSubscriptionsInfoByTopic(
|
|
1047
1125
|
this.handle,
|
|
1048
|
-
topic,
|
|
1126
|
+
this._getValidatedTopic(topic, noDemangle),
|
|
1049
1127
|
noDemangle
|
|
1050
1128
|
);
|
|
1051
1129
|
}
|
|
@@ -1381,7 +1459,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1381
1459
|
* Determine if a parameter descriptor exists.
|
|
1382
1460
|
*
|
|
1383
1461
|
* @param {string} name - The name of a descriptor to for.
|
|
1384
|
-
* @return {boolean} -
|
|
1462
|
+
* @return {boolean} - true if a descriptor has been declared; otherwise false.
|
|
1385
1463
|
*/
|
|
1386
1464
|
hasParameterDescriptor(name) {
|
|
1387
1465
|
return !!this.getParameterDescriptor(name);
|
|
@@ -1637,6 +1715,50 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1637
1715
|
return rclnodejs.getFullyQualifiedName(this.handle);
|
|
1638
1716
|
}
|
|
1639
1717
|
|
|
1718
|
+
/**
|
|
1719
|
+
* Get the RMW implementation identifier
|
|
1720
|
+
* @returns {string} - The RMW implementation identifier.
|
|
1721
|
+
*/
|
|
1722
|
+
getRMWImplementationIdentifier() {
|
|
1723
|
+
return rclnodejs.getRMWImplementationIdentifier();
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* Return a topic name expanded and remapped.
|
|
1728
|
+
* @param {string} topicName - Topic name to be expanded and remapped.
|
|
1729
|
+
* @param {boolean} [onlyExpand=false] - If `true`, remapping rules won't be applied.
|
|
1730
|
+
* @returns {string} - A fully qualified topic name.
|
|
1731
|
+
*/
|
|
1732
|
+
resolveTopicName(topicName, onlyExpand = false) {
|
|
1733
|
+
if (typeof topicName !== 'string') {
|
|
1734
|
+
throw new TypeError('Invalid argument: expected string');
|
|
1735
|
+
}
|
|
1736
|
+
return rclnodejs.resolveName(
|
|
1737
|
+
this.handle,
|
|
1738
|
+
topicName,
|
|
1739
|
+
onlyExpand,
|
|
1740
|
+
/*isService=*/ false
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* Return a service name expanded and remapped.
|
|
1746
|
+
* @param {string} service - Service name to be expanded and remapped.
|
|
1747
|
+
* @param {boolean} [onlyExpand=false] - If `true`, remapping rules won't be applied.
|
|
1748
|
+
* @returns {string} - A fully qualified service name.
|
|
1749
|
+
*/
|
|
1750
|
+
resolveServiceName(service, onlyExpand = false) {
|
|
1751
|
+
if (typeof service !== 'string') {
|
|
1752
|
+
throw new TypeError('Invalid argument: expected string');
|
|
1753
|
+
}
|
|
1754
|
+
return rclnodejs.resolveName(
|
|
1755
|
+
this.handle,
|
|
1756
|
+
service,
|
|
1757
|
+
onlyExpand,
|
|
1758
|
+
/*isService=*/ true
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1640
1762
|
// returns on 1st error or result {successful, reason}
|
|
1641
1763
|
_validateParameters(parameters = [], declareParameterMode = false) {
|
|
1642
1764
|
for (const parameter of parameters) {
|
|
@@ -1761,7 +1883,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1761
1883
|
return result;
|
|
1762
1884
|
});
|
|
1763
1885
|
|
|
1764
|
-
Type.
|
|
1886
|
+
Type.destroyRawROS(message);
|
|
1765
1887
|
}
|
|
1766
1888
|
|
|
1767
1889
|
_addActionClient(actionClient) {
|
|
@@ -1773,6 +1895,19 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1773
1895
|
this._actionServers.push(actionServer);
|
|
1774
1896
|
this.syncHandles();
|
|
1775
1897
|
}
|
|
1898
|
+
|
|
1899
|
+
_getValidatedTopic(topicName, noDemangle) {
|
|
1900
|
+
if (noDemangle) {
|
|
1901
|
+
return topicName;
|
|
1902
|
+
}
|
|
1903
|
+
const fqTopicName = rclnodejs.expandTopicName(
|
|
1904
|
+
topicName,
|
|
1905
|
+
this.name(),
|
|
1906
|
+
this.namespace()
|
|
1907
|
+
);
|
|
1908
|
+
validateFullTopicName(fqTopicName);
|
|
1909
|
+
return rclnodejs.remapTopicName(this.handle, fqTopicName);
|
|
1910
|
+
}
|
|
1776
1911
|
}
|
|
1777
1912
|
|
|
1778
1913
|
/**
|
package/lib/publisher.js
CHANGED
|
@@ -24,8 +24,12 @@ const Entity = require('./entity.js');
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
class Publisher extends Entity {
|
|
27
|
-
constructor(handle, typeClass, topic, options) {
|
|
27
|
+
constructor(handle, typeClass, topic, options, node, eventCallbacks) {
|
|
28
28
|
super(handle, typeClass, options);
|
|
29
|
+
if (node && eventCallbacks) {
|
|
30
|
+
this._events = eventCallbacks.createEventHandlers(this.handle);
|
|
31
|
+
node._events.push(...this._events);
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/**
|
|
@@ -60,17 +64,24 @@ class Publisher extends Entity {
|
|
|
60
64
|
debug(`Message of topic ${this.topic} has been published.`);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
static createPublisher(
|
|
67
|
+
static createPublisher(node, typeClass, topic, options, eventCallbacks) {
|
|
64
68
|
let type = typeClass.type();
|
|
65
69
|
let handle = rclnodejs.createPublisher(
|
|
66
|
-
|
|
70
|
+
node.handle,
|
|
67
71
|
type.pkgName,
|
|
68
72
|
type.subFolder,
|
|
69
73
|
type.interfaceName,
|
|
70
74
|
topic,
|
|
71
75
|
options.qos
|
|
72
76
|
);
|
|
73
|
-
return new Publisher(
|
|
77
|
+
return new Publisher(
|
|
78
|
+
handle,
|
|
79
|
+
typeClass,
|
|
80
|
+
topic,
|
|
81
|
+
options,
|
|
82
|
+
node,
|
|
83
|
+
eventCallbacks
|
|
84
|
+
);
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
/**
|
|
@@ -99,6 +110,22 @@ class Publisher extends Entity {
|
|
|
99
110
|
waitForAllAcked(timeout) {
|
|
100
111
|
return rclnodejs.waitForAllAcked(this._handle, timeout);
|
|
101
112
|
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the event handlers for this publisher.
|
|
116
|
+
* @returns {Array} The array of event handlers for this publisher.
|
|
117
|
+
*/
|
|
118
|
+
get events() {
|
|
119
|
+
return this._events;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Set the event handlers for this publisher.
|
|
124
|
+
* @param {Array} events - The array of event handlers to be set for this publisher.
|
|
125
|
+
*/
|
|
126
|
+
set events(events) {
|
|
127
|
+
this._events = events;
|
|
128
|
+
}
|
|
102
129
|
}
|
|
103
130
|
|
|
104
131
|
module.exports = Publisher;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright (c) 2025, The Robot Web Tools Contributors
|
|
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('bindings')('rclnodejs');
|
|
18
|
+
|
|
19
|
+
class Serialization {
|
|
20
|
+
/**
|
|
21
|
+
* Serialize a message to a buffer.
|
|
22
|
+
* @param {object} message - The message to serialize.
|
|
23
|
+
* @param {function} typeClass - The class of the message type to serialize.
|
|
24
|
+
* @return {Buffer} The serialized message as a Buffer.
|
|
25
|
+
*/
|
|
26
|
+
static serializeMessage(message, typeClass) {
|
|
27
|
+
if (!(message instanceof typeClass)) {
|
|
28
|
+
throw new TypeError('Message must be a valid ros2 message type');
|
|
29
|
+
}
|
|
30
|
+
return rclnodejs.serialize(
|
|
31
|
+
typeClass.type().pkgName,
|
|
32
|
+
typeClass.type().subFolder,
|
|
33
|
+
typeClass.type().interfaceName,
|
|
34
|
+
message.serialize()
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Deserialize a message from a buffer.
|
|
40
|
+
* @param {Buffer} buffer - The buffer containing the serialized message.
|
|
41
|
+
* @param {function} typeClass - The class of the message type to deserialize into.
|
|
42
|
+
* @return {object} The deserialized message object.
|
|
43
|
+
*/
|
|
44
|
+
static deserializeMessage(buffer, typeClass) {
|
|
45
|
+
if (!(buffer instanceof Buffer)) {
|
|
46
|
+
throw new TypeError('Buffer is required for deserialization');
|
|
47
|
+
}
|
|
48
|
+
const rosMsg = new typeClass();
|
|
49
|
+
rclnodejs.deserialize(
|
|
50
|
+
typeClass.type().pkgName,
|
|
51
|
+
typeClass.type().subFolder,
|
|
52
|
+
typeClass.type().interfaceName,
|
|
53
|
+
buffer,
|
|
54
|
+
rosMsg.toRawROS()
|
|
55
|
+
);
|
|
56
|
+
return rosMsg;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = Serialization;
|
package/lib/subscription.js
CHANGED
|
@@ -29,11 +29,24 @@ const debug = require('debug')('rclnodejs:subscription');
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
class Subscription extends Entity {
|
|
32
|
-
constructor(
|
|
32
|
+
constructor(
|
|
33
|
+
handle,
|
|
34
|
+
typeClass,
|
|
35
|
+
topic,
|
|
36
|
+
options,
|
|
37
|
+
callback,
|
|
38
|
+
node,
|
|
39
|
+
eventCallbacks
|
|
40
|
+
) {
|
|
33
41
|
super(handle, typeClass, options);
|
|
34
42
|
this._topic = topic;
|
|
35
43
|
this._callback = callback;
|
|
36
44
|
this._isRaw = options.isRaw || false;
|
|
45
|
+
|
|
46
|
+
if (node && eventCallbacks) {
|
|
47
|
+
this._events = eventCallbacks.createEventHandlers(this.handle);
|
|
48
|
+
node._events.push(...this._events);
|
|
49
|
+
}
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
processResponse(msg) {
|
|
@@ -45,7 +58,14 @@ class Subscription extends Entity {
|
|
|
45
58
|
}
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
static createSubscription(
|
|
61
|
+
static createSubscription(
|
|
62
|
+
node,
|
|
63
|
+
typeClass,
|
|
64
|
+
topic,
|
|
65
|
+
options,
|
|
66
|
+
callback,
|
|
67
|
+
eventCallbacks
|
|
68
|
+
) {
|
|
49
69
|
let type = typeClass.type();
|
|
50
70
|
|
|
51
71
|
// convert contentFilter.parameters to a string[]
|
|
@@ -56,14 +76,22 @@ class Subscription extends Entity {
|
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
let handle = rclnodejs.createSubscription(
|
|
59
|
-
|
|
79
|
+
node.handle,
|
|
60
80
|
type.pkgName,
|
|
61
81
|
type.subFolder,
|
|
62
82
|
type.interfaceName,
|
|
63
83
|
topic,
|
|
64
84
|
options
|
|
65
85
|
);
|
|
66
|
-
return new Subscription(
|
|
86
|
+
return new Subscription(
|
|
87
|
+
handle,
|
|
88
|
+
typeClass,
|
|
89
|
+
topic,
|
|
90
|
+
options,
|
|
91
|
+
callback,
|
|
92
|
+
node,
|
|
93
|
+
eventCallbacks
|
|
94
|
+
);
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
/**
|
|
@@ -124,6 +152,22 @@ class Subscription extends Entity {
|
|
|
124
152
|
get publisherCount() {
|
|
125
153
|
return rclnodejs.getPublisherCount(this._handle);
|
|
126
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get the event handlers for this subscription.
|
|
158
|
+
* @returns {Array} The array of event handlers for this subscription.
|
|
159
|
+
*/
|
|
160
|
+
get events() {
|
|
161
|
+
return this._events;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Set the event handlers for this subscription.
|
|
166
|
+
* @param {Array} events - The array of event handlers for this subscription.
|
|
167
|
+
*/
|
|
168
|
+
set events(events) {
|
|
169
|
+
this._events = events;
|
|
170
|
+
}
|
|
127
171
|
}
|
|
128
172
|
|
|
129
173
|
module.exports = Subscription;
|
|
@@ -18,6 +18,12 @@ const loader = require('./interface_loader.js');
|
|
|
18
18
|
const rclnodejs = require('bindings')('rclnodejs');
|
|
19
19
|
const Service = require('./service.js');
|
|
20
20
|
|
|
21
|
+
const {
|
|
22
|
+
ParameterType,
|
|
23
|
+
Parameter,
|
|
24
|
+
ParameterDescriptor,
|
|
25
|
+
} = require('../lib/parameter.js');
|
|
26
|
+
|
|
21
27
|
// This class is used to create a TypeDescriptionService which can be used to
|
|
22
28
|
// retrieve information about types used by the node’s publishers, subscribers,
|
|
23
29
|
// services or actions.
|
|
@@ -32,10 +38,30 @@ class TypeDescriptionService {
|
|
|
32
38
|
'type_description_interfaces/srv/GetTypeDescription'
|
|
33
39
|
);
|
|
34
40
|
this._typeDescriptionService = null;
|
|
41
|
+
|
|
42
|
+
this._enabled = false;
|
|
43
|
+
const startTypeDescriptionServiceParam = 'start_type_description_service';
|
|
44
|
+
if (!node.hasParameter(startTypeDescriptionServiceParam)) {
|
|
45
|
+
node.declareParameter(
|
|
46
|
+
new Parameter(
|
|
47
|
+
startTypeDescriptionServiceParam,
|
|
48
|
+
ParameterType.PARAMETER_BOOL,
|
|
49
|
+
true
|
|
50
|
+
),
|
|
51
|
+
new ParameterDescriptor(
|
|
52
|
+
startTypeDescriptionServiceParam,
|
|
53
|
+
ParameterType.PARAMETER_BOOL,
|
|
54
|
+
'If enabled, start the ~/get_type_description service.',
|
|
55
|
+
true
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const param = node.getParameter(startTypeDescriptionServiceParam);
|
|
60
|
+
this._enabled = param.value;
|
|
35
61
|
}
|
|
36
62
|
|
|
37
63
|
start() {
|
|
38
|
-
if (this._typeDescriptionService) {
|
|
64
|
+
if (!this._enabled || this._typeDescriptionService) {
|
|
39
65
|
return;
|
|
40
66
|
}
|
|
41
67
|
|
package/package.json
CHANGED
package/scripts/npmjs-readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# rclnodejs ](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml?query=branch%3Adevelop)[](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-arm64-build-and-test.yml?query=branch%3Adevelop)
|
|
2
2
|
|
|
3
3
|
`rclnodejs` is a Node.js client for the Robot Operating System (ROS 2). It provides a simple and easy JavaScript API for ROS 2 programming. TypeScript declarations are included to support use of rclnodejs in TypeScript projects.
|
|
4
4
|
|
|
@@ -22,8 +22,8 @@ rclnodejs.init().then(() => {
|
|
|
22
22
|
|
|
23
23
|
**ROS 2 SDK**
|
|
24
24
|
|
|
25
|
-
- See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/
|
|
26
|
-
- **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/
|
|
25
|
+
- See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/kilted/Installation.html) for details.
|
|
26
|
+
- **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/kilted/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.htmls)**
|
|
27
27
|
|
|
28
28
|
## Install rclnodejs
|
|
29
29
|
|
|
@@ -45,7 +45,7 @@ npm i rclnodejs@x.y.z
|
|
|
45
45
|
|
|
46
46
|
| RCLNODEJS Version | Compatible ROS 2 LTS |
|
|
47
47
|
| :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
48
|
-
| latest version (currently [v1.
|
|
48
|
+
| latest version (currently [v1.3.0](https://github.com/RobotWebTools/rclnodejs/tree/1.3.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
|
|
49
49
|
|
|
50
50
|
## Documentation
|
|
51
51
|
|
package/src/addon.cpp
CHANGED
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
#include "rcl_names_bindings.h"
|
|
31
31
|
#include "rcl_node_bindings.h"
|
|
32
32
|
#include "rcl_publisher_bindings.h"
|
|
33
|
+
#include "rcl_serialization_bindings.h"
|
|
33
34
|
#include "rcl_service_bindings.h"
|
|
34
35
|
#include "rcl_subscription_bindings.h"
|
|
35
36
|
#include "rcl_time_point_bindings.h"
|
|
36
37
|
#include "rcl_timer_bindings.h"
|
|
37
38
|
#if ROS_VERSION > 2205 // ROS2 > Humble
|
|
39
|
+
#include "rcl_event_handle_bindings.h"
|
|
38
40
|
#include "rcl_type_description_service_bindings.h"
|
|
39
41
|
#endif
|
|
40
42
|
#include "rcl_utilities.h"
|
|
@@ -84,8 +86,10 @@ Napi::Object InitModule(Napi::Env env, Napi::Object exports) {
|
|
|
84
86
|
rclnodejs::InitTimerBindings(env, exports);
|
|
85
87
|
#if ROS_VERSION > 2205 // ROS2 > Humble
|
|
86
88
|
rclnodejs::InitTypeDescriptionServiceBindings(env, exports);
|
|
89
|
+
rclnodejs::InitEventHandleBindings(env, exports);
|
|
87
90
|
#endif
|
|
88
91
|
rclnodejs::InitLifecycleBindings(env, exports);
|
|
92
|
+
rclnodejs::InitSerializationBindings(env, exports);
|
|
89
93
|
rclnodejs::ShadowNode::Init(env, exports);
|
|
90
94
|
rclnodejs::RclHandle::Init(env, exports);
|
|
91
95
|
|