Full Code of RobotecAI/ros2-for-unity for AI

develop 096e46ccd16a cached
59 files
122.3 KB
30.8k tokens
136 symbols
1 requests
Download .txt
Repository: RobotecAI/ros2-for-unity
Branch: develop
Commit: 096e46ccd16a
Files: 59
Total size: 122.3 KB

Directory structure:
gitextract_58lbld25/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── custom.md
│       └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.AL2
├── README-UBUNTU.md
├── README-WINDOWS.md
├── README.md
├── build.ps1
├── build.sh
├── create_unity_package.ps1
├── create_unity_package.sh
├── deploy_unity_plugins.ps1
├── deploy_unity_plugins.sh
├── docker/
│   ├── Dockerfile
│   ├── README.md
│   ├── build_image.sh
│   ├── custom_messages/
│   │   └── INSERT_CUSTOM_MESSAGES_HERE
│   ├── entrypoint.sh
│   └── run_container.sh
├── pull_repositories.ps1
├── pull_repositories.sh
├── ros2_for_unity_custom_messages.repos
├── ros2cs.repos
└── src/
    ├── Ros2ForUnity/
    │   ├── COLCON_IGNORE
    │   ├── Plugins.meta
    │   ├── Scripts/
    │   │   ├── PostInstall.cs
    │   │   ├── ROS2ClientExample.cs
    │   │   ├── ROS2ForUnity.cs
    │   │   ├── ROS2ForUnity.cs.meta
    │   │   ├── ROS2ListenerExample.cs
    │   │   ├── ROS2ListenerExample.cs.meta
    │   │   ├── ROS2Node.cs
    │   │   ├── ROS2Node.cs.meta
    │   │   ├── ROS2PerformanceTest.cs
    │   │   ├── ROS2PerformanceTest.cs.meta
    │   │   ├── ROS2ServiceExample.cs
    │   │   ├── ROS2TalkerExample.cs
    │   │   ├── ROS2TalkerExample.cs.meta
    │   │   ├── ROS2UnityComponent.cs
    │   │   ├── ROS2UnityComponent.cs.meta
    │   │   ├── ROS2UnityCore.cs
    │   │   ├── ROS2UnityCore.cs.meta
    │   │   ├── Sensor.cs
    │   │   ├── Sensor.cs.meta
    │   │   ├── Time/
    │   │   │   ├── DotnetTimeSource.cs
    │   │   │   ├── ITimeSource.cs
    │   │   │   ├── ROS2Clock.cs
    │   │   │   ├── ROS2Clock.cs.meta
    │   │   │   ├── ROS2ScalableTimeSource.cs
    │   │   │   ├── ROS2TimeSource.cs
    │   │   │   ├── TimeUtils.cs
    │   │   │   └── UnityTimeSource.cs
    │   │   ├── Transformations.cs
    │   │   └── Transformations.cs.meta
    │   └── Scripts.meta
    └── scripts/
        └── metadata_generator.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. Ubuntu20.04, Windows 10]
 - ros2 distro [e.g. foxy, galactic]
 - ros2-for-unity version [e.g. 1.1.0, git-sha 6dc898352c9d45996fb0d43e2c8225707e239f4a]
 - ros2cs version [e.g. git-sha 2a45785b080fffb3ae5e2f645e976a69698810f0]
 - ros2 dds middleware [e.g. cyclonedds, fastdds]
 - ros2 environment setup [e.g. single pc, local network]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''

---




================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .gitignore
================================================
install
log
build
.idea
src/ros2cs
**/metadata*.xml
src/Ros2ForUnity/Plugins
!src/Ros2ForUnity/Plugins/.gitkeep


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
office@robotec.ai.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
Any contribution that you make to this repository will
be under the Apache 2 License, as dictated by that
[license](http://www.apache.org/licenses/LICENSE-2.0.html):

> 5. Submission of Contributions. Unless You explicitly state otherwise,
>    any Contribution intentionally submitted for inclusion in the Work
>    by You to the Licensor shall be under the terms and conditions of
>    this License, without any additional terms or conditions.
>    Notwithstanding the above, nothing herein shall supersede or modify
>    the terms of any separate license agreement you may have executed
>    with Licensor regarding such Contributions.


================================================
FILE: LICENSE.AL2
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README-UBUNTU.md
================================================
# ROS2 For Unity - Ubuntu 20.04 and 22.04

This readme contains information specific to Ubuntu 20.04/22.04. For general information, please see [README.md](README.md)

## Building

We assume that working directory is `~/ros2-for-unity` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).

### Prerequisites

Start 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).

### Steps

* Clone this project.
    ```bash
    git clone git@github.com:RobotecAI/ros2-for-unity.git ~/ros2-for-unity
    ```
* 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.
    ```bash
    # galactic
    . /opt/ros/galactic/setup.bash
    ```
* Enter `Ros2ForUnity` working directory.
    ```bash
    cd ~/ros2-for-unity
    ```
* Set up you custom messages in `ros2_for_unity_custom_messages.repos`
* Import necessary and custom messages repositories.
    ```bash
    ./pull_repositories.sh
    ```
    > *NOTE* `pull_repositories.sh` script doesn't update already existing repositories, you have to remove `src/ros2cs` folder to re-import new versions.
* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.
    ```bash
    # standalone mode
    ./build.sh --standalone
    
    # overlay mode
    ./build.sh
    ```
    * You can add `--clean-install` flag to make sure your installation directory is cleaned before deploying.
* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.
* (optionally) To create `.unitypackage` in `install/unity_package`
    ```bash
    create_unity_package.sh -u <your-path-to-unity-editor-executable>
    ```
    > *NOTE* Unity license is required. 

## OS-Specific usage remarks

You can run Unity Editor or App executable from GUI (clicking) or from terminal as long as ROS2 is sourced in your environment.
The best way to ensure that system-wide is to add `source /opt/ros/foxy/setup.bash` to your `~/.profile` file.
Note that you need to re-log for changes in `~/.profile` to take place.
Running Unity Editor through Unity Hub is also supported.

## Usage troubleshooting

**No ROS environment sourced. You need to source your ROS2 (..)**

* 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).

* 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.

**There are no errors but I can't see topics published by Ros2ForUnity**

* Make sure your dds config is correct.
* 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`).


================================================
FILE: README-WINDOWS.md
================================================
# ROS2 For Unity - Windows 10

This readme contains information specific to Window 10. For general information, please see [README.md](README.md).

## Building

We assume that working directory is `C:\dev` and we are using `ROS2 galactic` (replace with `foxy` or `humble` where applicable).

### Prerequisites

It 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.

### Steps

* Make sure [long paths on Windows are enabled](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)
* Make sure you open [`Developer PowerShell for VS` with administrator privileges](https://github.com/RobotecAI/ros2cs/blob/master/README-WINDOWS.md#important-notices)
* 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)
* Clone this project.
  ```powershell
  git clone git@github.com:RobotecAI/ros2-for-unity.git C:\dev\ros2-for-unity
  ```
* Source your ROS2 installation (`C:\dev\ros2_foxy\local_setup.ps1`) in the terminal before you proceed.
  ```
  C:\dev\ros2_foxy\local_setup.ps1
  ```
* Enter `Ros2ForUnity` working directory.
    ```powershell
    cd C:\dev\ros2-for-unity
    ```
* Set up you custom messages in `ros2_for_unity_custom_messages.repos`
* Import necessary and custom messages repositories.
    ```powershell
    .\pull_repositories.ps1
    ```
    > *NOTE* `pull_repositories.ps1` script doesn't update already existing repositories, you have to remove `src\ros2cs` folder to re-import new versions.
* Build `Ros2ForUnty`. You can build it in standalone or overlay mode.
    ```powershell
    # standalone mode
    ./build.ps1 -standalone
    
    # overlay mode
    ./build.ps1
    ```
  * You can build with `-clean_install` to make sure your installation directory is cleaned before deploying.
* Unity Asset is ready to import into your Unity project. You can find it in `install/asset/` directory.
* (optionally) To create `.unitypackage` in `install/unity_package`
  ```powershell
  create_unity_package.ps1
  ```
  > *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.

## Build troubleshooting

- If you see one of the following errors:
><script_name> is not digitally signed

><script_name> cannot be loaded because running scripts is disabled on this system

Please 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).

- If you see the following error:
>     [4.437s] Traceback (most recent call last):
>     [4.437s]   File "<string>", line 1, in <module>
>     [4.437s]   File "C:\Python38\lib\site-packages\numpy\__init__.py", line 148, in <module>
>     [4.437s]     from . import _distributor_init
>     [4.437s]   File "C:\Python38\lib\site-packages\numpy\_distributor_init.py", line 26, in <module>
>     [4.437s]     WinDLL(os.path.abspath(filename))
>     [4.437s]   File "C:\Python38\lib\ctypes\__init__.py", line 373, in __init__
>     [4.453s]     self._handle = _dlopen(self._name, mode)
>     [4.453s] OSError: [WinError 193] %1 is not a valid Win32 application
>     [4.469s] CMake Error at C:/dev/ros2_foxy/share/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake:213 (message)
>     [4.469s]   execute_process(C:/Python38/python.exe -c 'import
>     [4.469s]   numpy;print(numpy.get_include())') returned error code 1
>     [4.469s] Call Stack (most recent call first):
>     [4.469s]   C:/dev/ros2_foxy/share/ament_cmake_core/cmake/core/ament_execute_extensions.cmake:48 (include)
>     [4.469s]   C:/dev/ros2_foxy/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:286 (ament_execute_extensions)
>     [4.484s]   CMakeLists.txt:16 (rosidl_generate_interfaces)
Please reinstall `numpy` package from python by typing:
```powershell
pip uninstall numpy
pip install numpy
```

**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)**

## OS-Specific usage remarks

> If the Asset is built with `-standalone` flag (the default), then nothing extra needs to be done.
Otherwise, you have to source your ros distribution before launching either Unity3D Editor or Application.

> Note that after you build the Asset, you can use it on a machine that has no ros2 installation (if built with `-standalone`).

> You can simply copy over the `Ros2ForUnity` subdirectory to update your Asset.


================================================
FILE: README.md
================================================

Ros2 For Unity
===============

> [!NOTE]  
> 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
> 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. 

ROS2 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.
Advantages of this module include:
- High performance - higher throughput and considerably lower latencies comparing to bridging solutions.
- 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.
- 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.
- Supports all standard ROS2 messages.
- 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.
- The module is wrapped as a Unity asset.

## Platforms

Supported OSes:
- Ubuntu 22.04 (bash)
- Ubuntu 20.04 (bash)
- Windows 10 (powershell)
- Windows 11* (powershel)

> \* 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.


Supported ROS2 distributions:
- Galactic
- Humble

Supported Unity3d:
- 2020+

Older 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.

This asset can be prepared in two flavours:

- 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.
- 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.

## Releases

The best way to start quickly is to use our releases.

You 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.

## Building

> **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.
It 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`.

Please see OS-specific instructions:
- [Instructions for Ubuntu](README-UBUNTU.md)
- [Instructions for Windows](README-WINDOWS.md)

## Custom messages

Custom messages can be included in the build by either:
* listing them in `ros2_for_unity_custom_messages.repos` file, or
* manually inserting them in `src/ros2cs` directory. If the folder doesn't exist, you must pull repositories first (see building steps for each OS).

## Installation

1. 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.
1. Open or create Unity project.
1. Import asset into project:
    1. copy `install/asset/Ros2ForUnity` into your project `Assets` folder, or
    1. if you have deployed an `.unitypackage` - import it in Unity Editor by selecting `Import Package` → `Custom Package`

## Usage

**Prerequisites**

* If your build was prepared with `--standalone` flag then you are fine, and all you have to do is run the editor

otherwise

* source ROS2 which matches the `Ros2ForUnity` version, then run the editor from within the very same terminal/console.

**Initializing Ros2ForUnity**

1. Initialize `Ros2ForUnity` by creating a "hook" object which will be your wrapper around ROS2. You have two options:
    1. `ROS2UnityComponent` based on `MonoBehaviour` which must be attached to a `GameObject` somewhere in the scene, then:
        ```c#
        using ROS2;
        ...
        // Example method of getting component, if ROS2UnityComponent lives in different GameObject, just use different get component methods.
        ROS2UnityComponent ros2Unity = GetComponent<ROS2UnityComponent>();
        ```
    1. or `ROS2UnityCore` which is a standard class that can be created anywhere
        ```c#
        using ROS2;
        ...
        ROS2UnityCore ros2Unity = new ROS2UnityCore();
        ```
1. Create a node. You must first check if `Ros2ForUnity` is initialized correctly:
    ```c#
    private ROS2Node ros2Node;
    ...
    if (ros2Unity.Ok()) {
        ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
    }
    ```

**Publishing messages:**

1. Create publisher
    ```c#
    private IPublisher<std_msgs.msg.String> chatter_pub;
    ...
    if (ros2Unity.Ok()){
        chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>("chatter"); 
    }
    ```
1. Send messages
    ```c#
    std_msgs.msg.String msg = new std_msgs.msg.String();
    msg.Data = "Hello Ros2ForUnity!";
    chatter_pub.Publish(msg);
    ```

**Subscribing to a topic**

1. Create subscriber:
    ```c#
    private ISubscription<std_msgs.msg.String> chatter_sub;
    ...
    if (ros2Unity.Ok()) {
        chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(
            "chatter", msg => Debug.Log("Unity listener heard: [" + msg.Data + "]"));
    }
    ```

**Creating a service**

1. Create service body:
    ```c#
    public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)
    {
        example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();
        response.Sum = msg.A + msg.B;
        return response;
    }
    ```

