diff --git a/source/docs/software/basic-programming/alliancecolor.rst b/source/docs/software/basic-programming/alliancecolor.rst index a5fed5ac77..453130d95c 100644 --- a/source/docs/software/basic-programming/alliancecolor.rst +++ b/source/docs/software/basic-programming/alliancecolor.rst @@ -1,6 +1,6 @@ # Get Alliance Color -The ``DriverStation`` class ([Java](https://github.wpilib.org/allwpilib/docs/beta/java/org/wpilib/driverstation/DriverStation.html), [C++](https://github.wpilib.org/allwpilib/docs/beta/cpp/classwpi_1_1_driver_station.html), :py:class:`Python `) has many useful features for getting data from the Driver Station computer. One of the most important features is ``getAlliance`` (Java & Python) / ``GetAlliance`` (C++). +The ``MatchState`` class ([Java](https://github.wpilib.org/allwpilib/docs/beta/java/org/wpilib/driverstation/MatchState.html), [C++](https://github.wpilib.org/allwpilib/docs/beta/cpp/classwpi_1_1_match_state.html), :py:class:`Python `) has many useful features for getting data from the Driver Station computer. One of the most important features is ``getAlliance`` (Java) / ``GetAlliance`` (C++) / ``get_alliance`` (Python). Note that there are three cases: red, blue, and no color yet. It is important that code handles the third case correctly because the alliance color will not be available until the Driver Station connects. In particular, code should not assume that the alliance color will be available during constructor methods, but it should be available by the time `autoInit` or `teleopInit` is called. FMS will set the alliance color automatically; when not connected to FMS, the alliance color can be set from the Driver Station (see :ref:`"Team Station" on the Operation Tab `). @@ -9,12 +9,12 @@ Note that there are three cases: red, blue, and no color yet. It is important t .. tab-set-code:: ```java - Optional ally = DriverStation.getAlliance(); + Optional ally = MatchState.getAlliance(); if (ally.isPresent()) { - if (ally.get() == Alliance.Red) { + if (ally.get() == Alliance.RED) { } - if (ally.get() == Alliance.Blue) { + if (ally.get() == Alliance.BLUE) { } } @@ -24,12 +24,11 @@ Note that there are three cases: red, blue, and no color yet. It is important t ``` ```c++ - using wpi::DriverStation::Alliance; - if (auto ally = wpi::DriverStation::GetAlliance()) { - if (ally.value() == Alliance::kRed) { + if (auto ally = wpi::MatchState::GetAlliance()) { + if (ally.value() == wpi::Alliance::RED) { } - if (ally.value() == Alliance::kBlue) { + if (ally.value() == wpi::Alliance::BLUE) { } } @@ -39,12 +38,12 @@ Note that there are three cases: red, blue, and no color yet. It is important t ``` ```Python - from wpilib import DriverStation - ally = DriverStation.get_alliance() + from wpilib import MatchState + ally = MatchState.get_alliance() if ally is not None: - if ally == DriverStation.Alliance.kRed: + if ally == MatchState.Alliance.RED: - elif ally == DriverStation.Alliance.kBlue: + elif ally == MatchState.Alliance.BLUE: else: diff --git a/source/docs/yearly-overview/2026-game-data.rst b/source/docs/yearly-overview/2026-game-data.rst index 76ffe65597..666f06257e 100644 --- a/source/docs/yearly-overview/2026-game-data.rst +++ b/source/docs/yearly-overview/2026-game-data.rst @@ -16,89 +16,75 @@ The alliance will be provided as a single character representing the color of th ## Testing Game Specific Data -You can test your Game Specific Data code without :term:`FMS` by using the Driver Station. Click on the Setup tab of the Driver Station, then enter the desired test string into the Game Data text field. The data will be transmitted to the robot in one of two conditions: Enable the robot in Teleop mode, or when the DS reaches the End Game time in a Practice Match (times are configurable on the Setup tab). It is recommended to run at least one match using the Practice functionality to verify that your code works correctly in a full match flow. +You can test your Game Specific Data code without :term:`FMS` by using the Driver Station. Click on the Settings tab of the Driver Station, then enter the desired test string into the :guilabel:`Game Data` text field. The data will be transmitted to the robot in one of two conditions: Enable the robot in Teleop mode, or when the DS reaches the End Game time in a Practice Match (times are configurable on the Settings tab). It is recommended to run at least one match using the Practice functionality to verify that your code works correctly in a full match flow. .. image:: images/2020-Game-Data/ds-game-data.png :alt: Game Data text box on the Driver Station. -.. note:: For 2026, the match timings have changed and do not match the driver station defaults. To match the 2026 game, change the values to: - - - Autonomous: 20 seconds - - Teleoperated: 110 seconds - - End Game: 30 seconds - ## Accessing the Data -The data is accessed using the Game Data methods or VIs in each language. Below are descriptions and examples of how to access the data from each of the three languages. As the data is provided to the Robot during the Teleop period, teams will likely want to query the data in Teleop periodic code. +The data is accessed using the Game Data methods in each language. Below are descriptions and examples of how to access the data from each of the three languages. As the data is provided to the Robot during the Teleop period, teams will likely want to query the data in Teleop periodic code. ### C++/Java/Python -In C++, Java, and Python the Game Data is accessed by using the GetGameSpecificMessage method of the DriverStation class. Teams likely want to query the data in a Teleop method such as Teleop Periodic in order to receive the data after it is sent during the match. Make sure to handle the case where the data is an empty string as this is what the data will be until the selected alliance is sent. +In C++, Java, and Python the Game Data is accessed by using the ``GetGameData`` method of the MatchState class ([Java](https://github.wpilib.org/allwpilib/docs/beta/java/org/wpilib/driverstation/MatchState.html), [C++](https://github.wpilib.org/allwpilib/docs/beta/cpp/classwpi_1_1_match_state.html), :py:class:`Python `). ``GetGameData`` returns an ``Optional`` type so Teams know that Game Data has been received. Teams likely want to query the data in a Teleop method such as Teleop Periodic in order to receive the data after it is sent during the match. Make sure to handle the case where the data has not been received. .. tab-set-code:: ```java - import edu.wpi.first.wpilibj.DriverStation; - String gameData; - gameData = DriverStation.getGameSpecificMessage(); - if(gameData.length() > 0) - { - switch (gameData.charAt(0)) - { - case 'B' : - //Blue case code - break; - case 'R' : - //Red case code - break; - default : - //This is corrupt data - break; - } - } else { - //Code for no data received yet - } + Optional gameData = MatchState.getGameData(); + if (gameData.isPresent() && gameData.get().length() > 0) { + switch (gameData.get().charAt(0)) { + case 'B': + // Blue case code + break; + case 'R': + // Red case code + break; + default: + // This is corrupt data + break; + } + } else { + // Code for no data received yet + } ``` ```c++ - #include - std::string gameData; - gameData = wpi::DriverStation::GetGameSpecificMessage(); - if(gameData.length() > 0) - { - switch (gameData[0]) - { - case 'B' : - //Blue case code - break; - case 'R' : - //Red case code - break; - default : - //This is corrupt data - break; - } - } else { - //Code for no data received yet - } + auto gameData = wpi::MatchState::GetGameData(); + if (gameData.has_value() && gameData->length() > 0) { + switch (gameData->at(0)) { + case 'B': + // Blue case code + break; + case 'R': + // Red case code + break; + default: + // This is corrupt data + break; + } + } else { + // Code for no data received yet + } ``` ```python - data = wpilib.DriverStation.getGameSpecificMessage() - if data: - match data: - case "B": - # Blue case code - ... - case "R": - # Red case code - ... - case _: - # This is corrupt data - ... - else: - # Code for no data received yet - ... + data = wpilib.MatchState.get_game_data() + if data: + match data: + case "B": + # Blue case code + ... + case "R": + # Red case code + ... + case _: + # This is corrupt data + ... + else: + # Code for no data received yet + ... ``` You can combine the Game Data with the current match time to determine whether your own alliance's hub is currently active. Note however the FMS doesn't send an official match time to robots, only an approximate match time. @@ -108,123 +94,168 @@ For example: .. tab-set-code:: ```java - public boolean isHubActive() { - Optional alliance = DriverStation.getAlliance(); - // If we have no alliance, we cannot be enabled, therefore no hub. - if (alliance.isEmpty()) { - return false; - } - // Hub is always enabled in autonomous. - if (DriverStation.isAutonomousEnabled()) { - return true; - } - // At this point, if we're not teleop enabled, there is no hub. - if (!DriverStation.isTeleopEnabled()) { - return false; - } - - // We're teleop enabled, compute. - double matchTime = DriverStation.getMatchTime(); - String gameData = DriverStation.getGameSpecificMessage(); - // If we have no game data, we cannot compute, assume hub is active, as its likely early in teleop. - if (gameData.isEmpty()) { - return true; - } - boolean redInactiveFirst = false; - switch (gameData.charAt(0)) { - case 'R' -> redInactiveFirst = true; - case 'B' -> redInactiveFirst = false; - default -> { - // If we have invalid game data, assume hub is active. - return true; - } - } - - // Shift was is active for blue if red won auto, or red if blue won auto. - boolean shift1Active = switch (alliance.get()) { - case Red -> !redInactiveFirst; - case Blue -> redInactiveFirst; - }; - - if (matchTime > 130) { - // Transition shift, hub is active. - return true; - } else if (matchTime > 105) { - // Shift 1 - return shift1Active; - } else if (matchTime > 80) { - // Shift 2 - return !shift1Active; - } else if (matchTime > 55) { - // Shift 3 - return shift1Active; - } else if (matchTime > 30) { - // Shift 4 - return !shift1Active; - } else { - // End game, hub always active. - return true; - } - } + public boolean isHubActive() { + Optional alliance = MatchState.getAlliance(); + // If we have no alliance, we cannot be enabled, therefore no hub. + if (alliance.isEmpty()) { + return false; + } + // Hub is always enabled in autonomous. + if (RobotState.isAutonomousEnabled()) { + return true; + } + // At this point, if we're not teleop enabled, there is no hub. + if (!RobotState.isTeleopEnabled()) { + return false; + } + + // We're teleop enabled, compute. + double matchTime = MatchState.getMatchTime(); + Optional gameData = MatchState.getGameData(); + // If we have no game data, we cannot compute, assume hub is active, as its + // likely early in teleop + if (!gameData.isPresent() || gameData.get().isEmpty()) { + return true; + } + boolean redInactiveFirst = false; + switch (gameData.get().charAt(0)) { + case 'R' -> redInactiveFirst = true; + case 'B' -> redInactiveFirst = false; + default -> { + // If we have invalid game data, assume hub is active. + return true; + } + } + + // Shift was is active for blue if red won auto, or red if blue won auto. + boolean shift1Active = + switch (alliance.get()) { + case Alliance.RED -> !redInactiveFirst; + case Alliance.BLUE -> redInactiveFirst; + }; + + if (matchTime > 130) { + // Transition shift, hub is active. + return true; + } else if (matchTime > 105) { + // Shift 1 + return shift1Active; + } else if (matchTime > 80) { + // Shift 2 + return !shift1Active; + } else if (matchTime > 55) { + // Shift 3 + return shift1Active; + } else if (matchTime > 30) { + // Shift 4 + return !shift1Active; + } else { + // End game, hub always active. + return true; + } + } ``` - ```python - from wpilib import DriverStation - - - def is_hub_active() -> bool: - alliance = DriverStation.get_alliance() - # If we have no alliance, we cannot be enabled, therefore no hub. - if alliance is None: - return False - - # Hub is always enabled in autonomous. - if DriverStation.isAutonomousEnabled(): - return True - - # At this point if we're not teleop enabled, there is no hub. - if not DriverStation.isTeleopEnabled(): - return False - - # We're teleop enabled, compute. - match_time = DriverStation.get_match_time() - game_data = DriverStation.getGameSpecificMessage() - - match game_data: - case "R": - red_inactive_first = True - case "B": - red_inactive_first = False - case _: - # No or invalid game data, assume hub is active. - return True - - # Shift 1 is active for blue if red won auto, or red if blue won auto. - shift1_active = not red_inactive_first if alliance == DriverStation.Alliance.kRed else red_inactive_first - - if match_time > 130: - return True # Transition shift, hub is active - elif match_time > 105: - # Shift 1 - return shift1_active - elif match_time > 80: - # Shift 2 - return not shift1_active - elif match_time > 55: - # Shift 3 - return shift1_active - elif match_time > 30: - # Shift 4 - return not shift1_active - else: - return True # End game, hub always active + ```c++ + bool IsHubActive() { + auto alliance = wpi::MatchState::GetAlliance(); + if (!alliance.has_value()) { + return false; + } + if (wpi::RobotState::IsAutonomousEnabled()) { + return true; + } + if (!wpi::RobotState::IsTeleopEnabled()) { + return false; + } + + auto matchTime = wpi::MatchState::GetMatchTime().value(); + auto gameData = wpi::MatchState::GetGameData(); + if (!gameData.has_value() || gameData->empty()) { + return true; + } + + bool redInactiveFirst; + switch ((*gameData)[0]) { + case 'R': + redInactiveFirst = true; + break; + case 'B': + redInactiveFirst = false; + break; + default: + return true; + } + + bool shift1Active = + *alliance == wpi::Alliance::RED ? !redInactiveFirst : redInactiveFirst; + + if (matchTime > 130) { + return true; + } else if (matchTime > 105) { + return shift1Active; + } else if (matchTime > 80) { + return !shift1Active; + } else if (matchTime > 55) { + return shift1Active; + } else if (matchTime > 30) { + return !shift1Active; + } else { + return true; + } + } ``` -### LabVIEW - -The Game Data in LabVIEW is accessed from the Game Specific Data VI. This VI can be found in the WPI Robotics Library -> Driver Station palette. - -LabVIEW teams will likely want to query the data in the Teleop or PeriodicTasks VIs and may choose to gate the query behind a button press or other action. The code below reads the data and then uses a case structure to react differently to each of the 3 possible cases (empty, or either of the 2 letters). + ```python + def is_hub_active() -> bool: + alliance = wpilib.MatchState.get_alliance() + # If we have no alliance, we cannot be enabled, therefore no hub. + if alliance is None: + return False + + # Hub is always enabled in autonomous. + if wpilib.RobotState.is_autonomous_enabled(): + return True + + # At this point if we're not teleop enabled, there is no hub. + if not wpilib.RobotState.is_teleop_enabled(): + return False + + # We're teleop enabled, compute. + match_time = wpilib.MatchState.get_match_time() + game_data = wpilib.MatchState.get_game_data() + + match game_data: + case "R": + red_inactive_first = True + case "B": + red_inactive_first = False + case _: + # No or invalid game data, assume hub is active. + return True + + # Shift 1 is active for blue if red won auto, or red if blue won auto. + shift1_active = ( + not red_inactive_first + if alliance == wpilib.Alliance.RED + else red_inactive_first + ) + + if match_time > 130: + return True # Transition shift, hub is active + elif match_time > 105: + # Shift 1 + return shift1_active + elif match_time > 80: + # Shift 2 + return not shift1_active + elif match_time > 55: + # Shift 3 + return shift1_active + elif match_time > 30: + # Shift 4 + return not shift1_active + else: + return True # End game, hub always active + ``` -.. image:: images/2020-Game-Data/labview-game-data-code2026.png - :alt: Making a decision what to do about the game data received using a case structure. diff --git a/source/docs/yearly-overview/images/2020-Game-Data/ds-game-data.png b/source/docs/yearly-overview/images/2020-Game-Data/ds-game-data.png index 2b391f8362..53727cbe88 100644 Binary files a/source/docs/yearly-overview/images/2020-Game-Data/ds-game-data.png and b/source/docs/yearly-overview/images/2020-Game-Data/ds-game-data.png differ