2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2016-05-25 20:23:37 -07:00
|
|
|
|
2025-11-07 19:56:21 -05:00
|
|
|
#include "wpi/hal/DriverStation.h"
|
2017-08-18 21:35:53 -07:00
|
|
|
|
2017-08-19 23:12:24 -07:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-11-30 02:19:15 -05:00
|
|
|
#include <atomic>
|
2017-08-19 22:56:39 -07:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
2017-11-01 21:58:44 -07:00
|
|
|
#include <cstring>
|
2017-08-19 22:56:39 -07:00
|
|
|
#include <string>
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <fmt/format.h>
|
2017-11-13 09:51:48 -08:00
|
|
|
|
2026-01-04 00:41:53 -08:00
|
|
|
#include "HALInitializer.hpp"
|
|
|
|
|
#include "mockdata/DriverStationDataInternal.hpp"
|
2025-12-12 21:25:57 -07:00
|
|
|
#include "wpi/hal/DriverStationTypes.h"
|
2025-11-07 19:56:21 -05:00
|
|
|
#include "wpi/hal/Errors.h"
|
2026-03-15 15:08:41 -07:00
|
|
|
#include "wpi/hal/monotonic_clock.hpp"
|
2025-11-07 19:56:21 -05:00
|
|
|
#include "wpi/hal/simulation/MockHooks.h"
|
2025-11-07 19:57:55 -05:00
|
|
|
#include "wpi/util/EventVector.hpp"
|
|
|
|
|
#include "wpi/util/mutex.hpp"
|
2025-12-12 21:25:57 -07:00
|
|
|
#include "wpi/util/string.h"
|
2016-05-21 01:42:16 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
static wpi::util::mutex msgMutex;
|
2019-07-23 23:55:51 -07:00
|
|
|
static std::atomic<HALSIM_SendErrorHandler> sendErrorHandler{nullptr};
|
2020-02-18 20:44:40 -08:00
|
|
|
static std::atomic<HALSIM_SendConsoleLineHandler> sendConsoleLineHandler{
|
|
|
|
|
nullptr};
|
2016-07-17 19:42:33 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
using namespace wpi::hal;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
|
|
|
|
|
static constexpr int kJoystickPorts = 6;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
struct JoystickDataCache {
|
|
|
|
|
JoystickDataCache() { std::memset(this, 0, sizeof(*this)); }
|
|
|
|
|
void Update();
|
|
|
|
|
|
|
|
|
|
HAL_JoystickAxes axes[kJoystickPorts];
|
|
|
|
|
HAL_JoystickPOVs povs[kJoystickPorts];
|
|
|
|
|
HAL_JoystickButtons buttons[kJoystickPorts];
|
2025-11-21 13:57:11 -08:00
|
|
|
HAL_JoystickTouchpads touchpads[kJoystickPorts];
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
HAL_AllianceStationID allianceStation;
|
|
|
|
|
double matchTime;
|
2024-02-18 14:30:40 -08:00
|
|
|
HAL_ControlWord controlWord;
|
2026-02-06 21:38:15 -08:00
|
|
|
HAL_GameData gameData;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
};
|
|
|
|
|
static_assert(std::is_standard_layout_v<JoystickDataCache>);
|
|
|
|
|
// static_assert(std::is_trivial_v<JoystickDataCache>);
|
|
|
|
|
|
|
|
|
|
static std::atomic_bool gShutdown{false};
|
|
|
|
|
|
2026-03-13 23:05:55 -07:00
|
|
|
struct FIRSTDriverStation {
|
|
|
|
|
~FIRSTDriverStation() { gShutdown = true; }
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::util::EventVector newDataEvents;
|
|
|
|
|
wpi::util::mutex cacheMutex;
|
|
|
|
|
wpi::util::mutex tcpCacheMutex;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void JoystickDataCache::Update() {
|
|
|
|
|
for (int i = 0; i < kJoystickPorts; i++) {
|
|
|
|
|
SimDriverStationData->GetJoystickAxes(i, &axes[i]);
|
|
|
|
|
SimDriverStationData->GetJoystickPOVs(i, &povs[i]);
|
|
|
|
|
SimDriverStationData->GetJoystickButtons(i, &buttons[i]);
|
2025-11-21 13:57:11 -08:00
|
|
|
SimDriverStationData->GetJoystickTouchpads(i, &touchpads[i]);
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
}
|
|
|
|
|
allianceStation = SimDriverStationData->allianceStationId;
|
|
|
|
|
matchTime = SimDriverStationData->matchTime;
|
2024-02-18 14:30:40 -08:00
|
|
|
|
2025-12-12 21:25:57 -07:00
|
|
|
controlWord = HAL_MakeControlWord(
|
|
|
|
|
SimDriverStationData->opMode, SimDriverStationData->robotMode,
|
|
|
|
|
SimDriverStationData->enabled, SimDriverStationData->eStop,
|
|
|
|
|
SimDriverStationData->fmsAttached, SimDriverStationData->dsAttached);
|
2026-02-06 21:38:15 -08:00
|
|
|
SimDriverStationData->GetGameData(&gameData);
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CHECK_JOYSTICK_NUMBER(stickNum) \
|
2026-03-13 01:04:29 -07:00
|
|
|
if ((stickNum) < 0 || (stickNum) >= HAL_MAX_JOYSTICKS) \
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_PARAMETER_OUT_OF_RANGE;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
|
|
|
|
|
static HAL_ControlWord newestControlWord;
|
|
|
|
|
static JoystickDataCache caches[3];
|
|
|
|
|
static JoystickDataCache* currentRead = &caches[0];
|
2022-12-15 18:27:52 -08:00
|
|
|
static JoystickDataCache* currentReadLocal = &caches[0];
|
2023-01-15 16:36:44 -08:00
|
|
|
static std::atomic<JoystickDataCache*> currentCache{nullptr};
|
2022-12-15 18:27:52 -08:00
|
|
|
static JoystickDataCache* lastGiven = &caches[1];
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
static JoystickDataCache* cacheToUpdate = &caches[2];
|
|
|
|
|
|
2024-11-19 04:56:32 +00:00
|
|
|
namespace {
|
|
|
|
|
struct TcpCache {
|
|
|
|
|
TcpCache() { std::memset(this, 0, sizeof(*this)); }
|
|
|
|
|
void Update();
|
|
|
|
|
void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
|
|
|
|
|
|
|
|
|
|
HAL_MatchInfo matchInfo;
|
2026-03-13 01:04:29 -07:00
|
|
|
HAL_JoystickDescriptor descriptors[HAL_MAX_JOYSTICKS];
|
2024-11-19 04:56:32 +00:00
|
|
|
};
|
|
|
|
|
static_assert(std::is_standard_layout_v<TcpCache>);
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
static TcpCache tcpCache;
|
|
|
|
|
static TcpCache tcpCurrent;
|
|
|
|
|
|
|
|
|
|
void TcpCache::Update() {
|
|
|
|
|
SimDriverStationData->GetMatchInfo(&matchInfo);
|
|
|
|
|
|
2026-03-13 01:04:29 -07:00
|
|
|
for (int i = 0; i < HAL_MAX_JOYSTICKS; i++) {
|
2024-11-19 04:56:32 +00:00
|
|
|
SimDriverStationData->GetJoystickDescriptor(i, &descriptors[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 23:05:55 -07:00
|
|
|
static ::FIRSTDriverStation* driverStation;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal::init {
|
2017-12-10 19:38:53 -08:00
|
|
|
void InitializeDriverStation() {
|
2026-03-13 23:05:55 -07:00
|
|
|
static FIRSTDriverStation ds;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
driverStation = &ds;
|
2017-12-10 19:38:53 -08:00
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal::init
|
2017-12-10 19:38:53 -08:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal {
|
2022-11-30 02:19:15 -05:00
|
|
|
static void DefaultPrintErrorImpl(const char* line, size_t size) {
|
|
|
|
|
std::fwrite(line, size, 1, stderr);
|
|
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal
|
2022-11-30 02:19:15 -05:00
|
|
|
|
|
|
|
|
static std::atomic<void (*)(const char* line, size_t size)> gPrintErrorImpl{
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::hal::DefaultPrintErrorImpl};
|
2022-11-30 02:19:15 -05:00
|
|
|
|
2015-11-26 00:08:32 -08:00
|
|
|
extern "C" {
|
2019-07-23 23:55:51 -07:00
|
|
|
|
|
|
|
|
void HALSIM_SetSendError(HALSIM_SendErrorHandler handler) {
|
|
|
|
|
sendErrorHandler.store(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 20:44:40 -08:00
|
|
|
void HALSIM_SetSendConsoleLine(HALSIM_SendConsoleLineHandler handler) {
|
|
|
|
|
sendConsoleLineHandler.store(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-17 19:42:33 -07:00
|
|
|
int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
|
|
|
|
|
const char* details, const char* location,
|
|
|
|
|
const char* callStack, HAL_Bool printMsg) {
|
2019-07-23 23:55:51 -07:00
|
|
|
auto errorHandler = sendErrorHandler.load();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (errorHandler) {
|
2019-07-23 23:55:51 -07:00
|
|
|
return errorHandler(isError, errorCode, isLVCode, details, location,
|
|
|
|
|
callStack, printMsg);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2016-07-17 19:42:33 -07:00
|
|
|
// Avoid flooding console by keeping track of previous 5 error
|
|
|
|
|
// messages and only printing again if they're longer than 1 second old.
|
|
|
|
|
static constexpr int KEEP_MSGS = 5;
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(msgMutex);
|
2016-08-12 13:45:28 -07:00
|
|
|
static std::string prevMsg[KEEP_MSGS];
|
2026-03-15 15:08:41 -07:00
|
|
|
static monotonic_clock::time_point prevMsgTime[KEEP_MSGS];
|
2016-12-02 00:32:01 -08:00
|
|
|
static bool initialized = false;
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
for (int i = 0; i < KEEP_MSGS; i++) {
|
2026-03-15 15:08:41 -07:00
|
|
|
prevMsgTime[i] = monotonic_clock::now() - std::chrono::seconds(2);
|
2016-12-02 00:32:01 -08:00
|
|
|
}
|
|
|
|
|
initialized = true;
|
|
|
|
|
}
|
2016-07-17 19:42:33 -07:00
|
|
|
|
2026-03-15 15:08:41 -07:00
|
|
|
auto curTime = monotonic_clock::now();
|
2016-07-17 19:42:33 -07:00
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < KEEP_MSGS; ++i) {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (prevMsg[i] == details) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
|
|
|
|
int retval = 0;
|
2016-12-02 00:32:01 -08:00
|
|
|
if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
|
2017-08-18 21:35:53 -07:00
|
|
|
printMsg = true;
|
2016-07-17 19:42:33 -07:00
|
|
|
if (printMsg) {
|
2021-06-06 16:13:58 -07:00
|
|
|
fmt::memory_buffer buf;
|
2016-07-17 19:42:33 -07:00
|
|
|
if (location && location[0] != '\0') {
|
2021-06-23 21:57:32 -07:00
|
|
|
fmt::format_to(fmt::appender{buf},
|
|
|
|
|
"{} at {}: ", isError ? "Error" : "Warning", location);
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
2021-06-23 21:57:32 -07:00
|
|
|
fmt::format_to(fmt::appender{buf}, "{}\n", details);
|
2016-07-17 19:42:33 -07:00
|
|
|
if (callStack && callStack[0] != '\0') {
|
2021-06-23 21:57:32 -07:00
|
|
|
fmt::format_to(fmt::appender{buf}, "{}\n", callStack);
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
2022-11-30 02:19:15 -05:00
|
|
|
auto printError = gPrintErrorImpl.load();
|
|
|
|
|
printError(buf.data(), buf.size());
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
|
|
|
|
if (i == KEEP_MSGS) {
|
|
|
|
|
// replace the oldest one
|
|
|
|
|
i = 0;
|
2016-12-02 00:32:01 -08:00
|
|
|
auto first = prevMsgTime[0];
|
2016-07-17 19:42:33 -07:00
|
|
|
for (int j = 1; j < KEEP_MSGS; ++j) {
|
2016-08-12 13:45:28 -07:00
|
|
|
if (prevMsgTime[j] < first) {
|
|
|
|
|
first = prevMsgTime[j];
|
2016-07-17 19:42:33 -07:00
|
|
|
i = j;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-12 13:45:28 -07:00
|
|
|
prevMsg[i] = details;
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
2016-08-12 13:45:28 -07:00
|
|
|
prevMsgTime[i] = curTime;
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
2015-11-26 00:08:32 -08:00
|
|
|
|
2022-11-30 02:19:15 -05:00
|
|
|
void HAL_SetPrintErrorImpl(void (*func)(const char* line, size_t size)) {
|
2025-11-07 20:00:05 -05:00
|
|
|
gPrintErrorImpl.store(func ? func : wpi::hal::DefaultPrintErrorImpl);
|
2022-11-30 02:19:15 -05:00
|
|
|
}
|
|
|
|
|
|
2020-02-18 20:44:40 -08:00
|
|
|
int32_t HAL_SendConsoleLine(const char* line) {
|
|
|
|
|
auto handler = sendConsoleLineHandler.load();
|
|
|
|
|
if (handler) {
|
|
|
|
|
return handler(line);
|
|
|
|
|
}
|
2021-06-06 16:13:58 -07:00
|
|
|
std::puts(line);
|
|
|
|
|
std::fflush(stdout);
|
2020-02-18 20:44:40 -08:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2025-12-12 21:25:57 -07:00
|
|
|
controlWord->value = 0;
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*controlWord = newestControlWord;
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
|
|
|
|
|
2025-12-12 21:25:57 -07:00
|
|
|
int32_t HAL_GetUncachedControlWord(HAL_ControlWord* controlWord) {
|
|
|
|
|
if (gShutdown) {
|
|
|
|
|
controlWord->value = 0;
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2025-12-12 21:25:57 -07:00
|
|
|
}
|
|
|
|
|
bool dsAttached = SimDriverStationData->dsAttached;
|
|
|
|
|
if (dsAttached) {
|
|
|
|
|
*controlWord = HAL_MakeControlWord(
|
|
|
|
|
SimDriverStationData->opMode, SimDriverStationData->robotMode,
|
|
|
|
|
SimDriverStationData->enabled, SimDriverStationData->eStop,
|
|
|
|
|
SimDriverStationData->fmsAttached, SimDriverStationData->dsAttached);
|
|
|
|
|
} else {
|
|
|
|
|
controlWord->value = 0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HAL_SetOpModeOptions(const struct HAL_OpModeOption* options,
|
|
|
|
|
int32_t count) {
|
|
|
|
|
if (gShutdown) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (count < 0 || count > 1000 || (count != 0 && !options)) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_PARAMETER_OUT_OF_RANGE;
|
2025-12-12 21:25:57 -07:00
|
|
|
}
|
|
|
|
|
SimDriverStationData->SetOpModeOptions({options, options + count});
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-10 16:24:57 -07:00
|
|
|
HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2026-03-13 01:04:29 -07:00
|
|
|
return HAL_ALLIANCE_STATION_UNKNOWN;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
return currentRead->allianceStation;
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
CHECK_JOYSTICK_NUMBER(joystickNum);
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*axes = currentRead->axes[joystickNum];
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
CHECK_JOYSTICK_NUMBER(joystickNum);
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*povs = currentRead->povs[joystickNum];
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int32_t HAL_GetJoystickButtons(int32_t joystickNum,
|
|
|
|
|
HAL_JoystickButtons* buttons) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
CHECK_JOYSTICK_NUMBER(joystickNum);
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*buttons = currentRead->buttons[joystickNum];
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
2018-05-31 20:47:15 -07:00
|
|
|
|
2025-11-21 13:57:11 -08:00
|
|
|
int32_t HAL_GetJoystickTouchpads(int32_t joystickNum,
|
|
|
|
|
HAL_JoystickTouchpads* touchpads) {
|
|
|
|
|
if (gShutdown) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2025-11-21 13:57:11 -08:00
|
|
|
}
|
|
|
|
|
CHECK_JOYSTICK_NUMBER(joystickNum);
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*touchpads = currentRead->touchpads[joystickNum];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 23:03:50 -07:00
|
|
|
void HAL_GetAllJoystickData(int32_t joystickNum, HAL_JoystickAxes* axes,
|
|
|
|
|
HAL_JoystickPOVs* povs,
|
2025-11-21 13:57:11 -08:00
|
|
|
HAL_JoystickButtons* buttons,
|
|
|
|
|
HAL_JoystickTouchpads* touchpads) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
2025-10-25 23:03:50 -07:00
|
|
|
*axes = currentRead->axes[joystickNum];
|
|
|
|
|
*povs = currentRead->povs[joystickNum];
|
|
|
|
|
*buttons = currentRead->buttons[joystickNum];
|
2025-11-21 13:57:11 -08:00
|
|
|
*touchpads = currentRead->touchpads[joystickNum];
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
|
|
|
|
|
HAL_JoystickDescriptor* desc) {
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
CHECK_JOYSTICK_NUMBER(joystickNum);
|
2024-11-19 04:56:32 +00:00
|
|
|
std::scoped_lock lock{driverStation->tcpCacheMutex};
|
|
|
|
|
*desc = tcpCurrent.descriptors[joystickNum];
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2016-05-20 17:30:37 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-16 22:15:14 -07:00
|
|
|
HAL_Bool HAL_GetJoystickIsGamepad(int32_t joystickNum) {
|
2024-11-19 04:56:32 +00:00
|
|
|
HAL_JoystickDescriptor joystickDesc;
|
|
|
|
|
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
2025-05-16 22:15:14 -07:00
|
|
|
return joystickDesc.isGamepad;
|
2024-11-19 04:56:32 +00:00
|
|
|
}
|
2017-11-12 19:33:43 -08:00
|
|
|
}
|
2016-05-20 17:30:37 -07:00
|
|
|
|
2025-11-17 14:36:14 -08:00
|
|
|
int32_t HAL_GetJoystickGamepadType(int32_t joystickNum) {
|
2024-11-19 04:56:32 +00:00
|
|
|
HAL_JoystickDescriptor joystickDesc;
|
|
|
|
|
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
2025-11-17 14:36:14 -08:00
|
|
|
return joystickDesc.gamepadType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-06 21:38:15 -08:00
|
|
|
int32_t HAL_GetGameData(HAL_GameData* gameData) {
|
|
|
|
|
if (gShutdown) {
|
2026-04-19 16:12:18 -07:00
|
|
|
return HAL_INCOMPATIBLE_STATE;
|
2026-02-06 21:38:15 -08:00
|
|
|
}
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
*gameData = currentRead->gameData;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-17 14:36:14 -08:00
|
|
|
int32_t HAL_GetJoystickSupportedOutputs(int32_t joystickNum) {
|
|
|
|
|
HAL_JoystickDescriptor joystickDesc;
|
|
|
|
|
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
return joystickDesc.supportedOutputs;
|
2024-11-19 04:56:32 +00:00
|
|
|
}
|
2017-11-12 19:33:43 -08:00
|
|
|
}
|
2016-05-20 17:30:37 -07:00
|
|
|
|
Change C APIs to a unified string implementation (#6299)
Currently in the entire C API of WPILib we have ~8 different ways of handling strings. The C API actually isn't built for pure C callers (We don't actually have any of those). Instead, they're built for interop between languages like LabVIEW and C# which can talk to C API's directly.
For output parameters, the choice was fairly obvious. An output struct containing a const string pointer and a length makes the most sense. Its easy to use these from most other languages, and doesn't require special null termination handling. Freeing these is also easy, as if you ever receive one of these string structures, theres just a single function call to free it.
Input parameters are a bit more complex. To be used from pure C, and from LabVIEW, a null terminated string is the best in most cases. However, null terminated strings in general have a lot of downsides. Additionally, from LabVIEW there are other considerations around encoding that having a wrapper struct helps make a bit easier. From a language like C#, a wrapper struct is by far the easiest, as custom marshalling can make it trivial to marshal both UTF8 and UTF16 strings down.
The final consideration is its nice to have an identical concept for both input and output. It makes the rules fairly easy to understand.
WPILib will not have any APIs that manipulate a string allocated externally. This means WPI_String can be const, as across the boundary it is always const.
If a WPILib API takes a const WPI_String*, WPILib will not manipulate or attempt to free that string, and that string is treated as an input. It is up to the caller to handle that memory, WPILib will never hold onto that memory longer than the call.
If a WPILib API takes a WPI_String*, that string is an output. WPILib will allocate that API with WPI_AllocateString(), fill in the string, and return to the caller. When the caller is done with the string, they must free it with WPI_FreeString().
If an output struct contains a WPI_String member, that member is considered read only, and should not be explicitly freed. The caller should call the free function for that struct.
If an array of WPI_Strings are returned, each individual string is considered read only, and should not be explicitly freed. The free function for that array should be called by the caller.
If an input struct containing a WPI_String, or an input array of WPI_Strings is passed to WPILib, the individual strings will not be manipulated or freed by WPILib, and the caller owns and should free that memory.
Callbacks also follow these rules. The most common is a callback either getting passed a const WPI_String* or a struct containing a WPI_String. In both of these cases, the callback target should consider these strings read only, and not attempt to free them or manipulate them.
2024-05-13 05:35:14 -07:00
|
|
|
void HAL_GetJoystickName(struct WPI_String* name, int32_t joystickNum) {
|
2024-11-19 04:56:32 +00:00
|
|
|
HAL_JoystickDescriptor joystickDesc;
|
|
|
|
|
const char* cName = joystickDesc.name;
|
|
|
|
|
if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
|
|
|
|
|
cName = "";
|
|
|
|
|
}
|
|
|
|
|
auto len = std::strlen(cName);
|
Change C APIs to a unified string implementation (#6299)
Currently in the entire C API of WPILib we have ~8 different ways of handling strings. The C API actually isn't built for pure C callers (We don't actually have any of those). Instead, they're built for interop between languages like LabVIEW and C# which can talk to C API's directly.
For output parameters, the choice was fairly obvious. An output struct containing a const string pointer and a length makes the most sense. Its easy to use these from most other languages, and doesn't require special null termination handling. Freeing these is also easy, as if you ever receive one of these string structures, theres just a single function call to free it.
Input parameters are a bit more complex. To be used from pure C, and from LabVIEW, a null terminated string is the best in most cases. However, null terminated strings in general have a lot of downsides. Additionally, from LabVIEW there are other considerations around encoding that having a wrapper struct helps make a bit easier. From a language like C#, a wrapper struct is by far the easiest, as custom marshalling can make it trivial to marshal both UTF8 and UTF16 strings down.
The final consideration is its nice to have an identical concept for both input and output. It makes the rules fairly easy to understand.
WPILib will not have any APIs that manipulate a string allocated externally. This means WPI_String can be const, as across the boundary it is always const.
If a WPILib API takes a const WPI_String*, WPILib will not manipulate or attempt to free that string, and that string is treated as an input. It is up to the caller to handle that memory, WPILib will never hold onto that memory longer than the call.
If a WPILib API takes a WPI_String*, that string is an output. WPILib will allocate that API with WPI_AllocateString(), fill in the string, and return to the caller. When the caller is done with the string, they must free it with WPI_FreeString().
If an output struct contains a WPI_String member, that member is considered read only, and should not be explicitly freed. The caller should call the free function for that struct.
If an array of WPI_Strings are returned, each individual string is considered read only, and should not be explicitly freed. The free function for that array should be called by the caller.
If an input struct containing a WPI_String, or an input array of WPI_Strings is passed to WPILib, the individual strings will not be manipulated or freed by WPILib, and the caller owns and should free that memory.
Callbacks also follow these rules. The most common is a callback either getting passed a const WPI_String* or a struct containing a WPI_String. In both of these cases, the callback target should consider these strings read only, and not attempt to free them or manipulate them.
2024-05-13 05:35:14 -07:00
|
|
|
auto write = WPI_AllocateString(name, len);
|
2024-11-19 04:56:32 +00:00
|
|
|
std::memcpy(write, cName, len);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2017-06-30 16:11:16 -07:00
|
|
|
|
2025-11-17 14:36:14 -08:00
|
|
|
int32_t HAL_SetJoystickRumble(int32_t joystickNum, int32_t leftRumble,
|
|
|
|
|
int32_t rightRumble, int32_t leftTriggerRumble,
|
|
|
|
|
int32_t rightTriggerRumble) {
|
|
|
|
|
SimDriverStationData->SetJoystickRumbles(joystickNum, leftRumble, rightRumble,
|
|
|
|
|
leftTriggerRumble,
|
|
|
|
|
rightTriggerRumble);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HAL_SetJoystickLeds(int32_t joystickNum, int32_t leds) {
|
|
|
|
|
SimDriverStationData->SetJoystickLeds(joystickNum, leds);
|
2017-08-18 21:35:53 -07:00
|
|
|
return 0;
|
2016-05-20 17:30:37 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 19:25:57 -07:00
|
|
|
double HAL_GetMatchTime(int32_t* status) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
return currentRead->matchTime;
|
2016-05-20 17:30:37 -07:00
|
|
|
}
|
|
|
|
|
|
2018-07-04 00:18:18 -07:00
|
|
|
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
|
2024-11-19 04:56:32 +00:00
|
|
|
std::scoped_lock lock{driverStation->tcpCacheMutex};
|
|
|
|
|
*info = tcpCurrent.matchInfo;
|
2017-11-01 21:58:44 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void HAL_ObserveUserProgramStarting(void) {
|
2025-11-29 10:10:01 -08:00
|
|
|
HALSIM_SetProgramStarted(true);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2016-05-20 17:30:37 -07:00
|
|
|
|
2025-12-12 21:25:57 -07:00
|
|
|
void HAL_ObserveUserProgram(HAL_ControlWord word) {
|
|
|
|
|
HALSIM_SetProgramState(word);
|
2015-08-19 11:12:54 -04:00
|
|
|
}
|
2015-11-26 00:08:32 -08:00
|
|
|
|
2023-01-15 16:36:44 -08:00
|
|
|
HAL_Bool HAL_RefreshDSData(void) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
2023-01-15 16:36:44 -08:00
|
|
|
return false;
|
2022-10-23 21:59:04 -07:00
|
|
|
}
|
2024-02-18 14:30:40 -08:00
|
|
|
bool dsAttached = SimDriverStationData->dsAttached;
|
2024-11-19 04:56:32 +00:00
|
|
|
JoystickDataCache* prev;
|
|
|
|
|
{
|
|
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
|
|
|
|
prev = currentCache.exchange(nullptr);
|
|
|
|
|
if (prev != nullptr) {
|
|
|
|
|
currentRead = prev;
|
|
|
|
|
}
|
|
|
|
|
// If newest state shows we have a DS attached, just use the
|
|
|
|
|
// control word out of the cache, As it will be the one in sync
|
|
|
|
|
// with the data. If no data has been updated, at this point,
|
|
|
|
|
// and a DS wasn't attached previously, this will still return
|
|
|
|
|
// a zeroed out control word, with is the correct state for
|
|
|
|
|
// no new data.
|
|
|
|
|
if (!dsAttached) {
|
|
|
|
|
// If the DS is not attached, we need to zero out the control word.
|
|
|
|
|
// This is because HAL_RefreshDSData is called asynchronously from
|
|
|
|
|
// the DS data. The dsAttached variable comes directly from netcomm
|
|
|
|
|
// and could be updated before the caches are. If that happens,
|
|
|
|
|
// we would end up returning the previous cached control word,
|
|
|
|
|
// which is out of sync with the current control word and could
|
|
|
|
|
// break invariants such as which alliance station is in used.
|
|
|
|
|
// Also, when the DS has never been connected the rest of the fields
|
|
|
|
|
// in control word are garbage, so we also need to zero out in that
|
|
|
|
|
// case too
|
2025-12-12 21:25:57 -07:00
|
|
|
currentRead->controlWord.value = 0;
|
2024-11-19 04:56:32 +00:00
|
|
|
}
|
|
|
|
|
newestControlWord = currentRead->controlWord;
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2024-11-19 04:56:32 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
tcpCache.Update();
|
|
|
|
|
std::scoped_lock tcpLock(driverStation->tcpCacheMutex);
|
|
|
|
|
tcpCache.CloneTo(&tcpCurrent);
|
2024-02-18 14:30:40 -08:00
|
|
|
}
|
2024-11-19 04:56:32 +00:00
|
|
|
|
2023-01-15 16:36:44 -08:00
|
|
|
return prev != nullptr;
|
2017-05-08 20:21:47 -07:00
|
|
|
}
|
|
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
|
|
|
|
|
if (gShutdown) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::hal::init::CheckInit();
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
driverStation->newDataEvents.Add(handle);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2017-05-08 20:21:47 -07:00
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle) {
|
|
|
|
|
if (gShutdown) {
|
|
|
|
|
return;
|
2020-07-13 21:57:54 -07:00
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
driverStation->newDataEvents.Remove(handle);
|
|
|
|
|
}
|
2020-07-13 21:57:54 -07:00
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
HAL_Bool HAL_GetOutputsEnabled(void) {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
std::scoped_lock lock{driverStation->cacheMutex};
|
2025-12-12 21:25:57 -07:00
|
|
|
return HAL_ControlWord_IsEnabled(newestControlWord) &&
|
|
|
|
|
HAL_ControlWord_IsDSAttached(newestControlWord);
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
|
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
} // extern "C"
|
2017-05-08 20:21:47 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal {
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
void NewDriverStationData() {
|
2022-10-23 21:59:04 -07:00
|
|
|
if (gShutdown) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-02-18 14:30:40 -08:00
|
|
|
SimDriverStationData->dsAttached = true;
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
cacheToUpdate->Update();
|
2022-12-15 18:27:52 -08:00
|
|
|
|
|
|
|
|
JoystickDataCache* given = cacheToUpdate;
|
|
|
|
|
JoystickDataCache* prev = currentCache.exchange(cacheToUpdate);
|
|
|
|
|
if (prev == nullptr) {
|
|
|
|
|
cacheToUpdate = currentReadLocal;
|
|
|
|
|
currentReadLocal = lastGiven;
|
|
|
|
|
} else {
|
|
|
|
|
// Current read local does not update
|
|
|
|
|
cacheToUpdate = prev;
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2022-12-15 18:27:52 -08:00
|
|
|
lastGiven = given;
|
|
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
driverStation->newDataEvents.Wakeup();
|
2020-08-14 22:14:07 -07:00
|
|
|
SimDriverStationData->CallNewDataCallbacks();
|
2017-05-08 20:21:47 -07:00
|
|
|
}
|
|
|
|
|
|
[hal, wpilib] New DS thread model and implementation (#3787)
The current DS thread model has some pretty major issues. It makes it difficult to know if all data is from the same remote packet, and if the data changes while the robot loop is running. Additionally, the DS thread is used for a few other things (MotorSafety and State Tracking for EducationalRobot). This also makes sim difficult, as user code has to wait for the thread to know it has new data.
This change completely rethinks how threading works in the driver station model.
First, the DS HAL system receives a new data callback, either from Netcomm or DriverStationSim. Inside the context of this callback, all the low latency data is read and put into a cache. Doing some investigation on the robot side, this is perfectly safe to do, and also ensures a ds packet will not be parsed before we finish reading the current packet data.
After all data is read, the cache is swapped with a 2nd buffer. This buffer just stores the data, none of the HAL DS calls read from this buffer. An event is then fired, stating there is new data ready to go.
Robot code calls HAL_UpdateDSData(). This swaps the 2nd buffer with a 3rd buffer, which always contains the current data. This data will not be updated until HAL_UpdateDSData is called again. Which solves the state problem.
The high level driver station classes have. an updateData() call, which calls HAL_UpdateDSData, and then update button state variables, then data log and update the NT FMS data table (Java also caches across the JNI boundary here, but that could trivially be removed). An extra event provider is provided, allowing other threads to know when this call has been completed.
IterativeRobotBase calls DS.updateData() at the beginning of each loop, and only once per loop. This means all commands will always have the same state.
All of this means there is no longer a DS thread. Everything happens synchronously. This means Sim and testing is easier, as you can just call DriverStationSim.NotifyNewData(), and then DriverStation.UpdateData(), and you can guarantee that all the DriverStation.*** data is up to date.
As for Motor Safety and Educational Robot State Handling, those can all be handled by their own threads. The Educational Thread only needs to run under EducationalRobot, and MotorSafety will only be started if there is a motor safety object enabled.
2022-10-21 22:01:55 -07:00
|
|
|
void InitializeDriverStation() {
|
2017-12-10 19:38:53 -08:00
|
|
|
SimDriverStationData->ResetData();
|
2016-07-17 19:42:33 -07:00
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal
|