1. Create a service with a service name and callback:
    ```c#
    IService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> service = 
        ros2Node.CreateService<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(
            "add_two_ints", addTwoInts);
    ```

**Calling a service**

1. Create a client:
    ```c#
    private IClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response> addTwoIntsClient;
    ...
    addTwoIntsClient = ros2Node.CreateClient<example_interfaces.srv.AddTwoInts_Request, example_interfaces.srv.AddTwoInts_Response>(
        "add_two_ints");
    ```

1. Create a request and call a service:
    ```c#
    example_interfaces.srv.AddTwoInts_Request request = new example_interfaces.srv.AddTwoInts_Request();
    request.A = 1;
    request.B = 2;
    var response = addTwoIntsClient.Call(request);
    ```

1. You can also make an async call:
    ```c#
    Task<example_interfaces.srv.AddTwoInts_Response> asyncTask = addTwoIntsClient.CallAsync(request);
    ...
    asyncTask.ContinueWith((task) => { Debug.Log("Got answer " + task.Result.Sum); });
    ```
### Examples

1. 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.
    > **Note:** Each example script looks for `ROS2UnityComponent` in its own game object. However, this is not a requirement, just example implementation.

**Topics**
1. Add `ROS2TalkerExample.cs` script to the very same game object.
1. Add `ROS2ListenerExample.cs` script to the very same game object.

Once 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.

**Services**
1. Add `ROS2ServiceExample.cs` script to the very same game object.
1. Add `ROS2ClientExample.cs` script to the very same game object.

Once you start the project in Unity, you should be able to see client node calling an example service.

## Acknowledgements 

Open-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.


================================================
FILE: build.ps1
================================================

<#
.SYNOPSIS
    Builds Ros2ForUnity asset
.DESCRIPTION
    This script builds Ros2DorUnity asset
.PARAMETER with_tests
    Build tests
.PARAMETER standalone
    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
.PARAMETER clean_install
    Makes a clean installation. Removes install dir before deploying
#>
Param (
    [Parameter(Mandatory=$false)][switch]$with_tests=$false,
    [Parameter(Mandatory=$false)][switch]$standalone=$false,
    [Parameter(Mandatory=$false)][switch]$clean_install=$false
)

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

if(-Not (Test-Path -Path "$scriptPath\src\ros2cs")) {
    Write-Host "Pull repositories with 'pull_repositories.ps1' first." -ForegroundColor Red
    exit 1
}

Write-Host $msg -ForegroundColor Green
$options = @{
    with_tests = $with_tests
    standalone = $standalone
}

if($clean_install) {
    Write-Host "Cleaning install directory..." -ForegroundColor White
    Remove-Item -Path "$scriptPath\install" -Force -Recurse -ErrorAction Ignore
}

if($standalone) {
  & "python" $SCRIPTPATH\src\scripts\metadata_generator.py --standalone
} else {
  & "python" $SCRIPTPATH\src\scripts\metadata_generator.py
}

& "$scriptPath\src\ros2cs\build.ps1" @options
if($?) {
    md -Force $scriptPath\install\asset | Out-Null
    Copy-Item -Path $scriptPath\src\Ros2ForUnity -Destination $scriptPath\install\asset\ -Recurse -Force
    
    $plugin_path=Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity\Plugins\"
    Write-Host "Deploying build to $plugin_path" -ForegroundColor Green
    & "$scriptPath\deploy_unity_plugins.ps1" $plugin_path

    Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\Windows\x86_64\
    Copy-Item -Path $scriptPath\src\Ros2ForUnity\metadata_ros2cs.xml -Destination $scriptPath\install\asset\Ros2ForUnity\Plugins\
} else {
    Write-Host "Ros2cs build failed!" -ForegroundColor Red
    exit 1
}




================================================
FILE: build.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`

display_usage() {
    echo "Usage: "
    echo ""
    echo "build.sh [--with-tests] [--standalone] [--clean-install]"
    echo ""
    echo "Options:"
    echo "--with-tests - build with tests"
    echo "--standalone - standalone version"
    echo "--clean-install - makes a clean installation, removes install directory before deploying"
}

if [ ! -d "$SCRIPTPATH/src/ros2cs" ]; then
    echo "Pull repositories with 'pull_repositories.sh' first."
    exit 1
fi

OPTIONS=""
STANDALONE=0
TESTS=0
CLEAN_INSTALL=0

while [[ $# -gt 0 ]]; do
  key="$1"
  case $key in
    -t|--with-tests)
      OPTIONS="$OPTIONS --with-tests"
      TESTS=1
      shift # past argument
      ;;
    -s|--standalone)
      if ! hash patchelf 2>/dev/null ; then
        echo "Patchelf missing. Standalone build requires patchelf. Install it via apt 'sudo apt install patchelf'."
        exit 1
      fi
      OPTIONS="$OPTIONS --standalone"
      STANDALONE=1
      shift # past argument
      ;;
    -c|--clean-install)
      CLEAN_INSTALL=1
      shift # past argument
      ;;
    -h|--help)
      display_usage
      exit 0
      shift # past argument
      ;;
    *)    # unknown option
      shift # past argument
      ;;
  esac
done

if [ $CLEAN_INSTALL == 1 ]; then
    echo "Cleaning install directory..."
    rm -rf $SCRIPTPATH/install/*
fi

if [ $STANDALONE == 1 ]; then
  python3 $SCRIPTPATH/src/scripts/metadata_generator.py --standalone
else
  python3 $SCRIPTPATH/src/scripts/metadata_generator.py
fi

if $SCRIPTPATH/src/ros2cs/build.sh $OPTIONS; then
    mkdir -p $SCRIPTPATH/install/asset && cp -R $SCRIPTPATH/src/Ros2ForUnity $SCRIPTPATH/install/asset/
    $SCRIPTPATH/deploy_unity_plugins.sh $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/
    cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/Linux/x86_64/metadata_ros2cs.xml
    cp $SCRIPTPATH/src/Ros2ForUnity/metadata_ros2cs.xml $SCRIPTPATH/install/asset/Ros2ForUnity/Plugins/metadata_ros2cs.xml
else
    echo "Ros2cs build failed!"
    exit 1
fi


================================================
FILE: create_unity_package.ps1
================================================

<#
.SYNOPSIS
    Creates a 'unitypackage' from an input asset.
.DESCRIPTION
    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.
.PARAMETER unity_path
    Unity editor executable path
.PARAMETER input_asset
    input asset to pack into unity package
.PARAMETER package_name
    Unity package name
.PARAMETER output_dir
    output file directory
#>
Param (
    [Parameter(Mandatory=$true)][string]$unity_path,
    [Parameter(Mandatory=$false)][string]$input_asset,
    [Parameter(Mandatory=$false)][string]$package_name="Ros2ForUnity",
    [Parameter(Mandatory=$false)][string]$output_dir
)

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$temp_dir = $Env:TEMP

if(-Not $PSBoundParameters.ContainsKey('input_asset')) {
    $input_asset= Join-Path -Path $scriptPath -ChildPath "\install\asset\Ros2ForUnity"
}

if(-Not $PSBoundParameters.ContainsKey('output_dir')) {
    $output_dir= Join-Path -Path $scriptPath -ChildPath "\install\unity_package"
}

if(-Not (Test-Path -Path "$input_asset")) {
    Write-Host "Input asset '$input_asset' doesn't exist! Use 'build.ps1' to build project first." -ForegroundColor Red
    exit 1
}

if(-Not (Test-Path -Path "$output_dir")) {
    mkdir ${output_dir} | Out-Null
}

& "$unity_path" -version | Tee-Object -Variable unity_version | Out-Null

if ($unity_version -match '^[0-9]{4}\.[0-9]*\.[0-9]*[f]?[0-9]*$') {
    Write-Host "Unity editor confirmed."
} else {
    while (1) {
        $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"
        if ($confirmation -eq 'y' -or $confirmation -eq 'Y') {
            break;
        } elseif ( $confirmation -eq 'n' -or $confirmation -eq 'N' ) {
            exit 1;
        } else {
            Write-Host "Please answer [y]es or [n]o.";
        }
    }
}
Write-Host "Using ${unity_path} editor."

$tmp_project_path = Join-Path -Path "$temp_dir" -ChildPath "\ros2cs_unity_project\$unity_version"

# Create temp project
if(Test-Path -Path "$tmp_project_path") {
    Write-Host "Found existing temporary project for Unity $unity_version."
    Remove-Item -Path "$tmp_project_path\Assets\*" -Force -Recurse -ErrorAction Ignore
} else {
    Write-Host "Creating Unity temporary project for Unity $unity_version..."
    & "$unity_path" -createProject "$tmp_project_path" -batchmode -quit | Out-Null
}

# Copy asset
Write-Host "Copying asset '$input_asset' to export..."
Copy-Item -Path "$input_asset" -Destination "$tmp_project_path\Assets\$package_name" -Recurse

# Creating asset
Write-Host "Saving unitypackage '$output_dir\$package_name.unitypackage'..."
& "$unity_path" -projectPath "$tmp_project_path" -exportPackage "Assets\$package_name" "$output_dir\$package_name.unitypackage" -batchmode -quit | Out-Null

# Cleaning up
Write-Host "Cleaning up temporary project..."
Remove-Item -Path "$tmp_project_path\Assets\*" -Force -Recurse -ErrorAction Ignore

Write-Host "Done!" -ForegroundColor Green



================================================
FILE: create_unity_package.sh
================================================
#!/bin/bash

SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`

display_usage() {
  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."
  echo ""
  echo "Usage:" 
  echo "create_unity_package.sh -u <UNITY_PATH> -i [INPUT_ASSET] -p [PACKAGE_NAME] -o [OUTPUT_DIR]"
  echo ""
  echo "UNITY_PATH - Unity editor executable path"
  echo "INPUT_ASSET - input asset to pack into unity package, default = 'install/asset/Ros2ForUnity'"
  echo "PACKAGE_NAME - unity package name, default = 'Ros2ForUnity'"
  echo "OUTPUT_DIR - output file directory, default = 'install/unity_package'"
}

UNITY_PATH=""
INPUT_ASSET="install/asset/Ros2ForUnity"
PACKAGE_NAME="Ros2ForUnity"
OUTPUT_DIR="$SCRIPTPATH/install/unity_package"

while [[ $# -gt 0 ]]; do
  key="$1"

  case $key in
    -u|--unity-path)
      UNITY_PATH="$2"
      shift # past argument
      shift # past value
      ;;
    -p|--package_name)
      PACKAGE_NAME="$2"
      shift # past argument
      shift # past value
      ;;
    -i|--input-directory)
      INPUT_ASSET="$2"
      shift # past argument
      shift # past value
      ;;
    -o|--output-directory)
      OUTPUT_DIR="$2"
      shift # past argument
      shift # past value
      ;;
    -h|--help)
      display_usage
      exit 0
      shift # past argument
      ;;
    *)    # unknown option
      shift # past argument
      ;;
  esac
done

if [ -z "$UNITY_PATH" ] || [ -z "$PACKAGE_NAME" ] || [ -z "$INPUT_ASSET" ] || [ -z "$OUTPUT_DIR" ]; then
    echo -e "\nMissing arguments!"
    echo ""
    display_usage
    exit 1
fi

if [ ! -d "$INPUT_ASSET" ]; then
    echo "Input asset '$INPUT_ASSET' doesn't exist!  Use 'build.sh' to build project first."
    exit 1
fi

UNITY_VERSION=`$UNITY_PATH -version`

# Test if unity editor is valid
if [[ $UNITY_VERSION =~ ^[0-9]{4}\.[0-9]*\.[0-9]*[f]?[0-9]*$ ]]; then
    echo "Unity editor confirmed."
else
    while true; do
      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
      yn=${yn:-"n"}
      case $yn in
          [Yy]* ) break;;
          [Nn]* ) exit 1;;
          * ) echo "Please answer [y]es or [n]o.";;
      esac
    done
fi

echo "Using \"${UNITY_PATH}\" editor."

