From 7d64d4e24cb9b89ddc7cdb2484a762c461ab86e3 Mon Sep 17 00:00:00 2001 From: Wispy <101812473+WispySparks@users.noreply.github.com> Date: Mon, 15 Jul 2024 19:28:05 -0500 Subject: [PATCH] [sim] Add GUI support for the REV PH (#6704) --- .../cpp/hardware/{PCM.cpp => Pneumatic.cpp} | 51 ++-- .../glass/hardware/{PCM.h => Pneumatic.h} | 38 ++- .../src/main/native/cpp/PCMSimGui.cpp | 65 +++-- .../src/main/native/cpp/PCMSimGui.h | 6 + .../src/main/native/cpp/PHSimGui.cpp | 225 ++++++++++++++++++ .../halsim_gui/src/main/native/cpp/PHSimGui.h | 20 ++ .../halsim_gui/src/main/native/cpp/main.cpp | 36 +++ 7 files changed, 374 insertions(+), 67 deletions(-) rename glass/src/lib/native/cpp/hardware/{PCM.cpp => Pneumatic.cpp} (75%) rename glass/src/lib/native/include/glass/hardware/{PCM.h => Pneumatic.h} (50%) create mode 100644 simulation/halsim_gui/src/main/native/cpp/PHSimGui.cpp create mode 100644 simulation/halsim_gui/src/main/native/cpp/PHSimGui.h diff --git a/glass/src/lib/native/cpp/hardware/PCM.cpp b/glass/src/lib/native/cpp/hardware/Pneumatic.cpp similarity index 75% rename from glass/src/lib/native/cpp/hardware/PCM.cpp rename to glass/src/lib/native/cpp/hardware/Pneumatic.cpp index 6238fd9ae4d..10ec61e142b 100644 --- a/glass/src/lib/native/cpp/hardware/PCM.cpp +++ b/glass/src/lib/native/cpp/hardware/Pneumatic.cpp @@ -2,7 +2,7 @@ // 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 "glass/hardware/PCM.h" +#include "glass/hardware/Pneumatic.h" #include #include @@ -20,8 +20,8 @@ using namespace glass; -bool glass::DisplayPCMSolenoids(PCMModel* model, int index, - bool outputsEnabled) { +bool glass::DisplayPneumaticControlSolenoids(PneumaticControlModel* model, + int index, bool outputsEnabled) { wpi::SmallVector channels; model->ForEachSolenoid([&](SolenoidModel& solenoid, int j) { if (auto data = solenoid.GetOutputData()) { @@ -50,7 +50,8 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index, wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###header", name, index); } else { - wpi::format_to_n_c_str(label, sizeof(label), "PCM[{}]###header", index); + wpi::format_to_n_c_str(label, sizeof(label), "{}[{}]###header", + model->GetName(), index); } // header @@ -85,32 +86,29 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index, return true; } -void glass::DisplayPCMsSolenoids(PCMsModel* model, bool outputsEnabled, - std::string_view noneMsg) { +void glass::DisplayPneumaticControlsSolenoids(PneumaticControlsModel* model, + bool outputsEnabled, + std::string_view noneMsg) { bool hasAny = false; - model->ForEachPCM([&](PCMModel& pcm, int i) { - PushID(i); - if (DisplayPCMSolenoids(&pcm, i, outputsEnabled)) { - hasAny = true; - } - PopID(); - }); + model->ForEachPneumaticControl( + [&](PneumaticControlModel& pneumaticControl, int i) { + PushID(i); + if (DisplayPneumaticControlSolenoids(&pneumaticControl, i, + outputsEnabled)) { + hasAny = true; + } + PopID(); + }); if (!hasAny && !noneMsg.empty()) { ImGui::TextUnformatted(noneMsg.data(), noneMsg.data() + noneMsg.size()); } } -void glass::DisplayCompressorDevice(PCMModel* model, int index, +void glass::DisplayCompressorDevice(CompressorModel* model, int index, bool outputsEnabled) { - auto compressor = model->GetCompressor(); - if (!compressor || !compressor->Exists()) { + if (!model || !model->Exists()) { return; } - DisplayCompressorDevice(compressor, index, outputsEnabled); -} - -void glass::DisplayCompressorDevice(CompressorModel* model, int index, - bool outputsEnabled) { char name[32]; wpi::format_to_n_c_str(name, sizeof(name), "Compressor[{}]", index); @@ -155,8 +153,11 @@ void glass::DisplayCompressorDevice(CompressorModel* model, int index, } } -void glass::DisplayCompressorsDevice(PCMsModel* model, bool outputsEnabled) { - model->ForEachPCM([&](PCMModel& pcm, int i) { - DisplayCompressorDevice(&pcm, i, outputsEnabled); - }); +void glass::DisplayCompressorsDevice(PneumaticControlsModel* model, + bool outputsEnabled) { + model->ForEachPneumaticControl( + [&](PneumaticControlModel& pneumaticControl, int i) { + DisplayCompressorDevice(pneumaticControl.GetCompressor(), i, + outputsEnabled); + }); } diff --git a/glass/src/lib/native/include/glass/hardware/PCM.h b/glass/src/lib/native/include/glass/hardware/Pneumatic.h similarity index 50% rename from glass/src/lib/native/include/glass/hardware/PCM.h rename to glass/src/lib/native/include/glass/hardware/Pneumatic.h index 107a2a84fc6..0e9e525f261 100644 --- a/glass/src/lib/native/include/glass/hardware/PCM.h +++ b/glass/src/lib/native/include/glass/hardware/Pneumatic.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include #include @@ -34,27 +36,45 @@ class SolenoidModel : public Model { virtual void SetOutput(bool val) = 0; }; -class PCMModel : public Model { +class PneumaticControlModel : public Model { public: virtual CompressorModel* GetCompressor() = 0; virtual void ForEachSolenoid( wpi::function_ref func) = 0; + + virtual std::string_view GetName() = 0; }; -class PCMsModel : public Model { +class PneumaticControlsModel : public Model { public: - virtual void ForEachPCM( - wpi::function_ref func) = 0; + virtual void ForEachPneumaticControl( + wpi::function_ref + func) = 0; +}; + +struct AllPneumaticControlsModel : public Model { + AllPneumaticControlsModel(std::unique_ptr pcms, + std::unique_ptr phs) + : pcms{std::move(pcms)}, phs{std::move(phs)} {}; + std::unique_ptr pcms; + std::unique_ptr phs; + void Update() override { + pcms->Update(); + phs->Update(); + }; + bool Exists() override { return true; } }; -bool DisplayPCMSolenoids(PCMModel* model, int index, bool outputsEnabled); -void DisplayPCMsSolenoids(PCMsModel* model, bool outputsEnabled, - std::string_view noneMsg = "No solenoids"); +bool DisplayPneumaticControlSolenoids(PneumaticControlModel* model, int index, + bool outputsEnabled); +void DisplayPneumaticControlsSolenoids( + PneumaticControlsModel* model, bool outputsEnabled, + std::string_view noneMsg = "No solenoids"); -void DisplayCompressorDevice(PCMModel* model, int index, bool outputsEnabled); void DisplayCompressorDevice(CompressorModel* model, int index, bool outputsEnabled); -void DisplayCompressorsDevice(PCMsModel* model, bool outputsEnabled); +void DisplayCompressorsDevice(PneumaticControlsModel* model, + bool outputsEnabled); } // namespace glass diff --git a/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.cpp b/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.cpp index 869c5b102cb..d5a3f3604e4 100644 --- a/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.cpp @@ -4,7 +4,7 @@ #include "PCMSimGui.h" -#include +#include #include #include @@ -25,7 +25,8 @@ namespace { HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(CTREPCMCompressorOn, "Compressor On"); HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(CTREPCMClosedLoopEnabled, "Closed Loop"); HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(CTREPCMPressureSwitch, "Pressure Switch"); -HALSIMGUI_DATASOURCE_DOUBLE_INDEXED(CTREPCMCompressorCurrent, "Comp Current"); +HALSIMGUI_DATASOURCE_DOUBLE_INDEXED(CTREPCMCompressorCurrent, + "Compressor Current"); HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED2(CTREPCMSolenoidOutput, "Solenoid"); class CompressorSimModel : public glass::CompressorModel { @@ -90,7 +91,7 @@ class SolenoidSimModel : public glass::SolenoidModel { CTREPCMSolenoidOutputSource m_output; }; -class PCMSimModel : public glass::PCMModel { +class PCMSimModel : public glass::PneumaticControlModel { public: explicit PCMSimModel(int32_t index) : m_index{index}, @@ -107,6 +108,8 @@ class PCMSimModel : public glass::PCMModel { wpi::function_ref func) override; + std::string_view GetName() override { return "PCM"; } + int GetNumSolenoids() const { return m_solenoidInitCount; } private: @@ -116,7 +119,7 @@ class PCMSimModel : public glass::PCMModel { int m_solenoidInitCount = 0; }; -class PCMsSimModel : public glass::PCMsModel { +class PCMsSimModel : public glass::PneumaticControlsModel { public: PCMsSimModel() : m_models(HAL_GetNumCTREPCMModules()) {} @@ -124,8 +127,9 @@ class PCMsSimModel : public glass::PCMsModel { bool Exists() override { return true; } - void ForEachPCM( - wpi::function_ref func) override; + void ForEachPneumaticControl( + wpi::function_ref + func) override; private: std::vector> m_models; @@ -176,8 +180,9 @@ void PCMsSimModel::Update() { } } -void PCMsSimModel::ForEachPCM( - wpi::function_ref func) { +void PCMsSimModel::ForEachPneumaticControl( + wpi::function_ref + func) { int32_t numCTREPCMs = m_models.size(); for (int32_t i = 0; i < numCTREPCMs; ++i) { if (auto model = m_models[i].get()) { @@ -186,7 +191,7 @@ void PCMsSimModel::ForEachPCM( } } -static bool PCMsAnyInitialized() { +bool PCMSimGui::PCMsAnyInitialized() { static const int32_t num = HAL_GetNumCTREPCMModules(); for (int32_t i = 0; i < num; ++i) { if (HALSIM_GetCTREPCMInitialized(i)) { @@ -196,31 +201,25 @@ static bool PCMsAnyInitialized() { return false; } -void PCMSimGui::Initialize() { - HALSimGui::halProvider->RegisterModel("CTREPCMs", PCMsAnyInitialized, [] { - return std::make_unique(); - }); - HALSimGui::halProvider->RegisterView( - "Solenoids", "CTREPCMs", - [](glass::Model* model) { - bool any = false; - static_cast(model)->ForEachPCM( - [&](glass::PCMModel& CTREPCM, int) { - if (static_cast(&CTREPCM)->GetNumSolenoids() > 0) { - any = true; - } - }); - return any; - }, - [](glass::Window* win, glass::Model* model) { - win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize); - win->SetDefaultPos(290, 20); - return glass::MakeFunctionView([=] { - glass::DisplayPCMsSolenoids( - static_cast(model), - HALSimGui::halProvider->AreOutputsEnabled()); - }); +bool PCMSimGui::PCMsAnySolenoids(glass::PneumaticControlsModel* model) { + bool any = false; + static_cast(model)->ForEachPneumaticControl( + [&](glass::PneumaticControlModel& CTREPCM, int) { + if (static_cast(&CTREPCM)->GetNumSolenoids() > 0) { + any = true; + } }); + return any; +} + +std::unique_ptr PCMSimGui::GetPCMsModel() { + return std::make_unique(); +} + +void PCMSimGui::Initialize() { + HALSimGui::halProvider->RegisterModel( + "CTREPCMs", PCMSimGui::PCMsAnyInitialized, + [] { return std::make_unique(); }); SimDeviceGui::GetDeviceTree().Add( HALSimGui::halProvider->GetModel("CTREPCMs"), [](glass::Model* model) { diff --git a/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.h b/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.h index aef771784d9..da20022ef14 100644 --- a/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.h +++ b/simulation/halsim_gui/src/main/native/cpp/PCMSimGui.h @@ -3,12 +3,18 @@ // the WPILib BSD license file in the root directory of this project. #pragma once +#include + +#include namespace halsimgui { class PCMSimGui { public: static void Initialize(); + static bool PCMsAnyInitialized(); + static bool PCMsAnySolenoids(glass::PneumaticControlsModel* model); + static std::unique_ptr GetPCMsModel(); }; } // namespace halsimgui diff --git a/simulation/halsim_gui/src/main/native/cpp/PHSimGui.cpp b/simulation/halsim_gui/src/main/native/cpp/PHSimGui.cpp new file mode 100644 index 00000000000..47e65e1e5b2 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/cpp/PHSimGui.cpp @@ -0,0 +1,225 @@ +// 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 "PHSimGui.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "HALDataSource.h" +#include "HALSimGui.h" +#include "SimDeviceGui.h" + +using namespace halsimgui; + +namespace { +HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(REVPHCompressorOn, "Compressor On"); +HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED(REVPHPressureSwitch, "Pressure Switch"); +HALSIMGUI_DATASOURCE_DOUBLE_INDEXED(REVPHCompressorCurrent, + "Compressor Current"); +HALSIMGUI_DATASOURCE_BOOLEAN_INDEXED2(REVPHSolenoidOutput, "Solenoid"); + +class CompressorSimModel : public glass::CompressorModel { + public: + explicit CompressorSimModel(int32_t index) + : m_index{index}, + m_running{index}, + m_pressureSwitch{index}, + m_current{index} {} + + void Update() override {} + + bool Exists() override { return HALSIM_GetREVPHInitialized(m_index); } + + glass::DataSource* GetRunningData() override { return &m_running; } + glass::DataSource* GetEnabledData() override { return nullptr; } + glass::DataSource* GetPressureSwitchData() override { + return &m_pressureSwitch; + } + glass::DataSource* GetCurrentData() override { return &m_current; } + + void SetRunning(bool val) override { + HALSIM_SetREVPHCompressorOn(m_index, val); + } + void SetEnabled(bool val) override {} + void SetPressureSwitch(bool val) override { + HALSIM_SetREVPHPressureSwitch(m_index, val); + } + void SetCurrent(double val) override { + HALSIM_SetREVPHCompressorCurrent(m_index, val); + } + + private: + int32_t m_index; + REVPHCompressorOnSource m_running; + REVPHPressureSwitchSource m_pressureSwitch; + REVPHCompressorCurrentSource m_current; +}; + +class SolenoidSimModel : public glass::SolenoidModel { + public: + SolenoidSimModel(int32_t index, int32_t channel) + : m_index{index}, m_channel{channel}, m_output{index, channel} {} + + void Update() override {} + + bool Exists() override { return HALSIM_GetREVPHInitialized(m_index); } + + glass::DataSource* GetOutputData() override { return &m_output; } + + void SetOutput(bool val) override { + HALSIM_SetREVPHSolenoidOutput(m_index, m_channel, val); + } + + private: + int32_t m_index; + int32_t m_channel; + REVPHSolenoidOutputSource m_output; +}; + +class PHSimModel : public glass::PneumaticControlModel { + public: + explicit PHSimModel(int32_t index) + : m_index{index}, + m_compressor{index}, + m_solenoids(HAL_GetNumREVPHChannels()) {} + + void Update() override; + + bool Exists() override { return true; } + + CompressorSimModel* GetCompressor() override { return &m_compressor; } + + void ForEachSolenoid( + wpi::function_ref func) + override; + + std::string_view GetName() override { return "PH"; } + + int GetNumSolenoids() const { return m_solenoidInitCount; } + + private: + int32_t m_index; + CompressorSimModel m_compressor; + std::vector> m_solenoids; + int m_solenoidInitCount = 0; +}; + +class PHsSimModel : public glass::PneumaticControlsModel { + public: + PHsSimModel() : m_models(HAL_GetNumREVPHModules()) {} + + void Update() override; + + bool Exists() override { return true; } + + void ForEachPneumaticControl( + wpi::function_ref + func) override; + + private: + std::vector> m_models; +}; +} // namespace + +void PHSimModel::Update() { + int32_t numChannels = m_solenoids.size(); + m_solenoidInitCount = 0; + for (int32_t i = 0; i < numChannels; ++i) { + auto& model = m_solenoids[i]; + if (HALSIM_GetREVPHInitialized(m_index)) { + if (!model) { + model = std::make_unique(m_index, i); + } + ++m_solenoidInitCount; + } else { + model.reset(); + } + } +} + +void PHSimModel::ForEachSolenoid( + wpi::function_ref func) { + if (m_solenoidInitCount == 0) { + return; + } + int32_t numSolenoids = m_solenoids.size(); + for (int32_t i = 0; i < numSolenoids; ++i) { + if (auto model = m_solenoids[i].get()) { + func(*model, i); + } + } +} + +void PHsSimModel::Update() { + for (int32_t i = 0, iend = static_cast(m_models.size()); i < iend; + ++i) { + auto& model = m_models[i]; + if (HALSIM_GetREVPHInitialized(i)) { + if (!model) { + model = std::make_unique(i); + } + model->Update(); + } else { + model.reset(); + } + } +} + +void PHsSimModel::ForEachPneumaticControl( + wpi::function_ref + func) { + int32_t numREVPHs = m_models.size(); + for (int32_t i = 0; i < numREVPHs; ++i) { + if (auto model = m_models[i].get()) { + func(*model, i); + } + } +} + +bool PHSimGui::PHsAnyInitialized() { + static const int32_t num = HAL_GetNumREVPHModules(); + for (int32_t i = 0; i < num; ++i) { + if (HALSIM_GetREVPHInitialized(i)) { + return true; + } + } + return false; +} + +bool PHSimGui::PHsAnySolenoids(glass::PneumaticControlsModel* model) { + bool any = false; + static_cast(model)->ForEachPneumaticControl( + [&](glass::PneumaticControlModel& REVPH, int) { + if (static_cast(&REVPH)->GetNumSolenoids() > 0) { + any = true; + } + }); + return any; +} + +std::unique_ptr PHSimGui::GetPHsModel() { + return std::make_unique(); +} + +void PHSimGui::Initialize() { + HALSimGui::halProvider->RegisterModel( + "REVPHs", PHSimGui::PHsAnyInitialized, + [] { return std::make_unique(); }); + + SimDeviceGui::GetDeviceTree().Add( + HALSimGui::halProvider->GetModel("REVPHs"), [](glass::Model* model) { + glass::DisplayCompressorsDevice( + static_cast(model), + HALSimGui::halProvider->AreOutputsEnabled()); + }); +} diff --git a/simulation/halsim_gui/src/main/native/cpp/PHSimGui.h b/simulation/halsim_gui/src/main/native/cpp/PHSimGui.h new file mode 100644 index 00000000000..84fe9d76ac7 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/cpp/PHSimGui.h @@ -0,0 +1,20 @@ +// 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. + +#pragma once +#include + +#include + +namespace halsimgui { + +class PHSimGui { + public: + static void Initialize(); + static bool PHsAnyInitialized(); + static bool PHsAnySolenoids(glass::PneumaticControlsModel* model); + static std::unique_ptr GetPHsModel(); +}; + +} // namespace halsimgui diff --git a/simulation/halsim_gui/src/main/native/cpp/main.cpp b/simulation/halsim_gui/src/main/native/cpp/main.cpp index 54510239bc8..574090b974c 100644 --- a/simulation/halsim_gui/src/main/native/cpp/main.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -26,6 +27,7 @@ #include "HALSimGuiExt.h" #include "NetworkTablesSimGui.h" #include "PCMSimGui.h" +#include "PHSimGui.h" #include "PWMSimGui.h" #include "PowerDistributionSimGui.h" #include "RelaySimGui.h" @@ -85,9 +87,43 @@ __declspec(dllexport) PowerDistributionSimGui::Initialize(); PWMSimGui::Initialize(); RelaySimGui::Initialize(); + PHSimGui::Initialize(); RoboRioSimGui::Initialize(); TimingGui::Initialize(); + HALSimGui::halProvider->RegisterModel( + "AllPneumaticControls", + [] { + return PCMSimGui::PCMsAnyInitialized() || PHSimGui::PHsAnyInitialized(); + }, + [] { + return std::make_unique( + PCMSimGui::GetPCMsModel(), PHSimGui::GetPHsModel()); + }); + + HALSimGui::halProvider->RegisterView( + "Solenoids", "AllPneumaticControls", + [](glass::Model* model) { + auto pneumaticModel = + static_cast(model); + return PCMSimGui::PCMsAnySolenoids(pneumaticModel->pcms.get()) || + PHSimGui::PHsAnySolenoids(pneumaticModel->phs.get()); + }, + [](glass::Window* win, glass::Model* model) { + win->SetFlags(ImGuiWindowFlags_AlwaysAutoResize); + win->SetDefaultPos(290, 20); + return glass::MakeFunctionView([=] { + auto pneumaticModel = + static_cast(model); + glass::DisplayPneumaticControlsSolenoids( + pneumaticModel->pcms.get(), + HALSimGui::halProvider->AreOutputsEnabled()); + glass::DisplayPneumaticControlsSolenoids( + pneumaticModel->phs.get(), + HALSimGui::halProvider->AreOutputsEnabled()); + }); + }); + HALSimGui::mainMenu.AddMainMenu([] { if (ImGui::BeginMenu("Hardware")) { HALSimGui::halProvider->DisplayMenu();