mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
303 lines
9.3 KiB
C++
303 lines
9.3 KiB
C++
|
|
// 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.
|
||
|
|
|
||
|
|
#include <atomic>
|
||
|
|
#include <cstdio>
|
||
|
|
#include <cstring>
|
||
|
|
#include <functional>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
#include <fmt/format.h>
|
||
|
|
|
||
|
|
#include "wpi/hal/DashboardOpMode.hpp"
|
||
|
|
#include "wpi/hal/cpp/MrcLibDs.hpp"
|
||
|
|
#include "wpi/hal/monotonic_clock.hpp"
|
||
|
|
#include "wpi/util/Synchronization.hpp"
|
||
|
|
#include "wpi/util/mutex.hpp"
|
||
|
|
|
||
|
|
static wpi::hal::MrcLibDs* mrcLibDs;
|
||
|
|
|
||
|
|
wpi::hal::MrcLibDs::~MrcLibDs() = default;
|
||
|
|
|
||
|
|
static void DefaultPrintErrorImpl(const struct WPI_String* line) {
|
||
|
|
if (line && line->str) {
|
||
|
|
std::fwrite(line->str, line->len, 1, stderr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::atomic<void (*)(const struct WPI_String* line)> gPrintErrorImpl{
|
||
|
|
DefaultPrintErrorImpl};
|
||
|
|
|
||
|
|
namespace wpi::hal {
|
||
|
|
int32_t DefaultSendErrorImpl(bool isError, int32_t errorCode,
|
||
|
|
const struct WPI_String* details,
|
||
|
|
const struct WPI_String* location,
|
||
|
|
const struct WPI_String* callStack, bool printMsg,
|
||
|
|
wpi::hal::BackendPrintFunction& backendWriteFunc) {
|
||
|
|
auto detailsView = wpi::util::to_string_view(details);
|
||
|
|
auto locationView = wpi::util::to_string_view(location);
|
||
|
|
auto callStackView = wpi::util::to_string_view(callStack);
|
||
|
|
// Avoid flooding console by keeping track of previous 5 error
|
||
|
|
// messages and only printing again if they're longer than 1 second old.
|
||
|
|
static wpi::util::mutex msgMutex;
|
||
|
|
static constexpr int KEEP_MSGS = 5;
|
||
|
|
std::scoped_lock lock(msgMutex);
|
||
|
|
static std::string prevMsg[KEEP_MSGS];
|
||
|
|
static std::chrono::time_point<monotonic_clock> prevMsgTime[KEEP_MSGS];
|
||
|
|
static bool initialized = false;
|
||
|
|
if (!initialized) {
|
||
|
|
for (int i = 0; i < KEEP_MSGS; i++) {
|
||
|
|
prevMsgTime[i] = monotonic_clock::now() - std::chrono::seconds(2);
|
||
|
|
}
|
||
|
|
initialized = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
auto curTime = monotonic_clock::now();
|
||
|
|
int i;
|
||
|
|
for (i = 0; i < KEEP_MSGS; ++i) {
|
||
|
|
if (prevMsg[i] == detailsView) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
int retval = 0;
|
||
|
|
if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
|
||
|
|
if (backendWriteFunc) {
|
||
|
|
retval = backendWriteFunc(isError, errorCode, details, location,
|
||
|
|
callStack, &printMsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (printMsg) {
|
||
|
|
fmt::memory_buffer buf;
|
||
|
|
if (!locationView.empty() && locationView[0] != '\0') {
|
||
|
|
fmt::format_to(fmt::appender{buf},
|
||
|
|
"{} at {}: ", isError ? "Error" : "Warning",
|
||
|
|
locationView);
|
||
|
|
}
|
||
|
|
fmt::format_to(fmt::appender{buf}, "{}\n", detailsView);
|
||
|
|
if (!callStackView.empty() && callStackView[0] != '\0') {
|
||
|
|
fmt::format_to(fmt::appender{buf}, "{}\n", callStackView);
|
||
|
|
}
|
||
|
|
auto printError = gPrintErrorImpl.load();
|
||
|
|
struct WPI_String line = {buf.data(), buf.size()};
|
||
|
|
printError(&line);
|
||
|
|
}
|
||
|
|
if (i == KEEP_MSGS) {
|
||
|
|
// replace the oldest one
|
||
|
|
i = 0;
|
||
|
|
auto first = prevMsgTime[0];
|
||
|
|
for (int j = 1; j < KEEP_MSGS; ++j) {
|
||
|
|
if (prevMsgTime[j] < first) {
|
||
|
|
first = prevMsgTime[j];
|
||
|
|
i = j;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
prevMsg[i] = detailsView;
|
||
|
|
}
|
||
|
|
prevMsgTime[i] = curTime;
|
||
|
|
}
|
||
|
|
return retval;
|
||
|
|
}
|
||
|
|
} // namespace wpi::hal
|
||
|
|
|
||
|
|
extern "C" {
|
||
|
|
|
||
|
|
void HAL_SetPrintErrorImpl(void (*func)(const struct WPI_String* line)) {
|
||
|
|
gPrintErrorImpl.store(func ? func : DefaultPrintErrorImpl);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode,
|
||
|
|
const struct WPI_String* details,
|
||
|
|
const struct WPI_String* location,
|
||
|
|
const struct WPI_String* callStack, HAL_Bool printMsg) {
|
||
|
|
return mrcLibDs->sendError(isError, errorCode, details, location, callStack,
|
||
|
|
printMsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SendConsoleLine(const struct WPI_String* line) {
|
||
|
|
return mrcLibDs->sendConsoleLine(line);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SendProgramCrash(const struct WPI_String* details,
|
||
|
|
const struct WPI_String* location,
|
||
|
|
const struct WPI_String* callStack) {
|
||
|
|
return mrcLibDs->sendProgramCrash(details, location, callStack);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
|
||
|
|
return mrcLibDs->getControlWord(controlWord);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetUncachedControlWord(HAL_ControlWord* controlWord) {
|
||
|
|
return mrcLibDs->getUncachedControlWord(controlWord);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SetOpModeOptions(const struct HAL_OpModeOption* options,
|
||
|
|
int32_t count) {
|
||
|
|
return mrcLibDs->setOpModeOptions(options, count);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
|
||
|
|
return mrcLibDs->getJoystickAxes(joystickNum, axes);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
|
||
|
|
return mrcLibDs->getJoystickPOVs(joystickNum, povs);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickButtons(int32_t joystickNum,
|
||
|
|
HAL_JoystickButtons* buttons) {
|
||
|
|
return mrcLibDs->getJoystickButtons(joystickNum, buttons);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickTouchpads(int32_t joystickNum,
|
||
|
|
HAL_JoystickTouchpads* touchpads) {
|
||
|
|
return mrcLibDs->getJoystickTouchpads(joystickNum, touchpads);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_GetAllJoystickData(int32_t joystickNum, HAL_JoystickAxes* axes,
|
||
|
|
HAL_JoystickPOVs* povs,
|
||
|
|
HAL_JoystickButtons* buttons,
|
||
|
|
HAL_JoystickTouchpads* touchpads) {
|
||
|
|
mrcLibDs->getAllJoystickData(joystickNum, axes, povs, buttons, touchpads);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
|
||
|
|
HAL_JoystickDescriptor* desc) {
|
||
|
|
return mrcLibDs->getJoystickDescriptor(joystickNum, desc);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
|
||
|
|
return mrcLibDs->getMatchInfo(info);
|
||
|
|
}
|
||
|
|
|
||
|
|
HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
|
||
|
|
HAL_AllianceStationID allianceStation;
|
||
|
|
*status = mrcLibDs->getAllianceStation(&allianceStation);
|
||
|
|
return allianceStation;
|
||
|
|
}
|
||
|
|
|
||
|
|
HAL_Bool HAL_GetJoystickIsGamepad(int32_t joystickNum) {
|
||
|
|
HAL_JoystickDescriptor joystickDesc;
|
||
|
|
int32_t status = mrcLibDs->getJoystickDescriptor(joystickNum, &joystickDesc);
|
||
|
|
if (status == 0) {
|
||
|
|
return joystickDesc.isGamepad != 0;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickGamepadType(int32_t joystickNum) {
|
||
|
|
HAL_JoystickDescriptor joystickDesc;
|
||
|
|
int32_t status = mrcLibDs->getJoystickDescriptor(joystickNum, &joystickDesc);
|
||
|
|
if (status == 0) {
|
||
|
|
return joystickDesc.gamepadType;
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetGameData(HAL_GameData* gameData) {
|
||
|
|
return mrcLibDs->getGameData(gameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_GetJoystickSupportedOutputs(int32_t joystickNum) {
|
||
|
|
HAL_JoystickDescriptor joystickDesc;
|
||
|
|
int32_t status = mrcLibDs->getJoystickDescriptor(joystickNum, &joystickDesc);
|
||
|
|
if (status == 0) {
|
||
|
|
return joystickDesc.supportedOutputs;
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_GetJoystickName(struct WPI_String* name, int32_t joystickNum) {
|
||
|
|
HAL_JoystickDescriptor joystickDesc;
|
||
|
|
const char* cName = joystickDesc.name;
|
||
|
|
if (mrcLibDs->getJoystickDescriptor(joystickNum, &joystickDesc) == 0) {
|
||
|
|
cName = joystickDesc.name;
|
||
|
|
} else {
|
||
|
|
cName = "";
|
||
|
|
}
|
||
|
|
auto len = std::strlen(cName);
|
||
|
|
auto write = WPI_AllocateString(name, len);
|
||
|
|
std::memcpy(write, cName, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SetJoystickRumble(int32_t joystickNum, int32_t leftRumble,
|
||
|
|
int32_t rightRumble, int32_t leftTriggerRumble,
|
||
|
|
int32_t rightTriggerRumble) {
|
||
|
|
return mrcLibDs->setJoystickRumble(joystickNum, leftRumble, rightRumble,
|
||
|
|
leftTriggerRumble, rightTriggerRumble);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t HAL_SetJoystickLeds(int32_t joystickNum, int32_t leds) {
|
||
|
|
return mrcLibDs->setJoystickLeds(joystickNum, leds);
|
||
|
|
}
|
||
|
|
|
||
|
|
double HAL_GetMatchTime(int32_t* status) {
|
||
|
|
double matchTime;
|
||
|
|
*status = mrcLibDs->getMatchTime(&matchTime);
|
||
|
|
if (*status != 0) {
|
||
|
|
return -1.0;
|
||
|
|
}
|
||
|
|
return matchTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_ObserveUserProgramStarting(void) {
|
||
|
|
mrcLibDs->observeUserProgramStarting();
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_ObserveUserProgram(HAL_ControlWord word) {
|
||
|
|
mrcLibDs->observeUserProgram(word);
|
||
|
|
}
|
||
|
|
|
||
|
|
HAL_Bool HAL_RefreshDSData(void) {
|
||
|
|
bool wasRefreshed;
|
||
|
|
if (mrcLibDs->refreshDSData(&wasRefreshed) < 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return wasRefreshed;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
|
||
|
|
mrcLibDs->provideNewDataEventHandle(handle);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle) {
|
||
|
|
mrcLibDs->removeNewDataEventHandle(handle);
|
||
|
|
}
|
||
|
|
|
||
|
|
HAL_Bool HAL_GetOutputsEnabled(void) {
|
||
|
|
bool outputsEnabled;
|
||
|
|
if (mrcLibDs->getOutputsEnabled(&outputsEnabled) < 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return outputsEnabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
HAL_Bool HAL_GetSystemTimeValid(int32_t* status) {
|
||
|
|
bool systemTimeValid;
|
||
|
|
*status = mrcLibDs->getSystemTimeValid(&systemTimeValid);
|
||
|
|
return systemTimeValid;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // extern "C"
|
||
|
|
|
||
|
|
namespace wpi::hal {
|
||
|
|
void InitializeDriverStation() {
|
||
|
|
InitializeDashboardOpMode();
|
||
|
|
mrcLibDs = wpi::hal::GetDefaultDriverStationImpl();
|
||
|
|
}
|
||
|
|
|
||
|
|
void ForceDsInstance(MrcLibDs* ds) {
|
||
|
|
mrcLibDs = ds;
|
||
|
|
}
|
||
|
|
|
||
|
|
void WaitForInitialPacket() {
|
||
|
|
wpi::util::Event waitForInitEvent;
|
||
|
|
mrcLibDs->provideNewDataEventHandle(waitForInitEvent.GetHandle());
|
||
|
|
bool timed_out = false;
|
||
|
|
wpi::util::WaitForObject(waitForInitEvent.GetHandle(), 0.1, &timed_out);
|
||
|
|
// Don't care what the result is, just want to give it a chance.
|
||
|
|
mrcLibDs->removeNewDataEventHandle(waitForInitEvent.GetHandle());
|
||
|
|
}
|
||
|
|
} // namespace wpi::hal
|