TMP_PROJECT_PATH=/tmp/ros2cs_unity_project/$UNITY_VERSION
# Create temp project
if [ -d "$TMP_PROJECT_PATH" ]; then
    echo "Found existing temporary project for Unity $UNITY_VERSION."
    rm -rf $TMP_PROJECT_PATH/Assets/*
else
  rm -rf $TMP_PROJECT_PATH
  echo "Creating Unity temporary project for Unity $UNITY_VERSION..."
  $UNITY_PATH -createProject $TMP_PROJECT_PATH -batchmode -quit
fi

# Copy asset
echo "Copying asset to export..."
cp -r "$INPUT_ASSET" "$TMP_PROJECT_PATH/Assets/$PACKAGE_NAME"

# Creating asset
echo "Saving unitypackage '$OUTPUT_DIR/$PACKAGE_NAME.unitypackage'..."
mkdir -p $OUTPUT_DIR
$UNITY_PATH -projectPath "$TMP_PROJECT_PATH" -exportPackage "Assets/$PACKAGE_NAME" "$OUTPUT_DIR/$PACKAGE_NAME.unitypackage" -batchmode -quit

# Cleaning up
echo "Cleaning up temporary project..."
rm -rf $TMP_PROJECT_PATH/Assets/*

echo "Done!"



================================================
FILE: deploy_unity_plugins.ps1
================================================
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$pluginDir=$args[0]

function Print-Help {
"
Usage: 
deploy_unity_plugins.ps1 <PLUGINS_DIR>

PLUGINS_DIR - Ros2ForUnity/Plugins.
"
}

if (([string]::IsNullOrEmpty($pluginDir)) -Or $args[0] -eq "--help" -Or $args[0] -eq "-h")
{
    Print-Help
    exit
}

if (Test-Path -Path $pluginDir) {
    Write-Host "Copying plugins to to: '$pluginDir' ..."
    Get-ChildItem $scriptPath\install\lib\dotnet\ -Recurse -Exclude @('*.pdb') | Copy-Item -Destination ${pluginDir}
    Write-Host "Plugins copied to: '$pluginDir'" -ForegroundColor Green
    if(-not (Test-Path -Path $pluginDir\Windows\x86_64\)) {
        mkdir ${pluginDir}\Windows\x86_64\
    }
    Write-Host "Copying libraries to: '$pluginDir\Windows\x86_64\' ..."
    Get-ChildItem $scriptPath\install\bin\ -Recurse -Exclude @('*_py.dll', '*_python.dll') | Copy-Item -Destination ${pluginDir}\Windows\x86_64\
    if(-not (Test-Path -Path $scriptPath\install\standalone\)) {
        mkdir $scriptPath\install\standalone
    }
    (Copy-Item -Path $scriptPath\install\standalone\*.dll -Destination ${pluginDir}\Windows\x86_64\ 4>&1).Message
    if(-not (Test-Path -Path $scriptPath\install\resources\)) {
        mkdir $scriptPath\install\resources
    }
    (Copy-Item -Path $scriptPath\install\resources\*.dll -Destination ${pluginDir}\Windows\x86_64\ 4>&1).Message
    Write-Host "Libraries copied to '${pluginDir}\Windows\x86_64\'" -ForegroundColor Green
} else {
    Write-Host "Plugins directory: '$pluginDir' doesn't exist. Please create it first manually." -ForegroundColor Red
}


================================================
FILE: deploy_unity_plugins.sh
================================================
#!/bin/bash

SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`

if [ $# -eq 0 ] || [ $1 = "-h" ] || [ $1 = "--help" ]; then
  echo "Usage:" 
  echo "deploy_unity_plugins.sh <PLUGINS_DIR>"
  echo ""
  echo "PLUGINS_DIR - Ros2ForUnity/Plugins folder."
  exit 1
fi

pluginDir=$1

mkdir -p  ${pluginDir}/Linux/x86_64/
find install/lib/dotnet/ -maxdepth 1 -not -name "*.pdb" -type f -exec cp {} ${pluginDir} \;
cp $SCRIPTPATH/install/standalone/* ${pluginDir}/Linux/x86_64/ 2>/dev/null
find install/lib/ -maxdepth 1 -not -name "*_python.so" -type f -exec cp {} ${pluginDir}/Linux/x86_64/ \;
cp $SCRIPTPATH/install/resources/*.so ${pluginDir}/Linux/x86_64/ 2>/dev/null


================================================
FILE: docker/Dockerfile
================================================
ARG ROS2_DISTRO=humble
FROM ros:${ROS2_DISTRO}-ros-base

RUN 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

RUN apt update && apt install -y curl wget git

RUN curl -s https://packagecloud.io/install/repositories/dirk-thomas/vcstool/script.deb.sh | sudo bash
RUN apt update && apt install -y python3-vcstool

RUN apt update && apt install -y apt-transport-https patchelf dotnet-sdk-6.0
RUN apt update && apt install -y ffmpeg libsm6 libxext6 libgtk-3-0

ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

RUN mkdir -p /workdir/ros2-for-unity
RUN chmod -R 777 /workdir
RUN chmod -R 777 /home

ENTRYPOINT [ "/entrypoint.sh" ]


================================================
FILE: docker/README.md
================================================
Ros2 For Unity Docker
===============

Currently only building asset on Ubuntu is supported. Build windows version is not supported.

## Build docker image

1. Source ROS2 (foxy or galactic):

```bash
. /opt/ros/<ROS_DISTRO>/setup.bash
```

2. Build image - image will be based on sourced ROS2 version:

```bash
./build_image.sh
```

## Using docker container

1. Run docker container. Container will fetch `master` version of `ros2-for-unity`:

```bash
./run_container.sh
```

2. Build asset. `./run_container.sh` script mounts `install` host directory inside docker, so you can find install results on host machine:

```bash
./build.sh --with-tests
```

## Adding custom messages

You 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`

================================================
FILE: docker/build_image.sh
================================================
#!/bin/bash

if [ -z "$ROS_DISTRO" ]; then
    echo "Source your ros2 distro first."
    exit 1
fi

docker build . --build-arg ROS2_DISTRO=$ROS_DISTRO --tag ros2-for-unity


================================================
FILE: docker/custom_messages/INSERT_CUSTOM_MESSAGES_HERE
================================================


================================================
FILE: docker/entrypoint.sh
================================================
#!/bin/bash

source "/opt/ros/$ROS_DISTRO/setup.bash"

echo "######################################################################"
echo ""
echo "Cloning recent version of 'ros2-for-unity'"
echo ""
echo "######################################################################"
echo ""

git clone https://github.com/RobotecAI/ros2-for-unity.git /workdir/.ros2-for-unity

shopt -s dotglob
mkdir -p /workdir/ros2-for-unity
mv /workdir/.ros2-for-unity/* /workdir/ros2-for-unity
cd /workdir/ros2-for-unity/ && ./pull_repositories.sh
mkdir -p /home/$(whoami)
git config --global --add safe.directory /workdir/ros2-for-unity
shopt -u dotglob

ln -s /workdir/custom_messages /workdir/ros2-for-unity/src/ros2cs/src/custom_messages

echo ""
echo "######################################################################"
echo ""
echo "Welcome to 'ros2-for-unity' docker container. Your ROS2 distro is $ROS_DISTRO."
echo ""
echo "Type './build.sh' to build 'ros2-for-unity'. You will find installed libs on your host machine inside 'install' directory"
echo ""
echo "######################################################################"
echo ""

exec bash


================================================
FILE: docker/run_container.sh
================================================
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`

mkdir -p $SCRIPTPATH/../install
docker run \
--rm \
-it \
--name ros2-for-unity \
--user $(id -u):$(id -g) \
-v /etc/passwd:/etc/passwd:ro \
-v /etc/group:/etc/group:ro \
-v /etc/shadow:/etc/shadow:ro \
-v $(pwd)/../install:/workdir/ros2-for-unity/install:rw \
-v $(pwd)/custom_messages:/workdir/custom_messages \
ros2-for-unity \
bash


================================================
FILE: pull_repositories.ps1
================================================
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

if (([string]::IsNullOrEmpty($Env:ROS_DISTRO)))
{
    Write-Host "Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported." -ForegroundColor Red
    exit
}

$ros2cs_repos = Join-Path -Path $scriptPath -ChildPath "\ros2cs.repos"
$custom_repos = Join-Path -Path $scriptPath -ChildPath "\ros2_for_unity_custom_messages.repos"

Write-Host "========================================="
Write-Host "* Pulling ros2cs repository:"
vcs import --input $ros2cs_repos

Write-Host ""
Write-Host "========================================="
Write-Host "Pulling custom repositories:"
vcs import --input $custom_repos

Write-Host ""
Write-Host "========================================="
Write-Host "Pulling ros2cs dependencies:"
& "$scriptPath/src/ros2cs/get_repos.ps1"


================================================
FILE: pull_repositories.sh
================================================
#!/bin/bash

SCRIPT=$(readlink -f $0)
SCRIPTPATH=`dirname $SCRIPT`

if [ -z "${ROS_DISTRO}" ]; then
    echo "Can't detect ROS2 version. Source your ros2 distro first. Foxy and Galactic are supported"
    exit 1
fi

echo "========================================="
echo "* Pulling ros2cs repository:"
vcs import < "ros2cs.repos"

echo ""
echo "========================================="
echo "Pulling custom repositories:"
vcs import < "ros2_for_unity_custom_messages.repos"

echo ""
echo "========================================="
echo "Pulling ros2cs dependencies:"
cd "$SCRIPTPATH/src/ros2cs"
./get_repos.sh
cd -


================================================
FILE: ros2_for_unity_custom_messages.repos
================================================
# NOTE: Use this file if you want to build with custom messages that reside in a separate remote repo.
# NOTE: use the following format

repositories:
#  src/ros2cs/custom_messages/<package_name>:
#    type: git
#    url: <repo_url>
#    version: <repo_branch>
#  custom_messages/<package2_name>:
#    ...
#    ...



================================================
FILE: ros2cs.repos
================================================
repositories:
  src/ros2cs/:
    type: git
    url:     https://github.com/RobotecAI/ros2cs.git
    version: 1.3.0


================================================
FILE: src/Ros2ForUnity/COLCON_IGNORE
================================================


================================================
FILE: src/Ros2ForUnity/Plugins.meta
================================================
fileFormatVersion: 2
guid: 79b46a636d96b67468a29e597cb7b06a
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/PostInstall.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;

namespace ROS2
{

/// <summary>
/// An internal class responsible for installing ros2-for-unity metadata files 
/// </summary>
internal class PostInstall : IPostprocessBuildWithReport
{
    public int callbackOrder { get { return 0; } }
    public void OnPostprocessBuild(BuildReport report)
    {
        var r2fuMetadataName = "metadata_ros2_for_unity.xml";
        var r2csMetadataName = "metadata_ros2cs.xml";

        // FileUtil.CopyFileOrDirectory: All file separators should be forward ones "/".
        var r2fuMeta = ROS2ForUnity.GetRos2ForUnityPath() + "/" + r2fuMetadataName; 
        var r2csMeta = ROS2ForUnity.GetPluginPath() + "/" + r2csMetadataName;
        var outputDir = Directory.GetParent(report.summary.outputPath);
        var execFilename = Path.GetFileNameWithoutExtension(report.summary.outputPath);
        FileUtil.CopyFileOrDirectory(
            r2fuMeta, outputDir + "/" + execFilename + "_Data/" + r2fuMetadataName);
        if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinux64) {
            FileUtil.CopyFileOrDirectory(
                r2csMeta, outputDir + "/" + execFilename + "_Data/Plugins/" + r2csMetadataName);

            // Copy versioned libraries (Unity skips them)
            Regex soWithVersionReg = new Regex(@".*\.so(\.[0-9])+$");
            var versionedLibs = new List<String>(Directory.GetFiles(ROS2ForUnity.GetPluginPath()))
                                    .Where(path => soWithVersionReg.IsMatch(path))
                                    .ToList();
            foreach (var libPath in versionedLibs) {
                FileUtil.CopyFileOrDirectory(
                    libPath, outputDir + "/" + execFilename + "_Data/Plugins/" + Path.GetFileName(libPath));
            }
        } else {
            FileUtil.CopyFileOrDirectory(
                r2csMeta, outputDir + "/" + execFilename + "_Data/Plugins/x86_64/" + r2csMetadataName);
        }
    }

}

}
#endif


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ClientExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using ROS2;

using addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;
using addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;

/// <summary>
/// An example class provided for testing of basic ROS2 client
/// </summary>
public class ROS2ClientExample : MonoBehaviour
{
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private IClient<addTwoIntsReq, addTwoIntsResp> addTwoIntsClient;
    private bool isRunning = false;
    private Task<addTwoIntsResp> asyncTask;

    IEnumerator periodicAsyncCall()
    {
        while (ros2Unity.Ok())
        {

            while (!addTwoIntsClient.IsServiceAvailable())
            {
                yield return new WaitForSecondsRealtime(1);
            }

            addTwoIntsReq request = new addTwoIntsReq();
            request.A = Random.Range(0, 100);
            request.B = Random.Range(0, 100);
            
            asyncTask = addTwoIntsClient.CallAsync(request);
            asyncTask.ContinueWith((task) => { Debug.Log("Got async answer " + task.Result.Sum); });
            
            yield return new WaitForSecondsRealtime(1);
        }
    }

    IEnumerator periodicCall()
    {
        while (ros2Unity.Ok())
        {

            while (!addTwoIntsClient.IsServiceAvailable())
            {
                yield return new WaitForSecondsRealtime(1);
            }

            addTwoIntsReq request = new addTwoIntsReq();
            request.A = Random.Range(0, 100);
            request.B = Random.Range(0, 100);
            var response = addTwoIntsClient.Call(request);

            Debug.Log("Got sync answer " + response.Sum);

            yield return new WaitForSecondsRealtime(1);
        }
    }

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
        if (ros2Unity.Ok())
        {
            if (ros2Node == null)
            {
                ros2Node = ros2Unity.CreateNode("ROS2UnityClient");
                addTwoIntsClient = ros2Node.CreateClient<addTwoIntsReq, addTwoIntsResp>(
                    "add_two_ints");
            }
        }
    }

    void Update()
    {
        if (!isRunning)
        {
            isRunning = true;

            // Async calls
            StartCoroutine(periodicAsyncCall());

            // Sync calls
            StartCoroutine(periodicCall());
        }
    }
}


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Xml;

namespace ROS2
{

/// <summary>
/// An internal class responsible for handling checking, proper initialization and shutdown of ROS2cs,
/// </summary>
internal class ROS2ForUnity
{
    private static bool isInitialized = false;
    private static string ros2ForUnityAssetFolderName = "Ros2ForUnity";
    private XmlDocument ros2csMetadata = new XmlDocument();
    private XmlDocument ros2ForUnityMetadata = new XmlDocument();

    public enum Platform
    {
        Windows,
        Linux
    }
    
    public static Platform GetOS()
    {
        if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer)
        {
            return Platform.Linux;
        }
        else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
        {
            return Platform.Windows;
        }
        throw new System.NotSupportedException("Only Linux and Windows are supported");
    }

    private static bool InEditor() {
        return Application.isEditor;
    }
    
    private static string GetOSName()
    {
        switch (GetOS())
        {
            case Platform.Linux:
                return "Linux";
            case Platform.Windows:
                return "Windows";
            default:
                throw new System.NotSupportedException("Only Linux and Windows are supported");
        }
    }
    
    private string GetEnvPathVariableName()
    {
      string envVariable = "LD_LIBRARY_PATH";
      if (GetOS() == Platform.Windows)
      {
          envVariable = "PATH";
      }
      return envVariable;
    }

    private string GetEnvPathVariableValue()
    {
        return Environment.GetEnvironmentVariable(GetEnvPathVariableName());
    }

    public static string GetRos2ForUnityPath()
    {
        char separator = Path.DirectorySeparatorChar;
        string appDataPath = Application.dataPath;
        string pluginPath = appDataPath;

        if (InEditor()) {
            pluginPath += separator + ros2ForUnityAssetFolderName;
        }
        return pluginPath; 
    }

    public static string GetPluginPath()
    {
        char separator = Path.DirectorySeparatorChar;
        string ros2ForUnityPath = GetRos2ForUnityPath();
        string pluginPath = ros2ForUnityPath;
        
        pluginPath += separator + "Plugins";
        
        if (InEditor()) {
            pluginPath += separator + GetOSName();
        }

        if (InEditor() || GetOS() == Platform.Windows)
        {
           pluginPath += separator + "x86_64";
        }
        
        if (GetOS() == Platform.Windows)
        {
           pluginPath = pluginPath.Replace("/", "\\");
        }

        return pluginPath;
    }

    /// <summary>
    /// Function responsible for setting up of environment paths for standalone builds
    /// </summary>
    /// <description>
    /// Note that on Linux, LD_LIBRARY_PATH as used for dlopen() is determined on process start and this change won't
    /// affect it. Ros2 looks for rmw implementation based on this variable (independently) and the change
    /// is effective for this process, however rmw implementation's dependencies itself are loaded by dynamic linker 
    /// anyway so setting it for Linux is pointless.
    /// </description>
    private void SetEnvPathVariable()
    {
        string currentPath = GetEnvPathVariableValue();
        string pluginPath = GetPluginPath();
        
        char envPathSep = ':';
        if (GetOS() == Platform.Windows)
        {
            envPathSep = ';';
        }

        Environment.SetEnvironmentVariable(GetEnvPathVariableName(), pluginPath + envPathSep + currentPath);
    }

    public bool IsStandalone() {
        return Convert.ToBoolean(Convert.ToInt16(GetMetadataValue(ros2csMetadata, "/ros2cs/standalone")));
    }

    public string GetROSVersion()
    {
        string ros2SourcedCodename = GetROSVersionSourced();
        string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");

        //  Sourced ROS2 libs takes priority
        if (string.IsNullOrEmpty(ros2SourcedCodename)) {
            return ros2FromRos4UMetadata;
        }
        
        return ros2SourcedCodename;
    }

    /// <summary>
    /// Checks if both ros2cs and ros2-for-unity were build for the same ros version as well as
    /// the current sourced ros version matches ros2cs binaries.
    /// </summary>
    public void CheckIntegrity()
    {
        string ros2SourcedCodename = GetROSVersionSourced();
        string ros2FromRos2csMetadata = GetMetadataValue(ros2csMetadata, "/ros2cs/ros2");
        string ros2FromRos4UMetadata = GetMetadataValue(ros2ForUnityMetadata, "/ros2_for_unity/ros2");

        if (ros2FromRos4UMetadata != ros2FromRos2csMetadata) {
            Debug.LogError(
                "ROS2 versions in 'ros2cs' and 'ros2-for-unity' metadata files are not the same. " +
                "This is caused by mixing versions/builds. Plugin might not work correctly."
            );
        }

        if(!IsStandalone() && ros2SourcedCodename != ros2FromRos2csMetadata) {
            Debug.LogError(
                "ROS2 version in 'ros2cs' metadata doesn't match currently sourced version. " +
                "This is caused by mixing versions/builds. Plugin might not work correctly."
            );
        }

        if (IsStandalone() && !string.IsNullOrEmpty(ros2SourcedCodename)) {
            Debug.LogError(
                "You should not source ROS2 in 'ros2-for-unity' standalone build. " +
                "Plugin might not work correctly."
            );
        }
    }

    public string GetROSVersionSourced()
    {
        return Environment.GetEnvironmentVariable("ROS_DISTRO");
    }

    /// <summary>
    /// Check if the ros version is supported, only applicable to non-standalone plugin versions
    /// (i. e. without ros2 libraries included in the plugin).
    /// </summary>
    private void CheckROSSupport(string ros2Codename)
    {
        List<string> supportedVersions = new List<string>() { "foxy", "galactic", "humble", "rolling" };
        var supportedVersionsString = String.Join(", ", supportedVersions);
        if (string.IsNullOrEmpty(ros2Codename))
        {
            string errMessage = "No ROS environment sourced. You need to source your ROS2 " + supportedVersionsString
              + " environment before launching Unity (ROS_DISTRO env variable not found)";
            Debug.LogError(errMessage);
#if UNITY_EDITOR
            EditorApplication.isPlaying = false;
            throw new System.InvalidOperationException(errMessage);
#else
            const int ROS_NOT_SOURCED_ERROR_CODE = 33;
            Application.Quit(ROS_NOT_SOURCED_ERROR_CODE);
#endif
        }

        if (!supportedVersions.Contains(ros2Codename))
        {
            string errMessage = "Currently sourced ROS version differs from supported one. Sourced: " + ros2Codename
              + ", supported: " + supportedVersionsString + ".";
            Debug.LogError(errMessage);
#if UNITY_EDITOR
            EditorApplication.isPlaying = false;
            throw new System.NotSupportedException(errMessage);
#else
            const int ROS_BAD_VERSION_CODE = 34;
            Application.Quit(ROS_BAD_VERSION_CODE);
#endif
        } else if (ros2Codename.Equals("rolling") ) {
            Debug.LogWarning("You are using ROS2 rolling version. Bleeding edge version might not work correctly.");
        }
    }

    private void RegisterCtrlCHandler()
    {
#if ENABLE_MONO
        // Il2CPP build does not support Console.CancelKeyPress currently
        Console.CancelKeyPress += (sender, eventArgs) => {
            eventArgs.Cancel = true;
            DestroyROS2ForUnity();
        };
#endif
    }

    private void ConnectLoggers()
    {
        Ros2csLogger.setCallback(LogLevel.ERROR, Debug.LogError);
        Ros2csLogger.setCallback(LogLevel.WARNING, Debug.LogWarning);
        Ros2csLogger.setCallback(LogLevel.INFO, Debug.Log);
        Ros2csLogger.setCallback(LogLevel.DEBUG, Debug.Log);
        Ros2csLogger.LogLevel = LogLevel.WARNING;
    }

    private string GetMetadataValue(XmlDocument doc, string valuePath)
    {
        return doc.DocumentElement.SelectSingleNode(valuePath).InnerText;
    }

    private void LoadMetadata() 
    {
        char separator = Path.DirectorySeparatorChar;
        try
        {
            ros2csMetadata.Load(GetPluginPath() + separator + "metadata_ros2cs.xml");
            ros2ForUnityMetadata.Load(GetRos2ForUnityPath() + separator + "metadata_ros2_for_unity.xml");
        }
        catch (System.IO.FileNotFoundException)
        {
#if UNITY_EDITOR
            var errMessage = "Could not find metadata files.";
            EditorApplication.isPlaying = false;
            throw new System.IO.FileNotFoundException(errMessage);
#else
            const int NO_METADATA = 1;
            Application.Quit(NO_METADATA);
#endif
        }
    }

    internal ROS2ForUnity()
    {
        // Load metadata
        LoadMetadata();
        string currentRos2Version = GetROSVersion();
        string standalone = IsStandalone() ? "standalone" : "non-standalone";

        // Self checks
        CheckROSSupport(currentRos2Version);
        CheckIntegrity();

        // Library loading
        if (GetOS() == Platform.Windows) {
            // Windows version can run standalone, modifies PATH to ensure all plugins visibility
            SetEnvPathVariable();
        } else {
            // For foxy, it is necessary to use modified version of librcpputils to resolve custom msgs packages.
            ROS2.GlobalVariables.absolutePath = GetPluginPath() + "/";
            if (currentRos2Version == "foxy") {
                ROS2.GlobalVariables.preloadLibrary = true;
                ROS2.GlobalVariables.preloadLibraryName = "librcpputils.so";
            }
        }

        // Initialize
        ConnectLoggers();
        Ros2cs.Init();
        RegisterCtrlCHandler();

        string rmwImpl = Ros2cs.GetRMWImplementation();

        Debug.Log("ROS2 version: " + currentRos2Version + ". Build type: " + standalone + ". RMW: " + rmwImpl);

#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += this.EditorPlayStateChanged;
        EditorApplication.quitting += this.DestroyROS2ForUnity;
#endif
        isInitialized = true;
    }

    private static void ThrowIfUninitialized(string callContext)
    {
        if (!isInitialized)
        {
            throw new InvalidOperationException("Ros2 For Unity is not initialized, can't " + callContext);
        }
    }

    /// <summary>
    /// Check if ROS2 module is properly initialized and no shutdown was called yet
    /// </summary>
    /// <returns>The state of ROS2 module. Should be checked before attempting to create or use pubs/subs</returns>
    public bool Ok()
    {
        if (!isInitialized)
        {
            return false;
        }
        return Ros2cs.Ok();
    }

    internal void DestroyROS2ForUnity()
    {
        if (isInitialized)
        {
            Debug.Log("Shutting down Ros2 For Unity");
            Ros2cs.Shutdown();
            isInitialized = false;
        }
    }

    ~ROS2ForUnity()
    {
        DestroyROS2ForUnity();
    }

#if UNITY_EDITOR
    void EditorPlayStateChanged(PlayModeStateChange change)
    {
        if (change == PlayModeStateChange.ExitingPlayMode)
        {
            DestroyROS2ForUnity();
        }
    }
#endif
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs.meta
================================================
fileFormatVersion: 2
guid: 4cdb4e72fb0aa46c09e52778257ed142
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using UnityEngine;

namespace ROS2
{

/// <summary>
/// An example class provided for testing of basic ROS2 communication
/// </summary>
public class ROS2ListenerExample : MonoBehaviour
{
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private ISubscription<std_msgs.msg.String> chatter_sub;

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
    }

    void Update()
    {
        if (ros2Node == null && ros2Unity.Ok())
        {
            ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
            chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(
              "chatter", msg => Debug.Log("Unity listener heard: [" + msg.Data + "]"));
        }
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs.meta
================================================
fileFormatVersion: 2
guid: 75a1bd43b302c4c578a744060319517e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

namespace ROS2
{

/// <summary>
/// A class representing a ros2 node. Multiple nodes can be used. Node can be removed by GC when not used anymore,
/// but will also be removed properly with Ros2cs Shutdown, which ROS2 for Unity performs on application quit
/// The node should be constructed through ROS2UnityComponent class, which also handles spinning
/// </summary>
public class ROS2Node
{
    internal INode node;
    public ROS2Clock clock;
    public string name;

    // Use ROS2UnityComponent to create a node
    internal ROS2Node(string unityROS2NodeName = "unity_ros2_node")
    {
        name = unityROS2NodeName;
        node = Ros2cs.CreateNode(name);
        clock = new ROS2Clock();
    }

    ~ROS2Node()
    {
        Ros2cs.RemoveNode(node);
    }

    private static void ThrowIfUninitialized(string callContext)
    {
        if (!Ros2cs.Ok())
        {
            throw new InvalidOperationException("Ros2 For Unity is not initialized, can't " + callContext);
        }
    }

    /// <summary>
    /// Create a publisher with QoS suitable for sensor data
    /// </summary>
    /// <returns>The publisher</returns>
    /// <param name="topicName">topic that will be used for publishing</param>
    public Publisher<T> CreateSensorPublisher<T>(string topicName) where T : Message, new()
    {
        QualityOfServiceProfile sensorProfile = new QualityOfServiceProfile(QosPresetProfile.SENSOR_DATA);
        return CreatePublisher<T>(topicName, sensorProfile);
    }

    /// <summary>
    /// Create a publisher with indicated QoS.
    /// </summary>
    /// <returns>The publisher</returns>
    /// <param name="topicName">topic that will be used for publishing</param>
    /// <param name="qos">QoS for publishing. If no QoS is selected, it will default to reliable, keep 10 last</param>
    public Publisher<T> CreatePublisher<T>(string topicName, QualityOfServiceProfile qos = null) where T : Message, new()
    {
        ThrowIfUninitialized("create publisher");
        return node.CreatePublisher<T>(topicName, qos);
    }

    /// <summary>
    /// Create a subscription
    /// </summary>
    /// <returns>The subscription</returns>
    /// <param name="topicName">topic to subscribe to</param>
    /// <param name="qos">QoS for subscription. If no QoS is selected, it will default to reliable, keep 10 last</param>
    public Subscription<T> CreateSubscription<T>(string topicName, Action<T> callback,
        QualityOfServiceProfile qos = null) where T : Message, new()
    {
        if (qos == null)
        {
            qos = new QualityOfServiceProfile(QosPresetProfile.DEFAULT);
        }
        ThrowIfUninitialized("create subscription");
        return node.CreateSubscription<T>(topicName, callback, qos);
    }


    /// <summary>
    /// Remove existing subscription (returned earlier with CreateSubscription)
    /// </summary>
    /// <returns>The whether subscription was found (e. g. false if removed earlier elsewhere) </returns>
    /// <param name="subscription">subscrition to remove, returned from CreateSubscription</param>
    public bool RemoveSubscription<T>(ISubscriptionBase subscription)
    {
        ThrowIfUninitialized("remove subscription");
        return node.RemoveSubscription(subscription);
    }

    /// <summary>
    /// Remove existing publisher
    /// </summary>
    /// <returns>The whether publisher was found (e. g. false if removed earlier elsewhere) </returns>
    /// <param name="publisher">publisher to remove, returned from CreatePublisher or CreateSensorPublisher</param>
    public bool RemovePublisher<T>(IPublisherBase publisher)
    {
        ThrowIfUninitialized("remove publisher");
        return node.RemovePublisher(publisher);
    }

    /// <inheritdoc cref="INode.CreateService"/>
    public Service<I, O> CreateService<I, O>(string topic, Func<I, O> callback, QualityOfServiceProfile qos = null)
        where I : Message, new()
        where O : Message, new()
    {
        ThrowIfUninitialized("create service");
        return node.CreateService<I, O>(topic, callback, qos);
    }

    /// <inheritdoc cref="INode.RemoveService"/>
    public bool RemoveService(IServiceBase service)
    {
        ThrowIfUninitialized("remove service");
        return node.RemoveService(service);
    }

    /// <inheritdoc cref="INode.CreateClient"/>
    public Client<I, O> CreateClient<I, O>(string topic, QualityOfServiceProfile qos = null)
        where I : Message, new()
        where O : Message, new()
    {
        ThrowIfUninitialized(callContext: "create client");
        return node.CreateClient<I, O>(topic, qos);
    }

    /// <inheritdoc cref="INode.RemoveClient"/>
    public bool RemoveClient(IClientBase client)
    {
        ThrowIfUninitialized(callContext: "remove client");
        return node.RemoveClient(client);
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs.meta
================================================
fileFormatVersion: 2
guid: 3e21db77b82bbeb8693eabe308d76f45
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;
using System.Threading;

namespace ROS2
{

/// <summary>
/// An example class provided for performance testing of ROS2 communication
/// </summary>
public class ROS2PerformanceTest : MonoBehaviour
{
    public int messageSize = 10000;
    public int rate = 10;
    private int interval_ms = 100;
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private IPublisher<sensor_msgs.msg.PointCloud2> perf_pub;
    sensor_msgs.msg.PointCloud2 msg;
    private bool initialized = false;

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
        PrepMessage();
    }

    void OnValidate()
    {
        if (rate < 1)
        {
            interval_ms = 0;
        }
        else
        {
            interval_ms = 1000 / rate;
        }
        PrepMessage();
    }

    private void Publish()
    {
        while(true)
        {
            if (ros2Unity.Ok())
            {
                if (ros2Node == null)
                {
                    ros2Node = ros2Unity.CreateNode("ros2_unity_performance_test_node");
                    perf_pub = ros2Node.CreateSensorPublisher<sensor_msgs.msg.PointCloud2>("perf_chatter");
                }

                var msgWithHeader = msg as MessageWithHeader;
                ros2Node.clock.UpdateROSTimestamp(ref msgWithHeader);
                perf_pub.Publish(msg);
                if (interval_ms > 0)
                {
                    Thread.Sleep(interval_ms);
                }
            }
        }
    }

    void FixedUpdate()
    {
        if (!initialized)
        {
            Thread publishThread = new Thread(() => Publish());
            publishThread.Start();
            initialized = true;
        }
    }

    private void AssignField(ref sensor_msgs.msg.PointField pf, string n, uint off, byte dt, uint count)
    {
        pf.Name = n;
        pf.Offset = off;
        pf.Datatype = dt;
        pf.Count = count;
    }

    private void PrepMessage()
    {
        uint count = (uint)messageSize; //point per message
        uint fieldsSize = 16;
        uint rowSize = count * fieldsSize;
        msg = new sensor_msgs.msg.PointCloud2()
        {
            Height = 1,
            Width = count,
            Is_bigendian = false,
            Is_dense = true,
            Point_step = fieldsSize,
            Row_step = rowSize,
            Data = new byte[rowSize * 1]
        };
        uint pointFieldCount = 4;
        msg.Fields = new sensor_msgs.msg.PointField[pointFieldCount];
        for (int i = 0; i < pointFieldCount; ++i)
        {
            msg.Fields[i] = new sensor_msgs.msg.PointField();
        }

        AssignField(ref msg.Fields[0], "x", 0, 7, 1);
        AssignField(ref msg.Fields[1], "y", 4, 7, 1);
        AssignField(ref msg.Fields[2], "z", 8, 7, 1);
        AssignField(ref msg.Fields[3], "intensity", 12, 7, 1);
        float[] pointsArray = new float[count * msg.Fields.Length];

        var floatIndex = 0;
        for (int i = 0; i < count; ++i)
        {
            float intensity = 100;
            pointsArray[floatIndex++] = 1;
            pointsArray[floatIndex++] = 2;
            pointsArray[floatIndex++] = 3;
            pointsArray[floatIndex++] = intensity;
        }
        System.Buffer.BlockCopy(pointsArray, 0, msg.Data, 0, msg.Data.Length);
        msg.SetHeaderFrame("pc");
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs.meta
================================================
fileFormatVersion: 2
guid: 387d300b788c9bd29b6e38808a481155
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ROS2;

using addTwoIntsReq = example_interfaces.srv.AddTwoInts_Request;
using addTwoIntsResp = example_interfaces.srv.AddTwoInts_Response;

/// <summary>
/// An example class provided for testing of basic ROS2 service
/// </summary>
public class ROS2ServiceExample : MonoBehaviour
{
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private IService<addTwoIntsReq, addTwoIntsResp> addTwoIntsService;

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
        if (ros2Unity.Ok())
        {
            if (ros2Node == null)
            {
                ros2Node = ros2Unity.CreateNode("ROS2UnityService");
                addTwoIntsService = ros2Node.CreateService<addTwoIntsReq, addTwoIntsResp>(
                    "add_two_ints", addTwoInts);
            }
        }
    }

    public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_interfaces.srv.AddTwoInts_Request msg)
    {
        Debug.Log("Incoming Service Request A=" + msg.A + " B=" + msg.B);
        example_interfaces.srv.AddTwoInts_Response response = new example_interfaces.srv.AddTwoInts_Response();
        response.Sum = msg.A + msg.B;
        return response;
    }
}


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;

namespace ROS2
{

/// <summary>
/// An example class provided for testing of basic ROS2 communication
/// </summary>
public class ROS2TalkerExample : MonoBehaviour
{
    // Start is called before the first frame update
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private IPublisher<std_msgs.msg.String> chatter_pub;
    private int i;

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
    }

    void Update()
    {
        if (ros2Unity.Ok())
        {
            if (ros2Node == null)
            {
                ros2Node = ros2Unity.CreateNode("ROS2UnityTalkerNode");
                chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>("chatter");
            }

            i++;
            std_msgs.msg.String msg = new std_msgs.msg.String();
            msg.Data = "Unity ROS2 sending: hello " + i;
            chatter_pub.Publish(msg);
        }
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs.meta
================================================
fileFormatVersion: 2
guid: 72620fb0a9290863f8643557405c48e3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;
using System;
using System.Collections.Generic;
using System.Threading;
using ROS2;

namespace ROS2
{

/// <summary>
/// The principal MonoBehaviour class for handling ros2 nodes and executables.
/// Use this to create ros2 node, check ros2 status.
/// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread
/// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work
/// anyway with more than one since the underlying library can handle multiple init and shutdown calls,
/// and does node name uniqueness check independently.
/// </summary>
public class ROS2UnityComponent : MonoBehaviour
{
    private ROS2ForUnity ros2forUnity;
    private List<ROS2Node> nodes;
    private List<INode> ros2csNodes; // For performance in spinning
    private List<Action> executableActions;
    private bool initialized = false;
    private bool quitting = false;
    private int interval = 2;  // Spinning / executor interval in ms
    private object mutex = new object();
    private double spinTimeout = 0.0001;

    public bool Ok()
    {
        lock (mutex)
        {
            if (ros2forUnity == null)
                LazyConstruct();
            return (nodes != null && ros2forUnity.Ok());
        }
    }

    private void LazyConstruct()
    {
        lock (mutex)
        {        
            if (ros2forUnity != null)
                return;

            ros2forUnity = new ROS2ForUnity();
            nodes = new List<ROS2Node>();
            ros2csNodes = new List<INode>();
            executableActions = new List<Action>();
        }
    }

    void Start()
    {
        LazyConstruct();
    }

    public ROS2Node CreateNode(string name)
    {
        LazyConstruct();

        lock (mutex)
        {
            foreach (ROS2Node n in nodes)
            {  // Assumed to be a rare operation on rather small (<1k) list
                if (n.name == name)
                {
                    throw new InvalidOperationException("Cannot create node " + name + ". A node with this name already exists!");
                }
            }
            ROS2Node node = new ROS2Node(name);
            nodes.Add(node);
            ros2csNodes.Add(node.node);
            return node;
        }
    }

    public void RemoveNode(ROS2Node node)
    {
        lock (mutex)
        {
            ros2csNodes.Remove(node.node);
            nodes.Remove(node); //Node will be later deleted if unused, by GC
        }
    }

    /// <summary>
    /// Works as a simple executor registration analogue. These functions will be called with each Tick()
    /// Actions need to take care of correct call resolution by checking in their body (TODO)
    /// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)
    /// </summary>
    public void RegisterExecutable(Action executable)
    {
        LazyConstruct();

        lock (mutex)
        {
            executableActions.Add(executable);
        }
    }

    public void UnregisterExecutable(Action executable)
    {
        lock (mutex)
        {
            executableActions.Remove(executable);
        }
    }

    /// <summary>
    /// "Executor" thread will tick all clocks and spin the node
    /// </summary>
    private void Tick()
    {
        while (!quitting)
        {
            if (Ok())
            {
                lock (mutex)
                {
                    foreach (Action action in executableActions)
                    {
                        action();
                    }
                    Ros2cs.SpinOnce(ros2csNodes, spinTimeout);
                }
            }
            Thread.Sleep(interval);
        }
    }

    void FixedUpdate()
    {
        if (!initialized)
        {
            Thread publishThread = new Thread(() => Tick());
            publishThread.Start();
            initialized = true;
        }
    }

    void OnApplicationQuit()
    {
        quitting = true;
        ros2forUnity.DestroyROS2ForUnity();
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs.meta
================================================
fileFormatVersion: 2
guid: feab04ad06492965492b3edc6423aa53
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;
using System;
using System.Collections.Generic;
using System.Threading;
using ROS2;

namespace ROS2
{

    /// <summary>
    /// The principal class for handling ros2 nodes and executables.
    /// Use this to create ros2 node, check ros2 status.
    /// Spins and executes actions (e. g. clock, sensor publish triggers) in a dedicated thread
    /// TODO: this is meant to be used as a one-of (a singleton). Enforce. However, things should work
    /// anyway with more than one since the underlying library can handle multiple init and shutdown calls,
    /// and does node name uniqueness check independently.
    /// </summary>
    public class ROS2UnityCore
    {
        private ROS2ForUnity ros2forUnity;
        private List<ROS2Node> nodes;
        private List<INode> ros2csNodes; // For performance in spinning
        private List<Action> executableActions;
        private bool quitting = false;
        private int interval = 2;  // Spinning / executor interval in ms
        private object mutex = new object();
        private double spinTimeout = 0.0001;

        public bool Ok()
        {
            lock (mutex)
            {
                return (nodes != null && ros2forUnity.Ok());
            }
        }

        public ROS2UnityCore()
        {
            lock (mutex)
            {
                ros2forUnity = new ROS2ForUnity();
                nodes = new List<ROS2Node>();
                ros2csNodes = new List<INode>();
                executableActions = new List<Action>();

                Thread publishThread = new Thread(() => Tick());
                publishThread.Start();
            }
        }

        public ROS2Node CreateNode(string name)
        {
            lock (mutex)
            {
                foreach (ROS2Node n in nodes)
                {  // Assumed to be a rare operation on rather small (<1k) list
                    if (n.name == name)
                    {
                        throw new InvalidOperationException("Cannot create node " + name + ". A node with this name already exists!");
                    }
                }
                ROS2Node node = new ROS2Node(name);
                nodes.Add(node);
                ros2csNodes.Add(node.node);
                return node;
            }
        }

        public void RemoveNode(ROS2Node node)
        {
            lock (mutex)
            {
                ros2csNodes.Remove(node.node);
                nodes.Remove(node); //Node will be later deleted if unused, by GC
            }
        }

        /// <summary>
        /// Works as a simple executor registration analogue. These functions will be called with each Tick()
        /// Actions need to take care of correct call resolution by checking in their body (TODO)
        /// Make sure actions are lightweight (TODO - separate out threads for spinning and executables?)
        /// </summary>
        public void RegisterExecutable(Action executable)
        {
            lock (mutex)
            {
                executableActions.Add(executable);
            }
        }

        public void UnregisterExecutable(Action executable)
        {
            lock (mutex)
            {
                executableActions.Remove(executable);
            }
        }

        /// <summary>
        /// "Executor" thread will tick all clocks and spin the node
        /// </summary>
        private void Tick()
        {
            while (!quitting)
            {
                if (Ok())
                {
                    lock (mutex)
                    {
                        foreach (Action action in executableActions)
                        {
                            action();
                        }
                        Ros2cs.SpinOnce(ros2csNodes, spinTimeout);
                    }
                }
                Thread.Sleep(interval);
            }
        }

        public void DestroyNow()
        {
            quitting = true;
            ros2forUnity.DestroyROS2ForUnity();
        }
    }

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs.meta
================================================
fileFormatVersion: 2
guid: d80a8dd00d331ce458b98a7707d03bb3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/Sensor.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;
using UnityEngine.Profiling;
using System;

namespace ROS2
{

/// <summary>
/// An abstract base class for ROS2-enabled sensor.
/// </summary>
public abstract class ISensor : MonoBehaviour
{
    /// <summary>
    /// The desired update frequency for the sensor. The maximum can be the rate with which FixedUpdate is called,
    /// which depends on the physics step (usually 50 or 100 times per second).
    /// </summary>
    public double desiredUpdateFreq = 25.0;

    /// <summary>
    /// The frameID corresponds to the ROS frame_id element of the header and is important
    /// for transformations
    /// </summary>
    public string frameID = "sensor";

    /// <summary>
    /// A topic to which the sensor publishes. Only one per sensor. Don't add the namespace of
    /// the agent name, it is handled externally (i.e. sensor does not know to what object it belongs).
    /// </summary>
    public string topicName = "";

    /// <summary>
    /// Controls whether sensor is publishing messages
    /// </summary>
    public bool publishing = false;

    /// <summary>
    /// Creates sensor publishers and registers it in the executor so that it publishes when new data is available
    /// </summary>
    /// <param name="ros2Unity"> Central ros2 monobehavior for Unity </param>
    /// <param name="node"> ros2 node that will publish sensor data </param>
    /// <param name="agentName"> name of the agent (vehicle) to be added to the sensor publish namespace </param>
    public abstract void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName);

    /// <summary>
    /// Returns the constructed frame name, taking in account the agent name(space)
    /// </summary>
    public abstract string frameName();
}

/// <summary>
/// A base template class for the sensor. The type is the message type of sensor data.
/// </summary>
public abstract class Sensor<T> : ISensor where T : MessageWithHeader, new()
{
    /// <summary>
    /// Acquires the value by performing sensor type characteristic computations (e.g. raycasts).
    /// Implemented in subclasses.
    /// </summary>
    /// <returns>The message which contains the sensor data.
    /// Mind that the header for message is handled in a generic way by this class.</returns>
    protected abstract T AcquireValue();

    /// <summary>
    /// Returns true when there is a new data available from sensor.
    /// </summary>
    protected abstract bool HasNewData();

    protected double desiredFrameTime = 0.0;
    private const double minimumFrequency = 0.001;
    private Publisher<T> publisher;
    private Subscription<rosgraph_msgs.msg.Clock> clockSubscriber;
    private ROS2UnityComponent ros2UnityComponent;
    private ROS2Node ros2Node;
    private string ownerAgentName;
    private double lastTimestamp;
    private double timeSinceLastFixedUpdate;

    private T readings;
    private bool newReadings;

    public override string frameName()
    {
        return ownerAgentName + "/" + frameID;
    }

    /// <summary>
    /// Visualises the effects of the sensor. It doesn't make sense for some sensor and the
    /// default implementation is empty.
    /// </summary>
    protected virtual void VisualiseEffects()
    {
    }

    /// <summary>
    /// When parameters in editor change (i.e. frequency),
    /// this function is called to calculate new frame time.
    /// </summary>
    protected virtual void OnValidate()
    {
        CalculateFrameTime();
    }

    /// <summary>
    /// An entry point for the per-frame processing done in subclass
    /// </summary>
    protected virtual void OnUpdate() {}

    /// <summary>
    /// See superclass definition
    /// </summary>
    public override void CreateROSParticipants(ROS2UnityComponent ros2Unity, ROS2Node node, string agentName)
    {
        if (!ros2Unity.Ok())
        {
            throw new System.InvalidOperationException("Publisher for sensor can't be created when node is not OK");
        }

        if (String.IsNullOrEmpty(topicName))
        {
            throw new System.InvalidOperationException("Topic name not set for the sensor " + this);
        }

        ownerAgentName = agentName;
        ros2UnityComponent = ros2Unity;
        ros2Node = node;
        string nsName = agentName.Replace(" ", "_");
        publisher = node.CreateSensorPublisher<T>(nsName + "/" + topicName);
        ros2UnityComponent.RegisterExecutable(ExecutorThreadSensorPublishAction);
        publishing = true;
    }

    /// <summary>
    /// This is executed in an executor thread (through RegisterExecutable)
    /// Sensor fequency is indirectly handed through newReadings, which are acquired at a requested
    /// frequency if possible (e. g. due to simulation resource constraints)
    /// </summary>
    internal void ExecutorThreadSensorPublishAction()
    {
        if (!HasNewData())
            return;

        if (publisher != null & publishing)
        {
            if (ros2UnityComponent.Ok())
            {
                readings = AcquireValue();
                readings.SetHeaderFrame(frameName());
                if (readings != null)
                {
                    MessageWithHeader readingsHeader = readings as MessageWithHeader;
                    ros2Node.clock.UpdateROSTimestamp(ref readingsHeader);
                    publisher.Publish(readings);
                }
            }
        }
    }

    /// <summary>
    /// Once each frame, visualise effects of the sensor (if any). Visualisation
    /// rate is independent of publishing/acquisition rate, which happen at the sensor
    /// frequency instead of the app frame rate.
    /// </summary>
    void Update()
    {
        VisualiseEffects();
        OnUpdate();
    }

    /// <summary>
    /// Initialize header and calculate frame time
    /// </summary>
    void Awake()
    {
        // turn on publishing on start
        publishing = true;
        CalculateFrameTime();
        lastTimestamp = DateTime.UtcNow.Ticks / 1E7;
    }

    /// <summary>
    /// Sensor frequency is used to calculate frame time, based on desired frequency and the bounds.
    /// </summary>
    void CalculateFrameTime()
    {
        double maxFrameFreq = 1.0 / Time.fixedDeltaTime;
        if (desiredUpdateFreq > maxFrameFreq)
        {
            Debug.LogWarning("Desired frame rate of " + desiredUpdateFreq + " can't be met, "
                            + "physics frequency is " + maxFrameFreq);
            desiredUpdateFreq = maxFrameFreq;  //Can't go faster than physics
        }
        if (desiredUpdateFreq < minimumFrequency)
        {
            Debug.LogWarning("Minimum frequency of " + minimumFrequency
                             + " applied instead of " + desiredUpdateFreq);
            desiredUpdateFreq = minimumFrequency;
        }
        desiredFrameTime = 1.0 / desiredUpdateFreq;
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Sensor.cs.meta
================================================
fileFormatVersion: 2
guid: d3ebd1e57dae48cca9c88c51f18cf510
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Diagnostics;

namespace ROS2
{

/// <summary>
/// DateTime based clock that has resolution increased using Stopwatch.
/// DateTime is used to synchronize since Stopwatch tends to drift.
/// </summary>
public class DotnetTimeSource : ITimeSource
{
    private readonly double maxUnsyncedSeconds = 10;

    private Stopwatch stopwatch = new Stopwatch();

    private readonly object mutex = new object();

    private double systemTimeIntervalStart = 0;

    private double stopwatchStartTimeStamp;

    private double TotalSystemTimeSeconds()
    {
        return TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds;
    }

    private void UpdateSystemTime()
    {
        systemTimeIntervalStart = TotalSystemTimeSeconds();
        stopwatchStartTimeStamp = Stopwatch.GetTimestamp();
    }

    public DotnetTimeSource()
    {
        UpdateSystemTime();
    }

    public void GetTime(out int seconds, out uint nanoseconds)
    {
        lock(mutex) // Threading
        {
            double endTimestamp = Stopwatch.GetTimestamp();
            var durationInSeconds = endTimestamp - stopwatchStartTimeStamp;
            double timeOffset = 0;
            if (durationInSeconds >= maxUnsyncedSeconds)
            {   // acquire DateTime to sync
                UpdateSystemTime();
            }
            else
            {   // use Stopwatch offset
                timeOffset = durationInSeconds;
            }
            
            TimeUtils.TimeFromTotalSeconds(systemTimeIntervalStart + timeOffset, out seconds, out nanoseconds);
        }
    }
}

}  // namespace ROS2



================================================
FILE: src/Ros2ForUnity/Scripts/Time/ITimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace ROS2
{

/// <summary>
/// Interace for acquiring time
/// </summary>
public interface ITimeSource
{
  public void GetTime(out int seconds, out uint nanoseconds);
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs
================================================
// Copyright 2019-2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using UnityEngine;

namespace ROS2
{

/// <summary>
/// A ros2 clock class that for interfacing between a time source (unity or ros2 system time) and ros2cs messages, structs. 
/// </summary>
public class ROS2Clock
{
    private ITimeSource _timeSource;

    public ROS2Clock() : this(new ROS2TimeSource())
    {   // By default, use ROS2TimeSource
    }

    public ROS2Clock(ITimeSource ts)
    {
        _timeSource = ts;
    }

    public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)
    {
        int seconds;
        uint nanoseconds;
        _timeSource.GetTime(out seconds, out nanoseconds);
        clockMessage.Clock_.Sec = seconds;
        clockMessage.Clock_.Nanosec = nanoseconds;
    }

    public void UpdateROSClockTime(builtin_interfaces.msg.Time time)
    {
        int seconds;
        uint nanoseconds;
        _timeSource.GetTime(out seconds, out nanoseconds);
        time.Sec = seconds;
        time.Nanosec = nanoseconds;
    }

    public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)
    {
        int seconds;
        uint nanoseconds;
        _timeSource.GetTime(out seconds, out nanoseconds);
        message.UpdateHeaderTime(seconds, nanoseconds);
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs.meta
================================================
fileFormatVersion: 2
guid: 69e097a4a027d5a55b991c0b0b1bdf88
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Threading;
using UnityEngine;

namespace ROS2
{

/// <summary>
/// ros2 time source (system time by default).
/// </summary>
public class ROS2ScalableTimeSource : ITimeSource
{
  private Thread mainThread;
  private double lastReadingSecs;
  private ROS2.Clock clock;
  private double initialTime = 0;
  private double initialTimeScale = 0;
  private bool initialTimeAcquired = false;
  private bool initialTimeScaleAcquired = false;
  private bool timeScaleChanged = false;

  public ROS2ScalableTimeSource()
  {
    mainThread = Thread.CurrentThread;
  }

  public void GetTime(out int seconds, out uint nanoseconds)
  {
    if (!ROS2.Ros2cs.Ok())
    {
      seconds = 0;
      nanoseconds = 0;
      Debug.LogWarning("Cannot acquire valid ros time, ros either not initialized or shut down already");
      return;
    }

    if (clock == null)
    { // Create clock which uses system time by default (unless use_sim_time is set in ros2)
      clock = new ROS2.Clock();
    }

    if (!initialTimeScaleAcquired)
    {
      initialTimeScaleAcquired = true;
      initialTimeScale = Time.timeScale;
    }

    if (initialTimeScale != Time.timeScale)
    {
      timeScaleChanged = true;
    }

    lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;

    if (initialTimeScale == 1.0 && !timeScaleChanged)
    {
      TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);
    }
    else
    {
      if (!initialTimeAcquired)
      {
        initialTimeAcquired = true;
        initialTime = clock.Now.Seconds - Time.timeAsDouble;
      }
      TimeUtils.TimeFromTotalSeconds(lastReadingSecs + initialTime, out seconds, out nanoseconds);
    }
  }

  ~ROS2ScalableTimeSource()
  {
    if (clock != null)
    {
      clock.Dispose();
    }
  }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;

namespace ROS2
{

/// <summary>
/// ros2 time source (system time by default).
/// </summary>
public class ROS2TimeSource : ITimeSource
{
  private ROS2.Clock clock;

  public void GetTime(out int seconds, out uint nanoseconds)
  {
    if (!ROS2.Ros2cs.Ok())
    {
      seconds = 0;
      nanoseconds = 0;
      Debug.LogWarning("Cannot acquire valid ros time, ros either not initialized or shut down already");
      return;
    }

    if (clock == null)
    { // Create clock which uses system time by default (unless use_sim_time is set in ros2)
      clock = new ROS2.Clock();
    }
  
    TimeUtils.TimeFromTotalSeconds(clock.Now.Seconds, out seconds, out nanoseconds);
  }

  ~ROS2TimeSource()
  {
    if (clock != null)
    {
      clock.Dispose();
    }
  }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Time/TimeUtils.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace ROS2
{

/// <summary>
/// Interace for acquiring time
/// </summary>
internal static class TimeUtils
{
  public static void TimeFromTotalSeconds(in double secondsIn, out int seconds, out uint nanoseconds)
  {
    long nanosec = (long)(secondsIn * 1e9);
    seconds = (int)(nanosec / 1000000000);
    nanoseconds = (uint)(nanosec % 1000000000);
  }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs
================================================
// Copyright 2022 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Threading;
using UnityEngine;

namespace ROS2
{

/// <summary>
/// Acquires Unity time. Note that Time API only allows main thread access,
/// but this class object also stores last acquired value for other threads.
/// This is done without a warning, so the class will not behave as expected
/// when not used by main thread.
/// </summary>
public class UnityTimeSource : ITimeSource
{
  private Thread mainThread;
  private double lastReadingSecs;

  public UnityTimeSource()
  {
    mainThread = Thread.CurrentThread;
  }

  public void GetTime(out int seconds, out uint nanoseconds)
  {
    lastReadingSecs = mainThread.Equals(Thread.CurrentThread) ? Time.timeAsDouble : lastReadingSecs;
    TimeUtils.TimeFromTotalSeconds(lastReadingSecs, out seconds, out nanoseconds);
  }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Transformations.cs
================================================
// Copyright 2019-2021 Robotec.ai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using UnityEngine;

namespace ROS2
{
/// <summary>
/// A set of transformation functions between coordinate systems of Unity and ROS
/// </summary>
public static class Transformations
{
    public static Vector3 Ros2Unity(this Vector3 vector3)
    {
        return new Vector3(-vector3.y, vector3.z, vector3.x);
    }

    public static Vector3 Unity2Ros(this Vector3 vector3)
    {
        return new Vector3(vector3.z, -vector3.x, vector3.y);
    }

    public static Vector3 Ros2UnityScale(this Vector3 vector3)
    {
        return new Vector3(vector3.y, vector3.z, vector3.x);
    }

    public static Vector3 Unity2RosScale(this Vector3 vector3)
    {
        return new Vector3(vector3.z, vector3.x, vector3.y);
    }

    public static Quaternion Ros2Unity(this Quaternion quaternion)
    {
        return new Quaternion(quaternion.y, -quaternion.z, -quaternion.x, quaternion.w);
    }

    public static Quaternion Unity2Ros(this Quaternion quaternion)
    {
        return new Quaternion(-quaternion.z, quaternion.x, -quaternion.y, quaternion.w);
    }

    public static void Unity2Ros(ref Quaternion quaternion)
    {
        var z = quaternion.z;
        var x = quaternion.x;
        var y = quaternion.y;
        quaternion.x = -z;
        quaternion.y = x;
        quaternion.z = -y;
    }

    public static void Unity2Ros(ref Vector3 vector)
    {
        var z = vector.z;
        var x = vector.x;
        var y = vector.y;
        vector.x = z;
        vector.y = -x;
        vector.z = y;
    }

    public static Matrix4x4 Unity2RosMatrix4x4()
    {
        // Note: The matrix here is written as-if on paper,
        // but Unity's Matrix4x4 is constructed from column-vectors, hence the transpose.
        return new Matrix4x4(
            new Vector4( 0.0f, 0.0f, 1.0f, 0.0f),
            new Vector4(-1.0f, 0.0f, 0.0f, 0.0f),
            new Vector4( 0.0f, 1.0f, 0.0f, 0.0f),
            new Vector4( 0.0f, 0.0f, 0.0f, 1.0f)
        ).transpose;
    }
}

}  // namespace ROS2


================================================
FILE: src/Ros2ForUnity/Scripts/Transformations.cs.meta
================================================
fileFormatVersion: 2
guid: 5ab7c5f5cc85e9e3aa6134862d9c1cba
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/Ros2ForUnity/Scripts.meta
================================================
fileFormatVersion: 2
guid: f750980d49c8bcf39830e89365689d16
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData: 
  assetBundleName: 
  assetBundleVariant: 


================================================
FILE: src/scripts/metadata_generator.py
================================================
# Copyright 2019-2022 Robotec.ai.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import xml.etree.ElementTree as ET
from xml.dom import minidom
import subprocess
import pathlib
import os

parser = argparse.ArgumentParser(description='Generate metadata file for ros2-for-unity.')
parser.add_argument('--standalone', action='store_true', help='is a standalone build')
args = parser.parse_args()

def get_git_commit(working_directory) -> str:
    return subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=working_directory).decode('ascii').strip()

def get_git_description(working_directory) -> str:
    return subprocess.check_output(['git', 'describe', '--tags', '--always'], cwd=working_directory).decode('ascii').strip()

def get_commit_date(working_directory) -> str:
    return subprocess.check_output(['git', 'show', '-s', '--format=%ci'], cwd=working_directory).decode('ascii').strip()

def get_git_abbrev(working_directory) -> str:
    return subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=working_directory).decode('ascii').strip()

def get_ros2_for_unity_root_path() -> pathlib.Path:
    return pathlib.Path(__file__).parents[2]

def get_ros2_for_unity_path() -> pathlib.Path:
    return pathlib.Path(__file__).parents[1].joinpath("Ros2ForUnity")

def get_ros2cs_path() -> pathlib.Path:
    return pathlib.Path(__file__).parents[1].joinpath("ros2cs")

def get_ros2_path() -> pathlib.Path:
    return get_ros2cs_path().joinpath("src").joinpath("ros2").joinpath("rcl_interfaces")

def get_ros2_version() -> str:
    return os.environ.get("ROS_DISTRO", "unknown")

ros2_for_unity = ET.Element("ros2_for_unity")
ET.SubElement(ros2_for_unity, "ros2").text = get_ros2_version()
ros2_for_unity_version = ET.SubElement(ros2_for_unity, "version")
ET.SubElement(ros2_for_unity_version, "sha").text = get_git_commit(get_ros2_for_unity_root_path())
ET.SubElement(ros2_for_unity_version, "desc").text = get_git_description(get_ros2_for_unity_root_path())
ET.SubElement(ros2_for_unity_version, "date").text = get_commit_date(get_ros2_for_unity_root_path())

ros2_cs = ET.Element("ros2cs")
ET.SubElement(ros2_cs, "ros2").text = get_ros2_version()
ros2_cs_version = ET.SubElement(ros2_cs, "version")
ET.SubElement(ros2_cs_version, "sha").text = get_git_commit(get_ros2cs_path())
ET.SubElement(ros2_cs_version, "desc").text = get_git_description(get_ros2cs_path())
ET.SubElement(ros2_cs_version, "date").text = get_commit_date(get_ros2cs_path())
ET.SubElement(ros2_cs, "standalone").text = str(int(args.standalone))

rf2u_xmlstr = minidom.parseString(ET.tostring(ros2_for_unity)).toprettyxml(indent="   ")
metadata_rf2u_file = get_ros2_for_unity_path().joinpath("metadata_ros2_for_unity.xml")
with open(str(metadata_rf2u_file), "w") as f:
    f.write(rf2u_xmlstr)
    
r2cs_xmlstr = minidom.parseString(ET.tostring(ros2_cs)).toprettyxml(indent="   ")
metadata_r2cs_file = get_ros2_for_unity_path().joinpath("metadata_ros2cs.xml")
with open(str(metadata_r2cs_file), "w") as f:
    f.write(r2cs_xmlstr)
Download .txt
gitextract_58lbld25/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── custom.md
│       └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.AL2
├── README-UBUNTU.md
├── README-WINDOWS.md
├── README.md
├── build.ps1
├── build.sh
├── create_unity_package.ps1
├── create_unity_package.sh
├── deploy_unity_plugins.ps1
├── deploy_unity_plugins.sh
├── docker/
│   ├── Dockerfile
│   ├── README.md
│   ├── build_image.sh
│   ├── custom_messages/
│   │   └── INSERT_CUSTOM_MESSAGES_HERE
│   ├── entrypoint.sh
│   └── run_container.sh
├── pull_repositories.ps1
├── pull_repositories.sh
├── ros2_for_unity_custom_messages.repos
├── ros2cs.repos
└── src/
    ├── Ros2ForUnity/
    │   ├── COLCON_IGNORE
    │   ├── Plugins.meta
    │   ├── Scripts/
    │   │   ├── PostInstall.cs
    │   │   ├── ROS2ClientExample.cs
    │   │   ├── ROS2ForUnity.cs
    │   │   ├── ROS2ForUnity.cs.meta
    │   │   ├── ROS2ListenerExample.cs
    │   │   ├── ROS2ListenerExample.cs.meta
    │   │   ├── ROS2Node.cs
    │   │   ├── ROS2Node.cs.meta
    │   │   ├── ROS2PerformanceTest.cs
    │   │   ├── ROS2PerformanceTest.cs.meta
    │   │   ├── ROS2ServiceExample.cs
    │   │   ├── ROS2TalkerExample.cs
    │   │   ├── ROS2TalkerExample.cs.meta
    │   │   ├── ROS2UnityComponent.cs
    │   │   ├── ROS2UnityComponent.cs.meta
    │   │   ├── ROS2UnityCore.cs
    │   │   ├── ROS2UnityCore.cs.meta
    │   │   ├── Sensor.cs
    │   │   ├── Sensor.cs.meta
    │   │   ├── Time/
    │   │   │   ├── DotnetTimeSource.cs
    │   │   │   ├── ITimeSource.cs
    │   │   │   ├── ROS2Clock.cs
    │   │   │   ├── ROS2Clock.cs.meta
    │   │   │   ├── ROS2ScalableTimeSource.cs
    │   │   │   ├── ROS2TimeSource.cs
    │   │   │   ├── TimeUtils.cs
    │   │   │   └── UnityTimeSource.cs
    │   │   ├── Transformations.cs
    │   │   └── Transformations.cs.meta
    │   └── Scripts.meta
    └── scripts/
        └── metadata_generator.py
Download .txt
SYMBOL INDEX (136 symbols across 20 files)

FILE: src/Ros2ForUnity/Scripts/PostInstall.cs
  class PostInstall (line 32) | internal class PostInstall : IPostprocessBuildWithReport
    method OnPostprocessBuild (line 35) | public void OnPostprocessBuild(BuildReport report)

FILE: src/Ros2ForUnity/Scripts/ROS2ClientExample.cs
  class ROS2ClientExample (line 27) | public class ROS2ClientExample : MonoBehaviour
    method periodicAsyncCall (line 35) | IEnumerator periodicAsyncCall()
    method periodicCall (line 56) | IEnumerator periodicCall()
    method Start (line 77) | void Start()
    method Update (line 91) | void Update()

FILE: src/Ros2ForUnity/Scripts/ROS2ForUnity.cs
  class ROS2ForUnity (line 28) | internal class ROS2ForUnity
    type Platform (line 35) | public enum Platform
    method GetOS (line 41) | public static Platform GetOS()
    method InEditor (line 54) | private static bool InEditor() {
    method GetOSName (line 58) | private static string GetOSName()
    method GetEnvPathVariableName (line 71) | private string GetEnvPathVariableName()
    method GetEnvPathVariableValue (line 81) | private string GetEnvPathVariableValue()
    method GetRos2ForUnityPath (line 86) | public static string GetRos2ForUnityPath()
    method GetPluginPath (line 98) | public static string GetPluginPath()
    method SetEnvPathVariable (line 132) | private void SetEnvPathVariable()
    method IsStandalone (line 146) | public bool IsStandalone() {
    method GetROSVersion (line 150) | public string GetROSVersion()
    method CheckIntegrity (line 167) | public void CheckIntegrity()
    method GetROSVersionSourced (line 195) | public string GetROSVersionSourced()
    method CheckROSSupport (line 204) | private void CheckROSSupport(string ros2Codename)
    method RegisterCtrlCHandler (line 239) | private void RegisterCtrlCHandler()
    method ConnectLoggers (line 250) | private void ConnectLoggers()
    method GetMetadataValue (line 259) | private string GetMetadataValue(XmlDocument doc, string valuePath)
    method LoadMetadata (line 264) | private void LoadMetadata()
    method ROS2ForUnity (line 285) | internal ROS2ForUnity()
    method ThrowIfUninitialized (line 325) | private static void ThrowIfUninitialized(string callContext)
    method Ok (line 337) | public bool Ok()
    method DestroyROS2ForUnity (line 346) | internal void DestroyROS2ForUnity()
    method EditorPlayStateChanged (line 362) | void EditorPlayStateChanged(PlayModeStateChange change)

FILE: src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs
  class ROS2ListenerExample (line 24) | public class ROS2ListenerExample : MonoBehaviour
    method Start (line 30) | void Start()
    method Update (line 35) | void Update()

FILE: src/Ros2ForUnity/Scripts/ROS2Node.cs
  class ROS2Node (line 28) | public class ROS2Node
    method ROS2Node (line 35) | internal ROS2Node(string unityROS2NodeName = "unity_ros2_node")
    method ThrowIfUninitialized (line 47) | private static void ThrowIfUninitialized(string callContext)
    method CreateSensorPublisher (line 60) | public Publisher<T> CreateSensorPublisher<T>(string topicName) where T...
    method CreatePublisher (line 72) | public Publisher<T> CreatePublisher<T>(string topicName, QualityOfServ...
    method CreateSubscription (line 84) | public Subscription<T> CreateSubscription<T>(string topicName, Action<...
    method RemoveSubscription (line 101) | public bool RemoveSubscription<T>(ISubscriptionBase subscription)
    method RemovePublisher (line 112) | public bool RemovePublisher<T>(IPublisherBase publisher)
    method CreateService (line 119) | public Service<I, O> CreateService<I, O>(string topic, Func<I, O> call...
    method RemoveService (line 128) | public bool RemoveService(IServiceBase service)
    method CreateClient (line 135) | public Client<I, O> CreateClient<I, O>(string topic, QualityOfServiceP...
    method RemoveClient (line 144) | public bool RemoveClient(IClientBase client)

FILE: src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs
  class ROS2PerformanceTest (line 24) | public class ROS2PerformanceTest : MonoBehaviour
    method Start (line 35) | void Start()
    method OnValidate (line 41) | void OnValidate()
    method Publish (line 54) | private void Publish()
    method FixedUpdate (line 77) | void FixedUpdate()
    method AssignField (line 87) | private void AssignField(ref sensor_msgs.msg.PointField pf, string n, ...
    method PrepMessage (line 95) | private void PrepMessage()

FILE: src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs
  class ROS2ServiceExample (line 26) | public class ROS2ServiceExample : MonoBehaviour
    method Start (line 32) | void Start()
    method addTwoInts (line 46) | public example_interfaces.srv.AddTwoInts_Response addTwoInts( example_...

FILE: src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs
  class ROS2TalkerExample (line 23) | public class ROS2TalkerExample : MonoBehaviour
    method Start (line 31) | void Start()
    method Update (line 36) | void Update()

FILE: src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs
  class ROS2UnityComponent (line 32) | public class ROS2UnityComponent : MonoBehaviour
    method Ok (line 44) | public bool Ok()
    method LazyConstruct (line 54) | private void LazyConstruct()
    method Start (line 68) | void Start()
    method CreateNode (line 73) | public ROS2Node CreateNode(string name)
    method RemoveNode (line 93) | public void RemoveNode(ROS2Node node)
    method RegisterExecutable (line 107) | public void RegisterExecutable(Action executable)
    method UnregisterExecutable (line 117) | public void UnregisterExecutable(Action executable)
    method Tick (line 128) | private void Tick()
    method FixedUpdate (line 147) | void FixedUpdate()
    method OnApplicationQuit (line 157) | void OnApplicationQuit()

FILE: src/Ros2ForUnity/Scripts/ROS2UnityCore.cs
  class ROS2UnityCore (line 32) | public class ROS2UnityCore
    method Ok (line 43) | public bool Ok()
    method ROS2UnityCore (line 51) | public ROS2UnityCore()
    method CreateNode (line 65) | public ROS2Node CreateNode(string name)
    method RemoveNode (line 83) | public void RemoveNode(ROS2Node node)
    method RegisterExecutable (line 97) | public void RegisterExecutable(Action executable)
    method UnregisterExecutable (line 105) | public void UnregisterExecutable(Action executable)
    method Tick (line 116) | private void Tick()
    method DestroyNow (line 135) | public void DestroyNow()

FILE: src/Ros2ForUnity/Scripts/Sensor.cs
  class ISensor (line 25) | public abstract class ISensor : MonoBehaviour
    method CreateROSParticipants (line 56) | public abstract void CreateROSParticipants(ROS2UnityComponent ros2Unit...
    method frameName (line 61) | public abstract string frameName();
  class Sensor (line 67) | public abstract class Sensor<T> : ISensor where T : MessageWithHeader, n...
    method AcquireValue (line 75) | protected abstract T AcquireValue();
    method HasNewData (line 80) | protected abstract bool HasNewData();
    method frameName (line 95) | public override string frameName()
    method VisualiseEffects (line 104) | protected virtual void VisualiseEffects()
    method OnValidate (line 112) | protected virtual void OnValidate()
    method OnUpdate (line 120) | protected virtual void OnUpdate() {}
    method CreateROSParticipants (line 125) | public override void CreateROSParticipants(ROS2UnityComponent ros2Unit...
    method ExecutorThreadSensorPublishAction (line 151) | internal void ExecutorThreadSensorPublishAction()
    method Update (line 177) | void Update()
    method Awake (line 186) | void Awake()
    method CalculateFrameTime (line 197) | void CalculateFrameTime()

FILE: src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs
  class DotnetTimeSource (line 25) | public class DotnetTimeSource : ITimeSource
    method TotalSystemTimeSeconds (line 37) | private double TotalSystemTimeSeconds()
    method UpdateSystemTime (line 42) | private void UpdateSystemTime()
    method DotnetTimeSource (line 48) | public DotnetTimeSource()
    method GetTime (line 53) | public void GetTime(out int seconds, out uint nanoseconds)

FILE: src/Ros2ForUnity/Scripts/Time/ITimeSource.cs
  type ITimeSource (line 21) | public interface ITimeSource
    method GetTime (line 23) | public void GetTime(out int seconds, out uint nanoseconds);

FILE: src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs
  class ROS2Clock (line 24) | public class ROS2Clock
    method ROS2Clock (line 28) | public ROS2Clock() : this(new ROS2TimeSource())
    method ROS2Clock (line 32) | public ROS2Clock(ITimeSource ts)
    method UpdateClockMessage (line 37) | public void UpdateClockMessage(ref rosgraph_msgs.msg.Clock clockMessage)
    method UpdateROSClockTime (line 46) | public void UpdateROSClockTime(builtin_interfaces.msg.Time time)
    method UpdateROSTimestamp (line 55) | public void UpdateROSTimestamp(ref ROS2.MessageWithHeader message)

FILE: src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs
  class ROS2ScalableTimeSource (line 24) | public class ROS2ScalableTimeSource : ITimeSource
    method ROS2ScalableTimeSource (line 35) | public ROS2ScalableTimeSource()
    method GetTime (line 40) | public void GetTime(out int seconds, out uint nanoseconds)

FILE: src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs
  class ROS2TimeSource (line 23) | public class ROS2TimeSource : ITimeSource
    method GetTime (line 27) | public void GetTime(out int seconds, out uint nanoseconds)

FILE: src/Ros2ForUnity/Scripts/Time/TimeUtils.cs
  class TimeUtils (line 21) | internal static class TimeUtils
    method TimeFromTotalSeconds (line 23) | public static void TimeFromTotalSeconds(in double secondsIn, out int s...

FILE: src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs
  class UnityTimeSource (line 28) | public class UnityTimeSource : ITimeSource
    method UnityTimeSource (line 33) | public UnityTimeSource()
    method GetTime (line 38) | public void GetTime(out int seconds, out uint nanoseconds)

FILE: src/Ros2ForUnity/Scripts/Transformations.cs
  class Transformations (line 22) | public static class Transformations
    method Ros2Unity (line 24) | public static Vector3 Ros2Unity(this Vector3 vector3)
    method Unity2Ros (line 29) | public static Vector3 Unity2Ros(this Vector3 vector3)
    method Ros2UnityScale (line 34) | public static Vector3 Ros2UnityScale(this Vector3 vector3)
    method Unity2RosScale (line 39) | public static Vector3 Unity2RosScale(this Vector3 vector3)
    method Ros2Unity (line 44) | public static Quaternion Ros2Unity(this Quaternion quaternion)
    method Unity2Ros (line 49) | public static Quaternion Unity2Ros(this Quaternion quaternion)
    method Unity2Ros (line 54) | public static void Unity2Ros(ref Quaternion quaternion)
    method Unity2Ros (line 64) | public static void Unity2Ros(ref Vector3 vector)
    method Unity2RosMatrix4x4 (line 74) | public static Matrix4x4 Unity2RosMatrix4x4()

FILE: src/scripts/metadata_generator.py
  function get_git_commit (line 26) | def get_git_commit(working_directory) -> str:
  function get_git_description (line 29) | def get_git_description(working_directory) -> str:
  function get_commit_date (line 32) | def get_commit_date(working_directory) -> str:
  function get_git_abbrev (line 35) | def get_git_abbrev(working_directory) -> str:
  function get_ros2_for_unity_root_path (line 38) | def get_ros2_for_unity_root_path() -> pathlib.Path:
  function get_ros2_for_unity_path (line 41) | def get_ros2_for_unity_path() -> pathlib.Path:
  function get_ros2cs_path (line 44) | def get_ros2cs_path() -> pathlib.Path:
  function get_ros2_path (line 47) | def get_ros2_path() -> pathlib.Path:
  function get_ros2_version (line 50) | def get_ros2_version() -> str:
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (134K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 939,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "chars": 126,
    "preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".gitignore",
    "chars": 112,
    "preview": "install\nlog\nbuild\n.idea\nsrc/ros2cs\n**/metadata*.xml\nsrc/Ros2ForUnity/Plugins\n!src/Ros2ForUnity/Plugins/.gitkeep\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5219,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 639,
    "preview": "Any contribution that you make to this repository will\nbe under the Apache 2 License, as dictated by that\n[license](http"
  },
  {
    "path": "LICENSE.AL2",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README-UBUNTU.md",
    "chars": 3688,
    "preview": "# ROS2 For Unity - Ubuntu 20.04 and 22.04\n\nThis readme contains information specific to Ubuntu 20.04/22.04. For general "
  },
  {
    "path": "README-WINDOWS.md",
    "chars": 5147,
    "preview": "# ROS2 For Unity - Windows 10\n\nThis readme contains information specific to Window 10. For general information, please s"
  },
  {
    "path": "README.md",
    "chars": 9110,
    "preview": "\nRos2 For Unity\n===============\n\n> [!NOTE]  \n> This project is officially supported for [AWSIM](https://github.com/tier4"
  },
  {
    "path": "build.ps1",
    "chars": 2085,
    "preview": "\n<#\n.SYNOPSIS\n    Builds Ros2ForUnity asset\n.DESCRIPTION\n    This script builds Ros2DorUnity asset\n.PARAMETER with_tests"
  },
  {
    "path": "build.sh",
    "chars": 2111,
    "preview": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n    echo \"Usage: \"\n    echo \"\"\n    "
  },
  {
    "path": "create_unity_package.ps1",
    "chars": 3119,
    "preview": "\n<#\n.SYNOPSIS\n    Creates a 'unitypackage' from an input asset.\n.DESCRIPTION\n    This script screates a temporary Unity "
  },
  {
    "path": "create_unity_package.sh",
    "chars": 3193,
    "preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\ndisplay_usage() {\n  echo \"This script creates a temp"
  },
  {
    "path": "deploy_unity_plugins.ps1",
    "chars": 1605,
    "preview": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n$pluginDir=$args[0]\n\nfunction Print-Help {\n\"\nUsage: "
  },
  {
    "path": "deploy_unity_plugins.sh",
    "chars": 667,
    "preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ $# -eq 0 ] || [ $1 = \"-h\" ] || [ $1 = \"--help\" "
  },
  {
    "path": "docker/Dockerfile",
    "chars": 774,
    "preview": "ARG ROS2_DISTRO=humble\nFROM ros:${ROS2_DISTRO}-ros-base\n\nRUN apt update && apt install -y ros-${ROS_DISTRO}-test-msgs ro"
  },
  {
    "path": "docker/README.md",
    "chars": 879,
    "preview": "Ros2 For Unity Docker\n===============\n\nCurrently only building asset on Ubuntu is supported. Build windows version is no"
  },
  {
    "path": "docker/build_image.sh",
    "chars": 172,
    "preview": "#!/bin/bash\n\nif [ -z \"$ROS_DISTRO\" ]; then\n    echo \"Source your ros2 distro first.\"\n    exit 1\nfi\n\ndocker build . --bui"
  },
  {
    "path": "docker/custom_messages/INSERT_CUSTOM_MESSAGES_HERE",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docker/entrypoint.sh",
    "chars": 1145,
    "preview": "#!/bin/bash\n\nsource \"/opt/ros/$ROS_DISTRO/setup.bash\"\n\necho \"###########################################################"
  },
  {
    "path": "docker/run_container.sh",
    "chars": 403,
    "preview": "#!/bin/bash\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nmkdir -p $SCRIPTPATH/../install\ndocker run \\\n--rm \\\n-"
  },
  {
    "path": "pull_repositories.ps1",
    "chars": 855,
    "preview": "$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition\n\nif (([string]::IsNullOrEmpty($Env:ROS_DISTRO)))\n{\n "
  },
  {
    "path": "pull_repositories.sh",
    "chars": 617,
    "preview": "#!/bin/bash\n\nSCRIPT=$(readlink -f $0)\nSCRIPTPATH=`dirname $SCRIPT`\n\nif [ -z \"${ROS_DISTRO}\" ]; then\n    echo \"Can't dete"
  },
  {
    "path": "ros2_for_unity_custom_messages.repos",
    "chars": 316,
    "preview": "# NOTE: Use this file if you want to build with custom messages that reside in a separate remote repo.\n# NOTE: use the f"
  },
  {
    "path": "ros2cs.repos",
    "chars": 115,
    "preview": "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",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Ros2ForUnity/Plugins.meta",
    "chars": 172,
    "preview": "fileFormatVersion: 2\nguid: 79b46a636d96b67468a29e597cb7b06a\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  us"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/PostInstall.cs",
    "chars": 2784,
    "preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ClientExample.cs",
    "chars": 3067,
    "preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs",
    "chars": 12629,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ForUnity.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 4cdb4e72fb0aa46c09e52778257ed142\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs",
    "chars": 1418,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ListenerExample.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 75a1bd43b302c4c578a744060319517e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2Node.cs",
    "chars": 5693,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2Node.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 3e21db77b82bbeb8693eabe308d76f45\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs",
    "chars": 4139,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2PerformanceTest.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 387d300b788c9bd29b6e38808a481155\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2ServiceExample.cs",
    "chars": 1888,
    "preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs",
    "chars": 1625,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2TalkerExample.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 72620fb0a9290863f8643557405c48e3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs",
    "chars": 4837,
    "preview": "// Copyright 2019-2021 Robotec.ai.\r\n//\r\n// Licensed under the Apache License, Version 2.0 (the \"License\");\r\n// you may n"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityComponent.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: feab04ad06492965492b3edc6423aa53\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs",
    "chars": 4664,
    "preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/ROS2UnityCore.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: d80a8dd00d331ce458b98a7707d03bb3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Sensor.cs",
    "chars": 7528,
    "preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Sensor.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: d3ebd1e57dae48cca9c88c51f18cf510\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/DotnetTimeSource.cs",
    "chars": 2201,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ITimeSource.cs",
    "chars": 785,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs",
    "chars": 1851,
    "preview": "// Copyright 2019-2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2Clock.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 69e097a4a027d5a55b991c0b0b1bdf88\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2ScalableTimeSource.cs",
    "chars": 2441,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/ROS2TimeSource.cs",
    "chars": 1400,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/TimeUtils.cs",
    "chars": 971,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Time/UnityTimeSource.cs",
    "chars": 1419,
    "preview": "// Copyright 2022 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use t"
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Transformations.cs",
    "chars": 2597,
    "preview": "// Copyright 2019-2021 Robotec.ai.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "src/Ros2ForUnity/Scripts/Transformations.cs.meta",
    "chars": 243,
    "preview": "fileFormatVersion: 2\nguid: 5ab7c5f5cc85e9e3aa6134862d9c1cba\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n "
  },
  {
    "path": "src/Ros2ForUnity/Scripts.meta",
    "chars": 172,
    "preview": "fileFormatVersion: 2\nguid: f750980d49c8bcf39830e89365689d16\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  us"
  },
  {
    "path": "src/scripts/metadata_generator.py",
    "chars": 3542,
    "preview": "# Copyright 2019-2022 Robotec.ai.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use "
  }
]

About this extraction

This page contains the full source code of the RobotecAI/ros2-for-unity GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (122.3 KB), approximately 30.8k tokens, and a symbol index with 136 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!