Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions radio/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ endif()
set(SRC
${SRC}
edgetx.cpp
model_load_sm.cpp
datastructs_model.cpp
datastructs_radio.cpp
functions.cpp
Expand Down
69 changes: 47 additions & 22 deletions radio/src/edgetx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,17 +676,20 @@ void resetBacklightTimeout()


#if defined(MULTIMODULE)
void checkMultiLowPower()
bool isMultiLowPowerWarningRequired()
{
bool low_power_warning = false;
for (uint8_t i = 0; i < MAX_MODULES; i++) {
if (isModuleMultimodule(i) &&
g_model.moduleData[i].multi.lowPowerMode) {
low_power_warning = true;
return true;
}
}
return false;
}

if (low_power_warning) {
void checkMultiLowPower()
{
if (isMultiLowPowerWarningRequired()) {
ALERT("MULTI", STR_WARN_MULTI_LOWPOWER, AU_ERROR);
}
}
Expand All @@ -706,7 +709,7 @@ void checkSDfreeStorage() {
}
}

static void checkFailsafe()
bool isFailsafeWarningRequired()
{
for (int i=0; i<NUM_MODULES; i++) {
#if defined(MULTIMODULE)
Expand All @@ -716,11 +719,18 @@ static void checkFailsafe()
if (isModuleFailsafeAvailable(i)) {
ModuleData & moduleData = g_model.moduleData[i];
if (moduleData.failsafeMode == FAILSAFE_NOT_SET) {
ALERT(STR_FAILSAFEWARN, STR_NO_FAILSAFE, AU_ERROR);
break;
return true;
}
}
}
return false;
}

static void checkFailsafe()
{
if (isFailsafeWarningRequired()) {
ALERT(STR_FAILSAFEWARN, STR_NO_FAILSAFE, AU_ERROR);
}
}

#if defined(GUI)
Expand Down Expand Up @@ -802,6 +812,17 @@ void checkAll(bool isBootCheck)
}
#endif // GUI

// Refresh the input readings the warning predicates rely on. Kept separate from
// the predicates themselves so they stay pure: the driver (the model-load state
// machine, or the boot/flightReset blocking loops) refreshes once per tick, then
// queries the predicates and the warning views read the same fresh state.
void refreshInputsForWarnings()
{
if (!mixerTaskRunning()) getADC();
evalInputs(e_perout_mode_notrainer);
getMovedSwitch();
}

