From 67324e464471509ada41eeef0d9b71ccb03a8dee Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 02:27:44 +0900 Subject: [PATCH 01/10] Adds readme and contribution guide contents. --- CONTRIBUTING.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 14 +++++- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2128c16 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,119 @@ +# Contributing to ssl_ros_bridge + +First off, thanks for taking the time to contribute! ❤️ + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) +- [Reporting Bugs](#reporting-bugs) +- [Suggesting Enhancements](#suggesting-enhancements) +- [Your First Code Contribution](#your-first-code-contribution) +- [Improving The Documentation](#improving-the-documentation) +- [Styleguides](#styleguides) +- [Commit Messages](#commit-messages) +- [Join The Project Team](#join-the-project-team) + + + +## I Have a Question + + + +Before you ask a question, it is best to search for existing [Issues](/issues) or [Discussions](/discussions) that might help you. In case you have found a suitable issue/topic and still need clarification, you can write your question in the existing issue/topic. + +If you don't find what you're looking for there, you can start a [new question topic](/discussions/new?category=q-a). + +If it turns out your question highlights a new bug in our software, we'll create an issue from your Q&A post. + +For quicker / shorter questions, you can also tag "@[A-Team] Matt Barulic" in the [Open Source channel](https://discord.com/channels/465455923415220234/467643497122496543) in the league Discord server. + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +### Reporting Bugs + +#### Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. + +- Make sure that you are using the latest version. +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)). +- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](issues?q=label%3Abug). +- Collect information about the bug: + - Any relevant traces or log output + - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) + - ROS version being used + - Can you reliably reproduce the issue? And can you also reproduce it with older versions? + +#### How Do I Submit a Good Bug Report? + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [Issue](/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Provide the information you collected in the previous section. + +Once it's filed: + +- The project team will label the issue accordingly. +- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. +- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). +- The project team may convert the issue to a discussions topic if it is a question or otherwise does not capture an actionable issue. + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for CONTRIBUTING.md, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest version. +- Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration. +- Perform a [search](/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. +- Feel free to have early discussions about your suggestions via discussions or on Discord as presented in [this section](#i-have-a-question). + +#### How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](/issues). + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. +- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. +- **Explain why this enhancement would be useful** to most ssl_ros_bridge users. You may also want to point out the other projects that solved it better and which could serve as inspiration. + + + +## Styleguides + +This project follows the [ROS 2 style guides](https://docs.ros.org/en/rolling/The-ROS2-Project/Contributing/Code-Style-Language-Versions.html). + +### Commit Messages + +Generally, commit messages should be a phrase starting with the verb capturing what the commit changes. A good rule of thumb is to write a sentence like "This commit fixes/changes/improves/etc..." and use everything after "This commit" as your commit message. + +Tag relevant issues in your commit messages. + +**Good commit message**: "Fixes #1 by reordering network calls." + +**Bad commit message**: "Network calls no longer broken." + +## Attribution + +This guide is based on **contributing.md**. [Make your own](https://contributing.md/)! diff --git a/README.md b/README.md index 8001445..43b28c9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # ssl_ros_bridge -Packages to connect ROS systems to RoboCup Small Size League standard systems + +This repository provides ROS 2 packages containing utilities for connecting your ROS system to the RoboCup Small Size League (SSL) standard systems, such as ssl-vision and the Game Controller. + +## Installation + +This repo is designed to be cloned into a colcon workspace. You can clone this repo alongside your own code or as a submodule within your own repository. + + + +## Contributing + +See [our contributing guidelines](blob/master/CONTRIBUTING.md) From e22aeefc06fb22f5677f47db8c7d715e1608e978 Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 02:27:57 +0900 Subject: [PATCH 02/10] Adds CI workflows. --- .github/workflows/CI.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..0c0057d --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,36 @@ +name: CI +on: + push: + branches: + - main + - comp/** + pull_request: + branches: [ main ] + workflow_dispatch: +jobs: + buildAndTest: + runs-on: ubuntu-24.04 + container: ros:jazzy + steps: + - uses: actions/checkout@v2 + with: + path: src/ateam_software + submodules: true + - name: Install Dependencies + shell: bash + run: | + source /opt/ros/jazzy/setup.bash + sudo apt-get update + rosdep update --rosdistro=jazzy + rosdep install --from-paths . --ignore-src -y + - name: Build + shell: bash + run: | + source /opt/ros/jazzy/setup.bash + colcon build + - name: Test + shell: bash + run: | + source /opt/ros/jazzy/setup.bash + colcon test + colcon test-result --verbose From 6161dd597019005c457ecd09a7adc91755a221f1 Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 02:28:26 +0900 Subject: [PATCH 03/10] Imports packages from software repo. --- ssl_league_msgs/CMakeLists.txt | 86 +++ .../msg/game_controller/ControllerReply.msg | 12 + .../msg/game_controller/GameEvent.msg | 103 +++ .../GameEventProposalGroup.msg | 2 + .../msg/game_controller/Referee.msg | 61 ++ .../msg/game_controller/TeamInfo.msg | 17 + .../msg/game_controller/common/Division.msg | 5 + .../msg/game_controller/common/RobotId.msg | 2 + .../msg/game_controller/common/Team.msg | 5 + .../game_events/AimlessKick.msg | 4 + .../game_events/AttackerDoubleTouchedBall.msg | 3 + .../AttackerTooCloseToDefenseArea.msg | 5 + .../AttackerTouchedBallInDefenseArea.msg | 4 + .../AttackerTouchedOpponentInDefenseArea.msg | 4 + .../game_events/BallLeftField.msg | 3 + .../game_events/BotCrashDrawn.msg | 6 + .../game_events/BotCrashUnique.msg | 7 + .../game_events/BotDribbledBallTooFar.msg | 4 + .../game_events/BotDroppedParts.msg | 4 + .../game_events/BotHeldBallDeliberately.msg | 4 + .../game_events/BotInterferedPlacement.msg | 3 + .../game_events/BotKickedBallTooFast.msg | 5 + .../game_events/BotPushedBot.msg | 5 + .../game_events/BotSubstitution.msg | 1 + .../game_events/BotTippedOver.msg | 4 + .../game_events/BotTooFastInStop.msg | 4 + .../game_events/BoundaryCrossing.msg | 2 + .../game_events/ChallengeFlag.msg | 1 + .../game_events/ChallengeFlagHandled.msg | 2 + .../game_events/ChippedGoal.msg | 5 + .../game_events/DefenderInDefenseArea.msg | 4 + .../DefenderInDefenseAreaPartially.msg | 5 + .../DefenderTooCloseToKickPoint.msg | 4 + .../game_events/EmergencyStop.msg | 1 + .../game_events/ExcessiveBotSubstitution.msg | 1 + .../msg/game_controller/game_events/Goal.msg | 9 + .../game_events/IndirectGoal.msg | 4 + .../game_events/KeeperHeldBall.msg | 3 + .../game_events/KickTimeout.msg | 3 + .../game_events/MultipleCards.msg | 1 + .../game_events/MultipleFouls.msg | 4 + .../game_events/MultiplePlacementFailures.msg | 1 + .../game_events/NoProgressInGame.msg | 2 + .../game_events/PenaltyKickFailed.msg | 3 + .../game_events/PlacementFailed.msg | 3 + .../game_events/PlacementSucceeded.msg | 4 + .../game_controller/game_events/Prepared.msg | 1 + .../game_events/TooManyRobots.msg | 4 + .../game_events/UnsportingBehaviorMajor.msg | 2 + .../game_events/UnsportingBehaviorMinor.msg | 2 + .../msg/simulator/SimulatorControl.msg | 4 + .../msg/simulator/TeleportBallCommand.msg | 19 + .../msg/simulator/TeleportRobotCommand.msg | 20 + .../msg/vision/VisionDetectionBall.msg | 4 + .../msg/vision/VisionDetectionFrame.msg | 8 + .../msg/vision/VisionDetectionRobot.msg | 5 + .../msg/vision/VisionFieldCircularArc.msg | 6 + .../msg/vision/VisionFieldLineSegment.msg | 4 + .../VisionGeometryCameraCalibration.msg | 6 + .../msg/vision/VisionGeometryData.msg | 2 + .../msg/vision/VisionGeometryFieldSize.msg | 15 + ssl_league_msgs/msg/vision/VisionWrapper.msg | 2 + ssl_league_msgs/package.xml | 25 + ssl_league_protobufs/CMakeLists.txt | 72 +++ ssl_league_protobufs/package.xml | 20 + ssl_league_protobufs/proto/ssl_gc_api.proto | 56 ++ .../proto/ssl_gc_change.proto | 200 ++++++ ssl_league_protobufs/proto/ssl_gc_ci.proto | 26 + .../proto/ssl_gc_common.proto | 28 + .../proto/ssl_gc_engine.proto | 150 +++++ .../proto/ssl_gc_engine_config.proto | 55 ++ .../proto/ssl_gc_game_event.proto | 596 ++++++++++++++++++ .../proto/ssl_gc_geometry.proto | 16 + ssl_league_protobufs/proto/ssl_gc_rcon.proto | 38 ++ .../proto/ssl_gc_rcon_autoref.proto | 32 + .../proto/ssl_gc_rcon_remotecontrol.proto | 117 ++++ .../proto/ssl_gc_rcon_team.proto | 56 ++ .../proto/ssl_gc_referee_message.proto | 238 +++++++ ssl_league_protobufs/proto/ssl_gc_state.proto | 133 ++++ .../proto/ssl_simulation_config.proto | 77 +++ .../proto/ssl_simulation_control.proto | 90 +++ .../proto/ssl_simulation_error.proto | 10 + .../proto/ssl_simulation_robot_control.proto | 66 ++ .../proto/ssl_simulation_robot_feedback.proto | 23 + .../proto/ssl_simulation_synchronous.proto | 25 + .../proto/ssl_vision_detection.proto | 57 ++ .../proto/ssl_vision_detection_tracked.proto | 88 +++ .../proto/ssl_vision_geometry.proto | 151 +++++ .../proto/ssl_vision_wrapper.proto | 9 + .../proto/ssl_vision_wrapper_tracked.proto | 14 + ssl_ros_bridge/CMakeLists.txt | 28 + ssl_ros_bridge/LICENSE | 17 + ssl_ros_bridge/package.xml | 27 + ssl_ros_bridge/src/core/CMakeLists.txt | 10 + ssl_ros_bridge/src/core/get_ip_addresses.cpp | 65 ++ ssl_ros_bridge/src/core/get_ip_addresses.hpp | 37 ++ .../src/core/multicast_receiver.cpp | 150 +++++ .../src/core/multicast_receiver.hpp | 74 +++ ssl_ros_bridge/src/core/protobuf_logging.hpp | 64 ++ .../src/game_controller_bridge/CMakeLists.txt | 21 + .../gc_multicast_bridge_node.cpp | 122 ++++ .../message_conversions.cpp | 552 ++++++++++++++++ .../message_conversions.hpp | 111 ++++ ssl_ros_bridge/src/team_client/CMakeLists.txt | 22 + .../src/team_client/team_client.cpp | 269 ++++++++ .../src/team_client/team_client.hpp | 110 ++++ .../src/team_client/team_client_node.cpp | 207 ++++++ .../src/vision_bridge/CMakeLists.txt | 22 + .../src/vision_bridge/message_conversions.cpp | 195 ++++++ .../src/vision_bridge/message_conversions.hpp | 55 ++ .../vision_bridge/ssl_vision_bridge_node.cpp | 77 +++ ssl_ros_bridge_msgs/CMakeLists.txt | 35 + ssl_ros_bridge_msgs/LICENSE | 17 + .../msg/TeamClientConnectionStatus.msg | 2 + ssl_ros_bridge_msgs/package.xml | 29 + .../srv/ReconnectTeamClient.srv | 9 + ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv | 6 + .../srv/SetTeamAdvantageChoice.srv | 10 + ssl_ros_bridge_msgs/srv/SubstituteBot.srv | 5 + 119 files changed, 5345 insertions(+) create mode 100644 ssl_league_msgs/CMakeLists.txt create mode 100644 ssl_league_msgs/msg/game_controller/ControllerReply.msg create mode 100644 ssl_league_msgs/msg/game_controller/GameEvent.msg create mode 100644 ssl_league_msgs/msg/game_controller/GameEventProposalGroup.msg create mode 100644 ssl_league_msgs/msg/game_controller/Referee.msg create mode 100644 ssl_league_msgs/msg/game_controller/TeamInfo.msg create mode 100644 ssl_league_msgs/msg/game_controller/common/Division.msg create mode 100644 ssl_league_msgs/msg/game_controller/common/RobotId.msg create mode 100644 ssl_league_msgs/msg/game_controller/common/Team.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/AimlessKick.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/AttackerDoubleTouchedBall.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/AttackerTooCloseToDefenseArea.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedBallInDefenseArea.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedOpponentInDefenseArea.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BallLeftField.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotCrashDrawn.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotCrashUnique.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotDribbledBallTooFar.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotDroppedParts.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotHeldBallDeliberately.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotInterferedPlacement.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotKickedBallTooFast.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotPushedBot.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotSubstitution.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotTippedOver.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BotTooFastInStop.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/BoundaryCrossing.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/ChallengeFlag.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/ChallengeFlagHandled.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/ChippedGoal.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseArea.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseAreaPartially.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/DefenderTooCloseToKickPoint.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/EmergencyStop.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/ExcessiveBotSubstitution.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/Goal.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/IndirectGoal.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/KeeperHeldBall.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/KickTimeout.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/MultipleCards.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/MultipleFouls.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/MultiplePlacementFailures.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/NoProgressInGame.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/PenaltyKickFailed.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/PlacementFailed.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/PlacementSucceeded.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/Prepared.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/TooManyRobots.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMajor.msg create mode 100644 ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMinor.msg create mode 100644 ssl_league_msgs/msg/simulator/SimulatorControl.msg create mode 100644 ssl_league_msgs/msg/simulator/TeleportBallCommand.msg create mode 100644 ssl_league_msgs/msg/simulator/TeleportRobotCommand.msg create mode 100644 ssl_league_msgs/msg/vision/VisionDetectionBall.msg create mode 100644 ssl_league_msgs/msg/vision/VisionDetectionFrame.msg create mode 100644 ssl_league_msgs/msg/vision/VisionDetectionRobot.msg create mode 100644 ssl_league_msgs/msg/vision/VisionFieldCircularArc.msg create mode 100644 ssl_league_msgs/msg/vision/VisionFieldLineSegment.msg create mode 100644 ssl_league_msgs/msg/vision/VisionGeometryCameraCalibration.msg create mode 100644 ssl_league_msgs/msg/vision/VisionGeometryData.msg create mode 100644 ssl_league_msgs/msg/vision/VisionGeometryFieldSize.msg create mode 100644 ssl_league_msgs/msg/vision/VisionWrapper.msg create mode 100644 ssl_league_msgs/package.xml create mode 100644 ssl_league_protobufs/CMakeLists.txt create mode 100644 ssl_league_protobufs/package.xml create mode 100644 ssl_league_protobufs/proto/ssl_gc_api.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_change.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_ci.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_common.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_engine.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_engine_config.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_game_event.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_geometry.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_rcon.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_rcon_autoref.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_rcon_remotecontrol.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_rcon_team.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_referee_message.proto create mode 100644 ssl_league_protobufs/proto/ssl_gc_state.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_config.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_control.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_error.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_robot_control.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_robot_feedback.proto create mode 100644 ssl_league_protobufs/proto/ssl_simulation_synchronous.proto create mode 100644 ssl_league_protobufs/proto/ssl_vision_detection.proto create mode 100644 ssl_league_protobufs/proto/ssl_vision_detection_tracked.proto create mode 100644 ssl_league_protobufs/proto/ssl_vision_geometry.proto create mode 100644 ssl_league_protobufs/proto/ssl_vision_wrapper.proto create mode 100644 ssl_league_protobufs/proto/ssl_vision_wrapper_tracked.proto create mode 100644 ssl_ros_bridge/CMakeLists.txt create mode 100644 ssl_ros_bridge/LICENSE create mode 100644 ssl_ros_bridge/package.xml create mode 100644 ssl_ros_bridge/src/core/CMakeLists.txt create mode 100644 ssl_ros_bridge/src/core/get_ip_addresses.cpp create mode 100644 ssl_ros_bridge/src/core/get_ip_addresses.hpp create mode 100644 ssl_ros_bridge/src/core/multicast_receiver.cpp create mode 100644 ssl_ros_bridge/src/core/multicast_receiver.hpp create mode 100644 ssl_ros_bridge/src/core/protobuf_logging.hpp create mode 100644 ssl_ros_bridge/src/game_controller_bridge/CMakeLists.txt create mode 100644 ssl_ros_bridge/src/game_controller_bridge/gc_multicast_bridge_node.cpp create mode 100644 ssl_ros_bridge/src/game_controller_bridge/message_conversions.cpp create mode 100644 ssl_ros_bridge/src/game_controller_bridge/message_conversions.hpp create mode 100644 ssl_ros_bridge/src/team_client/CMakeLists.txt create mode 100644 ssl_ros_bridge/src/team_client/team_client.cpp create mode 100644 ssl_ros_bridge/src/team_client/team_client.hpp create mode 100644 ssl_ros_bridge/src/team_client/team_client_node.cpp create mode 100644 ssl_ros_bridge/src/vision_bridge/CMakeLists.txt create mode 100644 ssl_ros_bridge/src/vision_bridge/message_conversions.cpp create mode 100644 ssl_ros_bridge/src/vision_bridge/message_conversions.hpp create mode 100644 ssl_ros_bridge/src/vision_bridge/ssl_vision_bridge_node.cpp create mode 100644 ssl_ros_bridge_msgs/CMakeLists.txt create mode 100644 ssl_ros_bridge_msgs/LICENSE create mode 100644 ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg create mode 100644 ssl_ros_bridge_msgs/package.xml create mode 100644 ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv create mode 100644 ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv create mode 100644 ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv create mode 100644 ssl_ros_bridge_msgs/srv/SubstituteBot.srv diff --git a/ssl_league_msgs/CMakeLists.txt b/ssl_league_msgs/CMakeLists.txt new file mode 100644 index 0000000..d5e0cd2 --- /dev/null +++ b/ssl_league_msgs/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.5) +project(ssl_league_msgs) + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(builtin_interfaces REQUIRED) +find_package(std_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + msg/game_controller/ControllerReply.msg + msg/game_controller/GameEvent.msg + msg/game_controller/GameEventProposalGroup.msg + msg/game_controller/Referee.msg + msg/game_controller/TeamInfo.msg + + msg/game_controller/common/Team.msg + msg/game_controller/common/RobotId.msg + msg/game_controller/common/Division.msg + + msg/game_controller/game_events/DefenderInDefenseArea.msg + msg/game_controller/game_events/TooManyRobots.msg + msg/game_controller/game_events/BotSubstitution.msg + msg/game_controller/game_events/EmergencyStop.msg + msg/game_controller/game_events/UnsportingBehaviorMajor.msg + msg/game_controller/game_events/AttackerTooCloseToDefenseArea.msg + msg/game_controller/game_events/BotInterferedPlacement.msg + msg/game_controller/game_events/KeeperHeldBall.msg + msg/game_controller/game_events/BotCrashUnique.msg + msg/game_controller/game_events/Prepared.msg + msg/game_controller/game_events/AttackerTouchedOpponentInDefenseArea.msg + msg/game_controller/game_events/DefenderTooCloseToKickPoint.msg + msg/game_controller/game_events/PlacementFailed.msg + msg/game_controller/game_events/DefenderInDefenseAreaPartially.msg + msg/game_controller/game_events/BotDribbledBallTooFar.msg + msg/game_controller/game_events/UnsportingBehaviorMinor.msg + msg/game_controller/game_events/PenaltyKickFailed.msg + msg/game_controller/game_events/KickTimeout.msg + msg/game_controller/game_events/BotCrashDrawn.msg + msg/game_controller/game_events/ChippedGoal.msg + msg/game_controller/game_events/NoProgressInGame.msg + msg/game_controller/game_events/PlacementSucceeded.msg + msg/game_controller/game_events/BotKickedBallTooFast.msg + msg/game_controller/game_events/MultiplePlacementFailures.msg + msg/game_controller/game_events/ChallengeFlag.msg + msg/game_controller/game_events/BallLeftField.msg + msg/game_controller/game_events/Goal.msg + msg/game_controller/game_events/BotHeldBallDeliberately.msg + msg/game_controller/game_events/AttackerTouchedBallInDefenseArea.msg + msg/game_controller/game_events/BotTooFastInStop.msg + msg/game_controller/game_events/AimlessKick.msg + msg/game_controller/game_events/IndirectGoal.msg + msg/game_controller/game_events/BotPushedBot.msg + msg/game_controller/game_events/MultipleFouls.msg + msg/game_controller/game_events/BotTippedOver.msg + msg/game_controller/game_events/AttackerDoubleTouchedBall.msg + msg/game_controller/game_events/BoundaryCrossing.msg + msg/game_controller/game_events/MultipleCards.msg + msg/game_controller/game_events/BotDroppedParts.msg + msg/game_controller/game_events/ChallengeFlagHandled.msg + msg/game_controller/game_events/ExcessiveBotSubstitution.msg + + msg/vision/VisionDetectionBall.msg + msg/vision/VisionDetectionRobot.msg + msg/vision/VisionDetectionFrame.msg + + msg/vision/VisionFieldCircularArc.msg + msg/vision/VisionFieldLineSegment.msg + msg/vision/VisionGeometryFieldSize.msg + msg/vision/VisionGeometryCameraCalibration.msg + msg/vision/VisionGeometryData.msg + + msg/vision/VisionWrapper.msg + + msg/simulator/SimulatorControl.msg + msg/simulator/TeleportBallCommand.msg + msg/simulator/TeleportRobotCommand.msg + + DEPENDENCIES + builtin_interfaces + std_msgs + geometry_msgs +) + +ament_export_dependencies(rosidl_default_runtime) +ament_package() diff --git a/ssl_league_msgs/msg/game_controller/ControllerReply.msg b/ssl_league_msgs/msg/game_controller/ControllerReply.msg new file mode 100644 index 0000000..bad4dd9 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/ControllerReply.msg @@ -0,0 +1,12 @@ +uint8 status_code +string reason +string next_token +uint8 verification + +uint8 STATUS_CODE_UNKNOWN = 0 +uint8 STATUS_CODE_OK = 1 +uint8 STATUS_CODE_REJECTED = 2 + +uint8 VERIFICATION_UNKNOWN = 0 +uint8 VERIFICATION_VERIFIED = 1 +uint8 VERIFICATION_UNVERIFIED = 2 \ No newline at end of file diff --git a/ssl_league_msgs/msg/game_controller/GameEvent.msg b/ssl_league_msgs/msg/game_controller/GameEvent.msg new file mode 100644 index 0000000..01f4d58 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/GameEvent.msg @@ -0,0 +1,103 @@ +string id +uint8 type +string[] origin +uint64 created_timestamp +ssl_league_msgs/BallLeftField[] ball_left_field_touch_line +ssl_league_msgs/BallLeftField[] ball_left_field_goal_line +ssl_league_msgs/AimlessKick[] aimless_kick +ssl_league_msgs/AttackerTooCloseToDefenseArea[] attacker_too_close_to_defense_area +ssl_league_msgs/DefenderInDefenseArea[] defender_in_defense_area +ssl_league_msgs/BoundaryCrossing[] boundary_crossing +ssl_league_msgs/KeeperHeldBall[] keeper_held_ball +ssl_league_msgs/BotDribbledBallTooFar[] bot_dribbled_ball_too_far +ssl_league_msgs/BotPushedBot[] bot_pushed_bot +ssl_league_msgs/BotHeldBallDeliberately[] bot_held_ball_deliberately +ssl_league_msgs/BotTippedOver[] bot_tipped_over +ssl_league_msgs/BotDroppedParts[] bot_dropped_parts +ssl_league_msgs/AttackerTouchedBallInDefenseArea[] attacker_touched_ball_in_defense_area +ssl_league_msgs/BotKickedBallTooFast[] bot_kicked_ball_too_fast +ssl_league_msgs/BotCrashUnique[] bot_crash_unique +ssl_league_msgs/BotCrashDrawn[] bot_crash_drawn +ssl_league_msgs/DefenderTooCloseToKickPoint[] defender_too_close_to_kick_point +ssl_league_msgs/BotTooFastInStop[] bot_too_fast_in_stop +ssl_league_msgs/BotInterferedPlacement[] bot_interfered_placement +ssl_league_msgs/Goal[] possible_goal +ssl_league_msgs/Goal[] goal +ssl_league_msgs/Goal[] invalid_goal +ssl_league_msgs/AttackerDoubleTouchedBall[] attacker_double_touched_ball +ssl_league_msgs/PlacementSucceeded[] placement_succeeded +ssl_league_msgs/PenaltyKickFailed[] penalty_kick_failed +ssl_league_msgs/NoProgressInGame[] no_progress_in_game +ssl_league_msgs/PlacementFailed[] placement_failed +ssl_league_msgs/MultipleCards[] multiple_cards +ssl_league_msgs/MultipleFouls[] multiple_fouls +ssl_league_msgs/BotSubstitution[] bot_substitution +ssl_league_msgs/ExcessiveBotSubstitution[] excessive_bot_substitution +ssl_league_msgs/TooManyRobots[] too_many_robots +ssl_league_msgs/ChallengeFlag[] challenge_flag +ssl_league_msgs/ChallengeFlagHandled[] challenge_flag_handled +ssl_league_msgs/EmergencyStop[] emergency_stop +ssl_league_msgs/UnsportingBehaviorMinor[] unsporting_behavior_minor +ssl_league_msgs/UnsportingBehaviorMajor[] unsporting_behavior_major +ssl_league_msgs/Prepared[] prepared +ssl_league_msgs/IndirectGoal[] indirect_goal +ssl_league_msgs/ChippedGoal[] chipped_goal +ssl_league_msgs/KickTimeout[] kick_timeout +ssl_league_msgs/AttackerTouchedOpponentInDefenseArea[] attacker_touched_opponent_in_defense_area +ssl_league_msgs/AttackerTouchedOpponentInDefenseArea[] attacker_touched_opponent_in_defense_area_skipped +ssl_league_msgs/BotCrashUnique[] bot_crash_unique_skipped +ssl_league_msgs/BotPushedBot[] bot_pushed_bot_skipped +ssl_league_msgs/DefenderInDefenseAreaPartially[] defender_in_defense_area_partially +ssl_league_msgs/MultiplePlacementFailures[] multiple_placement_failures + + +# Types +uint8 TYPE_UNKNOWN_GAME_EVENT_TYPE = 0 +uint8 TYPE_BALL_LEFT_FIELD_TOUCH_LINE = 6 +uint8 TYPE_BALL_LEFT_FIELD_GOAL_LINE = 7 +uint8 TYPE_AIMLESS_KICK = 11 +uint8 TYPE_ATTACKER_TOO_CLOSE_TO_DEFENSE_AREA = 19 +uint8 TYPE_DEFENDER_IN_DEFENSE_AREA = 31 +uint8 TYPE_BOUNDARY_CROSSING = 41 +uint8 TYPE_KEEPER_HELD_BALL = 13 +uint8 TYPE_BOT_DRIBBLED_BALL_TOO_FAR = 17 +uint8 TYPE_BOT_PUSHED_BOT = 24 +uint8 TYPE_BOT_HELD_BALL_DELIBERATELY = 26 +uint8 TYPE_BOT_TIPPED_OVER = 27 +uint8 TYPE_BOT_DROPPED_PARTS = 47 +uint8 TYPE_ATTACKER_TOUCHED_BALL_IN_DEFENSE_AREA = 15 +uint8 TYPE_BOT_KICKED_BALL_TOO_FAST = 18 +uint8 TYPE_BOT_CRASH_UNIQUE = 22 +uint8 TYPE_BOT_CRASH_DRAWN = 21 +uint8 TYPE_DEFENDER_TOO_CLOSE_TO_KICK_POINT = 29 +uint8 TYPE_BOT_TOO_FAST_IN_STOP = 28 +uint8 TYPE_BOT_INTERFERED_PLACEMENT = 20 +uint8 TYPE_EXCESSIVE_BOT_SUBSTITUTION = 48 +uint8 TYPE_POSSIBLE_GOAL = 39 +uint8 TYPE_GOAL = 8 +uint8 TYPE_INVALID_GOAL = 42 +uint8 TYPE_ATTACKER_DOUBLE_TOUCHED_BALL = 14 +uint8 TYPE_PLACEMENT_SUCCEEDED = 5 +uint8 TYPE_PENALTY_KICK_FAILED = 43 +uint8 TYPE_NO_PROGRESS_IN_GAME = 2 +uint8 TYPE_PLACEMENT_FAILED = 3 +uint8 TYPE_MULTIPLE_CARDS = 32 +uint8 TYPE_MULTIPLE_FOULS = 34 +uint8 TYPE_BOT_SUBSTITUTION = 37 +uint8 TYPE_TOO_MANY_ROBOTS = 38 +uint8 TYPE_CHALLENGE_FLAG = 44 +uint8 TYPE_CHALLENGE_FLAG_HANDLED = 46 +uint8 TYPE_EMERGENCY_STOP = 45 +uint8 TYPE_UNSPORTING_BEHAVIOR_MINOR = 35 +uint8 TYPE_UNSPORTING_BEHAVIOR_MAJOR = 36 +# deprecated types +uint8 TYPE_PREPARED = 1 +uint8 TYPE_INDIRECT_GOAL = 9 +uint8 TYPE_CHIPPED_GOAL = 10 +uint8 TYPE_KICK_TIMEOUT = 12 +uint8 TYPE_ATTACKER_TOUCHED_OPPONENT_IN_DEFENSE_AREA = 16 +uint8 TYPE_ATTACKER_TOUCHED_OPPONENT_IN_DEFENSE_AREA_SKIPPED = 40 +uint8 TYPE_BOT_CRASH_UNIQUE_SKIPPED = 23 +uint8 TYPE_BOT_PUSHED_BOT_SKIPPED = 25 +uint8 TYPE_DEFENDER_IN_DEFENSE_AREA_PARTIALLY = 30 +uint8 TYPE_MULTIPLE_PLACEMENT_FAILURES = 33 diff --git a/ssl_league_msgs/msg/game_controller/GameEventProposalGroup.msg b/ssl_league_msgs/msg/game_controller/GameEventProposalGroup.msg new file mode 100644 index 0000000..e1cad8f --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/GameEventProposalGroup.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/GameEvent[] game_events +bool accepted diff --git a/ssl_league_msgs/msg/game_controller/Referee.msg b/ssl_league_msgs/msg/game_controller/Referee.msg new file mode 100644 index 0000000..ab76c44 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/Referee.msg @@ -0,0 +1,61 @@ +string source_identifier +uint8 match_type +builtin_interfaces/Time timestamp +uint8 stage +int64 stage_time_left +uint8 command +uint32 command_counter +builtin_interfaces/Time command_timestamp +ssl_league_msgs/TeamInfo yellow +ssl_league_msgs/TeamInfo blue +geometry_msgs/Point32 designated_position +bool blue_team_on_positive_half +uint8 next_command +ssl_league_msgs/GameEvent[] game_events +ssl_league_msgs/GameEventProposalGroup[] game_event_proposals +int64 current_action_time_remaining +string status_message + + +# Match Types +uint8 MATCH_TYPE_UNKNOWN_MATCH = 0 +uint8 MATCH_TYPE_GROUP_PHASE = 1 +uint8 MATCH_TYPE_ELIMINATION_PHASE = 2 +uint8 MATCH_TYPE_FRIENDLY = 3 + +# Stages +uint8 STAGE_NORMAL_FIRST_HALF_PRE = 0 +uint8 STAGE_NORMAL_FIRST_HALF = 1 +uint8 STAGE_NORMAL_HALF_TIME = 2 +uint8 STAGE_NORMAL_SECOND_HALF_PRE = 3 +uint8 STAGE_NORMAL_SECOND_HALF = 4 +uint8 STAGE_EXTRA_TIME_BREAK = 5 +uint8 STAGE_EXTRA_FIRST_HALF_PRE = 6 +uint8 STAGE_EXTRA_FIRST_HALF = 7 +uint8 STAGE_EXTRA_HALF_TIME = 8 +uint8 STAGE_EXTRA_SECOND_HALF_PRE = 9 +uint8 STAGE_EXTRA_SECOND_HALF = 10 +uint8 STAGE_PENALTY_SHOOTOUT_BREAK = 11 +uint8 STAGE_PENALTY_SHOOTOUT = 12 +uint8 STAGE_POST_GAME = 13 + +# Commands +uint8 COMMAND_HALT = 0 +uint8 COMMAND_STOP = 1 +uint8 COMMAND_NORMAL_START = 2 +uint8 COMMAND_FORCE_START = 3 +uint8 COMMAND_PREPARE_KICKOFF_YELLOW = 4 +uint8 COMMAND_PREPARE_KICKOFF_BLUE = 5 +uint8 COMMAND_PREPARE_PENALTY_YELLOW = 6 +uint8 COMMAND_PREPARE_PENALTY_BLUE = 7 +uint8 COMMAND_DIRECT_FREE_YELLOW = 8 +uint8 COMMAND_DIRECT_FREE_BLUE = 9 +uint8 COMMAND_TIMEOUT_YELLOW = 12 +uint8 COMMAND_TIMEOUT_BLUE = 13 +uint8 COMMAND_BALL_PLACEMENT_YELLOW = 16 +uint8 COMMAND_BALL_PLACEMENT_BLUE = 17 +# Deprecated commands +uint8 COMMAND_INDIRECT_FREE_YELLOW = 10 +uint8 COMMAND_INDIRECT_FREE_BLUE = 11 +uint8 COMMAND_GOAL_YELLOW = 14 +uint8 COMMAND_GOAL_BLUE = 15 diff --git a/ssl_league_msgs/msg/game_controller/TeamInfo.msg b/ssl_league_msgs/msg/game_controller/TeamInfo.msg new file mode 100644 index 0000000..379a10c --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/TeamInfo.msg @@ -0,0 +1,17 @@ +string name +uint32 score +uint32 red_cards +uint32[] yellow_card_times +uint32 yellow_cards +uint32 timeouts +uint32 timeout_time +uint32 goalkeeper +uint32 foul_counter +uint32 ball_placement_failures +bool can_place_ball +uint32 max_allowed_bots +bool bot_substitution_intent +bool ball_placement_failures_reached +bool bot_substitution_allowed +uint32 bot_substitutions_left +uint32 bot_substitution_time_left diff --git a/ssl_league_msgs/msg/game_controller/common/Division.msg b/ssl_league_msgs/msg/game_controller/common/Division.msg new file mode 100644 index 0000000..b012f6d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/common/Division.msg @@ -0,0 +1,5 @@ +uint8 division + +uint8 DIVISON_UNKNOWN = 0 +uint8 DIVISION_DIV_A = 1 +uint8 DIVISION_DIV_B = 2 diff --git a/ssl_league_msgs/msg/game_controller/common/RobotId.msg b/ssl_league_msgs/msg/game_controller/common/RobotId.msg new file mode 100644 index 0000000..698aaff --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/common/RobotId.msg @@ -0,0 +1,2 @@ +uint32[] id +ssl_league_msgs/Team[] team diff --git a/ssl_league_msgs/msg/game_controller/common/Team.msg b/ssl_league_msgs/msg/game_controller/common/Team.msg new file mode 100644 index 0000000..375dfa7 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/common/Team.msg @@ -0,0 +1,5 @@ +uint8 color + +uint8 COLOR_UNKNOWN = 0 +uint8 COLOR_YELLOW = 1 +uint8 COLOR_BLUE = 2 diff --git a/ssl_league_msgs/msg/game_controller/game_events/AimlessKick.msg b/ssl_league_msgs/msg/game_controller/game_events/AimlessKick.msg new file mode 100644 index 0000000..36f4e95 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/AimlessKick.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] kick_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/AttackerDoubleTouchedBall.msg b/ssl_league_msgs/msg/game_controller/game_events/AttackerDoubleTouchedBall.msg new file mode 100644 index 0000000..61d9407 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/AttackerDoubleTouchedBall.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location diff --git a/ssl_league_msgs/msg/game_controller/game_events/AttackerTooCloseToDefenseArea.msg b/ssl_league_msgs/msg/game_controller/game_events/AttackerTooCloseToDefenseArea.msg new file mode 100644 index 0000000..b51ecbf --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/AttackerTooCloseToDefenseArea.msg @@ -0,0 +1,5 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] distance +geometry_msgs/Point32[] ball_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedBallInDefenseArea.msg b/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedBallInDefenseArea.msg new file mode 100644 index 0000000..d7fd7ef --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedBallInDefenseArea.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedOpponentInDefenseArea.msg b/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedOpponentInDefenseArea.msg new file mode 100644 index 0000000..93e10b5 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/AttackerTouchedOpponentInDefenseArea.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +uint32[] victim +geometry_msgs/Point32[] location diff --git a/ssl_league_msgs/msg/game_controller/game_events/BallLeftField.msg b/ssl_league_msgs/msg/game_controller/game_events/BallLeftField.msg new file mode 100644 index 0000000..61d9407 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BallLeftField.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotCrashDrawn.msg b/ssl_league_msgs/msg/game_controller/game_events/BotCrashDrawn.msg new file mode 100644 index 0000000..9278070 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotCrashDrawn.msg @@ -0,0 +1,6 @@ +uint32[] bot_yellow +uint32[] bot_blue +geometry_msgs/Point32[] location +float32[] crash_speed +float32[] speed_diff +float32[] crash_angle diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotCrashUnique.msg b/ssl_league_msgs/msg/game_controller/game_events/BotCrashUnique.msg new file mode 100644 index 0000000..f9ee9ec --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotCrashUnique.msg @@ -0,0 +1,7 @@ +ssl_league_msgs/Team by_team +uint32[] violator +uint32[] victim +geometry_msgs/Point32[] location +float32[] crash_speed +float32[] speed_diff +float32[] crash_angle diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotDribbledBallTooFar.msg b/ssl_league_msgs/msg/game_controller/game_events/BotDribbledBallTooFar.msg new file mode 100644 index 0000000..a74eb6b --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotDribbledBallTooFar.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] start +geometry_msgs/Point32[] end diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotDroppedParts.msg b/ssl_league_msgs/msg/game_controller/game_events/BotDroppedParts.msg new file mode 100644 index 0000000..73df4d6 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotDroppedParts.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] ball_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotHeldBallDeliberately.msg b/ssl_league_msgs/msg/game_controller/game_events/BotHeldBallDeliberately.msg new file mode 100644 index 0000000..c4009d9 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotHeldBallDeliberately.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] duration diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotInterferedPlacement.msg b/ssl_league_msgs/msg/game_controller/game_events/BotInterferedPlacement.msg new file mode 100644 index 0000000..61d9407 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotInterferedPlacement.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotKickedBallTooFast.msg b/ssl_league_msgs/msg/game_controller/game_events/BotKickedBallTooFast.msg new file mode 100644 index 0000000..100e0d4 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotKickedBallTooFast.msg @@ -0,0 +1,5 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] initial_ball_speed +bool[] chipped diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotPushedBot.msg b/ssl_league_msgs/msg/game_controller/game_events/BotPushedBot.msg new file mode 100644 index 0000000..f9314dc --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotPushedBot.msg @@ -0,0 +1,5 @@ +ssl_league_msgs/Team by_team +uint32[] violator +uint32[] victim +geometry_msgs/Point32[] location +float32[] pushed_distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotSubstitution.msg b/ssl_league_msgs/msg/game_controller/game_events/BotSubstitution.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotSubstitution.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotTippedOver.msg b/ssl_league_msgs/msg/game_controller/game_events/BotTippedOver.msg new file mode 100644 index 0000000..73df4d6 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotTippedOver.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] ball_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/BotTooFastInStop.msg b/ssl_league_msgs/msg/game_controller/game_events/BotTooFastInStop.msg new file mode 100644 index 0000000..7609dc0 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BotTooFastInStop.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] speed diff --git a/ssl_league_msgs/msg/game_controller/game_events/BoundaryCrossing.msg b/ssl_league_msgs/msg/game_controller/game_events/BoundaryCrossing.msg new file mode 100644 index 0000000..f0766ea --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/BoundaryCrossing.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/Team by_team +geometry_msgs/Point32[] location diff --git a/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlag.msg b/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlag.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlag.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlagHandled.msg b/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlagHandled.msg new file mode 100644 index 0000000..7d4cb72 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/ChallengeFlagHandled.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/Team by_team +bool accepted diff --git a/ssl_league_msgs/msg/game_controller/game_events/ChippedGoal.msg b/ssl_league_msgs/msg/game_controller/game_events/ChippedGoal.msg new file mode 100644 index 0000000..b3b6a4d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/ChippedGoal.msg @@ -0,0 +1,5 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] kick_location +float32[] max_ball_height diff --git a/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseArea.msg b/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseArea.msg new file mode 100644 index 0000000..d7fd7ef --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseArea.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseAreaPartially.msg b/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseAreaPartially.msg new file mode 100644 index 0000000..b51ecbf --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/DefenderInDefenseAreaPartially.msg @@ -0,0 +1,5 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] distance +geometry_msgs/Point32[] ball_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/DefenderTooCloseToKickPoint.msg b/ssl_league_msgs/msg/game_controller/game_events/DefenderTooCloseToKickPoint.msg new file mode 100644 index 0000000..d7fd7ef --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/DefenderTooCloseToKickPoint.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +float32[] distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/EmergencyStop.msg b/ssl_league_msgs/msg/game_controller/game_events/EmergencyStop.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/EmergencyStop.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/ExcessiveBotSubstitution.msg b/ssl_league_msgs/msg/game_controller/game_events/ExcessiveBotSubstitution.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/ExcessiveBotSubstitution.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/Goal.msg b/ssl_league_msgs/msg/game_controller/game_events/Goal.msg new file mode 100644 index 0000000..2469f41 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/Goal.msg @@ -0,0 +1,9 @@ +ssl_league_msgs/Team by_team +ssl_league_msgs/Team[] kicking_team +uint32[] kicking_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] kick_location +float32[] max_ball_height +uint32[] num_robots_by_team +uint64[] last_touch_by_team +string[] message diff --git a/ssl_league_msgs/msg/game_controller/game_events/IndirectGoal.msg b/ssl_league_msgs/msg/game_controller/game_events/IndirectGoal.msg new file mode 100644 index 0000000..36f4e95 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/IndirectGoal.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +uint32[] by_bot +geometry_msgs/Point32[] location +geometry_msgs/Point32[] kick_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/KeeperHeldBall.msg b/ssl_league_msgs/msg/game_controller/game_events/KeeperHeldBall.msg new file mode 100644 index 0000000..c15023b --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/KeeperHeldBall.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +geometry_msgs/Point32[] location +float32[] duration diff --git a/ssl_league_msgs/msg/game_controller/game_events/KickTimeout.msg b/ssl_league_msgs/msg/game_controller/game_events/KickTimeout.msg new file mode 100644 index 0000000..789bdcc --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/KickTimeout.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +geometry_msgs/Point32[] location +float32[] time diff --git a/ssl_league_msgs/msg/game_controller/game_events/MultipleCards.msg b/ssl_league_msgs/msg/game_controller/game_events/MultipleCards.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/MultipleCards.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/MultipleFouls.msg b/ssl_league_msgs/msg/game_controller/game_events/MultipleFouls.msg new file mode 100644 index 0000000..ae46dd7 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/MultipleFouls.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team + +# TODO(barulicm) Recursive message types aren't possible. Not sure how to handle this yet. +# ssl_league_msgs/GameEvent[] caused_game_events diff --git a/ssl_league_msgs/msg/game_controller/game_events/MultiplePlacementFailures.msg b/ssl_league_msgs/msg/game_controller/game_events/MultiplePlacementFailures.msg new file mode 100644 index 0000000..fbf970d --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/MultiplePlacementFailures.msg @@ -0,0 +1 @@ +ssl_league_msgs/Team by_team diff --git a/ssl_league_msgs/msg/game_controller/game_events/NoProgressInGame.msg b/ssl_league_msgs/msg/game_controller/game_events/NoProgressInGame.msg new file mode 100644 index 0000000..8e1f913 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/NoProgressInGame.msg @@ -0,0 +1,2 @@ +geometry_msgs/Point32[] location +float32[] time diff --git a/ssl_league_msgs/msg/game_controller/game_events/PenaltyKickFailed.msg b/ssl_league_msgs/msg/game_controller/game_events/PenaltyKickFailed.msg new file mode 100644 index 0000000..d0d9131 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/PenaltyKickFailed.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +geometry_msgs/Point32[] location +string[] reason diff --git a/ssl_league_msgs/msg/game_controller/game_events/PlacementFailed.msg b/ssl_league_msgs/msg/game_controller/game_events/PlacementFailed.msg new file mode 100644 index 0000000..f1c8414 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/PlacementFailed.msg @@ -0,0 +1,3 @@ +ssl_league_msgs/Team by_team +float32[] remaining_distance +float32[] nearest_own_bot_distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/PlacementSucceeded.msg b/ssl_league_msgs/msg/game_controller/game_events/PlacementSucceeded.msg new file mode 100644 index 0000000..6ea2fb2 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/PlacementSucceeded.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +float32[] time_taken +float32[] precision +float32[] distance diff --git a/ssl_league_msgs/msg/game_controller/game_events/Prepared.msg b/ssl_league_msgs/msg/game_controller/game_events/Prepared.msg new file mode 100644 index 0000000..bda9961 --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/Prepared.msg @@ -0,0 +1 @@ +float32[] time_taken diff --git a/ssl_league_msgs/msg/game_controller/game_events/TooManyRobots.msg b/ssl_league_msgs/msg/game_controller/game_events/TooManyRobots.msg new file mode 100644 index 0000000..35edadb --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/TooManyRobots.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/Team by_team +int32[] num_robots_allowed +int32[] num_robots_on_field +geometry_msgs/Point32[] ball_location diff --git a/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMajor.msg b/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMajor.msg new file mode 100644 index 0000000..ed644cf --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMajor.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/Team by_team +string reason diff --git a/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMinor.msg b/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMinor.msg new file mode 100644 index 0000000..ed644cf --- /dev/null +++ b/ssl_league_msgs/msg/game_controller/game_events/UnsportingBehaviorMinor.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/Team by_team +string reason diff --git a/ssl_league_msgs/msg/simulator/SimulatorControl.msg b/ssl_league_msgs/msg/simulator/SimulatorControl.msg new file mode 100644 index 0000000..6a0d434 --- /dev/null +++ b/ssl_league_msgs/msg/simulator/SimulatorControl.msg @@ -0,0 +1,4 @@ +ssl_league_msgs/TeleportBallCommand[] teleport_ball +ssl_league_msgs/TeleportRobotCommand[] teleport_robot + +float32 simulation_speed 1.0 \ No newline at end of file diff --git a/ssl_league_msgs/msg/simulator/TeleportBallCommand.msg b/ssl_league_msgs/msg/simulator/TeleportBallCommand.msg new file mode 100644 index 0000000..941d053 --- /dev/null +++ b/ssl_league_msgs/msg/simulator/TeleportBallCommand.msg @@ -0,0 +1,19 @@ +# Teleport the ball to a new location and optionally set it to some velocity +geometry_msgs/Pose pose +geometry_msgs/Twist twist + +# Teleport the ball safely to the target, for example by +# moving robots out of the way in case of collision and set speed of robots close-by to zero +bool teleport_safely + +# Adapt the angular ball velocity such that the ball is rolling +bool roll + +# Instead of teleporting the ball, apply some force to make sure +# the ball reaches the required position soon (velocity is ignored if true) +# WARNING: A command with by_force stays active (the move will take some time) +# until cancled by another TeleportBall command with by_force = false. +# To avoid teleporting the ball at the end and resetting its current spin, +# do not set any of the optional fields in this message to end the force without triggering +# an additional teleportation +bool by_force \ No newline at end of file diff --git a/ssl_league_msgs/msg/simulator/TeleportRobotCommand.msg b/ssl_league_msgs/msg/simulator/TeleportRobotCommand.msg new file mode 100644 index 0000000..26b74d8 --- /dev/null +++ b/ssl_league_msgs/msg/simulator/TeleportRobotCommand.msg @@ -0,0 +1,20 @@ +# Teleport a robot to some location and give it a velocity +ssl_league_msgs/RobotId id + +geometry_msgs/Pose pose +geometry_msgs/Twist twist + +# Robot should be present on the field? +# true -> robot will be added, if it does not exist yet +# false -> robot will be removed, if it is present +bool present + +# Instead of teleporting, apply some force to make sure +# the robot reaches the required position soon (velocity is ignored if true) +# WARNING: A command with by_force stays active (the move will take some time) +# until cancled by another TeleportRobot command for the same bot with by_force = false. +# To avoid teleporting at the end, +# do not set any of the optional fields in this message +# to end the force without triggering +# an additional teleportation +bool by_force \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionDetectionBall.msg b/ssl_league_msgs/msg/vision/VisionDetectionBall.msg new file mode 100644 index 0000000..be550e0 --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionDetectionBall.msg @@ -0,0 +1,4 @@ +float32 confidence +uint32 area +geometry_msgs/Point32 pos +geometry_msgs/Point32 pixel \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionDetectionFrame.msg b/ssl_league_msgs/msg/vision/VisionDetectionFrame.msg new file mode 100644 index 0000000..83582cc --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionDetectionFrame.msg @@ -0,0 +1,8 @@ +uint32 frame_number +builtin_interfaces/Time t_capture +builtin_interfaces/Time t_sent +builtin_interfaces/Time t_capture_camera +uint32 camera_id +ssl_league_msgs/VisionDetectionBall[] balls +ssl_league_msgs/VisionDetectionRobot[] robots_yellow +ssl_league_msgs/VisionDetectionRobot[] robots_blue \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionDetectionRobot.msg b/ssl_league_msgs/msg/vision/VisionDetectionRobot.msg new file mode 100644 index 0000000..29867b3 --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionDetectionRobot.msg @@ -0,0 +1,5 @@ +float32 confidence +uint32 robot_id +geometry_msgs/Pose pose +geometry_msgs/Point32 pixel +float32 height \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionFieldCircularArc.msg b/ssl_league_msgs/msg/vision/VisionFieldCircularArc.msg new file mode 100644 index 0000000..484f0f3 --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionFieldCircularArc.msg @@ -0,0 +1,6 @@ +string name +geometry_msgs/Point32 center +float32 radius +float32 a1 +float32 a2 +float32 thickness \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionFieldLineSegment.msg b/ssl_league_msgs/msg/vision/VisionFieldLineSegment.msg new file mode 100644 index 0000000..acd2fff --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionFieldLineSegment.msg @@ -0,0 +1,4 @@ +string name +geometry_msgs/Point32 p1 +geometry_msgs/Point32 p2 +float32 thickness \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionGeometryCameraCalibration.msg b/ssl_league_msgs/msg/vision/VisionGeometryCameraCalibration.msg new file mode 100644 index 0000000..bf64e56 --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionGeometryCameraCalibration.msg @@ -0,0 +1,6 @@ +uint32 camera_id +float32 focal_length +geometry_msgs/Point32 principal_point +float32 distortion +geometry_msgs/Pose pose +geometry_msgs/Point32 derived_camera_world_t \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionGeometryData.msg b/ssl_league_msgs/msg/vision/VisionGeometryData.msg new file mode 100644 index 0000000..2b2b7f9 --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionGeometryData.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/VisionGeometryFieldSize field +ssl_league_msgs/VisionGeometryCameraCalibration[] calibration \ No newline at end of file diff --git a/ssl_league_msgs/msg/vision/VisionGeometryFieldSize.msg b/ssl_league_msgs/msg/vision/VisionGeometryFieldSize.msg new file mode 100644 index 0000000..eb8579d --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionGeometryFieldSize.msg @@ -0,0 +1,15 @@ +float32 field_length +float32 field_width +float32 goal_width +float32 goal_depth +float32 boundary_width +ssl_league_msgs/VisionFieldLineSegment[] field_lines +ssl_league_msgs/VisionFieldCircularArc[] field_arcs +float32 penalty_area_depth +float32 penalty_area_width +float32 center_circle_radius +float32 line_thickness +float32 goal_center_to_penalty_mark +float32 goal_height +float32 ball_radius +float32 max_robot_radius diff --git a/ssl_league_msgs/msg/vision/VisionWrapper.msg b/ssl_league_msgs/msg/vision/VisionWrapper.msg new file mode 100644 index 0000000..57deadb --- /dev/null +++ b/ssl_league_msgs/msg/vision/VisionWrapper.msg @@ -0,0 +1,2 @@ +ssl_league_msgs/VisionDetectionFrame[] detection +ssl_league_msgs/VisionGeometryData[] geometry \ No newline at end of file diff --git a/ssl_league_msgs/package.xml b/ssl_league_msgs/package.xml new file mode 100644 index 0000000..1ff85b1 --- /dev/null +++ b/ssl_league_msgs/package.xml @@ -0,0 +1,25 @@ + + + + ssl_league_msgs + 0.0.0 + Message definitions for RoboCup SSL league-defined messages + Matthew Barulic + TODO: License declaration + + ament_cmake + + rosidl_default_generators + + rosidl_default_runtime + + builtin_interfaces + std_msgs + geometry_msgs + + rosidl_interface_packages + + + ament_cmake + + diff --git a/ssl_league_protobufs/CMakeLists.txt b/ssl_league_protobufs/CMakeLists.txt new file mode 100644 index 0000000..29b6eb5 --- /dev/null +++ b/ssl_league_protobufs/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.5) +project(ssl_league_protobufs) + +find_package(ament_cmake REQUIRED) +find_package(Protobuf REQUIRED) + +protobuf_generate_cpp( + PROTOBUF_SRCS + PROTOBUF_HDRS + proto/ssl_gc_api.proto + proto/ssl_gc_api.proto + proto/ssl_gc_change.proto + proto/ssl_gc_ci.proto + proto/ssl_gc_common.proto + proto/ssl_gc_engine_config.proto + proto/ssl_gc_engine.proto + proto/ssl_gc_game_event.proto + proto/ssl_gc_geometry.proto + proto/ssl_gc_rcon_autoref.proto + proto/ssl_gc_rcon_remotecontrol.proto + proto/ssl_gc_rcon_team.proto + proto/ssl_gc_rcon.proto + proto/ssl_gc_referee_message.proto + proto/ssl_gc_state.proto + proto/ssl_simulation_config.proto + proto/ssl_simulation_control.proto + proto/ssl_simulation_error.proto + proto/ssl_simulation_robot_control.proto + proto/ssl_simulation_robot_feedback.proto + proto/ssl_simulation_synchronous.proto + proto/ssl_vision_detection_tracked.proto + proto/ssl_vision_detection.proto + proto/ssl_vision_geometry.proto + proto/ssl_vision_wrapper_tracked.proto + proto/ssl_vision_wrapper.proto +) + +add_library(${PROJECT_NAME} SHARED + ${PROTOBUF_HDRS} + ${PROTOBUF_SRCS} +) +target_link_libraries(${PROJECT_NAME} + protobuf::libprotobuf +) +target_include_directories(${PROJECT_NAME} + INTERFACE + $ + $ +) +# Disable noisy deprecation warnings we can't control from league protobufs +target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated-declarations) + +ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET) +ament_export_dependencies(Protobuf) + +install( + DIRECTORY ${CMAKE_BINARY_DIR} + DESTINATION include +) +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME} + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/ssl_league_protobufs/package.xml b/ssl_league_protobufs/package.xml new file mode 100644 index 0000000..94a1a73 --- /dev/null +++ b/ssl_league_protobufs/package.xml @@ -0,0 +1,20 @@ + + + + ssl_league_protobufs + 0.0.0 + SSL protobufs. + Joseph Neiger + TODO: License declaration + + protobuf-dev + + ament_cmake + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/ssl_league_protobufs/proto/ssl_gc_api.proto b/ssl_league_protobufs/proto/ssl_gc_api.proto new file mode 100644 index 0000000..1ef2c6d --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_api.proto @@ -0,0 +1,56 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/api"; + +import "ssl_gc_state.proto"; +import "ssl_gc_change.proto"; +import "ssl_gc_engine.proto"; +import "ssl_gc_engine_config.proto"; + +import "google/protobuf/duration.proto"; + +// Message format that is pushed from the GC to the client +message Output { + // The current match state + optional State match_state = 1; + // The current GC state + optional GcState gc_state = 2; + // The protocol + optional Protocol protocol = 3; + // The engine config + optional Config config = 4; +} + +// The game protocol +message Protocol { + // Is this a delta only? + // Entries that were already sent are not sent again, because the protocol is immutable anyway. + // But if the game is reset, the whole protocol must be replaced. That's what this flag is for. + optional bool delta = 1; + // The (delta) list of entries + repeated ProtocolEntry entry = 2; +} + +// A protocol entry of a change +message ProtocolEntry { + // Id of the entry + optional int32 id = 1; + // The change that was made + optional Change change = 2; + // The match time elapsed when this change was made + optional google.protobuf.Duration match_time_elapsed = 3; + // The stage time elapsed when this change was made + optional google.protobuf.Duration stage_time_elapsed = 4; +} + +// Message format that can be send from the client to the GC +message Input { + // A change to be enqueued into the GC engine + optional Change change = 1; + // Reset the match + optional bool reset_match = 2; + // An updated config delta + optional Config config_delta = 3; + // Continue with action + optional ContinueAction continue_action = 4; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_change.proto b/ssl_league_protobufs/proto/ssl_gc_change.proto new file mode 100644 index 0000000..35c4c30 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_change.proto @@ -0,0 +1,200 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/statemachine"; + +import "ssl_gc_state.proto"; +import "ssl_gc_common.proto"; +import "ssl_gc_geometry.proto"; +import "ssl_gc_game_event.proto"; +import "ssl_gc_referee_message.proto"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +// A state change +message StateChange { + // A unique increasing id + optional int32 id = 1; + // The previous state + optional State state_pre = 2; + // The state after the change was applied + optional State state = 3; + // The change itself + optional Change change = 4; + // The timestamp when the change was triggered + optional google.protobuf.Timestamp timestamp = 5; +} + +// A certain change +message Change { + // An identifier of the origin that triggered the change + optional string origin = 1; + // Is this change revertible? + optional bool revertible = 16; + + oneof change { + NewCommand new_command_change = 2; + ChangeStage change_stage_change = 3; + SetBallPlacementPos set_ball_placement_pos_change = 4; + AddYellowCard add_yellow_card_change = 5; + AddRedCard add_red_card_change = 6; + YellowCardOver yellow_card_over_change = 7; + AddGameEvent add_game_event_change = 8; + AddPassiveGameEvent add_passive_game_event_change = 19; + AddProposal add_proposal_change = 9; + UpdateConfig update_config_change = 12; + UpdateTeamState update_team_state_change = 13; + SwitchColors switch_colors_change = 14; + Revert revert_change = 15; + NewGameState new_game_state_change = 17; + AcceptProposalGroup accept_proposal_group_change = 18; + SetStatusMessage set_status_message_change = 20; + } + + // New referee command + message NewCommand { + // The command + optional Command command = 1; + } + + // Switch to a new stage + message ChangeStage { + // The new stage + optional Referee.Stage new_stage = 1; + } + + // Set the ball placement pos + message SetBallPlacementPos { + // The position in [m] + optional Vector2 pos = 1; + } + + // Add a new yellow card + message AddYellowCard { + // The team that the card is for + optional Team for_team = 1; + // The game event that caused the card + optional GameEvent caused_by_game_event = 2; + } + + // Add a new red card + message AddRedCard { + // The team that the card is for + optional Team for_team = 1; + // The game event that caused the card + optional GameEvent caused_by_game_event = 2; + } + + // Trigger when a yellow card timed out + message YellowCardOver { + // The team that the card was for + optional Team for_team = 1; + } + + // Add a new game event + message AddGameEvent { + // The game event + optional GameEvent game_event = 1; + } + + // Add a new passive game event (that is only logged, but does not automatically trigger anything) + message AddPassiveGameEvent { + // The game event + optional GameEvent game_event = 1; + } + + // Add a new proposal (i.e. from an auto referee for majority voting) + message AddProposal { + // The proposal + optional Proposal proposal = 1; + } + + // Accept a proposal group (that contain one or more proposals of the same type) + message AcceptProposalGroup { + // The id of the group + optional string group_id = 3; + // An identifier of the acceptor + optional string accepted_by = 2; + } + + // Update some configuration + message UpdateConfig { + // The division to play with + optional Division division = 1; + // the team that does/did the first kick off + optional Team first_kickoff_team = 2; + reserved 3; // auto_continue moved to gcState + // The match type + optional MatchType match_type = 4; + // The number of robots per team + optional google.protobuf.Int32Value max_robots_per_team = 5; + } + + // Update the current state of a team (all fields that should be updated are set) + message UpdateTeamState { + // The team + optional Team for_team = 1; + + // Change the name of the team + optional google.protobuf.StringValue team_name = 2; + // Change the number of goals that the teams has at the moment + optional google.protobuf.Int32Value goals = 3; + // The id of the goal keeper + optional google.protobuf.Int32Value goalkeeper = 4; + // The number of timeouts that the team has left + optional google.protobuf.Int32Value timeouts_left = 5; + // The timeout time that the team has left + optional google.protobuf.StringValue timeout_time_left = 6; + // Does the team play on the positive or the negative half (in ssl-vision coordinates)? + optional google.protobuf.BoolValue on_positive_half = 7; + // The number of ball placement failures + optional google.protobuf.Int32Value ball_placement_failures = 8; + // Can the team place the ball, or is ball placement for this team disabled and should be skipped? + optional google.protobuf.BoolValue can_place_ball = 9; + // The number of challenge flags that the team has left + optional google.protobuf.Int32Value challenge_flags_left = 21; + // The number of bot substitutions left by the team in this halftime + optional google.protobuf.Int32Value bot_substitutions_left = 22; + // Does the team want to substitute a robot in the next possible situation? + optional google.protobuf.BoolValue requests_bot_substitution = 10; + // Does the team want to take a timeout in the next possible situation? + optional google.protobuf.BoolValue requests_timeout = 17; + // Does the team want to challenge a recent decision of the referee? + optional google.protobuf.BoolValue requests_challenge = 18; + // Does the team want to request an emergency stop? + optional google.protobuf.BoolValue requests_emergency_stop = 19; + // Update a certain yellow card of the team + optional YellowCard yellow_card = 20; + // Update a certain red card of the team + optional RedCard red_card = 12; + // Update a certain foul of the team + optional Foul foul = 13; + // Remove the yellow card with this id + optional google.protobuf.UInt32Value remove_yellow_card = 14; + // Remove the red card with this id + optional google.protobuf.UInt32Value remove_red_card = 15; + // Remove the foul with this id + optional google.protobuf.UInt32Value remove_foul = 16; + } + + // Switch the team colors + message SwitchColors { + } + + // Revert a certain change + message Revert { + // The id of the change + optional int32 change_id = 1; + } + + // Change the current game state + message NewGameState { + // The new game state + optional GameState game_state = 1; + } + + message SetStatusMessage { + // The new status message + optional string status_message = 1; + } +} diff --git a/ssl_league_protobufs/proto/ssl_gc_ci.proto b/ssl_league_protobufs/proto/ssl_gc_ci.proto new file mode 100644 index 0000000..0eddc73 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_ci.proto @@ -0,0 +1,26 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/ci"; + +import "ssl_vision_wrapper_tracked.proto"; +import "ssl_gc_api.proto"; +import "ssl_gc_referee_message.proto"; +import "ssl_vision_geometry.proto"; + +// The input format to the GC +message CiInput { + // New unix timestamp in [ns] for the GC + optional int64 timestamp = 1; + // New tracker packet with ball and robot data + optional TrackerWrapperPacket tracker_packet = 2; + // (UI) API input + repeated Input api_inputs = 3; + // Update geometry + optional SSL_GeometryData geometry = 4; +} + +// The output format of the GC response +message CiOutput { + // Latest referee message + optional Referee referee_msg = 1; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_common.proto b/ssl_league_protobufs/proto/ssl_gc_common.proto new file mode 100644 index 0000000..796e2d3 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_common.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; + +// Team is either blue or yellow +enum Team { + // team not set + UNKNOWN = 0; + // yellow team + YELLOW = 1; + // blue team + BLUE = 2; +} + +// RobotId is the combination of a team and a robot id +message RobotId { + // the robot number + optional uint32 id = 1; + // the team that the robot belongs to + optional Team team = 2; +} + +// Division denotes the current division, which influences some rules +enum Division { + DIV_UNKNOWN = 0; + DIV_A = 1; + DIV_B = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_engine.proto b/ssl_league_protobufs/proto/ssl_gc_engine.proto new file mode 100644 index 0000000..6e58206 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_engine.proto @@ -0,0 +1,150 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/engine"; + +import "ssl_gc_geometry.proto"; +import "ssl_gc_common.proto"; + +import "google/protobuf/timestamp.proto"; + +// The GC state contains settings and state independent of the match state +message GcState { + // the state of each team + map team_state = 1; + + // the states of the auto referees + map auto_ref_state = 2; + + // the attached trackers (uuid -> source_name) + map trackers = 3; + + // the next actions that can be executed when continuing + repeated ContinueAction continue_actions = 4; + + // the next actions that can be executed when continuing + repeated ContinueHint continue_hints = 5; +} + +// The GC state for a single team +message GcStateTeam { + // true: The team is connected + optional bool connected = 1; + + // true: The team connected via TLS with a verified certificate + optional bool connection_verified = 2; + + // true: The remote control for the team is connected + optional bool remote_control_connected = 3; + + // true: The remote control for the team connected via TLS with a verified certificate + optional bool remote_control_connection_verified = 4; + + // the advantage choice of the team + optional TeamAdvantageChoice advantage_choice = 5; +} + +// The choice from a team regarding the advantage rule +message TeamAdvantageChoice { + // the choice of the team + optional AdvantageChoice choice = 1; + + // possible advantage choices + enum AdvantageChoice { + // stop the game + STOP = 0; + // keep the match running + CONTINUE = 1; + } +} + +// The GC state of an auto referee +message GcStateAutoRef { + // true: The autoRef connected via TLS with a verified certificate + optional bool connection_verified = 1; +} + +// GC state of a tracker +message GcStateTracker { + // Name of the source + optional string source_name = 1; + + // UUID of the source + optional string uuid = 4; + + // Current ball + optional Ball ball = 2; + + // Current robots + repeated Robot robots = 3; +} + +// The ball state +message Ball { + // ball position [m] + optional Vector3 pos = 1; + + // ball velocity [m/s] + optional Vector3 vel = 2; +} + +// The robot state +message Robot { + // robot id and team + optional RobotId id = 1; + + // robot position [m] + optional Vector2 pos = 2; +} + +message ContinueAction { + // type of action that will be performed next + required Type type = 1; + + // for which team (if team specific) + required Team for_team = 2; + + // list of issues that hinders the game from continuing + repeated string continuation_issues = 3; + + // timestamp at which the action will be ready (to give some preparation time) + optional google.protobuf.Timestamp ready_at = 4; + + // state of the action + optional State state = 5; + + enum Type { + TYPE_UNKNOWN = 0; + HALT = 1; + RESUME_FROM_HALT = 10; + STOP_GAME = 2; + FORCE_START = 11; + FREE_KICK = 17; + NEXT_COMMAND = 3; + BALL_PLACEMENT_START = 4; + BALL_PLACEMENT_CANCEL = 9; + BALL_PLACEMENT_COMPLETE = 14; + BALL_PLACEMENT_FAIL = 15; + TIMEOUT_START = 5; + TIMEOUT_STOP = 6; + BOT_SUBSTITUTION = 7; + NEXT_STAGE = 8; + END_GAME = 16; + ACCEPT_GOAL = 12; + NORMAL_START = 13; + CHALLENGE_ACCEPT = 18; + CHALLENGE_REJECT = 19; + } + + enum State { + STATE_UNKNOWN = 0; + BLOCKED = 1; + WAITING = 2; + READY_AUTO = 3; + READY_MANUAL = 4; + DISABLED = 5; + } +} + +message ContinueHint { + required string message = 1; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_engine_config.proto b/ssl_league_protobufs/proto/ssl_gc_engine_config.proto new file mode 100644 index 0000000..b523840 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_engine_config.proto @@ -0,0 +1,55 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/engine"; + +// The engine config +message Config { + // The behavior for each game event + map game_event_behavior = 1; + + // The config for each auto referee + map auto_ref_configs = 2; + + // The selected tracker source + optional string active_tracker_source = 3; + + // The list of available teams + repeated string teams = 4; + + // Enable or disable auto continuation + optional bool auto_continue = 5; + + // Behaviors for each game event + enum Behavior { + // Not set or unknown + BEHAVIOR_UNKNOWN = 0; + // Always accept the game event + BEHAVIOR_ACCEPT = 1; + // Accept the game event if was reported by a majority + BEHAVIOR_ACCEPT_MAJORITY = 2; + // Only propose the game event (can be accepted in the UI by a human) + BEHAVIOR_PROPOSE_ONLY = 3; + // Only log the game event to the protocol + BEHAVIOR_LOG = 4; + // Silently ignore the game event + BEHAVIOR_IGNORE = 5; + } +} + +// The config for an auto referee +message AutoRefConfig { + // The game event behaviors for this auto referee + map game_event_behavior = 1; + + // Behaviors for the game events reported by this auto referee + enum Behavior { + // Not set or unknown + BEHAVIOR_UNKNOWN = 0; + // Accept the game event + BEHAVIOR_ACCEPT = 1; + // Log the game event + BEHAVIOR_LOG = 2; + // Silently ignore the game event + BEHAVIOR_IGNORE = 3; + } +} diff --git a/ssl_league_protobufs/proto/ssl_gc_game_event.proto b/ssl_league_protobufs/proto/ssl_gc_game_event.proto new file mode 100644 index 0000000..78b68cc --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_game_event.proto @@ -0,0 +1,596 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; + +import "ssl_gc_common.proto"; +import "ssl_gc_geometry.proto"; + +// GameEvent contains exactly one game event +// Each game event has optional and required fields. The required fields are mandatory to process the event. +// Some optional fields are only used for visualization, others are required to determine the ball placement position. +// If fields are missing that are required for the ball placement position, no ball placement command will be issued. +// Fields are marked optional to make testing and extending of the protocol easier. +// An autoRef should ideally set all fields, except if there are good reasons to not do so. +message GameEvent { + + // A globally unique id of the game event. + optional string id = 50; + + // The type of the game event. + optional Type type = 40; + + // The origins of this game event. + // Empty, if it originates from game controller. + // Contains autoRef name(s), if it originates from one or more autoRefs. + // Ignored if sent by autoRef to game controller. + repeated string origin = 41; + + // Unix timestamp in microseconds when the event was created. + optional uint64 created_timestamp = 49; + + // the event that occurred + oneof event { + + // Ball out of field events (stopping) + + BallLeftField ball_left_field_touch_line = 6; + BallLeftField ball_left_field_goal_line = 7; + AimlessKick aimless_kick = 11; + + // Stopping Fouls + + AttackerTooCloseToDefenseArea attacker_too_close_to_defense_area = 19; + DefenderInDefenseArea defender_in_defense_area = 31; + BoundaryCrossing boundary_crossing = 43; + KeeperHeldBall keeper_held_ball = 13; + BotDribbledBallTooFar bot_dribbled_ball_too_far = 17; + + BotPushedBot bot_pushed_bot = 24; + BotHeldBallDeliberately bot_held_ball_deliberately = 26; + BotTippedOver bot_tipped_over = 27; + BotDroppedParts bot_dropped_parts = 51; + + // Non-Stopping Fouls + + AttackerTouchedBallInDefenseArea attacker_touched_ball_in_defense_area = 15; + BotKickedBallTooFast bot_kicked_ball_too_fast = 18; + BotCrashUnique bot_crash_unique = 22; + BotCrashDrawn bot_crash_drawn = 21; + + // Fouls while ball out of play + + DefenderTooCloseToKickPoint defender_too_close_to_kick_point = 29; + BotTooFastInStop bot_too_fast_in_stop = 28; + BotInterferedPlacement bot_interfered_placement = 20; + + // Scoring goals + + Goal possible_goal = 39; + Goal goal = 8; + Goal invalid_goal = 44; + + // Other events + + AttackerDoubleTouchedBall attacker_double_touched_ball = 14; + PlacementSucceeded placement_succeeded = 5; + PenaltyKickFailed penalty_kick_failed = 45; + + NoProgressInGame no_progress_in_game = 2; + PlacementFailed placement_failed = 3; + MultipleCards multiple_cards = 32; + MultipleFouls multiple_fouls = 34; + BotSubstitution bot_substitution = 37; + ExcessiveBotSubstitution excessive_bot_substitution = 52; + TooManyRobots too_many_robots = 38; + ChallengeFlag challenge_flag = 46; + ChallengeFlagHandled challenge_flag_handled = 48; + EmergencyStop emergency_stop = 47; + + UnsportingBehaviorMinor unsporting_behavior_minor = 35; + UnsportingBehaviorMajor unsporting_behavior_major = 36; + + // Deprecated events + + // replaced by ready_to_continue flag + Prepared prepared = 1 [deprecated = true]; + // obsolete + IndirectGoal indirect_goal = 9 [deprecated = true]; + // replaced by the meta-information in the possible_goal event + ChippedGoal chipped_goal = 10 [deprecated = true]; + // obsolete + KickTimeout kick_timeout = 12 [deprecated = true]; + // rule removed + AttackerTouchedOpponentInDefenseArea attacker_touched_opponent_in_defense_area = 16 [deprecated = true]; + // obsolete + AttackerTouchedOpponentInDefenseArea attacker_touched_opponent_in_defense_area_skipped = 42 [deprecated = true]; + // obsolete + BotCrashUnique bot_crash_unique_skipped = 23 [deprecated = true]; + // can not be used as long as autoRefs do not judge pushing + BotPushedBot bot_pushed_bot_skipped = 25 [deprecated = true]; + // rule removed + DefenderInDefenseAreaPartially defender_in_defense_area_partially = 30 [deprecated = true]; + // the referee msg already indicates this + MultiplePlacementFailures multiple_placement_failures = 33 [deprecated = true]; + } + + // the ball left the field normally + message BallLeftField { + // the team that last touched the ball + required Team by_team = 1; + // the bot that last touched the ball + optional uint32 by_bot = 2; + // the location where the ball left the field [m] + optional Vector2 location = 3; + } + // the ball left the field via goal line and a team committed an aimless kick + message AimlessKick { + // the team that last touched the ball + required Team by_team = 1; + // the bot that last touched the ball + optional uint32 by_bot = 2; + // the location where the ball left the field [m] + optional Vector2 location = 3; + // the location where the ball was last touched [m] + optional Vector2 kick_location = 4; + } + // a team shot a goal + message Goal { + // the team that scored the goal + required Team by_team = 1; + // the team that shot the goal (different from by_team for own goals) + optional Team kicking_team = 6; + // the bot that shot the goal + optional uint32 kicking_bot = 2; + // the location where the ball entered the goal [m] + optional Vector2 location = 3; + // the location where the ball was kicked (for deciding if this was a valid goal) [m] + optional Vector2 kick_location = 4; + // the maximum height the ball reached during the goal kick (for deciding if this was a valid goal) [m] + optional float max_ball_height = 5; + // number of robots of scoring team when the ball entered the goal (for deciding if this was a valid goal) + optional uint32 num_robots_by_team = 7; + // The UNIX timestamp [μs] when the scoring team last touched the ball + optional uint64 last_touch_by_team = 8; + // An additional message with e.g. a reason for invalid goals + optional string message = 9; + } + // the ball entered the goal directly during an indirect free kick + message IndirectGoal { + // the team that tried to shoot the goal + required Team by_team = 1; + // the bot that kicked the ball - at least the team must be set + optional uint32 by_bot = 2; + // the location where the ball entered the goal [m] + optional Vector2 location = 3; + // the location where the ball was kicked [m] + optional Vector2 kick_location = 4; + } + // the ball entered the goal, but was initially chipped + message ChippedGoal { + // the team that tried to shoot the goal + required Team by_team = 1; + // the bot that kicked the ball + optional uint32 by_bot = 2; + // the location where the ball entered the goal [m] + optional Vector2 location = 3; + // the location where the ball was kicked [m] + optional Vector2 kick_location = 4; + // the maximum height [m] of the ball, before it entered the goal and since the last kick [m] + optional float max_ball_height = 5; + } + // a bot moved too fast while the game was stopped + message BotTooFastInStop { + // the team that found guilty + required Team by_team = 1; + // the bot that was too fast + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the bot speed [m/s] + optional float speed = 4; + } + // a bot of the defending team got too close to the kick point during a free kick + message DefenderTooCloseToKickPoint { + // the team that was found guilty + required Team by_team = 1; + // the bot that violates the distance to the kick point + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the distance [m] from bot to the kick point (including the minimum radius) + optional float distance = 4; + } + // two robots crashed into each other with similar speeds + message BotCrashDrawn { + // the bot of the yellow team + optional uint32 bot_yellow = 1; + // the bot of the blue team + optional uint32 bot_blue = 2; + // the location of the crash (center between both bots) [m] + optional Vector2 location = 3; + // the calculated crash speed [m/s] of the two bots + optional float crash_speed = 4; + // the difference [m/s] of the velocity of the two bots + optional float speed_diff = 5; + // the angle [rad] in the range [0, π] of the bot velocity vectors + // an angle of 0 rad ( 0°) means, the bots barely touched each other + // an angle of π rad (180°) means, the bots crashed frontal into each other + optional float crash_angle = 6; + } + // two robots crashed into each other and one team was found guilty to due significant speed difference + message BotCrashUnique { + // the team that caused the crash + required Team by_team = 1; + // the bot that caused the crash + optional uint32 violator = 2; + // the bot of the opposite team that was involved in the crash + optional uint32 victim = 3; + // the location of the crash (center between both bots) [m] + optional Vector2 location = 4; + // the calculated crash speed vector [m/s] of the two bots + optional float crash_speed = 5; + // the difference [m/s] of the velocity of the two bots + optional float speed_diff = 6; + // the angle [rad] in the range [0, π] of the bot velocity vectors + // an angle of 0 rad ( 0°) means, the bots barely touched each other + // an angle of π rad (180°) means, the bots crashed frontal into each other + optional float crash_angle = 7; + } + // a bot pushed another bot over a significant distance + message BotPushedBot { + // the team that pushed the other team + required Team by_team = 1; + // the bot that pushed the other bot + optional uint32 violator = 2; + // the bot of the opposite team that was pushed + optional uint32 victim = 3; + // the location of the push (center between both bots) [m] + optional Vector2 location = 4; + // the pushed distance [m] + optional float pushed_distance = 5; + } + // a bot tipped over + message BotTippedOver { + // the team that found guilty + required Team by_team = 1; + // the bot that tipped over + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the location of the ball at the moment when this foul occurred [m] + optional Vector2 ball_location = 4; + } + // a bot dropped parts + message BotDroppedParts { + // the team that found guilty + required Team by_team = 1; + // the bot that dropped the parts + optional uint32 by_bot = 2; + // the location where the parts were dropped [m] + optional Vector2 location = 3; + // the location of the ball at the moment when this foul occurred [m] + optional Vector2 ball_location = 4; + } + // a defender other than the keeper was fully located inside its own defense and touched the ball + message DefenderInDefenseArea { + // the team that found guilty + required Team by_team = 1; + // the bot that is inside the penalty area + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the distance [m] from bot case to the nearest point outside the defense area + optional float distance = 4; + } + // a defender other than the keeper was partially located inside its own defense area and touched the ball + message DefenderInDefenseAreaPartially { + // the team that found guilty + required Team by_team = 1; + // the bot that is partially inside the penalty area + optional uint32 by_bot = 2; + // the location of the bot + optional Vector2 location = 3; + // the distance [m] that the bot is inside the penalty area + optional float distance = 4; + // the location of the ball at the moment when this foul occurred [m] + optional Vector2 ball_location = 5; + } + // an attacker touched the ball inside the opponent defense area + message AttackerTouchedBallInDefenseArea { + // the team that found guilty + required Team by_team = 1; + // the bot that is inside the penalty area + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the distance [m] that the bot is inside the penalty area + optional float distance = 4; + } + // a bot kicked the ball too fast + message BotKickedBallTooFast { + // the team that found guilty + required Team by_team = 1; + // the bot that kicked too fast + optional uint32 by_bot = 2; + // the location of the ball at the time of the highest speed [m] + optional Vector2 location = 3; + // the absolute initial ball speed (kick speed) [m/s] + optional float initial_ball_speed = 4; + // was the ball chipped? + optional bool chipped = 5; + } + // a bot dribbled to ball too far + message BotDribbledBallTooFar { + // the team that found guilty + required Team by_team = 1; + // the bot that dribbled too far + optional uint32 by_bot = 2; + // the location where the dribbling started [m] + optional Vector2 start = 3; + // the location where the maximum dribbling distance was reached [m] + optional Vector2 end = 4; + } + // an attacker touched the opponent robot inside defense area + message AttackerTouchedOpponentInDefenseArea { + // the team that found guilty + required Team by_team = 1; + // the bot that touched the opponent robot + optional uint32 by_bot = 2; + // the bot of the opposite team that was touched + optional uint32 victim = 4; + // the location of the contact point between both bots [m] + optional Vector2 location = 3; + } + // an attacker touched the ball multiple times when it was not allowed to + message AttackerDoubleTouchedBall { + // the team that found guilty + required Team by_team = 1; + // the bot that touched the ball twice + optional uint32 by_bot = 2; + // the location of the ball when it was first touched [m] + optional Vector2 location = 3; + } + // an attacker was located too near to the opponent defense area during stop or free kick + message AttackerTooCloseToDefenseArea { + // the team that found guilty + required Team by_team = 1; + // the bot that is too close to the defense area + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + // the distance [m] of the bot to the penalty area + optional float distance = 4; + // the location of the ball at the moment when this foul occurred [m] + optional Vector2 ball_location = 5; + } + // a bot held the ball for too long + message BotHeldBallDeliberately { + // the team that found guilty + required Team by_team = 1; + // the bot that holds the ball + optional uint32 by_bot = 2; + // the location of the ball [m] + optional Vector2 location = 3; + // the duration [s] that the bot hold the ball + optional float duration = 4; + } + // a bot interfered the ball placement of the other team + message BotInterferedPlacement { + // the team that found guilty + required Team by_team = 1; + // the bot that interfered the placement + optional uint32 by_bot = 2; + // the location of the bot [m] + optional Vector2 location = 3; + } + // a team collected multiple yellow cards + message MultipleCards { + // the team that received multiple yellow cards + required Team by_team = 1; + } + // a team collected multiple fouls, which results in a yellow card + message MultipleFouls { + // the team that collected multiple fouls + required Team by_team = 1; + // the list of game events that caused the multiple fouls + repeated GameEvent caused_game_events = 2; + } + // a team failed to place the ball multiple times in a row + message MultiplePlacementFailures { + // the team that failed multiple times + required Team by_team = 1; + } + // timeout waiting for the attacking team to perform the free kick + message KickTimeout { + // the team that that should have kicked + required Team by_team = 1; + // the location of the ball [m] + optional Vector2 location = 2; + // the time [s] that was waited + optional float time = 3; + } + // game was stuck + message NoProgressInGame { + // the location of the ball + optional Vector2 location = 1; + // the time [s] that was waited + optional float time = 2; + } + // ball placement failed + message PlacementFailed { + // the team that failed + required Team by_team = 1; + // the remaining distance [m] from ball to placement position + optional float remaining_distance = 2; + // the distance [m] of the nearest own robot to the ball + optional float nearest_own_bot_distance = 3; + } + // a team was found guilty for minor unsporting behavior + message UnsportingBehaviorMinor { + // the team that found guilty + required Team by_team = 1; + // an explanation of the situation and decision + required string reason = 2; + } + // a team was found guilty for major unsporting behavior + message UnsportingBehaviorMajor { + // the team that found guilty + required Team by_team = 1; + // an explanation of the situation and decision + required string reason = 2; + } + // a keeper held the ball in its defense area for too long + message KeeperHeldBall { + // the team that found guilty + required Team by_team = 1; + // the location of the ball [m] + optional Vector2 location = 2; + // the duration [s] that the keeper hold the ball + optional float duration = 3; + } + // a team successfully placed the ball + message PlacementSucceeded { + // the team that did the placement + required Team by_team = 1; + // the time [s] taken for placing the ball + optional float time_taken = 2; + // the distance [m] between placement location and actual ball position + optional float precision = 3; + // the distance [m] between the initial ball location and the placement position + optional float distance = 4; + } + // both teams are prepared - all conditions are met to continue (with kickoff or penalty kick) + message Prepared { + // the time [s] taken for preparing + optional float time_taken = 1; + } + // bots are being substituted by a team + message BotSubstitution { + // the team that substitutes robots + required Team by_team = 1; + } + // A foul for excessive bot substitutions + message ExcessiveBotSubstitution { + // the team that substitutes robots + required Team by_team = 1; + } + // A challenge flag, requested by a team previously, is flagged + message ChallengeFlag { + // the team that requested the challenge flag + required Team by_team = 1; + } + // A challenge, flagged recently, has been handled by the referee + message ChallengeFlagHandled { + // the team that requested the challenge flag + required Team by_team = 1; + // the challenge was accepted by the referee + required bool accepted = 2; + } + // An emergency stop, requested by team previously, occurred + message EmergencyStop { + // the team that substitutes robots + required Team by_team = 1; + } + // a team has too many robots on the field + message TooManyRobots { + // the team that has too many robots + required Team by_team = 1; + // number of robots allowed at the moment + optional int32 num_robots_allowed = 2; + // number of robots currently on the field + optional int32 num_robots_on_field = 3; + // the location of the ball at the moment when this foul occurred [m] + optional Vector2 ball_location = 4; + } + // a robot chipped the ball over the field boundary out of the playing surface + message BoundaryCrossing { + // the team that has too many robots + required Team by_team = 1; + // the location of the ball [m] + optional Vector2 location = 2; + } + // the penalty kick failed (by time or by keeper) + message PenaltyKickFailed { + // the team that last touched the ball + required Team by_team = 1; + // the location of the ball at the moment of this event [m] + optional Vector2 location = 2; + // an explanation of the failure + optional string reason = 3; + } + + enum Type { + UNKNOWN_GAME_EVENT_TYPE = 0; + + // Ball out of field events (stopping) + + BALL_LEFT_FIELD_TOUCH_LINE = 6; // triggered by autoRef + BALL_LEFT_FIELD_GOAL_LINE = 7; // triggered by autoRef + AIMLESS_KICK = 11; // triggered by autoRef + + // Stopping Fouls + + ATTACKER_TOO_CLOSE_TO_DEFENSE_AREA = 19; // triggered by autoRef + DEFENDER_IN_DEFENSE_AREA = 31; // triggered by autoRef + BOUNDARY_CROSSING = 41; // triggered by autoRef + KEEPER_HELD_BALL = 13; // triggered by GC + BOT_DRIBBLED_BALL_TOO_FAR = 17; // triggered by autoRef + + BOT_PUSHED_BOT = 24; // triggered by human ref + BOT_HELD_BALL_DELIBERATELY = 26; // triggered by human ref + BOT_TIPPED_OVER = 27; // triggered by human ref + BOT_DROPPED_PARTS = 47; // triggered by human ref + + // Non-Stopping Fouls + + ATTACKER_TOUCHED_BALL_IN_DEFENSE_AREA = 15; // triggered by autoRef + BOT_KICKED_BALL_TOO_FAST = 18; // triggered by autoRef + BOT_CRASH_UNIQUE = 22; // triggered by autoRef + BOT_CRASH_DRAWN = 21; // triggered by autoRef + + // Fouls while ball out of play + + DEFENDER_TOO_CLOSE_TO_KICK_POINT = 29; // triggered by autoRef + BOT_TOO_FAST_IN_STOP = 28; // triggered by autoRef + BOT_INTERFERED_PLACEMENT = 20; // triggered by autoRef + EXCESSIVE_BOT_SUBSTITUTION = 48; // triggered by GC + + // Scoring goals + + POSSIBLE_GOAL = 39; // triggered by autoRef + GOAL = 8; // triggered by GC + INVALID_GOAL = 42; // triggered by GC + + // Other events + + ATTACKER_DOUBLE_TOUCHED_BALL = 14; // triggered by autoRef + PLACEMENT_SUCCEEDED = 5; // triggered by autoRef + PENALTY_KICK_FAILED = 43; // triggered by GC and autoRef + + NO_PROGRESS_IN_GAME = 2; // triggered by GC + PLACEMENT_FAILED = 3; // triggered by GC + MULTIPLE_CARDS = 32; // triggered by GC + MULTIPLE_FOULS = 34; // triggered by GC + BOT_SUBSTITUTION = 37; // triggered by GC + TOO_MANY_ROBOTS = 38; // triggered by GC + CHALLENGE_FLAG = 44; // triggered by GC + CHALLENGE_FLAG_HANDLED = 46; // triggered by GC + EMERGENCY_STOP = 45; // triggered by GC + + UNSPORTING_BEHAVIOR_MINOR = 35; // triggered by human ref + UNSPORTING_BEHAVIOR_MAJOR = 36; // triggered by human ref + + // Deprecated events + + PREPARED = 1 [deprecated = true]; + INDIRECT_GOAL = 9 [deprecated = true]; + CHIPPED_GOAL = 10 [deprecated = true]; + KICK_TIMEOUT = 12 [deprecated = true]; + ATTACKER_TOUCHED_OPPONENT_IN_DEFENSE_AREA = 16 [deprecated = true]; + ATTACKER_TOUCHED_OPPONENT_IN_DEFENSE_AREA_SKIPPED = 40 [deprecated = true]; + BOT_CRASH_UNIQUE_SKIPPED = 23 [deprecated = true]; + BOT_PUSHED_BOT_SKIPPED = 25 [deprecated = true]; + DEFENDER_IN_DEFENSE_AREA_PARTIALLY = 30 [deprecated = true]; + MULTIPLE_PLACEMENT_FAILURES = 33 [deprecated = true]; + } +} diff --git a/ssl_league_protobufs/proto/ssl_gc_geometry.proto b/ssl_league_protobufs/proto/ssl_gc_geometry.proto new file mode 100644 index 0000000..47f04f6 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_geometry.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/geom"; + +// A vector with two dimensions +message Vector2 { + required float x = 1; + required float y = 2; +} + +// A vector with three dimensions +message Vector3 { + required float x = 1; + required float y = 2; + required float z = 3; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_rcon.proto b/ssl_league_protobufs/proto/ssl_gc_rcon.proto new file mode 100644 index 0000000..91d9b5c --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_rcon.proto @@ -0,0 +1,38 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; + +// a reply that is sent by the controller for each request from teams or autoRefs +message ControllerReply { + // status_code is an optional code that indicates the result of the last request + optional StatusCode status_code = 1; + // reason is an optional explanation of the status code + optional string reason = 2; + // next_token must be send with the next request, if secure communication is used + // the token is used to avoid replay attacks + // the token is always present in the very first message before the registration starts + // the token is not present, if secure communication is not used + optional string next_token = 3; + // verification indicates if the last request could be verified (secure communication) + optional Verification verification = 4; + + enum StatusCode { + UNKNOWN_STATUS_CODE = 0; + OK = 1; + REJECTED = 2; + } + + enum Verification { + UNKNOWN_VERIFICATION = 0; + VERIFIED = 1; + UNVERIFIED = 2; + } +} + +// Signature can be added to a request to let it be verfied by the controller +message Signature { + // the token that was received with the last controller reply + required string token = 1; + // the PKCS1v15 signature of this message + required bytes pkcs1v15 = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_rcon_autoref.proto b/ssl_league_protobufs/proto/ssl_gc_rcon_autoref.proto new file mode 100644 index 0000000..56d2e7a --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_rcon_autoref.proto @@ -0,0 +1,32 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; + +import "ssl_gc_game_event.proto"; +import "ssl_gc_rcon.proto"; + +// AutoRefRegistration is the first message that a client must send to the controller to identify itself +message AutoRefRegistration { + // identifier is a unique name of the client + required string identifier = 1; + // signature can optionally be specified to enable secure communication + optional Signature signature = 2; +} + +// AutoRefToController is the wrapper message for all subsequent messages from the autoRef to the controller +message AutoRefToController { + // reserve fields for removed fields + reserved 3, 4; + // signature can optionally be specified to enable secure communication + optional Signature signature = 1; + // game_event is an optional event that the autoRef detected during the game + optional GameEvent game_event = 2; +} + +// ControllerToAutoRef is the wrapper message for all messages from controller to autoRef +message ControllerToAutoRef { + oneof msg { + // a reply from the controller + ControllerReply controller_reply = 1; + } +} \ No newline at end of file diff --git a/ssl_league_protobufs/proto/ssl_gc_rcon_remotecontrol.proto b/ssl_league_protobufs/proto/ssl_gc_rcon_remotecontrol.proto new file mode 100644 index 0000000..4e2717e --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_rcon_remotecontrol.proto @@ -0,0 +1,117 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; + +import "ssl_gc_common.proto"; +import "ssl_gc_rcon.proto"; + +// a registration that must be send by the remote control to the controller as the very first message +message RemoteControlRegistration { + // the team to be controlled + required Team team = 1; + // signature can optionally be specified to enable secure communication + optional Signature signature = 2; +} + +// wrapper for all messages from the remote control to the controller +message RemoteControlToController { + // signature can optionally be specified to enable secure communication + optional Signature signature = 1; + + oneof msg { + // send a ping to the GC to test if the connection is still open. + // the value is ignored and a reply is sent back + Request request = 2; + + // request a new desired keeper id + int32 desired_keeper = 3; + + // true: request to substitute a robot at the next possibility + // false: cancel request + bool request_robot_substitution = 4; + + // true: request a timeout with the next stoppage + // false: cancel the request + bool request_timeout = 5; + + // true: initiate an emergency stop + // false: cancel the request + bool request_emergency_stop = 6; + } + + enum Request { + UNKNOWN = 0; + // Ping the GC to test the connection. The GC will respond with OK and the current team state + PING = 1; + // Raise a challenge flag (this is not revocable) + CHALLENGE_FLAG = 2; + // Stop an ongoing timeout + STOP_TIMEOUT = 3; + } +} + +// wrapper for all messages from controller to a team's computer +message ControllerToRemoteControl { + // a reply from the controller + optional ControllerReply controller_reply = 1; + + // current team state + optional RemoteControlTeamState state = 2; +} + +// Current team state from Controller for remote control +message RemoteControlTeamState { + // the team that is controlled + optional Team team = 12; + + // list of all currently available request types that can be made + repeated RemoteControlRequestType available_requests = 1; + + // list of all currently active request types that are pending + repeated RemoteControlRequestType active_requests = 2; + + // currently set keeper id + optional int32 keeper_id = 3; + + // number of seconds till emergency stop is executed + // zero, if no emergency stop requested + optional float emergency_stop_in = 4; + + // number of timeouts left for the team + optional int32 timeouts_left = 5; + + // number of seconds left for timeout for the team + optional float timeout_time_left = 10; + + // number of challenge flags left for the team + optional int32 challenge_flags_left = 6; + + // max number of robots currently allowed + optional int32 max_robots = 7; + + // current number of robots visible on field + optional int32 robots_on_field = 9; + + // list of due times for each active yellow card (in seconds) + repeated float yellow_cards_due = 8; + + // if true, team is allowed to substitute robots + optional bool can_substitute_robot = 11; + + // number of bot substitutions left by the team in this halftime + optional uint32 bot_substitutions_left = 13; + + // number of seconds left for current bot substitution + optional float bot_substitution_time_left = 14; +} + +// All possible request types that the remote control can make +enum RemoteControlRequestType { + UNKNOWN_REQUEST_TYPE = 0; + EMERGENCY_STOP = 1; + ROBOT_SUBSTITUTION = 2; + TIMEOUT = 3; + CHALLENGE_FLAG = 4; + CHANGE_KEEPER_ID = 5; + STOP_TIMEOUT = 6; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_rcon_team.proto b/ssl_league_protobufs/proto/ssl_gc_rcon_team.proto new file mode 100644 index 0000000..a3f0772 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_rcon_team.proto @@ -0,0 +1,56 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; + +import "ssl_gc_rcon.proto"; +import "ssl_gc_common.proto"; + +// a registration that must be send by teams to the controller as the very first message +message TeamRegistration { + // the exact team name as published by the game-controller + required string team_name = 1; + // signature can optionally be specified to enable secure communication + optional Signature signature = 2; + // the team (relevant only if a team plays against itself) + optional Team team = 3; +} + +// wrapper for all messages from a team's computer to the controller +message TeamToController { + // signature can optionally be specified to enable secure communication + optional Signature signature = 1; + + oneof msg { + // request a new desired keeper id + int32 desired_keeper = 2; + // response to an advantage choice request + AdvantageChoice advantage_choice = 3; + // request to substitute a robot at the next possibility + bool substitute_bot = 4; + // send a ping to the GC to test if the connection is still open. + // the value is ignored and a reply is sent back + bool ping = 5; + } +} + +// the current advantage choice of the team +// the choice is valid until another choice is received +// if the team disconnects, the choice is reset to its default (STOP) +// teams may either send their current choice continuously or only on change +enum AdvantageChoice { + // stop the game + STOP = 0; + // keep the game running + CONTINUE = 1; +} + +// wrapper for all messages from controller to a team's computer +message ControllerToTeam { + // reserve obsolete field ids + reserved 2; + + oneof msg { + // a reply from the controller + ControllerReply controller_reply = 1; + } +} diff --git a/ssl_league_protobufs/proto/ssl_gc_referee_message.proto b/ssl_league_protobufs/proto/ssl_gc_referee_message.proto new file mode 100644 index 0000000..d71a4b1 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_referee_message.proto @@ -0,0 +1,238 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; + +import "ssl_gc_game_event.proto"; + +// Each UDP packet contains one of these messages. +message Referee { + // A random UUID of the source that is kept constant at the source while running + // If multiple sources are broadcasting to the same network, this id can be used to identify individual sources + optional string source_identifier = 18; + + // The match type is a meta information about the current match that helps to process the logs after a competition + optional MatchType match_type = 19 [default = UNKNOWN_MATCH]; + + // The UNIX timestamp when the packet was sent, in microseconds. + // Divide by 1,000,000 to get a time_t. + required uint64 packet_timestamp = 1; + + // These are the "coarse" stages of the game. + enum Stage { + // The first half is about to start. + // A kickoff is called within this stage. + // This stage ends with the NORMAL_START. + NORMAL_FIRST_HALF_PRE = 0; + // The first half of the normal game, before half time. + NORMAL_FIRST_HALF = 1; + // Half time between first and second halves. + NORMAL_HALF_TIME = 2; + // The second half is about to start. + // A kickoff is called within this stage. + // This stage ends with the NORMAL_START. + NORMAL_SECOND_HALF_PRE = 3; + // The second half of the normal game, after half time. + NORMAL_SECOND_HALF = 4; + // The break before extra time. + EXTRA_TIME_BREAK = 5; + // The first half of extra time is about to start. + // A kickoff is called within this stage. + // This stage ends with the NORMAL_START. + EXTRA_FIRST_HALF_PRE = 6; + // The first half of extra time. + EXTRA_FIRST_HALF = 7; + // Half time between first and second extra halves. + EXTRA_HALF_TIME = 8; + // The second half of extra time is about to start. + // A kickoff is called within this stage. + // This stage ends with the NORMAL_START. + EXTRA_SECOND_HALF_PRE = 9; + // The second half of extra time. + EXTRA_SECOND_HALF = 10; + // The break before penalty shootout. + PENALTY_SHOOTOUT_BREAK = 11; + // The penalty shootout. + PENALTY_SHOOTOUT = 12; + // The game is over. + POST_GAME = 13; + } + required Stage stage = 2; + + // The number of microseconds left in the stage. + // The following stages have this value; the rest do not: + // NORMAL_FIRST_HALF + // NORMAL_HALF_TIME + // NORMAL_SECOND_HALF + // EXTRA_TIME_BREAK + // EXTRA_FIRST_HALF + // EXTRA_HALF_TIME + // EXTRA_SECOND_HALF + // PENALTY_SHOOTOUT_BREAK + // + // If the stage runs over its specified time, this value + // becomes negative. + optional sint64 stage_time_left = 3; + + // These are the "fine" states of play on the field. + enum Command { + // All robots should completely stop moving. + HALT = 0; + // Robots must keep 50 cm from the ball. + STOP = 1; + // A prepared kickoff or penalty may now be taken. + NORMAL_START = 2; + // The ball is dropped and free for either team. + FORCE_START = 3; + // The yellow team may move into kickoff position. + PREPARE_KICKOFF_YELLOW = 4; + // The blue team may move into kickoff position. + PREPARE_KICKOFF_BLUE = 5; + // The yellow team may move into penalty position. + PREPARE_PENALTY_YELLOW = 6; + // The blue team may move into penalty position. + PREPARE_PENALTY_BLUE = 7; + // The yellow team may take a direct free kick. + DIRECT_FREE_YELLOW = 8; + // The blue team may take a direct free kick. + DIRECT_FREE_BLUE = 9; + // The yellow team may take an indirect free kick. + INDIRECT_FREE_YELLOW = 10 [deprecated = true]; + // The blue team may take an indirect free kick. + INDIRECT_FREE_BLUE = 11 [deprecated = true]; + // The yellow team is currently in a timeout. + TIMEOUT_YELLOW = 12; + // The blue team is currently in a timeout. + TIMEOUT_BLUE = 13; + // The yellow team just scored a goal. + // For information only. + // Deprecated: Use the score field from the team infos instead. That way, you can also detect revoked goals. + GOAL_YELLOW = 14 [deprecated = true]; + // The blue team just scored a goal. See also GOAL_YELLOW. + GOAL_BLUE = 15 [deprecated = true]; + // Equivalent to STOP, but the yellow team must pick up the ball and + // drop it in the Designated Position. + BALL_PLACEMENT_YELLOW = 16; + // Equivalent to STOP, but the blue team must pick up the ball and drop + // it in the Designated Position. + BALL_PLACEMENT_BLUE = 17; + } + required Command command = 4; + + // The number of commands issued since startup (mod 2^32). + required uint32 command_counter = 5; + + // The UNIX timestamp when the command was issued, in microseconds. + // This value changes only when a new command is issued, not on each packet. + required uint64 command_timestamp = 6; + + // Information about a single team. + message TeamInfo { + // The team's name (empty string if operator has not typed anything). + required string name = 1; + // The number of goals scored by the team during normal play and overtime. + required uint32 score = 2; + // The number of red cards issued to the team since the beginning of the game. + required uint32 red_cards = 3; + // The amount of time (in microseconds) left on each yellow card issued to the team. + // If no yellow cards are issued, this array has no elements. + // Otherwise, times are ordered from smallest to largest. + repeated uint32 yellow_card_times = 4 [packed = true]; + // The total number of yellow cards ever issued to the team. + required uint32 yellow_cards = 5; + // The number of timeouts this team can still call. + // If in a timeout right now, that timeout is excluded. + required uint32 timeouts = 6; + // The number of microseconds of timeout this team can use. + required uint32 timeout_time = 7; + // The pattern number of this team's goalkeeper. + required uint32 goalkeeper = 8; + // The total number of countable fouls that act towards yellow cards + optional uint32 foul_counter = 9; + // The number of consecutive ball placement failures of this team + optional uint32 ball_placement_failures = 10; + // Indicate if the team is able and allowed to place the ball + optional bool can_place_ball = 12; + // The maximum number of bots allowed on the field based on division and cards + optional uint32 max_allowed_bots = 13; + // The team has submitted an intent to substitute one or more robots at the next chance + optional bool bot_substitution_intent = 14; + // Indicate if the team reached the maximum allowed ball placement failures and is thus not allowed to place the ball anymore + optional bool ball_placement_failures_reached = 15; + // The team is allowed to substitute one or more robots currently + optional bool bot_substitution_allowed = 16; + // The number of bot substitutions left by the team in this halftime + optional uint32 bot_substitutions_left = 17; + // The number of microseconds left for current bot substitution + optional uint32 bot_substitution_time_left = 18; + } + + // Information about the two teams. + required TeamInfo yellow = 7; + required TeamInfo blue = 8; + + // The coordinates of the Designated Position. These are measured in + // millimetres and correspond to SSL-Vision coordinates. These fields are + // always either both present (in the case of a ball placement command) or + // both absent (in the case of any other command). + message Point { + required float x = 1; + required float y = 2; + } + optional Point designated_position = 9; + + // Information about the direction of play. + // True, if the blue team will have it's goal on the positive x-axis of the ssl-vision coordinate system. + // Obviously, the yellow team will play on the opposite half. + optional bool blue_team_on_positive_half = 10; + + // The game event that caused the referee command. + // deprecated in favor of game_events. + // optional Game_Event game_event = 11 [deprecated = true]; + reserved 11; + + // The command that will be issued after the current stoppage and ball placement to continue the game. + optional Command next_command = 12; + + // All game events that were detected since the last RUNNING state. + // Will be cleared as soon as the game is continued. + reserved 13; + repeated GameEvent game_events = 16; + + // All proposed game events that were detected since the last RUNNING state. + reserved 14; + repeated GameEventProposalGroup game_event_proposals = 17; + + // The time in microseconds that is remaining until the current action times out + // The time will not be reset. It can get negative. + // An autoRef would raise an appropriate event, if the time gets negative. + // Possible actions where this time is relevant: + // * free kicks + // * kickoff, penalty kick, force start + // * ball placement + optional int64 current_action_time_remaining = 15; + + // A message that can be displayed to the spectators, like a reason for a stoppage. + optional string status_message = 20; +} + +// List of matching proposals +message GameEventProposalGroup { + // Unique ID of this group + optional string id = 3; + // The proposed game events + repeated GameEvent game_events = 1; + // Whether the proposal group was accepted + optional bool accepted = 2; +} + +// MatchType is a meta information about the current match for easier log processing +enum MatchType { + // not set + UNKNOWN_MATCH = 0; + // match is part of the group phase + GROUP_PHASE = 1; + // match is part of the elimination phase + ELIMINATION_PHASE = 2; + // a friendly match, not part of a tournament + FRIENDLY = 3; +} diff --git a/ssl_league_protobufs/proto/ssl_gc_state.proto b/ssl_league_protobufs/proto/ssl_gc_state.proto new file mode 100644 index 0000000..7a2a289 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_gc_state.proto @@ -0,0 +1,133 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; + +import "ssl_gc_common.proto"; +import "ssl_gc_geometry.proto"; +import "ssl_gc_game_event.proto"; +import "ssl_gc_referee_message.proto"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +message YellowCard { + optional uint32 id = 1; + optional GameEvent caused_by_game_event = 2; + optional google.protobuf.Duration time_remaining = 3; +} + +message RedCard { + optional uint32 id = 1; + optional GameEvent caused_by_game_event = 2; +} + +message Foul { + optional uint32 id = 1; + optional GameEvent caused_by_game_event = 2; + optional google.protobuf.Timestamp timestamp = 3; +} + +message Command { + required Type type = 1; + required Team for_team = 2; + + enum Type { + UNKNOWN = 0; + HALT = 1; + STOP = 2; + NORMAL_START = 3; + FORCE_START = 4; + DIRECT = 5; + reserved 6; // INDIRECT + KICKOFF = 7; + PENALTY = 8; + TIMEOUT = 9; + BALL_PLACEMENT = 10; + } +} + +message GameState { + required Type type = 1; + optional Team for_team = 2; + + enum Type { + UNKNOWN = 0; + HALT = 1; + STOP = 2; + RUNNING = 3; + FREE_KICK = 4; + KICKOFF = 5; + PENALTY = 6; + TIMEOUT = 7; + BALL_PLACEMENT = 8; + } +} + +message Proposal { + // The timestamp when the game event proposal occurred + optional google.protobuf.Timestamp timestamp = 1; + // The proposed game event. + optional GameEvent game_event = 2; +} + +message ProposalGroup { + // Unique ID of this group + optional string id = 4; + // The proposals in this group + repeated Proposal proposals = 1; + // Whether the proposal group was accepted + optional bool accepted = 2; + reserved 3; // uint32 id +} + +message TeamInfo { + optional string name = 1; + optional int32 goals = 2; + optional int32 goalkeeper = 3; + repeated YellowCard yellow_cards = 4; + repeated RedCard red_cards = 5; + optional int32 timeouts_left = 6; + optional google.protobuf.Duration timeout_time_left = 7; + optional bool on_positive_half = 8; + repeated Foul fouls = 9; + optional int32 ball_placement_failures = 10; + optional bool ball_placement_failures_reached = 11; + optional bool can_place_ball = 12; + optional int32 max_allowed_bots = 13; + optional google.protobuf.Timestamp requests_bot_substitution_since = 14; + optional google.protobuf.Timestamp requests_timeout_since = 15; + optional google.protobuf.Timestamp requests_emergency_stop_since = 16; + optional int32 challenge_flags = 17; + optional bool bot_substitution_allowed = 18; + optional int32 bot_substitutions_left = 19; + optional google.protobuf.Duration bot_substitution_time_left = 20; +} + +message State { + optional Referee.Stage stage = 1; + optional Command command = 2; + optional GameState game_state = 19; + optional google.protobuf.Duration stage_time_elapsed = 4; + optional google.protobuf.Duration stage_time_left = 5; + optional google.protobuf.Timestamp match_time_start = 6; + map team_state = 8; + optional Vector2 placement_pos = 9; + optional Command next_command = 10; + optional google.protobuf.Duration current_action_time_remaining = 12; + repeated GameEvent game_events = 13; + repeated ProposalGroup proposal_groups = 14; + optional Division division = 15; + reserved 16; + optional Team first_kickoff_team = 17; + optional MatchType match_type = 18; + optional google.protobuf.Timestamp ready_continue_time = 20; + optional ShootoutState shootout_state = 21; + optional string status_message = 22; + // The maximum number of bots per team (overwrites the division config) + optional int32 max_bots_per_team = 23; +} + +message ShootoutState { + optional Team next_team = 1; + map number_of_attempts = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_simulation_config.proto b/ssl_league_protobufs/proto/ssl_simulation_config.proto new file mode 100644 index 0000000..da8b6ae --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_config.proto @@ -0,0 +1,77 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +import "ssl_gc_common.proto"; +import "ssl_vision_geometry.proto"; +import "google/protobuf/any.proto"; + +// Movement limits for a robot +message RobotLimits { + // Max absolute speed-up acceleration [m/s^2] + optional float acc_speedup_absolute_max = 1; + // Max angular speed-up acceleration [rad/s^2] + optional float acc_speedup_angular_max = 2; + // Max absolute brake acceleration [m/s^2] + optional float acc_brake_absolute_max = 3; + // Max angular brake acceleration [rad/s^2] + optional float acc_brake_angular_max = 4; + // Max absolute velocity [m/s] + optional float vel_absolute_max = 5; + // Max angular velocity [rad/s] + optional float vel_angular_max = 6; +} + +// Robot wheel angle configuration +// all angles are relative to looking forward, +// all wheels / angles are clockwise +message RobotWheelAngles { + // Angle front right [rad] + required float front_right = 1; + // Angle back right [rad] + required float back_right = 2; + // Angle back left [rad] + required float back_left = 3; + // Angle front left [rad] + required float front_left = 4; +} + +// Specs of a robot +message RobotSpecs { + // Id of the robot + required RobotId id = 1; + // Robot radius [m] + optional float radius = 2 [default = 0.09]; + // Robot height [m] + optional float height = 3 [default = 0.15]; + // Robot mass [kg] + optional float mass = 4; + // Max linear kick speed [m/s] (unset = unlimited) + optional float max_linear_kick_speed = 7; + // Max chip kick speed [m/s] (unset = unlimited) + optional float max_chip_kick_speed = 8; + // Distance from robot center to dribbler [m] (implicitly defines the opening angle and dribbler width) + optional float center_to_dribbler = 9; + // Movement limits + optional RobotLimits limits = 10; + // Wheel angle configuration + optional RobotWheelAngles wheel_angles = 13; + // Custom robot spec for specific simulators (the protobuf files are managed by the simulators) + repeated google.protobuf.Any custom = 14; +} + +message RealismConfig { + // Custom config for specific simulators (the protobuf files are managed by the simulators) + repeated google.protobuf.Any custom = 1; +} + +// Change the simulator configuration +message SimulatorConfig { + // Update the geometry + optional SSL_GeometryData geometry = 1; + // Update the robot specs + repeated RobotSpecs robot_specs = 2; + // Update realism configuration + optional RealismConfig realism_config = 3; + // Change the vision publish port + optional uint32 vision_port = 4; +} \ No newline at end of file diff --git a/ssl_league_protobufs/proto/ssl_simulation_control.proto b/ssl_league_protobufs/proto/ssl_simulation_control.proto new file mode 100644 index 0000000..55f639e --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_control.proto @@ -0,0 +1,90 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +import "ssl_gc_common.proto"; +import "ssl_simulation_config.proto"; +import "ssl_simulation_error.proto"; + +// Teleport the ball to a new location and optionally set it to some velocity +message TeleportBall { + // x-coordinate [m] + optional float x = 1; + // y-coordinate [m] + optional float y = 2; + // z-coordinate (height) [m] + optional float z = 3; + // Velocity in x-direction [m/s] + optional float vx = 4; + // Velocity in y-direction [m/s] + optional float vy = 5; + // Velocity in z-direction [m/s] + optional float vz = 6; + // Teleport the ball safely to the target, for example by + // moving robots out of the way in case of collision and set speed of robots close-by to zero + optional bool teleport_safely = 7 [default = false]; + // Adapt the angular ball velocity such that the ball is rolling + optional bool roll = 8 [default = false]; + // Instead of teleporting the ball, apply some force to make sure + // the ball reaches the required position soon (velocity is ignored if true) + // WARNING: A command with by_force stays active (the move will take some time) + // until cancled by another TeleportBall command with by_force = false. + // To avoid teleporting the ball at the end and resetting its current spin, + // do not set any of the optional fields in this message to end the force without triggering + // an additional teleportation + optional bool by_force = 9 [ default = false]; +} + +// Teleport a robot to some location and give it a velocity +message TeleportRobot { + // Robot id to teleport + required RobotId id = 1; + // x-coordinate [m] + optional float x = 2; + // y-coordinate [m] + optional float y = 3; + // Orientation [rad], measured from the x-axis counter-clockwise + optional float orientation = 4; + // Global velocity [m/s] towards x-axis + optional float v_x = 5 [default = 0]; + // Global velocity [m/s] towards y-axis + optional float v_y = 6 [default = 0]; + // Angular velocity [rad/s] + optional float v_angular = 7 [default = 0]; + // Robot should be present on the field? + // true -> robot will be added, if it does not exist yet + // false -> robot will be removed, if it is present + optional bool present = 8; + // Instead of teleporting, apply some force to make sure + // the robot reaches the required position soon (velocity is ignored if true) + // WARNING: A command with by_force stays active (the move will take some time) + // until cancled by another TeleportRobot command for the same bot with by_force = false. + // To avoid teleporting at the end, + // do not set any of the optional fields in this message + // to end the force without triggering + // an additional teleportation + optional bool by_force = 9 [ default = false]; +} + +// Control the simulation +message SimulatorControl { + // Teleport the ball + optional TeleportBall teleport_ball = 1; + // Teleport robots + repeated TeleportRobot teleport_robot = 2; + // Change the simulation speed + optional float simulation_speed = 3; +} + +// Command from the connected client to the simulator +message SimulatorCommand { + // Control the simulation + optional SimulatorControl control = 1; + // Configure the simulation + optional SimulatorConfig config = 2; +} + +// Response of the simulator to the connected client +message SimulatorResponse { + // List of errors, like using unsupported features + repeated SimulatorError errors = 1; +} diff --git a/ssl_league_protobufs/proto/ssl_simulation_error.proto b/ssl_league_protobufs/proto/ssl_simulation_error.proto new file mode 100644 index 0000000..92c732f --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_error.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +// Errors in the simulator +message SimulatorError { + // Unique code of the error for automatic handling on client side + optional string code = 1; + // Human readable description of the error + optional string message = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_simulation_robot_control.proto b/ssl_league_protobufs/proto/ssl_simulation_robot_control.proto new file mode 100644 index 0000000..981b843 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_robot_control.proto @@ -0,0 +1,66 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +// Full command for a single robot +message RobotCommand { + // Id of the robot + required uint32 id = 1; + // Movement command + optional RobotMoveCommand move_command = 2; + // Absolute (3 dimensional) kick speed [m/s] + optional float kick_speed = 3; + // Kick angle [degree] (defaults to 0 degrees for a straight kick) + optional float kick_angle = 4 [default = 0]; + // Dribbler speed in rounds per minute [rpm] + optional float dribbler_speed = 5; +} + +// Wrapper for different kinds of movement commands +message RobotMoveCommand { + oneof command { + // Move with wheel velocities + MoveWheelVelocity wheel_velocity = 1; + // Move with local velocity + MoveLocalVelocity local_velocity = 2; + // Move with global velocity + MoveGlobalVelocity global_velocity = 3; + } +} + +// Move robot with wheel velocities +message MoveWheelVelocity { + // Velocity [m/s] of front right wheel + required float front_right = 1; + // Velocity [m/s] of back right wheel + required float back_right = 2; + // Velocity [m/s] of back left wheel + required float back_left = 3; + // Velocity [m/s] of front left wheel + required float front_left = 4; +} + +// Move robot with local velocity +message MoveLocalVelocity { + // Velocity forward [m/s] (towards the dribbler) + required float forward = 1; + // Velocity to the left [m/s] + required float left = 2; + // Angular velocity counter-clockwise [rad/s] + required float angular = 3; +} + +// Move robot with global velocity +message MoveGlobalVelocity { + // Velocity on x-axis of the field [m/s] + required float x = 1; + // Velocity on y-axis of the field [m/s] + required float y = 2; + // Angular velocity counter-clockwise [rad/s] + required float angular = 3; +} + +// Command from the connected client to the simulator +message RobotControl { + // Control the robots + repeated RobotCommand robot_commands = 1; +} diff --git a/ssl_league_protobufs/proto/ssl_simulation_robot_feedback.proto b/ssl_league_protobufs/proto/ssl_simulation_robot_feedback.proto new file mode 100644 index 0000000..ddbf214 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_robot_feedback.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +import "ssl_simulation_error.proto"; +import "google/protobuf/any.proto"; + +// Feedback from a robot +message RobotFeedback { + // Id of the robot + required uint32 id = 1; + // Has the dribbler contact to the ball right now + optional bool dribbler_ball_contact = 2; + // Custom robot feedback for specific simulators (the protobuf files are managed by the simulators) + optional google.protobuf.Any custom = 3; +} + +// Response to RobotControl from the simulator to the connected client +message RobotControlResponse { + // List of errors, like using unsupported features + repeated SimulatorError errors = 1; + // Feedback of the robots + repeated RobotFeedback feedback = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_simulation_synchronous.proto b/ssl_league_protobufs/proto/ssl_simulation_synchronous.proto new file mode 100644 index 0000000..b8dde5d --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_simulation_synchronous.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; +option go_package = "github.com/RoboCup-SSL/ssl-simulation-protocol/pkg/sim"; + +import "ssl_vision_detection.proto"; +import "ssl_simulation_robot_feedback.proto"; +import "ssl_simulation_robot_control.proto"; +import "ssl_simulation_control.proto"; + +// Request from the team to the simulator +message SimulationSyncRequest { + // The simulation step [s] to perform + optional float sim_step = 1; + // An optional simulator command + optional SimulatorCommand simulator_command = 2; + // An optional robot control command + optional RobotControl robot_control = 3; +} + +// Response to last SimulationSyncRequest +message SimulationSyncResponse { + // List of detection frames for all cameras with the state after the simulation step in the request was performed + repeated SSL_DetectionFrame detection = 1; + // An optional robot control response + optional RobotControlResponse robot_control_response = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_vision_detection.proto b/ssl_league_protobufs/proto/ssl_vision_detection.proto new file mode 100644 index 0000000..090bb64 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_vision_detection.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +message SSL_DetectionBall { + // Confidence in [0-1] of the detection + required float confidence = 1; + optional uint32 area = 2; + // X-coordinate in [mm] in global ssl-vision coordinate system + required float x = 3; + // Y-coordinate in [mm] in global ssl-vision coordinate system + required float y = 4; + // Z-coordinate in [mm] in global ssl-vision coordinate system + // Not supported by ssl-vision, but might be set by simulators + optional float z = 5; + // X-coordinate in [pixel] in the image + required float pixel_x = 6; + // Y-coordinate in [pixel] in the image + required float pixel_y = 7; +} + +message SSL_DetectionRobot { + // Confidence in [0-1] of the detection + required float confidence = 1; + // Id of the robot + optional uint32 robot_id = 2; + // X-coordinate in [mm] in global ssl-vision coordinate system + required float x = 3; + // Y-coordinate in [mm] in global ssl-vision coordinate system + required float y = 4; + // Orientation in [rad] + optional float orientation = 5; + // X-coordinate in [pixel] in the image + required float pixel_x = 6; + // Y-coordinate in [pixel] in the image + required float pixel_y = 7; + // Height, as configured in ssl-vision for the respective team + optional float height = 8; +} + +message SSL_DetectionFrame { + // monotonously increasing frame number + required uint32 frame_number = 1; + // Unix timestamp in [seconds] at which the image has been received by ssl-vision + required double t_capture = 2; + // Unix timestamp in [seconds] at which this message has been sent to the network + required double t_sent = 3; + // Camera timestamp in [seconds] as reported by the camera, if supported + // This is not necessarily a unix timestamp + optional double t_capture_camera = 8; + // Identifier of the camera + required uint32 camera_id = 4; + // Detected balls + repeated SSL_DetectionBall balls = 5; + // Detected yellow robots + repeated SSL_DetectionRobot robots_yellow = 6; + // Detected blue robots + repeated SSL_DetectionRobot robots_blue = 7; +} diff --git a/ssl_league_protobufs/proto/ssl_vision_detection_tracked.proto b/ssl_league_protobufs/proto/ssl_vision_detection_tracked.proto new file mode 100644 index 0000000..4d73f4d --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_vision_detection_tracked.proto @@ -0,0 +1,88 @@ +syntax = "proto2"; + +import "ssl_gc_common.proto"; +import "ssl_gc_geometry.proto"; + +// Default network address: 224.5.23.2:10010 + +// Capabilities that a source implementation can have +enum Capability { + CAPABILITY_UNKNOWN = 0; + CAPABILITY_DETECT_FLYING_BALLS = 1; + CAPABILITY_DETECT_MULTIPLE_BALLS = 2; + CAPABILITY_DETECT_KICKED_BALLS = 3; +} + +// A single tracked ball +message TrackedBall { + // The position (x, y, height) [m] in the ssl-vision coordinate system + required Vector3 pos = 1; + + // The velocity [m/s] in the ssl-vision coordinate system + optional Vector3 vel = 2; + + // The visibility of the ball + // A value between 0 (not visible) and 1 (visible) + // The exact implementation depends on the source software + optional float visibility = 3; +} + +// A ball kicked by a robot, including predictions when the ball will come to a stop +message KickedBall { + // The initial position [m] from which the ball was kicked + required Vector2 pos = 1; + // The initial velocity [m/s] with which the ball was kicked + required Vector3 vel = 2; + // The unix timestamp [s] when the kick was performed + required double start_timestamp = 3; + + // The predicted unix timestamp [s] when the ball comes to a stop + optional double stop_timestamp = 4; + // The predicted position [m] at which the ball will come to a stop + optional Vector2 stop_pos = 5; + + // The robot that kicked the ball + optional RobotId robot_id = 6; +} + +// A single tracked robot +message TrackedRobot { + required RobotId robot_id = 1; + + // The position [m] in the ssl-vision coordinate system + required Vector2 pos = 2; + // The orientation [rad] in the ssl-vision coordinate system + required float orientation = 3; + + // The velocity [m/s] in the ssl-vision coordinate system + optional Vector2 vel = 4; + // The angular velocity [rad/s] in the ssl-vision coordinate system + optional float vel_angular = 5; + + // The visibility of the robot + // A value between 0 (not visible) and 1 (visible) + // The exact implementation depends on the source software + optional float visibility = 6; +} + +// A frame that contains all currently tracked objects on the field on all cameras +message TrackedFrame { + // A monotonous increasing frame counter + required uint32 frame_number = 1; + // The unix timestamp in [s] of the data + required double timestamp = 2; + + // The list of detected balls + // The first ball is the primary one + // Sources may add additional balls based on their capabilities + repeated TrackedBall balls = 3; + // The list of detected robots of both teams + repeated TrackedRobot robots = 4; + + // Information about a kicked ball, if the ball was kicked by a robot and is still moving + // Note: This field is optional. Some source implementations might not set this at any time + optional KickedBall kicked_ball = 5; + + // List of capabilities of the source implementation + repeated Capability capabilities = 6; +} diff --git a/ssl_league_protobufs/proto/ssl_vision_geometry.proto b/ssl_league_protobufs/proto/ssl_vision_geometry.proto new file mode 100644 index 0000000..3cdc756 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_vision_geometry.proto @@ -0,0 +1,151 @@ +syntax = "proto2"; +// A 2D float vector. +message Vector2f { + // X-coordinate in mm + required float x = 1; + // Y-coordinate in mm + required float y = 2; +} + +// Represents a field marking as a line segment represented by a start point p1, +// and end point p2, and a line thickness. The start and end points are along +// the center of the line, so the thickness of the line extends by thickness / 2 +// on either side of the line. +message SSL_FieldLineSegment { + // Name of this field marking. + required string name = 1; + // Start point of the line segment. + required Vector2f p1 = 2; + // End point of the line segment. + required Vector2f p2 = 3; + // Thickness of the line segment. + required float thickness = 4; + // The type of this shape + optional SSL_FieldShapeType type = 5; +} + +// Represents a field marking as a circular arc segment represented by center point, a +// start angle, an end angle, and an arc thickness. +message SSL_FieldCircularArc { + // Name of this field marking. + required string name = 1; + // Center point of the circular arc. + required Vector2f center = 2; + // Radius of the arc. + required float radius = 3; + // Start angle in counter-clockwise order. + required float a1 = 4; + // End angle in counter-clockwise order. + required float a2 = 5; + // Thickness of the arc. + required float thickness = 6; + // The type of this shape + optional SSL_FieldShapeType type = 7; +} + +message SSL_GeometryFieldSize { + // Field length (distance between goal lines) in mm + required int32 field_length = 1; + // Field width (distance between touch lines) in mm + required int32 field_width = 2; + // Goal width (distance between inner edges of goal posts) in mm + required int32 goal_width = 3; + // Goal depth (distance from outer goal line edge to inner goal back) in mm + required int32 goal_depth = 4; + // Boundary width (distance from touch/goal line centers to boundary walls) in mm + required int32 boundary_width = 5; + // Generated line segments based on the other parameters + repeated SSL_FieldLineSegment field_lines = 6; + // Generated circular arcs based on the other parameters + repeated SSL_FieldCircularArc field_arcs = 7; + // Depth of the penalty/defense area (measured between line centers) in mm + optional int32 penalty_area_depth = 8; + // Width of the penalty/defense area (measured between line centers) in mm + optional int32 penalty_area_width = 9; + // Radius of the center circle (measured between line centers) in mm + optional int32 center_circle_radius = 10; + // Thickness/width of the lines on the field in mm + optional int32 line_thickness = 11; + // Distance between the goal center and the center of the penalty mark in mm + optional int32 goal_center_to_penalty_mark = 12; + // Goal height in mm + optional int32 goal_height = 13; + // Ball radius in mm (note that this is a float type to represent sub-mm precision) + optional float ball_radius = 14; + // Max allowed robot radius in mm (note that this is a float type to represent sub-mm precision) + optional float max_robot_radius = 15; +} + +message SSL_GeometryCameraCalibration { + required uint32 camera_id = 1; + required float focal_length = 2; + required float principal_point_x = 3; + required float principal_point_y = 4; + required float distortion = 5; + required float q0 = 6; + required float q1 = 7; + required float q2 = 8; + required float q3 = 9; + required float tx = 10; + required float ty = 11; + required float tz = 12; + optional float derived_camera_world_tx = 13; + optional float derived_camera_world_ty = 14; + optional float derived_camera_world_tz = 15; + optional uint32 pixel_image_width = 16; + optional uint32 pixel_image_height = 17; +} + +// Two-Phase model for straight-kicked balls. +// There are two phases with different accelerations during the ball kicks: +// 1. Sliding +// 2. Rolling +// The full model is described in the TDP of ER-Force from 2016, which can be found here: +// https://ssl.robocup.org/wp-content/uploads/2019/01/2016_ETDP_ER-Force.pdf +message SSL_BallModelStraightTwoPhase { + // Ball sliding acceleration [m/s^2] (should be negative) + required double acc_slide = 1; + // Ball rolling acceleration [m/s^2] (should be negative) + required double acc_roll = 2; + // Fraction of the initial velocity where the ball starts to roll + required double k_switch = 3; +} + +// Fixed-Loss model for chipped balls. +// Uses fixed damping factors for xy and z direction per hop. +message SSL_BallModelChipFixedLoss { + // Chip kick velocity damping factor in XY direction for the first hop + required double damping_xy_first_hop = 1; + // Chip kick velocity damping factor in XY direction for all following hops + required double damping_xy_other_hops = 2; + // Chip kick velocity damping factor in Z direction for all hops + required double damping_z = 3; +} + +message SSL_GeometryModels { + optional SSL_BallModelStraightTwoPhase straight_two_phase = 1; + optional SSL_BallModelChipFixedLoss chip_fixed_loss = 2; +} + +message SSL_GeometryData { + required SSL_GeometryFieldSize field = 1; + repeated SSL_GeometryCameraCalibration calib = 2; + optional SSL_GeometryModels models = 3; +} + +enum SSL_FieldShapeType { + Undefined = 0; + CenterCircle = 1; + TopTouchLine = 2; + BottomTouchLine = 3; + LeftGoalLine = 4; + RightGoalLine = 5; + HalfwayLine = 6; + CenterLine = 7; + LeftPenaltyStretch = 8; + RightPenaltyStretch = 9; + LeftFieldLeftPenaltyStretch = 10; + LeftFieldRightPenaltyStretch = 11; + RightFieldLeftPenaltyStretch = 12; + RightFieldRightPenaltyStretch = 13; +} diff --git a/ssl_league_protobufs/proto/ssl_vision_wrapper.proto b/ssl_league_protobufs/proto/ssl_vision_wrapper.proto new file mode 100644 index 0000000..e8be7fb --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_vision_wrapper.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "ssl_vision_detection.proto"; +import "ssl_vision_geometry.proto"; + +message SSL_WrapperPacket { + optional SSL_DetectionFrame detection = 1; + optional SSL_GeometryData geometry = 2; +} diff --git a/ssl_league_protobufs/proto/ssl_vision_wrapper_tracked.proto b/ssl_league_protobufs/proto/ssl_vision_wrapper_tracked.proto new file mode 100644 index 0000000..c30c4b1 --- /dev/null +++ b/ssl_league_protobufs/proto/ssl_vision_wrapper_tracked.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; +import "ssl_vision_detection_tracked.proto"; + +// A wrapper packet containing meta data of the source +// Also serves for the possibility to extend the protocol later +message TrackerWrapperPacket { + // A random UUID of the source that is kept constant at the source while running + // If multiple sources are broadcasting to the same network, this id can be used to identify individual sources + required string uuid = 1; + // The name of the source software that is producing this messages. + optional string source_name = 2; + // The tracked frame + optional TrackedFrame tracked_frame = 3; +} diff --git a/ssl_ros_bridge/CMakeLists.txt b/ssl_ros_bridge/CMakeLists.txt new file mode 100644 index 0000000..96fc1bd --- /dev/null +++ b/ssl_ros_bridge/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.8) +project(ssl_ros_bridge) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(Boost REQUIRED) +find_package(rclcpp REQUIRED) +find_package(rclcpp_components REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_geometry_msgs REQUIRED) +find_package(ssl_league_msgs REQUIRED) +find_package(ssl_league_protobufs REQUIRED) +find_package(ssl_ros_bridge_msgs REQUIRED) + +add_subdirectory(src/core) +add_subdirectory(src/game_controller_bridge) +add_subdirectory(src/team_client) +add_subdirectory(src/vision_bridge) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/ssl_ros_bridge/LICENSE b/ssl_ros_bridge/LICENSE new file mode 100644 index 0000000..30e8e2e --- /dev/null +++ b/ssl_ros_bridge/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ssl_ros_bridge/package.xml b/ssl_ros_bridge/package.xml new file mode 100644 index 0000000..f7aba0a --- /dev/null +++ b/ssl_ros_bridge/package.xml @@ -0,0 +1,27 @@ + + + + ssl_ros_bridge + 0.0.0 + Provides nodes to bridge SSL league packets into a ROS system. + Matthew Barulic + MIT + + ament_cmake + + boost + rclcpp + rclcpp_components + tf2 + tf2_geometry_msgs + ssl_league_msgs + ssl_league_protobufs + ssl_ros_bridge_msgs + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/ssl_ros_bridge/src/core/CMakeLists.txt b/ssl_ros_bridge/src/core/CMakeLists.txt new file mode 100644 index 0000000..1c374a3 --- /dev/null +++ b/ssl_ros_bridge/src/core/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(${PROJECT_NAME}_core SHARED + get_ip_addresses.cpp + multicast_receiver.cpp +) +target_include_directories(${PROJECT_NAME}_core PUBLIC .) +target_link_libraries(${PROJECT_NAME}_core + Boost::boost + protobuf::libprotobuf +) +install(TARGETS ${PROJECT_NAME}_core DESTINATION lib) diff --git a/ssl_ros_bridge/src/core/get_ip_addresses.cpp b/ssl_ros_bridge/src/core/get_ip_addresses.cpp new file mode 100644 index 0000000..0960461 --- /dev/null +++ b/ssl_ros_bridge/src/core/get_ip_addresses.cpp @@ -0,0 +1,65 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "get_ip_addresses.hpp" + +#include +#include +#include + +namespace ssl_ros_bridge::core +{ + +std::vector GetIpAdresses(const bool include_ipv6) +{ + std::vector addresses; + ifaddrs * iface_info = nullptr; + getifaddrs(&iface_info); + auto iface_info_current = iface_info; + while(iface_info_current != nullptr) { + if(!iface_info_current->ifa_addr) { + continue; + } + + if(iface_info_current->ifa_addr->sa_family == AF_INET) { + // IPv4 address + void * net_address = + &(reinterpret_cast(iface_info_current->ifa_addr)->sin_addr); + char buffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, net_address, buffer, INET_ADDRSTRLEN); + addresses.emplace_back(buffer); + } else if(include_ipv6 && iface_info_current->ifa_addr->sa_family == AF_INET6) { + // IPv6 address + void * net_address = + &(reinterpret_cast(iface_info_current->ifa_addr)->sin_addr); + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, net_address, buffer, INET6_ADDRSTRLEN); + addresses.emplace_back(buffer); + } + + iface_info_current = iface_info_current->ifa_next; + } + if(iface_info != nullptr) { + freeifaddrs(iface_info); + } + return addresses; +} + +} // namespace ssl_ros_bridge::core diff --git a/ssl_ros_bridge/src/core/get_ip_addresses.hpp b/ssl_ros_bridge/src/core/get_ip_addresses.hpp new file mode 100644 index 0000000..e4a874a --- /dev/null +++ b/ssl_ros_bridge/src/core/get_ip_addresses.hpp @@ -0,0 +1,37 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CORE__GET_IP_ADDRESSES_HPP_ +#define CORE__GET_IP_ADDRESSES_HPP_ + +#include +#include + +namespace ssl_ros_bridge::core +{ + +/** + * Returns a vector of local IP addresses. Effectively enumerating available network interfaces. + */ +std::vector GetIpAdresses(const bool include_ipv6 = true); + +} // namespace ssl_ros_bridge::core + +#endif // CORE__GET_IP_ADDRESSES_HPP_ diff --git a/ssl_ros_bridge/src/core/multicast_receiver.cpp b/ssl_ros_bridge/src/core/multicast_receiver.cpp new file mode 100644 index 0000000..020fb08 --- /dev/null +++ b/ssl_ros_bridge/src/core/multicast_receiver.cpp @@ -0,0 +1,150 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "multicast_receiver.hpp" + + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "get_ip_addresses.hpp" + +namespace ssl_ros_bridge::core +{ + +MulticastReceiver::MulticastReceiver( + std::string multicast_address_string, + uint16_t multicast_port, + ReceiveCallback receive_callback, + std::string interface_address) +: receive_callback_(receive_callback), + multicast_socket_(io_service_) +{ + const auto multicast_address = boost::asio::ip::make_address(multicast_address_string).to_v4(); + const boost::asio::ip::udp::endpoint multicast_endpoint(multicast_address, multicast_port); + multicast_socket_.open(multicast_endpoint.protocol()); + multicast_socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + multicast_socket_.bind(multicast_endpoint); + if (interface_address.empty()) { + // If no interface specified, join on all interfaces + const auto available_interface_addresses = GetIpAdresses(false); + for(const auto & address : available_interface_addresses) { + try { + multicast_socket_.set_option( + boost::asio::ip::multicast::join_group( + multicast_address, + boost::asio::ip::make_address_v4(address))); + } catch (const boost::system::system_error & e) { + /* Ignore "address already in use" exceptions. This just indicates multiple addresses + * assigned to the same interface. + */ + if(e.code().value() != EADDRINUSE) { + boost::rethrow_exception(boost::current_exception()); + } + } + } + } else { + multicast_socket_.set_option( + boost::asio::ip::multicast::join_group( + multicast_address, + boost::asio::ip::make_address_v4(interface_address))); + } + multicast_socket_.async_receive_from( + boost::asio::buffer(buffer_), sender_endpoint_, + boost::bind( + &MulticastReceiver::HandleMulticastReceiveFrom, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + + io_service_thread_ = std::thread( + [this]() { + io_service_.run(); + }); +} + +MulticastReceiver::~MulticastReceiver() +{ + io_service_.stop(); + if (io_service_thread_.joinable()) { + io_service_thread_.join(); + } +} + +void MulticastReceiver::SendTo( + const std::string & address, const uint16_t port, + const char * const data, const size_t length) +{ + if (length >= send_buffer_.size()) { + std::cout << "WARNING: UDP send data length is larger than buffer" << std::endl; + + return; + } + + // Copy to buffer + // Better to send an invalid packet than to overrun the buffer + // With the if statement above, this should never happen + memcpy(send_buffer_.data(), data, std::min(send_buffer_.size(), length)); + + boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(address), port); + + multicast_socket_.async_send_to( + boost::asio::buffer(send_buffer_, length), + endpoint, + boost::bind( + &MulticastReceiver::HandleUDPSendTo, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); +} + +void MulticastReceiver::HandleMulticastReceiveFrom( + const boost::system::error_code & error, + size_t bytes_received) +{ + if (error) { + std::cerr << "Receive from error: " << error.message() << std::endl; + return; + } + + receive_callback_( + sender_endpoint_.address().to_string(), sender_endpoint_.port(), + buffer_.data(), bytes_received); + + multicast_socket_.async_receive_from( + boost::asio::buffer(buffer_), sender_endpoint_, + boost::bind( + &MulticastReceiver::HandleMulticastReceiveFrom, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); +} + + +void MulticastReceiver::HandleUDPSendTo(const boost::system::error_code & error, size_t) +{ + if (error) { + std::cerr << "Error sending UDP data: " << error.message() << std::endl; + } +} + +} // namespace ssl_ros_bridge::core diff --git a/ssl_ros_bridge/src/core/multicast_receiver.hpp b/ssl_ros_bridge/src/core/multicast_receiver.hpp new file mode 100644 index 0000000..b7992e1 --- /dev/null +++ b/ssl_ros_bridge/src/core/multicast_receiver.hpp @@ -0,0 +1,74 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CORE__MULTICAST_RECEIVER_HPP_ +#define CORE__MULTICAST_RECEIVER_HPP_ + +#include +#include + +#include + +namespace ssl_ros_bridge::core +{ +class MulticastReceiver +{ +public: + /** + * @param sender_address IP address of sender + * @param sender_port Port number of sender + * @param data Data received in latest packet + * @param data_length Length of data received + */ + using ReceiveCallback = + std::function; + + MulticastReceiver( + std::string multicast_ip_address, + uint16_t multicast_port, + ReceiveCallback receive_callback, + std::string interface_address = ""); + + ~MulticastReceiver(); + + void SendTo( + const std::string & address, const uint16_t port, const char * const data, + const size_t length); + +private: + ReceiveCallback receive_callback_; + boost::asio::io_service io_service_; + boost::asio::ip::udp::socket multicast_socket_; + boost::asio::ip::udp::endpoint sender_endpoint_; + std::array send_buffer_; + std::array buffer_; + std::thread io_service_thread_; + + void HandleMulticastReceiveFrom(const boost::system::error_code & error, size_t bytes_received); + + void HandleUDPSendTo(const boost::system::error_code & error, size_t bytes_sent); + + void JoinMulticastGroupOnAllV4Interfaces(const boost::asio::ip::address & multicast_address); +}; + +} // namespace ssl_ros_bridge::core + +#endif // CORE__MULTICAST_RECEIVER_HPP_ diff --git a/ssl_ros_bridge/src/core/protobuf_logging.hpp b/ssl_ros_bridge/src/core/protobuf_logging.hpp new file mode 100644 index 0000000..de42a7f --- /dev/null +++ b/ssl_ros_bridge/src/core/protobuf_logging.hpp @@ -0,0 +1,64 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CORE__PROTOBUF_LOGGING_HPP_ +#define CORE__PROTOBUF_LOGGING_HPP_ + +#include +#include +#include +#include + +/** + * @brief Sets up the protobuf log handler to redirect all messages through ROS. + * + * Recommended use: + * Call this macro in the constructor of your node. For the logger name, append ".protobuf" to your + * node name to create a child of your node's logger. + * @code{.cpp} + * SET_ROS_PROTOBUF_LOG_HANDLER("my_node.protobuf"); + * @endcode + * + */ +#define SET_ROS_PROTOBUF_LOG_HANDLER(logger_name) \ + { \ + google::protobuf::SetLogHandler( \ + [](google::protobuf::LogLevel level, const char * filename, int line, \ + const std::string & message) { \ + auto ros_logger = rclcpp::get_logger(logger_name); \ + switch (level) { \ + case google::protobuf::LogLevel::LOGLEVEL_INFO: \ + RCLCPP_INFO(ros_logger, "[%s:%d] %s", filename, line, message.c_str()); \ + break; \ + case google::protobuf::LogLevel::LOGLEVEL_WARNING: \ + RCLCPP_WARN(ros_logger, "[%s:%d] %s", filename, line, message.c_str()); \ + break; \ + case google::protobuf::LogLevel::LOGLEVEL_ERROR: \ + RCLCPP_ERROR(ros_logger, "[%s:%d] %s", filename, line, message.c_str()); \ + break; \ + case google::protobuf::LogLevel::LOGLEVEL_FATAL: \ + RCLCPP_FATAL(ros_logger, "[%s:%d] %s", filename, line, message.c_str()); \ + break; \ + } \ + }); \ + RCLCPP_INFO(rclcpp::get_logger(logger_name), "Routing protobuf logs through ROS"); \ + } + +#endif // CORE__PROTOBUF_LOGGING_HPP_ diff --git a/ssl_ros_bridge/src/game_controller_bridge/CMakeLists.txt b/ssl_ros_bridge/src/game_controller_bridge/CMakeLists.txt new file mode 100644 index 0000000..85a15ce --- /dev/null +++ b/ssl_ros_bridge/src/game_controller_bridge/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(${PROJECT_NAME}_game_controller_bridge SHARED + gc_multicast_bridge_node.cpp + message_conversions.cpp +) +target_include_directories(${PROJECT_NAME}_game_controller_bridge PRIVATE ..) +ament_target_dependencies(${PROJECT_NAME}_game_controller_bridge + rclcpp + rclcpp_components + ssl_ros_bridge_msgs + ssl_league_msgs + ssl_league_protobufs +) +target_link_libraries(${PROJECT_NAME}_game_controller_bridge ${PROJECT_NAME}_core) + +rclcpp_components_register_node( + ${PROJECT_NAME}_game_controller_bridge + PLUGIN "ssl_ros_bridge::game_controller_bridge::GCMulticastBridgeNode" + EXECUTABLE gc_multicast_bridge_node +) + +install(TARGETS ${PROJECT_NAME}_game_controller_bridge DESTINATION lib) diff --git a/ssl_ros_bridge/src/game_controller_bridge/gc_multicast_bridge_node.cpp b/ssl_ros_bridge/src/game_controller_bridge/gc_multicast_bridge_node.cpp new file mode 100644 index 0000000..937d637 --- /dev/null +++ b/ssl_ros_bridge/src/game_controller_bridge/gc_multicast_bridge_node.cpp @@ -0,0 +1,122 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include +#include +#include +#include "core/multicast_receiver.hpp" +#include "core/protobuf_logging.hpp" +#include +#include +#include +#include "message_conversions.hpp" + +namespace ssl_ros_bridge::game_controller_bridge +{ + +class GCMulticastBridgeNode : public rclcpp::Node +{ +public: + explicit GCMulticastBridgeNode(const rclcpp::NodeOptions & options) + : rclcpp::Node("gc_multicast_bridge", options) + { + SET_ROS_PROTOBUF_LOG_HANDLER("gc_multicast_bridge.protobuf"); + + referee_publisher_ = create_publisher("~/referee_messages", + rclcpp::SystemDefaultsQoS()); + + reconnect_client_ = + create_client("/team_client_node/reconnect"); + + team_client_connection_subscription_ = + create_subscription( + "/team_client_node/connection_status", rclcpp::SystemDefaultsQoS(), + std::bind(&GCMulticastBridgeNode::TeamClientConnectionStatusCallback, this, + std::placeholders::_1)); + + const auto multicast_address = + declare_parameter("multicast.address", "224.5.23.1"); + const auto multicast_port = declare_parameter("multicast.port", 10003); + RCLCPP_INFO( + get_logger(), "Listening for multicast packets at %s:%ld", + multicast_address.c_str(), multicast_port); + multicast_receiver_ = std::make_unique( + multicast_address, + multicast_port, + std::bind(&GCMulticastBridgeNode::PublishMulticastMessage, this, std::placeholders::_1, + std::placeholders::_3, std::placeholders::_4), + declare_parameter("net_interface_address", "")); + } + +private: + const std::chrono::seconds kReconnectTimeout{2}; + const std::chrono::seconds kReconnectRetryTime{1}; + bool team_client_connected_ = false; + std::chrono::steady_clock::time_point last_reconnect_attempt_time_; + rclcpp::Publisher::SharedPtr referee_publisher_; + rclcpp::Client::SharedPtr reconnect_client_; + rclcpp::Subscription::SharedPtr + team_client_connection_subscription_; + std::unique_ptr multicast_receiver_; + + void PublishMulticastMessage( + const std::string & sender_address, const uint8_t * buffer, + const size_t bytes_received) + { + Referee referee_proto; + if(!referee_proto.ParseFromArray(buffer, bytes_received)) { + RCLCPP_WARN(get_logger(), "Failed to parse referee protobuf packet"); + return; + } + referee_publisher_->publish(message_conversions::fromProto(referee_proto)); + if(team_client_connected_ || !reconnect_client_->service_is_ready()) { + return; + } + const auto now = std::chrono::steady_clock::now(); + const auto time_since_last_attempt = now - last_reconnect_attempt_time_; + if(time_since_last_attempt < kReconnectRetryTime) { + return; + } + last_reconnect_attempt_time_ = now; + auto request = std::make_shared(); + request->server_address = sender_address; + auto service_future = reconnect_client_->async_send_request(request); + if(service_future.wait_for(kReconnectTimeout) == std::future_status::timeout) { + RCLCPP_WARN(get_logger(), "Timed out trying to reconnect team client."); + return; + } + if(!service_future.get()->success) { + RCLCPP_WARN(get_logger(), "Connecting team client to deduced GC server failed."); + return; + } + team_client_connected_ = true; + } + + void TeamClientConnectionStatusCallback( + const ssl_ros_bridge_msgs::msg::TeamClientConnectionStatus::ConstSharedPtr msg) + { + team_client_connected_ = msg->connected; + } +}; + +} // namespace ssl_ros_bridge::game_controller_bridge + +RCLCPP_COMPONENTS_REGISTER_NODE(ssl_ros_bridge::game_controller_bridge::GCMulticastBridgeNode) diff --git a/ssl_ros_bridge/src/game_controller_bridge/message_conversions.cpp b/ssl_ros_bridge/src/game_controller_bridge/message_conversions.cpp new file mode 100644 index 0000000..474901c --- /dev/null +++ b/ssl_ros_bridge/src/game_controller_bridge/message_conversions.cpp @@ -0,0 +1,552 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "message_conversions.hpp" +#include +#include + +#define CopyOptional(proto_msg, ros_msg, var_name) \ + if (proto_msg.has_ ## var_name() ) { \ + ros_msg.var_name = {proto_msg.var_name()}; \ + } + +#define CopyOptionalStruct(proto_msg, ros_msg, var_name) \ + if (proto_msg.has_ ## var_name() ) { \ + ros_msg.var_name = {fromProto(proto_msg.var_name())}; \ + } + +namespace ssl_ros_bridge::game_controller_bridge::message_conversions +{ + +geometry_msgs::msg::Point32 fromProto(const Vector2 & proto_msg) +{ + geometry_msgs::msg::Point32 ros_msg; + ros_msg.x = proto_msg.x(); + ros_msg.y = proto_msg.y(); + return ros_msg; +} +geometry_msgs::msg::Point32 fromProto(const Vector3 & proto_msg) +{ + geometry_msgs::msg::Point32 ros_msg; + ros_msg.x = proto_msg.x(); + ros_msg.y = proto_msg.y(); + ros_msg.z = proto_msg.z(); + return ros_msg; +} + +ssl_league_msgs::msg::Referee fromProto(const Referee & proto_msg) +{ + ssl_league_msgs::msg::Referee ros_msg; + ros_msg.source_identifier = proto_msg.source_identifier(); + ros_msg.match_type = proto_msg.match_type(); + ros_msg.timestamp = rclcpp::Time(proto_msg.packet_timestamp() * 1000); + ros_msg.stage = proto_msg.stage(); + ros_msg.stage_time_left = proto_msg.stage_time_left(); + ros_msg.command = proto_msg.command(); + ros_msg.command_counter = proto_msg.command_counter(); + ros_msg.command_timestamp = rclcpp::Time(proto_msg.command_timestamp() * 1000); + ros_msg.yellow = fromProto(proto_msg.yellow()); + ros_msg.blue = fromProto(proto_msg.blue()); + ros_msg.designated_position.x = proto_msg.designated_position().x() / 1000.0; + ros_msg.designated_position.y = proto_msg.designated_position().y() / 1000.0; + ros_msg.blue_team_on_positive_half = proto_msg.blue_team_on_positive_half(); + ros_msg.next_command = proto_msg.next_command(); + std::transform( + proto_msg.game_events().begin(), + proto_msg.game_events().end(), + std::back_inserter(ros_msg.game_events), + [](const auto & p) {return fromProto(p);}); + std::transform( + proto_msg.game_event_proposals().begin(), + proto_msg.game_event_proposals().end(), + std::back_inserter(ros_msg.game_event_proposals), + [](const auto & p) {return fromProto(p);}); + ros_msg.current_action_time_remaining = proto_msg.current_action_time_remaining(); + ros_msg.status_message = proto_msg.status_message(); + return ros_msg; +} + +ssl_league_msgs::msg::TeamInfo fromProto(const Referee::TeamInfo & proto_msg) +{ + ssl_league_msgs::msg::TeamInfo ros_msg; + ros_msg.name = proto_msg.name(); + ros_msg.score = proto_msg.score(); + ros_msg.red_cards = proto_msg.red_cards(); + std::copy( + proto_msg.yellow_card_times().begin(), + proto_msg.yellow_card_times().end(), std::back_inserter(ros_msg.yellow_card_times)); + ros_msg.yellow_cards = proto_msg.yellow_cards(); + ros_msg.timeouts = proto_msg.timeouts(); + ros_msg.timeout_time = proto_msg.timeout_time(); + ros_msg.goalkeeper = proto_msg.goalkeeper(); + ros_msg.foul_counter = proto_msg.foul_counter(); + ros_msg.ball_placement_failures = proto_msg.ball_placement_failures(); + ros_msg.can_place_ball = proto_msg.can_place_ball(); + ros_msg.max_allowed_bots = proto_msg.max_allowed_bots(); + ros_msg.bot_substitution_intent = proto_msg.bot_substitution_intent(); + ros_msg.ball_placement_failures_reached = proto_msg.ball_placement_failures_reached(); + ros_msg.bot_substitution_allowed = proto_msg.bot_substitution_allowed(); + ros_msg.bot_substitutions_left = proto_msg.bot_substitutions_left(); + ros_msg.bot_substitution_time_left = proto_msg.bot_substitution_time_left(); + return ros_msg; +} + +ssl_league_msgs::msg::GameEvent fromProto(const GameEvent & proto_msg) +{ + ssl_league_msgs::msg::GameEvent ros_msg; + ros_msg.id = proto_msg.id(); + ros_msg.type = proto_msg.type(); + std::copy( + proto_msg.origin().begin(), proto_msg.origin().end(), + std::back_inserter(ros_msg.origin)); + ros_msg.created_timestamp = proto_msg.created_timestamp(); + CopyOptionalStruct(proto_msg, ros_msg, ball_left_field_touch_line); + CopyOptionalStruct(proto_msg, ros_msg, ball_left_field_goal_line); + CopyOptionalStruct(proto_msg, ros_msg, aimless_kick); + CopyOptionalStruct(proto_msg, ros_msg, attacker_too_close_to_defense_area); + CopyOptionalStruct(proto_msg, ros_msg, defender_in_defense_area); + CopyOptionalStruct(proto_msg, ros_msg, boundary_crossing); + CopyOptionalStruct(proto_msg, ros_msg, keeper_held_ball); + CopyOptionalStruct(proto_msg, ros_msg, bot_dribbled_ball_too_far); + CopyOptionalStruct(proto_msg, ros_msg, bot_pushed_bot); + CopyOptionalStruct(proto_msg, ros_msg, bot_held_ball_deliberately); + CopyOptionalStruct(proto_msg, ros_msg, bot_tipped_over); + CopyOptionalStruct(proto_msg, ros_msg, bot_dropped_parts); + CopyOptionalStruct(proto_msg, ros_msg, attacker_touched_ball_in_defense_area); + CopyOptionalStruct(proto_msg, ros_msg, bot_kicked_ball_too_fast); + CopyOptionalStruct(proto_msg, ros_msg, bot_crash_unique); + CopyOptionalStruct(proto_msg, ros_msg, bot_crash_drawn); + CopyOptionalStruct(proto_msg, ros_msg, defender_too_close_to_kick_point); + CopyOptionalStruct(proto_msg, ros_msg, bot_too_fast_in_stop); + CopyOptionalStruct(proto_msg, ros_msg, bot_interfered_placement); + CopyOptionalStruct(proto_msg, ros_msg, possible_goal); + CopyOptionalStruct(proto_msg, ros_msg, goal); + CopyOptionalStruct(proto_msg, ros_msg, invalid_goal); + CopyOptionalStruct(proto_msg, ros_msg, attacker_double_touched_ball); + CopyOptionalStruct(proto_msg, ros_msg, placement_succeeded); + CopyOptionalStruct(proto_msg, ros_msg, penalty_kick_failed); + CopyOptionalStruct(proto_msg, ros_msg, no_progress_in_game); + CopyOptionalStruct(proto_msg, ros_msg, placement_failed); + CopyOptionalStruct(proto_msg, ros_msg, multiple_cards); + CopyOptionalStruct(proto_msg, ros_msg, multiple_fouls); + CopyOptionalStruct(proto_msg, ros_msg, bot_substitution); + CopyOptionalStruct(proto_msg, ros_msg, excessive_bot_substitution); + CopyOptionalStruct(proto_msg, ros_msg, too_many_robots); + CopyOptionalStruct(proto_msg, ros_msg, challenge_flag); + CopyOptionalStruct(proto_msg, ros_msg, challenge_flag_handled); + CopyOptionalStruct(proto_msg, ros_msg, emergency_stop); + CopyOptionalStruct(proto_msg, ros_msg, unsporting_behavior_minor); + CopyOptionalStruct(proto_msg, ros_msg, unsporting_behavior_major); + return ros_msg; +} + +ssl_league_msgs::msg::GameEventProposalGroup fromProto(const GameEventProposalGroup & proto_msg) +{ + ssl_league_msgs::msg::GameEventProposalGroup ros_msg; + std::transform( + proto_msg.game_events().begin(), + proto_msg.game_events().end(), + std::back_inserter(ros_msg.game_events), + [](const auto & p) {return fromProto(p);}); + ros_msg.accepted = proto_msg.accepted(); + return ros_msg; +} + +ssl_league_msgs::msg::Division fromProto(const Division & proto_msg) +{ + ssl_league_msgs::msg::Division ros_msg; + ros_msg.division = proto_msg; + return ros_msg; +} + +ssl_league_msgs::msg::RobotId fromProto(const RobotId & proto_msg) +{ + ssl_league_msgs::msg::RobotId ros_msg; + CopyOptionalStruct(proto_msg, ros_msg, team); + CopyOptional(proto_msg, ros_msg, id); + return ros_msg; +} + +ssl_league_msgs::msg::Team fromProto(const Team & proto_msg) +{ + ssl_league_msgs::msg::Team ros_msg; + ros_msg.color = proto_msg; + return ros_msg; +} + +ssl_league_msgs::msg::AimlessKick fromProto(const GameEvent_AimlessKick & proto_msg) +{ + ssl_league_msgs::msg::AimlessKick ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, kick_location); + return ros_msg; +} +ssl_league_msgs::msg::AttackerDoubleTouchedBall fromProto( + const GameEvent_AttackerDoubleTouchedBall & proto_msg) +{ + ssl_league_msgs::msg::AttackerDoubleTouchedBall ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::AttackerTooCloseToDefenseArea fromProto( + const GameEvent_AttackerTooCloseToDefenseArea & proto_msg) +{ + ssl_league_msgs::msg::AttackerTooCloseToDefenseArea ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, distance); + CopyOptionalStruct(proto_msg, ros_msg, ball_location); + return ros_msg; +} +ssl_league_msgs::msg::AttackerTouchedBallInDefenseArea fromProto( + const GameEvent_AttackerTouchedBallInDefenseArea & proto_msg) +{ + ssl_league_msgs::msg::AttackerTouchedBallInDefenseArea ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, distance); + return ros_msg; +} +ssl_league_msgs::msg::AttackerTouchedOpponentInDefenseArea fromProto( + const GameEvent_AttackerTouchedOpponentInDefenseArea & proto_msg) +{ + ssl_league_msgs::msg::AttackerTouchedOpponentInDefenseArea ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptional(proto_msg, ros_msg, victim); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::BallLeftField fromProto(const GameEvent_BallLeftField & proto_msg) +{ + ssl_league_msgs::msg::BallLeftField ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::BotCrashDrawn fromProto(const GameEvent_BotCrashDrawn & proto_msg) +{ + ssl_league_msgs::msg::BotCrashDrawn ros_msg; + CopyOptional(proto_msg, ros_msg, bot_yellow); + CopyOptional(proto_msg, ros_msg, bot_blue); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, crash_speed); + CopyOptional(proto_msg, ros_msg, speed_diff); + CopyOptional(proto_msg, ros_msg, crash_angle); + return ros_msg; +} +ssl_league_msgs::msg::BotCrashUnique fromProto(const GameEvent_BotCrashUnique & proto_msg) +{ + ssl_league_msgs::msg::BotCrashUnique ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, violator); + CopyOptional(proto_msg, ros_msg, victim); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, crash_speed); + CopyOptional(proto_msg, ros_msg, speed_diff); + CopyOptional(proto_msg, ros_msg, crash_angle); + return ros_msg; +} +ssl_league_msgs::msg::BotDribbledBallTooFar fromProto( + const GameEvent_BotDribbledBallTooFar & proto_msg) +{ + ssl_league_msgs::msg::BotDribbledBallTooFar ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, start); + CopyOptionalStruct(proto_msg, ros_msg, end); + return ros_msg; +} +ssl_league_msgs::msg::BotHeldBallDeliberately fromProto( + const GameEvent_BotHeldBallDeliberately & proto_msg) +{ + ssl_league_msgs::msg::BotHeldBallDeliberately ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, duration); + return ros_msg; +} +ssl_league_msgs::msg::BotInterferedPlacement fromProto( + const GameEvent_BotInterferedPlacement & proto_msg) +{ + ssl_league_msgs::msg::BotInterferedPlacement ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::BotKickedBallTooFast fromProto( + const GameEvent_BotKickedBallTooFast & proto_msg) +{ + ssl_league_msgs::msg::BotKickedBallTooFast ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, initial_ball_speed); + CopyOptional(proto_msg, ros_msg, chipped); + return ros_msg; +} +ssl_league_msgs::msg::BotPushedBot fromProto(const GameEvent_BotPushedBot & proto_msg) +{ + ssl_league_msgs::msg::BotPushedBot ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, violator); + CopyOptional(proto_msg, ros_msg, victim); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, pushed_distance); + return ros_msg; +} +ssl_league_msgs::msg::BotSubstitution fromProto(const GameEvent_BotSubstitution & proto_msg) +{ + ssl_league_msgs::msg::BotSubstitution ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::BotTippedOver fromProto(const GameEvent_BotTippedOver & proto_msg) +{ + ssl_league_msgs::msg::BotTippedOver ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, ball_location); + return ros_msg; +} +ssl_league_msgs::msg::BotTooFastInStop fromProto(const GameEvent_BotTooFastInStop & proto_msg) +{ + ssl_league_msgs::msg::BotTooFastInStop ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, speed); + return ros_msg; +} +ssl_league_msgs::msg::BoundaryCrossing fromProto(const GameEvent_BoundaryCrossing & proto_msg) +{ + ssl_league_msgs::msg::BoundaryCrossing ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::ChallengeFlag fromProto(const GameEvent_ChallengeFlag & proto_msg) +{ + ssl_league_msgs::msg::ChallengeFlag ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::ChippedGoal fromProto(const GameEvent_ChippedGoal & proto_msg) +{ + ssl_league_msgs::msg::ChippedGoal ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, kick_location); + CopyOptional(proto_msg, ros_msg, max_ball_height); + return ros_msg; +} +ssl_league_msgs::msg::DefenderInDefenseArea fromProto( + const GameEvent_DefenderInDefenseArea & proto_msg) +{ + ssl_league_msgs::msg::DefenderInDefenseArea ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, distance); + return ros_msg; +} +ssl_league_msgs::msg::DefenderInDefenseAreaPartially fromProto( + const GameEvent_DefenderInDefenseAreaPartially & proto_msg) +{ + ssl_league_msgs::msg::DefenderInDefenseAreaPartially ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, distance); + CopyOptionalStruct(proto_msg, ros_msg, ball_location); + return ros_msg; +} +ssl_league_msgs::msg::DefenderTooCloseToKickPoint fromProto( + const GameEvent_DefenderTooCloseToKickPoint & proto_msg) +{ + ssl_league_msgs::msg::DefenderTooCloseToKickPoint ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, distance); + return ros_msg; +} +ssl_league_msgs::msg::EmergencyStop fromProto(const GameEvent_EmergencyStop & proto_msg) +{ + ssl_league_msgs::msg::EmergencyStop ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::Goal fromProto(const GameEvent_Goal & proto_msg) +{ + ssl_league_msgs::msg::Goal ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptionalStruct(proto_msg, ros_msg, kicking_team); + CopyOptional(proto_msg, ros_msg, kicking_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, kick_location); + CopyOptional(proto_msg, ros_msg, max_ball_height); + CopyOptional(proto_msg, ros_msg, num_robots_by_team); + CopyOptional(proto_msg, ros_msg, last_touch_by_team); + CopyOptional(proto_msg, ros_msg, message); + return ros_msg; +} +ssl_league_msgs::msg::IndirectGoal fromProto(const GameEvent_IndirectGoal & proto_msg) +{ + ssl_league_msgs::msg::IndirectGoal ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, kick_location); + return ros_msg; +} +ssl_league_msgs::msg::KeeperHeldBall fromProto(const GameEvent_KeeperHeldBall & proto_msg) +{ + ssl_league_msgs::msg::KeeperHeldBall ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, duration); + return ros_msg; +} +ssl_league_msgs::msg::KickTimeout fromProto(const GameEvent_KickTimeout & proto_msg) +{ + ssl_league_msgs::msg::KickTimeout ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, time); + return ros_msg; +} +ssl_league_msgs::msg::MultipleCards fromProto(const GameEvent_MultipleCards & proto_msg) +{ + ssl_league_msgs::msg::MultipleCards ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::MultipleFouls fromProto(const GameEvent_MultipleFouls & proto_msg) +{ + ssl_league_msgs::msg::MultipleFouls ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::MultiplePlacementFailures fromProto( + const GameEvent_MultiplePlacementFailures & proto_msg) +{ + ssl_league_msgs::msg::MultiplePlacementFailures ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} +ssl_league_msgs::msg::NoProgressInGame fromProto(const GameEvent_NoProgressInGame & proto_msg) +{ + ssl_league_msgs::msg::NoProgressInGame ros_msg; + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptional(proto_msg, ros_msg, time); + return ros_msg; +} +ssl_league_msgs::msg::PenaltyKickFailed fromProto(const GameEvent_PenaltyKickFailed & proto_msg) +{ + ssl_league_msgs::msg::PenaltyKickFailed ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptionalStruct(proto_msg, ros_msg, location); + return ros_msg; +} +ssl_league_msgs::msg::PlacementFailed fromProto(const GameEvent_PlacementFailed & proto_msg) +{ + ssl_league_msgs::msg::PlacementFailed ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, remaining_distance); + return ros_msg; +} +ssl_league_msgs::msg::PlacementSucceeded fromProto(const GameEvent_PlacementSucceeded & proto_msg) +{ + ssl_league_msgs::msg::PlacementSucceeded ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, time_taken); + CopyOptional(proto_msg, ros_msg, precision); + CopyOptional(proto_msg, ros_msg, distance); + return ros_msg; +} +ssl_league_msgs::msg::Prepared fromProto(const GameEvent_Prepared & proto_msg) +{ + ssl_league_msgs::msg::Prepared ros_msg; + CopyOptional(proto_msg, ros_msg, time_taken); + return ros_msg; +} +ssl_league_msgs::msg::TooManyRobots fromProto(const GameEvent_TooManyRobots & proto_msg) +{ + ssl_league_msgs::msg::TooManyRobots ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, num_robots_allowed); + CopyOptional(proto_msg, ros_msg, num_robots_on_field); + CopyOptionalStruct(proto_msg, ros_msg, ball_location); + return ros_msg; +} +ssl_league_msgs::msg::UnsportingBehaviorMajor fromProto( + const GameEvent_UnsportingBehaviorMajor & proto_msg) +{ + ssl_league_msgs::msg::UnsportingBehaviorMajor ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + ros_msg.reason = proto_msg.reason(); + return ros_msg; +} +ssl_league_msgs::msg::UnsportingBehaviorMinor fromProto( + const GameEvent_UnsportingBehaviorMinor & proto_msg) +{ + ssl_league_msgs::msg::UnsportingBehaviorMinor ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + ros_msg.reason = proto_msg.reason(); + return ros_msg; +} +ssl_league_msgs::msg::BotDroppedParts fromProto(const GameEvent_BotDroppedParts & proto_msg) +{ + ssl_league_msgs::msg::BotDroppedParts ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + CopyOptional(proto_msg, ros_msg, by_bot); + CopyOptionalStruct(proto_msg, ros_msg, location); + CopyOptionalStruct(proto_msg, ros_msg, ball_location); + return ros_msg; +} +ssl_league_msgs::msg::ChallengeFlagHandled fromProto( + const GameEvent_ChallengeFlagHandled & proto_msg) +{ + ssl_league_msgs::msg::ChallengeFlagHandled ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + ros_msg.accepted = proto_msg.accepted(); + return ros_msg; +} +ssl_league_msgs::msg::ExcessiveBotSubstitution fromProto( + const GameEvent_ExcessiveBotSubstitution & proto_msg) +{ + ssl_league_msgs::msg::ExcessiveBotSubstitution ros_msg; + ros_msg.by_team = fromProto(proto_msg.by_team()); + return ros_msg; +} + +} // namespace ssl_ros_bridge::game_controller_bridge::message_conversions diff --git a/ssl_ros_bridge/src/game_controller_bridge/message_conversions.hpp b/ssl_ros_bridge/src/game_controller_bridge/message_conversions.hpp new file mode 100644 index 0000000..7c52f1c --- /dev/null +++ b/ssl_ros_bridge/src/game_controller_bridge/message_conversions.hpp @@ -0,0 +1,111 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef GAME_CONTROLLER_BRIDGE__MESSAGE_CONVERSIONS_HPP_ +#define GAME_CONTROLLER_BRIDGE__MESSAGE_CONVERSIONS_HPP_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ssl_ros_bridge::game_controller_bridge::message_conversions +{ + +geometry_msgs::msg::Point32 fromProto(const Vector2 & proto_msg); +geometry_msgs::msg::Point32 fromProto(const Vector3 & proto_msg); + +ssl_league_msgs::msg::Referee fromProto(const Referee & proto_msg); +ssl_league_msgs::msg::TeamInfo fromProto(const Referee::TeamInfo & proto_msg); +ssl_league_msgs::msg::GameEvent fromProto(const GameEvent & proto_msg); +ssl_league_msgs::msg::GameEventProposalGroup fromProto(const GameEventProposalGroup & proto_msg); + +ssl_league_msgs::msg::Division fromProto(const Division & proto_msg); +ssl_league_msgs::msg::RobotId fromProto(const RobotId & proto_msg); +ssl_league_msgs::msg::Team fromProto(const Team & proto_msg); + +ssl_league_msgs::msg::AimlessKick fromProto(const GameEvent_AimlessKick & proto_msg); +ssl_league_msgs::msg::AttackerDoubleTouchedBall fromProto( + const GameEvent_AttackerDoubleTouchedBall & proto_msg); +ssl_league_msgs::msg::AttackerTooCloseToDefenseArea fromProto( + const GameEvent_AttackerTooCloseToDefenseArea & proto_msg); +ssl_league_msgs::msg::AttackerTouchedBallInDefenseArea fromProto( + const GameEvent_AttackerTouchedBallInDefenseArea & proto_msg); +ssl_league_msgs::msg::AttackerTouchedOpponentInDefenseArea fromProto( + const GameEvent_AttackerTouchedOpponentInDefenseArea & proto_msg); +ssl_league_msgs::msg::BallLeftField fromProto(const GameEvent_BallLeftField & proto_msg); +ssl_league_msgs::msg::BotCrashDrawn fromProto(const GameEvent_BotCrashDrawn & proto_msg); +ssl_league_msgs::msg::BotCrashUnique fromProto(const GameEvent_BotCrashUnique & proto_msg); +ssl_league_msgs::msg::BotDribbledBallTooFar fromProto( + const GameEvent_BotDribbledBallTooFar & proto_msg); +ssl_league_msgs::msg::BotHeldBallDeliberately fromProto( + const GameEvent_BotHeldBallDeliberately & proto_msg); +ssl_league_msgs::msg::BotInterferedPlacement fromProto( + const GameEvent_BotInterferedPlacement & proto_msg); +ssl_league_msgs::msg::BotKickedBallTooFast fromProto( + const GameEvent_BotKickedBallTooFast & proto_msg); +ssl_league_msgs::msg::BotPushedBot fromProto(const GameEvent_BotPushedBot & proto_msg); +ssl_league_msgs::msg::BotSubstitution fromProto(const GameEvent_BotSubstitution & proto_msg); +ssl_league_msgs::msg::BotTippedOver fromProto(const GameEvent_BotTippedOver & proto_msg); +ssl_league_msgs::msg::BotTooFastInStop fromProto(const GameEvent_BotTooFastInStop & proto_msg); +ssl_league_msgs::msg::BoundaryCrossing fromProto(const GameEvent_BoundaryCrossing & proto_msg); +ssl_league_msgs::msg::ChallengeFlag fromProto(const GameEvent_ChallengeFlag & proto_msg); +ssl_league_msgs::msg::ChippedGoal fromProto(const GameEvent_ChippedGoal & proto_msg); +ssl_league_msgs::msg::DefenderInDefenseArea fromProto( + const GameEvent_DefenderInDefenseArea & proto_msg); +ssl_league_msgs::msg::DefenderInDefenseAreaPartially fromProto( + const GameEvent_DefenderInDefenseAreaPartially & proto_msg); +ssl_league_msgs::msg::DefenderTooCloseToKickPoint fromProto( + const GameEvent_DefenderTooCloseToKickPoint & proto_msg); +ssl_league_msgs::msg::EmergencyStop fromProto(const GameEvent_EmergencyStop & proto_msg); +ssl_league_msgs::msg::Goal fromProto(const GameEvent_Goal & proto_msg); +ssl_league_msgs::msg::IndirectGoal fromProto(const GameEvent_IndirectGoal & proto_msg); +ssl_league_msgs::msg::KeeperHeldBall fromProto(const GameEvent_KeeperHeldBall & proto_msg); +ssl_league_msgs::msg::KickTimeout fromProto(const GameEvent_KickTimeout & proto_msg); +ssl_league_msgs::msg::MultipleCards fromProto(const GameEvent_MultipleCards & proto_msg); +ssl_league_msgs::msg::MultipleFouls fromProto(const GameEvent_MultipleFouls & proto_msg); +ssl_league_msgs::msg::MultiplePlacementFailures fromProto( + const GameEvent_MultiplePlacementFailures & proto_msg); +ssl_league_msgs::msg::NoProgressInGame fromProto(const GameEvent_NoProgressInGame & proto_msg); +ssl_league_msgs::msg::PenaltyKickFailed fromProto(const GameEvent_PenaltyKickFailed & proto_msg); +ssl_league_msgs::msg::PlacementFailed fromProto(const GameEvent_PlacementFailed & proto_msg); +ssl_league_msgs::msg::PlacementSucceeded fromProto(const GameEvent_PlacementSucceeded & proto_msg); +ssl_league_msgs::msg::Prepared fromProto(const GameEvent_Prepared & proto_msg); +ssl_league_msgs::msg::TooManyRobots fromProto(const GameEvent_TooManyRobots & proto_msg); +ssl_league_msgs::msg::UnsportingBehaviorMajor fromProto( + const GameEvent_UnsportingBehaviorMajor & proto_msg); +ssl_league_msgs::msg::UnsportingBehaviorMinor fromProto( + const GameEvent_UnsportingBehaviorMinor & proto_msg); +ssl_league_msgs::msg::BotDroppedParts fromProto(const GameEvent_BotDroppedParts & proto_msg); +ssl_league_msgs::msg::ChallengeFlagHandled fromProto( + const GameEvent_ChallengeFlagHandled & proto_msg); +ssl_league_msgs::msg::ExcessiveBotSubstitution fromProto( + const GameEvent_ExcessiveBotSubstitution & proto_msg); + +} // namespace ssl_ros_bridge::game_controller_bridge::message_conversions + +#endif // GAME_CONTROLLER_BRIDGE__MESSAGE_CONVERSIONS_HPP_ diff --git a/ssl_ros_bridge/src/team_client/CMakeLists.txt b/ssl_ros_bridge/src/team_client/CMakeLists.txt new file mode 100644 index 0000000..b34c39c --- /dev/null +++ b/ssl_ros_bridge/src/team_client/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library(${PROJECT_NAME}_tame_client SHARED + team_client_node.cpp + team_client.cpp +) +target_include_directories(${PROJECT_NAME}_tame_client PRIVATE ..) +ament_target_dependencies(${PROJECT_NAME}_tame_client + rclcpp + rclcpp_components + ssl_ros_bridge_msgs + ssl_league_msgs + ssl_league_protobufs +) +target_link_libraries(${PROJECT_NAME}_tame_client ${PROJECT_NAME}_core) + +rclcpp_components_register_node( + ${PROJECT_NAME}_tame_client + PLUGIN "ssl_ros_bridge::game_controller_bridge::TeamClientNode" + EXECUTABLE team_client_node +) + +install(TARGETS ${PROJECT_NAME}_tame_client DESTINATION lib) + diff --git a/ssl_ros_bridge/src/team_client/team_client.cpp b/ssl_ros_bridge/src/team_client/team_client.cpp new file mode 100644 index 0000000..76a048f --- /dev/null +++ b/ssl_ros_bridge/src/team_client/team_client.cpp @@ -0,0 +1,269 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "team_client.hpp" +#include +#include + +namespace ssl_ros_bridge::game_controller_bridge +{ + +TeamClient::TeamClient(rclcpp::Logger logger) +: logger_(logger), + socket_(io_service_) +{ +} + +bool TeamClient::Connect(const ConnectionParameters & parameters) +{ + connected_ = false; + if (!AttemptToConnectSocket(parameters.address, parameters.port)) { + return false; + } + if (!AttemptToRegister(parameters.team_name, parameters.team_color)) { + socket_.close(); + return false; + } + RCLCPP_INFO(logger_, "Team client connected to Game Controller!"); + connected_ = true; + return true; +} + +void TeamClient::Disconnect() +{ + connected_ = false; + socket_.close(); +} + +TeamClient::Result TeamClient::SetDesiredKeeper(const int keeper_id) +{ + TeamToController team_to_controller; + team_to_controller.set_desired_keeper(keeper_id); + return SendRequest(team_to_controller); +} + +TeamClient::Result TeamClient::RequestBotSubstitution() +{ + TeamToController team_to_controller; + team_to_controller.set_substitute_bot(true); + return SendRequest(team_to_controller); +} + +TeamClient::Result TeamClient::SetAdvantageChoice(const AdvantageChoiceOption & choice) +{ + TeamToController team_to_controller; + switch (choice) { + case AdvantageChoiceOption::Stop: + team_to_controller.set_advantage_choice(STOP); + break; + case AdvantageChoiceOption::Continue: + team_to_controller.set_advantage_choice(CONTINUE); + break; + } + return SendRequest(team_to_controller); +} + +TeamClient::PingResult TeamClient::Ping() +{ + TeamToController team_to_controller; + team_to_controller.set_ping(true); + PingResult result; + const auto send_time = std::chrono::steady_clock::now(); + result.request_result = SendRequest(team_to_controller); + result.ping = std::chrono::steady_clock::now() - send_time; + return result; +} + +bool TeamClient::AttemptToConnectSocket( + const boost::asio::ip::address & address, + const uint16_t port) +{ + if (socket_.is_open()) { + socket_.close(); + } + boost::system::error_code error_code; + RCLCPP_INFO(logger_, "Connecting to game controller at %s:%d", address.to_string().c_str(), port); + socket_.connect(boost::asio::ip::tcp::endpoint(address, port), error_code); + if (error_code) { + RCLCPP_WARN(logger_, "Team client connect failed: %s", error_code.message().c_str()); + return false; + } + + ControllerToTeam controller_to_team; + if (!WaitForReply(controller_to_team)) { + return false; + } + + if (!controller_to_team.has_controller_reply()) { + RCLCPP_ERROR(logger_, "Got ControllerToTeam message with no ControllerReply payload!"); + return false; + } + + const auto & controller_reply = controller_to_team.controller_reply(); + + if (controller_reply.has_status_code() && controller_reply.status_code() != ControllerReply::OK) { + if (controller_reply.has_reason()) { + RCLCPP_ERROR( + logger_, "Game controller sent bad status code (%d) with reason: %s", + controller_reply.status_code(), controller_reply.reason().c_str()); + } else { + RCLCPP_ERROR( + logger_, "Game controller sent bad status code: %d", + controller_reply.status_code()); + } + socket_.close(); + return false; + } + + if (!controller_reply.has_next_token()) { + RCLCPP_ERROR(logger_, "Controller reply did not include a token!"); + } else { + next_token_ = controller_reply.next_token(); + } + + return true; +} + +bool TeamClient::AttemptToRegister(const std::string & team_name, const TeamColor team_color) +{ + RCLCPP_INFO(logger_, "Registering team..."); + TeamRegistration team_registration_msg; + team_registration_msg.set_team_name(team_name); + switch (team_color) { + case TeamColor::Blue: + team_registration_msg.set_team(BLUE); + break; + case TeamColor::Yellow: + team_registration_msg.set_team(YELLOW); + break; + default: + break; + } + // team_registration_msg.mutable_signature()->set_token(next_token_); + + boost::asio::streambuf boost_streambuf; + std::ostream std_stream(&boost_streambuf); + google::protobuf::util::SerializeDelimitedToOstream(team_registration_msg, &std_stream); + + boost::asio::write(socket_, boost_streambuf, boost::asio::transfer_all()); + + ControllerToTeam controller_to_team; + if (!WaitForReply(controller_to_team)) { + return false; + } + + if (!controller_to_team.has_controller_reply()) { + RCLCPP_ERROR(logger_, "Got ControllerToTeam message with no ControllerReply payload!"); + return false; + } + + const auto & controller_reply = controller_to_team.controller_reply(); + + if (controller_reply.has_status_code() && controller_reply.status_code() != ControllerReply::OK) { + if (controller_reply.has_reason()) { + RCLCPP_WARN( + logger_, "Game controller sent bad status code (%d) with reason: %s", + controller_reply.status_code(), controller_reply.reason().c_str()); + } else { + RCLCPP_WARN( + logger_, "Game controller sent bad status code: %d", + controller_reply.status_code()); + } + return false; + } + + return true; +} + +bool TeamClient::WaitForReply(ControllerToTeam & reply) +{ + boost::system::error_code error_code; + rclcpp::WallRate retry_rate(10 /*Hz*/); + std::size_t bytes_received = 0; + const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(1); + while (true) { + if (std::chrono::steady_clock::now() >= timeout) { + RCLCPP_ERROR(logger_, "Team client timed out waiting for a reply!"); + return false; + } + const auto bytes_available = socket_.available(error_code); + if (error_code && error_code != boost::asio::error::eof) { + RCLCPP_ERROR(logger_, "Team client TCP error: %s", error_code.message().c_str()); + return false; + } + if (bytes_available == 0) { + retry_rate.sleep(); + continue; + } + bytes_received = boost::asio::read( + socket_, boost::asio::buffer( + buffer_), boost::asio::transfer_at_least(bytes_available), error_code); + if (error_code && error_code != boost::asio::error::eof) { + RCLCPP_ERROR(logger_, "Team client TCP error: %s", error_code.message().c_str()); + return false; + } + break; + } + google::protobuf::io::ArrayInputStream array_input_stream(buffer_.data(), bytes_received); + if (!google::protobuf::util::ParseDelimitedFromZeroCopyStream( + &reply, &array_input_stream, + nullptr)) + { + RCLCPP_ERROR(logger_, "Team client could not parse reply message."); + return false; + } + + return true; +} + +TeamClient::Result TeamClient::SendRequest(TeamToController & request) +{ + boost::asio::streambuf boost_streambuf; + std::ostream std_stream(&boost_streambuf); + google::protobuf::util::SerializeDelimitedToOstream(request, &std_stream); + + boost::asio::write(socket_, boost_streambuf, boost::asio::transfer_all()); + + ControllerToTeam controller_to_team; + if (!WaitForReply(controller_to_team)) { + return {false, "Client error."}; + } + + if (!controller_to_team.has_controller_reply()) { + RCLCPP_ERROR(logger_, "Got ControllerToTeam message with no ControllerReply payload!"); + return {false, "Client error."}; + } + + const auto & controller_reply = controller_to_team.controller_reply(); + + if (controller_reply.has_next_token()) { + next_token_ = controller_reply.next_token(); + } + + Result result; + result.accepted = controller_reply.has_status_code() && + (controller_reply.status_code() == ControllerReply::OK); + if (controller_reply.has_reason()) { + result.reason = controller_reply.reason(); + } + return result; +} + +} // namespace ssl_ros_bridge::game_controller_bridge diff --git a/ssl_ros_bridge/src/team_client/team_client.hpp b/ssl_ros_bridge/src/team_client/team_client.hpp new file mode 100644 index 0000000..7e031c4 --- /dev/null +++ b/ssl_ros_bridge/src/team_client/team_client.hpp @@ -0,0 +1,110 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef TEAM_CLIENT_HPP_ +#define TEAM_CLIENT_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ssl_ros_bridge::game_controller_bridge +{ + +class TeamClient +{ +public: + struct Result + { + bool accepted; + std::string reason; + }; + + struct PingResult + { + Result request_result; + std::chrono::duration ping; + }; + + enum class TeamColor + { + Auto, + Blue, + Yellow + }; + + struct ConnectionParameters + { + boost::asio::ip::address address; + uint16_t port; + std::string team_name; + TeamColor team_color; + }; + + enum class AdvantageChoiceOption + { + Stop, + Continue + }; + + explicit TeamClient(rclcpp::Logger logger); + + bool Connect(const ConnectionParameters & parameters); + + void Disconnect(); + + bool IsConnected() const + { + return connected_; + } + + Result SetDesiredKeeper(const int keeper_id); + + Result RequestBotSubstitution(); + + Result SetAdvantageChoice(const AdvantageChoiceOption & choice); + + PingResult Ping(); + +private: + rclcpp::Logger logger_; + bool connected_{false}; + std::string next_token_; + std::array buffer_; + std::size_t buffer_index_{0}; + boost::asio::io_service io_service_; + boost::asio::ip::tcp::socket socket_; + + bool AttemptToConnectSocket(const boost::asio::ip::address & address, const uint16_t port); + + bool AttemptToRegister(const std::string & team_name, const TeamColor team_color); + + bool WaitForReply(ControllerToTeam & reply); + + Result SendRequest(TeamToController & request); +}; + +} // namespace ssl_ros_bridge::game_controller_bridge +#endif // TEAM_CLIENT_HPP_ diff --git a/ssl_ros_bridge/src/team_client/team_client_node.cpp b/ssl_ros_bridge/src/team_client/team_client_node.cpp new file mode 100644 index 0000000..0d38481 --- /dev/null +++ b/ssl_ros_bridge/src/team_client/team_client_node.cpp @@ -0,0 +1,207 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include +#include +#include "core/protobuf_logging.hpp" +#include +#include +#include +#include +#include +#include "team_client.hpp" + +namespace ssl_ros_bridge::game_controller_bridge +{ +class TeamClientNode : public rclcpp::Node +{ +public: + explicit TeamClientNode(const rclcpp::NodeOptions & options) + : rclcpp::Node("team_client_node", options), + team_client_(get_logger().get_child("team_client")) + { + SET_ROS_PROTOBUF_LOG_HANDLER("team_client_node.protobuf"); + + declare_parameter("gc_ip_address", ""); + declare_parameter("gc_port", 10008); + declare_parameter("team_name", "A-Team"); + declare_parameter("team_color", "auto"); + + set_desired_keeper_service_ = create_service( + "~/set_desired_keeper", + std::bind( + &TeamClientNode::HandleSetDesiredKeeper, this, std::placeholders::_1, + std::placeholders::_2), rclcpp::ServicesQoS()); + + substitute_bot_service_ = create_service( + "~/substitute_bot", + std::bind( + &TeamClientNode::HandleSubstituteBot, this, std::placeholders::_1, + std::placeholders::_2), rclcpp::ServicesQoS()); + + reconnect_service_ = create_service( + "~/reconnect", + std::bind( + &TeamClientNode::HandleReconnectTeamClient, this, std::placeholders::_1, + std::placeholders::_2), rclcpp::ServicesQoS()); + + advantage_choice_service_ = create_service( + "~/set_advantage_choice", + std::bind( + &TeamClientNode::HandleSetAdvantageChoice, this, std::placeholders::_1, + std::placeholders::_2), rclcpp::ServicesQoS()); + + connection_status_publisher_ = create_publisher( + "~/connection_status", rclcpp::ServicesQoS()); + + const auto ping_period = declare_parameter("ping_period", 5); + ping_timer_ = + create_wall_timer( + std::chrono::duration(ping_period), + std::bind(&TeamClientNode::PingCallback, this)); + + if (!Connect()) { + RCLCPP_ERROR(get_logger(), "Failed to connect to Game Controller."); + } + } + +private: + TeamClient team_client_; + rclcpp::Service::SharedPtr set_desired_keeper_service_; + rclcpp::Service::SharedPtr substitute_bot_service_; + rclcpp::Service::SharedPtr reconnect_service_; + rclcpp::Service::SharedPtr advantage_choice_service_; + rclcpp::Publisher::SharedPtr + connection_status_publisher_; + rclcpp::TimerBase::SharedPtr ping_timer_; + + bool Connect() + { + const auto address_string = get_parameter("gc_ip_address").as_string(); + if(address_string.empty()) { + RCLCPP_WARN(get_logger(), "Address parameter empty. Cannot attempt to connect."); + // Returning "success" because this isn't a problem during setup. + return true; + } + TeamClient::ConnectionParameters connection_parameters; + connection_parameters.address = boost::asio::ip::address::from_string(address_string); + connection_parameters.port = get_parameter("gc_port").as_int(); + connection_parameters.team_name = get_parameter("team_name").as_string(); + const auto team_color_name = get_parameter("team_color").as_string(); + if (team_color_name == "auto") { + connection_parameters.team_color = TeamClient::TeamColor::Auto; + } else if (team_color_name == "blue") { + connection_parameters.team_color = TeamClient::TeamColor::Blue; + } else if (team_color_name == "yellow") { + connection_parameters.team_color = TeamClient::TeamColor::Yellow; + } + return team_client_.Connect(connection_parameters); + } + + void HandleSetDesiredKeeper( + const ssl_ros_bridge_msgs::srv::SetDesiredKeeper::Request::SharedPtr request, + ssl_ros_bridge_msgs::srv::SetDesiredKeeper::Response::SharedPtr response) + { + if (!team_client_.IsConnected()) { + response->success = false; + response->reason = "Team client is not connected to the Game Controller."; + RCLCPP_ERROR( + get_logger(), "Service %s called before team client connected.", + set_desired_keeper_service_->get_service_name()); + return; + } + const auto result = team_client_.SetDesiredKeeper(request->desired_keeper); + response->success = result.accepted; + response->reason = result.reason; + } + + void HandleSubstituteBot( + const ssl_ros_bridge_msgs::srv::SubstituteBot::Request::SharedPtr /*request*/, + ssl_ros_bridge_msgs::srv::SubstituteBot::Response::SharedPtr response) + { + if (!team_client_.IsConnected()) { + response->success = false; + response->reason = "Team client is not connected to the Game Controller."; + RCLCPP_ERROR( + get_logger(), "Service %s called before team client connected.", + substitute_bot_service_->get_service_name()); + return; + } + const auto result = team_client_.RequestBotSubstitution(); + response->success = result.accepted; + response->reason = result.reason; + } + + void HandleReconnectTeamClient( + const ssl_ros_bridge_msgs::srv::ReconnectTeamClient::Request::SharedPtr request, + ssl_ros_bridge_msgs::srv::ReconnectTeamClient::Response::SharedPtr response) + { + if(!request->server_address.empty()) { + set_parameter(rclcpp::Parameter("gc_ip_address", request->server_address)); + } + response->success = Connect(); + } + + void HandleSetAdvantageChoice( + const ssl_ros_bridge_msgs::srv::SetTeamAdvantageChoice::Request::SharedPtr request, + ssl_ros_bridge_msgs::srv::SetTeamAdvantageChoice::Response::SharedPtr response) + { + if (!team_client_.IsConnected()) { + response->success = false; + response->reason = "Team client is not connected to the Game Controller."; + RCLCPP_ERROR( + get_logger(), "Service %s called before team client connected.", + advantage_choice_service_->get_service_name()); + return; + } + TeamClient::AdvantageChoiceOption choice; + switch (request->choice) { + case ssl_ros_bridge_msgs::srv::SetTeamAdvantageChoice::Request::STOP: + choice = TeamClient::AdvantageChoiceOption::Stop; + break; + case ssl_ros_bridge_msgs::srv::SetTeamAdvantageChoice::Request::CONTINUE: + choice = TeamClient::AdvantageChoiceOption::Continue; + break; + } + const auto result = team_client_.SetAdvantageChoice(choice); + response->success = result.accepted; + response->reason = result.reason; + } + + void PingCallback() + { + ssl_ros_bridge_msgs::msg::TeamClientConnectionStatus status_msg; + status_msg.connected = team_client_.IsConnected(); + if (team_client_.IsConnected()) { + const auto result = team_client_.Ping(); + status_msg.connected = result.request_result.accepted; + status_msg.ping = rclcpp::Duration(result.ping); + if(!result.request_result.accepted) { + team_client_.Disconnect(); + RCLCPP_WARN(get_logger(), "Ping failed. Team client disconnected."); + } + } + connection_status_publisher_->publish(status_msg); + } +}; +} // namespace ssl_ros_bridge::game_controller_bridge + +RCLCPP_COMPONENTS_REGISTER_NODE(ssl_ros_bridge::game_controller_bridge::TeamClientNode) diff --git a/ssl_ros_bridge/src/vision_bridge/CMakeLists.txt b/ssl_ros_bridge/src/vision_bridge/CMakeLists.txt new file mode 100644 index 0000000..bff7608 --- /dev/null +++ b/ssl_ros_bridge/src/vision_bridge/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library(${PROJECT_NAME}_vision_bridge SHARED + ssl_vision_bridge_node.cpp + message_conversions.cpp +) +target_include_directories(${PROJECT_NAME}_vision_bridge PRIVATE ..) +ament_target_dependencies(${PROJECT_NAME}_vision_bridge + rclcpp + rclcpp_components + ssl_league_msgs + ssl_league_protobufs + tf2 + tf2_geometry_msgs +) +target_link_libraries(${PROJECT_NAME}_vision_bridge ${PROJECT_NAME}_core) + +rclcpp_components_register_node( + ${PROJECT_NAME}_vision_bridge + PLUGIN "ssl_ros_bridge::vision_bridge::SSLVisionBridgeNode" + EXECUTABLE vision_bridge_node +) + +install(TARGETS ${PROJECT_NAME}_vision_bridge DESTINATION lib) diff --git a/ssl_ros_bridge/src/vision_bridge/message_conversions.cpp b/ssl_ros_bridge/src/vision_bridge/message_conversions.cpp new file mode 100644 index 0000000..55de93d --- /dev/null +++ b/ssl_ros_bridge/src/vision_bridge/message_conversions.cpp @@ -0,0 +1,195 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "message_conversions.hpp" + +#include + +#include + +#include + +namespace ssl_ros_bridge::vision_bridge::message_conversions +{ + +constexpr float mmTom = 1.0e-3f; +constexpr int secToNanosec = 1e9; + +ssl_league_msgs::msg::VisionDetectionBall fromProto(const SSL_DetectionBall & proto_msg) +{ + ssl_league_msgs::msg::VisionDetectionBall ros_msg; + ros_msg.confidence = proto_msg.confidence(); + ros_msg.area = proto_msg.area(); // assuming this is in pixels, not verified + ros_msg.pos.x = proto_msg.x() * mmTom; + ros_msg.pos.y = proto_msg.y() * mmTom; + ros_msg.pos.z = proto_msg.z() * mmTom; + ros_msg.pixel.x = proto_msg.pixel_x(); + ros_msg.pixel.y = proto_msg.pixel_y(); + + return ros_msg; +} +ssl_league_msgs::msg::VisionDetectionRobot fromProto(const SSL_DetectionRobot & proto_msg) +{ + ssl_league_msgs::msg::VisionDetectionRobot ros_msg; + ros_msg.confidence = proto_msg.confidence(); + ros_msg.robot_id = proto_msg.robot_id(); + ros_msg.pose.position.x = proto_msg.x() * mmTom; + ros_msg.pose.position.y = proto_msg.y() * mmTom; + ros_msg.pose.position.z = 0; + ros_msg.pose.orientation = + tf2::toMsg(tf2::Quaternion(tf2::Vector3(0, 0, 1), proto_msg.orientation())); + ros_msg.pixel.x = proto_msg.pixel_x(); + ros_msg.pixel.y = proto_msg.pixel_y(); + ros_msg.height = proto_msg.height(); + + return ros_msg; +} +ssl_league_msgs::msg::VisionDetectionFrame fromProto(const SSL_DetectionFrame & proto_msg) +{ + ssl_league_msgs::msg::VisionDetectionFrame ros_msg; + ros_msg.frame_number = proto_msg.frame_number(); + ros_msg.t_capture = rclcpp::Time(static_cast(proto_msg.t_capture() * secToNanosec)); + ros_msg.t_sent = rclcpp::Time(static_cast(proto_msg.t_sent() * secToNanosec)); + ros_msg.t_capture_camera = + rclcpp::Time(static_cast(proto_msg.t_capture_camera() * secToNanosec)); + ros_msg.camera_id = proto_msg.camera_id(); + std::transform( + proto_msg.balls().begin(), + proto_msg.balls().end(), + std::back_inserter(ros_msg.balls), + [](const auto & p) {return fromProto(p);}); + std::transform( + proto_msg.robots_yellow().begin(), + proto_msg.robots_yellow().end(), + std::back_inserter(ros_msg.robots_yellow), + [](const auto & p) {return fromProto(p);}); + std::transform( + proto_msg.robots_blue().begin(), + proto_msg.robots_blue().end(), + std::back_inserter(ros_msg.robots_blue), + [](const auto & p) {return fromProto(p);}); + + return ros_msg; +} + +ssl_league_msgs::msg::VisionFieldLineSegment fromProto(const SSL_FieldLineSegment & proto_msg) +{ + ssl_league_msgs::msg::VisionFieldLineSegment ros_msg; + ros_msg.name = proto_msg.name(); + ros_msg.p1.x = proto_msg.p1().x() * mmTom; + ros_msg.p1.y = proto_msg.p1().y() * mmTom; + ros_msg.p2.x = proto_msg.p2().x() * mmTom; + ros_msg.p2.y = proto_msg.p2().y() * mmTom; + ros_msg.thickness = proto_msg.thickness() * mmTom; + + return ros_msg; +} +ssl_league_msgs::msg::VisionFieldCircularArc fromProto(const SSL_FieldCircularArc & proto_msg) +{ + ssl_league_msgs::msg::VisionFieldCircularArc ros_msg; + ros_msg.name = proto_msg.name(); + ros_msg.center.x = proto_msg.center().x() * mmTom; + ros_msg.center.y = proto_msg.center().y() * mmTom; + ros_msg.radius = proto_msg.radius() * mmTom; + ros_msg.a1 = proto_msg.a1(); + ros_msg.a2 = proto_msg.a2(); + ros_msg.thickness = proto_msg.thickness() * mmTom; + + return ros_msg; +} +ssl_league_msgs::msg::VisionGeometryFieldSize fromProto(const SSL_GeometryFieldSize & proto_msg) +{ + ssl_league_msgs::msg::VisionGeometryFieldSize ros_msg; + ros_msg.field_length = proto_msg.field_length() * mmTom; + ros_msg.field_width = proto_msg.field_width() * mmTom; + ros_msg.goal_width = proto_msg.goal_width() * mmTom; + ros_msg.goal_depth = proto_msg.goal_depth() * mmTom; + ros_msg.boundary_width = proto_msg.boundary_width() * mmTom; + std::transform( + proto_msg.field_lines().begin(), + proto_msg.field_lines().end(), + std::back_inserter(ros_msg.field_lines), + [](const auto & p) {return fromProto(p);}); + std::transform( + proto_msg.field_arcs().begin(), + proto_msg.field_arcs().end(), + std::back_inserter(ros_msg.field_arcs), + [](const auto & p) {return fromProto(p);}); + + ros_msg.penalty_area_depth = proto_msg.penalty_area_depth() * mmTom; + ros_msg.penalty_area_width = proto_msg.penalty_area_width() * mmTom; + ros_msg.center_circle_radius = proto_msg.center_circle_radius() * mmTom; + ros_msg.line_thickness = proto_msg.line_thickness() * mmTom; + ros_msg.goal_center_to_penalty_mark = proto_msg.goal_center_to_penalty_mark() * mmTom; + ros_msg.goal_height = proto_msg.goal_height() * mmTom; + ros_msg.ball_radius = proto_msg.ball_radius() * mmTom; + ros_msg.max_robot_radius = proto_msg.max_robot_radius() * mmTom; + + return ros_msg; +} +ssl_league_msgs::msg::VisionGeometryCameraCalibration fromProto( + const SSL_GeometryCameraCalibration & proto_msg) +{ + ssl_league_msgs::msg::VisionGeometryCameraCalibration ros_msg; + ros_msg.camera_id = proto_msg.camera_id(); + ros_msg.focal_length = proto_msg.focal_length(); + ros_msg.principal_point.x = proto_msg.principal_point_x(); + ros_msg.principal_point.y = proto_msg.principal_point_y(); + ros_msg.distortion = proto_msg.distortion(); + ros_msg.pose.orientation.x = proto_msg.q0(); + ros_msg.pose.orientation.y = proto_msg.q1(); + ros_msg.pose.orientation.z = proto_msg.q2(); + ros_msg.pose.orientation.w = proto_msg.q3(); + ros_msg.pose.position.x = proto_msg.tx() * mmTom; + ros_msg.pose.position.y = proto_msg.ty() * mmTom; + ros_msg.pose.position.z = proto_msg.tz() * mmTom; + ros_msg.derived_camera_world_t.x = proto_msg.derived_camera_world_tx() * mmTom; + ros_msg.derived_camera_world_t.y = proto_msg.derived_camera_world_ty() * mmTom; + ros_msg.derived_camera_world_t.z = proto_msg.derived_camera_world_tz() * mmTom; + + return ros_msg; +} +ssl_league_msgs::msg::VisionGeometryData fromProto(const SSL_GeometryData & proto_msg) +{ + ssl_league_msgs::msg::VisionGeometryData ros_msg; + ros_msg.field = fromProto(proto_msg.field()); + std::transform( + proto_msg.calib().begin(), + proto_msg.calib().end(), + std::back_inserter(ros_msg.calibration), + [](const auto & p) {return fromProto(p);}); + + return ros_msg; +} + +ssl_league_msgs::msg::VisionWrapper fromProto(const SSL_WrapperPacket & proto_msg) +{ + ssl_league_msgs::msg::VisionWrapper ros_msg; + if (proto_msg.has_detection()) { + ros_msg.detection.push_back(fromProto(proto_msg.detection())); + } + if (proto_msg.has_geometry()) { + ros_msg.geometry.push_back(fromProto(proto_msg.geometry())); + } + + return ros_msg; +} + +} // namespace ssl_ros_bridge::vision_bridge::message_conversions diff --git a/ssl_ros_bridge/src/vision_bridge/message_conversions.hpp b/ssl_ros_bridge/src/vision_bridge/message_conversions.hpp new file mode 100644 index 0000000..663f4ea --- /dev/null +++ b/ssl_ros_bridge/src/vision_bridge/message_conversions.hpp @@ -0,0 +1,55 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef VISION_BRIDGE__MESSAGE_CONVERSIONS_HPP_ +#define VISION_BRIDGE__MESSAGE_CONVERSIONS_HPP_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ssl_ros_bridge::vision_bridge::message_conversions +{ +ssl_league_msgs::msg::VisionDetectionBall fromProto(const SSL_DetectionBall & proto_msg); +ssl_league_msgs::msg::VisionDetectionRobot fromProto(const SSL_DetectionRobot & proto_msg); +ssl_league_msgs::msg::VisionDetectionFrame fromProto(const SSL_DetectionFrame & proto_msg); + +ssl_league_msgs::msg::VisionFieldLineSegment fromProto(const SSL_FieldLineSegment & proto_msg); +ssl_league_msgs::msg::VisionFieldCircularArc fromProto(const SSL_FieldCircularArc & proto_msg); +ssl_league_msgs::msg::VisionGeometryFieldSize fromProto(const SSL_GeometryFieldSize & proto_msg); +ssl_league_msgs::msg::VisionGeometryCameraCalibration fromProto( + const SSL_GeometryCameraCalibration & proto_msg); +ssl_league_msgs::msg::VisionGeometryData fromProto(const SSL_GeometryData & proto_msg); + +ssl_league_msgs::msg::VisionWrapper fromProto(const SSL_WrapperPacket & proto_msg); + +} // namespace ssl_ros_bridge::vision_bridge::message_conversions + +#endif // VISION_BRIDGE__MESSAGE_CONVERSIONS_HPP_ diff --git a/ssl_ros_bridge/src/vision_bridge/ssl_vision_bridge_node.cpp b/ssl_ros_bridge/src/vision_bridge/ssl_vision_bridge_node.cpp new file mode 100644 index 0000000..2ce5f61 --- /dev/null +++ b/ssl_ros_bridge/src/vision_bridge/ssl_vision_bridge_node.cpp @@ -0,0 +1,77 @@ +// Copyright 2024 A Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include + +#include +#include + +#include +#include + +#include "core/multicast_receiver.hpp" +#include "core/protobuf_logging.hpp" +#include + +#include "message_conversions.hpp" + +namespace ssl_ros_bridge::vision_bridge +{ + +class SSLVisionBridgeNode : public rclcpp::Node +{ +public: + explicit SSLVisionBridgeNode(const rclcpp::NodeOptions & options) + : rclcpp::Node("ssl_vision_bridge", options), + vision_publisher_(create_publisher("~/vision_messages", + rclcpp::SystemDefaultsQoS())), + multicast_receiver_( + declare_parameter("ssl_vision_ip", "224.5.23.2"), + declare_parameter("ssl_vision_port", 10020), + std::bind(&SSLVisionBridgeNode::multicastCallback, this, std::placeholders::_3, + std::placeholders::_4), + declare_parameter("net_interface_address", "")) + { + SET_ROS_PROTOBUF_LOG_HANDLER("ssl_vision_bridge.protobuf"); + } + +private: + rclcpp::Publisher::SharedPtr vision_publisher_; + core::MulticastReceiver multicast_receiver_; + + void multicastCallback(uint8_t * buffer, size_t bytes_received) + { + SSL_WrapperPacket vision_proto; + + // Note: "- 1" is needed due to some weird bug where if the entire buffer + // is used to do the conversion to protobuf, it would silently fail. + // But, if all but the last byte is used, it succeeds and at worst some + // data is lost + if (!vision_proto.ParseFromArray(buffer, bytes_received - 1)) { + vision_publisher_->publish(message_conversions::fromProto(vision_proto)); + } else { + RCLCPP_WARN(get_logger(), "Failed to parse vision protobuf packet"); + } + } +}; + +} // namespace ssl_ros_bridge::vision_bridge + +RCLCPP_COMPONENTS_REGISTER_NODE(ssl_ros_bridge::vision_bridge::SSLVisionBridgeNode) diff --git a/ssl_ros_bridge_msgs/CMakeLists.txt b/ssl_ros_bridge_msgs/CMakeLists.txt new file mode 100644 index 0000000..beda64f --- /dev/null +++ b/ssl_ros_bridge_msgs/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.8) +project(ssl_ros_bridge_msgs) + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(builtin_interfaces REQUIRED) +find_package(std_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(ssl_league_msgs REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + msg/TeamClientConnectionStatus.msg + + srv/ReconnectTeamClient.srv + srv/SetDesiredKeeper.srv + srv/SetTeamAdvantageChoice.srv + srv/SubstituteBot.srv + + DEPENDENCIES + builtin_interfaces + std_msgs + geometry_msgs + sensor_msgs + ssl_league_msgs + ADD_LINTER_TESTS +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_export_dependencies(rosidl_default_runtime) +ament_package() diff --git a/ssl_ros_bridge_msgs/LICENSE b/ssl_ros_bridge_msgs/LICENSE new file mode 100644 index 0000000..30e8e2e --- /dev/null +++ b/ssl_ros_bridge_msgs/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg b/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg new file mode 100644 index 0000000..c2efdda --- /dev/null +++ b/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg @@ -0,0 +1,2 @@ +bool connected +builtin_interfaces/Duration ping diff --git a/ssl_ros_bridge_msgs/package.xml b/ssl_ros_bridge_msgs/package.xml new file mode 100644 index 0000000..13d60dd --- /dev/null +++ b/ssl_ros_bridge_msgs/package.xml @@ -0,0 +1,29 @@ + + + + ssl_ros_bridge_msgs + 0.0.0 + Custom message types for ssl_ros_bridge + Matthew Barulic + MIT + + ament_cmake + + rosidl_default_generators + + rosidl_default_runtime + + builtin_interfaces + std_msgs + geometry_msgs + ssl_league_msgs + + ament_lint_auto + ament_lint_common + + rosidl_interface_packages + + + ament_cmake + + diff --git a/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv b/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv new file mode 100644 index 0000000..251ec62 --- /dev/null +++ b/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv @@ -0,0 +1,9 @@ +# Request + +# Address of the server to connect to. If blank, will reconnect to the last used address. +string server_address "" + +--- +# Response + +bool success diff --git a/ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv b/ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv new file mode 100644 index 0000000..8a54d57 --- /dev/null +++ b/ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv @@ -0,0 +1,6 @@ +# Request +int32 desired_keeper +--- +# Response +bool success +string reason diff --git a/ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv b/ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv new file mode 100644 index 0000000..6cb00d6 --- /dev/null +++ b/ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv @@ -0,0 +1,10 @@ +# Request +uint8 choice + +uint8 STOP=0 +uint8 CONTINUE=1 + +--- +# Response +bool success +string reason diff --git a/ssl_ros_bridge_msgs/srv/SubstituteBot.srv b/ssl_ros_bridge_msgs/srv/SubstituteBot.srv new file mode 100644 index 0000000..7b785ef --- /dev/null +++ b/ssl_ros_bridge_msgs/srv/SubstituteBot.srv @@ -0,0 +1,5 @@ +# Request +--- +# Response +bool success +string reason From d71ee5adb4e14a8ea62601174e9f87fe57ab858f Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 07:46:15 +0900 Subject: [PATCH 04/10] Adds package descriptions to readme. --- README.md | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 43b28c9..29fc359 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,152 @@ This repository provides ROS 2 packages containing utilities for connecting your This repo is designed to be cloned into a colcon workspace. You can clone this repo alongside your own code or as a submodule within your own repository. - +## Usage + +The intended usage of these packages is to configure and run the nodes in the ssl_ros_bridge package alongside your own nodes. Each node is provided as a component with a standalone executable available. Either can be used. + +For most teams, in most scenarios, the only parameter that needs to be configured is the team name. Automatic discovery and multi-interface multicast listening will handle most common network setups. + +## Packages + +### ssl_league_protobufs + +This package includes the league-defined protobuf files and builds them into a library available for other packages. + +### ssl_league_msgs + +This package defines ROS messages which closely mirror the league protobufs. + +#### Optional Fields + +The SSL League protobufs make extensive use of optional fields, which are unfortunately not currently supported in ROS messages. ssl_ros_bridge uses array field types to hold optionals. These fields will either hold one or zero elements. This does introduce some ambiguity between optional fields and actual array fields. Users should refer to the corresponding protobuf definitions for clarity. + +_Note:_ ROS fields do support bounded-size array types. We could enforce the "zero or one" behavior with these size limits, but this has caused problems with some ROS tools in the past (ie. PlotJuggler) which don't parse the message definitions correctly with these limits in place. + +#### Recursive Message Definitions + +The league protobufs include a few recursive definitions. For example, the `GameEvent` message may hold a `MultipleFouls` message which itself holds an array of `GameEvent` messages. ROS does not support recursive message types. In practice, most teams will not need these fields, so ssl_ros_bridge omits them from the ROS messages. + +### ssl_ros_bridge_msgs + +This package defines interfaces used by the bridge nodes which are not mirrors of league messages. These are mostly services users can use to send requests up to league software. + +### ssl_ros_bridge + +This package provides the nodes which implement the bridge between league software and ROS systems. + +#### vision_bridge + +This node listens for the multicast vision messages sent by ssl-vision and publishes them into ROS. + +##### Published Topics + +* ~/vision_messages + * Type: [ssl_league_msgs/msg/VisionWrapper](/blob/main/ssl_league_msgs/msg/vision/VisionWrapper.msg) + * Contains vision data including robot detections, ball detections, and field geometry. + +##### Parameters + +* ssl_vision_ip + * Type: string + * Default: "224.5.23.2" + * The multicast group address to listen for. +* ssl_vision_port + * Type: int + * Default: 10020 + * The multicast group port to listen for. +* net_interface_address + * Type: string + * Default: empty + * When empty, the node will join the multicast group on all interfaces. When set to an IP address associated with one of your machine's network interfaces, the node will only join the multicast group on that interface. + +#### game_controller_bridge + +This node listens for multicast game controller messages and republishes them into ROS. + +##### Automatic Game Controller Discovery + +The team client node needs to know the IP address fo the game controller server to establish its connection. This address is often different at different fields even within the same event and can be tedious to keep configured correctly. The game controller bridge node will automatically configure the team client node with the correct address based on the sender of the multicast messages. The game controller bridge will only reconfigure the team client node if it is not already connected. + +##### Published Topics + +* ~/referee_messages + * Type: [ssl_league_msgs/msg/Referee](/blob/main/ssl_league_msgs/msg/game_controller/Referee.msg) + * Contains the latest information from the game controller. + +##### Subscribed Topics + +* /team_client_node/connection_status + * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](/blob/main/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) + * Used for automatic game controller discovery. + +##### Service Clients + +* /team_client_node/reconnect + * Type: [ssl_ros_bridge_msgs/srv/ReconnectTeamClient](/blob/main/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) + * Used for automatic game controller discovery. + +##### Parameters + +* multicast.address + * Type: string + * Default: "224.5.23.1" + * The multicast group address to listen for. +* multicast.port + * Type: int + * Default: 10003 + * The multicast group port to listen for. +* net_interface_address + * Type: string + * Default: empty + * When empty, the node will join the multicast group on all interfaces. When set to an IP address associated with one of your machine's network interfaces, the node will only join the multicast group on that interface. + +#### team_client + +This node connects as a team client to the game controller server. ROS services can then be used to send requests to the game controller, such as changing the goal keeper ID number. + +_Note_: Encrypted connections are not currently supported. + +##### Published Topics + +* ~/connection_status + * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](/blob/main/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) + * Contains the connection status and ping time of the team client to the game controller server. + +##### Services + +* ~/set_desired_keeper + * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv) + * Sends a request to the game controller to change your team's keeper ID. +* ~/substitute_bot + * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SubstituteBot.srv) + * Sends a request to the game controller to substitute a bot. +* ~/reconnect + * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) + * Reconnects the team client to the game controller. + * _Note_: If the address field of the request is empty, the team client node will reconnect to the same server it was previously configured for. If the address field of the request is not empty, the `gc_ip_address` parameter will be overwritten with this new address. +* ~/set_advantage_choice + * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv) + * Sends a request to set the team's advantage choice to the game controller. + +##### Parameters + +* gc_ip_address + * Type: string + * Default: empty + * The address of the game controller server. Leave empty to let the game controller bridge discovery the address automatically. +* gc_port + * Type: int + * Default: 10008 + * The port on the game controller server the team client will connect to. +* team_name + * Type: string + * Default: "A-Team" + * The name of the team the client will try to connect as. +* team_color + * Type: string + * Default: "auto" + * The team color to connect as. Only relevant if a team is playing against itself and the identical team names need to be disambiguated. ## Contributing From 609140d191a2c3fa58246c545cc5a59308d127f6 Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 13:02:23 +0900 Subject: [PATCH 05/10] Fixes broken links in readme and contributing.md --- CONTRIBUTING.md | 10 +++++----- README.md | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2128c16..8c62f9e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,9 +29,9 @@ to <>. --> -Before you ask a question, it is best to search for existing [Issues](/issues) or [Discussions](/discussions) that might help you. In case you have found a suitable issue/topic and still need clarification, you can write your question in the existing issue/topic. +Before you ask a question, it is best to search for existing [Issues](https://github.com/SSL-A-Team/ssl_ros_bridge/issues) or [Discussions](https://github.com/SSL-A-Team/ssl_ros_bridge/discussions) that might help you. In case you have found a suitable issue/topic and still need clarification, you can write your question in the existing issue/topic. -If you don't find what you're looking for there, you can start a [new question topic](/discussions/new?category=q-a). +If you don't find what you're looking for there, you can start a [new question topic](https://github.com/SSL-A-Team/ssl_ros_bridge/discussions/new?category=q-a). If it turns out your question highlights a new bug in our software, we'll create an issue from your Q&A post. @@ -61,7 +61,7 @@ A good bug report shouldn't leave others needing to chase you up for more inform We use GitHub issues to track bugs and errors. If you run into an issue with the project: -- Open an [Issue](/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Open an [Issue](https://github.com/SSL-A-Team/ssl_ros_bridge/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) - Explain the behavior you would expect and the actual behavior. - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. - Provide the information you collected in the previous section. @@ -81,13 +81,13 @@ This section guides you through submitting an enhancement suggestion for CONTRIB - Make sure that you are using the latest version. - Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration. -- Perform a [search](/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Perform a [search](https://github.com/SSL-A-Team/ssl_ros_bridge/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. - Feel free to have early discussions about your suggestions via discussions or on Discord as presented in [this section](#i-have-a-question). #### How Do I Submit a Good Enhancement Suggestion? -Enhancement suggestions are tracked as [GitHub issues](/issues). +Enhancement suggestions are tracked as [GitHub issues](https://github.com/SSL-A-Team/ssl_ros_bridge/issues). - Use a **clear and descriptive title** for the issue to identify the suggestion. - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. diff --git a/README.md b/README.md index 29fc359..45e5d0d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ This node listens for the multicast vision messages sent by ssl-vision and publi ##### Published Topics * ~/vision_messages - * Type: [ssl_league_msgs/msg/VisionWrapper](/blob/main/ssl_league_msgs/msg/vision/VisionWrapper.msg) + * Type: [ssl_league_msgs/msg/VisionWrapper](ssl_league_msgs/msg/vision/VisionWrapper.msg) * Contains vision data including robot detections, ball detections, and field geometry. ##### Parameters @@ -76,19 +76,19 @@ The team client node needs to know the IP address fo the game controller server ##### Published Topics * ~/referee_messages - * Type: [ssl_league_msgs/msg/Referee](/blob/main/ssl_league_msgs/msg/game_controller/Referee.msg) + * Type: [ssl_league_msgs/msg/Referee](ssl_league_msgs/msg/game_controller/Referee.msg) * Contains the latest information from the game controller. ##### Subscribed Topics * /team_client_node/connection_status - * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](/blob/main/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) + * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) * Used for automatic game controller discovery. ##### Service Clients * /team_client_node/reconnect - * Type: [ssl_ros_bridge_msgs/srv/ReconnectTeamClient](/blob/main/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) + * Type: [ssl_ros_bridge_msgs/srv/ReconnectTeamClient](ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) * Used for automatic game controller discovery. ##### Parameters @@ -115,23 +115,23 @@ _Note_: Encrypted connections are not currently supported. ##### Published Topics * ~/connection_status - * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](/blob/main/ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) + * Type: [ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus](ssl_ros_bridge_msgs/msg/TeamClientConnectionStatus.msg) * Contains the connection status and ping time of the team client to the game controller server. ##### Services * ~/set_desired_keeper - * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv) + * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv) * Sends a request to the game controller to change your team's keeper ID. * ~/substitute_bot - * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SubstituteBot.srv) + * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SubstituteBot.srv) * Sends a request to the game controller to substitute a bot. * ~/reconnect - * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) + * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) * Reconnects the team client to the game controller. * _Note_: If the address field of the request is empty, the team client node will reconnect to the same server it was previously configured for. If the address field of the request is not empty, the `gc_ip_address` parameter will be overwritten with this new address. * ~/set_advantage_choice - * Type: [ssl_ros_bridge_msgs/srv/](/blob/main/ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv) + * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv) * Sends a request to set the team's advantage choice to the game controller. ##### Parameters From 94a6865cfa3862c68c2b5e7a4633251af8212aff Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 13:04:30 +0900 Subject: [PATCH 06/10] Fixes more bad links in readme. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 45e5d0d..fefa1cd 100644 --- a/README.md +++ b/README.md @@ -121,17 +121,17 @@ _Note_: Encrypted connections are not currently supported. ##### Services * ~/set_desired_keeper - * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv) + * Type: [ssl_ros_bridge_msgs/srv/SetDesiredKeeper](ssl_ros_bridge_msgs/srv/SetDesiredKeeper.srv) * Sends a request to the game controller to change your team's keeper ID. * ~/substitute_bot - * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SubstituteBot.srv) + * Type: [ssl_ros_bridge_msgs/srv/SubstituteBot](ssl_ros_bridge_msgs/srv/SubstituteBot.srv) * Sends a request to the game controller to substitute a bot. * ~/reconnect - * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) + * Type: [ssl_ros_bridge_msgs/srv/ReconnectTeamClient](ssl_ros_bridge_msgs/srv/ReconnectTeamClient.srv) * Reconnects the team client to the game controller. * _Note_: If the address field of the request is empty, the team client node will reconnect to the same server it was previously configured for. If the address field of the request is not empty, the `gc_ip_address` parameter will be overwritten with this new address. * ~/set_advantage_choice - * Type: [ssl_ros_bridge_msgs/srv/](ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv) + * Type: [ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice](ssl_ros_bridge_msgs/srv/SetTeamAdvantageChoice.srv) * Sends a request to set the team's advantage choice to the game controller. ##### Parameters @@ -155,4 +155,4 @@ _Note_: Encrypted connections are not currently supported. ## Contributing -See [our contributing guidelines](blob/master/CONTRIBUTING.md) +See [our contributing guidelines](CONTRIBUTING.md) From 068778d3e99c63dbf95ffa0defc7f34467e4bf71 Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 13:35:05 +0900 Subject: [PATCH 07/10] Adds basic launch files. --- README.md | 14 +++++++++++++- ssl_ros_bridge/CMakeLists.txt | 5 +++++ ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml | 8 ++++++++ .../ssl_ros_bridge_localhost_only.launch.xml | 13 +++++++++++++ .../src/team_client/team_client_node.cpp | 2 +- 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml create mode 100644 ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml diff --git a/README.md b/README.md index fefa1cd..5487e48 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,18 @@ The intended usage of these packages is to configure and run the nodes in the ss For most teams, in most scenarios, the only parameter that needs to be configured is the team name. Automatic discovery and multi-interface multicast listening will handle most common network setups. +Two launch files are provided in ssl_ros_bridge for basic scenarios: [ssl_ros_bridge.launch.xml](ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml) and [ssl_ros_bridge_localhost_only.launch.xml](ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml). The first listens for multicast traffic on all interfaces. The localhost only launch file restricts the multicast listening to the loopback interface (127.0.0.1) and sets the game controller server address to 127.0.0.1. The localhost only launch file is useful when using simulation to prevent traffic from other simulation setups on the same network from interfering with your system. + +_Note_: You may need to enable multicast on the loopback interface as many linux environments have it disabled by default. + +Either of these launch files can be included in your own launch file, specifying your team name. + +```xml + + + +``` + ## Packages ### ssl_league_protobufs @@ -146,7 +158,7 @@ _Note_: Encrypted connections are not currently supported. * The port on the game controller server the team client will connect to. * team_name * Type: string - * Default: "A-Team" + * Default: "Test Team" * The name of the team the client will try to connect as. * team_color * Type: string diff --git a/ssl_ros_bridge/CMakeLists.txt b/ssl_ros_bridge/CMakeLists.txt index 96fc1bd..cf49800 100644 --- a/ssl_ros_bridge/CMakeLists.txt +++ b/ssl_ros_bridge/CMakeLists.txt @@ -20,6 +20,11 @@ add_subdirectory(src/game_controller_bridge) add_subdirectory(src/team_client) add_subdirectory(src/vision_bridge) +install(DIRECTORY + launch + DESTINATION share/${PROJECT_NAME} +) + if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() diff --git a/ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml b/ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml new file mode 100644 index 0000000..4c8ce4c --- /dev/null +++ b/ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml b/ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml new file mode 100644 index 0000000..d6dee44 --- /dev/null +++ b/ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/ssl_ros_bridge/src/team_client/team_client_node.cpp b/ssl_ros_bridge/src/team_client/team_client_node.cpp index 0d38481..20ff6fe 100644 --- a/ssl_ros_bridge/src/team_client/team_client_node.cpp +++ b/ssl_ros_bridge/src/team_client/team_client_node.cpp @@ -42,7 +42,7 @@ class TeamClientNode : public rclcpp::Node declare_parameter("gc_ip_address", ""); declare_parameter("gc_port", 10008); - declare_parameter("team_name", "A-Team"); + declare_parameter("team_name", "Test Team"); declare_parameter("team_color", "auto"); set_desired_keeper_service_ = create_service( From 107dd65ecf03392c456fdf869f572b3572648eca Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 08:34:18 -0500 Subject: [PATCH 08/10] Imports uncrustify config file. --- ament_code_style.cfg | 3694 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3694 insertions(+) create mode 100644 ament_code_style.cfg diff --git a/ament_code_style.cfg b/ament_code_style.cfg new file mode 100644 index 0000000..3d57b30 --- /dev/null +++ b/ament_code_style.cfg @@ -0,0 +1,3694 @@ +# Uncrustify-0.78.1 + +# +# General options +# + +# The type of line endings. +# +# Default: auto +newlines = auto # lf/crlf/cr/auto + +# The original size of tabs in the input. +# +# Default: 8 +input_tab_size = 8 # unsigned number + +# The size of tabs in the output (only used if align_with_tabs=true). +# +# Default: 8 +output_tab_size = 8 # unsigned number + +# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). +# +# Default: 92 +string_escape_char = 92 # unsigned number + +# Alternate string escape char (usually only used for Pawn). +# Only works right before the quote char. +string_escape_char2 = 0 # unsigned number + +# Replace tab characters found in string literals with the escape sequence \t +# instead. +string_replace_tab_chars = false # true/false + +# Allow interpreting '>=' and '>>=' as part of a template in code like +# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. +# Improvements to template detection may make this option obsolete. +tok_split_gte = false # true/false + +# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multi-line macros). +disable_processing_nl_cont = false # true/false + +# Specify the marker used in comments to disable processing of part of the +# file. +# +# Default: *INDENT-OFF* +disable_processing_cmt = " *INDENT-OFF*" # string + +# Specify the marker used in comments to (re)enable processing in a file. +# +# Default: *INDENT-ON* +enable_processing_cmt = " *INDENT-ON*" # string + +# Enable parsing of digraphs. +enable_digraphs = false # true/false + +# Option to allow both disable_processing_cmt and enable_processing_cmt +# strings, if specified, to be interpreted as ECMAScript regular expressions. +# If true, a regex search will be performed within comments according to the +# specified patterns in order to disable/enable processing. +processing_cmt_as_regex = false # true/false + +# Add or remove the UTF-8 BOM (recommend 'remove'). +utf8_bom = ignore # ignore/add/remove/force/not_defined + +# If the file contains bytes with values between 128 and 255, but is not +# UTF-8, then output as UTF-8. +utf8_byte = false # true/false + +# Force the output encoding to UTF-8. +utf8_force = false # true/false + +# +# Spacing options +# + +# Add or remove space around arithmetic operator '+', '-', '/', '*', etc +# also '>>>' '<<' '>>' '%' '|'. +sp_arith = force # ignore/add/remove/force + +# Add or remove space around arithmetic operators '+' and '-'. +# +# Overrides sp_arith. +sp_arith_additive = force # ignore/add/remove/force/not_defined + +# Add or remove space around assignment operator '=', '+=', etc. +sp_assign = force # ignore/add/remove/force + +# Add or remove space around '=' in C++11 lambda capture specifications. +# +# Overrides sp_assign. +sp_cpp_lambda_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the capture specification of a C++11 lambda when +# an argument list is present, as in '[] (int x){ ... }'. +sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the capture specification of a C++11 lambda with +# no argument list is present, as in '[] { ... }'. +sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the opening parenthesis and before the closing +# parenthesis of a argument list of a C++11 lambda, as in +# '[]( ){ ... }' +# with an empty list. +sp_cpp_lambda_argument_list_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the opening parenthesis and before the closing +# parenthesis of a argument list of a C++11 lambda, as in +# '[]( int x ){ ... }'. +sp_cpp_lambda_argument_list = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the argument list of a C++11 lambda, as in +# '[](int x) { ... }'. +sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a lambda body and its call operator of an +# immediately invoked lambda, as in '[]( ... ){ ... } ( ... )'. +sp_cpp_lambda_fparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around assignment operator '=' in a prototype. +# +# If set to ignore, use sp_assign. +sp_assign_default = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space in 'enum {'. +# +# Default: add +sp_enum_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space in 'NS_ENUM ('. +sp_enum_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around assignment '=' in enum. +sp_enum_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around assignment ':' in enum. +sp_enum_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around preprocessor '##' concatenation operator. +# +# Default: add +sp_pp_concat = force # ignore/add/remove/force/not_defined + +# Add or remove space after preprocessor '#' stringify operator. +# Also affects the '#@' charizing operator. +sp_pp_stringify = remove # ignore/add/remove/force/not_defined + +# Add or remove space before preprocessor '#' stringify operator +# as in '#define x(y) L#y'. +sp_before_pp_stringify = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around boolean operators '&&' and '||'. +sp_bool = force # ignore/add/remove/force/not_defined + +# Add or remove space around compare operator '<', '>', '==', etc. +sp_compare = force # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')'. +sp_inside_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. +sp_paren_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. +sp_cparen_oparen = ignore # ignore/add/remove/force/not_defined + +# Whether to balance spaces inside nested parentheses. +sp_balance_nested_parens = false # true/false + +# Add or remove space between ')' and '{'. +sp_paren_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between nested braces, i.e. '{{' vs. '{ {'. +sp_brace_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*'. +sp_before_ptr_star = force # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that isn't followed by a +# variable name. If set to ignore, sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that is followed by a qualifier. +# If set to ignore, sp_before_unnamed_ptr_star is used instead. +sp_before_qualifier_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that is followed by 'operator' keyword. +# If set to ignore, sp_before_unnamed_ptr_star is used instead. +sp_before_operator_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that is followed by +# a class scope (as in 'int *MyClass::method()') or namespace scope +# (as in 'int *my_ns::func()'). +# If set to ignore, sp_before_unnamed_ptr_star is used instead. +sp_before_scope_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that is followed by '::', +# as in 'int *::func()'. +# If set to ignore, sp_before_unnamed_ptr_star is used instead. +sp_before_global_scope_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' that isn't +# followed by a variable name, as in '(char const *)'. If set to ignore, +# sp_before_ptr_star is used instead. +sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between pointer stars '*', as in 'int ***a;'. +sp_between_ptr_star = remove # ignore/add/remove/force/not_defined + +# Add or remove space between pointer star '*' and reference '&', as in 'int *& a;'. +sp_between_ptr_ref = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after pointer star '*', if followed by a word. +# +# Overrides sp_type_func. +sp_after_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after pointer caret '^', if followed by a word. +sp_after_ptr_block_caret = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_ptr_star and sp_type_func. +sp_after_ptr_star_func = force # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*' in the trailing return of a +# function prototype or function definition. +sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the pointer star '*' and the name of the variable +# in a function pointer definition. +sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the pointer star '*' and the name of the type +# in a function pointer type definition. +sp_ptr_star_func_type = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*', if followed by an open +# parenthesis, as in 'void* (*)()'. +sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a pointer star '*', if followed by a function +# prototype or function definition. If set to ignore, sp_before_ptr_star is +# used instead. +sp_before_ptr_star_func = force # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' followed by +# the name of the function in a function prototype or definition, as in +# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead. +sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a pointer star '*' in the trailing return of a +# function prototype or function definition. +sp_before_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' in the +# trailing return of a function prototype or function definition, as in +# 'auto foo() -> char const *'. +sp_qualifier_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&'. +sp_before_byref = force # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&' that isn't followed by a +# variable name. If set to ignore, sp_before_byref is used instead. +sp_before_unnamed_byref = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after reference sign '&', if followed by a word. +# +# Overrides sp_type_func. +sp_after_byref = force # ignore/add/remove/force/not_defined + +# Add or remove space after a reference sign '&', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_byref and sp_type_func. +sp_after_byref_func = force # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&', if followed by a function +# prototype or function definition. +sp_before_byref_func = force # ignore/add/remove/force/not_defined + +# Add or remove space after a reference sign '&', if followed by an open +# parenthesis, as in 'char& (*)()'. +sp_byref_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between type and word. In cases where total removal of +# whitespace would be a syntax error, a value of 'remove' is treated the same +# as 'force'. +# +# This also affects some other instances of space following a type that are +# not covered by other options; for example, between the return type and +# parenthesis of a function type template argument, between the type and +# parenthesis of an array parameter, or between 'decltype(...)' and the +# following word. +# +# Default: force +sp_after_type = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'decltype(...)' and word, +# brace or function call. +sp_after_decltype = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space before the parenthesis in the D constructs +# 'template Foo(' and 'class Foo('. +sp_before_template_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'template' and '<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before '<'. +sp_before_angle = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '<' and '>'. +sp_inside_angle = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '<>'. +# if empty. +sp_inside_angle_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and ':'. +sp_angle_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '>'. +sp_after_angle = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '(' as found in 'new List(foo);'. +sp_angle_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '()' as found in 'new List();'. +sp_angle_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and a word as in 'List m;' or +# 'template static ...'. +sp_angle_word = force # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '>' in '>>' (template stuff). +# +# Default: add +sp_angle_shift = remove # ignore/add/remove/force/not_defined + +# (C++11) Permit removal of the space between '>>' in 'foo >'. Note +# that sp_angle_shift cannot remove the space without this option. +sp_permit_cpp11_shift = true # true/false + +# Add or remove space before '(' of control statements ('if', 'for', 'switch', +# 'while', etc.). +sp_before_sparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')' of control statements other than +# 'for'. +sp_inside_sparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of control statements other than 'for'. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of control statements other than 'for'. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')' of 'for' statements. +sp_inside_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '((' or '))' of control statements. +sp_sparen_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ')' of control statements. +sp_after_sparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of control statements. +sp_sparen_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'do' and '{'. +sp_do_brace_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'while'. +sp_brace_close_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'while' and '('. Overrides sp_before_sparen. +sp_while_paren_open = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'invariant' and '('. +sp_invariant_paren = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space after the ')' in 'invariant (C) c'. +sp_after_invariant_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while'. +sp_special_semi = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ';'. +# +# Default: remove +sp_before_semi = remove # ignore/add/remove/force/not_defined + +# Add or remove space before ';' in non-empty 'for' statements. +sp_before_semi_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a semicolon of an empty left part of a for +# statement, as in 'for ( ; ; )'. +sp_before_semi_for_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ';', except when followed by a comment. +# +# Default: add +sp_after_semi = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the semicolons of an empty middle part of a for +# statement, as in 'for ( ; ; )'. +sp_between_semi_for_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ';' in non-empty 'for' statements. +# +# Default: force +sp_after_semi_for = force # ignore/add/remove/force/not_defined + +# Add or remove space after the final semicolon of an empty part of a for +# statement, as in 'for ( ; ; )'. +sp_after_semi_for_empty = force # ignore/add/remove/force/not_defined + +# Add or remove space before '[' (except '[]'). +sp_before_square = remove # ignore/add/remove/force/not_defined + +# Add or remove space before '[' for a variable definition. +# +# Default: remove +sp_before_vardef_square = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before '[' for asm block. +sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before '[]'. +sp_before_squares = remove # ignore/add/remove/force/not_defined + +# Add or remove space before C++17 structured bindings. +sp_cpp_before_struct_binding = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside a non-empty '[' and ']'. +sp_inside_square = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '[]'. +# if empty. +sp_inside_square_empty = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and +# ']'. If set to ignore, sp_inside_square is used. +sp_inside_square_oc_array = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. +sp_after_comma = force # ignore/add/remove/force/not_defined + +# Add or remove space before ',', i.e. 'a,b' vs. 'a ,b'. +# +# Default: remove +sp_before_comma = remove # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between ',' and ']' in multidimensional array type +# like 'int[,,]'. +sp_after_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between '[' and ',' in multidimensional array type +# like 'int[,,]'. +sp_before_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between ',' in multidimensional array type +# like 'int[,,]'. +sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between an open parenthesis and comma, +# i.e. '(,' vs. '( ,'. +# +# Default: force +sp_paren_comma = force # ignore/add/remove/force/not_defined + +# Add or remove space between a type and ':'. +sp_type_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overridden with FORCE +sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overridden with FORCE +sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a type and '...'. +sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a '*' and '...'. +sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '...'. +sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '&&' and '...'. +sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and a qualifier such as 'const'. +sp_paren_qualifier = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and 'noexcept'. +sp_paren_noexcept = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after class ':'. +sp_after_class_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space before class ':'. +sp_before_class_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space after class constructor ':'. +# +# Default: add +sp_after_constr_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before class constructor ':'. +# +# Default: add +sp_before_constr_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before case ':'. +# +# Default: remove +sp_before_case_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'operator' and operator sign. +sp_after_operator = remove # ignore/add/remove/force/not_defined + +# Add or remove space between the operator symbol and the open parenthesis, as +# in 'operator ++('. +sp_after_operator_sym = remove # ignore/add/remove/force/not_defined + +# Overrides sp_after_operator_sym when the operator has no arguments, as in +# 'operator *()'. +sp_after_operator_sym_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or +# '(int)a' vs. '(int) a'. +sp_after_cast = ignore # ignore/add/remove/force/not_defined + +# Add or remove spaces inside cast parentheses. +sp_inside_paren_cast = remove # ignore/add/remove/force/not_defined + +# Add or remove space between the type and open parenthesis in a C++ cast, +# i.e. 'int(exp)' vs. 'int (exp)'. +sp_cpp_cast_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof' and '('. +sp_sizeof_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof' and '...'. +sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof...' and '('. +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '...' and a parameter pack. +sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a parameter pack and '...'. +sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'decltype' and '('. +sp_decltype_paren = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Add or remove space after the tag keyword. +sp_after_tag = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside enum '{' and '}'. +sp_inside_braces_enum = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside struct/union '{' and '}'. +sp_inside_braces_struct = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' +sp_inside_braces_oc_dict = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after open brace in an unnamed temporary +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before close brace in an unnamed temporary +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside an unnamed temporary direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore +# works only if sp_before_type_brace_init_lst_close is set to ignore. +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside '{' and '}'. +sp_inside_braces = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '{}'. +# if empty. +sp_inside_braces_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space around trailing return operator '->'. +sp_trailing_return = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between return type and function name. A minimum of 1 +# is forced except for pointer return types. +sp_type_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between type and open brace of an unnamed temporary +# direct-list-initialization. +sp_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' on function declaration. +sp_func_proto_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function declaration +# if empty. +sp_func_proto_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' with a typedef specifier. +sp_func_type_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between alias name and '(' of a non-pointer function type typedef. +sp_func_def_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function definition +# if empty. +sp_func_def_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside empty function '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_fparens = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside function '(' and ')'. +sp_inside_fparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside user functor '(' and ')'. +sp_func_call_user_inside_rparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside empty functor '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_rparens = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside functor '(' and ')'. +sp_inside_rparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside the first parentheses in a function type, as in +# 'void (*x)(...)'. +sp_inside_tparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the ')' and '(' in a function type, as in +# 'void (*x)(...)'. +sp_after_tparen_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of function. +sp_fparen_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of a function call in object +# initialization. +# +# Overrides sp_fparen_brace. +sp_fparen_brace_initializer = force # ignore/add/remove/force/not_defined + +# (Java) Add or remove space between ')' and '{{' of double brace initializer. +sp_fparen_dbrace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' on function calls. +sp_func_call_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function calls without +# parameters. If set to ignore (the default), sp_func_call_paren is used. +sp_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the user function name and '(' on function +# calls. You need to set a keyword to be a user function in the config file, +# like: +# set func_call_user tr _ i18n +sp_func_call_user_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside user function '(' and ')'. +sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between nested parentheses with user functions, +# i.e. '((' vs. '( ('. +sp_func_call_user_paren_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a constructor/destructor and the open +# parenthesis. +sp_func_class_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between a constructor without parameters or destructor +# and '()'. +sp_func_class_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space after 'return'. +# +# Default: force +sp_return = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'return' and '('. +sp_return_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'return' and '{'. +sp_return_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '__attribute__' and '('. +sp_attribute_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. +sp_defined_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'throw' and '(' in 'throw (something)'. +sp_throw_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'throw' and anything other than '(' as in +# '@throw [...];'. +sp_after_throw = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'catch' and '(' in 'catch (something) { }'. +# If set to ignore, sp_before_sparen is used. +sp_catch_paren = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@catch' and '(' +# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. +sp_oc_catch_paren = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before Objective-C protocol list +# as in '@protocol Protocol' or '@interface MyClass : NSObject'. +sp_before_oc_proto_list = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between class name and '(' +# in '@interface className(categoryName):BaseClass' +sp_oc_classname_paren = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'version' and '(' +# in 'version (something) { }'. If set to ignore, sp_before_sparen is used. +sp_version_paren = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'scope' and '(' +# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. +sp_scope_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'super' and '(' in 'super (something)'. +# +# Default: remove +sp_super_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'this' and '(' in 'this (something)'. +# +# Default: remove +sp_this_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between a macro name and its definition. +sp_macro = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a macro function ')' and its definition. +sp_macro_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'else' and '{' if on the same line. +sp_else_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'else' if on the same line. +sp_brace_else = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and the name of a typedef on the same line. +sp_brace_typedef = force # ignore/add/remove/force/not_defined + +# Add or remove space before the '{' of a 'catch' statement, if the '{' and +# 'catch' are on the same line, as in 'catch (decl) {'. +sp_catch_brace = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' +# and '@catch' are on the same line, as in '@catch (decl) {'. +# If set to ignore, sp_catch_brace is used. +sp_oc_catch_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'catch' if on the same line. +sp_brace_catch = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '}' and '@catch' if on the same line. +# If set to ignore, sp_brace_catch is used. +sp_oc_brace_catch = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'finally' and '{' if on the same line. +sp_finally_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'finally' if on the same line. +sp_brace_finally = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'try' and '{' if on the same line. +sp_try_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between get/set and '{' if on the same line. +sp_getset_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a variable and '{' for C++ uniform +# initialization. +sp_word_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a variable and '{' for a namespace. +# +# Default: add +sp_word_brace_ns = add # ignore/add/remove/force/not_defined + +# Add or remove space before the '::' operator. +sp_before_dc = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '::' operator. +sp_after_dc = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove around the D named array initializer ':' operator. +sp_d_array_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the '!' (not) unary operator. +# +# Default: remove +sp_not = remove # ignore/add/remove/force/not_defined + +# Add or remove space between two '!' (not) unary operators. +# If set to ignore, sp_not will be used. +sp_not_not = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the '~' (invert) unary operator. +# +# Default: remove +sp_inv = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '&' (address-of) unary operator. This does not +# affect the spacing after a '&' that is part of a type. +# +# Default: remove +sp_addr = remove # ignore/add/remove/force/not_defined + +# Add or remove space around the '.' or '->' operators. +# +# Default: remove +sp_member = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '*' (dereference) unary operator. This does +# not affect the spacing after a '*' that is part of a type. +# +# Default: remove +sp_deref = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. +# +# Default: remove +sp_sign = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '++' and '--' the word to which it is being +# applied, as in '(--x)' or 'y++;'. +# +# Default: remove +sp_incdec = remove # ignore/add/remove/force/not_defined + +# Add or remove space before a backslash-newline at the end of a line. +# +# Default: add +sp_before_nl_cont = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' +# or '+(int) bar;'. +sp_after_oc_scope = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in message specs, +# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. +sp_after_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in message specs, +# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. +sp_before_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_after_oc_dict_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_before_oc_dict_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue: 1];'. +sp_after_send_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue :1];'. +sp_before_send_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the (type) in message specs, +# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. +sp_after_oc_type = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the first (type) in message specs, +# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. +sp_after_oc_return_type = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@selector' and '(', +# i.e. '@selector(msgName)' vs. '@selector (msgName)'. +# Also applies to '@protocol()' constructs. +sp_after_oc_at_sel = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@selector(x)' and the following word, +# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside '@selector' parentheses, +# i.e. '@selector(foo)' vs. '@selector( foo )'. +# Also applies to '@protocol()' constructs. +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before a block pointer caret, +# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. +sp_before_oc_block_caret = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after a block pointer caret, +# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. +sp_after_oc_block_caret = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between the receiver and selector in a message, +# as in '[receiver selector ...]'. +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after '@property'. +sp_after_oc_property = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@synchronized' and the open parenthesis, +# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. +sp_after_oc_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around the ':' in 'b ? t : f'. +sp_cond_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space before the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_before = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_after = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around the '?' in 'b ? t : f'. +sp_cond_question = force # ignore/add/remove/force/not_defined + +# Add or remove space before the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_before = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_after = ignore # ignore/add/remove/force/not_defined + +# In the abbreviated ternary form '(a ?: b)', add or remove space between '?' +# and ':'. +# +# Overrides all other sp_cond_* options. +sp_cond_ternary_short = ignore # ignore/add/remove/force/not_defined + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make +# sense here. +sp_case_label = force # ignore/add/remove/force/not_defined + +# (D) Add or remove space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_after_for_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_before_for_colon = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. +sp_extern_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the opening of a C++ comment, as in '// A'. +sp_cmt_cpp_start = ignore # ignore/add/remove/force/not_defined + +# remove space after the '//' and the pvs command '-V1234', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_pvs = false # true/false + +# remove space after the '//' and the command 'lint', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_lint = false # true/false + +# Add or remove space in a C++ region marker comment, as in '// BEGIN'. +# A region marker is defined as a comment which is not preceded by other text +# (i.e. the comment is the first non-whitespace on the line), and which starts +# with either 'BEGIN' or 'END'. +# +# Overrides sp_cmt_cpp_start. +sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined + +# If true, space added with sp_cmt_cpp_start will be added after Doxygen +# sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # true/false + +# If true, space added with sp_cmt_cpp_start will be added after Qt translator +# or meta-data comments like '//:', '//=', and '//~'. +sp_cmt_cpp_qttr = false # true/false + +# Add or remove space between #else or #endif and a trailing comment. +sp_endif_cmt = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after 'new', 'delete' and 'delete[]'. +sp_after_new = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'new' and '(' in 'new()'. +sp_between_new_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and type in 'new(foo) BAR'. +sp_after_newop_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside parentheses of the new operator +# as in 'new(foo) BAR'. +sp_inside_newop_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the open parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before the close parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a trailing comment. +sp_before_tr_cmt = ignore # ignore/add/remove/force/not_defined + +# Number of spaces before a trailing comment. +sp_num_before_tr_cmt = 2 # unsigned number + +# Add or remove space before an embedded comment. +# +# Default: force +sp_before_emb_cmt = ignore # ignore/add/remove/force/not_defined + +# Number of spaces before an embedded comment. +# +# Default: 1 +sp_num_before_emb_cmt = 1 # unsigned number + +# Add or remove space after an embedded comment. +# +# Default: force +sp_after_emb_cmt = ignore # ignore/add/remove/force/not_defined + +# Number of spaces after an embedded comment. +# +# Default: 1 +sp_num_after_emb_cmt = 1 # unsigned number + +# (Java) Add or remove space between an annotation and the open parenthesis. +sp_annotation_paren = ignore # ignore/add/remove/force/not_defined + +# If true, vbrace tokens are dropped to the previous token and skipped. +sp_skip_vbrace_tokens = false # true/false + +# Add or remove space after 'noexcept'. +sp_after_noexcept = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '_'. +sp_vala_after_translation = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a bit colon ':'. +sp_before_bit_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after a bit colon ':'. +sp_after_bit_colon = ignore # ignore/add/remove/force/not_defined + +# If true, a is inserted after #define. +force_tab_after_define = false # true/false + +# +# Indenting options +# + +# The number of columns to indent per level. Usually 2, 3, 4, or 8. +# +# Default: 8 +indent_columns = 2 # unsigned number + +# Whether to ignore indent for the first continuation line. Subsequent +# continuation lines will still be indented to match the first. +indent_ignore_first_continue = false # true/false + +# The continuation indent. If non-zero, this overrides the indent of '(', '[' +# and '=' continuation indents. Negative values are OK; negative value is +# absolute and not increased for each '(' or '[' level. +# +# For FreeBSD, this is set to 4. +# Requires indent_ignore_first_continue=false. +indent_continue = 2 # number + +# The continuation indent, only for class header line(s). If non-zero, this +# overrides the indent of 'class' continuation indents. +# Requires indent_ignore_first_continue=false. +indent_continue_class_head = 0 # unsigned number + +# Whether to indent empty lines (i.e. lines which contain only spaces before +# the newline character). +indent_single_newlines = false # true/false + +# The continuation indent for func_*_param if they are true. If non-zero, this +# overrides the indent. +indent_param = 0 # unsigned number + +# How to use tabs when indenting code. +# +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces (default) +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: 1 +indent_with_tabs = 0 # unsigned number + +# Whether to indent comments that are not at a brace level with tabs on a +# tabstop. Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # true/false + +# Whether to indent strings broken by '\' so that they line up. +indent_align_string = false # true/false + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=true. +indent_xml_string = 0 # unsigned number + +# Spaces to indent '{' from level. +indent_brace = 0 # unsigned number + +# Whether braces are indented to the body level. +indent_braces = false # true/false + +# Whether to disable indenting function braces if indent_braces=true. +indent_braces_no_func = false # true/false + +# Whether to disable indenting class braces if indent_braces=true. +indent_braces_no_class = false # true/false + +# Whether to disable indenting struct braces if indent_braces=true. +indent_braces_no_struct = false # true/false + +# Whether to indent based on the size of the brace parent, +# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # true/false + +# Whether to indent based on the open parenthesis instead of the open brace +# in '({\n'. +indent_paren_open_brace = false # true/false + +# (C#) Whether to indent the brace of a C# delegate by another level. +indent_cs_delegate_brace = false # true/false + +# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by +# another level. +indent_cs_delegate_body = false # true/false + +# Whether to indent the body of a 'namespace'. +indent_namespace = false # true/false + +# Whether to indent only the first namespace, and not any nested namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # true/false + +# The number of spaces to indent a namespace block. +# If set to zero, use the value indent_columns +indent_namespace_level = 0 # unsigned number + +# If the body of the namespace is longer than this number, it won't be +# indented. Requires indent_namespace=true. 0 means no limit. +indent_namespace_limit = 0 # unsigned number + +# Whether to indent only in inner namespaces (nested in other namespaces). +# Requires indent_namespace=true. +indent_namespace_inner_only = false # true/false + +# Whether the 'extern "C"' body is indented. +indent_extern = false # true/false + +# Whether the 'class' body is indented. +indent_class = true # true/false + +# Whether to ignore indent for the leading base class colon. +indent_ignore_before_class_colon = false # true/false + +# Additional indent before the leading base class colon. +# Negative values decrease indent down to the first column. +# Requires indent_ignore_before_class_colon=false and a newline break before +# the colon (see pos_class_colon and nl_class_colon) +indent_before_class_colon = 0 # number + +# Whether to indent the stuff after a leading base class colon. +indent_class_colon = false # true/false + +# Whether to indent based on a class colon instead of the stuff after the +# colon. Requires indent_class_colon=true. +indent_class_on_colon = false # true/false + +# Whether to ignore indent for a leading class initializer colon. +indent_ignore_before_constr_colon = false # true/false + +# Whether to indent the stuff after a leading class initializer colon. +indent_constr_colon = true # true/false + +# Virtual indent from the ':' for leading member initializers. +# +# Default: 2 +indent_ctor_init_leading = 2 # unsigned number + +# Virtual indent from the ':' for following member initializers. +# +# Default: 2 +indent_ctor_init_following = 2 # unsigned number + +# Additional indent for constructor initializer list. +# Negative values decrease indent down to the first column. +indent_ctor_init = -2 # number + +# Whether to indent 'if' following 'else' as a new block under the 'else'. +# If false, 'else\nif' is treated as 'else if' for indenting purposes. +indent_else_if = false # true/false + +# Amount to indent variable declarations after a open brace. +# +# <0: Relative +# >=0: Absolute +indent_var_def_blk = 0 # number + +# Whether to indent continued variable declarations instead of aligning. +indent_var_def_cont = false # true/false + +# How to indent continued shift expressions ('<<' and '>>'). +# Set align_left_shift=false when using this. +# 0: Align shift operators instead of indenting them (default) +# 1: Indent by one level +# -1: Preserve original indentation +indent_shift = -1 # number + +# Whether to force indentation of function definitions to start in column 1. +indent_func_def_force_col1 = false # true/false + +# Whether to indent continued function call parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_call_param = false # true/false + +# Whether to indent continued function definition parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_def_param = false # true/false + +# for function definitions, only if indent_func_def_param is false +# Allows to align params when appropriate and indent them when not +# behave as if it was true if paren position is more than this value +# if paren position is more than the option value +indent_func_def_param_paren_pos_threshold = 0 # unsigned number + +# Whether to indent continued function call prototype one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_proto_param = false # true/false + +# Whether to indent continued function call declaration one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_class_param = false # true/false + +# Whether to indent continued class variable constructors one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_ctor_var_param = false # true/false + +# Whether to indent continued template parameter list one indent level, +# rather than aligning parameters under the open parenthesis. +indent_template_param = false # true/false + +# Double the indent for indent_func_xxx_param options. +# Use both values of the options indent_columns and indent_param. +indent_func_param_double = false # true/false + +# Indentation column for standalone 'const' qualifier on a function +# prototype. +indent_func_const = 0 # unsigned number + +# Indentation column for standalone 'throw' qualifier on a function +# prototype. +indent_func_throw = 0 # unsigned number + +# How to indent within a macro followed by a brace on the same line +# This allows reducing the indent in macros that have (for example) +# `do { ... } while (0)` blocks bracketing them. +# +# true: add an indent for the brace on the same line as the macro +# false: do not add an indent for the brace on the same line as the macro +# +# Default: true +indent_macro_brace = false # true/false + +# The number of spaces to indent a continued '->' or '.'. +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # unsigned number + +# Whether lines broken at '.' or '->' should be indented by a single indent. +# The indent_member option will not be effective if this is set to true. +indent_member_single = false # true/false + +# Spaces to indent single line ('//') comments on lines before code. +indent_single_line_comments_before = 0 # unsigned number + +# Spaces to indent single line ('//') comments on lines after code. +indent_single_line_comments_after = 0 # unsigned number + +# When opening a paren for a control statement (if, for, while, etc), increase +# the indent level by this value. Negative values decrease the indent level. +indent_sparen_extra = 0 # number + +# Whether to indent trailing single line ('//') comments relative to the code +# instead of trying to keep the same absolute column. +indent_relative_single_line_comments = false # true/false + +# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. +# It might be wise to choose the same value for the option indent_case_brace. +indent_switch_case = 2 # unsigned number + +# Spaces to indent the body of a 'switch' before any 'case'. +# Usually the same as indent_columns or indent_switch_case. +indent_switch_body = 0 # unsigned number + +# Whether to ignore indent for '{' following 'case'. +indent_ignore_case_brace = false # true/false + +# Spaces to indent '{' from 'case'. By default, the brace will appear under +# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. +# It might be wise to choose the same value for the option indent_switch_case. +indent_case_brace = 2 # number + +# indent 'break' with 'case' from 'switch'. +indent_switch_break_with_case = false # true/false + +# Whether to indent preprocessor statements inside of switch statements. +# +# Default: true +indent_switch_pp = true # true/false + +# Spaces to shift the 'case' line, without affecting any other lines. +# Usually 0. +indent_case_shift = 0 # unsigned number + +# Whether to align comments before 'case' with the 'case'. +# +# Default: true +indent_case_comment = false # true/false + +# Whether to indent comments not found in first column. +# +# Default: true +indent_comment = false # true/false + +# Whether to indent comments found in first column. +indent_col1_comment = false # true/false + +# Whether to indent multi string literal in first column. +indent_col1_multi_string_literal = false # true/false + +# Align comments on adjacent lines that are this many columns apart or less. +# +# Default: 3 +indent_comment_align_thresh = 3 # unsigned number + +# Whether to ignore indent for goto labels. +indent_ignore_label = false # true/false + +# How to indent goto labels. Requires indent_ignore_label=false. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_label = 1 # number + +# How to indent access specifiers that are followed by a +# colon. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_access_spec = 1 # number + +# Whether to indent the code after an access specifier by one level. +# If true, this option forces 'indent_access_spec=0'. +indent_access_spec_body = false # true/false + +# If an open parenthesis is followed by a newline, whether to indent the next +# line so that it lines up after the open parenthesis (not recommended). +indent_paren_nl = false # true/false + +# How to indent a close parenthesis after a newline. +# +# 0: Indent to body level (default) +# 1: Align under the open parenthesis +# 2: Indent to the brace level +# -1: Preserve original indentation +indent_paren_close = 2 # number + +# Whether to indent the open parenthesis of a function definition, +# if the parenthesis is on its own line. +indent_paren_after_func_def = false # true/false + +# Whether to indent the open parenthesis of a function declaration, +# if the parenthesis is on its own line. +indent_paren_after_func_decl = false # true/false + +# Whether to indent the open parenthesis of a function call, +# if the parenthesis is on its own line. +indent_paren_after_func_call = false # true/false + +# How to indent a comma when inside braces. +# 0: Indent by one level (default) +# 1: Align under the open brace +# -1: Preserve original indentation +indent_comma_brace = -1 # number + +# How to indent a comma when inside parentheses. +# 0: Indent by one level (default) +# 1: Align under the open parenthesis +# -1: Preserve original indentation +indent_comma_paren = -1 # number + +# How to indent a Boolean operator when inside parentheses. +# 0: Indent by one level (default) +# 1: Align under the open parenthesis +# -1: Preserve original indentation +indent_bool_paren = -1 # number + +# Whether to ignore the indentation of a Boolean operator when outside +# parentheses. +indent_ignore_bool = false # true/false + +# Whether to ignore the indentation of an arithmetic operator. +indent_ignore_arith = false # true/false + +# Whether to indent a semicolon when inside a for parenthesis. +# If true, aligns under the open for parenthesis. +indent_semicolon_for_paren = false # true/false + +# Whether to ignore the indentation of a semicolon outside of a 'for' +# statement. +indent_ignore_semicolon = false # true/false + +# Whether to align the first expression to following ones +# if indent_bool_paren=1. +indent_first_bool_expr = false # true/false + +# Whether to align the first expression to following ones +# if indent_semicolon_for_paren=true. +indent_first_for_expr = false # true/false + +# If an open square is followed by a newline, whether to indent the next line +# so that it lines up after the open square (not recommended). +indent_square_nl = false # true/false + +# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. +indent_preserve_sql = false # true/false + +# Whether to ignore the indentation of an assignment operator. +indent_ignore_assign = false # true/false + +# Whether to align continued statements at the '='. If false or if the '=' is +# followed by a newline, the next line is indent one tab. +# +# Default: true +indent_align_assign = true # true/false + +# If true, the indentation of the chunks after a '=' sequence will be set at +# LHS token indentation column before '='. +indent_off_after_assign = false # true/false + +# Whether to align continued statements at the '('. If false or the '(' is +# followed by a newline, the next line indent is one tab. +# +# Default: true +indent_align_paren = true # true/false + +# (OC) Whether to indent Objective-C code inside message selectors. +indent_oc_inside_msg_sel = false # true/false + +# (OC) Whether to indent Objective-C blocks at brace level instead of usual +# rules. +indent_oc_block = false # true/false + +# (OC) Indent for Objective-C blocks in a message relative to the parameter +# name. +# +# =0: Use indent_oc_block rules +# >0: Use specified number of spaces to indent +indent_oc_block_msg = 0 # unsigned number + +# (OC) Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # unsigned number + +# (OC) Whether to prioritize aligning with initial colon (and stripping spaces +# from lines, if necessary). +# +# Default: true +indent_oc_msg_prioritize_first_colon = true # true/false + +# (OC) Whether to indent blocks the way that Xcode does by default +# (from the keyword if the parameter is on its own line; otherwise, from the +# previous indentation level). Requires indent_oc_block_msg=true. +indent_oc_block_msg_xcode_style = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a +# message keyword. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_keyword = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a message +# colon. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_colon = false # true/false + +# (OC) Whether to indent blocks from where the block caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_caret = false # true/false + +# (OC) Whether to indent blocks from where the brace caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_brace = false # true/false + +# When indenting after virtual brace open and newline add further spaces to +# reach this minimum indent. +indent_min_vbrace_open = 0 # unsigned number + +# Whether to add further spaces after regular indent to reach next tabstop +# when indenting after virtual brace open and newline. +indent_vbrace_open_on_tabstop = false # true/false + +# How to indent after a brace followed by another token (not a newline). +# true: indent all contained lines to match the token +# false: indent all contained lines to match the brace +# +# Default: true +indent_token_after_brace = false # true/false + +# Whether to indent the body of a C++11 lambda. +indent_cpp_lambda_body = true # true/false + +# How to indent compound literals that are being returned. +# true: add both the indent from return & the compound literal open brace +# (i.e. 2 indent levels) +# false: only indent 1 level, don't add the indent for the open brace, only +# add the indent for the return. +# +# Default: true +indent_compound_literal_return = true # true/false + +# (C#) Whether to indent a 'using' block if no braces are used. +# +# Default: true +indent_using_block = true # true/false + +# How to indent the continuation of ternary operator. +# +# 0: Off (default) +# 1: When the `if_false` is a continuation, indent it under the `if_true` branch +# 2: When the `:` is a continuation, indent it under `?` +indent_ternary_operator = 0 # unsigned number + +# Whether to indent the statements inside ternary operator. +indent_inside_ternary_operator = false # true/false + +# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. +indent_off_after_return = false # true/false + +# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. +indent_off_after_return_new = false # true/false + +# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. +indent_single_after_return = false # true/false + +# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they +# have their own indentation). +indent_ignore_asm_block = false # true/false + +# Don't indent the close parenthesis of a function definition, +# if the parenthesis is on its own line. +donot_indent_func_def_close_paren = false # true/false + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}' except for functions. +# Use nl_collapse_empty_body_functions to specify how empty function braces +# should be formatted. +nl_collapse_empty_body = false # true/false + +# Whether to collapse empty blocks between '{' and '}' for functions only. +# If true, overrides nl_inside_empty_func. +nl_collapse_empty_body_functions = false # true/false + +# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'. +nl_assign_leave_one_liners = true # false/true + +# Don't split one-line braced statements inside a class xx { } body. +nl_class_leave_one_liners = true # false/true + +# Don't split one-line enums: 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = true # false/true + +# Don't split one-line get or set functions. +nl_getset_leave_one_liners = true # false/true + +# (C#) Don't split one-line property get or set functions. +nl_cs_property_leave_one_liners = false # false/true + +# Don't split one-line function definitions, as in 'int foo() { return 0; }'. +# might modify nl_func_type_name +nl_func_leave_one_liners = true # false/true + +# Don't split one-line C++11 lambdas - '[]() { return 0; }'. +nl_cpp_lambda_leave_one_liners = true # false/true + +# Don't split one-line if/else statements, as in 'if(...) b++;'. +nl_if_leave_one_liners = true # true/false + +# Don't split one-line while statements, as in 'while(...) b++;'. +nl_while_leave_one_liners = true # true/false + +# Don't split one-line do statements, as in 'do { b++; } while(...);'. +nl_do_leave_one_liners = false # true/false + +# Don't split one-line for statements, as in 'for(...) b++;'. +nl_for_leave_one_liners = false # true/false + +# (OC) Don't split one-line Objective-C messages. +nl_oc_msg_leave_one_liner = false # true/false + +# (OC) Add or remove newline between method declaration and '{'. +nl_oc_mdef_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between Objective-C block signature and '{'. +nl_oc_block_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@interface' statement. +nl_oc_before_interface = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@implementation' statement. +nl_oc_before_implementation = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@end' statement. +nl_oc_before_end = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '@interface' and '{'. +nl_oc_interface_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '@implementation' and '{'. +nl_oc_implementation_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newlines at the start of the file. +nl_start_of_file = remove # ignore/add/remove/force/not_defined + +# The minimum number of newlines at the start of the file (only used if +# nl_start_of_file is 'add' or 'force'). +nl_start_of_file_min = 0 # unsigned number + +# Add or remove newline at the end of the file. +nl_end_of_file = force # ignore/add/remove/force/not_defined + +# The minimum number of newlines at the end of the file (only used if +# nl_end_of_file is 'add' or 'force'). +nl_end_of_file_min = 1 # unsigned number + +# Add or remove newline between '=' and '{'. +nl_assign_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between '=' and '['. +nl_assign_square = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '[]' and '{'. +nl_tsquare_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline after '= ['. Will also affect the newline before +# the ']'. +nl_after_square_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function call's ')' and '{', as in +# 'list_for_each(item, &list) { }'. +nl_fcall_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum' and '{'. +nl_enum_brace = force # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum' and 'class'. +nl_enum_class = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class' and the identifier. +nl_enum_class_identifier = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class' type and ':'. +nl_enum_identifier_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class identifier :' and type. +nl_enum_colon_type = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'struct and '{'. +nl_struct_brace = force # ignore/add/remove/force/not_defined + +# Add or remove newline between 'union' and '{'. +nl_union_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'if' and '{'. +nl_if_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'else'. +nl_brace_else = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else if' and '{'. If set to ignore, +# nl_if_brace is used instead. +nl_elseif_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else' and '{'. +nl_else_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else' and 'if'. +nl_else_if = remove # ignore/add/remove/force/not_defined + +# Add or remove newline before '{' opening brace +nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before 'if'/'else if' closing parenthesis. +nl_before_if_closing_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'finally'. +nl_brace_finally = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'finally' and '{'. +nl_finally_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'try' and '{'. +nl_try_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between get/set and '{'. +nl_getset_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'for' and '{'. +nl_for_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline before the '{' of a 'catch' statement, as in +# 'catch (decl) {'. +nl_catch_brace = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline before the '{' of a '@catch' statement, as in +# '@catch (decl) {'. If set to ignore, nl_catch_brace is used. +nl_oc_catch_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'catch'. +nl_brace_catch = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '}' and '@catch'. If set to ignore, +# nl_brace_catch is used. +nl_oc_brace_catch = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and ']'. +nl_brace_square = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and ')' in a function invocation. +nl_brace_fparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'while' and '{'. +nl_while_brace = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'scope (x)' and '{'. +nl_scope_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'unittest' and '{'. +nl_unittest_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'version (x)' and '{'. +nl_version_brace = ignore # ignore/add/remove/force/not_defined + +# (C#) Add or remove newline between 'using' and '{'. +nl_using_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between two open or close braces. Due to general +# newline/brace handling, REMOVE may not work. +nl_brace_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'do' and '{'. +nl_do_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'while' of 'do' statement. +nl_brace_while = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'switch' and '{'. +nl_switch_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'synchronized' and '{'. +nl_synchronized_brace = remove # ignore/add/remove/force/not_defined + +# Add a newline between ')' and '{' if the ')' is on a different line than the +# if/for/etc. +# +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and +# nl_catch_brace. +nl_multi_line_cond = true # false/true + +# Add a newline after '(' if an if/for/while/switch condition spans multiple +# lines +nl_multi_line_sparen_open = ignore # ignore/add/remove/force/not_defined + +# Add a newline before ')' if an if/for/while/switch condition spans multiple +# lines. Overrides nl_before_if_closing_paren if both are specified. +nl_multi_line_sparen_close = ignore # ignore/add/remove/force/not_defined + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # true/false + +# Whether to add a newline before 'case', and a blank line before a 'case' +# statement that follows a ';' or '}'. +nl_before_case = false # true/false + +# Whether to add a newline after a 'case' statement. +nl_after_case = false # true/false + +# Add or remove newline between a case ':' and '{'. +# +# Overrides nl_after_case. +nl_case_colon_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between ')' and 'throw'. +nl_before_throw = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'namespace' and '{'. +nl_namespace_brace = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class. +nl_template_class = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class declaration. +# +# Overrides nl_template_class. +nl_template_class_decl = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized class declaration. +# +# Overrides nl_template_class_decl. +nl_template_class_decl_special = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class definition. +# +# Overrides nl_template_class. +nl_template_class_def = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized class definition. +# +# Overrides nl_template_class_def. +nl_template_class_def_special = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function. +nl_template_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function +# declaration. +# +# Overrides nl_template_func. +nl_template_func_decl = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized function +# declaration. +# +# Overrides nl_template_func_decl. +nl_template_func_decl_special = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function +# definition. +# +# Overrides nl_template_func. +nl_template_func_def = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized function +# definition. +# +# Overrides nl_template_func_def. +nl_template_func_def_special = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template variable. +nl_template_var = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'template<...>' and 'using' of a templated +# type alias. +nl_template_using = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'class' and '{'. +nl_class_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before or after (depending on pos_class_comma, +# may not be IGNORE) each',' in the base class list. +nl_class_init_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in the constructor member +# initialization. Related to nl_constr_colon, pos_constr_colon and +# pos_constr_comma. +nl_constr_init_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before first element, after comma, and after last +# element, in 'enum'. +nl_enum_own_lines = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name in a function +# definition. +# might be modified by nl_func_leave_one_liners +nl_func_type_name = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name inside a class +# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name +# is used instead. +nl_func_type_name_class = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between class specification and '::' +# in 'void A::f() { }'. Only appears in separate member implementation (does +# not appear with in-line implementation). +nl_func_class_scope = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between function scope and name, as in +# 'void A :: f() { }'. +nl_func_scope_name = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name in a prototype. +nl_func_proto_type_name = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# declaration. +nl_func_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_paren for functions with no parameters. +nl_func_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# definition. +nl_func_def_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_paren for functions with no parameters. +nl_func_def_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# call. +nl_func_call_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_call_paren for functions with no parameters. +nl_func_call_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function declaration. +nl_func_decl_start = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function definition. +nl_func_def_start = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_decl_start when there is only one parameter. +nl_func_decl_start_single = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_start when there is only one parameter. +nl_func_def_start_single = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_start is used instead. +nl_func_decl_start_multi_line = true # true/false + +# Whether to add a newline after '(' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_start is used instead. +nl_func_def_start_multi_line = true # true/false + +# Add or remove newline after each ',' in a function declaration. +nl_func_decl_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in a function definition. +nl_func_def_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in a function call. +nl_func_call_args = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after each ',' in a function declaration if '(' +# and ')' are in different lines. If false, nl_func_decl_args is used instead. +nl_func_decl_args_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function definition if '(' +# and ')' are in different lines. If false, nl_func_def_args is used instead. +nl_func_def_args_multi_line = false # true/false + +# Add or remove newline before the ')' in a function declaration. +nl_func_decl_end = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before the ')' in a function definition. +nl_func_def_end = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_decl_end when there is only one parameter. +nl_func_decl_end_single = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_end when there is only one parameter. +nl_func_def_end_single = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before ')' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_end is used instead. +nl_func_decl_end_multi_line = false # true/false + +# Whether to add a newline before ')' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_end is used instead. +nl_func_def_end_multi_line = false # true/false + +# Add or remove newline between '()' in a function declaration. +nl_func_decl_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '()' in a function definition. +nl_func_def_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '()' in a function call. +nl_func_call_empty = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function call, +# has preference over nl_func_call_start_multi_line. +nl_func_call_start = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before ')' in a function call. +nl_func_call_end = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function call if '(' and ')' are in +# different lines. +nl_func_call_start_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function call if '(' and ')' +# are in different lines. +nl_func_call_args_multi_line = false # true/false + +# Whether to add a newline before ')' in a function call if '(' and ')' are in +# different lines. +nl_func_call_end_multi_line = false # true/false + +# Whether to respect nl_func_call_XXX option in case of closure args. +nl_func_call_args_multi_line_ignore_closures = false # true/false + +# Whether to add a newline after '<' of a template parameter list. +nl_template_start = false # true/false + +# Whether to add a newline after each ',' in a template parameter list. +nl_template_args = false # true/false + +# Whether to add a newline before '>' of a template parameter list. +nl_template_end = false # true/false + +# (OC) Whether to put each Objective-C message parameter on a separate line. +# See nl_oc_msg_leave_one_liner. +nl_oc_msg_args = false # true/false + +# (OC) Minimum number of Objective-C message parameters before applying nl_oc_msg_args. +nl_oc_msg_args_min_params = 0 # unsigned number + +# (OC) Max code width of Objective-C message before applying nl_oc_msg_args. +nl_oc_msg_args_max_code_width = 0 # unsigned number + +# Add or remove newline between function signature and '{'. +nl_fdef_brace = force # ignore/add/remove/force/not_defined + +# Add or remove newline between function signature and '{', +# if signature ends with ')'. Overrides nl_fdef_brace. +nl_fdef_brace_cond = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between C++11 lambda signature and '{'. +nl_cpp_ldef_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'return' and the return expression. +nl_return_expr = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'throw' and the throw expression. +nl_throw_expr = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after semicolons, except in 'for' statements. +nl_after_semicolon = false # true/false + +# (Java) Add or remove newline between the ')' and '{{' of the double brace +# initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after the type in an unnamed temporary +# direct-list-initialization, better: +# before a direct-list-initialization. +nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after the open brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before the close brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before '{'. +nl_before_brace_open = false # true/false + +# Whether to add a newline after '{'. +nl_after_brace_open = false # true/false + +# Whether to add a newline between the open brace and a trailing single-line +# comment. Requires nl_after_brace_open=true. +nl_after_brace_open_cmt = false # true/false + +# Whether to add a newline after a virtual brace open with a non-empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = false # true/false + +# Whether to add a newline after a virtual brace open with an empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open_empty = false # true/false + +# Whether to add a newline after '}'. Does not apply if followed by a +# necessary ';'. +nl_after_brace_close = false # true/false + +# Whether to add a newline after a virtual brace close, +# as in 'if (foo) a++; return;'. +nl_after_vbrace_close = true # true/false + +# Add or remove newline between the close brace and identifier, +# as in 'struct { int a; } b;'. Affects enumerations, unions and +# structures. If set to ignore, uses nl_after_brace_close. +nl_brace_struct_var = ignore # ignore/add/remove/force/not_defined + +# Whether to alter newlines in '#define' macros. +nl_define_macro = false # true/false + +# Whether to alter newlines between consecutive parenthesis closes. The number +# of closing parentheses in a line will depend on respective open parenthesis +# lines. +nl_squeeze_paren_close = false # true/false + +# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and +# '#endif'. Does not affect top-level #ifdefs. +nl_squeeze_ifdef = false # true/false + +# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. +nl_squeeze_ifdef_top_level = false # true/false + +# Add or remove blank line before 'if'. +nl_before_if = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'if' statement. Add/Force work only if the +# next token is not a closing brace. +nl_after_if = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'for'. +nl_before_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'for' statement. +nl_after_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'while'. +nl_before_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'while' statement. +nl_after_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'switch'. +nl_before_switch = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'switch' statement. +nl_after_switch = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'synchronized'. +nl_before_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'synchronized' statement. +nl_after_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'do'. +nl_before_do = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'do/while' statement. +nl_after_do = ignore # ignore/add/remove/force/not_defined + +# Ignore nl_before_{if,for,switch,do,synchronized} if the control +# statement is immediately after a case statement. +# if nl_before_{if,for,switch,do} is set to remove, this option +# does nothing. +nl_before_ignore_after_case = false # true/false + +# Whether to put a blank line before 'return' statements, unless after an open +# brace. +nl_before_return = false # true/false + +# Whether to put a blank line after 'return' statements, unless followed by a +# close brace. +nl_after_return = false # true/false + +# Whether to put a blank line before a member '.' or '->' operators. +nl_before_member = ignore # ignore/add/remove/force/not_defined + +# (Java) Whether to put a blank line after a member '.' or '->' operators. +nl_after_member = ignore # ignore/add/remove/force/not_defined + +# Whether to double-space commented-entries in 'struct'/'union'/'enum'. +nl_ds_struct_enum_cmt = false # true/false + +# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. +# (Lower priority than eat_blanks_before_close_brace.) +nl_ds_struct_enum_close_brace = false # true/false + +# Add or remove newline before or after (depending on pos_class_colon) a class +# colon, as in 'class Foo : public Bar'. +nl_class_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline around a class constructor colon. The exact position +# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. +nl_constr_colon = force # ignore/add/remove/force/not_defined + +# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' +# into a single line. If true, prevents other brace newline rules from turning +# such code into four lines. If true, it also preserves one-liner namespaces. +nl_namespace_two_to_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced if statements, turning them +# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. +nl_create_if_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced for statements, turning them +# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. +nl_create_for_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced while statements, turning +# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. +nl_create_while_one_liner = false # true/false + +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_func_def_one_liner = false # true/false + +# Whether to split one-line simple list definitions into three lines by +# adding newlines, as in 'int a[12] = { 0 };'. +nl_create_list_one_liner = false # true/false + +# Whether to split one-line simple unbraced if statements into two lines by +# adding a newline, as in 'if(b) i++;'. +nl_split_if_one_liner = false # true/false + +# Whether to split one-line simple unbraced for statements into two lines by +# adding a newline, as in 'for (...) stmt;'. +nl_split_for_one_liner = false # true/false + +# Whether to split one-line simple unbraced while statements into two lines by +# adding a newline, as in 'while (expr) stmt;'. +nl_split_while_one_liner = false # true/false + +# Don't add a newline before a cpp-comment in a parameter list of a function +# call. +donot_add_nl_before_cpp_comment = false # true/false + +# +# Blank line options +# + +# The maximum number of consecutive newlines (3 = 2 blank lines). +nl_max = 3 # unsigned number + +# The maximum number of consecutive newlines in a function. +nl_max_blank_in_func = 0 # unsigned number + +# The number of newlines inside an empty function body. +# This option overrides eat_blanks_after_open_brace and +# eat_blanks_before_close_brace, but is ignored when +# nl_collapse_empty_body_functions=true +nl_inside_empty_func = 0 # unsigned number + +# The number of newlines before a function prototype. +nl_before_func_body_proto = 0 # unsigned number + +# The number of newlines before a multi-line function definition. Where +# applicable, this option is overridden with eat_blanks_after_open_brace=true +nl_before_func_body_def = 0 # unsigned number + +# The number of newlines before a class constructor/destructor prototype. +nl_before_func_class_proto = 0 # unsigned number + +# The number of newlines before a class constructor/destructor definition. +nl_before_func_class_def = 0 # unsigned number + +# The number of newlines after a function prototype. +nl_after_func_proto = 0 # unsigned number + +# The number of newlines after a function prototype, if not followed by +# another function prototype. +nl_after_func_proto_group = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype. +nl_after_func_class_proto = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype, +# if not followed by another constructor/destructor prototype. +nl_after_func_class_proto_group = 0 # unsigned number + +# Whether one-line method definitions inside a class body should be treated +# as if they were prototypes for the purposes of adding newlines. +# +# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def +# and nl_before_func_class_def for one-liners. +nl_class_leave_one_liner_groups = false # true/false + +# The number of newlines after '}' of a multi-line function body. +# +# Overrides nl_min_after_func_body and nl_max_after_func_body. +nl_after_func_body = 0 # unsigned number + +# The minimum number of newlines after '}' of a multi-line function body. +# +# Only works when nl_after_func_body is 0. +nl_min_after_func_body = 0 # unsigned number + +# The maximum number of newlines after '}' of a multi-line function body. +# +# Only works when nl_after_func_body is 0. +# Takes precedence over nl_min_after_func_body. +nl_max_after_func_body = 0 # unsigned number + +# The number of newlines after '}' of a multi-line function body in a class +# declaration. Also affects class constructors/destructors. +# +# Overrides nl_after_func_body. +nl_after_func_body_class = 0 # unsigned number + +# The number of newlines after '}' of a single line function body. Also +# affects class constructors/destructors. +# +# Overrides nl_after_func_body and nl_after_func_body_class. +nl_after_func_body_one_liner = 0 # unsigned number + +# The number of newlines before a block of typedefs. If nl_after_access_spec +# is non-zero, that option takes precedence. +# +# 0: No change (default). +nl_typedef_blk_start = 0 # unsigned number + +# The number of newlines after a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_in = 0 # unsigned number + +# The minimum number of blank lines after a block of variable definitions +# at the top of a function body. If any preprocessor directives appear +# between the opening brace of the function and the variable block, then +# it is considered as not at the top of the function.Newlines are added +# before trailing preprocessor directives, if any exist. +# +# 0: No change (default). +nl_var_def_blk_end_func_top = 0 # unsigned number + +# The minimum number of empty newlines before a block of variable definitions +# not at the top of a function body. If nl_after_access_spec is non-zero, +# that option takes precedence. Newlines are not added at the top of the +# file or just after an opening brace. Newlines are added above any +# preprocessor directives before the block. +# +# 0: No change (default). +nl_var_def_blk_start = 0 # unsigned number + +# The minimum number of empty newlines after a block of variable definitions +# not at the top of a function body. Newlines are not added if the block +# is at the bottom of the file or just before a preprocessor directive. +# +# 0: No change (default). +nl_var_def_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of variable +# definitions. +# +# 0: No change (default). +nl_var_def_blk_in = 0 # unsigned number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 0 # unsigned number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 0 # unsigned number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 0 # unsigned number + +# Whether to force a newline after a multi-line comment. +nl_after_multiline_comment = false # true/false + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # true/false + +# The number of newlines before a struct definition. +nl_before_struct = 0 # unsigned number + +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number + +# The number of newlines before a class definition. +nl_before_class = 0 # unsigned number + +# The number of newlines after '}' or ';' of a class definition. +nl_after_class = 0 # unsigned number + +# The number of newlines before a namespace. +nl_before_namespace = 0 # unsigned number + +# The number of newlines after '{' of a namespace. This also adds newlines +# before the matching '}'. +# +# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if +# applicable, otherwise no change. +# +# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. +nl_inside_namespace = 0 # unsigned number + +# The number of newlines after '}' of a namespace. +nl_after_namespace = 0 # unsigned number + +# The number of newlines before an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +nl_before_access_spec = 2 # unsigned number + +# The number of newlines after an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +# +# Overrides nl_typedef_blk_start and nl_var_def_blk_start. +nl_after_access_spec = 1 # unsigned number + +# The number of newlines between a function definition and the function +# comment, as in '// comment\n void foo() {...}'. +# +# 0: No change (default). +nl_comment_func_def = 0 # unsigned number + +# The number of newlines after a try-catch-finally block that isn't followed +# by a brace close. +# +# 0: No change (default). +nl_after_try_catch_finally = 0 # unsigned number + +# (C#) The number of newlines before and after a property, indexer or event +# declaration. +# +# 0: No change (default). +nl_around_cs_property = 0 # unsigned number + +# (C#) The number of newlines between the get/set/add/remove handlers. +# +# 0: No change (default). +nl_between_get_set = 0 # unsigned number + +# (C#) Add or remove newline between property and the '{'. +nl_property_brace = ignore # ignore/add/remove/force/not_defined + +# Whether to remove blank lines after '{'. +eat_blanks_after_open_brace = false # true/false + +# Whether to remove blank lines before '}'. +eat_blanks_before_close_brace = false # true/false + +# How aggressively to remove extra newlines not in preprocessor. +# +# 0: No change (default) +# 1: Remove most newlines not handled by other config +# 2: Remove all newlines and reformat completely by config +nl_remove_extra_newlines = 0 # unsigned number + +# (Java) Add or remove newline after an annotation statement. Only affects +# annotations that are after a newline. +nl_after_annotation = ignore # ignore/add/remove/force/not_defined + +# (Java) Add or remove newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force/not_defined + +# The number of newlines before a whole-file #ifdef. +# +# 0: No change (default). +nl_before_whole_file_ifdef = 0 # unsigned number + +# The number of newlines after a whole-file #ifdef. +# +# 0: No change (default). +nl_after_whole_file_ifdef = 0 # unsigned number + +# The number of newlines before a whole-file #endif. +# +# 0: No change (default). +nl_before_whole_file_endif = 0 # unsigned number + +# The number of newlines after a whole-file #endif. +# +# 0: No change (default). +nl_after_whole_file_endif = 0 # unsigned number + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions. +pos_arith = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of assignment in wrapped expressions. Do not affect '=' +# followed by '{'. +pos_assign = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of Boolean operators in wrapped expressions. +pos_bool = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of comparison operators in wrapped expressions. +pos_compare = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of conditional operators, as in the '?' and ':' of +# 'expr ? stmt : stmt', in wrapped expressions. +pos_conditional = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in wrapped expressions. +pos_comma = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in enum entries. +pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the base class list if there is more than one +# line. Affects nl_class_init_args. +pos_class_comma = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the constructor initialization list. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. +pos_constr_comma = trail # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of trailing/leading class colon, between class and base class +# list. Affects nl_class_colon. +pos_class_colon = lead_force # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of colons between constructor and member initialization. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. +pos_constr_colon = lead_break # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of shift operators in wrapped expressions. +pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# +# Line splitting options +# + +# Try to limit code width to N columns. +code_width = 100 # unsigned number + +# Whether to fully split long 'for' statements at semi-colons. +ls_for_split_full = false # true/false + +# Whether to fully split long function prototypes/calls at commas. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_func_split_full = false # true/false + +# Whether to split lines as close to code_width as possible and ignore some +# groupings. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_code_width = false # true/false + +# +# Code alignment options (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs. +align_keep_tabs = false # true/false + +# Whether to use tabs for aligning. +align_with_tabs = false # true/false + +# Whether to bump out to the next tab when aligning. +align_on_tabstop = false # true/false + +# Whether to right-align numbers. +align_number_right = false # true/false + +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # true/false + +# Whether to align variable definitions in prototypes and functions. +align_func_params = false # true/false + +# The span for aligning parameter definitions in function on parameter name. +# +# 0: Don't align (default). +align_func_params_span = 0 # unsigned number + +# The threshold for aligning function parameter definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_params_thresh = 0 # number + +# The gap for aligning function parameter definitions. +align_func_params_gap = 0 # unsigned number + +# Align parameters in single-line functions that have the same name. +# The function names must already be aligned with each other. +align_same_func_call_params = false # false/true + +# The span for aligning function-call parameters for single line functions. +# +# 0: Don't align (default). +align_same_func_call_params_span = 0 # unsigned number + +# The threshold for aligning function-call parameters for single line +# functions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_same_func_call_params_thresh = 0 # number + +# The span for aligning variable definitions. +# +# 0: Don't align (default). +align_var_def_span = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of variable definitions. +# +# 0: Part of the type 'void * foo;' (default) +# 1: Part of the variable 'void *foo;' +# 2: Dangling 'void *foo;' +# Dangling: the '*' will not be taken into account when aligning. +align_var_def_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of variable definitions. +# +# 0: Part of the type 'long & foo;' (default) +# 1: Part of the variable 'long &foo;' +# 2: Dangling 'long &foo;' +# Dangling: the '&' will not be taken into account when aligning. +align_var_def_amp_style = 0 # unsigned number + +# The threshold for aligning variable definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_def_thresh = 0 # number + +# The gap for aligning variable definitions. +align_var_def_gap = 0 # unsigned number + +# Whether to align the colon in struct bit fields. +align_var_def_colon = false # true/false + +# The gap for aligning the colon in struct bit fields. +align_var_def_colon_gap = 0 # unsigned number + +# Whether to align any attribute after the variable name. +align_var_def_attribute = false # true/false + +# Whether to align inline struct/enum/union variable definitions. +align_var_def_inline = false # true/false + +# The span for aligning on '=' in assignments. +# +# 0: Don't align (default). +align_assign_span = 0 # unsigned number + +# The span for aligning on '=' in function prototype modifier. +# +# 0: Don't align (default). +align_assign_func_proto_span = 0 # unsigned number + +# The threshold for aligning on '=' in assignments. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_assign_thresh = 0 # number + +# Whether to align on the left most assignment when multiple +# definitions are found on the same line. +# Depends on 'align_assign_span' and 'align_assign_thresh' settings. +align_assign_on_multi_var_defs = false # true/false + +# The span for aligning on '{' in braced init list. +# +# 0: Don't align (default). +align_braced_init_list_span = 0 # unsigned number + +# The threshold for aligning on '{' in braced init list. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_braced_init_list_thresh = 0 # number + +# How to apply align_assign_span to function declaration "assignments", i.e. +# 'virtual void foo() = 0' or '~foo() = {default|delete}'. +# +# 0: Align with other assignments (default) +# 1: Align with each other, ignoring regular assignments +# 2: Don't align +align_assign_decl_func = 0 # unsigned number + +# The span for aligning on '=' in enums. +# +# 0: Don't align (default). +align_enum_equ_span = 0 # unsigned number + +# The threshold for aligning on '=' in enums. +# Use a negative number for absolute thresholds. +# +# 0: no limit (default). +align_enum_equ_thresh = 0 # number + +# The span for aligning class member definitions. +# +# 0: Don't align (default). +align_var_class_span = 0 # unsigned number + +# The threshold for aligning class member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_class_thresh = 0 # number + +# The gap for aligning class member definitions. +align_var_class_gap = 0 # unsigned number + +# The span for aligning struct/union member definitions. +# +# 0: Don't align (default). +align_var_struct_span = 0 # unsigned number + +# The threshold for aligning struct/union member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_struct_thresh = 0 # number + +# The gap for aligning struct/union member definitions. +align_var_struct_gap = 0 # unsigned number + +# The span for aligning struct initializer values. +# +# 0: Don't align (default). +align_struct_init_span = 0 # unsigned number + +# The span for aligning single-line typedefs. +# +# 0: Don't align (default). +align_typedef_span = 0 # unsigned number + +# The minimum space between the type and the synonym of a typedef. +align_typedef_gap = 0 # unsigned number + +# How to align typedef'd functions with other typedefs. +# +# 0: Don't mix them at all (default) +# 1: Align the open parenthesis with the types +# 2: Align the function type name with the other type names +align_typedef_func = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int * pint;' (default) +# 1: Part of type name: 'typedef int *pint;' +# 2: Dangling: 'typedef int *pint;' +# Dangling: the '*' will not be taken into account when aligning. +align_typedef_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int & intref;' (default) +# 1: Part of type name: 'typedef int &intref;' +# 2: Dangling: 'typedef int &intref;' +# Dangling: the '&' will not be taken into account when aligning. +align_typedef_amp_style = 0 # unsigned number + +# The span for aligning comments that end lines. +# +# 0: Don't align (default). +align_right_cmt_span = 0 # unsigned number + +# Minimum number of columns between preceding text and a trailing comment in +# order for the comment to qualify for being aligned. Must be non-zero to have +# an effect. +align_right_cmt_gap = 0 # unsigned number + +# If aligning comments, whether to mix with comments after '}' and #endif with +# less than three spaces before the comment. +align_right_cmt_mix = false # true/false + +# Whether to only align trailing comments that are at the same brace level. +align_right_cmt_same_level = false # true/false + +# Minimum column at which to align trailing comments. Comments which are +# aligned beyond this column, but which can be aligned in a lesser column, +# may be "pulled in". +# +# 0: Ignore (default). +align_right_cmt_at_col = 0 # unsigned number + +# The span for aligning function prototypes. +# +# 0: Don't align (default). +align_func_proto_span = 0 # unsigned number + +# Whether to ignore continuation lines when evaluating the number of +# new lines for the function prototype alignment's span. +# +# false: continuation lines are part of the newlines count +# true: continuation lines are not counted +align_func_proto_span_ignore_cont_lines = false # true/false + +# How to consider (or treat) the '*' in the alignment of function prototypes. +# +# 0: Part of the type 'void * foo();' (default) +# 1: Part of the function 'void *foo();' +# 2: Dangling 'void *foo();' +# Dangling: the '*' will not be taken into account when aligning. +align_func_proto_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of function prototypes. +# +# 0: Part of the type 'long & foo();' (default) +# 1: Part of the function 'long &foo();' +# 2: Dangling 'long &foo();' +# Dangling: the '&' will not be taken into account when aligning. +align_func_proto_amp_style = 0 # unsigned number + +# The threshold for aligning function prototypes. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_proto_thresh = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # unsigned number + +# Whether to align function prototypes on the 'operator' keyword instead of +# what follows. +align_on_operator = false # true/false + +# Whether to mix aligning prototype and variable declarations. If true, +# align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # true/false + +# Whether to align single-line functions with function prototypes. +# Uses align_func_proto_span. +align_single_line_func = false # true/false + +# Whether to align the open brace of single-line functions. +# Requires align_single_line_func=true. Uses align_func_proto_span. +align_single_line_brace = false # true/false + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # unsigned number + +# (OC) The span for aligning Objective-C message specifications. +# +# 0: Don't align (default). +align_oc_msg_spec_span = 0 # unsigned number + +# Whether and how to align backslashes that split a macro onto multiple lines. +# This will not work right if the macro contains a multi-line comment. +# +# 0: Do nothing (default) +# 1: Align the backslashes in the column at the end of the longest line +# 2: Align with the backslash that is farthest to the left, or, if that +# backslash is farther left than the end of the longest line, at the end of +# the longest line +# 3: Align with the backslash that is farthest to the right +align_nl_cont = 0 # unsigned number + +# The minimum number of spaces between the end of a line and its continuation +# backslash. Requires align_nl_cont. +# +# Default: 1 +align_nl_cont_spaces = 1 # unsigned number + +# Whether to align macro functions and variables together. +align_pp_define_together = false # true/false + +# The span for aligning on '#define' bodies. +# +# =0: Don't align (default) +# >0: Number of lines (including comments) between blocks +align_pp_define_span = 0 # unsigned number + +# The minimum space between label and value of a preprocessor define. +align_pp_define_gap = 0 # unsigned number + +# Whether to align lines that start with '<<' with previous '<<'. +# +# Default: true +align_left_shift = true # true/false + +# Whether to align comma-separated statements following '<<' (as used to +# initialize Eigen matrices). +align_eigen_comma_init = false # true/false + +# Whether to align text after 'asm volatile ()' colons. +align_asm_colon = false # true/false + +# (OC) Span for aligning parameters in an Objective-C message call +# on the ':'. +# +# 0: Don't align. +align_oc_msg_colon_span = 0 # unsigned number + +# (OC) Whether to always align with the first parameter, even if it is too +# short. +align_oc_msg_colon_first = false # true/false + +# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration +# on the ':'. +align_oc_decl_colon = false # true/false + +# (OC) Whether to not align parameters in an Objectve-C message call if first +# colon is not on next line of the message call (the same way Xcode does +# alignment) +align_oc_msg_colon_xcode_like = false # true/false + +# +# Comment modification options +# + +# Try to wrap comments at N columns. +cmt_width = 0 # unsigned number + +# How to reflow comments. +# +# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) +# 1: No touching at all +# 2: Full reflow (enable cmt_indent_multi for indent with line wrapping due to cmt_width) +cmt_reflow_mode = 0 # unsigned number + +# Path to a file that contains regular expressions describing patterns for +# which the end of one line and the beginning of the next will be folded into +# the same sentence or paragraph during full comment reflow. The regular +# expressions are described using ECMAScript syntax. The syntax for this +# specification is as follows, where "..." indicates the custom regular +# expression and "n" indicates the nth end_of_prev_line_regex and +# beg_of_next_line_regex regular expression pair: +# +# end_of_prev_line_regex[1] = "...$" +# beg_of_next_line_regex[1] = "^..." +# end_of_prev_line_regex[2] = "...$" +# beg_of_next_line_regex[2] = "^..." +# . +# . +# . +# end_of_prev_line_regex[n] = "...$" +# beg_of_next_line_regex[n] = "^..." +# +# Note that use of this option overrides the default reflow fold regular +# expressions, which are internally defined as follows: +# +# end_of_prev_line_regex[1] = "[\w,\]\)]$" +# beg_of_next_line_regex[1] = "^[\w,\[\(]" +# end_of_prev_line_regex[2] = "\.$" +# beg_of_next_line_regex[2] = "^[A-Z]" +cmt_reflow_fold_regex_file = "" # string + +# Whether to indent wrapped lines to the start of the encompassing paragraph +# during full comment reflow (cmt_reflow_mode = 2). Overrides the value +# specified by cmt_sp_after_star_cont. +# +# Note that cmt_align_doxygen_javadoc_tags overrides this option for +# paragraphs associated with javadoc tags +cmt_reflow_indent_to_paragraph_start = false # true/false + +# Whether to convert all tabs to spaces in comments. If false, tabs in +# comments are left alone, unless used for indenting. +cmt_convert_tab_to_spaces = false # true/false + +# Whether to apply changes to multi-line comments, including cmt_width, +# keyword substitution and leading chars. +# +# Default: true +cmt_indent_multi = false # true/false + +# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.) +# and corresponding fields such that groups of consecutive block tags, +# parameter names, and descriptions align with one another. Overrides that +# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may +# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2 +# in order to achieve the desired alignment for line-wrapping. +cmt_align_doxygen_javadoc_tags = false # true/false + +# The number of spaces to insert after the star and before doxygen +# javadoc-style tags (@param, @return, etc). Requires enabling +# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the +# cmt_sp_after_star_cont. +# +# Default: 1 +cmt_sp_before_doxygen_javadoc_tags = 1 # unsigned number + +# Whether to change trailing, single-line c-comments into cpp-comments. +cmt_trailing_single_line_c_to_cpp = false # true/false + +# Whether to group c-comments that look like they are in a block. +cmt_c_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined c-comment. +cmt_c_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined c-comment. +cmt_c_nl_end = false # true/false + +# Whether to change cpp-comments into c-comments. +cmt_cpp_to_c = false # true/false + +# Whether to group cpp-comments that look like they are in a block. Only +# meaningful if cmt_cpp_to_c=true. +cmt_cpp_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_end = false # true/false + +# Whether to put a star on subsequent comment lines. +cmt_star_cont = false # true/false + +# The number of spaces to insert at the start of subsequent comment lines. +cmt_sp_before_star_cont = 0 # unsigned number + +# The number of spaces to insert after the star on subsequent comment lines. +cmt_sp_after_star_cont = 0 # unsigned number + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length. +# +# Default: true +cmt_multi_check_last = true # true/false + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length AND if the length is +# bigger as the first_len minimum. +# +# Default: 4 +cmt_multi_first_len_minimum = 4 # unsigned number + +# Path to a file that contains text to insert at the beginning of a file if +# the file doesn't start with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_header = "" # string + +# Path to a file that contains text to insert at the end of a file if the +# file doesn't end with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_footer = "" # string + +# Path to a file that contains text to insert before a function definition if +# the function isn't preceded by a C/C++ comment. If the inserted text +# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be +# replaced with, respectively, the name of the function, the javadoc '@param' +# and '@return' stuff, or the name of the class to which the member function +# belongs. +cmt_insert_func_header = "" # string + +# Path to a file that contains text to insert before a class if the class +# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', +# that will be replaced with the class name. +cmt_insert_class_header = "" # string + +# Path to a file that contains text to insert before an Objective-C message +# specification, if the method isn't preceded by a C/C++ comment. If the +# inserted text contains '$(message)' or '$(javaparam)', these will be +# replaced with, respectively, the name of the function, or the javadoc +# '@param' and '@return' stuff. +cmt_insert_oc_msg_header = "" # string + +# Whether a comment should be inserted if a preprocessor is encountered when +# stepping backwards from a function name. +# +# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and +# cmt_insert_class_header. +cmt_insert_before_preproc = false # true/false + +# Whether a comment should be inserted if a function is declared inline to a +# class definition. +# +# Applies to cmt_insert_func_header. +# +# Default: true +cmt_insert_before_inlines = true # true/false + +# Whether a comment should be inserted if the function is a class constructor +# or destructor. +# +# Applies to cmt_insert_func_header. +cmt_insert_before_ctor_dtor = false # true/false + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on a single-line 'do' statement. +mod_full_brace_do = force # ignore/add/remove/force/not_defined + +# Add or remove braces on a single-line 'for' statement. +mod_full_brace_for = force # ignore/add/remove/force/not_defined + +# (Pawn) Add or remove braces on a single-line function definition. +mod_full_brace_function = force # ignore/add/remove/force/not_defined + +# Add or remove braces on a single-line 'if' statement. Braces will not be +# removed if the braced statement contains an 'else'. +mod_full_brace_if = force # ignore/add/remove/force/not_defined + +# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either +# have, or do not have, braces. Overrides mod_full_brace_if. +# +# 0: Don't override mod_full_brace_if +# 1: Add braces to all blocks if any block needs braces and remove braces if +# they can be removed from all blocks +# 2: Add braces to all blocks if any block already has braces, regardless of +# whether it needs them +# 3: Add braces to all blocks if any block needs braces and remove braces if +# they can be removed from all blocks, except if all blocks have braces +# despite none needing them +mod_full_brace_if_chain = 0 # unsigned number + +# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. +# If true, mod_full_brace_if_chain will only remove braces from an 'if' that +# does not have an 'else if' or 'else'. +mod_full_brace_if_chain_only = false # true/false + +# Add or remove braces on single-line 'while' statement. +mod_full_brace_while = force # ignore/add/remove/force/not_defined + +# Add or remove braces on single-line 'using ()' statement. +mod_full_brace_using = force # ignore/add/remove/force/not_defined + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # unsigned number + +# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks +# which span multiple lines. +# +# Affects: +# mod_full_brace_for +# mod_full_brace_if +# mod_full_brace_if_chain +# mod_full_brace_if_chain_only +# mod_full_brace_while +# mod_full_brace_using +# +# Does not affect: +# mod_full_brace_do +# mod_full_brace_function +mod_full_brace_nl_block_rem_mlcond = false # true/false + +# Add or remove unnecessary parentheses on 'return' statement. +mod_paren_on_return = remove # ignore/add/remove/force/not_defined + +# Add or remove unnecessary parentheses on 'throw' statement. +mod_paren_on_throw = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Whether to change optional semicolons to real semicolons. +mod_pawn_semicolon = true # true/false + +# Whether to fully parenthesize Boolean expressions in 'while' and 'if' +# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. +mod_full_paren_if_bool = false # true/false + +# Whether to fully parenthesize Boolean expressions after '=' +# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'. +mod_full_paren_assign_bool = false # true/false + +# Whether to fully parenthesize Boolean expressions after '=' +# statement, as in 'return a && b > c;' => 'return (a && (b > c));'. +mod_full_paren_return_bool = false # true/false + +# Whether to remove superfluous semicolons. +mod_remove_extra_semicolon = true # true/false + +# Whether to remove duplicate include. +mod_remove_duplicate_include = false # true/false + +# the following options (mod_XX_closebrace_comment) use different comment, +# depending of the setting of the next option. +# false: Use the c comment (default) +# true : Use the cpp comment +mod_add_force_c_closebrace_comment = false # true/false + +# If a function body exceeds the specified number of newlines and doesn't have +# a comment after the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # unsigned number + +# If a namespace body exceeds the specified number of newlines and doesn't +# have a comment after the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # unsigned number + +# If a class body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_class_closebrace_comment = 0 # unsigned number + +# If a switch body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # unsigned number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have +# a comment after the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 0 # unsigned number + +# If an #ifdef or #else body exceeds the specified number of newlines and +# doesn't have a comment after the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 0 # unsigned number + +# Whether to take care of the case by the mod_sort_xx options. +mod_sort_case_sensitive = false # true/false + +# Whether to sort consecutive single-line 'import' statements. +mod_sort_import = false # true/false + +# (C#) Whether to sort consecutive single-line 'using' statements. +mod_sort_using = false # true/false + +# Whether to sort consecutive single-line '#include' statements (C/C++) and +# '#import' statements (Objective-C). Be aware that this has the potential to +# break your code if your includes/imports have ordering dependencies. +mod_sort_include = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# filename without extension when sorting is enabled. +mod_sort_incl_import_prioritize_filename = false # true/false + +# Whether to prioritize '#include' and '#import' statements that does not +# contain extensions when sorting is enabled. +mod_sort_incl_import_prioritize_extensionless = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# angle over quotes when sorting is enabled. +mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false + +# Whether to ignore file extension in '#include' and '#import' statements +# for sorting comparison. +mod_sort_incl_import_ignore_extension = false # true/false + +# Whether to group '#include' and '#import' statements when sorting is enabled. +mod_sort_incl_import_grouping_enabled = false # true/false + +# Whether to move a 'break' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. +mod_move_case_break = false # true/false + +# Whether to move a 'return' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'. +mod_move_case_return = false # true/false + +# Add or remove braces around a fully braced case statement. Will only remove +# braces if there are no variable declarations in the block. +mod_case_brace = ignore # ignore/add/remove/force/not_defined + +# Whether to remove a void 'return;' that appears as the last statement in a +# function. +mod_remove_empty_return = true # true/false + +# Add or remove the comma after the last value of an enumeration. +mod_enum_last_comma = ignore # ignore/add/remove/force/not_defined + +# Syntax to use for infinite loops. +# +# 0: Leave syntax alone (default) +# 1: Rewrite as `for(;;)` +# 2: Rewrite as `while(true)` +# 3: Rewrite as `do`...`while(true);` +# 4: Rewrite as `while(1)` +# 5: Rewrite as `do`...`while(1);` +# +# Infinite loops that do not already match one of these syntaxes are ignored. +# Other options that affect loop formatting will be applied after transforming +# the syntax. +mod_infinite_loop = 0 # unsigned number + +# Add or remove the 'int' keyword in 'int short'. +mod_int_short = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'short int'. +mod_short_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int long'. +mod_int_long = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'long int'. +mod_long_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int signed'. +mod_int_signed = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'signed int'. +mod_signed_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int unsigned'. +mod_int_unsigned = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'unsigned int'. +mod_unsigned_int = ignore # ignore/add/remove/force/not_defined + +# If there is a situation where mod_int_* and mod_*_int would result in +# multiple int keywords, whether to keep the rightmost int (the default) or the +# leftmost int. +mod_int_prefer_int_on_left = false # true/false + +# (OC) Whether to organize the properties. If true, properties will be +# rearranged according to the mod_sort_oc_property_*_weight factors. +mod_sort_oc_properties = false # true/false + +# (OC) Weight of a class property modifier. +mod_sort_oc_property_class_weight = 0 # number + +# (OC) Weight of 'atomic' and 'nonatomic'. +mod_sort_oc_property_thread_safe_weight = 0 # number + +# (OC) Weight of 'readwrite' when organizing properties. +mod_sort_oc_property_readwrite_weight = 0 # number + +# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', +# 'weak', 'strong') when organizing properties. +mod_sort_oc_property_reference_weight = 0 # number + +# (OC) Weight of getter type ('getter=') when organizing properties. +mod_sort_oc_property_getter_weight = 0 # number + +# (OC) Weight of setter type ('setter=') when organizing properties. +mod_sort_oc_property_setter_weight = 0 # number + +# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', +# 'null_resettable') when organizing properties. +mod_sort_oc_property_nullability_weight = 0 # number + +# +# Preprocessor options +# + +# How to use tabs when indenting preprocessor code. +# +# -1: Use 'indent_with_tabs' setting (default) +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: -1 +pp_indent_with_tabs = -1 # number + +# Add or remove indentation of preprocessor directives inside #if blocks +# at brace level 0 (file-level). +pp_indent = ignore # ignore/add/remove/force/not_defined + +# Whether to indent #if/#else/#endif at the brace level. If false, these are +# indented from column 1. +pp_indent_at_level = false # true/false + +# Whether to indent #if/#else/#endif at the parenthesis level if the brace +# level is 0. If false, these are indented from column 1. +pp_indent_at_level0 = false # true/false + +# Specifies the number of columns to indent preprocessors per level +# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies +# the number of columns to indent preprocessors per level +# at brace level > 0 (function-level). +# +# Default: 1 +pp_indent_count = 2 # unsigned number + +# Add or remove space after # based on pp level of #if blocks. +pp_space_after = ignore # ignore/add/remove/force/not_defined + +# Sets the number of spaces per level added with pp_space_after. +pp_space_count = 0 # unsigned number + +# The indent for '#region' and '#endregion' in C# and '#pragma region' in +# C/C++. Negative values decrease indent down to the first column. +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion. +pp_region_indent_code = false # true/false + +# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when +# not at file-level. Negative values decrease indent down to the first column. +# +# =0: Indent preprocessors using output_tab_size +# >0: Column at which all preprocessors will be indented +pp_indent_if = 0 # number + +# Whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # true/false + +# Whether to indent the body of an #if that encompasses all the code in the file. +pp_indent_in_guard = false # true/false + +# Whether to indent '#define' at the brace level. If false, these are +# indented from column 1. +pp_define_at_level = false # true/false + +# Whether to indent '#include' at the brace level. +pp_include_at_level = false # true/false + +# Whether to ignore the '#define' body while formatting. +pp_ignore_define_body = false # true/false + +# An offset value that controls the indentation of the body of a multiline #define. +# 'body' refers to all the lines of a multiline #define except the first line. +# Requires 'pp_ignore_define_body = false'. +# +# <0: Absolute column: the body indentation starts off at the specified column +# (ex. -3 ==> the body is indented starting from column 3) +# >=0: Relative to the column of the '#' of '#define' +# (ex. 3 ==> the body is indented starting 3 columns at the right of '#') +# +# Default: 8 +pp_multiline_define_body_indent = 2 # number + +# Whether to indent case statements between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the case statements +# directly inside of. +# +# Default: true +pp_indent_case = true # true/false + +# Whether to indent whole function definitions between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the function definition +# is directly inside of. +# +# Default: true +pp_indent_func_def = true # true/false + +# Whether to indent extern C blocks between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the extern block is +# directly inside of. +# +# Default: true +pp_indent_extern = true # true/false + +# How to indent braces directly inside #if, #else, and #endif. +# Requires pp_if_indent_code=true and only applies to the indent of the +# preprocessor that the braces are directly inside of. +# 0: No extra indent +# 1: Indent by one level +# -1: Preserve original indentation +# +# Default: 1 +pp_indent_brace = 1 # number + +# Whether to print warning messages for unbalanced #if and #else blocks. +# This will print a message in the following cases: +# - if an #ifdef block ends on a different indent level than +# where it started from. Example: +# +# #ifdef TEST +# int i; +# { +# int j; +# #endif +# +# - an #elif/#else block ends on a different indent level than +# the corresponding #ifdef block. Example: +# +# #ifdef TEST +# int i; +# #else +# } +# int j; +# #endif +pp_warn_unbalanced_if = false # true/false + +# +# Sort includes options +# + +# The regex for include category with priority 0. +include_category_0 = "" # string + +# The regex for include category with priority 1. +include_category_1 = "" # string + +# The regex for include category with priority 2. +include_category_2 = "" # string + +# +# Use or Do not Use options +# + +# true: indent_func_call_param will be used (default) +# false: indent_func_call_param will NOT be used +# +# Default: true +use_indent_func_call_param = false # true/false + +# The value of the indentation for a continuation line is calculated +# differently if the statement is: +# - a declaration: your case with QString fileName ... +# - an assignment: your case with pSettings = new QSettings( ... +# +# At the second case the indentation value might be used twice: +# - at the assignment +# - at the function call (if present) +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indent_continue will be used only once +# false: indent_continue will be used every time (default) +# +# Requires indent_ignore_first_continue=false. +use_indent_continue_only_once = false # true/false + +# The indentation can be: +# - after the assignment, at the '[' character +# - at the beginning of the lambda body +# +# true: indentation will be at the beginning of the lambda body +# false: indentation will be after the assignment (default) +indent_cpp_lambda_only_once = false # true/false + +# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the +# historic behavior, but is probably not the desired behavior, so this is off +# by default. +use_sp_after_angle_always = false # true/false + +# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, +# this tries to format these so that they match Qt's normalized form (i.e. the +# result of QMetaObject::normalizedSignature), which can slightly improve the +# performance of the QObject::connect call, rather than how they would +# otherwise be formatted. +# +# See options_for_QT.cpp for details. +# +# Default: true +use_options_overriding_for_qt_macros = true # true/false + +# If true: the form feed character is removed from the list of whitespace +# characters. See https://en.cppreference.com/w/cpp/string/byte/isspace. +use_form_feed_no_more_as_whitespace_character = false # true/false + +# +# Warn levels - 1: error, 2: warning (default), 3: note +# + +# (C#) Warning is given if doing tab-to-\t replacement and we have found one +# in a C# verbatim string literal. +# +# Default: 2 +warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number + +# Limit the number of loops. +# Used by uncrustify.cpp to exit from infinite loop. +# 0: no limit. +debug_max_number_of_loops = 0 # number + +# Set the number of the line to protocol; +# Used in the function prot_the_line if the 2. parameter is zero. +# 0: nothing protocol. +debug_line_number_to_protocol = 0 # number + +# Set the number of second(s) before terminating formatting the current file, +# 0: no timeout. +# only for linux +debug_timeout = 0 # number + +# Set the number of characters to be printed if the text is too long, +# 0: do not truncate. +debug_truncate = 0 # unsigned number + +# sort (or not) the tracking info. +# +# Default: true +debug_sort_the_tracks = false # true/false + +# decode (or not) the flags as a new line. +# only if the -p option is set. +debug_decode_the_flags = false # true/false + +# use (or not) the exit(EX_SOFTWARE) function. +# +# Default: true +debug_use_the_exit_function_pop = true # true/false + +# insert the number of the line at the beginning of each line +set_numbering_for_html_output = false # true/false + +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' +# +# +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` +# +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` +# +# +# option(s) with 'not default' value: 0 +# \ No newline at end of file From 3bb0906a0d6fc02bb5467fb709aea0ccbddbcfdd Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 08:34:29 -0500 Subject: [PATCH 09/10] Fixes linting failures. --- ssl_ros_bridge/src/team_client/team_client.hpp | 6 +++--- ssl_ros_bridge/src/team_client/team_client_node.cpp | 9 ++++++--- ssl_ros_bridge_msgs/CMakeLists.txt | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ssl_ros_bridge/src/team_client/team_client.hpp b/ssl_ros_bridge/src/team_client/team_client.hpp index 7e031c4..0a24deb 100644 --- a/ssl_ros_bridge/src/team_client/team_client.hpp +++ b/ssl_ros_bridge/src/team_client/team_client.hpp @@ -18,8 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#ifndef TEAM_CLIENT_HPP_ -#define TEAM_CLIENT_HPP_ +#ifndef TEAM_CLIENT__TEAM_CLIENT_HPP_ +#define TEAM_CLIENT__TEAM_CLIENT_HPP_ #include #include @@ -107,4 +107,4 @@ class TeamClient }; } // namespace ssl_ros_bridge::game_controller_bridge -#endif // TEAM_CLIENT_HPP_ +#endif // TEAM_CLIENT__TEAM_CLIENT_HPP_ diff --git a/ssl_ros_bridge/src/team_client/team_client_node.cpp b/ssl_ros_bridge/src/team_client/team_client_node.cpp index 20ff6fe..93e2a9c 100644 --- a/ssl_ros_bridge/src/team_client/team_client_node.cpp +++ b/ssl_ros_bridge/src/team_client/team_client_node.cpp @@ -69,7 +69,8 @@ class TeamClientNode : public rclcpp::Node &TeamClientNode::HandleSetAdvantageChoice, this, std::placeholders::_1, std::placeholders::_2), rclcpp::ServicesQoS()); - connection_status_publisher_ = create_publisher( + connection_status_publisher_ = + create_publisher( "~/connection_status", rclcpp::ServicesQoS()); const auto ping_period = declare_parameter("ping_period", 5); @@ -85,10 +86,12 @@ class TeamClientNode : public rclcpp::Node private: TeamClient team_client_; - rclcpp::Service::SharedPtr set_desired_keeper_service_; + rclcpp::Service::SharedPtr + set_desired_keeper_service_; rclcpp::Service::SharedPtr substitute_bot_service_; rclcpp::Service::SharedPtr reconnect_service_; - rclcpp::Service::SharedPtr advantage_choice_service_; + rclcpp::Service::SharedPtr + advantage_choice_service_; rclcpp::Publisher::SharedPtr connection_status_publisher_; rclcpp::TimerBase::SharedPtr ping_timer_; diff --git a/ssl_ros_bridge_msgs/CMakeLists.txt b/ssl_ros_bridge_msgs/CMakeLists.txt index beda64f..feb609b 100644 --- a/ssl_ros_bridge_msgs/CMakeLists.txt +++ b/ssl_ros_bridge_msgs/CMakeLists.txt @@ -11,7 +11,7 @@ find_package(ssl_league_msgs REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} msg/TeamClientConnectionStatus.msg - + srv/ReconnectTeamClient.srv srv/SetDesiredKeeper.srv srv/SetTeamAdvantageChoice.srv From f896636db3118793554887f50642756da677ba0c Mon Sep 17 00:00:00 2001 From: Matthew Barulic Date: Sat, 4 Jan 2025 08:42:24 -0500 Subject: [PATCH 10/10] Finishes first draft of docs. --- CONTRIBUTING.md | 21 ++++++--------------- README.md | 12 ++++++++---- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c62f9e..c4ffca8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,16 +6,12 @@ All types of contributions are encouraged and valued. See the [Table of Contents ## Table of Contents -- [Code of Conduct](#code-of-conduct) + - [I Have a Question](#i-have-a-question) - [I Want To Contribute](#i-want-to-contribute) -- [Reporting Bugs](#reporting-bugs) -- [Suggesting Enhancements](#suggesting-enhancements) -- [Your First Code Contribution](#your-first-code-contribution) -- [Improving The Documentation](#improving-the-documentation) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) - [Styleguides](#styleguides) -- [Commit Messages](#commit-messages) -- [Join The Project Team](#join-the-project-team) ## I Have a Question - +> If you want to ask a question, we assume that you have read the available [documentation](README.md). Before you ask a question, it is best to search for existing [Issues](https://github.com/SSL-A-Team/ssl_ros_bridge/issues) or [Discussions](https://github.com/SSL-A-Team/ssl_ros_bridge/discussions) that might help you. In case you have found a suitable issue/topic and still need clarification, you can write your question in the existing issue/topic. @@ -49,7 +45,7 @@ For quicker / shorter questions, you can also tag "@[A-Team] Matt Barulic" in th A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. - Make sure that you are using the latest version. -- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)). +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](README.md). If you are looking for support, you might want to check [this section](#i-have-a-question)). - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](issues?q=label%3Abug). - Collect information about the bug: - Any relevant traces or log output @@ -80,7 +76,7 @@ This section guides you through submitting an enhancement suggestion for CONTRIB #### Before Submitting an Enhancement - Make sure that you are using the latest version. -- Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration. +- Read the [documentation](README.md) carefully and find out if the functionality is already covered, maybe by an individual configuration. - Perform a [search](https://github.com/SSL-A-Team/ssl_ros_bridge/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. - Feel free to have early discussions about your suggestions via discussions or on Discord as presented in [this section](#i-have-a-question). @@ -95,11 +91,6 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/SSL-A- - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. - **Explain why this enhancement would be useful** to most ssl_ros_bridge users. You may also want to point out the other projects that solved it better and which could serve as inspiration. - - ## Styleguides This project follows the [ROS 2 style guides](https://docs.ros.org/en/rolling/The-ROS2-Project/Contributing/Code-Style-Language-Versions.html). diff --git a/README.md b/README.md index 5487e48..7475117 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository provides ROS 2 packages containing utilities for connecting your ## Installation -This repo is designed to be cloned into a colcon workspace. You can clone this repo alongside your own code or as a submodule within your own repository. +This repo is designed to be built in a colcon workspace. You can clone this repo alongside your own code or as a submodule within your own repository. ## Usage @@ -14,7 +14,9 @@ For most teams, in most scenarios, the only parameter that needs to be configure Two launch files are provided in ssl_ros_bridge for basic scenarios: [ssl_ros_bridge.launch.xml](ssl_ros_bridge/launch/ssl_ros_bridge.launch.xml) and [ssl_ros_bridge_localhost_only.launch.xml](ssl_ros_bridge/launch/ssl_ros_bridge_localhost_only.launch.xml). The first listens for multicast traffic on all interfaces. The localhost only launch file restricts the multicast listening to the loopback interface (127.0.0.1) and sets the game controller server address to 127.0.0.1. The localhost only launch file is useful when using simulation to prevent traffic from other simulation setups on the same network from interfering with your system. -_Note_: You may need to enable multicast on the loopback interface as many linux environments have it disabled by default. +> **Note** +> +> You may need to enable multicast on the loopback interface as many linux environments have it disabled by default. Either of these launch files can be included in your own launch file, specifying your team name. @@ -38,7 +40,9 @@ This package defines ROS messages which closely mirror the league protobufs. The SSL League protobufs make extensive use of optional fields, which are unfortunately not currently supported in ROS messages. ssl_ros_bridge uses array field types to hold optionals. These fields will either hold one or zero elements. This does introduce some ambiguity between optional fields and actual array fields. Users should refer to the corresponding protobuf definitions for clarity. -_Note:_ ROS fields do support bounded-size array types. We could enforce the "zero or one" behavior with these size limits, but this has caused problems with some ROS tools in the past (ie. PlotJuggler) which don't parse the message definitions correctly with these limits in place. +> **Note** +> +> ROS fields do support bounded-size array types. We could enforce the "zero or one" behavior with these size limits, but this has caused problems with some ROS tools in the past (ie. PlotJuggler) which don't parse the message definitions correctly with these limits in place. #### Recursive Message Definitions @@ -167,4 +171,4 @@ _Note_: Encrypted connections are not currently supported. ## Contributing -See [our contributing guidelines](CONTRIBUTING.md) +See [our contributing guidelines](CONTRIBUTING.md).