diff --git a/Images/HostSim/include/platform_api.h b/Images/HostSim/include/platform_api.h index c9279edc..9452b6e3 100644 --- a/Images/HostSim/include/platform_api.h +++ b/Images/HostSim/include/platform_api.h @@ -48,11 +48,25 @@ float platform_adc_get_offset_v_a(void); bool platform_get_encoder_angle(float* angle_deg); float platform_get_encoder_angle_latest(void); +float platform_get_motor_rpm(void); float platform_get_dc_link_voltage(void); +float platform_phase_voltage_u(void); +float platform_phase_voltage_v(void); +float platform_phase_voltage_w(void); float platform_get_throttle_a(void); float platform_get_throttle_b(void); +bool platform_get_throttle_valid(void); float platform_get_motor_temperature(void); float platform_get_inverter_temperature(uint8_t channel); + +/* Digital IO stubs. */ +bool platform_digital_read(uint8_t pin); +void platform_digital_write(uint8_t pin, bool value); + +/* CAN bus stubs (bus 1 = A, 2 = B). */ +bool platform_can_send(uint8_t bus, uint32_t id, bool ext, + const uint8_t* data, uint8_t dlc); +int platform_can_rx(uint8_t bus, uint32_t id, uint8_t* data, uint32_t* seq_out); void platform_sample_application_sensors(void); void platform_raise_fault(uint32_t source, uint8_t reason); diff --git a/Images/HostSim/scripts/run_spwm_live.ps1 b/Images/HostSim/scripts/run_spwm_live.ps1 index 82d5f965..ea410c4f 100644 --- a/Images/HostSim/scripts/run_spwm_live.ps1 +++ b/Images/HostSim/scripts/run_spwm_live.ps1 @@ -1,15 +1,40 @@ -# Emit the SPWM demo graph, build HostSim, and launch live + NodeGUI. +# Emit a graph into HostSim, build it, and launch live + NodeGUI. +# Defaults to the SPWM demo graph for backward compatibility with the demo menu. param( [switch]$NoGui, - [switch]$ForceEmit + [switch]$ForceEmit, + [string]$Graph = "", + [string]$Scenario = "" ) $ErrorActionPreference = "Stop" $hostSimRoot = Split-Path $PSScriptRoot -Parent $repoRoot = Split-Path (Split-Path $hostSimRoot -Parent) -Parent -$graph = Join-Path $hostSimRoot "graphs\spwm_demo_graph.json" -$scenario = Join-Path $hostSimRoot "scenarios\spwm_demo.json" + +if ([string]::IsNullOrEmpty($Graph)) { + $Graph = Join-Path $hostSimRoot "graphs\spwm_demo_graph.json" +} +if (-not (Test-Path $Graph)) { throw "Graph not found: $Graph" } + +$graphName = [System.IO.Path]::GetFileNameWithoutExtension($Graph) + +if ([string]::IsNullOrEmpty($Scenario)) { + $scenarioBase = $graphName + if ($scenarioBase.EndsWith("_graph")) { + $scenarioBase = $scenarioBase.Substring(0, $scenarioBase.Length - 6) + } + $candidate = Join-Path $hostSimRoot "scenarios\${scenarioBase}.json" + if (Test-Path $candidate) { + $Scenario = $candidate + } else { + $Scenario = Join-Path $hostSimRoot "scenarios\default_motor.json" + } +} +if (-not (Test-Path $Scenario)) { throw "Scenario not found: $Scenario" } + +$graph = $Graph +$scenario = $Scenario function To-WslPath([string]$p) { $full = (Resolve-Path $p).Path @@ -35,13 +60,13 @@ function Clear-EmittedTree([string]$repoRoot, [string]$relativePath) { wsl -d Ubuntu -u root -- bash -lc "cd $wslRepo && rm -rf $relativePath" 2>$null } -$emittedRel = "build/hostsim_spwm_emitted" -$buildDir = Join-Path $repoRoot "build\hostsim_spwm_emitted_build" +$emittedRel = "build/hostsim_${graphName}_emitted" +$buildDir = Join-Path $repoRoot "build\hostsim_${graphName}_emitted_build" Write-Host "Stopping running HostSim / NodeGUI (unlocks emit output)..." Stop-SimApps -$emitted = Join-Path $repoRoot "build\hostsim_spwm_emitted" +$emitted = Join-Path $repoRoot "build\hostsim_${graphName}_emitted" $exe = Join-Path $buildDir "Debug\host_sim.exe" if (-not (Test-Path $exe)) { $exe = Join-Path $buildDir "host_sim.exe" } @@ -59,7 +84,7 @@ if ($needEmit) { Clear-EmittedTree $repoRoot $emittedRel Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue - Write-Host "Emitting SPWM graph..." + Write-Host "Emitting ${graphName} graph into HostSim..." $emitCmd = "cd $wslRepo && ${emitter} --base-src Images/HostSim --graph $wslGraph --output $emittedRel --verbosity info" wsl -d Ubuntu -u root -- bash -lc $emitCmd if ($LASTEXITCODE -ne 0) { throw "RTECodeEmitter failed" } @@ -72,12 +97,12 @@ if ($needEmit) { $exe = Join-Path $buildDir "Debug\host_sim.exe" if (-not (Test-Path $exe)) { $exe = Join-Path $buildDir "host_sim.exe" } } else { - Write-Host "Using existing emitted SPWM build (pass -ForceEmit to rebuild)." + Write-Host "Using existing emitted ${graphName} build (pass -ForceEmit to rebuild)." } if (-not (Test-Path $exe)) { throw "host_sim.exe not found in $buildDir" } -Write-Host "Starting HostSim SPWM live..." +Write-Host "Starting HostSim live for ${graphName}..." Start-Process -FilePath $exe -ArgumentList $scenario, "--live", "--realtime", "1.0" -WorkingDirectory $emitted if (-not $NoGui) { @@ -87,11 +112,9 @@ if (-not $NoGui) { Start-Process -FilePath (Join-Path $guiWd "NodeGUI.exe") ` -ArgumentList $graphArg, "--tcp", "127.0.0.1:14608", "--protocol", "ivp" ` -WorkingDirectory $guiWd - Write-Host "NodeGUI opened with SPWM graph + live telemetry." + Write-Host "NodeGUI opened with ${graphName} graph + live telemetry." } Write-Host "" -Write-Host "Live controls:" -Write-Host " Throttle A -> modulation index (0..1)" -Write-Host " Throttle B -> electrical frequency map (1..20 Hz)" -Write-Host "Plot: duty_u/v/w (slow), pwm_gate_u/v/w + pwm_v_uv (scope), i_a/b/c" +Write-Host "Scenario: $scenario" +Write-Host "Live telemetry: 127.0.0.1:14608 (IVP)" diff --git a/Images/HostSim/scripts/run_spwm_live.sh b/Images/HostSim/scripts/run_spwm_live.sh index 8561f0d3..9f85e934 100755 --- a/Images/HostSim/scripts/run_spwm_live.sh +++ b/Images/HostSim/scripts/run_spwm_live.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -# Linux equivalent of run_spwm_live.ps1 -# Emit SPWM graph, build HostSim, launch live TCP server + NodeGUI. +# Linux HostSim live launcher. +# Emits the requested graph into HostSim, builds it, and starts the live TCP server. +# Defaults to the SPWM demo graph for backward compatibility with the demo menu. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -9,17 +10,56 @@ REPO_ROOT="$(cd "${HOSTSIM_ROOT}/../.." && pwd)" NO_GUI=0 FORCE_EMIT=0 +GRAPH="" +SCENARIO="" while [[ $# -gt 0 ]]; do case "$1" in --no-gui) NO_GUI=1 ;; --force-emit) FORCE_EMIT=1 ;; + --graph) + GRAPH="${2:-}" + shift + ;; + --scenario) + SCENARIO="${2:-}" + shift + ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac shift done -GRAPH="${HOSTSIM_ROOT}/graphs/spwm_demo_graph.json" -SCENARIO="${HOSTSIM_ROOT}/scenarios/spwm_demo.json" +if [[ -z "${GRAPH}" ]]; then + GRAPH="${HOSTSIM_ROOT}/graphs/spwm_demo_graph.json" +fi + +if [[ ! -f "${GRAPH}" ]]; then + echo "Graph not found: ${GRAPH}" >&2 + exit 1 +fi + +GRAPH_NAME="$(basename "${GRAPH}" .json)" + +if [[ -z "${SCENARIO}" ]]; then + # Prefer a scenario that matches the graph name (stripping a trailing _graph suffix); + # fall back to the generic motor scenario. + SCENARIO_BASE="${GRAPH_NAME}" + if [[ "${SCENARIO_BASE}" == *_graph ]]; then + SCENARIO_BASE="${SCENARIO_BASE%_graph}" + fi + CANDIDATE="${HOSTSIM_ROOT}/scenarios/${SCENARIO_BASE}.json" + if [[ -f "${CANDIDATE}" ]]; then + SCENARIO="${CANDIDATE}" + else + SCENARIO="${HOSTSIM_ROOT}/scenarios/default_motor.json" + fi +fi + +if [[ ! -f "${SCENARIO}" ]]; then + echo "Scenario not found: ${SCENARIO}" >&2 + exit 1 +fi + EMITTER="${RTE_EMITTER:-${REPO_ROOT}/build/Source/RTECodeEmitter/RTECodeEmitter}" NODEGUI="${REPO_ROOT}/build/Source/NodeGUI/NodeGUI" @@ -29,8 +69,8 @@ if [[ ! -x "${EMITTER}" ]]; then exit 1 fi -EMITTED_REL="build/hostsim_spwm_emitted" -BUILD_DIR="${REPO_ROOT}/build/hostsim_spwm_emitted_build" +EMITTED_REL="build/hostsim_${GRAPH_NAME}_emitted" +BUILD_DIR="${REPO_ROOT}/${EMITTED_REL}_build" cleanup_apps() { # Use exact-name matching so we do not kill the shell running this script @@ -53,7 +93,7 @@ fi if [[ "${need_emit}" -eq 1 ]]; then rm -rf "${REPO_ROOT}/${EMITTED_REL}" "${BUILD_DIR}" - echo "Emitting SPWM graph..." + echo "Emitting ${GRAPH_NAME} graph into HostSim..." "${EMITTER}" \ --base-src "${HOSTSIM_ROOT}" \ --graph "${GRAPH}" \ @@ -63,7 +103,7 @@ if [[ "${need_emit}" -eq 1 ]]; then cmake -S "${REPO_ROOT}/${EMITTED_REL}" -B "${BUILD_DIR}" cmake --build "${BUILD_DIR}" -j"$(nproc)" else - echo "Using existing emitted SPWM build (pass --force-emit to rebuild)." + echo "Using existing emitted ${GRAPH_NAME} build (pass --force-emit to rebuild)." fi if [[ ! -x "${EXE}" ]]; then @@ -71,7 +111,7 @@ if [[ ! -x "${EXE}" ]]; then exit 1 fi -echo "Starting HostSim SPWM live..." +echo "Starting HostSim live for ${GRAPH_NAME}..." nohup "${EXE}" "${SCENARIO}" --live --realtime 1.0 >/dev/null 2>&1 & if [[ "${NO_GUI}" -eq 0 ]]; then @@ -82,11 +122,9 @@ if [[ "${NO_GUI}" -eq 0 ]]; then fi sleep 1 nohup "${NODEGUI}" "${GRAPH}" --tcp 127.0.0.1:14608 --protocol ivp >/dev/null 2>&1 & - echo "NodeGUI opened with SPWM graph + live telemetry." + echo "NodeGUI opened with ${GRAPH_NAME} graph + live telemetry." fi echo "" -echo "Live controls:" -echo " Throttle A -> modulation index (0..1)" -echo " Throttle B -> electrical frequency map (1..20 Hz)" -echo "Plot: duty_u/v/w (slow), pwm_gate_u/v/w + pwm_v_uv (scope), i_a/b/c" +echo "Scenario: ${SCENARIO}" +echo "Live telemetry: 127.0.0.1:14608 (IVP)" diff --git a/Images/HostSim/src/platform_api.cpp b/Images/HostSim/src/platform_api.cpp index c36b1a19..6f5147bc 100644 --- a/Images/HostSim/src/platform_api.cpp +++ b/Images/HostSim/src/platform_api.cpp @@ -179,10 +179,41 @@ float platform_get_encoder_angle_latest(void) { return hostsim::g_motor->ThetaElectricalDeg(); } +float platform_get_motor_rpm(void) { + float omega_e = 0.0f; + int pole_pairs = 7; + if (hostsim::g_plant) { + omega_e = hostsim::g_plant->OmegaElectricalRadPerSec(); + } else if (hostsim::g_motor) { + omega_e = hostsim::g_motor->OmegaElectricalRadPerSec(); + } else { + return 0.0f; + } + if (hostsim::g_motor) { + pole_pairs = hostsim::g_motor->Params().pole_pairs; + } + return omega_e * 60.0f / (hostsim::kTwoPi * static_cast(pole_pairs)); +} + float platform_get_dc_link_voltage(void) { return hostsim::GetSimContext().vdc_v; } +float platform_phase_voltage_u(void) { + const auto& c = hostsim::GetSimContext(); + return c.duty_u * c.vdc_v / 100.0f; +} + +float platform_phase_voltage_v(void) { + const auto& c = hostsim::GetSimContext(); + return c.duty_v * c.vdc_v / 100.0f; +} + +float platform_phase_voltage_w(void) { + const auto& c = hostsim::GetSimContext(); + return c.duty_w * c.vdc_v / 100.0f; +} + float platform_get_throttle_a(void) { return hostsim::GetSimContext().throttle_a; } @@ -191,12 +222,47 @@ float platform_get_throttle_b(void) { return hostsim::GetSimContext().throttle_b; } +bool platform_get_throttle_valid(void) { + const float a = platform_get_throttle_a(); + const float b = platform_get_throttle_b(); + if (a < 0.0f || a > 1.0f || b < 0.0f || b > 1.0f) return false; + return std::fabs(a - b) < 0.1f; +} + float platform_get_motor_temperature(void) { return 25.0f; } float platform_get_inverter_temperature(uint8_t channel) { (void)channel; return 25.0f; } +bool platform_digital_read(uint8_t pin) { + (void)pin; + return false; +} + +void platform_digital_write(uint8_t pin, bool value) { + (void)pin; + (void)value; +} + +bool platform_can_send(uint8_t bus, uint32_t id, bool ext, + const uint8_t* data, uint8_t dlc) { + (void)bus; + (void)id; + (void)ext; + (void)data; + (void)dlc; + return false; +} + +int platform_can_rx(uint8_t bus, uint32_t id, uint8_t* data, uint32_t* seq_out) { + (void)bus; + (void)id; + (void)data; + (void)seq_out; + return -1; +} + void platform_sample_application_sensors(void) {} void platform_raise_fault(uint32_t source, uint8_t reason) { diff --git a/Source/NodeGUI/src/MainWindow.cpp b/Source/NodeGUI/src/MainWindow.cpp index 5d96fc3a..909f26b6 100644 --- a/Source/NodeGUI/src/MainWindow.cpp +++ b/Source/NodeGUI/src/MainWindow.cpp @@ -962,7 +962,9 @@ void MainWindow::StartBuildCommand(BuildCommand command) { << QStringLiteral("-ExecutionPolicy") << QStringLiteral("Bypass") << QStringLiteral("-File") << QString::fromStdString(psScript.string()) << QStringLiteral("-ForceEmit") - << QStringLiteral("-KeepGui"); + << QStringLiteral("-KeepGui") + << QStringLiteral("-Graph") + << absoluteGraphPath; } else { AppendBuildLog( QStringLiteral("\n[error] Firmware build/flash on Windows is not included in this " @@ -975,7 +977,11 @@ void MainWindow::StartBuildCommand(BuildCommand command) { if (command == BuildCommand::BuildSimulation) { program = QStringLiteral("bash"); const std::filesystem::path shScript = projectRoot / "Images" / "HostSim" / "scripts" / "run_spwm_live.sh"; - arguments << QString::fromStdString(shScript.string()) << QStringLiteral("--force-emit") << QStringLiteral("--no-gui"); + arguments << QString::fromStdString(shScript.string()) + << QStringLiteral("--force-emit") + << QStringLiteral("--no-gui") + << QStringLiteral("--graph") + << absoluteGraphPath; } else { const std::filesystem::path script = projectRoot / "Tools" / "build_flash_graph.sh"; if (!std::filesystem::is_regular_file(script)) { diff --git a/Source/NodeGUI/src/runtime/RuntimeTab.cpp b/Source/NodeGUI/src/runtime/RuntimeTab.cpp index 5866216f..9cab7a83 100644 --- a/Source/NodeGUI/src/runtime/RuntimeTab.cpp +++ b/Source/NodeGUI/src/runtime/RuntimeTab.cpp @@ -28,7 +28,7 @@ constexpr int kMaxRecentPresets = 10; // Bumped when the built-in layout changes so autosaves written before it // existed are discarded instead of masking the demo preset. -constexpr int kAutosaveVersion = 2; +constexpr int kAutosaveVersion = 3; QSettings MakeSettings() { return QSettings(QStringLiteral("RTE"), QStringLiteral("NodeGUI")); @@ -72,26 +72,53 @@ std::array RuntimeTab::BuiltinSpwmLayout() { }}; } +std::array RuntimeTab::BuiltinFocLayout() { + return {{ + QStringList{QStringLiteral("cg_id_a"), QStringLiteral("cg_iq_a")}, + QStringList{QStringLiteral("cg_vd_v"), QStringLiteral("cg_vq_v")}, + QStringList{QStringLiteral("cg_iu_a"), + QStringLiteral("cg_iv_a"), + QStringLiteral("cg_iw_a")}, + }}; +} + void RuntimeTab::EnsureBuiltinPresets() { auto settings = MakeSettings(); - if (settings.contains(QStringLiteral("runtime/presets/SPWM"))) { + const bool hasSpwm = settings.contains(QStringLiteral("runtime/presets/SPWM")); + const bool hasFoc = settings.contains(QStringLiteral("runtime/presets/FOC")); + if (hasSpwm && hasFoc) { return; } - const auto layout = BuiltinSpwmLayout(); - settings.beginGroup(QStringLiteral("runtime/presets/SPWM")); - for (int i = 0; i < 3; ++i) { - settings.setValue(QStringLiteral("graph%1").arg(i + 1), layout[i]); + + if (!hasSpwm) { + const auto layout = BuiltinSpwmLayout(); + settings.beginGroup(QStringLiteral("runtime/presets/SPWM")); + for (int i = 0; i < 3; ++i) { + settings.setValue(QStringLiteral("graph%1").arg(i + 1), layout[i]); + } + settings.endGroup(); + } + + if (!hasFoc) { + const auto layout = BuiltinFocLayout(); + settings.beginGroup(QStringLiteral("runtime/presets/FOC")); + for (int i = 0; i < 3; ++i) { + settings.setValue(QStringLiteral("graph%1").arg(i + 1), layout[i]); + } + settings.endGroup(); } - settings.endGroup(); QStringList recent = settings.value(QStringLiteral("runtime/recent")).toStringList(); - if (!recent.contains(QStringLiteral("SPWM"))) { + if (!hasSpwm && !recent.contains(QStringLiteral("SPWM"))) { recent.prepend(QStringLiteral("SPWM")); - while (recent.size() > kMaxRecentPresets) { - recent.removeLast(); - } - settings.setValue(QStringLiteral("runtime/recent"), recent); } + if (!hasFoc && !recent.contains(QStringLiteral("FOC"))) { + recent.prepend(QStringLiteral("FOC")); + } + while (recent.size() > kMaxRecentPresets) { + recent.removeLast(); + } + settings.setValue(QStringLiteral("runtime/recent"), recent); } void RuntimeTab::ApplyLayoutIfEmpty(const std::array& layout) { @@ -99,8 +126,13 @@ void RuntimeTab::ApplyLayoutIfEmpty(const std::array& layout) { return; } signalTablePanel_->SetGraphSignalSets(layout); - ApplySpwmViewWindows(); - presetStatus_->setText(QStringLiteral("applied SPWM plot layout")); + if (layout == BuiltinFocLayout()) { + ApplyFocViewWindows(); + presetStatus_->setText(QStringLiteral("applied FOC plot layout")); + } else { + ApplySpwmViewWindows(); + presetStatus_->setText(QStringLiteral("applied SPWM plot layout")); + } } void RuntimeTab::ApplySpwmViewWindows() { @@ -109,6 +141,12 @@ void RuntimeTab::ApplySpwmViewWindows() { signalTablePanel_->SetViewSeconds(1.0); } +void RuntimeTab::ApplyFocViewWindows() { + // G1 d/q current, G2 d/q voltage, G3 phase currents. + telemetryPanel_->SetGraphViewSeconds({0.5, 0.5, 0.5}); + signalTablePanel_->SetViewSeconds(0.5); +} + RuntimeTab::RuntimeTab(RuntimeController* controller, QWidget* parent) : QWidget(parent) , controller_(controller) { @@ -176,6 +214,11 @@ RuntimeTab::RuntimeTab(RuntimeController* controller, QWidget* parent) spwmButton->setToolTip(QStringLiteral("Restore the SPWM demo plot layout")); connect(spwmButton, &QPushButton::clicked, this, &RuntimeTab::OnLoadBuiltinSpwm); presetRow->addWidget(spwmButton); + + auto* focButton = new QPushButton(QStringLiteral("FOC"), this); + focButton->setToolTip(QStringLiteral("Restore the FOC plot layout")); + connect(focButton, &QPushButton::clicked, this, &RuntimeTab::OnLoadBuiltinFoc); + presetRow->addWidget(focButton); presetStatus_ = new QLabel(this); presetRow->addWidget(presetStatus_); presetRow->addStretch(1); @@ -212,11 +255,19 @@ RuntimeTab::RuntimeTab(RuntimeController* controller, QWidget* parent) } void RuntimeTab::OnStoreChanged() { - if (!applied_spwm_layout_) { - float duty = 0.0f; - if (controller_->Store().LatestValue("duty_u", duty)) { - ApplyLayoutIfEmpty(BuiltinSpwmLayout()); - applied_spwm_layout_ = true; + if (!applied_builtin_layout_) { + // FOC graphs publish d/q currents; prefer the FOC layout when those are + // present. SPWM demo graphs do not, so duty_u triggers the SPWM layout. + float id = 0.0f; + if (controller_->Store().LatestValue("cg_id_a", id)) { + ApplyLayoutIfEmpty(BuiltinFocLayout()); + applied_builtin_layout_ = true; + } else { + float duty = 0.0f; + if (controller_->Store().LatestValue("duty_u", duty)) { + ApplyLayoutIfEmpty(BuiltinSpwmLayout()); + applied_builtin_layout_ = true; + } } } @@ -325,7 +376,15 @@ void RuntimeTab::OnLoadBuiltinSpwm() { ApplySpwmViewWindows(); recentCombo_->setCurrentText(QStringLiteral("SPWM")); presetStatus_->setText(QStringLiteral("loaded SPWM layout")); - applied_spwm_layout_ = true; + applied_builtin_layout_ = true; +} + +void RuntimeTab::OnLoadBuiltinFoc() { + signalTablePanel_->SetGraphSignalSets(BuiltinFocLayout()); + ApplyFocViewWindows(); + recentCombo_->setCurrentText(QStringLiteral("FOC")); + presetStatus_->setText(QStringLiteral("loaded FOC layout")); + applied_builtin_layout_ = true; } void RuntimeTab::OnLoadPreset() { @@ -435,7 +494,7 @@ void RuntimeTab::LoadAutosave() { if (version < kAutosaveVersion || LayoutIsEmpty(sets)) { signalTablePanel_->SetGraphSignalSets(BuiltinSpwmLayout()); presetStatus_->setText(QStringLiteral("applied SPWM plot layout")); - applied_spwm_layout_ = true; + applied_builtin_layout_ = true; return; } signalTablePanel_->SetGraphSignalSets(sets); diff --git a/Source/NodeGUI/src/runtime/RuntimeTab.h b/Source/NodeGUI/src/runtime/RuntimeTab.h index 9c3e7a93..ab3cb6a6 100644 --- a/Source/NodeGUI/src/runtime/RuntimeTab.h +++ b/Source/NodeGUI/src/runtime/RuntimeTab.h @@ -32,15 +32,18 @@ class RuntimeTab : public QWidget { void LoadAutosave(); void SaveAutosave(); static std::array BuiltinSpwmLayout(); + static std::array BuiltinFocLayout(); void EnsureBuiltinPresets(); void ApplyLayoutIfEmpty(const std::array& layout); void ApplySpwmViewWindows(); + void ApplyFocViewWindows(); private slots: void OnStoreChanged(); void OnSavePreset(); void OnLoadPreset(); void OnLoadBuiltinSpwm(); + void OnLoadBuiltinFoc(); void OnTogglePause(); void OnSimPauseChanged(bool paused); void OnExportSession(); @@ -50,7 +53,7 @@ private slots: void RefreshRecentCombo(); void UpdatePauseButton(bool paused); - bool applied_spwm_layout_ = false; + bool applied_builtin_layout_ = false; RuntimeController* controller_;