// 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 "wpi/hal/Alert.h" #include #include #include #include #include "HALInitializer.hpp" #include "wpi/hal/Errors.h" #include "wpi/hal/HALBase.h" #include "wpi/hal/Types.h" #include "wpi/hal/handles/UnlimitedHandleResource.hpp" #include "wpi/hal/simulation/AlertData.h" #include "wpi/util/mutex.hpp" #include "wpi/util/string.h" namespace { struct Alert { Alert(std::string_view group, std::string_view text, int32_t level) : group{group}, text{text}, level{level} {} wpi::util::mutex textMutex; std::string group; std::string text; std::atomic activeStartTime{0}; // non-zero when active int32_t level = 0; }; } // namespace using namespace wpi::hal; static UnlimitedHandleResource* alertHandles; namespace wpi::hal::init { void InitializeAlert() { static UnlimitedHandleResource aH; alertHandles = &aH; } } // namespace wpi::hal::init extern "C" { HAL_AlertHandle HAL_CreateAlert(const WPI_String* group, const WPI_String* text, int32_t level, int32_t* status) { wpi::hal::init::CheckInit(); std::shared_ptr alert = std::make_shared( wpi::util::to_string_view(group), wpi::util::to_string_view(text), level); HAL_AlertHandle handle = alertHandles->Allocate(alert); if (handle == HAL_kInvalidHandle) { *status = HAL_HANDLE_ERROR; return HAL_kInvalidHandle; } return handle; } void HAL_DestroyAlert(HAL_AlertHandle alertHandle) { alertHandles->Free(alertHandle); } void HAL_SetAlertActive(HAL_AlertHandle alertHandle, HAL_Bool active, int32_t* status) { auto alert = alertHandles->Get(alertHandle); if (!alert) { *status = HAL_HANDLE_ERROR; return; } if (active) { if (alert->activeStartTime.load(std::memory_order_relaxed) != 0) { // Already active, do nothing (avoids cost of getting time) return; } int64_t now = HAL_GetFPGATime(status); int64_t expected = 0; // use compare-exchange to avoid potential race alert->activeStartTime.compare_exchange_strong(expected, now); } else { alert->activeStartTime = 0; } } HAL_Bool HAL_IsAlertActive(HAL_AlertHandle alertHandle, int32_t* status) { auto alert = alertHandles->Get(alertHandle); if (!alert) { *status = HAL_HANDLE_ERROR; return false; } return alert->activeStartTime != 0; } void HAL_SetAlertText(HAL_AlertHandle alertHandle, const WPI_String* text, int32_t* status) { auto alert = alertHandles->Get(alertHandle); if (!alert) { *status = HAL_HANDLE_ERROR; return; } std::scoped_lock lock(alert->textMutex); alert->text = wpi::util::to_string_view(text); } void HAL_GetAlertText(HAL_AlertHandle alertHandle, struct WPI_String* text, int32_t* status) { auto alert = alertHandles->Get(alertHandle); if (alert) { std::scoped_lock lock(alert->textMutex); *text = wpi::util::alloc_wpi_string(alert->text); } else { *status = HAL_HANDLE_ERROR; *text = WPI_String{}; } } int32_t HALSIM_GetNumAlerts(void) { int32_t count = 0; alertHandles->ForEach([&](auto, auto) { ++count; }); return count; } int32_t HALSIM_GetAlerts(struct HALSIM_AlertInfo* arr, int32_t length) { int32_t num = 0; alertHandles->ForEach([&](HAL_AlertHandle handle, Alert* alert) { if (num < length) { arr[num].handle = handle; arr[num].group = wpi::util::alloc_wpi_string(alert->group); { std::scoped_lock lock(alert->textMutex); arr[num].text = wpi::util::alloc_wpi_string(alert->text); } arr[num].activeStartTime = alert->activeStartTime.load(std::memory_order_relaxed); arr[num].level = alert->level; } ++num; }); return num; } void HALSIM_FreeAlerts(struct HALSIM_AlertInfo* arr, int32_t length) { for (int32_t i = 0; i < length; ++i) { WPI_FreeString(&arr[i].group); WPI_FreeString(&arr[i].text); } } void HALSIM_ResetAlertData(void) { alertHandles->ResetHandles(); } } // extern "C"