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-01-02 03:02:34 -08:00
|
|
|
|
2026-03-13 17:19:39 -07:00
|
|
|
#include "wpi/hal/HAL.h"
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <cstdio>
|
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
|
|
|
#include <cstring>
|
2024-09-20 17:43:39 -07:00
|
|
|
#include <utility>
|
2020-09-04 08:59:26 -07:00
|
|
|
#include <vector>
|
|
|
|
|
|
2020-06-28 20:45:40 -04:00
|
|
|
#ifdef _WIN32
|
2025-11-11 18:05:12 -08:00
|
|
|
#include <windows.h>
|
2024-08-02 21:56:58 -04:00
|
|
|
#pragma comment(lib, "Winmm.lib")
|
2024-08-01 19:51:02 -07:00
|
|
|
#pragma comment(lib, "ntdll.lib")
|
|
|
|
|
extern "C" NTSYSAPI NTSTATUS NTAPI NtSetTimerResolution(
|
|
|
|
|
ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
|
|
|
|
extern "C" NTSYSAPI NTSTATUS NTAPI
|
|
|
|
|
NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution,
|
|
|
|
|
PULONG CurrentResolution);
|
2020-06-28 20:45:40 -04:00
|
|
|
#endif // _WIN32
|
|
|
|
|
|
2026-01-04 00:41:53 -08:00
|
|
|
#include "HALInitializer.hpp"
|
|
|
|
|
#include "MockHooksInternal.hpp"
|
|
|
|
|
#include "mockdata/RoboRioDataInternal.hpp"
|
2026-03-13 15:53:24 -07:00
|
|
|
#include "wpi/hal/CAN.h"
|
2025-11-07 19:56:21 -05:00
|
|
|
#include "wpi/hal/Errors.h"
|
|
|
|
|
#include "wpi/hal/Extensions.h"
|
|
|
|
|
#include "wpi/hal/simulation/DriverStationData.h"
|
2026-01-04 00:41:53 -08:00
|
|
|
#include "wpi/hal/simulation/MockHooks.h"
|
|
|
|
|
#include "wpi/hal/simulation/SimCallbackRegistry.hpp"
|
2025-11-07 19:57:55 -05:00
|
|
|
#include "wpi/util/mutex.hpp"
|
|
|
|
|
#include "wpi/util/spinlock.hpp"
|
2015-12-30 19:06:47 -08:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
using namespace wpi::hal;
|
2016-06-05 15:23:58 -07:00
|
|
|
|
2020-11-30 23:55:36 -08:00
|
|
|
namespace {
|
|
|
|
|
class SimPeriodicCallbackRegistry : public impl::SimCallbackRegistryBase {
|
|
|
|
|
public:
|
|
|
|
|
int32_t Register(HALSIM_SimPeriodicCallback callback, void* param) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
return DoRegister(reinterpret_cast<RawFunctor>(callback), param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operator()() const {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
if (m_callbacks) {
|
2020-12-28 12:58:06 -08:00
|
|
|
for (auto&& cb : *m_callbacks) {
|
2020-11-30 23:55:36 -08:00
|
|
|
reinterpret_cast<HALSIM_SimPeriodicCallback>(cb.callback)(cb.param);
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2020-11-30 23:55:36 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2026-03-14 10:33:36 -07:00
|
|
|
static HAL_RuntimeType runtimeType{HAL_RUNTIME_SIMULATION};
|
2025-11-07 20:00:05 -05:00
|
|
|
static wpi::util::spinlock gOnShutdownMutex;
|
2020-09-04 08:59:26 -07:00
|
|
|
static std::vector<std::pair<void*, void (*)(void*)>> gOnShutdown;
|
2020-11-30 23:55:36 -08:00
|
|
|
static SimPeriodicCallbackRegistry gSimPeriodicBefore;
|
|
|
|
|
static SimPeriodicCallbackRegistry gSimPeriodicAfter;
|
2020-06-26 20:46:13 -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 InitializeDriverStation();
|
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
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
namespace wpi::hal::init {
|
2017-12-10 19:38:53 -08:00
|
|
|
void InitializeHAL() {
|
2019-11-17 15:05:56 -08:00
|
|
|
InitializeAddressableLEDData();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeAnalogInData();
|
|
|
|
|
InitializeCanData();
|
2018-05-21 16:09:38 -07:00
|
|
|
InitializeCANAPI();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeDigitalPWMData();
|
2019-11-01 23:41:30 -07:00
|
|
|
InitializeDutyCycleData();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeDIOData();
|
|
|
|
|
InitializeDriverStationData();
|
|
|
|
|
InitializeEncoderData();
|
|
|
|
|
InitializeI2CData();
|
2021-06-05 22:36:39 -07:00
|
|
|
InitializeCTREPCMData();
|
2021-10-10 20:04:50 -07:00
|
|
|
InitializeREVPHData();
|
2021-08-04 20:31:17 -07:00
|
|
|
InitializePowerDistributionData();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializePWMData();
|
|
|
|
|
InitializeRoboRioData();
|
2019-09-28 11:34:46 -07:00
|
|
|
InitializeSimDeviceData();
|
2019-11-17 15:05:56 -08:00
|
|
|
InitializeAddressableLED();
|
2026-03-03 21:58:47 -07:00
|
|
|
InitializeAlert();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeAnalogInput();
|
|
|
|
|
InitializeAnalogInternal();
|
|
|
|
|
InitializeCAN();
|
|
|
|
|
InitializeCounter();
|
|
|
|
|
InitializeDigitalInternal();
|
|
|
|
|
InitializeDIO();
|
2019-11-01 23:41:30 -07:00
|
|
|
InitializeDutyCycle();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeDriverStation();
|
2017-12-31 15:37:14 -05:00
|
|
|
InitializeEncoder();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeExtensions();
|
|
|
|
|
InitializeI2C();
|
2019-09-28 15:43:24 -07:00
|
|
|
InitializeMain();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeMockHooks();
|
|
|
|
|
InitializeNotifier();
|
2021-08-04 20:31:17 -07:00
|
|
|
InitializePowerDistribution();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializePorts();
|
|
|
|
|
InitializePower();
|
2021-06-05 22:36:39 -07:00
|
|
|
InitializeCTREPCM();
|
2021-10-10 20:04:50 -07:00
|
|
|
InitializeREVPH();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializePWM();
|
|
|
|
|
InitializeSerialPort();
|
2019-09-28 11:34:46 -07:00
|
|
|
InitializeSimDevice();
|
2017-12-10 19:38:53 -08:00
|
|
|
InitializeThreads();
|
|
|
|
|
}
|
2025-11-07 20:00:05 -05:00
|
|
|
} // namespace wpi::hal::init
|
2017-12-10 19:38:53 -08:00
|
|
|
|
2015-11-26 00:08:32 -08:00
|
|
|
extern "C" {
|
|
|
|
|
|
2016-07-09 00:24:26 -07:00
|
|
|
const char* HAL_GetErrorMessage(int32_t code) {
|
2016-05-20 17:30:37 -07:00
|
|
|
switch (code) {
|
|
|
|
|
case 0:
|
|
|
|
|
return "";
|
|
|
|
|
case VOLTAGE_OUT_OF_RANGE:
|
|
|
|
|
return VOLTAGE_OUT_OF_RANGE_MESSAGE;
|
|
|
|
|
case INCOMPATIBLE_STATE:
|
|
|
|
|
return INCOMPATIBLE_STATE_MESSAGE;
|
|
|
|
|
case NO_AVAILABLE_RESOURCES:
|
|
|
|
|
return NO_AVAILABLE_RESOURCES_MESSAGE;
|
|
|
|
|
case RESOURCE_IS_ALLOCATED:
|
|
|
|
|
return RESOURCE_IS_ALLOCATED_MESSAGE;
|
2016-07-13 20:29:28 -07:00
|
|
|
case RESOURCE_OUT_OF_RANGE:
|
|
|
|
|
return RESOURCE_OUT_OF_RANGE_MESSAGE;
|
2016-07-03 17:27:06 -07:00
|
|
|
case HAL_HANDLE_ERROR:
|
|
|
|
|
return HAL_HANDLE_ERROR_MESSAGE;
|
2016-05-20 17:30:37 -07:00
|
|
|
case NULL_PARAMETER:
|
|
|
|
|
return NULL_PARAMETER_MESSAGE;
|
|
|
|
|
case PARAMETER_OUT_OF_RANGE:
|
|
|
|
|
return PARAMETER_OUT_OF_RANGE_MESSAGE;
|
2016-07-03 15:22:22 -07:00
|
|
|
case HAL_COUNTER_NOT_SUPPORTED:
|
|
|
|
|
return HAL_COUNTER_NOT_SUPPORTED_MESSAGE;
|
2017-09-07 21:40:30 -07:00
|
|
|
case HAL_ERR_CANSessionMux_InvalidBuffer:
|
2016-05-20 17:30:37 -07:00
|
|
|
return ERR_CANSessionMux_InvalidBuffer_MESSAGE;
|
2017-09-07 21:40:30 -07:00
|
|
|
case HAL_ERR_CANSessionMux_MessageNotFound:
|
2016-05-20 17:30:37 -07:00
|
|
|
return ERR_CANSessionMux_MessageNotFound_MESSAGE;
|
2017-09-07 21:40:30 -07:00
|
|
|
case HAL_WARN_CANSessionMux_NoToken:
|
2016-05-20 17:30:37 -07:00
|
|
|
return WARN_CANSessionMux_NoToken_MESSAGE;
|
2017-09-07 21:40:30 -07:00
|
|
|
case HAL_ERR_CANSessionMux_NotAllowed:
|
2016-05-20 17:30:37 -07:00
|
|
|
return ERR_CANSessionMux_NotAllowed_MESSAGE;
|
2017-09-07 21:40:30 -07:00
|
|
|
case HAL_ERR_CANSessionMux_NotInitialized:
|
2016-05-20 17:30:37 -07:00
|
|
|
return ERR_CANSessionMux_NotInitialized_MESSAGE;
|
2018-05-21 16:09:38 -07:00
|
|
|
case HAL_CAN_TIMEOUT:
|
|
|
|
|
return HAL_CAN_TIMEOUT_MESSAGE;
|
2019-11-01 23:41:30 -07:00
|
|
|
case HAL_SIM_NOT_SUPPORTED:
|
|
|
|
|
return HAL_SIM_NOT_SUPPORTED_MESSAGE;
|
2019-11-08 22:51:11 -08:00
|
|
|
case HAL_CAN_BUFFER_OVERRUN:
|
|
|
|
|
return HAL_CAN_BUFFER_OVERRUN_MESSAGE;
|
2021-04-29 09:56:54 -07:00
|
|
|
case HAL_USE_LAST_ERROR:
|
|
|
|
|
return HAL_USE_LAST_ERROR_MESSAGE;
|
2016-05-20 17:30:37 -07:00
|
|
|
default:
|
|
|
|
|
return "Unknown error status";
|
|
|
|
|
}
|
2013-12-15 18:30:16 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
HAL_RuntimeType HAL_GetRuntimeType(void) {
|
|
|
|
|
return runtimeType;
|
|
|
|
|
}
|
2020-06-26 20:46:13 -07:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void HALSIM_SetRuntimeType(HAL_RuntimeType type) {
|
|
|
|
|
runtimeType = type;
|
|
|
|
|
}
|
2016-09-13 21:21:57 -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_GetSerialNumber(struct WPI_String* serialNumber) {
|
|
|
|
|
HALSIM_GetRoboRioSerialNumber(serialNumber);
|
2022-12-09 00:58:55 -05: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_GetComments(struct WPI_String* comments) {
|
|
|
|
|
HALSIM_GetRoboRioComments(comments);
|
2022-12-26 14:39:51 -05:00
|
|
|
}
|
|
|
|
|
|
2023-09-02 02:34:18 -04:00
|
|
|
int32_t HAL_GetTeamNumber(void) {
|
|
|
|
|
return HALSIM_GetRoboRioTeamNumber();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-15 15:08:41 -07:00
|
|
|
uint64_t HAL_GetMonotonicTime(void) {
|
|
|
|
|
return wpi::hal::GetMonotonicTime();
|
2019-11-14 22:52:34 -08:00
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_Bool HAL_GetSystemActive(int32_t* status) {
|
2020-05-01 09:05:54 -07:00
|
|
|
return HALSIM_GetDriverStationEnabled();
|
2014-11-18 10:56:25 -05:00
|
|
|
}
|
2015-08-19 11:12:54 -04:00
|
|
|
|
2016-07-12 10:45:14 -07:00
|
|
|
HAL_Bool HAL_GetBrownedOut(int32_t* status) {
|
2017-08-18 21:35:53 -07:00
|
|
|
return false; // Figure out if we need to detect a brownout condition
|
2016-12-02 00:32:01 -08:00
|
|
|
}
|
|
|
|
|
|
2024-08-11 02:25:02 -04:00
|
|
|
int32_t HAL_GetCommsDisableCount(int32_t* status) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-13 00:30:19 -04:00
|
|
|
HAL_Bool HAL_GetRSLState(int32_t* status) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-30 09:22:51 -07:00
|
|
|
HAL_Bool HAL_GetSystemTimeValid(int32_t* status) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-01 00:43:06 -07:00
|
|
|
HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) {
|
2017-10-16 20:00:32 -07:00
|
|
|
static std::atomic_bool initialized{false};
|
2025-11-07 20:00:05 -05:00
|
|
|
static wpi::util::mutex initializeMutex;
|
2017-10-16 20:00:32 -07:00
|
|
|
// Initial check, as if it's true initialization has finished
|
2020-12-28 12:58:06 -08:00
|
|
|
if (initialized) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-10-16 20:00:32 -07:00
|
|
|
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(initializeMutex);
|
2017-10-16 20:00:32 -07:00
|
|
|
// Second check in case another thread was waiting
|
2020-12-28 12:58:06 -08:00
|
|
|
if (initialized) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-10-16 20:00:32 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::hal::init::InitializeHAL();
|
2017-12-10 19:38:53 -08:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::hal::init::HAL_IsInitialized.store(true);
|
2018-05-13 22:02:47 -07:00
|
|
|
|
2025-11-07 20:00:05 -05:00
|
|
|
wpi::hal::RestartTiming();
|
|
|
|
|
wpi::hal::InitializeDriverStation();
|
2017-10-16 20:00:32 -07:00
|
|
|
|
|
|
|
|
initialized = true;
|
2020-06-28 20:45:40 -04:00
|
|
|
|
2024-08-01 19:51:02 -07:00
|
|
|
// Set Timer Precision to 0.5ms on Windows
|
2020-06-28 20:45:40 -04:00
|
|
|
#ifdef _WIN32
|
2024-08-02 21:56:58 -04:00
|
|
|
// Use timeGetDevCaps as well to prevent Java from interfering
|
|
|
|
|
TIMECAPS tc;
|
|
|
|
|
if (timeGetDevCaps(&tc, sizeof(tc)) == TIMERR_NOERROR) {
|
|
|
|
|
UINT target = (std::max)(static_cast<UINT>(1), tc.wPeriodMin);
|
|
|
|
|
timeBeginPeriod(target);
|
|
|
|
|
std::atexit([]() {
|
|
|
|
|
TIMECAPS tc;
|
|
|
|
|
if (timeGetDevCaps(&tc, sizeof(tc)) == TIMERR_NOERROR) {
|
|
|
|
|
UINT target = (std::max)(static_cast<UINT>(1), tc.wPeriodMin);
|
|
|
|
|
timeEndPeriod(target);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-08-01 19:51:02 -07:00
|
|
|
// https://stackoverflow.com/questions/3141556/how-to-setup-timer-resolution-to-0-5-ms
|
|
|
|
|
ULONG min, max, current;
|
|
|
|
|
if (NtQueryTimerResolution(&min, &max, ¤t) == 0) {
|
|
|
|
|
ULONG currentRes;
|
|
|
|
|
if (NtSetTimerResolution(max, TRUE, ¤tRes) == 0) {
|
|
|
|
|
std::atexit([]() {
|
|
|
|
|
ULONG currentRes;
|
|
|
|
|
NtSetTimerResolution(0, FALSE, ¤tRes);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setprocessinformation
|
|
|
|
|
// Enable HighQoS to achieve maximum performance, and turn off power saving.
|
|
|
|
|
|
|
|
|
|
// https://forums.oculusvr.com/t5/General/SteamVR-has-fixed-the-problems-with-Windows-11/td-p/956413
|
|
|
|
|
// Always honor Timer Resolution Requests. This is to ensure that the timer
|
|
|
|
|
// resolution set-up above sticks through transitions of the main window (eg:
|
|
|
|
|
// minimization).
|
2024-08-02 21:56:58 -04:00
|
|
|
// This setting was introduced in Windows 11 and the definition is not
|
|
|
|
|
// available in older headers.
|
2024-08-01 19:51:02 -07:00
|
|
|
#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
|
2024-08-02 21:56:58 -04:00
|
|
|
const auto PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION = 0x4U;
|
2024-08-01 19:51:02 -07:00
|
|
|
#endif
|
|
|
|
|
|
2024-08-02 21:56:58 -04:00
|
|
|
// Try both at once, and if that doesn't succeed, only set HighQoS
|
|
|
|
|
PROCESS_POWER_THROTTLING_STATE PowerThrottling{};
|
|
|
|
|
PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
|
|
|
|
|
PowerThrottling.ControlMask =
|
|
|
|
|
PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION |
|
|
|
|
|
PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
|
|
|
|
|
PowerThrottling.StateMask = 0;
|
|
|
|
|
|
|
|
|
|
auto status =
|
|
|
|
|
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling,
|
|
|
|
|
&PowerThrottling, sizeof(PowerThrottling));
|
|
|
|
|
|
|
|
|
|
// setting both failed, fall back to HighQoS only
|
|
|
|
|
if (status == 0) {
|
2024-08-01 19:51:02 -07:00
|
|
|
PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
|
2024-08-02 21:56:58 -04:00
|
|
|
PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
|
2024-08-01 19:51:02 -07:00
|
|
|
PowerThrottling.StateMask = 0;
|
|
|
|
|
|
|
|
|
|
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling,
|
|
|
|
|
&PowerThrottling, sizeof(PowerThrottling));
|
2020-06-28 20:45:40 -04:00
|
|
|
}
|
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
setlinebuf(stdin);
|
|
|
|
|
setlinebuf(stdout);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
if (HAL_LoadExtensions() < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-05 22:12:12 -07:00
|
|
|
|
2017-08-18 21:35:53 -07:00
|
|
|
return true; // Add initialization if we need to at a later point
|
2014-01-06 10:12:21 -05:00
|
|
|
}
|
|
|
|
|
|
2020-09-04 08:59:26 -07:00
|
|
|
void HAL_Shutdown(void) {
|
|
|
|
|
std::vector<std::pair<void*, void (*)(void*)>> funcs;
|
|
|
|
|
{
|
|
|
|
|
std::scoped_lock lock(gOnShutdownMutex);
|
|
|
|
|
funcs.swap(gOnShutdown);
|
|
|
|
|
}
|
|
|
|
|
for (auto&& func : funcs) {
|
|
|
|
|
func.second(func.first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HAL_OnShutdown(void* param, void (*func)(void*)) {
|
|
|
|
|
std::scoped_lock lock(gOnShutdownMutex);
|
|
|
|
|
gOnShutdown.emplace_back(param, func);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void HAL_SimPeriodicBefore(void) {
|
|
|
|
|
gSimPeriodicBefore();
|
|
|
|
|
}
|
2020-11-30 23:55:36 -08:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
void HAL_SimPeriodicAfter(void) {
|
|
|
|
|
gSimPeriodicAfter();
|
|
|
|
|
}
|
2020-11-30 23:55:36 -08:00
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimPeriodicBeforeCallback(
|
|
|
|
|
HALSIM_SimPeriodicCallback callback, void* param) {
|
|
|
|
|
return gSimPeriodicBefore.Register(callback, param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimPeriodicBeforeCallback(int32_t uid) {
|
|
|
|
|
gSimPeriodicBefore.Cancel(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimPeriodicAfterCallback(
|
|
|
|
|
HALSIM_SimPeriodicCallback callback, void* param) {
|
|
|
|
|
return gSimPeriodicAfter.Register(callback, param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimPeriodicAfterCallback(int32_t uid) {
|
|
|
|
|
gSimPeriodicAfter.Cancel(uid);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-23 12:46:01 -05:00
|
|
|
void HALSIM_CancelAllSimPeriodicCallbacks(void) {
|
|
|
|
|
gSimPeriodicBefore.Reset();
|
|
|
|
|
gSimPeriodicAfter.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 18:28:57 -05:00
|
|
|
void HAL_ReportUsage(const struct WPI_String* resource,
|
|
|
|
|
const struct WPI_String* data) {
|
|
|
|
|
// Do nothing for now
|
2014-05-02 17:54:01 -04:00
|
|
|
}
|
2013-12-15 18:30:16 -05:00
|
|
|
|
2015-11-26 00:08:32 -08:00
|
|
|
} // extern "C"
|