[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. Ubuntu20.04, Windows 10]\n - ros2 distro [e.g. foxy, galactic]\n - ros2-for-unity version [e.g. 1.1.0, git-sha 6dc898352c9d45996fb0d43e2c8225707e239f4a]\n - ros2cs version [e.g. git-sha 2a45785b080fffb3ae5e2f645e976a69698810f0]\n - ros2 dds middleware [e.g. cyclonedds, fastdds]\n - ros2 environment setup [e.g. single pc, local network]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".gitignore",
    "content": "install\nlog\nbuild\n.idea\nsrc/ros2cs\n**/metadata*.xml\nsrc/Ros2ForUnity/Plugins\n!src/Ros2ForUnity/Plugins/.gitkeep\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\noffice@robotec.ai.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Any contribution that you make to this repository will\nbe under the Apache 2 License, as dictated by that\n[license](http://www.apache.org/licenses/LICENSE-2.0.html):\n\n> 5. Submission of Contributions. Unless You explicitly state otherwise,\n>    any Contribution intentionally submitted for inclusion in the Work\n>    by You to the Licensor shall be under the terms and conditions of\n>    this License, without any additional terms or conditions.\n>    Notwithstanding the above, nothing herein shall supersede or modify\n>    the terms of any separate license agreement you may have executed\n>    with Licensor regarding such Contributions.\n"
  },
  {
    "path": "LICENSE.AL2",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README-UBUNTU.md",
    "content": "# ROS2 For Unity - Ubuntu 20.04 and 22.04\n\nThis readme contains information specific to Ubuntu 20.04/22.04. For general information, please see [README.md](README.md)\n\n## Building\n\nWe assume that working directory is `~/ros2-for-unity` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).\n\n### Prerequisites\n\nStart with installation of dependencies. Make sure to complete each step of `ros2cs` [Prerequisites section](https://github.com/RobotecAI/ros2cs/blob/master/README-UBUNTU.md#prerequisites).\n\n### Steps\n\n* Clone this project.\n    ```bash\n    git clone git@github.com:RobotecAI/ros2-for-unity.git ~/ros2-for-unity\n    ```\n* You need to source your ROS2 installation before you proceed, for each new open terminal. It is convenient to include this command in your `~/.profile` file.\n    ```bash\n    # galactic\n    . /opt/ros/galactic/setup.bash\n    ```\n* Enter `Ros2ForUnity` working directory.\n    ```bash\n    cd ~/ros2-for-unity\n    ```\n* Set up you custom messages in `ros2_for_unity_custom_messages.repos`\n* Import necessary and custom messages repositories.\n    ```bash\n    ./pull_repositories.sh\n    ```\n    > *NOTE* `pull_repositories.sh` script doesn't update already existing repositories, you have to remove `src/ros2cs` folder to re-import new versions.\n* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.\n    ```bash\n    # standalone mode\n    ./build.sh --standalone\n    \n    # overlay mode\n    ./build.sh\n    ```\n    * You can add `--clean-install` flag to make sure your installation directory is cleaned before deploying.\n* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.\n* (optionally) To create `.unitypackage` in `install/unity_package`\n    ```bash\n    create_unity_package.sh -u <your-path-to-unity-editor-executable>\n    ```\n    > *NOTE* Unity license is required. \n\n## OS-Specific usage remarks\n\nYou can run Unity Editor or App executable from GUI (clicking) or from terminal as long as ROS2 is sourced in your environment.\nThe best way to ensure that system-wide is to add `source /opt/ros/foxy/setup.bash` to your `~/.profile` file.\nNote that you need to re-log for changes in `~/.profile` to take place.\nRunning Unity Editor through Unity Hub is also supported.\n\n## Usage troubleshooting\n\n**No ROS environment sourced. You need to source your ROS2 (..)**\n\n* If you see `\"No ROS environment sourced. You need to source your ROS2 (..)\"` message in Unity3D Editor, it means your environment was not sourced properly. This could happen if you run Unity but it redirects to Hub and ignores your console environment variables (this behavior can depend on Unity3D version). In such case, run project directly with `-projectPath` or add ros2 sourcing to your `~/.profile` file (you need to re-log for it to take effect).\n\n* Keep in mind that `UnityHub` stays in the background after its first launch and Unity Editor launch without `-projectPath` will redirect to it and the Hub will start Unity Editor. Since environment variables for the process are set on launch and inherited by child processes, your sourced ros2 environment in the console launching the Editor this way won't be applied. To make sure it applies (and to change between different ros2 distributions), make sure to terminate existing UnityHub process and run it with the correct ros2 distribution sourced.\n\n**There are no errors but I can't see topics published by Ros2ForUnity**\n\n* Make sure your dds config is correct.\n* Sometimes ROS2 daemon brakes up when changing network interfaces or ROS2 version. Try to stop it forcefully (`pkill -9 _ros2_daemon`) and restart (`ros2 daemon start`).\n"
  },
  {
    "path": "README-WINDOWS.md",
    "content": "# ROS2 For Unity - Windows 10\n\nThis readme contains information specific to Window 10. For general information, please see [README.md](README.md).\n\n## Building\n\nWe assume that working directory is `C:\\dev` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).\n\n### Prerequisites\n\nIt is necessary to complete all the steps for `ros2cs` [Prerequisites](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#prerequisites) and consider [Important notices](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices) sections.\n\n### Steps\n\n* Make sure [long paths on Windows are enabled](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)\n* Make sure you open [`Developer PowerShell for VS` with administrator privileges](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)\n* For `ros2 galactic` distribution, it is best to [create a `C:\\ci\\ws\\install\\include` directory](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)\n* Clone this project.\n  ```powershell\n  git clone git@github.com:RobotecAI/ros2-for-unity.git C:\\dev\\ros2-for-unity\n  ```\n* Source your ROS2 installation (`C:\\dev\\ros2_foxy\\local_setup.ps1`) in the terminal before you proceed.\n  ```\n  C:\\dev\\ros2_foxy\\local_setup.ps1\n  ```\n* Enter `Ros2ForUnity` working directory.\n    ```powershell\n    cd C:\\dev\\ros2-for-unity\n    ```\n* Set up you custom messages in `ros2_for_unity_custom_messages.repos`\n* Import necessary and custom messages repositories.\n    ```powershell\n    .\\pull_repositories.ps1\n    ```\n    > *NOTE* `pull_repositories.ps1` script doesn't update already existing repositories, you have to remove `src\\ros2cs` folder to re-import new versions.\n* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.\n    ```powershell\n    # standalone mode\n    ./build.ps1 -standalone\n    \n    # overlay mode\n    ./build.ps1\n    ```\n  * You can build with `-clean_install` to make sure your installation directory is cleaned before deploying.\n* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.\n* (optionally) To create `.unitypackage` in `install/unity_package`\n  ```powershell\n  create_unity_package.ps1\n  ```\n  > *NOTE* Please provide path to your Unity executable when prompted. Unity license is required. In case your Unity license has expired, the `create_unity_package.ps1` won't throw any errors but `Ros2ForUnity.unitypackage` won't be generated too.\n\n## Build troubleshooting\n\n- If you see one of the following errors:\n><script_name> is not digitally signed\n\n><script_name> cannot be loaded because running scripts is disabled on this system\n\nPlease execute `Set-ExecutionPolicy Bypass -Scope Process` in PS shell session to enable third party scripts execution only for this session. Otherwise please refer to official [Execution Policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.1).\n\n- If you see the following error:\n>     [4.437s] Traceback (most recent call last):\n>     [4.437s]   File \"<string>\", line 1, in <module>\n>     [4.437s]   File \"C:\\Python38\\lib\\site-packages\\numpy\\__init__.py\", line 148, in <module>\n>     [4.437s]     from . import _distributor_init\n>     [4.437s]   File \"C:\\Python38\\lib\\site-packages\\numpy\\_distributor_init.py\", line 26, in <module>\n>     [4.437s]     WinDLL(os.path.abspath(filename))\n>     [4.437s]   File \"C:\\Python38\\lib\\ctypes\\__init__.py\", line 373, in __init__\n>     [4.453s]     self._handle = _dlopen(self._name, mode)\n>     [4.453s] OSError: [WinError 193] %1 is not a valid Win32 application\n>     [4.469s] CMake Error at C:/dev/ros2_foxy/share/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake:213 (message)\n>     [4.469s]   execute_process(C:/Python38/python.exe -c 'import\n>     [4.469s]   numpy;print(numpy.get_include())') returned error code 1\n>     [4.469s] Call Stack (most recent call first):\n>     [4.469s]   C:/dev/ros2_foxy/share/ament_cmake_core/cmake/core/ament_execute_extensions.cmake:48 (include)\n>     [4.469s]   C:/dev/ros2_foxy/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:286 (ament_execute_extensions)\n>     [4.484s]   CMakeLists.txt:16 (rosidl_generate_interfaces)\nPlease reinstall `numpy` package from python by typing:\n```powershell\npip uninstall numpy\npip install numpy\n```\n\n**If no solution of your problem is present in the section above, please make sure to check out `ros2cs` [Troubleshooting section](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#troubleshooting)**\n\n## OS-Specific usage remarks\n\n> If the Asset is built with `-standalone` flag (the default), then nothing extra needs to be done.\nOtherwise, you have to source your ros distribution before launching either Unity3D Editor or Application.\n\n> Note that after you build the Asset, you can use it on a machine that has no ros2 installation (if built with `-standalone`).\n\n> You can simply copy over the `Ros2ForUnity` subdirectory to update your Asset.\n"
  },
  {
    "path": "README.md",
    "content": "\nRos2 For Unity\n===============\n\n> [!NOTE]  \n> This project is officially supported for [AWSIM](https://github.com/tier4/AWSIM) users of Autoware. However, the Robotec team is unable to provide support and maintain the project for the general\n> community. If you are looking for an alternative to Unity3D, [Open 3D Engine (O3DE)](https://o3de.org/) is a great, open-source and free simulation engine with excellent [ROS 2 integration](https://development--o3deorg.netlify.app/docs/user-guide/interactivity/), which Robotec is actively supporting and developing. \n\nROS2 For Unity is a high-performance communication solution to connect Unity3D and ROS2 ecosystem in a ROS2 \"native\" way. Communication is not bridged as in several other solutions, but instead it uses ROS2 middleware stack (rcl layer and below), which means you can have ROS2 nodes in your simulation.\nAdvantages of this module include:\n- High performance - higher throughput and considerably lower latencies comparing to bridging solutions.\n- Your simulation entities are real ROS2 nodes / publishers / subscribers. They will behave correctly with e.g. command line tools such as `ros2 topic`. They will respect QoS settings and can use ROS2 native time.\n- The module supplies abstractions and tools to use in your Unity project, including transformations, sensor interface, a clock, spinning loop wrapped in a MonoBehavior, handling initialization and shutdown.\n- Supports all standard ROS2 messages.\n- Custom messages are generated automatically with build, using standard ROS2 way. It is straightforward to generate and use them without having to define `.cs` equivalents by hand.\n- The module is wrapped as a Unity asset.\n\n## Platforms\n\nSupported OSes:\n- Ubuntu 22.04 (bash)\n- Ubuntu 20.04 (bash)\n- Windows 10 (powershell)\n- Windows 11* (powershel)\n\n> \\* ROS2 Galactic and Humble support only Windows 10 ([ROS 2 Windows system requirements](https://docs.ros.org/en/humble/Installation/Windows-Install-Binary.html#system-requirements)), but it is proven that it also works fine on Windows 11.\n\n\nSupported ROS2 distributions:\n- Galactic\n- Humble\n\nSupported Unity3d:\n- 2020+\n\nOlder versions of Unity3d may work, but the editor executable most probably won't be detected properly by deployment script. This would require user confirmation for using unsupported version.\n\nThis asset can be prepared in two flavours:\n\n- standalone mode, where no ROS2 installation is required on target machine, e.g., your Unity3D simulation server. All required dependencies are installed and can be used e.g. as a complete set of Unity3D plugins.\n- overlay mode, where the ROS2 installation is required on target machine. Only asset libraries and generated messages are installed therefore ROS2 instance must be sourced.\n\n## Releases\n\nThe best way to start quickly is to use our releases.\n\nYou can download pre-built [releases](https://github.com/RobotecAI/ros2-for-unity/releases) of the Asset that support both platforms and specific ros2 and Unity3D versions.\n\n## Building\n\n> **Note:** The project will pull `ros2cs` into the workspace, which also functions independently as it is a more general project aimed at any `C# / .Net` environment.\nIt has its own README and scripting, but for building the Unity Asset, please use instructions and scripting in this document instead, unless you also wish to run tests or examples for `ros2cs`.\n\nPlease see OS-specific instructions:\n- [Instructions for Ubuntu](README-UBUNTU.md)\n- [Instructions for Windows](README-WINDOWS.md)\n\n## Custom messages\n\nCustom messages can be included in the build by either:\n* listing them in `ros2_for_unity_custom_messages.repos` file, or\n* manually inserting them in `src/ros2cs` directory. If the folder doesn't exist, you must pull repositories first (see building steps for each OS).\n\n## Installation\n\n1. Perform building steps described in the OS-specific readme or download pre-built Unity package. Do not source `ros2-for-unity` nor `ros2cs` project into ROS2 workspace.\n1. Open or create Unity project.\n1. Import asset into project:\n    1. copy `install/asset/Ros2ForUnity` into your project `Assets` folder, or\n    1. if you have deployed an `.unitypackage` - import it in Unity Editor by selecting `Import Package` → `Custom Package`\n\n## Usage\n\n**Prerequisites**\n\n* If your build was prepared with `--standalone` flag then you are fine, and all you have to do is run the editor\n\notherwise\n\n* source ROS2 which matches the `Ros2ForUnity` version, then run the editor from within the very same terminal/console.\n\n**Initializing Ros2ForUnity**\n\n1. Initialize `Ros2ForUnity` by creating a \"hook\" object which will be your wrapper around ROS2. You have two options:\n    1. `ROS2UnityComponent` based on `MonoBehaviour` which must be attached to a `GameObject` somewhere in the scene, then:\n        ```c#\n        using ROS2;\n        ...\n        // Example method of getting component, if ROS2UnityComponent lives in different GameObject, just use different get component methods.\n        ROS2UnityComponent ros2Unity = GetComponent<ROS2UnityComponent>();\n        ```\n    1. or `ROS2UnityCore` which is a standard class that can be created anywhere\n        ```c#\n        using ROS2;\n        ...\n        ROS2UnityCore ros2Unity = new ROS2UnityCore();\n        ```\n1. Create a node. You must first check if `Ros2ForUnity` is initialized correctly:\n    ```c#\n    private ROS2Node ros2Node;\n    ...\n    if (ros2Unity.Ok()) {\n        ros2Node = ros2Unity.CreateNode(\"ROS2UnityListenerNode\");\n    }\n    ```\n\n**Publishing messages:**\n\n1. Create publisher\n    ```c#\n    private IPublisher<std_msgs.msg.String> chatter_pub;\n    ...\n    if (ros2Unity.Ok()){\n        chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>(\"chatter\"); \n    }\n    ```\n1. Send messages\n    ```c#\n    std_msgs.msg.String msg = new std_msgs.msg.String();\n    msg.Data = \"Hello Ros2ForUnity!\";\n    chatter_pub.Publish(msg);\n    ```\n\n**Subscribing to a topic**\n\n1. Create subscriber:\n    ```c#\n    private ISubscription<std_msgs.msg.String> chatter_sub;\n    ...\n    if (ros2Unity.Ok()) {\n        chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(\n            \"chatter\", msg => Debug.Log(\"Unity listener heard: [\" + msg.Data + \"]\"));\n    }\n    ```\n\n**Creating a service**\n\n1. Create service body:\n    ```c#\n    public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)\n    {\n        example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();\n        response.Sum = msg.A + msg.B;\n        return response;\n    }\n    ```\n\n1. Create a service with a service name and callback:\n    ```c#\n    IService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> service = \n        ros2Node.CreateService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(\n            \"add_two_ints\", addTwoInts);\n    ```\n\n**Calling a service**\n\n1. Create a client:\n    ```c#\n    private IClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> addTwoIntsClient;\n    ...\n    addTwoIntsClient = ros2Node.CreateClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(\n        \"add_two_ints\");\n    ```\n\n1. Create a request and call a service:\n    ```c#\n    example_interfaces.srv.AddTwoInts_Request request = new example_interfaces.srv.AddTwoInts_Request();\n    request.A = 1;\n    request.B = 2;\n    var response = addTwoIntsClient.Call(request);\n    ```\n\n1. You can also make an async call:\n    ```c#\n    Task<example_interfaces.srv.AddTwoInts_Response> asyncTask = addTwoIntsClient.CallAsync(request);\n    ...\n    asyncTask.ContinueWith((task) => { Debug.Log(\"Got answer \" + task.Result.Sum); });\n    ```\n### Examples\n\n1. Create a top-level object containing `ROS2UnityComponent.cs`. This is the central `Monobehavior` for `Ros2ForUnity` that manages all the nodes. Refer to class documentation for details.\n    > **Note:** Each example script looks for `ROS2UnityComponent` in its own game object. However, this is not a requirement, just example implementation.\n\n**Topics**\n1. Add `ROS2TalkerExample.cs` script to the very same game object.\n1. Add `ROS2ListenerExample.cs` script to the very same game object.\n\nOnce you start the project in Unity, you should be able to see two nodes talking with each other in  Unity Editor's console or use `ros2 node list` and `ros2 topic echo /chatter` to verify ros2 communication.\n\n**Services**\n1. Add `ROS2ServiceExample.cs` script to the very same game object.\n1. Add `ROS2ClientExample.cs` script to the very same game object.\n\nOnce you start the project in Unity, you should be able to see client node calling an example service.\n\n## Acknowledgements \n\nOpen-source release of ROS2 For Unity was made possible through cooperation with [TIER IV](https://tier4.jp). Thanks to encouragement, support and requirements driven by TIER IV the project was significantly improved in terms of portability, stability, core structure and user-friendliness.\n"
  },
  {
    "path": "build.ps1",
    "content": "\n<#\n.SYNOPSIS\n    Builds Ros2ForUnity asset\n.DESCRIPTION\n    This script builds Ros2DorUnity asset\n.PARAMETER with_tests\n    Build tests\n.PARAMETER standalone\n    Add ros2 binaries. Currently standalone flag is fixed to true, so there is no way to build without standalone libs. Parameter kept for future releases\n.PARAMETER clean_install\n    Makes a clean installation. Removes install dir before deploying\n#>\nParam (\n    [Parameter(Mandatory=$false)][switch]$with_tests=$false,\n    [Parameter(Mandatory=$false)][switch]$standalone=$false,\n    [Parameter(Mandatory=$false)][switch]$clean_install=$false\n)\n\n$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n\nif(-Not (Test-Path -Path \"$scriptPath\\src\\ros2cs\")) {\n    Write-Host \"Pull repositories with 'pull_repositories.ps1' first.\" -ForegroundColor Red\n    exit 1\n}\n\nWrite-Host $msg -ForegroundColor Green\n$options = @{\n    with_tests = $with_tests\n    standalone = $standalone\n}\n\nif($clean_install) {\n    Write-Host \"Cleaning install directory...\" -ForegroundColor White\n    Remove-Item -Path \"$scriptPath\\install\" -Force -Recurse -ErrorAction Ignore\n}\n\nif($standalone) {\n  & \"python\" $SCRIPTPATH\\src\\scripts\\metadata_generator.py --standalone\n} else {\n  & \"python\" $SCRIPTPATH\\src\\scripts\\metadata_generator.py\n}\n\n& \"$scriptPath\\src\\ros2cs\\build.ps1\" @options\nif($?) {\n    md -Force $scriptPath\\install\\asset | Out-Null\n    Copy-Item -Path $scriptPath\\src\\Ros2ForUnity -Destination $scriptPath\\install\\asset\\ -Recurse -Force\n    \n    $plugin_path=Join-Path -Path $scriptPath -ChildPath \"\\install\\asset\\Ros2ForUnity\\Plugins\\\"\n    Write-Host \"Deploying build to $plugin_path\" -ForegroundColor Green\n    & \"$scriptPath\\deploy_unity_plugins.ps1\" $plugin_path\n\n    Copy-Item -Path $scriptPath\\src\\Ros2ForUnity\\metadata_ros2cs.xml -Destination $scriptPath\\install\\asset\\Ros2ForUnity\\Plugins\\Windows\\x86_64\\\n    Copy-Item -Path $scriptPath\\src\\Ros2ForUnity\\metadata_ros2cs.xml -Destination $scriptPath\\install\\asset\\Ros2ForUnity\\Plugins\\\n} else {\n    Write-Host \"Ros2cs build failed!\" -ForegroundColor Red\n    exit 1\n}\n\n\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n    echo \"Usage: \"\n    echo \"\"\n    echo \"build.sh [--with-tests] [--standalone] [--clean-install]\"\n    echo \"\"\n    echo \"Options:\"\n    echo \"--with-tests - build with tests\"\n    echo \"--standalone - standalone version\"\n    echo \"--clean-install - makes a clean installation, removes install directory before deploying\"\n}\n\nif [ ! -d \"$SCRIPTPATH/src/ros2cs\" ]; then\n    echo \"Pull repositories with 'pull_repositories.sh' first.\"\n    exit 1\nfi\n\nOPTIONS=\"\"\nSTANDALONE=0\nTESTS=0\nCLEAN_INSTALL=0\n\nwhile [[ $# -gt 0 ]]; do\n  key=\"$1\"\n  case $key in\n    -t|--with-tests)\n      OPTIONS=\"$OPTIONS --with-tests\"\n      TESTS=1\n      shift # past argument\n      ;;\n    -s|--standalone)\n      if ! hash patchelf 2>/dev/null ; then\n        echo \"Patchelf missing. Standalone build requires patchelf. Install it via apt 'sudo apt install patchelf'.\"\n        exit 1\n      fi\n      OPTIONS=\"$OPTIONS --standalone\"\n      STANDALONE=1\n      shift # past argument\n      ;;\n    -c|--clean-install)\n      CLEAN_INSTALL=1\n      shift # past argument\n      ;;\n    -h|--help)\n      display_usage\n      exit 0\n      shift # past argument\n      ;;\n    *)    # unknown option\n      shift # past argument\n      ;;\n  esac\ndone\n\nif [ $CLEAN_INSTALL == 1 ]; then\n    echo \"Cleaning install directory...\"\n    rm -rf $SCRIPTPATH/install/*\nfi\n\nif [ $STANDALONE == 1 ]; then\n  python3 $SCRIPTPATH/src/scripts/metadata_generator.py --standalone\nelse\n  python3 $SCRIPTPATH/src/scripts/metadata_generator.py\nfi\n\nif $SCRIPTPATH/src/ros2cs/build.sh $OPTIONS; then\n    mkdir -p $SCRIPTPATH/install/asset && cp -R $SCRIPTPATH/src/Ros2ForUnity $SCRIPTPATH/install/asset/\n    $SCRIPTPATH/deploy_unity_plugins.sh $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/\n    cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/Linux/x86_64/metadata_ros2cs.xml\n    cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/metadata_ros2cs.xml\nelse\n    echo \"Ros2cs build failed!\"\n    exit 1\nfi\n"
  },
  {
    "path": "create_unity_package.ps1",
    "content": "\n<#\n.SYNOPSIS\n    Creates a 'unitypackage' from an input asset.\n.DESCRIPTION\n    This script screates a temporary Unity project in \"%USERPROFILE%\\AppData\\Local\\Temp\" directory, copy input asset and makes an unity package out of it. Valid Unity license is required.\n.PARAMETER unity_path\n    Unity editor executable path\n.PARAMETER input_asset\n    input asset to pack into unity package\n.PARAMETER package_name\n    Unity package name\n.PARAMETER output_dir\n    output file directory\n#>\nParam (\n    [Parameter(Mandatory=$true)][string]$unity_path,\n    [Parameter(Mandatory=$false)][string]$input_asset,\n    [Parameter(Mandatory=$false)][string]$package_name=\"Ros2ForUnity\",\n    [Parameter(Mandatory=$false)][string]$output_dir\n)\n\n$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n$temp_dir = $Env:TEMP\n\nif(-Not $PSBoundParameters.ContainsKey('input_asset')) {\n    $input_asset= Join-Path -Path $scriptPath -ChildPath \"\\install\\asset\\Ros2ForUnity\"\n}\n\nif(-Not $PSBoundParameters.ContainsKey('output_dir')) {\n    $output_dir= Join-Path -Path $scriptPath -ChildPath \"\\install\\unity_package\"\n}\n\nif(-Not (Test-Path -Path \"$input_asset\")) {\n    Write-Host \"Input asset '$input_asset' doesn't exist! Use 'build.ps1' to build project first.\" -ForegroundColor Red\n    exit 1\n}\n\nif(-Not (Test-Path -Path \"$output_dir\")) {\n    mkdir ${output_dir} | Out-Null\n}\n\n& \"$unity_path\" -version | Tee-Object -Variable unity_version | Out-Null\n\nif ($unity_version -match '^[0-9]{4}\\.[0-9]*\\.[0-9]*[f]?[0-9]*$') {\n    Write-Host \"Unity editor confirmed.\"\n} else {\n    while (1) {\n        $confirmation = Read-Host \"Can't confirm Unity editor. Do you want to force $unity_path as an Unity editor executable? [y]es or [n]o\"\n        if ($confirmation -eq 'y' -or $confirmation -eq 'Y') {\n            break;\n        } elseif ( $confirmation -eq 'n' -or $confirmation -eq 'N' ) {\n            exit 1;\n        } else {\n            Write-Host \"Please answer [y]es or [n]o.\";\n        }\n    }\n}\nWrite-Host \"Using ${unity_path} editor.\"\n\n$tmp_project_path = Join-Path -Path \"$temp_dir\" -ChildPath \"\\ros2cs_unity_project\\$unity_version\"\n\n# Create temp project\nif(Test-Path -Path \"$tmp_project_path\") {\n    Write-Host \"Found existing temporary project for Unity $unity_version.\"\n    Remove-Item -Path \"$tmp_project_path\\Assets\\*\" -Force -Recurse -ErrorAction Ignore\n} else {\n    Write-Host \"Creating Unity temporary project for Unity $unity_version...\"\n    & \"$unity_path\" -createProject \"$tmp_project_path\" -batchmode -quit | Out-Null\n}\n\n# Copy asset\nWrite-Host \"Copying asset '$input_asset' to export...\"\nCopy-Item -Path \"$input_asset\" -Destination \"$tmp_project_path\\Assets\\$package_name\" -Recurse\n\n# Creating asset\nWrite-Host \"Saving unitypackage '$output_dir\\$package_name.unitypackage'...\"\n& \"$unity_path\" -projectPath \"$tmp_project_path\" -exportPackage \"Assets\\$package_name\" \"$output_dir\\$package_name.unitypackage\" -batchmode -quit | Out-Null\n\n# Cleaning up\nWrite-Host \"Cleaning up temporary project...\"\nRemove-Item -Path \"$tmp_project_path\\Assets\\*\" -Force -Recurse -ErrorAction Ignore\n\nWrite-Host \"Done!\" -ForegroundColor Green\n\n"
  },
  {
    "path": "create_unity_package.sh",
    "content": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n  echo \"This script creates a temporary Unity project in '/tmp' directory, copy input asset and makes an unity package out of it. Valid Unity license is required.\"\n  echo \"\"\n  echo \"Usage:\" \n  echo \"create_unity_package.sh -u <UNITY_PATH> -i [INPUT_ASSET] -p [PACKAGE_NAME] -o [OUTPUT_DIR]\"\n  echo \"\"\n  echo \"UNITY_PATH - Unity editor executable path\"\n  echo \"INPUT_ASSET - input asset to pack into unity package, default = 'install/asset/Ros2ForUnity'\"\n  echo \"PACKAGE_NAME - unity package name, default = 'Ros2ForUnity'\"\n  echo \"OUTPUT_DIR - output file directory, default = 'install/unity_package'\"\n}\n\nUNITY_PATH=\"\"\nINPUT_ASSET=\"install/asset/Ros2ForUnity\"\nPACKAGE_NAME=\"Ros2ForUnity\"\nOUTPUT_DIR=\"$SCRIPTPATH/install/unity_package\"\n\nwhile [[ $# -gt 0 ]]; do\n  key=\"$1\"\n\n  case $key in\n    -u|--unity-path)\n      UNITY_PATH=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    -p|--package_name)\n      PACKAGE_NAME=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    -i|--input-directory)\n      INPUT_ASSET=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    -o|--output-directory)\n      OUTPUT_DIR=\"$2\"\n      shift # past argument\n      shift # past value\n      ;;\n    -h|--help)\n      display_usage\n      exit 0\n      shift # past argument\n      ;;\n    *)    # unknown option\n      shift # past argument\n      ;;\n  esac\ndone\n\nif [ -z \"$UNITY_PATH\" ] || [ -z \"$PACKAGE_NAME\" ] || [ -z \"$INPUT_ASSET\" ] || [ -z \"$OUTPUT_DIR\" ]; then\n    echo -e \"\\nMissing arguments!\"\n    echo \"\"\n    display_usage\n    exit 1\nfi\n\nif [ ! -d \"$INPUT_ASSET\" ]; then\n    echo \"Input asset '$INPUT_ASSET' doesn't exist!  Use 'build.sh' to build project first.\"\n    exit 1\nfi\n\nUNITY_VERSION=`$UNITY_PATH -version`\n\n# Test if unity editor is valid\nif [[ $UNITY_VERSION =~ ^[0-9]{4}\\.[0-9]*\\.[0-9]*[f]?[0-9]*$ ]]; then\n    echo \"Unity editor confirmed.\"\nelse\n    while true; do\n      read -p \"Can't confirm Unity editor. Do you want to force \\\"$UNITY_PATH\\\" as an Unity editor executable? [y]es or [N]o: \" yn\n      yn=${yn:-\"n\"}\n      case $yn in\n          [Yy]* ) break;;\n          [Nn]* ) exit 1;;\n          * ) echo \"Please answer [y]es or [n]o.\";;\n      esac\n    done\nfi\n\necho \"Using \\\"${UNITY_PATH}\\\" editor.\"\n\nTMP_PROJECT_PATH=/tmp/ros2cs_unity_project/$UNITY_VERSION\n# Create temp project\nif [ -d \"$TMP_PROJECT_PATH\" ]; then\n    echo \"Found existing temporary project for Unity $UNITY_VERSION.\"\n    rm -rf $TMP_PROJECT_PATH/Assets/*\nelse\n  rm -rf $TMP_PROJECT_PATH\n  echo \"Creating Unity temporary project for Unity $UNITY_VERSION...\"\n  $UNITY_PATH -createProject $TMP_PROJECT_PATH -batchmode -quit\nfi\n\n# Copy asset\necho \"Copying asset to export...\"\ncp -r \"$INPUT_ASSET\" \"$TMP_PROJECT_PATH/Assets/$PACKAGE_NAME\"\n\n# Creating asset\necho \"Saving unitypackage '$OUTPUT_DIR/$PACKAGE_NAME.unitypackage'...\"\nmkdir -p $OUTPUT_DIR\n$UNITY_PATH -projectPath \"$TMP_PROJECT_PATH\" -exportPackage \"Assets/$PACKAGE_NAME\" \"$OUTPUT_DIR/$PACKAGE_NAME.unitypackage\" -batchmode -quit\n\n# Cleaning up\necho \"Cleaning up temporary project...\"\nrm -rf $TMP_PROJECT_PATH/Assets/*\n\necho \"Done!\"\n\n"
  },
  {
    "path": "deploy_unity_plugins.ps1",
    "content": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n$pluginDir=$args[0]\n\nfunction Print-Help {\n\"\nUsage: \ndeploy_unity_plugins.ps1 <PLUGINS_DIR>\n\nPLUGINS_DIR - Ros2ForUnity/Plugins.\n\"\n}\n\nif (([string]::IsNullOrEmpty($pluginDir)) -Or $args[0] -eq \"--help\" -Or $args[0] -eq \"-h\")\n{\n    Print-Help\n    exit\n}\n\nif (Test-Path -Path $pluginDir) {\n    Write-Host \"Copying plugins to to: '$pluginDir' ...\"\n    Get-ChildItem $scriptPath\\install\\lib\\dotnet\\ -Recurse -Exclude @('*.pdb') | Copy-Item -Destination ${pluginDir}\n    Write-Host \"Plugins copied to: '$pluginDir'\" -ForegroundColor Green\n    if(-not (Test-Path -Path $pluginDir\\Windows\\x86_64\\)) {\n        mkdir ${pluginDir}\\Windows\\x86_64\\\n    }\n    Write-Host \"Copying libraries to: '$pluginDir\\Windows\\x86_64\\' ...\"\n    Get-ChildItem $scriptPath\\install\\bin\\ -Recurse -Exclude @('*_py.dll', '*_python.dll') | Copy-Item -Destination ${pluginDir}\\Windows\\x86_64\\\n    if(-not (Test-Path -Path $scriptPath\\install\\standalone\\)) {\n        mkdir $scriptPath\\install\\standalone\n    }\n    (Copy-Item -Path $scriptPath\\install\\standalone\\*.dll -Destination ${pluginDir}\\Windows\\x86_64\\ 4>&1).Message\n    if(-not (Test-Path -Path $scriptPath\\install\\resources\\)) {\n        mkdir $scriptPath\\install\\resources\n    }\n    (Copy-Item -Path $scriptPath\\install\\resources\\*.dll -Destination ${pluginDir}\\Windows\\x86_64\\ 4>&1).Message\n    Write-Host \"Libraries copied to '${pluginDir}\\Windows\\x86_64\\'\" -ForegroundColor Green\n} else {\n    Write-Host \"Plugins directory: '$pluginDir' doesn't exist. Please create it first manually.\" -ForegroundColor Red\n}\n"
  },
  {
    "path": "deploy_unity_plugins.sh",
    "content": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ $# -eq 0 ] || [ $1 = \"-h\" ] || [ $1 = \"--help\" ]; then\n  echo \"Usage:\" \n  echo \"deploy_unity_plugins.sh <PLUGINS_DIR>\"\n  echo \"\"\n  echo \"PLUGINS_DIR - Ros2ForUnity/Plugins folder.\"\n  exit 1\nfi\n\npluginDir=$1\n\nmkdir -p  ${pluginDir}/Linux/x86_64/\nfind install/lib/dotnet/ -maxdepth 1 -not -name \"*.pdb\" -type f -exec cp {} ${pluginDir} \\;\ncp $SCRIPTPATH/install/standalone/* ${pluginDir}/Linux/x86_64/ 2>/dev/null\nfind install/lib/ -maxdepth 1 -not -name \"*_python.so\" -type f -exec cp {} ${pluginDir}/Linux/x86_64/ \\;\ncp $SCRIPTPATH/install/resources/*.so ${pluginDir}/Linux/x86_64/ 2>/dev/null\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "ARG ROS2_DISTRO=humble\nFROM ros:${ROS2_DISTRO}-ros-base\n\nRUN apt update && apt install -y ros-${ROS_DISTRO}-test-msgs ros-${ROS_DISTRO}-fastrtps ros-${ROS_DISTRO}-rmw-fastrtps-cpp ros-${ROS_DISTRO}-cyclonedds ros-${ROS_DISTRO}-rmw-cyclonedds-cpp\n\nRUN apt update && apt install -y curl wget git\n\nRUN curl -s https://packagecloud.io/install/repositories/dirk-thomas/vcstool/script.deb.sh | sudo bash\nRUN apt update && apt install -y python3-vcstool\n\nRUN apt update && apt install -y apt-transport-https patchelf dotnet-sdk-6.0\nRUN apt update && apt install -y ffmpeg libsm6 libxext6 libgtk-3-0\n\nADD entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh\n\nRUN mkdir -p /workdir/ros2-for-unity\nRUN chmod -R 777 /workdir\nRUN chmod -R 777 /home\n\nENTRYPOINT [ \"/entrypoint.sh\" ]\n"
  },
  {
    "path": "docker/README.md",
    "content": "Ros2 For Unity Docker\n===============\n\nCurrently only building asset on Ubuntu is supported. Build windows version is not supported.\n\n## Build docker image\n\n1. Source ROS2 (foxy or galactic):\n\n```bash\n. /opt/ros/<ROS_DISTRO>/setup.bash\n```\n\n2. Build image - image will be based on sourced ROS2 version:\n\n```bash\n./build_image.sh\n```\n\n## Using docker container\n\n1. Run docker container. Container will fetch `master` version of `ros2-for-unity`:\n\n```bash\n./run_container.sh\n```\n\n2. Build asset. `./run_container.sh` script mounts `install` host directory inside docker, so you can find install results on host machine:\n\n```bash\n./build.sh --with-tests\n```\n\n## Adding custom messages\n\nYou can add custom messages by putting them inside `docker/custom_messages` folder or just simply `git clone` them inside docker containers `/workdir/ros2-for-unity/src/ros2cs/src/custom_messages`"
  },
  {
    "path": "docker/build_image.sh",
    "content": "#!/bin/bash\n\nif [ -z \"$ROS_DISTRO\" ]; then\n    echo \"Source your ros2 distro first.\"\n    exit 1\nfi\n\ndocker build . --build-arg ROS2_DISTRO=$ROS_DISTRO --tag ros2-for-unity\n"
  },
  {
    "path": "docker/custom_messages/INSERT_CUSTOM_MESSAGES_HERE",
    "content": ""
  },
  {
    "path": "docker/entrypoint.sh",
    "content": "#!/bin/bash\n\nsource \"/opt/ros/$ROS_DISTRO/setup.bash\"\n\necho \"######################################################################\"\necho \"\"\necho \"Cloning recent version of 'ros2-for-unity'\"\necho \"\"\necho \"######################################################################\"\necho \"\"\n\ngit clone https://github.com/RobotecAI/ros2-for-unity.git /workdir/.ros2-for-unity\n\nshopt -s dotglob\nmkdir -p /workdir/ros2-for-unity\nmv /workdir/.ros2-for-unity/* /workdir/ros2-for-unity\ncd /workdir/ros2-for-unity/ && ./pull_repositories.sh\nmkdir -p /home/$(whoami)\ngit config --global --add safe.directory /workdir/ros2-for-unity\nshopt -u dotglob\n\nln -s /workdir/custom_messages /workdir/ros2-for-unity/src/ros2cs/src/custom_messages\n\necho \"\"\necho \"######################################################################\"\necho \"\"\necho \"Welcome to 'ros2-for-unity' docker container. Your ROS2 distro is $ROS_DISTRO.\"\necho \"\"\necho \"Type './build.sh' to build 'ros2-for-unity'. You will find installed libs on your host machine inside 'install' directory\"\necho \"\"\necho \"######################################################################\"\necho \"\"\n\nexec bash\n"
  },
  {
    "path": "docker/run_container.sh",
    "content": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nmkdir -p $SCRIPTPATH/../install\ndocker run \\\n--rm \\\n-it \\\n--name ros2-for-unity \\\n--user $(id -u):$(id -g) \\\n-v /etc/passwd:/etc/passwd:ro \\\n-v /etc/group:/etc/group:ro \\\n-v /etc/shadow:/etc/shadow:ro \\\n-v $(pwd)/../install:/workdir/ros2-for-unity/install:rw \\\n-v $(pwd)/custom_messages:/workdir/custom_messages \\\nros2-for-unity \\\nbash\n"
  },
  {
    "path": "pull_repositories.ps1",
    "content": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n\nif (([string]::IsNullOrEmpty($Env:ROS_DISTRO)))\n{\n    Write-Host \"Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported.\" -ForegroundColor Red\n    exit\n}\n\n$ros2cs_repos = Join-Path -Path $scriptPath -ChildPath \"\\ros2cs.repos\"\n$custom_repos = Join-Path -Path $scriptPath -ChildPath \"\\ros2_for_unity_custom_messages.repos\"\n\nWrite-Host \"=========================================\"\nWrite-Host \"* Pulling ros2cs repository:\"\nvcs import --input $ros2cs_repos\n\nWrite-Host \"\"\nWrite-Host \"=========================================\"\nWrite-Host \"Pulling custom repositories:\"\nvcs import --input $custom_repos\n\nWrite-Host \"\"\nWrite-Host \"=========================================\"\nWrite-Host \"Pulling ros2cs dependencies:\"\n& \"$scriptPath/src/ros2cs/get_repos.ps1\"\n"
  },
  {
    "path": "pull_repositories.sh",
    "content": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ -z \"${ROS_DISTRO}\" ]; then\n    echo \"Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported\"\n    exit 1\nfi\n\necho \"=========================================\"\necho \"* Pulling ros2cs repository:\"\nvcs import < \"ros2cs.repos\"\n\necho \"\"\necho \"=========================================\"\necho \"Pulling custom repositories:\"\nvcs import < \"ros2_for_unity_custom_messages.repos\"\n\necho \"\"\necho \"=========================================\"\necho \"Pulling ros2cs dependencies:\"\ncd \"$SCRIPTPATH/src/ros2cs\"\n./get_repos.sh\ncd -\n"
  },
  {
    "path": "ros2_for_unity_custom_messages.repos",
    "content": "# NOTE: Use this file if you want to build with custom messages that reside in a separate remote repo.\n# NOTE: use the following format\n\nrepositories:\n#  src/ros2cs/custom_messages/<package_name>:\n#    type: git\n#    url: <repo_url>\n#    version: <repo_branch>\n#  custom_messages/<package2_name>:\n#    ...\n#    ...\n\n"
  },
  {
    "path": "ros2cs.repos",
    "content": "repositories:\n  src/ros2cs/:\n    type: git\n    url:     https://github.com/RobotecAI/ros2cs.git\n    version: 1.3.0\n"
  },
  {
    "path": "src/Ros2ForUnity/COLCON_IGNORE",
    "content": ""
  },
  {
    "path": "src/Ros2ForUnity/Plugins.meta",
    "content": "fileFormatVersion: 2\nguid: 79b46a636d96b67468a29e597cb7b06a\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/PostInstall.cs",
    "content": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#if UNITY_EDITOR\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text.RegularExpressions;\nusing UnityEngine;\nusing UnityEditor;\nusing UnityEditor.Build;\nusing UnityEditor.Build.Reporting;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// An internal class responsible for installing ros2-for-unity metadata files \n/// </summary>\ninternal class PostInstall : IPostprocessBuildWithReport\n{\n    public int callbackOrder { get { return 0; } }\n    public void OnPostprocessBuild(BuildReport report)\n    {\n        var r2fuMetadataName = \"metadata_ros2_for_unity.xml\";\n        var r2csMetadataName = \"metadata_ros2cs.xml\";\n\n        // FileUtil.CopyFileOrDirectory: All file separators should be forward ones \"/\".\n        var r2fuMeta = ROS2ForUnity.GetRos2ForUnityPath() + \"/\" + r2fuMetadataName; \n        var r2csMeta = ROS2ForUnity.GetPluginPath() + \"/\" + r2csMetadataName;\n        var outputDir = Directory.GetParent(report.summary.outputPath);\n        var execFilename = Path.GetFileNameWithoutExtension(report.summary.outputPath);\n        FileUtil.CopyFileOrDirectory(\n            r2fuMeta, outputDir + \"/\" + execFilename + \"_Data/\" + r2fuMetadataName);\n        if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinux64) {\n            FileUtil.CopyFileOrDirectory(\n                r2csMeta, outputDir + \"/\" + execFilename + \"_Data/Plugins/\" + r2csMetadataName);\n\n            // Copy versioned libraries (Unity skips them)\n            Regex soWithVersionReg = new Regex(@\".*\\.so(\\.[0-9])+$\");\n            var versionedLibs = new List<String>(Directory.GetFiles(ROS2ForUnity.GetPluginPath()))\n                                    .Where(path => soWithVersionReg.IsMatch(path))\n                                    .ToList();\n            foreach (var libPath in versionedLibs) {\n                FileUtil.CopyFileOrDirectory(\n                    libPath, outputDir + \"/\" + execFilename + \"_Data/Plugins/\" + Path.GetFileName(libPath));\n            }\n        } else {\n            FileUtil.CopyFileOrDirectory(\n                r2csMeta, outputDir + \"/\" + execFilename + \"_Data/Plugins/x86_64/\" + r2csMetadataName);\n        }\n    }\n\n}\n\n}\n#endif\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ClientExample.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing UnityEngine;\nusing ROS2;\n\nusing addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;\nusing addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;\n\n/// <summary>\n/// An example class provided for testing of basic ROS2 client\n/// </summary>\npublic class ROS2ClientExample : MonoBehaviour\n{\n    private ROS2UnityComponent ros2Unity;\n    private ROS2Node ros2Node;\n    private IClient<addTwoIntsReq, addTwoIntsResp> addTwoIntsClient;\n    private bool isRunning = false;\n    private Task<addTwoIntsResp> asyncTask;\n\n    IEnumerator periodicAsyncCall()\n    {\n        while (ros2Unity.Ok())\n        {\n\n            while (!addTwoIntsClient.IsServiceAvailable())\n            {\n                yield return new WaitForSecondsRealtime(1);\n            }\n\n            addTwoIntsReq request = new addTwoIntsReq();\n            request.A = Random.Range(0, 100);\n            request.B = Random.Range(0, 100);\n            \n            asyncTask = addTwoIntsClient.CallAsync(request);\n            asyncTask.ContinueWith((task) => { Debug.Log(\"Got async answer \" + task.Result.Sum); });\n            \n            yield return new WaitForSecondsRealtime(1);\n        }\n    }\n\n    IEnumerator periodicCall()\n    {\n        while (ros2Unity.Ok())\n        {\n\n            while (!addTwoIntsClient.IsServiceAvailable())\n            {\n                yield return new WaitForSecondsRealtime(1);\n            }\n\n            addTwoIntsReq request = new addTwoIntsReq();\n            request.A = Random.Range(0, 100);\n            request.B = Random.Range(0, 100);\n            var response = addTwoIntsClient.Call(request);\n\n            Debug.Log(\"Got sync answer \" + response.Sum);\n\n            yield return new WaitForSecondsRealtime(1);\n        }\n    }\n\n    void Start()\n    {\n        ros2Unity = GetComponent<ROS2UnityComponent>();\n        if (ros2Unity.Ok())\n        {\n            if (ros2Node == null)\n            {\n                ros2Node = ros2Unity.CreateNode(\"ROS2UnityClient\");\n                addTwoIntsClient = ros2Node.CreateClient<addTwoIntsReq, addTwoIntsResp>(\n                    \"add_two_ints\");\n            }\n        }\n    }\n\n    void Update()\n    {\n        if (!isRunning)\n        {\n            isRunning = true;\n\n            // Async calls\n            StartCoroutine(periodicAsyncCall());\n\n            // Sync calls\n            StartCoroutine(periodicCall());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing System;\r\nusing System.IO;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\nusing UnityEditor;\r\nusing System.Xml;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// An internal class responsible for handling checking, proper initialization and shutdown of ROS2cs,\r\n/// </summary>\r\ninternal class ROS2ForUnity\r\n{\r\n    private static bool isInitialized = false;\r\n    private static string ros2ForUnityAssetFolderName = \"Ros2ForUnity\";\r\n    private XmlDocument ros2csMetadata = new XmlDocument();\r\n    private XmlDocument ros2ForUnityMetadata = new XmlDocument();\r\n\r\n    public enum Platform\r\n    {\r\n        Windows,\r\n        Linux\r\n    }\r\n    \r\n    public static Platform GetOS()\r\n    {\r\n        if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer)\r\n        {\r\n            return Platform.Linux;\r\n        }\r\n        else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)\r\n        {\r\n            return Platform.Windows;\r\n        }\r\n        throw new System.NotSupportedException(\"Only Linux and Windows are supported\");\r\n    }\r\n\r\n    private static bool InEditor() {\r\n        return Application.isEditor;\r\n    }\r\n    \r\n    private static string GetOSName()\r\n    {\r\n        switch (GetOS())\r\n        {\r\n            case Platform.Linux:\r\n                return \"Linux\";\r\n            case Platform.Windows:\r\n                return \"Windows\";\r\n            default:\r\n                throw new System.NotSupportedException(\"Only Linux and Windows are supported\");\r\n        }\r\n    }\r\n    \r\n    private string GetEnvPathVariableName()\r\n    {\r\n      string envVariable = \"LD_LIBRARY_PATH\";\r\n      if (GetOS() == Platform.Windows)\r\n      {\r\n          envVariable = \"PATH\";\r\n      }\r\n      return envVariable;\r\n    }\r\n\r\n    private string GetEnvPathVariableValue()\r\n    {\r\n        return Environment.GetEnvironmentVariable(GetEnvPathVariableName());\r\n    }\r\n\r\n    public static string GetRos2ForUnityPath()\r\n    {\r\n        char separator = Path.DirectorySeparatorChar;\r\n        string appDataPath = Application.dataPath;\r\n        string pluginPath = appDataPath;\r\n\r\n        if (InEditor()) {\r\n            pluginPath += separator + ros2ForUnityAssetFolderName;\r\n        }\r\n        return pluginPath; \r\n    }\r\n\r\n    public static string GetPluginPath()\r\n    {\r\n        char separator = Path.DirectorySeparatorChar;\r\n        string ros2ForUnityPath = GetRos2ForUnityPath();\r\n        string pluginPath = ros2ForUnityPath;\r\n        \r\n        pluginPath += separator + \"Plugins\";\r\n        \r\n        if (InEditor()) {\r\n            pluginPath += separator + GetOSName();\r\n        }\r\n\r\n        if (InEditor() || GetOS() == Platform.Windows)\r\n        {\r\n           pluginPath += separator + \"x86_64\";\r\n        }\r\n        \r\n        if (GetOS() == Platform.Windows)\r\n        {\r\n           pluginPath = pluginPath.Replace(\"/\", \"\\\\\");\r\n        }\r\n\r\n        return pluginPath;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Function responsible for setting up of environment paths for standalone builds\r\n    /// </summary>\r\n    /// <description>\r\n    /// Note that on Linux, LD_LIBRARY_PATH as used for dlopen() is determined on process start and this change won't\r\n    /// affect it. Ros2 looks for rmw implementation based on this variable (independently) and the change\r\n    /// is effective for this process, however rmw implementation's dependencies itself are loaded by dynamic linker \r\n    /// anyway so setting it for Linux is pointless.\r\n    /// </description>\r\n    private void SetEnvPathVariable()\r\n    {\r\n        string currentPath = GetEnvPathVariableValue();\r\n        string pluginPath = GetPluginPath();\r\n        \r\n        char envPathSep = ':';\r\n        if (GetOS() == Platform.Windows)\r\n        {\r\n            envPathSep = ';';\r\n        }\r\n\r\n        Environment.SetEnvironmentVariable(GetEnvPathVariableName(), pluginPath + envPathSep + currentPath);\r\n    }\r\n\r\n    public bool IsStandalone() {\r\n        return Convert.ToBoolean(Convert.ToInt16(GetMetadataValue(ros2csMetadata, \"/ros2cs/standalone\")));\r\n    }\r\n\r\n    public string GetROSVersion()\r\n    {\r\n        string ros2SourcedCodename = GetROSVersionSourced();\r\n        string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, \"/ros2_for_unity/ros2\");\r\n\r\n        //  Sourced ROS2 libs takes priority\r\n        if (string.IsNullOrEmpty(ros2SourcedCodename)) {\r\n            return ros2FromRos4UMetadata;\r\n        }\r\n        \r\n        return ros2SourcedCodename;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as\r\n    /// the current sourced ros version matches ros2cs binaries.\r\n    /// </summary>\r\n    public void CheckIntegrity()\r\n    {\r\n        string ros2SourcedCodename = GetROSVersionSourced();\r\n        string ros2FromRos2csMetadata = GetMetadataValue(ros2csMetadata, \"/ros2cs/ros2\");\r\n        string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, \"/ros2_for_unity/ros2\");\r\n\r\n        if (ros2FromRos4UMetadata != ros2FromRos2csMetadata) {\r\n            Debug.LogError(\r\n                \"ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. \" +\r\n                \"This is caused by mixing versions/builds. Plugin might not work correctly.\"\r\n            );\r\n        }\r\n\r\n        if(!IsStandalone() && ros2SourcedCodename != ros2FromRos2csMetadata) {\r\n            Debug.LogError(\r\n                \"ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. \" +\r\n                \"This is caused by mixing versions/builds. Plugin might not work correctly.\"\r\n            );\r\n        }\r\n\r\n        if (IsStandalone() && !string.IsNullOrEmpty(ros2SourcedCodename)) {\r\n            Debug.LogError(\r\n                \"You should not source ROS2 in 'ros2-for-unity' standalone build. \" +\r\n                \"Plugin might not work correctly.\"\r\n            );\r\n        }\r\n    }\r\n\r\n    public string GetROSVersionSourced()\r\n    {\r\n        return Environment.GetEnvironmentVariable(\"ROS_DISTRO\");\r\n    }\r\n\r\n    /// <summary>\r\n    /// Check if the ros version is supported, only applicable to non-standalone plugin versions\r\n    /// (i. e. without ros2 libraries included in the plugin).\r\n    /// </summary>\r\n    private void CheckROSSupport(string ros2Codename)\r\n    {\r\n        List<string> supportedVersions = new List<string>() { \"foxy\", \"galactic\", \"humble\", \"rolling\" };\r\n        var supportedVersionsString = String.Join(\", \", supportedVersions);\r\n        if (string.IsNullOrEmpty(ros2Codename))\r\n        {\r\n            string errMessage = \"No ROS environment sourced. You need to source your ROS2 \" + supportedVersionsString\r\n              + \" environment before launching Unity (ROS_DISTRO env variable not found)\";\r\n            Debug.LogError(errMessage);\r\n#if UNITY_EDITOR\r\n            EditorApplication.isPlaying = false;\r\n            throw new System.InvalidOperationException(errMessage);\r\n#else\r\n            const int ROS_NOT_SOURCED_ERROR_CODE = 33;\r\n            Application.Quit(ROS_NOT_SOURCED_ERROR_CODE);\r\n#endif\r\n        }\r\n\r\n        if (!supportedVersions.Contains(ros2Codename))\r\n        {\r\n            string errMessage = \"Currently sourced ROS version differs from supported one. Sourced: \" + ros2Codename\r\n              + \", supported: \" + supportedVersionsString + \".\";\r\n            Debug.LogError(errMessage);\r\n#if UNITY_EDITOR\r\n            EditorApplication.isPlaying = false;\r\n            throw new System.NotSupportedException(errMessage);\r\n#else\r\n            const int ROS_BAD_VERSION_CODE = 34;\r\n            Application.Quit(ROS_BAD_VERSION_CODE);\r\n#endif\r\n        } else if (ros2Codename.Equals(\"rolling\") ) {\r\n            Debug.LogWarning(\"You are using ROS2 rolling version. Bleeding edge version might not work correctly.\");\r\n        }\r\n    }\r\n\r\n    private void RegisterCtrlCHandler()\r\n    {\r\n#if ENABLE_MONO\r\n        // Il2CPP build does not support Console.CancelKeyPress currently\r\n        Console.CancelKeyPress += (sender, eventArgs) => {\r\n            eventArgs.Cancel = true;\r\n            DestroyROS2ForUnity();\r\n        };\r\n#endif\r\n    }\r\n\r\n    private void ConnectLoggers()\r\n    {\r\n        Ros2csLogger.setCallback(LogLevel.ERROR, Debug.LogError);\r\n        Ros2csLogger.setCallback(LogLevel.WARNING, Debug.LogWarning);\r\n        Ros2csLogger.setCallback(LogLevel.INFO, Debug.Log);\r\n        Ros2csLogger.setCallback(LogLevel.DEBUG, Debug.Log);\r\n        Ros2csLogger.LogLevel = LogLevel.WARNING;\r\n    }\r\n\r\n    private string GetMetadataValue(XmlDocument doc, string valuePath)\r\n    {\r\n        return doc.DocumentElement.SelectSingleNode(valuePath).InnerText;\r\n    }\r\n\r\n    private void LoadMetadata() \r\n    {\r\n        char separator = Path.DirectorySeparatorChar;\r\n        try\r\n        {\r\n            ros2csMetadata.Load(GetPluginPath() + separator + \"metadata_ros2cs.xml\");\r\n            ros2ForUnityMetadata.Load(GetRos2ForUnityPath() + separator + \"metadata_ros2_for_unity.xml\");\r\n        }\r\n        catch (System.IO.FileNotFoundException)\r\n        {\r\n#if UNITY_EDITOR\r\n            var errMessage = \"Could not find metadata files.\";\r\n            EditorApplication.isPlaying = false;\r\n            throw new System.IO.FileNotFoundException(errMessage);\r\n#else\r\n            const int NO_METADATA = 1;\r\n            Application.Quit(NO_METADATA);\r\n#endif\r\n        }\r\n    }\r\n\r\n    internal ROS2ForUnity()\r\n    {\r\n        // Load metadata\r\n        LoadMetadata();\r\n        string currentRos2Version = GetROSVersion();\r\n        string standalone = IsStandalone() ? \"standalone\" : \"non-standalone\";\r\n\r\n        // Self checks\r\n        CheckROSSupport(currentRos2Version);\r\n        CheckIntegrity();\r\n\r\n        // Library loading\r\n        if (GetOS() == Platform.Windows) {\r\n            // Windows version can run standalone, modifies PATH to ensure all plugins visibility\r\n            SetEnvPathVariable();\r\n        } else {\r\n            // For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.\r\n            ROS2.GlobalVariables.absolutePath = GetPluginPath() + \"/\";\r\n            if (currentRos2Version == \"foxy\") {\r\n                ROS2.GlobalVariables.preloadLibrary = true;\r\n                ROS2.GlobalVariables.preloadLibraryName = \"librcpputils.so\";\r\n            }\r\n        }\r\n\r\n        // Initialize\r\n        ConnectLoggers();\r\n        Ros2cs.Init();\r\n        RegisterCtrlCHandler();\r\n\r\n        string rmwImpl = Ros2cs.GetRMWImplementation();\r\n\r\n        Debug.Log(\"ROS2 version: \" + currentRos2Version + \". Build type: \" + standalone + \". RMW: \" + rmwImpl);\r\n\r\n#if UNITY_EDITOR\r\n        EditorApplication.playModeStateChanged += this.EditorPlayStateChanged;\r\n        EditorApplication.quitting += this.DestroyROS2ForUnity;\r\n#endif\r\n        isInitialized = true;\r\n    }\r\n\r\n    private static void ThrowIfUninitialized(string callContext)\r\n    {\r\n        if (!isInitialized)\r\n        {\r\n            throw new InvalidOperationException(\"Ros2 For Unity is not initialized, can't \" + callContext);\r\n        }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Check if ROS2 module is properly initialized and no shutdown was called yet\r\n    /// </summary>\r\n    /// <returns>The state of ROS2 module. Should be checked before attempting to create or use pubs/subs</returns>\r\n    public bool Ok()\r\n    {\r\n        if (!isInitialized)\r\n        {\r\n            return false;\r\n        }\r\n        return Ros2cs.Ok();\r\n    }\r\n\r\n    internal void DestroyROS2ForUnity()\r\n    {\r\n        if (isInitialized)\r\n        {\r\n            Debug.Log(\"Shutting down Ros2 For Unity\");\r\n            Ros2cs.Shutdown();\r\n            isInitialized = false;\r\n        }\r\n    }\r\n\r\n    ~ROS2ForUnity()\r\n    {\r\n        DestroyROS2ForUnity();\r\n    }\r\n\r\n#if UNITY_EDITOR\r\n    void EditorPlayStateChanged(PlayModeStateChange change)\r\n    {\r\n        if (change == PlayModeStateChange.ExitingPlayMode)\r\n        {\r\n            DestroyROS2ForUnity();\r\n        }\r\n    }\r\n#endif\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4cdb4e72fb0aa46c09e52778257ed142\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing System;\r\nusing UnityEngine;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// An example class provided for testing of basic ROS2 communication\r\n/// </summary>\r\npublic class ROS2ListenerExample : MonoBehaviour\r\n{\r\n    private ROS2UnityComponent ros2Unity;\r\n    private ROS2Node ros2Node;\r\n    private ISubscription<std_msgs.msg.String> chatter_sub;\r\n\r\n    void Start()\r\n    {\r\n        ros2Unity = GetComponent<ROS2UnityComponent>();\r\n    }\r\n\r\n    void Update()\r\n    {\r\n        if (ros2Node == null && ros2Unity.Ok())\r\n        {\r\n            ros2Node = ros2Unity.CreateNode(\"ROS2UnityListenerNode\");\r\n            chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(\r\n              \"chatter\", msg => Debug.Log(\"Unity listener heard: [\" + msg.Data + \"]\"));\r\n        }\r\n    }\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 75a1bd43b302c4c578a744060319517e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2Node.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\nusing UnityEditor;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// A class representing a ros2 node. Multiple nodes can be used. Node can be removed by GC when not used anymore,\r\n/// but will also be removed properly with Ros2cs Shutdown, which ROS2 for Unity performs on application quit\r\n/// The node should be constructed through ROS2UnityComponent class, which also handles spinning\r\n/// </summary>\r\npublic class ROS2Node\r\n{\r\n    internal INode node;\r\n    public ROS2Clock clock;\r\n    public string name;\r\n\r\n    // Use ROS2UnityComponent to create a node\r\n    internal ROS2Node(string unityROS2NodeName = \"unity_ros2_node\")\r\n    {\r\n        name = unityROS2NodeName;\r\n        node = Ros2cs.CreateNode(name);\r\n        clock = new ROS2Clock();\r\n    }\r\n\r\n    ~ROS2Node()\r\n    {\r\n        Ros2cs.RemoveNode(node);\r\n    }\r\n\r\n    private static void ThrowIfUninitialized(string callContext)\r\n    {\r\n        if (!Ros2cs.Ok())\r\n        {\r\n            throw new InvalidOperationException(\"Ros2 For Unity is not initialized, can't \" + callContext);\r\n        }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Create a publisher with QoS suitable for sensor data\r\n    /// </summary>\r\n    /// <returns>The publisher</returns>\r\n    /// <param name=\"topicName\">topic that will be used for publishing</param>\r\n    public Publisher<T> CreateSensorPublisher<T>(string topicName) where T : Message, new()\r\n    {\r\n        QualityOfServiceProfile sensorProfile = new QualityOfServiceProfile(QosPresetProfile.SENSOR_DATA);\r\n        return CreatePublisher<T>(topicName, sensorProfile);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Create a publisher with indicated QoS.\r\n    /// </summary>\r\n    /// <returns>The publisher</returns>\r\n    /// <param name=\"topicName\">topic that will be used for publishing</param>\r\n    /// <param name=\"qos\">QoS for publishing. If no QoS is selected, it will default to reliable, keep 10 last</param>\r\n    public Publisher<T> CreatePublisher<T>(string topicName, QualityOfServiceProfile qos = null) where T : Message, new()\r\n    {\r\n        ThrowIfUninitialized(\"create publisher\");\r\n        return node.CreatePublisher<T>(topicName, qos);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Create a subscription\r\n    /// </summary>\r\n    /// <returns>The subscription</returns>\r\n    /// <param name=\"topicName\">topic to subscribe to</param>\r\n    /// <param name=\"qos\">QoS for subscription. If no QoS is selected, it will default to reliable, keep 10 last</param>\r\n    public Subscription<T> CreateSubscription<T>(string topicName, Action<T> callback,\r\n        QualityOfServiceProfile qos = null) where T : Message, new()\r\n    {\r\n        if (qos == null)\r\n        {\r\n            qos = new QualityOfServiceProfile(QosPresetProfile.DEFAULT);\r\n        }\r\n        ThrowIfUninitialized(\"create subscription\");\r\n        return node.CreateSubscription<T>(topicName, callback, qos);\r\n    }\r\n\r\n\r\n    /// <summary>\r\n    /// Remove existing subscription (returned earlier with CreateSubscription)\r\n    /// </summary>\r\n    /// <returns>The whether subscription was found (e. g. false if removed earlier elsewhere) </returns>\r\n    /// <param name=\"subscription\">subscrition to remove, returned from CreateSubscription</param>\r\n    public bool RemoveSubscription<T>(ISubscriptionBase subscription)\r\n    {\r\n        ThrowIfUninitialized(\"remove subscription\");\r\n        return node.RemoveSubscription(subscription);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Remove existing publisher\r\n    /// </summary>\r\n    /// <returns>The whether publisher was found (e. g. false if removed earlier elsewhere) </returns>\r\n    /// <param name=\"publisher\">publisher to remove, returned from CreatePublisher or CreateSensorPublisher</param>\r\n    public bool RemovePublisher<T>(IPublisherBase publisher)\r\n    {\r\n        ThrowIfUninitialized(\"remove publisher\");\r\n        return node.RemovePublisher(publisher);\r\n    }\r\n\r\n    /// <inheritdoc cref=\"INode.CreateService\"/>\r\n    public Service<I, O> CreateService<I, O>(string topic, Func<I, O> callback, QualityOfServiceProfile qos = null)\r\n        where I : Message, new()\r\n        where O : Message, new()\r\n    {\r\n        ThrowIfUninitialized(\"create service\");\r\n        return node.CreateService<I, O>(topic, callback, qos);\r\n    }\r\n\r\n    /// <inheritdoc cref=\"INode.RemoveService\"/>\r\n    public bool RemoveService(IServiceBase service)\r\n    {\r\n        ThrowIfUninitialized(\"remove service\");\r\n        return node.RemoveService(service);\r\n    }\r\n\r\n    /// <inheritdoc cref=\"INode.CreateClient\"/>\r\n    public Client<I, O> CreateClient<I, O>(string topic, QualityOfServiceProfile qos = null)\r\n        where I : Message, new()\r\n        where O : Message, new()\r\n    {\r\n        ThrowIfUninitialized(callContext: \"create client\");\r\n        return node.CreateClient<I, O>(topic, qos);\r\n    }\r\n\r\n    /// <inheritdoc cref=\"INode.RemoveClient\"/>\r\n    public bool RemoveClient(IClientBase client)\r\n    {\r\n        ThrowIfUninitialized(callContext: \"remove client\");\r\n        return node.RemoveClient(client);\r\n    }\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2Node.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 3e21db77b82bbeb8693eabe308d76f45\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing UnityEngine;\r\nusing System.Threading;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// An example class provided for performance testing of ROS2 communication\r\n/// </summary>\r\npublic class ROS2PerformanceTest : MonoBehaviour\r\n{\r\n    public int messageSize = 10000;\r\n    public int rate = 10;\r\n    private int interval_ms = 100;\r\n    private ROS2UnityComponent ros2Unity;\r\n    private ROS2Node ros2Node;\r\n    private IPublisher<sensor_msgs.msg.PointCloud2> perf_pub;\r\n    sensor_msgs.msg.PointCloud2 msg;\r\n    private bool initialized = false;\r\n\r\n    void Start()\r\n    {\r\n        ros2Unity = GetComponent<ROS2UnityComponent>();\r\n        PrepMessage();\r\n    }\r\n\r\n    void OnValidate()\r\n    {\r\n        if (rate < 1)\r\n        {\r\n            interval_ms = 0;\r\n        }\r\n        else\r\n        {\r\n            interval_ms = 1000 / rate;\r\n        }\r\n        PrepMessage();\r\n    }\r\n\r\n    private void Publish()\r\n    {\r\n        while(true)\r\n        {\r\n            if (ros2Unity.Ok())\r\n            {\r\n                if (ros2Node == null)\r\n                {\r\n                    ros2Node = ros2Unity.CreateNode(\"ros2_unity_performance_test_node\");\r\n                    perf_pub = ros2Node.CreateSensorPublisher<sensor_msgs.msg.PointCloud2>(\"perf_chatter\");\r\n                }\r\n\r\n                var msgWithHeader = msg as MessageWithHeader;\r\n                ros2Node.clock.UpdateROSTimestamp(ref msgWithHeader);\r\n                perf_pub.Publish(msg);\r\n                if (interval_ms > 0)\r\n                {\r\n                    Thread.Sleep(interval_ms);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    void FixedUpdate()\r\n    {\r\n        if (!initialized)\r\n        {\r\n            Thread publishThread = new Thread(() => Publish());\r\n            publishThread.Start();\r\n            initialized = true;\r\n        }\r\n    }\r\n\r\n    private void AssignField(ref sensor_msgs.msg.PointField pf, string n, uint off, byte dt, uint count)\r\n    {\r\n        pf.Name = n;\r\n        pf.Offset = off;\r\n        pf.Datatype = dt;\r\n        pf.Count = count;\r\n    }\r\n\r\n    private void PrepMessage()\r\n    {\r\n        uint count = (uint)messageSize; //point per message\r\n        uint fieldsSize = 16;\r\n        uint rowSize = count * fieldsSize;\r\n        msg = new sensor_msgs.msg.PointCloud2()\r\n        {\r\n            Height = 1,\r\n            Width = count,\r\n            Is_bigendian = false,\r\n            Is_dense = true,\r\n            Point_step = fieldsSize,\r\n            Row_step = rowSize,\r\n            Data = new byte[rowSize * 1]\r\n        };\r\n        uint pointFieldCount = 4;\r\n        msg.Fields = new sensor_msgs.msg.PointField[pointFieldCount];\r\n        for (int i = 0; i < pointFieldCount; ++i)\r\n        {\r\n            msg.Fields[i] = new sensor_msgs.msg.PointField();\r\n        }\r\n\r\n        AssignField(ref msg.Fields[0], \"x\", 0, 7, 1);\r\n        AssignField(ref msg.Fields[1], \"y\", 4, 7, 1);\r\n        AssignField(ref msg.Fields[2], \"z\", 8, 7, 1);\r\n        AssignField(ref msg.Fields[3], \"intensity\", 12, 7, 1);\r\n        float[] pointsArray = new float[count * msg.Fields.Length];\r\n\r\n        var floatIndex = 0;\r\n        for (int i = 0; i < count; ++i)\r\n        {\r\n            float intensity = 100;\r\n            pointsArray[floatIndex++] = 1;\r\n            pointsArray[floatIndex++] = 2;\r\n            pointsArray[floatIndex++] = 3;\r\n            pointsArray[floatIndex++] = intensity;\r\n        }\r\n        System.Buffer.BlockCopy(pointsArray, 0, msg.Data, 0, msg.Data.Length);\r\n        msg.SetHeaderFrame(\"pc\");\r\n    }\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 387d300b788c9bd29b6e38808a481155\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing ROS2;\n\nusing addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;\nusing addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;\n\n/// <summary>\n/// An example class provided for testing of basic ROS2 service\n/// </summary>\npublic class ROS2ServiceExample : MonoBehaviour\n{\n    private ROS2UnityComponent ros2Unity;\n    private ROS2Node ros2Node;\n    private IService<addTwoIntsReq, addTwoIntsResp> addTwoIntsService;\n\n    void Start()\n    {\n        ros2Unity = GetComponent<ROS2UnityComponent>();\n        if (ros2Unity.Ok())\n        {\n            if (ros2Node == null)\n            {\n                ros2Node = ros2Unity.CreateNode(\"ROS2UnityService\");\n                addTwoIntsService = ros2Node.CreateService<addTwoIntsReq, addTwoIntsResp>(\n                    \"add_two_ints\", addTwoInts);\n            }\n        }\n    }\n\n    public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)\n    {\n        Debug.Log(\"Incoming Service Request A=\" + msg.A + \" B=\" + msg.B);\n        example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();\n        response.Sum = msg.A + msg.B;\n        return response;\n    }\n}\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing UnityEngine;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// An example class provided for testing of basic ROS2 communication\r\n/// </summary>\r\npublic class ROS2TalkerExample : MonoBehaviour\r\n{\r\n    // Start is called before the first frame update\r\n    private ROS2UnityComponent ros2Unity;\r\n    private ROS2Node ros2Node;\r\n    private IPublisher<std_msgs.msg.String> chatter_pub;\r\n    private int i;\r\n\r\n    void Start()\r\n    {\r\n        ros2Unity = GetComponent<ROS2UnityComponent>();\r\n    }\r\n\r\n    void Update()\r\n    {\r\n        if (ros2Unity.Ok())\r\n        {\r\n            if (ros2Node == null)\r\n            {\r\n                ros2Node = ros2Unity.CreateNode(\"ROS2UnityTalkerNode\");\r\n                chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>(\"chatter\");\r\n            }\r\n\r\n            i++;\r\n            std_msgs.msg.String msg = new std_msgs.msg.String();\r\n            msg.Data = \"Unity ROS2 sending: hello \" + i;\r\n            chatter_pub.Publish(msg);\r\n        }\r\n    }\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 72620fb0a9290863f8643557405c48e3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may not use this file except in compliance with the License.\r\n// You may obtain a copy of the License at\r\n//\r\n//     http://www.apache.org/licenses/LICENSE-2.0\r\n//\r\n// Unless required by applicable law or agreed to in writing, software\r\n// distributed under the License is distributed on an \"AS IS\" BASIS,\r\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n// See the License for the specific language governing permissions and\r\n// limitations under the License.\r\n\r\nusing UnityEngine;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Threading;\r\nusing ROS2;\r\n\r\nnamespace ROS2\r\n{\r\n\r\n/// <summary>\r\n/// The principal MonoBehaviour class for handling ros2 nodes and executables.\r\n/// Use this to create ros2 node, check ros2 status.\r\n/// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread\r\n/// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work\r\n/// anyway with more than one since the underlying library can handle multiple init and shutdown calls,\r\n/// and does node name uniqueness check independently.\r\n/// </summary>\r\npublic class ROS2UnityComponent : MonoBehaviour\r\n{\r\n    private ROS2ForUnity ros2forUnity;\r\n    private List<ROS2Node> nodes;\r\n    private List<INode> ros2csNodes; // For performance in spinning\r\n    private List<Action> executableActions;\r\n    private bool initialized = false;\r\n    private bool quitting = false;\r\n    private int interval = 2;  // Spinning / executor interval in ms\r\n    private object mutex = new object();\r\n    private double spinTimeout = 0.0001;\r\n\r\n    public bool Ok()\r\n    {\r\n        lock (mutex)\r\n        {\r\n            if (ros2forUnity == null)\r\n                LazyConstruct();\r\n            return (nodes != null && ros2forUnity.Ok());\r\n        }\r\n    }\r\n\r\n    private void LazyConstruct()\r\n    {\r\n        lock (mutex)\r\n        {        \r\n            if (ros2forUnity != null)\r\n                return;\r\n\r\n            ros2forUnity = new ROS2ForUnity();\r\n            nodes = new List<ROS2Node>();\r\n            ros2csNodes = new List<INode>();\r\n            executableActions = new List<Action>();\r\n        }\r\n    }\r\n\r\n    void Start()\r\n    {\r\n        LazyConstruct();\r\n    }\r\n\r\n    public ROS2Node CreateNode(string name)\r\n    {\r\n        LazyConstruct();\r\n\r\n        lock (mutex)\r\n        {\r\n            foreach (ROS2Node n in nodes)\r\n            {  // Assumed to be a rare operation on rather small (<1k) list\r\n                if (n.name == name)\r\n                {\r\n                    throw new InvalidOperationException(\"Cannot create node \" + name + \". A node with this name already exists!\");\r\n                }\r\n            }\r\n            ROS2Node node = new ROS2Node(name);\r\n            nodes.Add(node);\r\n            ros2csNodes.Add(node.node);\r\n            return node;\r\n        }\r\n    }\r\n\r\n    public void RemoveNode(ROS2Node node)\r\n    {\r\n        lock (mutex)\r\n        {\r\n            ros2csNodes.Remove(node.node);\r\n            nodes.Remove(node); //Node will be later deleted if unused, by GC\r\n        }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Works as a simple executor registration analogue. These functions will be called with each Tick()\r\n    /// Actions need to take care of correct call resolution by checking in their body (TODO)\r\n    /// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)\r\n    /// </summary>\r\n    public void RegisterExecutable(Action executable)\r\n    {\r\n        LazyConstruct();\r\n\r\n        lock (mutex)\r\n        {\r\n            executableActions.Add(executable);\r\n        }\r\n    }\r\n\r\n    public void UnregisterExecutable(Action executable)\r\n    {\r\n        lock (mutex)\r\n        {\r\n            executableActions.Remove(executable);\r\n        }\r\n    }\r\n\r\n    /// <summary>\r\n    /// \"Executor\" thread will tick all clocks and spin the node\r\n    /// </summary>\r\n    private void Tick()\r\n    {\r\n        while (!quitting)\r\n        {\r\n            if (Ok())\r\n            {\r\n                lock (mutex)\r\n                {\r\n                    foreach (Action action in executableActions)\r\n                    {\r\n                        action();\r\n                    }\r\n                    Ros2cs.SpinOnce(ros2csNodes, spinTimeout);\r\n                }\r\n            }\r\n            Thread.Sleep(interval);\r\n        }\r\n    }\r\n\r\n    void FixedUpdate()\r\n    {\r\n        if (!initialized)\r\n        {\r\n            Thread publishThread = new Thread(() => Tick());\r\n            publishThread.Start();\r\n            initialized = true;\r\n        }\r\n    }\r\n\r\n    void OnApplicationQuit()\r\n    {\r\n        quitting = true;\r\n        ros2forUnity.DestroyROS2ForUnity();\r\n    }\r\n}\r\n\r\n}  // namespace ROS2\r\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs.meta",
    "content": "fileFormatVersion: 2\nguid: feab04ad06492965492b3edc6423aa53\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs",
    "content": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing UnityEngine;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing ROS2;\n\nnamespace ROS2\n{\n\n    /// <summary>\n    /// The principal class for handling ros2 nodes and executables.\n    /// Use this to create ros2 node, check ros2 status.\n    /// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread\n    /// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work\n    /// anyway with more than one since the underlying library can handle multiple init and shutdown calls,\n    /// and does node name uniqueness check independently.\n    /// </summary>\n    public class ROS2UnityCore\n    {\n        private ROS2ForUnity ros2forUnity;\n        private List<ROS2Node> nodes;\n        private List<INode> ros2csNodes; // For performance in spinning\n        private List<Action> executableActions;\n        private bool quitting = false;\n        private int interval = 2;  // Spinning / executor interval in ms\n        private object mutex = new object();\n        private double spinTimeout = 0.0001;\n\n        public bool Ok()\n        {\n            lock (mutex)\n            {\n                return (nodes != null && ros2forUnity.Ok());\n            }\n        }\n\n        public ROS2UnityCore()\n        {\n            lock (mutex)\n            {\n                ros2forUnity = new ROS2ForUnity();\n                nodes = new List<ROS2Node>();\n                ros2csNodes = new List<INode>();\n                executableActions = new List<Action>();\n\n                Thread publishThread = new Thread(() => Tick());\n                publishThread.Start();\n            }\n        }\n\n        public ROS2Node CreateNode(string name)\n        {\n            lock (mutex)\n            {\n                foreach (ROS2Node n in nodes)\n                {  // Assumed to be a rare operation on rather small (<1k) list\n                    if (n.name == name)\n                    {\n                        throw new InvalidOperationException(\"Cannot create node \" + name + \". A node with this name already exists!\");\n                    }\n                }\n                ROS2Node node = new ROS2Node(name);\n                nodes.Add(node);\n                ros2csNodes.Add(node.node);\n                return node;\n            }\n        }\n\n        public void RemoveNode(ROS2Node node)\n        {\n            lock (mutex)\n            {\n                ros2csNodes.Remove(node.node);\n                nodes.Remove(node); //Node will be later deleted if unused, by GC\n            }\n        }\n\n        /// <summary>\n        /// Works as a simple executor registration analogue. These functions will be called with each Tick()\n        /// Actions need to take care of correct call resolution by checking in their body (TODO)\n        /// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)\n        /// </summary>\n        public void RegisterExecutable(Action executable)\n        {\n            lock (mutex)\n            {\n                executableActions.Add(executable);\n            }\n        }\n\n        public void UnregisterExecutable(Action executable)\n        {\n            lock (mutex)\n            {\n                executableActions.Remove(executable);\n            }\n        }\n\n        /// <summary>\n        /// \"Executor\" thread will tick all clocks and spin the node\n        /// </summary>\n        private void Tick()\n        {\n            while (!quitting)\n            {\n                if (Ok())\n                {\n                    lock (mutex)\n                    {\n                        foreach (Action action in executableActions)\n                        {\n                            action();\n                        }\n                        Ros2cs.SpinOnce(ros2csNodes, spinTimeout);\n                    }\n                }\n                Thread.Sleep(interval);\n            }\n        }\n\n        public void DestroyNow()\n        {\n            quitting = true;\n            ros2forUnity.DestroyROS2ForUnity();\n        }\n    }\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d80a8dd00d331ce458b98a7707d03bb3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Sensor.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing UnityEngine;\nusing UnityEngine.Profiling;\nusing System;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// An abstract base class for ROS2-enabled sensor.\n/// </summary>\npublic abstract class ISensor : MonoBehaviour\n{\n    /// <summary>\n    /// The desired update frequency for the sensor. The maximum can be the rate with which FixedUpdate is called,\n    /// which depends on the physics step (usually 50 or 100 times per second).\n    /// </summary>\n    public double desiredUpdateFreq = 25.0;\n\n    /// <summary>\n    /// The frameID corresponds to the ROS frame_id element of the header and is important\n    /// for transformations\n    /// </summary>\n    public string frameID = \"sensor\";\n\n    /// <summary>\n    /// A topic to which the sensor publishes. Only one per sensor. Don't add the namespace of\n    /// the agent name, it is handled externally (i.e. sensor does not know to what object it belongs).\n    /// </summary>\n    public string topicName = \"\";\n\n    /// <summary>\n    /// Controls whether sensor is publishing messages\n    /// </summary>\n    public bool publishing = false;\n\n    /// <summary>\n    /// Creates sensor publishers and registers it in the executor so that it publishes when new data is available\n    /// </summary>\n    /// <param name=\"ros2Unity\"> Central ros2 monobehavior for Unity </param>\n    /// <param name=\"node\"> ros2 node that will publish sensor data </param>\n    /// <param name=\"agentName\"> name of the agent (vehicle) to be added to the sensor publish namespace </param>\n    public abstract void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName);\n\n    /// <summary>\n    /// Returns the constructed frame name, taking in account the agent name(space)\n    /// </summary>\n    public abstract string frameName();\n}\n\n/// <summary>\n/// A base template class for the sensor. The type is the message type of sensor data.\n/// </summary>\npublic abstract class Sensor<T> : ISensor where T : MessageWithHeader, new()\n{\n    /// <summary>\n    /// Acquires the value by performing sensor type characteristic computations (e.g. raycasts).\n    /// Implemented in subclasses.\n    /// </summary>\n    /// <returns>The message which contains the sensor data.\n    /// Mind that the header for message is handled in a generic way by this class.</returns>\n    protected abstract T AcquireValue();\n\n    /// <summary>\n    /// Returns true when there is a new data available from sensor.\n    /// </summary>\n    protected abstract bool HasNewData();\n\n    protected double desiredFrameTime = 0.0;\n    private const double minimumFrequency = 0.001;\n    private Publisher<T> publisher;\n    private Subscription<rosgraph_msgs.msg.Clock> clockSubscriber;\n    private ROS2UnityComponent ros2UnityComponent;\n    private ROS2Node ros2Node;\n    private string ownerAgentName;\n    private double lastTimestamp;\n    private double timeSinceLastFixedUpdate;\n\n    private T readings;\n    private bool newReadings;\n\n    public override string frameName()\n    {\n        return ownerAgentName + \"/\" + frameID;\n    }\n\n    /// <summary>\n    /// Visualises the effects of the sensor. It doesn't make sense for some sensor and the\n    /// default implementation is empty.\n    /// </summary>\n    protected virtual void VisualiseEffects()\n    {\n    }\n\n    /// <summary>\n    /// When parameters in editor change (i.e. frequency),\n    /// this function is called to calculate new frame time.\n    /// </summary>\n    protected virtual void OnValidate()\n    {\n        CalculateFrameTime();\n    }\n\n    /// <summary>\n    /// An entry point for the per-frame processing done in subclass\n    /// </summary>\n    protected virtual void OnUpdate() {}\n\n    /// <summary>\n    /// See superclass definition\n    /// </summary>\n    public override void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName)\n    {\n        if (!ros2Unity.Ok())\n        {\n            throw new System.InvalidOperationException(\"Publisher for sensor can't be created when node is not OK\");\n        }\n\n        if (String.IsNullOrEmpty(topicName))\n        {\n            throw new System.InvalidOperationException(\"Topic name not set for the sensor \" + this);\n        }\n\n        ownerAgentName = agentName;\n        ros2UnityComponent = ros2Unity;\n        ros2Node = node;\n        string nsName = agentName.Replace(\" \", \"_\");\n        publisher = node.CreateSensorPublisher<T>(nsName + \"/\" + topicName);\n        ros2UnityComponent.RegisterExecutable(ExecutorThreadSensorPublishAction);\n        publishing = true;\n    }\n\n    /// <summary>\n    /// This is executed in an executor thread (through RegisterExecutable)\n    /// Sensor fequency is indirectly handed through newReadings, which are acquired at a requested\n    /// frequency if possible (e. g. due to simulation resource constraints)\n    /// </summary>\n    internal void ExecutorThreadSensorPublishAction()\n    {\n        if (!HasNewData())\n            return;\n\n        if (publisher != null & publishing)\n        {\n            if (ros2UnityComponent.Ok())\n            {\n                readings = AcquireValue();\n                readings.SetHeaderFrame(frameName());\n                if (readings != null)\n                {\n                    MessageWithHeader readingsHeader = readings as MessageWithHeader;\n                    ros2Node.clock.UpdateROSTimestamp(ref readingsHeader);\n                    publisher.Publish(readings);\n                }\n            }\n        }\n    }\n\n    /// <summary>\n    /// Once each frame, visualise effects of the sensor (if any). Visualisation\n    /// rate is independent of publishing/acquisition rate, which happen at the sensor\n    /// frequency instead of the app frame rate.\n    /// </summary>\n    void Update()\n    {\n        VisualiseEffects();\n        OnUpdate();\n    }\n\n    /// <summary>\n    /// Initialize header and calculate frame time\n    /// </summary>\n    void Awake()\n    {\n        // turn on publishing on start\n        publishing = true;\n        CalculateFrameTime();\n        lastTimestamp = DateTime.UtcNow.Ticks / 1E7;\n    }\n\n    /// <summary>\n    /// Sensor frequency is used to calculate frame time, based on desired frequency and the bounds.\n    /// </summary>\n    void CalculateFrameTime()\n    {\n        double maxFrameFreq = 1.0 / Time.fixedDeltaTime;\n        if (desiredUpdateFreq > maxFrameFreq)\n        {\n            Debug.LogWarning(\"Desired frame rate of \" + desiredUpdateFreq + \" can't be met, \"\n                            + \"physics frequency is \" + maxFrameFreq);\n            desiredUpdateFreq = maxFrameFreq;  //Can't go faster than physics\n        }\n        if (desiredUpdateFreq < minimumFrequency)\n        {\n            Debug.LogWarning(\"Minimum frequency of \" + minimumFrequency\n                             + \" applied instead of \" + desiredUpdateFreq);\n            desiredUpdateFreq = minimumFrequency;\n        }\n        desiredFrameTime = 1.0 / desiredUpdateFreq;\n    }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Sensor.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d3ebd1e57dae48cca9c88c51f18cf510\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System;\nusing System.Diagnostics;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// DateTime based clock that has resolution increased using Stopwatch.\n/// DateTime is used to synchronize since Stopwatch tends to drift.\n/// </summary>\npublic class DotnetTimeSource : ITimeSource\n{\n    private readonly double maxUnsyncedSeconds = 10;\n\n    private Stopwatch stopwatch = new Stopwatch();\n\n    private readonly object mutex = new object();\n\n    private double systemTimeIntervalStart = 0;\n\n    private double stopwatchStartTimeStamp;\n\n    private double TotalSystemTimeSeconds()\n    {\n        return TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds;\n    }\n\n    private void UpdateSystemTime()\n    {\n        systemTimeIntervalStart = TotalSystemTimeSeconds();\n        stopwatchStartTimeStamp = Stopwatch.GetTimestamp();\n    }\n\n    public DotnetTimeSource()\n    {\n        UpdateSystemTime();\n    }\n\n    public void GetTime(out int seconds, out uint nanoseconds)\n    {\n        lock(mutex) // Threading\n        {\n            double endTimestamp = Stopwatch.GetTimestamp();\n            var durationInSeconds = endTimestamp - stopwatchStartTimeStamp;\n            double timeOffset = 0;\n            if (durationInSeconds >= maxUnsyncedSeconds)\n            {   // acquire DateTime to sync\n                UpdateSystemTime();\n            }\n            else\n            {   // use Stopwatch offset\n                timeOffset = durationInSeconds;\n            }\n            \n            TimeUtils.TimeFromTotalSeconds(systemTimeIntervalStart + timeOffset, out seconds, out nanoseconds);\n        }\n    }\n}\n\n}  // namespace ROS2\n\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ITimeSource.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace ROS2\n{\n\n/// <summary>\n/// Interace for acquiring time\n/// </summary>\npublic interface ITimeSource\n{\n  public void GetTime(out int seconds, out uint nanoseconds);\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs",
    "content": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System;\nusing UnityEngine;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// A ros2 clock class that for interfacing between a time source (unity or ros2 system time) and ros2cs messages, structs. \n/// </summary>\npublic class ROS2Clock\n{\n    private ITimeSource _timeSource;\n\n    public ROS2Clock() : this(new ROS2TimeSource())\n    {   // By default, use ROS2TimeSource\n    }\n\n    public ROS2Clock(ITimeSource ts)\n    {\n        _timeSource = ts;\n    }\n\n    public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)\n    {\n        int seconds;\n        uint nanoseconds;\n        _timeSource.GetTime(out seconds, out nanoseconds);\n        clockMessage.Clock_.Sec = seconds;\n        clockMessage.Clock_.Nanosec = nanoseconds;\n    }\n\n    public void UpdateROSClockTime(builtin_interfaces.msg.Time time)\n    {\n        int seconds;\n        uint nanoseconds;\n        _timeSource.GetTime(out seconds, out nanoseconds);\n        time.Sec = seconds;\n        time.Nanosec = nanoseconds;\n    }\n\n    public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)\n    {\n        int seconds;\n        uint nanoseconds;\n        _timeSource.GetTime(out seconds, out nanoseconds);\n        message.UpdateHeaderTime(seconds, nanoseconds);\n    }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 69e097a4a027d5a55b991c0b0b1bdf88\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Threading;\nusing UnityEngine;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// ros2 time source (system time by default).\n/// </summary>\npublic class ROS2ScalableTimeSource : ITimeSource\n{\n  private Thread mainThread;\n  private double lastReadingSecs;\n  private ROS2.Clock clock;\n  private double initialTime = 0;\n  private double initialTimeScale = 0;\n  private bool initialTimeAcquired = false;\n  private bool initialTimeScaleAcquired = false;\n  private bool timeScaleChanged = false;\n\n  public ROS2ScalableTimeSource()\n  {\n    mainThread = Thread.CurrentThread;\n  }\n\n  public void GetTime(out int seconds, out uint nanoseconds)\n  {\n    if (!ROS2.Ros2cs.Ok())\n    {\n      seconds = 0;\n      nanoseconds = 0;\n      Debug.LogWarning(\"Cannot acquire valid ros time, ros either not initialized or shut down already\");\n      return;\n    }\n\n    if (clock == null)\n    { // Create clock which uses system time by default (unless use_sim_time is set in ros2)\n      clock = new ROS2.Clock();\n    }\n\n    if (!initialTimeScaleAcquired)\n    {\n      initialTimeScaleAcquired = true;\n      initialTimeScale = Time.timeScale;\n    }\n\n    if (initialTimeScale != Time.timeScale)\n    {\n      timeScaleChanged = true;\n    }\n\n    lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;\n\n    if (initialTimeScale == 1.0 && !timeScaleChanged)\n    {\n      TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);\n    }\n    else\n    {\n      if (!initialTimeAcquired)\n      {\n        initialTimeAcquired = true;\n        initialTime = clock.Now.Seconds - Time.timeAsDouble;\n      }\n      TimeUtils.TimeFromTotalSeconds(lastReadingSecs + initialTime, out seconds, out nanoseconds);\n    }\n  }\n\n  ~ROS2ScalableTimeSource()\n  {\n    if (clock != null)\n    {\n      clock.Dispose();\n    }\n  }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing UnityEngine;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// ros2 time source (system time by default).\n/// </summary>\npublic class ROS2TimeSource : ITimeSource\n{\n  private ROS2.Clock clock;\n\n  public void GetTime(out int seconds, out uint nanoseconds)\n  {\n    if (!ROS2.Ros2cs.Ok())\n    {\n      seconds = 0;\n      nanoseconds = 0;\n      Debug.LogWarning(\"Cannot acquire valid ros time, ros either not initialized or shut down already\");\n      return;\n    }\n\n    if (clock == null)\n    { // Create clock which uses system time by default (unless use_sim_time is set in ros2)\n      clock = new ROS2.Clock();\n    }\n  \n    TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);\n  }\n\n  ~ROS2TimeSource()\n  {\n    if (clock != null)\n    {\n      clock.Dispose();\n    }\n  }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/TimeUtils.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace ROS2\n{\n\n/// <summary>\n/// Interace for acquiring time\n/// </summary>\ninternal static class TimeUtils\n{\n  public static void TimeFromTotalSeconds(in double secondsIn, out int seconds, out uint nanoseconds)\n  {\n    long nanosec = (long)(secondsIn * 1e9);\n    seconds = (int)(nanosec / 1000000000);\n    nanoseconds = (uint)(nanosec % 1000000000);\n  }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs",
    "content": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System;\nusing System.Threading;\nusing UnityEngine;\n\nnamespace ROS2\n{\n\n/// <summary>\n/// Acquires Unity time. Note that Time API only allows main thread access,\n/// but this class object also stores last acquired value for other threads.\n/// This is done without a warning, so the class will not behave as expected\n/// when not used by main thread.\n/// </summary>\npublic class UnityTimeSource : ITimeSource\n{\n  private Thread mainThread;\n  private double lastReadingSecs;\n\n  public UnityTimeSource()\n  {\n    mainThread = Thread.CurrentThread;\n  }\n\n  public void GetTime(out int seconds, out uint nanoseconds)\n  {\n    lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;\n    TimeUtils.TimeFromTotalSeconds(lastReadingSecs, out seconds, out nanoseconds);\n  }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Transformations.cs",
    "content": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing UnityEngine;\n\nnamespace ROS2\n{\n/// <summary>\n/// A set of transformation functions between coordinate systems of Unity and ROS\n/// </summary>\npublic static class Transformations\n{\n    public static Vector3 Ros2Unity(this Vector3 vector3)\n    {\n        return new Vector3(-vector3.y, vector3.z, vector3.x);\n    }\n\n    public static Vector3 Unity2Ros(this Vector3 vector3)\n    {\n        return new Vector3(vector3.z, -vector3.x, vector3.y);\n    }\n\n    public static Vector3 Ros2UnityScale(this Vector3 vector3)\n    {\n        return new Vector3(vector3.y, vector3.z, vector3.x);\n    }\n\n    public static Vector3 Unity2RosScale(this Vector3 vector3)\n    {\n        return new Vector3(vector3.z, vector3.x, vector3.y);\n    }\n\n    public static Quaternion Ros2Unity(this Quaternion quaternion)\n    {\n        return new Quaternion(quaternion.y, -quaternion.z, -quaternion.x, quaternion.w);\n    }\n\n    public static Quaternion Unity2Ros(this Quaternion quaternion)\n    {\n        return new Quaternion(-quaternion.z, quaternion.x, -quaternion.y, quaternion.w);\n    }\n\n    public static void Unity2Ros(ref Quaternion quaternion)\n    {\n        var z = quaternion.z;\n        var x = quaternion.x;\n        var y = quaternion.y;\n        quaternion.x = -z;\n        quaternion.y = x;\n        quaternion.z = -y;\n    }\n\n    public static void Unity2Ros(ref Vector3 vector)\n    {\n        var z = vector.z;\n        var x = vector.x;\n        var y = vector.y;\n        vector.x = z;\n        vector.y = -x;\n        vector.z = y;\n    }\n\n    public static Matrix4x4 Unity2RosMatrix4x4()\n    {\n        // Note: The matrix here is written as-if on paper,\n        // but Unity's Matrix4x4 is constructed from column-vectors, hence the transpose.\n        return new Matrix4x4(\n            new Vector4( 0.0f, 0.0f, 1.0f, 0.0f),\n            new Vector4(-1.0f, 0.0f, 0.0f, 0.0f),\n            new Vector4( 0.0f, 1.0f, 0.0f, 0.0f),\n            new Vector4( 0.0f, 0.0f, 0.0f, 1.0f)\n        ).transpose;\n    }\n}\n\n}  // namespace ROS2\n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Transformations.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5ab7c5f5cc85e9e3aa6134862d9c1cba\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts.meta",
    "content": "fileFormatVersion: 2\nguid: f750980d49c8bcf39830e89365689d16\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/scripts/metadata_generator.py",
    "content": "# Copyright 2019-2022 Robotec.ai.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport xml.etree.ElementTree as ET\nfrom xml.dom import minidom\nimport subprocess\nimport pathlib\nimport os\n\nparser = argparse.ArgumentParser(description='Generate metadata file for ros2-for-unity.')\nparser.add_argument('--standalone', action='store_true', help='is a standalone build')\nargs = parser.parse_args()\n\ndef get_git_commit(working_directory) -> str:\n    return subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=working_directory).decode('ascii').strip()\n\ndef get_git_description(working_directory) -> str:\n    return subprocess.check_output(['git', 'describe', '--tags', '--always'], cwd=working_directory).decode('ascii').strip()\n\ndef get_commit_date(working_directory) -> str:\n    return subprocess.check_output(['git', 'show', '-s', '--format=%ci'], cwd=working_directory).decode('ascii').strip()\n\ndef get_git_abbrev(working_directory) -> str:\n    return subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=working_directory).decode('ascii').strip()\n\ndef get_ros2_for_unity_root_path() -> pathlib.Path:\n    return pathlib.Path(__file__).parents[2]\n\ndef get_ros2_for_unity_path() -> pathlib.Path:\n    return pathlib.Path(__file__).parents[1].joinpath(\"Ros2ForUnity\")\n\ndef get_ros2cs_path() -> pathlib.Path:\n    return pathlib.Path(__file__).parents[1].joinpath(\"ros2cs\")\n\ndef get_ros2_path() -> pathlib.Path:\n    return get_ros2cs_path().joinpath(\"src\").joinpath(\"ros2\").joinpath(\"rcl_interfaces\")\n\ndef get_ros2_version() -> str:\n    return os.environ.get(\"ROS_DISTRO\", \"unknown\")\n\nros2_for_unity = ET.Element(\"ros2_for_unity\")\nET.SubElement(ros2_for_unity, \"ros2\").text = get_ros2_version()\nros2_for_unity_version = ET.SubElement(ros2_for_unity, \"version\")\nET.SubElement(ros2_for_unity_version, \"sha\").text = get_git_commit(get_ros2_for_unity_root_path())\nET.SubElement(ros2_for_unity_version, \"desc\").text = get_git_description(get_ros2_for_unity_root_path())\nET.SubElement(ros2_for_unity_version, \"date\").text = get_commit_date(get_ros2_for_unity_root_path())\n\nros2_cs = ET.Element(\"ros2cs\")\nET.SubElement(ros2_cs, \"ros2\").text = get_ros2_version()\nros2_cs_version = ET.SubElement(ros2_cs, \"version\")\nET.SubElement(ros2_cs_version, \"sha\").text = get_git_commit(get_ros2cs_path())\nET.SubElement(ros2_cs_version, \"desc\").text = get_git_description(get_ros2cs_path())\nET.SubElement(ros2_cs_version, \"date\").text = get_commit_date(get_ros2cs_path())\nET.SubElement(ros2_cs, \"standalone\").text = str(int(args.standalone))\n\nrf2u_xmlstr = minidom.parseString(ET.tostring(ros2_for_unity)).toprettyxml(indent=\"   \")\nmetadata_rf2u_file = get_ros2_for_unity_path().joinpath(\"metadata_ros2_for_unity.xml\")\nwith open(str(metadata_rf2u_file), \"w\") as f:\n    f.write(rf2u_xmlstr)\n    \nr2cs_xmlstr = minidom.parseString(ET.tostring(ros2_cs)).toprettyxml(indent=\"   \")\nmetadata_r2cs_file = get_ros2_for_unity_path().joinpath(\"metadata_ros2cs.xml\")\nwith open(str(metadata_r2cs_file), \"w\") as f:\n    f.write(r2cs_xmlstr)\n"
  }
]