diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f94fa7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +.gradle +build diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..53b6da1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' + id 'idea' + id 'com.google.protobuf' version '0.8.19' +} + +group 'nl.roboteamtwente.autoref' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.google.protobuf:protobuf-java:3.21.5' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.6.1' + } +} + +test { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae04661 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..a69d9cb --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f127cfd --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..26f8f24 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'autoref' + diff --git a/src/main/java/nl/roboteamtwente/autoref/GameLog.java b/src/main/java/nl/roboteamtwente/autoref/GameLog.java new file mode 100644 index 0000000..3c17f1b --- /dev/null +++ b/src/main/java/nl/roboteamtwente/autoref/GameLog.java @@ -0,0 +1,79 @@ +package nl.roboteamtwente.autoref; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.robocup.ssl.proto.SslGcRefereeMessage; +import org.robocup.ssl.proto.SslVisionWrapper; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class GameLog { + private static final String IDENTIFIER = "SSL_LOG_FILE"; + + private final List messages = new ArrayList<>(); + + public GameLog(InputStream inputStream) throws IOException, IllegalStateException { + DataInputStream dataInputStream = new DataInputStream(inputStream); + + for (char c : IDENTIFIER.toCharArray()) { + if ((char) dataInputStream.readByte() != c) { + throw new IllegalStateException("Incorrect file header."); + } + } + + int version = dataInputStream.readInt(); + if (version != 1) { + throw new IllegalStateException("Unsupported log file format version " + version + "."); + } + + + while (dataInputStream.available() != 0) { + long timestamp = dataInputStream.readLong(); + int type = dataInputStream.readInt(); + int size = dataInputStream.readInt(); + byte[] buffer = new byte[size]; + dataInputStream.readFully(buffer, 0, size); + + Message message = Message.parse(type, buffer); + message.timestamp = timestamp; + message.size = size; + messages.add(message); + } + } + + public List getMessages() { + return messages; + } + + public static abstract class Message { + public long timestamp; + public int size; + + public static Message parse(int type, byte[] buffer) throws InvalidProtocolBufferException { + return switch (type) { + case 3 -> new Refbox2013(buffer); + case 4 -> new Vision2014(buffer); + default -> throw new IllegalStateException("Unhandled log message type " + type + "."); + }; + } + + public static class Refbox2013 extends Message { + public Refbox2013(byte[] buffer) throws InvalidProtocolBufferException { + this.packet = SslGcRefereeMessage.Referee.parseFrom(buffer); + } + + public SslGcRefereeMessage.Referee packet; + } + + public static class Vision2014 extends Message { + public Vision2014(byte[] buffer) throws InvalidProtocolBufferException { + this.packet = SslVisionWrapper.SSL_WrapperPacket.parseFrom(buffer); + } + + public SslVisionWrapper.SSL_WrapperPacket packet; + } + } +} diff --git a/src/main/java/nl/roboteamtwente/autoref/Main.java b/src/main/java/nl/roboteamtwente/autoref/Main.java new file mode 100644 index 0000000..b929bfc --- /dev/null +++ b/src/main/java/nl/roboteamtwente/autoref/Main.java @@ -0,0 +1,18 @@ +package nl.roboteamtwente.autoref; + +import java.io.FileInputStream; +import java.io.IOException; + +public class Main { + public static void main(String[] args) throws IOException { + GameLog gameLog = new GameLog(new FileInputStream(args[0])); + + for (GameLog.Message message : gameLog.getMessages()) { + if (message instanceof GameLog.Message.Refbox2013 refbox) { + System.out.println("refbox -> " + refbox.packet); + } else if (message instanceof GameLog.Message.Vision2014 vision) { + System.out.println("vision -> " + vision.packet); + } + } + } +} diff --git a/src/main/proto/ssl_autoref_ci.proto b/src/main/proto/ssl_autoref_ci.proto new file mode 100644 index 0000000..937cd11 --- /dev/null +++ b/src/main/proto/ssl_autoref_ci.proto @@ -0,0 +1,30 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/ci/autoref"; +option java_package = "org.robocup.ssl.proto"; + +import "ssl_vision_wrapper_tracked.proto"; +import "ssl_vision_detection.proto"; +import "ssl_vision_geometry.proto"; +import "ssl_gc_referee_message.proto"; + +// The AutoRefCiInput contains all packets/messages that would otherwise be received through multicast by the auto-referee +// It may contain either a raw or a tracked SSL-vision packet. If both are given, the implementation may choose either one. +message AutoRefCiInput { + // Latest referee message + optional Referee referee_message = 1; + // A tracked SSL-Vision packet to be processed without filtering + optional TrackerWrapperPacket tracker_wrapper_packet = 2; + // A list of unfiltered SSL-Vision packets (for multiple cameras) to be filtered and processed + repeated SSL_DetectionFrame detection = 3; + // Current geometry data, to be sent at least once at the beginning of the connection + optional SSL_GeometryData geometry = 4; +} + +// The AutoRefCiOutput contains any new data created by the auto-referee for further processing +message AutoRefCiOutput { + // A resulting tracked SSL-Vision packet for input into the ssl-game-controller. + // The auto-referee will either generate it from the unfiltered SSL-Vision packets + // or simply return the tracked packet from the input. + optional TrackerWrapperPacket tracker_wrapper_packet = 1; +} diff --git a/src/main/proto/ssl_gc_api.proto b/src/main/proto/ssl_gc_api.proto new file mode 100644 index 0000000..052d83d --- /dev/null +++ b/src/main/proto/ssl_gc_api.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/api"; +option java_package = "org.robocup.ssl.proto"; + +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/src/main/proto/ssl_gc_change.proto b/src/main/proto/ssl_gc_change.proto new file mode 100644 index 0000000..97dc8f5 --- /dev/null +++ b/src/main/proto/ssl_gc_change.proto @@ -0,0 +1,191 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/statemachine"; +option java_package = "org.robocup.ssl.proto"; + +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; + } + + // 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 uint32 group_id = 1; + // 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; + } + + // 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; + // 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; + } +} diff --git a/src/main/proto/ssl_gc_ci.proto b/src/main/proto/ssl_gc_ci.proto new file mode 100644 index 0000000..f13310a --- /dev/null +++ b/src/main/proto/ssl_gc_ci.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/ci"; +option java_package = "org.robocup.ssl.proto"; + +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/src/main/proto/ssl_gc_common.proto b/src/main/proto/ssl_gc_common.proto new file mode 100644 index 0000000..8fa261b --- /dev/null +++ b/src/main/proto/ssl_gc_common.proto @@ -0,0 +1,29 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; +option java_package = "org.robocup.ssl.proto"; + +// 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/src/main/proto/ssl_gc_engine.proto b/src/main/proto/ssl_gc_engine.proto new file mode 100644 index 0000000..fed8d45 --- /dev/null +++ b/src/main/proto/ssl_gc_engine.proto @@ -0,0 +1,143 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/engine"; +option java_package = "org.robocup.ssl.proto"; + +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 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; + } +} diff --git a/src/main/proto/ssl_gc_engine_config.proto b/src/main/proto/ssl_gc_engine_config.proto new file mode 100644 index 0000000..e95e6ad --- /dev/null +++ b/src/main/proto/ssl_gc_engine_config.proto @@ -0,0 +1,56 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/engine"; +option java_package = "org.robocup.ssl.proto"; + +// 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/src/main/proto/ssl_gc_game_event.proto b/src/main/proto/ssl_gc_game_event.proto new file mode 100644 index 0000000..354964a --- /dev/null +++ b/src/main/proto/ssl_gc_game_event.proto @@ -0,0 +1,568 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; +option java_package = "org.robocup.ssl.proto"; + +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 { + + 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; + + // 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; + + // 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; + 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 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 cards (yellow and red), which results in a penalty kick + 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; + } + // 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 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 + + // 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 + + // 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/src/main/proto/ssl_gc_geometry.proto b/src/main/proto/ssl_gc_geometry.proto new file mode 100644 index 0000000..c7bce27 --- /dev/null +++ b/src/main/proto/ssl_gc_geometry.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/geom"; +option java_package = "org.robocup.ssl.proto"; + +// 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/src/main/proto/ssl_gc_rcon.proto b/src/main/proto/ssl_gc_rcon.proto new file mode 100644 index 0000000..662adc1 --- /dev/null +++ b/src/main/proto/ssl_gc_rcon.proto @@ -0,0 +1,39 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; +option java_package = "org.robocup.ssl.proto"; + +// 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/src/main/proto/ssl_gc_rcon_autoref.proto b/src/main/proto/ssl_gc_rcon_autoref.proto new file mode 100644 index 0000000..ba46f42 --- /dev/null +++ b/src/main/proto/ssl_gc_rcon_autoref.proto @@ -0,0 +1,33 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; +option java_package = "org.robocup.ssl.proto"; + +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; + } +} diff --git a/src/main/proto/ssl_gc_rcon_remotecontrol.proto b/src/main/proto/ssl_gc_rcon_remotecontrol.proto new file mode 100644 index 0000000..fa3a357 --- /dev/null +++ b/src/main/proto/ssl_gc_rcon_remotecontrol.proto @@ -0,0 +1,106 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; +option java_package = "org.robocup.ssl.proto"; + +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 { + // 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; +} + +// 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/src/main/proto/ssl_gc_rcon_team.proto b/src/main/proto/ssl_gc_rcon_team.proto new file mode 100644 index 0000000..9f24951 --- /dev/null +++ b/src/main/proto/ssl_gc_rcon_team.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"; +option java_package = "org.robocup.ssl.proto"; + +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/src/main/proto/ssl_gc_referee_message.proto b/src/main/proto/ssl_gc_referee_message.proto new file mode 100644 index 0000000..3947af9 --- /dev/null +++ b/src/main/proto/ssl_gc_referee_message.proto @@ -0,0 +1,229 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; +option java_package = "org.robocup.ssl.proto"; + +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 sint32 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. + // For rules compliance, teams must treat as STOP. + // 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; + } + + // 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 non-finished proposed game events that may be processed next. + 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 int32 current_action_time_remaining = 15; +} + +// List of matching proposals +message GameEventProposalGroup { + // The proposed game event. + repeated GameEvent game_event = 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/src/main/proto/ssl_gc_state.proto b/src/main/proto/ssl_gc_state.proto new file mode 100644 index 0000000..bad103c --- /dev/null +++ b/src/main/proto/ssl_gc_state.proto @@ -0,0 +1,119 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"; +option java_package = "org.robocup.ssl.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/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 { + // List of proposals in this group + repeated Proposal proposals = 1; + // Whether the proposal group was accepted + optional bool accepted = 2; +} + +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; +} + +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; +} diff --git a/src/main/proto/ssl_vision_detection.proto b/src/main/proto/ssl_vision_detection.proto new file mode 100644 index 0000000..5606ca9 --- /dev/null +++ b/src/main/proto/ssl_vision_detection.proto @@ -0,0 +1,35 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"; +option java_package = "org.robocup.ssl.proto"; + +message SSL_DetectionBall { + required float confidence = 1; + optional uint32 area = 2; + required float x = 3; + required float y = 4; + optional float z = 5; + required float pixel_x = 6; + required float pixel_y = 7; +} + +message SSL_DetectionRobot { + required float confidence = 1; + optional uint32 robot_id = 2; + required float x = 3; + required float y = 4; + optional float orientation = 5; + required float pixel_x = 6; + required float pixel_y = 7; + optional float height = 8; +} + +message SSL_DetectionFrame { + required uint32 frame_number = 1; + required double t_capture = 2; + required double t_sent = 3; + required uint32 camera_id = 4; + repeated SSL_DetectionBall balls = 5; + repeated SSL_DetectionRobot robots_yellow = 6; + repeated SSL_DetectionRobot robots_blue = 7; +} diff --git a/src/main/proto/ssl_vision_detection_tracked.proto b/src/main/proto/ssl_vision_detection_tracked.proto new file mode 100644 index 0000000..c78ea30 --- /dev/null +++ b/src/main/proto/ssl_vision_detection_tracked.proto @@ -0,0 +1,92 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/tracker"; +option java_package = "org.robocup.ssl.proto"; + +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 + // If timestamp is larger than timestamp_captured, the source has applied a prediction already + 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/src/main/proto/ssl_vision_geometry.proto b/src/main/proto/ssl_vision_geometry.proto new file mode 100644 index 0000000..e270d9f --- /dev/null +++ b/src/main/proto/ssl_vision_geometry.proto @@ -0,0 +1,138 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"; +option java_package = "org.robocup.ssl.proto"; + +// A 2D float vector. +message Vector2f { + required float x = 1; + 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 { + required int32 field_length = 1; + required int32 field_width = 2; + required int32 goal_width = 3; + required int32 goal_depth = 4; + required int32 boundary_width = 5; + repeated SSL_FieldLineSegment field_lines = 6; + repeated SSL_FieldCircularArc field_arcs = 7; + optional int32 penalty_area_depth = 8; + optional int32 penalty_area_width = 9; + optional int32 center_circle_radius = 10; + optional int32 line_thickness = 11; + optional int32 goal_center_to_penalty_mark = 12; + optional int32 goal_height = 13; + optional float ball_radius = 14; + 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/src/main/proto/ssl_vision_wrapper.proto b/src/main/proto/ssl_vision_wrapper.proto new file mode 100644 index 0000000..51446f6 --- /dev/null +++ b/src/main/proto/ssl_vision_wrapper.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"; +option java_package = "org.robocup.ssl.proto"; + +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/src/main/proto/ssl_vision_wrapper_tracked.proto b/src/main/proto/ssl_vision_wrapper_tracked.proto new file mode 100644 index 0000000..a49238d --- /dev/null +++ b/src/main/proto/ssl_vision_wrapper_tracked.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +option go_package = "github.com/RoboCup-SSL/ssl-game-controller/internal/app/tracker"; +option java_package = "org.robocup.ssl.proto"; + +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; +}