bool isThrottleWarningAlertNeeded()
{
if (g_model.disableThrottleWarning) {
Expand All @@ -816,9 +837,6 @@ bool isThrottleWarningAlertNeeded()
thr_src = throttleSource2Source(0);
}

if (!mixerTaskRunning()) getADC();
evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job

int16_t v = getValue(thr_src);

// TODO: this looks fishy....
Expand All @@ -844,25 +862,31 @@ bool isThrottleWarningAlertNeeded()
void checkThrottleStick()
{
char throttleNotIdle[strlen(STR_THROTTLE_NOT_IDLE) + 9];
if (isThrottleWarningAlertNeeded()) {
if (g_model.enableCustomThrottleWarning) {
refreshInputsForWarnings();
if (!isThrottleWarningAlertNeeded()) return;

if (g_model.enableCustomThrottleWarning) {
sprintf(throttleNotIdle, "%s (%d%%)", STR_THROTTLE_NOT_IDLE, g_model.customThrottleWarningPosition);
}
else {
strcpy(throttleNotIdle, STR_THROTTLE_NOT_IDLE);
}
LED_ERROR_BEGIN();
auto dialog = new ThrottleWarnDialog(throttleNotIdle);
MainWindow::instance()->blockUntilClose(true, [=]() {
return dialog->deleted();
});
LED_ERROR_END();
} else {
strcpy(throttleNotIdle, STR_THROTTLE_NOT_IDLE);
}

LED_ERROR_BEGIN();
// The dialog is a dumb view; this loop owns the refresh + predicate and closes
// when the stick is lowered (predicate clears) or a key is pressed (deleted).
auto dialog = new ThrottleWarnDialog(throttleNotIdle);
MainWindow::instance()->blockUntilClose(true, [=]() {
refreshInputsForWarnings();
return dialog->deleted() || !isThrottleWarningAlertNeeded();
});
if (!dialog->deleted()) dialog->deleteLater();
LED_ERROR_END();
}
#else
void checkThrottleStick()
{
char throttleNotIdle[strlen(STR_THROTTLE_NOT_IDLE) + 9];
refreshInputsForWarnings();
if (!isThrottleWarningAlertNeeded()) {
return;
}
Expand All @@ -881,6 +905,7 @@ void checkThrottleStick()
#endif

while (!keyDown()) {
refreshInputsForWarnings();
if (!isThrottleWarningAlertNeeded()) {
return;
}
Expand Down
11 changes: 11 additions & 0 deletions radio/src/edgetx.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,17 @@ void checkSwitches();
void checkAlarm();
void checkAll(bool isBootCheck = false);

// Warning predicates (pure reads) and the input refresh they rely on. The
// driver (boot/flightReset loops or the model-load state machine) calls
// refreshInputsForWarnings() once per tick, then queries the predicates.
void refreshInputsForWarnings();
bool isThrottleWarningAlertNeeded();
bool isFailsafeWarningRequired();
void checkSDfreeStorage();
#if defined(MULTIMODULE)
bool isMultiLowPowerWarningRequired();
#endif

void getADC();

#include "sbus.h"
Expand Down
1 change: 1 addition & 0 deletions radio/src/gui/colorlcd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ set(GUI_SRC
lcd.cpp
LvglWrapper.cpp
startup_shutdown.cpp
model_load_view.cpp

model/curveedit.cpp
model/input_edit.cpp
Expand Down
18 changes: 3 additions & 15 deletions radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ void SwitchWarnDialog::checkEvents()
{
FullScreenDialog::checkEvents();

uint16_t bad_pots;
if (!isSwitchWarningRequired(bad_pots)) {
deleteLater();
return;
};

// Display only — the driver (boot/flightReset loop or the model-load state
// machine) has already refreshed the inputs and owns the close decision. Here
// we just rebuild the list of switches/pots still in the wrong position.
std::string warn_txt;
for (int i = 0; i < switchGetMaxAllSwitches(); ++i) {
if (SWITCH_WARNING_ALLOWED(i)) {
Expand Down Expand Up @@ -85,12 +82,3 @@ ThrottleWarnDialog::ThrottleWarnDialog(const char* msg) :
lv_label_set_long_mode(messageLabel->getLvObj(), LV_LABEL_LONG_WRAP);
AUDIO_ERROR_MESSAGE(AU_THROTTLE_ALERT);
}

void ThrottleWarnDialog::checkEvents()
{
FullScreenDialog::checkEvents();

extern bool isThrottleWarningAlertNeeded();
if (!isThrottleWarningAlertNeeded())
deleteLater();
}
9 changes: 6 additions & 3 deletions radio/src/gui/colorlcd/controls/switch_warn_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
#include "mainwindow.h"
#include "edgetx.h"

// Dumb display-only dialogs. Their driver (the boot/flightReset blocking loops
// or the model-load state machine) owns the input refresh, the warning
// predicate and the close decision. Closing on a key press self-deletes as
// usual (default FullScreenDialog behaviour); for the state machine the
// model-load view reports that close as an acknowledge.
class SwitchWarnDialog : public FullScreenDialog
{
public:
Expand All @@ -35,6 +40,7 @@ class SwitchWarnDialog : public FullScreenDialog
#endif

protected:
// rebuilds the bad-switch/pot list text each frame (display only)
void checkEvents() override;
};

Expand All @@ -46,7 +52,4 @@ class ThrottleWarnDialog : public FullScreenDialog
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "ThrottleWarnDialog"; }
#endif

protected:
void checkEvents() override;
};
6 changes: 6 additions & 0 deletions radio/src/gui/colorlcd/libui/view_text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,12 @@ void readChecklist()
}
}

// Non-blocking version, driven by the model-load state machine view.
Window* showModelChecklist()
{
return _readModelNotes(false);
}

ModelNotesPage::ModelNotesPage(const PageDef& pageDef) : PageGroupItem(pageDef, PAD_ZERO)
{
}
Expand Down
5 changes: 5 additions & 0 deletions radio/src/gui/colorlcd/libui/view_text.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ class ModelNotesPage : public PageGroupItem

void readModelNotes(bool fromMenu = false);
void readChecklist();

// Non-blocking variant of readChecklist(): opens the model notes/checklist
// window and returns it (or nullptr if there is none) without spinning a UI
// loop. Used by the model-load state machine view.
Window* showModelChecklist();
121 changes: 121 additions & 0 deletions radio/src/gui/colorlcd/model_load_view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "model_load_view.h"

#include "edgetx.h"
#include "model_load_sm.h"
#include "controls/switch_warn_dialog.h"
#include "libui/fullscreen_dialog.h"
#include "libui/view_text.h"

// The single warning dialog currently displayed for the active machine state,
// and the state it corresponds to (so we only (re)build on state changes).
static Window* s_shown = nullptr;
static ModelLoadState s_shownState = MLS_IDLE;
// set while we tear a dialog down ourselves, to distinguish a view-initiated
// close (no acknowledge) from a user/condition close (acknowledge).
static bool s_tearingDown = false;

// Invoked from Window::deleteLater() (before the window is trashed/freed) when
// the dialog we are showing closes — either because its warning condition
// cleared, or the user pressed a key. We must react here rather than polling
// s_shown->deleted() afterwards, since perMain's MainWindow::run() empties the
// trash (freeing the window) before the next modelLoadViewSync().
static void onShownClosed()
{
s_shown = nullptr;
if (!s_tearingDown) modelLoadAcknowledge();
}

static FullScreenDialog* makeAckAlert(const char* title, const char* msg)
{
AUDIO_ERROR_MESSAGE(AU_ERROR);
return new FullScreenDialog(WARNING_TYPE_ALERT, title, msg,
STR_PRESS_ANY_KEY_TO_SKIP);
}

void modelLoadViewSync()
{
// Suspend main-view widget refresh for the whole model-load sequence: the
// model is being swapped and the warning view covers the main view anyway.
// Edge-triggered so we don't fight other users of the flag (standalone Lua).
static bool s_seqActive = false;
bool active = !modelLoadIdle();
if (active != s_seqActive) {
s_seqActive = active;
MainWindow::instance()->enableWidgetRefresh(!active);
}

ModelLoadState want = modelLoadActiveWarning();
if (want == s_shownState) return;

// Tear down a dialog that is still up (e.g. the sequence was restarted by a
// new model switch). Suppress the acknowledge for this view-initiated close.
if (s_shown) {
s_tearingDown = true;
s_shown->deleteLater(); // fires onShownClosed -> s_shown = nullptr
s_tearingDown = false;
}
s_shownState = want;

Window* dlg = nullptr;
switch (want) {
case MLS_CHECK_THROTTLE:
LED_ERROR_BEGIN();
dlg = new ThrottleWarnDialog(modelLoadWarningText());
break;
case MLS_CHECK_SWITCHES:
LED_ERROR_BEGIN();
dlg = new SwitchWarnDialog();
break;
case MLS_CHECK_SD:
LED_ERROR_BEGIN();
dlg = makeAckAlert(STR_SD_CARD, STR_SDCARD_FULL);
break;
case MLS_CHECK_FAILSAFE:
LED_ERROR_BEGIN();
dlg = makeAckAlert(STR_FAILSAFEWARN, STR_NO_FAILSAFE);
break;
#if defined(MULTIMODULE)
case MLS_CHECK_MULTI:
LED_ERROR_BEGIN();
dlg = makeAckAlert("MULTI", STR_WARN_MULTI_LOWPOWER);
break;
#endif
case MLS_CHECK_CHECKLIST:
cancelSplash();
dlg = showModelChecklist();
// no notes window could be opened: don't stall the sequence
if (!dlg) modelLoadAcknowledge();
break;
case MLS_IDLE:
default:
// sequence finished: clear the error indication
LED_ERROR_END();
break;
}

if (dlg) {
dlg->setCloseHandler(onShownClosed);
s_shown = dlg;
}
}
28 changes: 28 additions & 0 deletions radio/src/gui/colorlcd/model_load_view.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#pragma once

// COLORLCD view for the model-load state machine. Polled once per perMain()
// iteration (after MainWindow::run): it mirrors the machine's active warning
// into the matching full-screen dialog and reports user "skip" key presses back
// to the machine. The machine owns all the logic; this is display only.
void modelLoadViewSync();
Loading