diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..d3ccfeb1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,46 @@ +env: + node: true + es6: true + +globals: + every: true + after: true + constantly: true + +rules: + camelcase: [2, {properties: "always"}] + comma-dangle: 0 + comma-spacing: [2, {before: false, after: true}] + comma-style: [2, "last"] + handle-callback-err: [2, "^.*(e|E)rr" ] + indent: [2, 2] + key-spacing: [2, { beforeColon: false, afterColon: true }] + max-depth: [1, 3] + max-len: [1, 80, 4] + max-nested-callbacks: [1, 3] + no-cond-assign: 2 + no-constant-condition: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-else-return: 2 + no-empty: 2 + no-lonely-if: 2 + no-multiple-empty-lines: 2 + no-nested-ternary: 2 + no-self-compare: 2 + no-sync: 1 + no-throw-literal: 2 + no-underscore-dangle: 0 + quote-props: [2, "as-needed"] + quotes: [2, "single", "avoid-escape"] + radix: 2 + semi-spacing: [2, {before: false, after: true}] + semi: [2, "always"] + keyword-spacing: [2, {before: true, after: true}] + space-before-blocks: [2, "always"] + space-before-function-paren: [1, "never"] + space-in-parens: [2, "never"] + spaced-comment: [1, "always"] + strict: [2, "global"] + valid-jsdoc: 2 + yoda: [2, "never"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..60951f28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*~ +.DS_Store +.*.sw? +*.gypcmd +*.mk +*.pyc +*.tar.gz +*.log +build +cscope.* +gen +node_modules +npm-debug.log +tags \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c76b25b4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +services: + - docker + +sudo: required + +branches: + only: + - master + +before_install: + - sudo docker pull ubuntu:xenial + - sudo docker build -t rcldocker . + +script: + - sudo docker run -v $(pwd):/root/rclnodejs --rm rcldocker bash -i -c '/root/rclnodejs/build.sh' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b26878a9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:xenial + +ENV GIT_USER_NAME mrbuild +ENV GIT_USER_EMAIL mrbuild@github.com +ENV LANG en_US.UTF-8 + +RUN apt-get update && apt-get install -y git wget locales python + +# Install ROS2 requirements +RUN locale-gen en_US en_US.UTF-8 && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 + +RUN /bin/bash -c 'echo "deb http://packages.ros.org/ros/ubuntu xenial main" > /etc/apt/sources.list.d/ros-latest.list' \ + && apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116 + +RUN /bin/bash -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu xenial main" > /etc/apt/sources.list.d/gazebo-latest.list' \ + && apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys D2486D2DD83DB69272AFE98867170598AF249743 + +RUN apt-get update && apt-get install -y build-essential cppcheck cmake libopencv-dev libpoco-dev libpocofoundation9v5 \ + libpocofoundation9v5-dbg python-empy python3-dev python3-empy python3-nose python3-pip python3-setuptools python3-vcstool libtinyxml-dev libeigen3-dev + +# Dependencies for testing +RUN apt-get install -y clang-format pydocstyle pyflakes python3-coverage python3-mock python3-pep8 uncrustify \ + && pip3 install flake8 flake8-import-order + +# Dependencies for FastRTPS +RUN apt-get install -y libasio-dev libtinyxml2-dev + +# Configure git +RUN git config --global user.name $GIT_USER_NAME \ + && git config --global user.email $GIT_USER_EMAIL + +# Get ROS2 code and build +ENV ROS2_WS=/root/ros2_ws + +RUN mkdir -p $ROS2_WS/src + +WORKDIR $ROS2_WS + +RUN wget https://raw.githubusercontent.com/ros2/ros2/master/ros2.repos \ + && vcs import src < ros2.repos \ + && src/ament/ament_tools/scripts/ament.py build --build-tests --symlink-install + +RUN echo "source $ROS2_WS/install/local_setup.bash" >> $HOME/.bashrc + +# Install nvm, Node.js and node-gyp +ENV NODE_VERSION v6.10.3 +RUN wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash \ + && . $HOME/.nvm/nvm.sh \ + && nvm install $NODE_VERSION && nvm alias default $NODE_VERSION \ + && npm install -g node-gyp + + +ENV PATH /bin/versions/node/$NODE_VERSION/bin:$PATH diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md index c8c2e788..b8bb8584 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # rclnodejs -Nodejs version of ROS2.0 client +ROS2.0 Client Library for JavaScript with nodejs + +[![Build Status](https://travis-ci.org/RobotWebTools/rclnodejs.svg?branch=master)](https://travis-ci.org/RobotWebTools/rclnodejs) diff --git a/addon.cpp b/addon.cpp new file mode 100644 index 00000000..a1f31b2d --- /dev/null +++ b/addon.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include + +#include "shadow_node.hpp" +#include "rcl_bindings.hpp" +#include "rcl_handle.hpp" + +void InitModule(v8::Local exports) { + for (uint32_t i = 0; i < rclnodejs::GetBindingMethodsCount( + rclnodejs::binding_methods); i++) { + exports->Set(Nan::New(rclnodejs::binding_methods[i].name).ToLocalChecked(), + Nan::New( + rclnodejs::binding_methods[i].function)->GetFunction()); + } + + rclnodejs::ShadowNode::Init(exports); + rclnodejs::RclHandle::Init(exports); +} + +NODE_MODULE(rclnodejs, InitModule); diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 00000000..d3d33d63 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,78 @@ +{ + "targets": [ + { + "target_name": "rclnodejs", + "variables": { + "ROS2_INSTALL_PATH": " /dev/null + +npm install +npm run lint diff --git a/cpplint.js b/cpplint.js new file mode 100644 index 00000000..fa55230c --- /dev/null +++ b/cpplint.js @@ -0,0 +1,38 @@ +// Copyright (c) 2017 Intel Corporation. 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 exec = require('child_process').exec; + +const cmd = 'wget -nc '; +const cpplintUrl = 'https://raw.githubusercontent.com/google/styleguide' + + '/gh-pages/cpplint/cpplint.py'; +const args = '--extensions=cpp,h,hpp,cc addon.cpp src/* include/*'; + +console.log('Downloading the cpplint...'); +exec(cmd + cpplintUrl, (err, stdout, stderr) => { + if (err) { + console.log(`Downloading failed: ${stderr}`); + } else { + console.log('Running the cpplint...'); + exec('python cpplint.py ' + args, (err, stdout, stderr) =>{ + console.log(stdout); + if (err) { + console.log(stderr); + throw Error('cpplint failed.'); + } + }); + } +}); diff --git a/example/timer-example.js b/example/timer-example.js new file mode 100644 index 00000000..f97f967d --- /dev/null +++ b/example/timer-example.js @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Intel Corporation. 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'); + +rclnodejs.init(); + +let node = rclnodejs.createNode('timer'); + +let timer = node.createTimer(1000000000, () => { + console.log('One second escaped!'); + + console.log('Cancel this timer.'); + timer.cancel(); + + if (timer.isCanceled()) { + console.log('The timer has been canceled successfully.'); + } + + console.log('Reset the timer.'); + timer.reset(); + console.log('The next call will be ' + + timer.timeUntilNextCall() + + 'ns later.'); + + console.log('Shuting down...'); + rclnodejs.shutdown(); +}); + +rclnodejs.spin(node); diff --git a/include/executor.hpp b/include/executor.hpp new file mode 100644 index 00000000..e20778e6 --- /dev/null +++ b/include/executor.hpp @@ -0,0 +1,57 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#ifndef RCLNODEJS_EXECUTOR_HPP_ +#define RCLNODEJS_EXECUTOR_HPP_ + +#include + +#include +#include + +namespace rclnodejs { + +class HandleManager; + +class Executor { + public: + class Delegate { + public: + virtual void Execute() = 0; + virtual void CatchException(std::exception_ptr e_ptr) = 0; + }; + + Executor(HandleManager* handle_manager, Delegate* delegate); + ~Executor(); + + void Start(); + void Stop(); + + static void DoWork(uv_async_t* handle); + static void Run(void* arg); + + private: + uv_loop_t* mainthread_loop_; + uv_async_t* async_; + uv_thread_t thread_; + + HandleManager* handle_manager_; + Delegate* delegate_; + + std::atomic_bool running_; +}; + +} // namespace rclnodejs + +#endif diff --git a/include/handle_manager.hpp b/include/handle_manager.hpp new file mode 100644 index 00000000..71b93044 --- /dev/null +++ b/include/handle_manager.hpp @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#ifndef RCLNODEJS_HANDLE_MANAGER_HPP_ +#define RCLNODEJS_HANDLE_MANAGER_HPP_ + +#include +#include +#include + +namespace rclnodejs { + +class HandleManager { + public: + HandleManager(); + ~HandleManager(); + + void CollectHandles(const v8::Local node); + + uint32_t SubscriptionsCount(); + uint32_t ServicesCount(); + uint32_t ClientsCount(); + uint32_t TimersCount(); + + bool AddHandlesToWaitSet(rcl_wait_set_t* wait_set); + void ClearHandles(); + + protected: + template void CollectHandlesByType( + const v8::Local& typeObject, std::vector* vec); + + private: + std::vector timers_; + std::vector clients_; + std::vector services_; + std::vector subscriptions_; + std::vector guard_conditions_; + + uv_mutex_t mutex_; +}; + +} // namespace rclnodejs + +#endif diff --git a/include/rcl_bindings.hpp b/include/rcl_bindings.hpp new file mode 100644 index 00000000..7dade920 --- /dev/null +++ b/include/rcl_bindings.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#ifndef RCLNODEJS_RCL_BINDINGS_HPP_ +#define RCLNODEJS_RCL_BINDINGS_HPP_ + +#include + +namespace rclnodejs { + +typedef void (*JsCFuntcion)(const Nan::FunctionCallbackInfo&); + +typedef struct { + const char* name; + JsCFuntcion function; +} BindingMethod; + +uint32_t GetBindingMethodsCount(BindingMethod* methods); + +extern BindingMethod binding_methods[]; + +} // namespace rclnodejs + +#endif diff --git a/include/rcl_handle.hpp b/include/rcl_handle.hpp new file mode 100644 index 00000000..9e0024d6 --- /dev/null +++ b/include/rcl_handle.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#ifndef RCLNODEJS_RCL_HANDLE_HPP_ +#define RCLNODEJS_RCL_HANDLE_HPP_ + +#include + +namespace rclnodejs { + +class RclHandle : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static v8::Local NewInstance(void* handle); + + void* GetPtr() { return handle_; } + void SetPtr(void* handle) { handle_ = handle; } + + private: + RclHandle(); + ~RclHandle(); + + static Nan::Persistent constructor; + static void New(const Nan::FunctionCallbackInfo& info); + void* handle_; +}; + +} // namespace rclnodejs + +#endif diff --git a/include/shadow_node.hpp b/include/shadow_node.hpp new file mode 100644 index 00000000..d6062da2 --- /dev/null +++ b/include/shadow_node.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#ifndef RCLNODEJS_SHADOW_NODE_HPP_ +#define RCLNODEJS_SHADOW_NODE_HPP_ + +#include + +#include +#include + +#include "executor.hpp" + +namespace rclnodejs { + +class HandleManager; +class Executor; + +class ShadowNode : public Nan::ObjectWrap, + public Executor::Delegate { + public: + static void Init(v8::Local exports); + void Spin(); + void Shutdown(); + + // Executor::Delegate overrides: + void Execute() override; + void CatchException(std::exception_ptr e_ptr) override; + + private: + ShadowNode(); + ~ShadowNode(); + + static void New(const Nan::FunctionCallbackInfo& info); + static Nan::Persistent constructor; + + std::unique_ptr handle_manager_; + std::unique_ptr executor_; +}; + +} // namespace rclnodejs + +#endif diff --git a/index.js b/index.js new file mode 100644 index 00000000..ffc0457e --- /dev/null +++ b/index.js @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Intel Corporation. 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('bindings')('rclnodejs'); +const Node = require('./lib/node.js'); + +function inherits(target, source) { + // eslint-disable-next-line + let properties = Object.getOwnPropertyNames(source.prototype); + properties.forEach((property) => { + target.prototype[property] = source.prototype[property]; + }); +} + +inherits(rclnodejs.ShadowNode, Node); + +let Rclnodejs = { + _nodes: [], + + createNode(nodeName, namespace = '') { + let handle = rclnodejs.createNode(nodeName, namespace); + + let node = new rclnodejs.ShadowNode(); + node._handle = handle; + node._publishers = []; + node._subscribers = []; + node._clients = []; + node._services = []; + node._timers = []; + node.spinning = false; + + this._nodes.push(node); + + return node; + }, + + init(...args) { + rclnodejs.init(args); + }, + + spin(node) { + if (node.spinning) { + throw new Error('The node is already spinning.'); + } + + rclnodejs.spin(node); + node.spinning = true; + }, + + shutdown() { + this._nodes.forEach((node) => { + rclnodejs.shutdown(node); + }); + this._nodes = []; + }, +}; + +module.exports = Rclnodejs; diff --git a/lib/client.js b/lib/client.js new file mode 100644 index 00000000..45d1a681 --- /dev/null +++ b/lib/client.js @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Intel Corporation. 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'; + +class Client { + constructor(handle, serviceType, serviceName) { + this._handle = handle; + this._serviceName = serviceName; + } + + call(request, callback) { + this.callback = callback; + } + + get handle() { + return this._handle; + } + + receiveResponse(response) { + + } +}; + +module.exports = Client; diff --git a/lib/node.js b/lib/node.js new file mode 100644 index 00000000..3755632b --- /dev/null +++ b/lib/node.js @@ -0,0 +1,86 @@ +// Copyright (c) 2017 Intel Corporation. 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('bindings')('rclnodejs'); +const Timer = require('./timer.js'); + +class Node { + execute() { + this._timers.forEach(function(timer, index, array) { + if (timer.isReady()) { + rclnodejs.callTimer(timer.handle); + timer.callback(); + } + }); + } + + get handle() { + return this._handle; + } + + createTimer(period, callback) { + let timerHandle = rclnodejs.createTimer(period); + let timer = new Timer(timerHandle, period, callback); + this._timers.push(timer); + + return timer; + } + + createPublisher(messageType, topic) { + + } + + createSubscription(messageType, topic, callback) { + + } + + createClient(serviceType, serviceName) { + + } + + createService(serviceType, serviceName, callback) { + + } + + destoryNode() { + for (let timer in this._timers) { + rclnodejs.destoryEntity('timer', timer.handle); + } + this._timers = []; + } + + destoryPublisher(publisher) { + + } + + destorySubscription(subscription) { + + } + + destoryClient(client) { + + } + + destoryService(service) { + + } + + destoryTimer(timer) { + + } +} + +module.exports = Node; diff --git a/lib/publisher.js b/lib/publisher.js new file mode 100644 index 00000000..c65af610 --- /dev/null +++ b/lib/publisher.js @@ -0,0 +1,37 @@ +// Copyright (c) 2017 Intel Corporation. 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'; + +class Publisher { + constsructor(handle, messageType, messageName, topic) { + this._handle = handle; + this._messageName = messageName; + this._topic = topic; + } + + get handle() { + return this._handle; + } + + get topic() { + return this._topic; + } + + publish(message) { + + } +}; + +module.exports = Publisher; diff --git a/lib/service.js b/lib/service.js new file mode 100644 index 00000000..cc8ab4f5 --- /dev/null +++ b/lib/service.js @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Intel Corporation. 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'; + +class Service { + constsructor(handle, serviceType, serviceName) { + this._handle = handle; + this._serviceName = serviceName; + } + + processRequest() { + + } + + get handle() { + return this._handle; + } + + sendResponse() { + + } +}; + +module.exports = Service; diff --git a/lib/subscription.js b/lib/subscription.js new file mode 100644 index 00000000..bf1b3a1f --- /dev/null +++ b/lib/subscription.js @@ -0,0 +1,30 @@ +// Copyright (c) 2017 Intel Corporation. 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'; + +class Subscription { + constructor(handle, messageType, messageName, topic, callback) { + this._handle = handle; + this._messageName = messageName; + this._topic = topic; + this.callback = callback; + } + + get handle() { + return this._handle; + } +}; + +module.exports = Subscriber; diff --git a/lib/timer.js b/lib/timer.js new file mode 100644 index 00000000..4e57a31e --- /dev/null +++ b/lib/timer.js @@ -0,0 +1,59 @@ +// Copyright (c) 2017 Intel Corporation. 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('bindings')('rclnodejs'); + +class Timer { + constructor(handle, period, callback) { + this._handle = handle; + this._period = period; + this.callback = callback; + } + + get period() { + return this._period; + } + + get handle() { + return this._handle; + } + + isReady() { + return rclnodejs.isTimerReady(this._handle); + } + + isCanceled() { + return rclnodejs.isTimerCanceled(this._handle); + } + + cancel() { + rclnodejs.cancelTimer(this._handle); + } + + reset() { + rclnodejs.resetTimer(this._handle); + } + + timeSinceLastCall() { + return rclnodejs.timerGetTimeSinceLastCall(this._handle); + } + + timeUntilNextCall() { + return rclnodejs.timerGetTimeUntilNextCall(this._handle); + } +}; + +module.exports = Timer; diff --git a/package.json b/package.json new file mode 100644 index 00000000..cf8715ce --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "rclnodejs", + "version": "0.0.1", + "description": "ROS2.0 JavaScript client with Node.js", + "main": "index.js", + "keywords": [ + "rclnodejs", + "ros2" + ], + "scripts": { + "install": "node-gyp rebuild", + "lint": "eslint index.js cpplint.js lib example && node cpplint.js" + }, + "author": [ + "Minggang Wang ", + "Kenny Yuan " + ], + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/RobotWebTools/rclnodejs.git" + }, + "devDependencies": { + "eslint": "^3.19.0" + }, + "dependencies": { + "bindings": "^1.2.1", + "nan": "^2.6.2" + } +} diff --git a/src/executor.cpp b/src/executor.cpp new file mode 100644 index 00000000..84c26686 --- /dev/null +++ b/src/executor.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include "executor.hpp" + +#include +#include +#include +#include + +#include "handle_manager.hpp" + +namespace rclnodejs { + +static std::exception_ptr g_exception_ptr = nullptr; + +Executor::Executor(HandleManager* handle_manager, Delegate* delegate) + : mainthread_loop_(uv_default_loop()), + handle_manager_(handle_manager), + delegate_(delegate) { + async_ = reinterpret_cast(malloc(sizeof(uv_async_t))); + async_->data = this; +} + +Executor::~Executor() { + free(async_); +} + +void Executor::Start() { + if (!running_.load()) { + uv_async_init(mainthread_loop_, async_, DoWork); + uv_thread_create(&thread_, Executor::Run, this); + running_.store(true); + } +} + +void Executor::Stop() { + if (running_.load()) { + running_.store(false); + uv_close(reinterpret_cast(async_), nullptr); + uv_thread_join(&thread_); + } +} + +void Executor::DoWork(uv_async_t* handle) { + Executor* executor = reinterpret_cast(handle->data); + if (executor->delegate_) { + if (g_exception_ptr) { + executor->delegate_->CatchException(g_exception_ptr); + g_exception_ptr = nullptr; + } + executor->delegate_->Execute(); + } +} + +void Executor::Run(void* arg) { + Executor* executor = reinterpret_cast(arg); + HandleManager* handle_manager = executor->handle_manager_; + + rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set(); + rcl_ret_t ret = rcl_wait_set_init(&wait_set, 0, 2, 0, 0, 0, + rcl_get_default_allocator()); + if (ret != RCL_RET_OK) { + throw std::runtime_error(std::string("Init waitset failed: ") + + rcl_get_error_string_safe()); + } + + try { + while (executor->running_.load()) { + if (rcl_wait_set_resize_subscriptions( + &wait_set, handle_manager->SubscriptionsCount()) != RCL_RET_OK) { + throw std::runtime_error(std::string( + "Couldn't resize the number of subscriptions in waitset : ") + + rcl_get_error_string_safe()); + } + + if (rcl_wait_set_resize_services( + &wait_set, handle_manager->ServicesCount()) != RCL_RET_OK) { + throw std::runtime_error(std::string( + "Couldn't resize the number of services in waitset : ") + + rcl_get_error_string_safe()); + } + + if (rcl_wait_set_resize_clients( + &wait_set, handle_manager->ClientsCount()) != RCL_RET_OK) { + throw std::runtime_error(std::string( + "Couldn't resize the number of clients in waitset : ") + + rcl_get_error_string_safe()); + } + + if (rcl_wait_set_resize_timers( + &wait_set, handle_manager->TimersCount()) != RCL_RET_OK) { + throw std::runtime_error(std::string( + "Couldn't resize the number of timers in waitset : ") + + rcl_get_error_string_safe()); + } + + if (!handle_manager->AddHandlesToWaitSet(&wait_set)) { + throw std::runtime_error("Couldn't fill waitset"); + } + + rcl_ret_t status = + rcl_wait(&wait_set, RCL_MS_TO_NS(10)); + if (status == RCL_RET_WAIT_SET_EMPTY) { + } else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) { + throw std::runtime_error(std::string("rcl_wait() failed: ") + + rcl_get_error_string_safe()); + } else { + uv_async_send(executor->async_); + } + + if (rcl_wait_set_clear_subscriptions(&wait_set) != RCL_RET_OK) { + throw std::runtime_error("Couldn't clear subscriptions from waitset"); + } + + if (rcl_wait_set_clear_services(&wait_set) != RCL_RET_OK) { + throw std::runtime_error("Couldn't clear servicess from waitset"); + } + + if (rcl_wait_set_clear_clients(&wait_set) != RCL_RET_OK) { + throw std::runtime_error("Couldn't clear clients from waitset"); + } + + if (rcl_wait_set_clear_guard_conditions(&wait_set) != RCL_RET_OK) { + throw std::runtime_error( + "Couldn't clear guard conditions from waitset"); + } + + if (rcl_wait_set_clear_timers(&wait_set) != RCL_RET_OK) { + throw std::runtime_error("Couldn't clear timers from waitset"); + } + } + + if (rcl_wait_set_fini(&wait_set) != RCL_RET_OK) { + throw std::runtime_error(std::string( + "Failed to destroy guard waitset:") + rcl_get_error_string_safe()); + } + } catch(...) { + g_exception_ptr = std::current_exception(); + uv_async_send(executor->async_); + } +} + +} // namespace rclnodejs diff --git a/src/handle_manager.cpp b/src/handle_manager.cpp new file mode 100644 index 00000000..98df9338 --- /dev/null +++ b/src/handle_manager.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include "handle_manager.hpp" + +#include + +#include "rcl_handle.hpp" + +namespace rclnodejs { + +struct ScopedMutex { + explicit ScopedMutex(uv_mutex_t mutex) : mutex_(mutex) { + uv_mutex_lock(&mutex_); + } + ~ScopedMutex() { + uv_mutex_unlock(&mutex_); + } + uv_mutex_t mutex_; +}; + +HandleManager::HandleManager() { + uv_mutex_init(&mutex_); +} + +HandleManager::~HandleManager() { + uv_mutex_destroy(&mutex_); +} + +void HandleManager::CollectHandles(const v8::Local node) { + Nan::HandleScope scope; + Nan::MaybeLocal timers = + Nan::Get(node, Nan::New("_timers").ToLocalChecked()); + + CollectHandlesByType(timers.ToLocalChecked()->ToObject(), &timers_); +} + +uint32_t HandleManager::SubscriptionsCount() { + return subscriptions_.size(); +} + +uint32_t HandleManager::ServicesCount() { + return services_.size(); +} + +uint32_t HandleManager::ClientsCount() { + return clients_.size(); +} + +uint32_t HandleManager::TimersCount() { + return timers_.size(); +} + +bool HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) { + ScopedMutex scoped_mutex(mutex_); + + for (auto& timer : timers_) { + if (rcl_wait_set_add_timer(wait_set, timer) != RCL_RET_OK) + return false; + } + return true; +} + +void HandleManager::ClearHandles() { + ScopedMutex scoped_mutex(mutex_); + timers_.clear(); + clients_.clear(); + services_.clear(); + subscriptions_.clear(); + guard_conditions_.clear(); +} + +template +void HandleManager::CollectHandlesByType( + const v8::Local& typeObject, std::vector* vec) { + ScopedMutex scoped_mutex(mutex_); + Nan::HandleScope scope; + + if (typeObject->IsArray()) { + uint32_t length = Nan::Get(typeObject, Nan::New("length").ToLocalChecked()). + ToLocalChecked()->Uint32Value(); + + for (uint32_t index = 0; index < length; index++) { + v8::Local obj = typeObject->Get(index)->ToObject(); + Nan::MaybeLocal handle = + Nan::Get(obj, Nan::New("_handle").ToLocalChecked()); + rclnodejs::RclHandle* rcl_handle = rclnodejs::RclHandle::Unwrap< + rclnodejs::RclHandle>(handle.ToLocalChecked()->ToObject()); + vec->push_back(reinterpret_cast(rcl_handle->GetPtr())); + } + } +} + +} // namespace rclnodejs diff --git a/src/rcl_bindings.cpp b/src/rcl_bindings.cpp new file mode 100644 index 00000000..4577e77c --- /dev/null +++ b/src/rcl_bindings.cpp @@ -0,0 +1,307 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include "rcl_bindings.hpp" + +#include +#include +#include + +#include "handle_manager.hpp" +#include "shadow_node.hpp" +#include "rcl_handle.hpp" + +namespace rclnodejs { + +void Init(const Nan::FunctionCallbackInfo& info) { + rcl_ret_t ret = rcl_init(0, nullptr, rcl_get_default_allocator()); + if (ret != RCL_RET_OK) + Nan::ThrowError(rcl_get_error_string_safe()); +} + +void CreateNode(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 2) { + Nan::ThrowError("Wrong number of argments"); + return; + } + + if (!info[0]->IsString() || !info[1]->IsString()) { + Nan::ThrowError("Wrong argments"); + return; + } + + const char* nodeName = *Nan::Utf8String(info[0]->ToString()); + const char* nameSpace = *Nan::Utf8String(info[1]->ToString()); + + rcl_node_t* node = reinterpret_cast(malloc(sizeof(rcl_node_t))); + + *node = rcl_get_zero_initialized_node(); + rcl_node_options_t options = rcl_node_get_default_options(); + if (rcl_node_init(node, nodeName, nameSpace, &options) != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } + info.GetReturnValue().Set(rclnodejs::RclHandle::NewInstance(node)); +} + +void CreateTimer(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1) { + Nan::ThrowError("Wrong number of argments"); + return; + } + + if (!info[0]->IsNumber()) { + Nan::ThrowError("Wrong argments"); + return; + } + + // TODO(minggang): Add support for uint64_t. + int64_t period = info[0]->IntegerValue(); + + rcl_timer_t* timer = reinterpret_cast( + malloc(sizeof(rcl_timer_t))); + *timer = rcl_get_zero_initialized_timer(); + + rcl_ret_t ret = rcl_timer_init(timer, period, nullptr, + rcl_get_default_allocator()); + if (ret != RCL_RET_OK) { + Nan::ThrowError("Create timer failed"); + return; + } + + info.GetReturnValue().Set(rclnodejs::RclHandle::NewInstance(timer)); +} + +void IsTimerReady(const Nan::FunctionCallbackInfo& info) { + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = reinterpret_cast(timerHandler->GetPtr()); + + bool is_ready = false; + rcl_ret_t ret = rcl_timer_is_ready(timer, &is_ready); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } + + info.GetReturnValue().Set(Nan::New(is_ready)); +} + +void DestroyEntity(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 2) { + Nan::ThrowError("Wrong number of argments"); + return; + } + + if (!info[0]->IsString()) { + Nan::ThrowError("Wrong argments"); + return; + } + + const char* type = *Nan::Utf8String(info[0]->ToString()); + + rcl_ret_t ret = 0; + + if (0 == strcmp(type, "timer")) { + rcl_timer_t* timer = reinterpret_cast( + rclnodejs::RclHandle::Unwrap( + info[0]->ToObject())->GetPtr()); + ret = rcl_timer_fini(timer); + } + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + } +} + +void CallTimer(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + rcl_ret_t ret = rcl_timer_call(timer); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } +} + +void CancelTimer(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + rcl_ret_t ret = rcl_timer_cancel(timer); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } +} + +void IsTimerCanceled(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + bool is_canceled = false; + + rcl_ret_t ret = rcl_timer_is_canceled(timer, &is_canceled); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } + + info.GetReturnValue().Set(Nan::New(is_canceled)); +} + +void ResetTimer(const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + rcl_ret_t ret = rcl_timer_reset(timer); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } +} + +void TimerGetTimeUntilNextCall( + const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + int64_t remaining_time = 0; + + rcl_ret_t ret = rcl_timer_get_time_until_next_call(timer, &remaining_time); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } + + info.GetReturnValue().Set(Nan::New((uint32_t)remaining_time)); +} + +void TimerGetTimeSinceLastCall( + const Nan::FunctionCallbackInfo& info) { + if (info.Length() < 1 && !info[0]->IsObject()) { + Nan::ThrowError("Wrong argments"); + return; + } + + rclnodejs::RclHandle* timerHandler = + rclnodejs::RclHandle::Unwrap(info[0]->ToObject()); + + rcl_timer_t* timer = + reinterpret_cast(timerHandler->GetPtr()); + + uint64_t elapsed_time = 0; + + rcl_ret_t ret = rcl_timer_get_time_since_last_call(timer, &elapsed_time); + + if (ret != RCL_RET_OK) { + Nan::ThrowError(rcl_get_error_string_safe()); + return; + } + + info.GetReturnValue().Set(Nan::New((uint32_t)elapsed_time)); +} + +void Spin(const Nan::FunctionCallbackInfo& info) { + if (info.Length() == 1 && info[0]->IsObject()) { + rclnodejs::ShadowNode* node = + rclnodejs::ShadowNode::Unwrap( + info[0]->ToObject()); + node->Spin(); + } +} + +void Shutdown(const Nan::FunctionCallbackInfo& info) { + if (info.Length() == 1 && info[0]->IsObject()) { + rclnodejs::ShadowNode* node = + rclnodejs::ShadowNode::Unwrap( + info[0]->ToObject()); + node->Shutdown(); + } +} + +uint32_t GetBindingMethodsCount(BindingMethod* methods) { + uint32_t count = 0; + while (methods[count].function) { + count++; + } + return count; +} + +BindingMethod binding_methods[] = { + {"init", Init}, + {"createNode", CreateNode}, + {"createTimer", CreateTimer}, + {"isTimerReady", IsTimerReady}, + {"destroyEntity", DestroyEntity}, + {"callTimer", CallTimer}, + {"cancelTimer", CancelTimer}, + {"isTimerCanceled", IsTimerCanceled}, + {"resetTimer", ResetTimer}, + {"timerGetTimeSinceLastCall", TimerGetTimeSinceLastCall}, + {"timerGetTimeUntilNextCall", TimerGetTimeUntilNextCall}, + {"spin", Spin}, + {"shutdown", Shutdown}, + {"", nullptr} +}; + +} // namespace rclnodejs diff --git a/src/rcl_handle.cpp b/src/rcl_handle.cpp new file mode 100644 index 00000000..f5c250a1 --- /dev/null +++ b/src/rcl_handle.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include "rcl_handle.hpp" + +namespace rclnodejs { + +Nan::Persistent RclHandle::constructor; + +RclHandle::RclHandle() : handle_(nullptr) { +} + +RclHandle::~RclHandle() { + free(handle_); +} + +void RclHandle::Init(v8::Local exports) { + v8::Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("RclHandle").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + constructor.Reset(tpl->GetFunction()); + exports->Set(Nan::New("RclHandle").ToLocalChecked(), tpl->GetFunction()); +} + +void RclHandle::New(const Nan::FunctionCallbackInfo& info) { + if (info.IsConstructCall()) { + RclHandle* obj = new RclHandle(); + obj->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } +} + +v8::Local RclHandle::NewInstance(void* handle) { + Nan::EscapableHandleScope scope; + + v8::Local cons = Nan::New(constructor); + v8::Local context = + v8::Isolate::GetCurrent()->GetCurrentContext(); + + v8::Local instance = + cons->NewInstance(context, 0, nullptr).ToLocalChecked(); + + auto wrapper = Nan::ObjectWrap::Unwrap(instance); + wrapper->SetPtr(handle); + + return scope.Escape(instance); +} + +} // namespace rclnodejs diff --git a/src/shadow_node.cpp b/src/shadow_node.cpp new file mode 100644 index 00000000..c7be423d --- /dev/null +++ b/src/shadow_node.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Intel Corporation. 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. + +#include "shadow_node.hpp" + +#include + +#include "executor.hpp" +#include "handle_manager.hpp" + +namespace rclnodejs { + +Nan::Persistent ShadowNode::constructor; + +ShadowNode::ShadowNode() { + handle_manager_ = std::make_unique(); + executor_ = std::make_unique(handle_manager_.get(), this); +} + +ShadowNode::~ShadowNode() { + executor_->Stop(); + + Nan::HandleScope scope; + v8::Local argv[0]; + Nan::MakeCallback(Nan::New(this->persistent()), "destoryNode", 0, argv); +} + +void ShadowNode::Init(v8::Local exports) { + Nan::HandleScope scope; + + // Prepare constructor template + v8::Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("ShadowNode").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + + constructor.Reset(tpl->GetFunction()); + exports->Set(Nan::New("ShadowNode").ToLocalChecked(), tpl->GetFunction()); +} + +void ShadowNode::Spin() { + handle_manager_->CollectHandles(this->handle()); + executor_->Start(); +} + +void ShadowNode::Shutdown() { + executor_->Stop(); +} + +void ShadowNode::Execute() { + Nan::HandleScope scope; + v8::Local argv[0]; + Nan::MakeCallback(Nan::New(this->persistent()), "execute", 0, argv); +} + +void ShadowNode::CatchException(std::exception_ptr e_ptr) { + try { + std::rethrow_exception(e_ptr); + } catch (const std::exception& e) { + Nan::ThrowError(e.what()); + } +} + +void ShadowNode::New(const Nan::FunctionCallbackInfo& info) { + if (info.IsConstructCall()) { + // Invoked as constructor: `new ShadowNode(...)` + ShadowNode* obj = new ShadowNode(); + obj->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } +} + +} // namespace rclnodejs