diff --git a/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp new file mode 100644 index 0000000000..554da2c894 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp @@ -0,0 +1,122 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "AddressableLEDGui.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ExtraGuiWidgets.h" +#include "HALSimGui.h" + +using namespace halsimgui; + +static constexpr int kDefaultColumns = 10; +static std::vector numColumns; + +// read/write columns setting to ini file +static void* AddressableLEDReadOpen(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, + const char* name) { + int num; + if (wpi::StringRef{name}.getAsInteger(10, num)) return nullptr; + if (num < 0) return nullptr; + if (num >= static_cast(numColumns.size())) + numColumns.resize(num + 1, kDefaultColumns); + return &numColumns[num]; +} + +static void AddressableLEDReadLine(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, void* entry, + const char* lineStr) { + int* cols = static_cast(entry); + // format: columns=# + wpi::StringRef line{lineStr}; + auto [name, value] = line.split('='); + name = name.trim(); + value = value.trim(); + if (name == "columns") { + int num; + if (value.getAsInteger(10, num)) return; + *cols = num; + } +} + +static void AddressableLEDWriteAll(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, + ImGuiTextBuffer* out_buf) { + for (size_t i = 0; i < numColumns.size(); ++i) { + out_buf->appendf("[AddressableLED][%d]\ncolumns=%d\n\n", + static_cast(i), numColumns[i]); + } +} + +static void DisplayAddressableLEDs() { + bool hasAny = false; + static const int numLED = HAL_GetNumAddressableLEDs(); + if (numLED > static_cast(numColumns.size())) + numColumns.resize(numLED, kDefaultColumns); + + for (int i = 0; i < numLED; ++i) { + if (!HALSIM_GetAddressableLEDInitialized(i)) continue; + hasAny = true; + + if (numLED > 1) ImGui::Text("LEDs[%d]", i); + + static HAL_AddressableLEDData data[HAL_kAddressableLEDMaxLength]; + int length = HALSIM_GetAddressableLEDData(i, data); + bool running = HALSIM_GetAddressableLEDRunning(i); + + ImGui::PushItemWidth(ImGui::GetFontSize() * 6); + ImGui::LabelText("Length", "%d", length); + ImGui::LabelText("Running", "%s", running ? "Yes" : "No"); + ImGui::InputInt("Columns", &numColumns[i]); + if (numColumns[i] < 1) numColumns[i] = 1; + ImGui::PopItemWidth(); + + // show as LED indicators + static int values[HAL_kAddressableLEDMaxLength]; + static ImU32 colors[HAL_kAddressableLEDMaxLength]; + + if (!running) { + colors[0] = IM_COL32(128, 128, 128, 255); + for (int j = 0; j < length; ++j) values[j] = -1; + } else { + for (int j = 0; j < length; ++j) { + values[j] = j + 1; + colors[j] = IM_COL32(data[j].r, data[j].g, data[j].b, 255); + } + } + + DrawLEDs(values, length, numColumns[i], colors); + } + if (!hasAny) ImGui::Text("No addressable LEDs"); +} + +void AddressableLEDGui::Initialize() { + // hook ini handler to save columns settings + ImGuiSettingsHandler iniHandler; + iniHandler.TypeName = "AddressableLED"; + iniHandler.TypeHash = ImHashStr(iniHandler.TypeName); + iniHandler.ReadOpenFn = AddressableLEDReadOpen; + iniHandler.ReadLineFn = AddressableLEDReadLine; + iniHandler.WriteAllFn = AddressableLEDWriteAll; + ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); + + HALSimGui::AddWindow("Addressable LEDs", DisplayAddressableLEDs, + ImGuiWindowFlags_AlwaysAutoResize); + HALSimGui::SetWindowVisibility("Addressable LEDs", HALSimGui::kHide); + HALSimGui::SetDefaultWindowPos("Addressable LEDs", 290, 100); +} diff --git a/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.h b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.h new file mode 100644 index 0000000000..e376b9b1ef --- /dev/null +++ b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.h @@ -0,0 +1,17 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +namespace halsimgui { + +class AddressableLEDGui { + public: + static void Initialize(); +}; + +} // namespace halsimgui diff --git a/simulation/halsim_gui/src/main/native/cpp/PWMGui.cpp b/simulation/halsim_gui/src/main/native/cpp/PWMGui.cpp index 8303f04ea5..430502dbc7 100644 --- a/simulation/halsim_gui/src/main/native/cpp/PWMGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/PWMGui.cpp @@ -9,9 +9,11 @@ #include #include +#include #include #include +#include #include #include "HALSimGui.h" @@ -20,6 +22,19 @@ using namespace halsimgui; static void DisplayPWMs() { bool hasOutputs = false; + static const int numPWM = HAL_GetNumPWMChannels(); + static const int numLED = HAL_GetNumAddressableLEDs(); + static auto ledMap = std::make_unique(numPWM); + + std::memset(ledMap.get(), 0, numPWM * sizeof(ledMap[0])); + + for (int i = 0; i < numLED; ++i) { + if (HALSIM_GetAddressableLEDInitialized(i)) { + int channel = HALSIM_GetAddressableLEDOutputPort(i); + if (channel >= 0 && channel < numPWM) ledMap[channel] = i + 1; + } + } + // struct History { // History() { std::memset(data, 0, 90 * sizeof(float)); } // History(const History&) = delete; @@ -30,7 +45,6 @@ static void DisplayPWMs() { //}; // static std::vector> history; bool first = true; - static const int numPWM = HAL_GetNumPWMChannels(); for (int i = 0; i < numPWM; ++i) { if (HALSIM_GetPWMInitialized(i)) { hasOutputs = true; @@ -42,8 +56,12 @@ static void DisplayPWMs() { char name[32]; std::snprintf(name, sizeof(name), "PWM[%d]", i); - float val = HALSimGui::AreOutputsDisabled() ? 0 : HALSIM_GetPWMSpeed(i); - ImGui::Value(name, val, "%0.3f"); + if (ledMap[i] > 0) { + ImGui::Text("%s: LED[%d]", name, ledMap[i] - 1); + } else { + float val = HALSimGui::AreOutputsDisabled() ? 0 : HALSIM_GetPWMSpeed(i); + ImGui::Value(name, val, "%0.3f"); + } // lazily build history storage // if (static_cast(i) > history.size()) diff --git a/simulation/halsim_gui/src/main/native/cpp/main.cpp b/simulation/halsim_gui/src/main/native/cpp/main.cpp index c570a82870..d5e0b168a6 100644 --- a/simulation/halsim_gui/src/main/native/cpp/main.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/main.cpp @@ -9,6 +9,7 @@ #include #include "AccelerometerGui.h" +#include "AddressableLEDGui.h" #include "AnalogGyroGui.h" #include "AnalogInputGui.h" #include "AnalogOutGui.h" @@ -33,6 +34,7 @@ __declspec(dllexport) #endif int HALSIM_InitExtension(void) { HALSimGui::Add(AccelerometerGui::Initialize); + HALSimGui::Add(AddressableLEDGui::Initialize); HALSimGui::Add(AnalogGyroGui::Initialize); HALSimGui::Add(AnalogInputGui::Initialize); HALSimGui::Add(AnalogOutGui::Initialize);