Source: lib/parameter_service.js

  1. /* eslint-disable max-depth */
  2. // Copyright (c) 2020 Wayne Parrott. All rights reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. 'use strict';
  16. const rclnodejs = require('bindings')('rclnodejs');
  17. const { Parameter, PARAMETER_SEPARATOR } = require('./parameter.js');
  18. /**
  19. * Implements the ros2 service interfaces for interacting with a node's parameters.
  20. *
  21. * The interfaces implemented are:
  22. * rcl_interfaces/srv/ListParameters
  23. * rcl_interfaces/srv/DescribeParameters
  24. * rcl_interfaces/srv/GetParameters
  25. * rcl_interfaces/srv/SetParameters
  26. * rcl_interfaces/srv/SetParametersAtomically
  27. *
  28. * Call start() to begin receiving client request.
  29. * All service requests are forwarded to the node this service works for.
  30. * @class
  31. */
  32. class ParameterService {
  33. /**
  34. * Create a new instance.
  35. * @constructor
  36. * @param {Node} node - The node these services will support.
  37. */
  38. constructor(node) {
  39. this._node = node;
  40. this._isRunning = false;
  41. }
  42. /**
  43. * Get the node this
  44. * @return {Node} - The supported node.
  45. */
  46. get node() {
  47. return this._node;
  48. }
  49. /**
  50. * Check if interface services are configured and accepting requests.
  51. * @return {boolean} - True if services are active; false otherwise.
  52. */
  53. isStarted() {
  54. return this._isRunning;
  55. }
  56. /**
  57. * Configure parameter services and begin processing client requests.
  58. * If this service is already started the request is ignored, i.e., a nop.
  59. *
  60. * @return {undefined}
  61. */
  62. start() {
  63. // do nothing if service is already running
  64. if (this._isRunning) return;
  65. this._isRunning = true;
  66. const nodeName = this.node.name();
  67. // create ListParameters service
  68. const listParametersServiceName = nodeName + '/list_parameters';
  69. this.node.createService(
  70. 'rcl_interfaces/srv/ListParameters',
  71. listParametersServiceName,
  72. (request, response) => this._handleListParameters(request, response)
  73. );
  74. // create DescribeParameters service
  75. const describeParametersServiceName = nodeName + '/describe_parameters';
  76. this._node.createService(
  77. 'rcl_interfaces/srv/DescribeParameters',
  78. describeParametersServiceName,
  79. (request, response) => {
  80. this._handleDescribeParameters(request, response);
  81. }
  82. );
  83. // create GetParameters service
  84. const getParametersServiceName = nodeName + '/get_parameters';
  85. this._node.createService(
  86. 'rcl_interfaces/srv/GetParameters',
  87. getParametersServiceName,
  88. (request, response) => {
  89. this._handleGetParameters(request, response);
  90. }
  91. );
  92. // create SetParameters service
  93. const setParametersServiceName = nodeName + '/set_parameters';
  94. this._node.createService(
  95. 'rcl_interfaces/srv/SetParameters',
  96. setParametersServiceName,
  97. (request, response) => {
  98. this._handleSetParameters(request, response);
  99. }
  100. );
  101. // create SetParametersAtomically service
  102. const setParametersAtomicallyServiceName =
  103. nodeName + '/set_parameters_atomically';
  104. this._node.createService(
  105. 'rcl_interfaces/srv/SetParametersAtomically',
  106. setParametersAtomicallyServiceName,
  107. (request, response) => {
  108. this._handleSetParametersAtomically(request, response);
  109. }
  110. );
  111. }
  112. /**
  113. * Get a list of parameter names.
  114. *
  115. * The body of the response is a rcl_interfaces.msg.ListParametersResult.
  116. *
  117. * @param {ListParameters_Request} request - The client request.
  118. * @param {ListParameters_Response} response - The service response with
  119. * a list of parameter names.
  120. * @return {undefined} -
  121. */
  122. _handleListParameters(request, response) {
  123. const DEPTH_RECURSIVE = 0;
  124. let prefixedNames = [];
  125. const parameterNames = this._node.getParameterNames();
  126. const msg = response.template;
  127. const result = msg.result;
  128. for (const paramName of parameterNames) {
  129. if (paramName.includes(PARAMETER_SEPARATOR)) {
  130. prefixedNames.push(paramName);
  131. continue;
  132. } else if (request.prefixes.length > 0) {
  133. for (const prefix of request.prefixes) {
  134. if (paramName.startsWith(prefix)) {
  135. result.names.push(paramName);
  136. break;
  137. }
  138. }
  139. } else {
  140. result.names.push(paramName);
  141. }
  142. }
  143. if (request.depth == 1) {
  144. response.send(msg);
  145. return;
  146. }
  147. if (request.depth != DEPTH_RECURSIVE) {
  148. prefixedNames = prefixedNames.filter(
  149. (name) => name.split(PARAMETER_SEPARATOR).length - 1 < request.depth
  150. );
  151. }
  152. for (const paramName of prefixedNames) {
  153. if (request.prefixes.length > 0) {
  154. for (const prefix of request.prefixes) {
  155. if (paramName.startsWith(prefix + PARAMETER_SEPARATOR)) {
  156. result.names.push(paramName);
  157. // drop the last segment of paramName to reveal a prefix
  158. let fullPrefix = paramName.split(PARAMETER_SEPARATOR);
  159. fullPrefix.pop();
  160. fullPrefix = fullPrefix.join(PARAMETER_SEPARATOR);
  161. if (!result.prefixes.includes(fullPrefix)) {
  162. result.prefixes.push(fullPrefix);
  163. }
  164. if (!request.prefixes.includes(prefix)) {
  165. result.prefixes.push(prefix);
  166. }
  167. }
  168. }
  169. } else {
  170. result.names.push(paramName);
  171. // drop the last segment of paramName to reveal a prefix
  172. let prefix = paramName.split(PARAMETER_SEPARATOR);
  173. prefix.pop();
  174. prefix = prefix.join(PARAMETER_SEPARATOR);
  175. if (!request.prefixes.includes(prefix)) {
  176. result.prefixes.push(prefix);
  177. }
  178. }
  179. }
  180. response.send(msg);
  181. }
  182. /**
  183. * Get a list of ParameterDescriptors.
  184. *
  185. * Request.names identifies the descriptors to get.
  186. * If request.names is empty, get all descriptors.
  187. * The body of the response is a rcl_interfaces.msg.DescribeParametersResult.
  188. *
  189. * @param {DescribeParameters_Request} request - The client request
  190. * @param {DescribeParameters_Response} response - The server response with
  191. * an array of ParameterDescriptors.
  192. * @return {undefined} -
  193. */
  194. _handleDescribeParameters(request, response) {
  195. const names = request.names;
  196. const msg = response.template; // ParameterDescriptor
  197. if (names.length > 0) {
  198. const descriptors = this._node.getParameterDescriptors(names);
  199. msg.descriptors = descriptors.map((descriptor) => descriptor.toMessage());
  200. }
  201. response.send(msg);
  202. }
  203. /**
  204. * Get a list of ParameterValue.
  205. *
  206. * request.names identifies the parameter values to get.
  207. * If request.names is empty return the value of all parameters.
  208. * The body of the response is a rcl_interfaces.msg.ParameterValue[].
  209. *
  210. * @param {GetParameters_Request} request - The client request.
  211. * @param {GetParameters_Response} response - The service response with
  212. * an array of ParameterValue.
  213. * @return {undefined} -
  214. */
  215. _handleGetParameters(request, response) {
  216. const parameters = this._node.getParameters(request.names);
  217. const msg = response.template;
  218. msg.values = parameters.map((param) => param.toParameterValueMessage());
  219. response.send(msg);
  220. }
  221. /**
  222. * Update a list of parameters.
  223. *
  224. * Process each setParameter operation in the order defined by the request.
  225. * The result is an rcl_interfaces.msg.SetParametersResult[], one result
  226. * for each parameter.
  227. *
  228. * @param {SetParameters_Request} request - The client request.
  229. * @param {SetParameters_Response} response - The service response
  230. * with a SetParametersResult[]
  231. * @return {undefined} -
  232. */
  233. _handleSetParameters(request, response) {
  234. const parameters = request.parameters.map((paramMsg) =>
  235. Parameter.fromParameterMessage(paramMsg)
  236. );
  237. const msg = response.template;
  238. msg.results = this._node.setParameters(parameters);
  239. response.send(msg);
  240. }
  241. /**
  242. * Update a list of parameters atomically.
  243. *
  244. * The body of the response is a rcl_interfaces.msg.SetParametersResult
  245. *
  246. * @param {SetParameters_Request} request - The client request.
  247. * @param {SetParameters_Response} response - The service response
  248. * with a single SetParametersResult for the entire process.
  249. * @return {undefined} -
  250. */
  251. _handleSetParametersAtomically(request, response) {
  252. const parameters = request.parameters.map((paramMsg) =>
  253. Parameter.fromParameterMessage(paramMsg)
  254. );
  255. const msg = response.template;
  256. msg.result = this._node.setParametersAtomically(parameters);
  257. response.send(msg);
  258. }
  259. }
  260. module.exports = ParameterService;