Marlin/Marlin/src/lcd/extui/dgus_e3s1pro/DGUSScreenHandler.cpp
2024-01-22 00:04:08 -06:00

545 lines
15 KiB
C++

/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(DGUS_LCD_UI_E3S1PRO)
#include "DGUSDisplay.h"
#include "DGUSScreenHandler.h"
#include "DGUSSDCardHandler.h"
#include "definition/DGUS_ScreenAddrList.h"
#include "definition/DGUS_ScreenSetup.h"
#include "../../../gcode/queue.h"
#define DGUS_SCREEN_PAGE_DEBUG // uncomment to debug page changes
DGUSScreenHandler::eeprom_data_t DGUSScreenHandler::config = {};
uint16_t DGUSScreenHandler::currentMeshPointIndex = 0;
bool DGUSScreenHandler::isLeveling = false;
char DGUSScreenHandler::homeStatusMessage[128];
bool DGUSScreenHandler::settings_ready = false;
bool DGUSScreenHandler::booted = false;
DGUS_ScreenID DGUSScreenHandler::current_screen = DGUS_ScreenID::BOOT;
DGUS_ScreenID DGUSScreenHandler::new_screen = DGUS_ScreenID::BOOT;
DGUS_ScreenID DGUSScreenHandler::wait_return_screen = DGUS_ScreenID::BOOT;
DGUS_ScreenID DGUSScreenHandler::confirm_return_screen = DGUS_ScreenID::BOOT;
bool DGUSScreenHandler::full_update = false;
uint8_t DGUSScreenHandler::angry_beeps = 0;
#if HAS_MEDIA
static const char* const noFileSelected = "";
const char* DGUSScreenHandler::sdPrintFilename = noFileSelected;
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
bool DGUSScreenHandler::powerLossRecoveryAvailable = false;
#endif
#if ENABLED(DGUS_SOFTWARE_AUTOSCROLL)
ssize_t DGUSScreenHandler::currentScrollIndex = 0;
size_t DGUSScreenHandler::pageMaxStringLen = 0;
size_t DGUSScreenHandler::pageMaxControlLen = 0;
#endif
millis_t DGUSScreenHandler::status_expire = 0;
millis_t DGUSScreenHandler::eeprom_save = 0;
void DGUSScreenHandler::init() {
dgus.init();
moveToScreen(DGUS_ScreenID::BOOT, true);
}
void DGUSScreenHandler::ready() {
dgus.playSound(1);
}
void DGUSScreenHandler::loop() {
const millis_t ms = ExtUI::safe_millis();
static millis_t next_event_ms = 0, next_beep_ms = 0;
static bool wasLeveling = isLeveling;
if (new_screen != DGUS_ScreenID::BOOT) {
const DGUS_ScreenID screen = new_screen;
new_screen = DGUS_ScreenID::BOOT;
#if ENABLED(DGUS_SOFTWARE_AUTOSCROLL)
currentScrollIndex = -DGUS_AUTOSCROLL_START_CYCLES;
pageMaxStringLen = 0;
pageMaxControlLen = 0;
#endif
if (current_screen == screen)
triggerFullUpdate();
else
moveToScreen(screen);
return;
}
if (!booted && current_screen == DGUS_ScreenID::HOME) {
// Boot complete
booted = true;
dgus.readVersions();
return;
}
#if ENABLED(POWER_LOSS_RECOVERY)
if (booted && powerLossRecoveryAvailable) {
triggerScreenChange(DGUS_ScreenID::POWERCONTINUE);
powerLossRecoveryAvailable = false;
}
#endif
if (ELAPSED(ms, next_event_ms) || full_update) {
next_event_ms = ms + (booted ? DGUS_UPDATE_INTERVAL_MS : 50);
#if ENABLED(DGUS_SOFTWARE_AUTOSCROLL)
currentScrollIndex += 1;
if (currentScrollIndex > (ssize_t)(pageMaxStringLen - pageMaxControlLen) + DGUS_AUTOSCROLL_END_CYCLES)
currentScrollIndex = -DGUS_AUTOSCROLL_START_CYCLES;
#endif
if (angry_beeps) {
--angry_beeps;
dgus.playSound(3);
}
if (!sendScreenVPData(current_screen, full_update))
DEBUG_ECHOLNPGM("SendScreenVPData failed");
return;
}
if (ELAPSED(ms, next_beep_ms)) {
next_beep_ms = ms + 300;
if (angry_beeps) {
--angry_beeps;
dgus.playSound(0, 500/8, 100);
}
}
if (wasLeveling && !isLeveling) {
#if ENABLED(AUTO_BED_LEVELING_UBL)
ExtUI::injectCommands(ExtUI::getLevelingIsValid() ? F("G29 S0") : F("G29 S1\nG29 P3\nG29 S0"));
#endif
config.levelingEnabled = ExtUI::getLevelingIsValid();
ExtUI::setLevelingActive(config.levelingEnabled);
triggerEEPROMSave();
}
wasLeveling = isLeveling;
if (!settings_ready && booted) return;
if (status_expire > 0 && ELAPSED(ms, status_expire)) {
setStatusMessage(FPSTR(NUL_STR), 0);
return;
}
if (eeprom_save > 0 && ELAPSED(ms, eeprom_save) && isPrinterIdle()) {
eeprom_save = 0;
ExtUI::injectCommands(F("M500"));
return;
}
dgus.loop();
}
void DGUSScreenHandler::printerKilled(FSTR_P const error, FSTR_P const component) {
dgus.playSound(0, (uint8_t)(3000/8), 100);
setStatusMessage(error);
moveToScreen(DGUS_ScreenID::ABNORMAL);
}
void DGUSScreenHandler::userConfirmRequired(const char * const msg) {
#if ENABLED(DGUS_USERCONFIRM)
if (confirm_return_screen == DGUS_ScreenID::BOOT)
confirm_return_screen = getCurrentScreen();
strcpy(dgus_sdcard_handler.filenames[0], msg);
dgus_sdcard_handler.filenames[1][0] = '\0';
dgus_sdcard_handler.filenames[2][0] = '\0';
dgus_sdcard_handler.filenames[3][0] = '\0';
strcpy(dgus_sdcard_handler.filenames[4], "[");
strcat(dgus_sdcard_handler.filenames[4], GET_TEXT(MSG_BUTTON_CONFIRM));
strcat(dgus_sdcard_handler.filenames[4], "]");
new_screen = DGUS_ScreenID::FILE1;
#if ALL(DEBUG_OUT, DGUS_SCREEN_PAGE_DEBUG)
DEBUG_ECHOLNPGM("trig confirm: ", msg, ", ret: ", (uint16_t)confirm_return_screen);
#endif
#else
UNUSED(msg);
#endif
}
void DGUSScreenHandler::userConfirmation() {
#if ENABLED(DGUS_USERCONFIRM)
if (confirm_return_screen == DGUS_ScreenID::BOOT) {
DEBUG_ECHOLNPGM("DGUS: User confirmation triggered but no return screen");
return;
}
if (confirm_return_screen >= DGUS_ScreenID::FILE1 && confirm_return_screen <= DGUS_ScreenID::FILE4)
dgus_sdcard_handler.onPageLoad(DGUS_SCREEN_TO_PAGE(confirm_return_screen));
#ifdef DEBUG_DGUSLCD
DEBUG_ECHOLNPGM("trig confirmed, ret:", (uint16_t)confirm_return_screen);
#endif
new_screen = confirm_return_screen;
confirm_return_screen = DGUS_ScreenID::BOOT;
ExtUI::setUserConfirmed();
#endif
}
void DGUSScreenHandler::settingsReset() {
config.initialized = true;
config.volume = DGUS_DEFAULT_VOLUME;
config.brightness = DGUS_DEFAULT_BRIGHTNESS;
config.language = DGUS_Data::Language::Default;
config.jogLength = DGUS_Data::AxisControlCommand::Jog_1mm;
config.plaExtruderTemp = PREHEAT_1_TEMP_HOTEND;
config.plaBedTemp = PREHEAT_1_TEMP_BED;
config.absExtruderTemp = PREHEAT_2_TEMP_HOTEND;
config.absBedTemp = PREHEAT_2_TEMP_BED;
config.levelingEnabled = ExtUI::getLevelingActive();
if (!settings_ready) {
settings_ready = true;
ready();
}
setStatusMessage(GET_TEXT_F(DGUS_MSG_RESET_EEPROM));
}
void DGUSScreenHandler::storeSettings(char *buff) {
static_assert(sizeof(config) <= ExtUI::eeprom_data_size, "sizeof(eeprom_data_t) > eeprom_data_size.");
config.initialized = true;
config.volume = dgus.getVolume();
config.brightness = dgus.getBrightness();
memcpy(buff, &config, sizeof(config));
}
void DGUSScreenHandler::loadSettings(const char *buff) {
static_assert(sizeof(config) <= ExtUI::eeprom_data_size, "sizeof(eeprom_data_t) > eeprom_data_size.");
memcpy(&config, buff, sizeof(config));
if (!config.initialized
|| config.language < DGUS_Data::Language::Chinese_Simplified
|| config.language > DGUS_Data::Language::Turkish
|| config.jogLength < DGUS_Data::AxisControlCommand::Jog_10mm
|| config.jogLength > DGUS_Data::AxisControlCommand::Jog_0_1mm) {
DEBUG_ECHOLNPGM("invalid DGUS settings, resetting");
settingsReset();
}
ExtUI::setLevelingActive(config.levelingEnabled);
dgus.setVolume(config.volume);
dgus.setBrightness(config.brightness);
}
void DGUSScreenHandler::configurationStoreWritten(bool success) {
if (!success)
setStatusMessage(GET_TEXT_F(DGUS_MSG_WRITE_EEPROM_FAILED));
}
void DGUSScreenHandler::configurationStoreRead(bool success) {
if (!success) {
setStatusMessage(GET_TEXT_F(DGUS_MSG_READ_EEPROM_FAILED));
}
else if (!settings_ready) {
settings_ready = true;
ready();
}
}
void DGUSScreenHandler::playTone(const uint16_t frequency, const uint16_t duration/*=0*/) {
if (WITHIN(frequency, 1, 255)) {
if (WITHIN(duration, 1, 255))
dgus.playSound((uint8_t)frequency, (uint8_t)duration);
else
dgus.playSound((uint8_t)frequency);
}
}
void DGUSScreenHandler::angryBeeps(const uint8_t beepCount) {
angry_beeps = beepCount;
}
void DGUSScreenHandler::levelingStart() {
isLeveling = true;
currentMeshPointIndex = 0;
triggerFullUpdate();
}
void DGUSScreenHandler::levelingEnd() {
if (!isLeveling) return;
#if ENABLED(DEBUG_DGUSLCD)
DEBUG_ECHOLNPGM("levelingEnd(), valid=", ExtUI::getLevelingIsValid());
#endif
isLeveling = false;
currentMeshPointIndex = 0;
triggerFullUpdate();
}
void DGUSScreenHandler::meshUpdate(const int8_t xpos, const int8_t ypos) {
if (!isLeveling) return;
currentMeshPointIndex++;
triggerFullUpdate();
}
void DGUSScreenHandler::printTimerStarted() {
TERN_(HAS_FILAMENT_SENSOR, ExtUI::setFilamentRunoutState(false));
triggerScreenChange(DGUS_ScreenID::PAUSE);
}
void DGUSScreenHandler::printTimerPaused() {
dgus.playSound(3);
triggerScreenChange(DGUS_ScreenID::RESUME);
}
void DGUSScreenHandler::printTimerStopped() {
dgus.playSound(3);
triggerScreenChange(DGUS_ScreenID::FINISH);
}
void DGUSScreenHandler::filamentRunout(const ExtUI::extruder_t extruder) {
triggerScreenChange(DGUS_ScreenID::FILAMENTUSEUP);
dgus.playSound(3);
}
ssize_t DGUSScreenHandler::getScrollIndex() {
return currentScrollIndex;
}
void DGUSScreenHandler::addCurrentPageStringLength(size_t stringLength, size_t textControlLength) {
NOLESS(pageMaxStringLen, stringLength);
NOLESS(pageMaxControlLen, textControlLength);
}
#if HAS_MEDIA
void DGUSScreenHandler::sdCardInserted() {}
void DGUSScreenHandler::sdCardRemoved() {
sdPrintFilename = noFileSelected;
if (WITHIN(getCurrentScreen(), DGUS_ScreenID::FILE1, DGUS_ScreenID::FILE4))
triggerTempScreenChange(DGUS_ScreenID::SDCARDCHECK, DGUS_ScreenID::HOME);
}
void DGUSScreenHandler::sdCardError() {}
#endif // HAS_MEDIA
#if ENABLED(POWER_LOSS_RECOVERY)
void DGUSScreenHandler::powerLossResume() {
powerLossRecoveryAvailable = true;
}
#endif
#if HAS_PID_HEATING
void DGUSScreenHandler::pidTuning(const ExtUI::result_t rst) {
dgus.playSound(3);
}
#endif
void DGUSScreenHandler::steppersStatusChanged(bool steppersEnabled) {
refreshVP(DGUS_Addr::AXIS_StepperStatus);
}
void DGUSScreenHandler::homingDone() {
if (isOnTempScreen(DGUS_ScreenID::AUTOHOME))
triggerReturnScreen();
}
void DGUSScreenHandler::startPrintFromSD(const char* const filename) {
triggerScreenChange(DGUS_ScreenID::HOME);
sdPrintFilename = filename;
setStatusMessage(sdPrintFilename, 0);
}
void DGUSScreenHandler::setStatusMessage(FSTR_P msg, const millis_t duration) {
setStatusMessage(FTOP(msg), duration);
}
void DGUSScreenHandler::setStatusMessage(const char* msg, const millis_t duration) {
homeStatusMessage[0] = '\0';
strncat(homeStatusMessage, msg, sizeof(homeStatusMessage) / sizeof(char) - 1);
status_expire = duration ? ExtUI::safe_millis() + duration : 0;
}
DGUS_ScreenID DGUSScreenHandler::getCurrentScreen() { return current_screen; }
void DGUSScreenHandler::homeThenChangeScreen(DGUS_ScreenID screen) {
triggerTempScreenChange(DGUS_ScreenID::AUTOHOME, screen);
ExtUI::injectCommands(F("G28"));
}
void DGUSScreenHandler::triggerScreenChange(DGUS_ScreenID screen) {
if (confirm_return_screen != DGUS_ScreenID::BOOT)
confirm_return_screen = screen;
else
new_screen = screen;
wait_return_screen = DGUS_ScreenID::BOOT; // cancel temp screen
#if ALL(DEBUG_OUT, DGUS_SCREEN_PAGE_DEBUG)
DEBUG_ECHOLNPGM("trig scr: ", (uint16_t)screen);
#endif
}
void DGUSScreenHandler::triggerTempScreenChange(DGUS_ScreenID screen, DGUS_ScreenID returnScreen) {
if (confirm_return_screen != DGUS_ScreenID::BOOT)
confirm_return_screen = screen;
else
new_screen = screen;
wait_return_screen = returnScreen;
#if ALL(DEBUG_OUT, DGUS_SCREEN_PAGE_DEBUG)
DEBUG_ECHOLNPGM("trig tmp: ", (uint16_t)screen, " ret: ", (uint16_t)returnScreen);
#endif
}
void DGUSScreenHandler::triggerReturnScreen() {
new_screen = wait_return_screen;
wait_return_screen = DGUS_ScreenID::BOOT;
#if ALL(DEBUG_OUT, DGUS_SCREEN_PAGE_DEBUG)
DEBUG_ECHOLNPGM("trig ret scr");
#endif
}
bool DGUSScreenHandler::isOnTempScreen(DGUS_ScreenID screen) {
return wait_return_screen != DGUS_ScreenID::BOOT
&& (screen == DGUS_ScreenID::BOOT || current_screen == screen);
}
void DGUSScreenHandler::triggerFullUpdate() {
full_update = true;
}
void DGUSScreenHandler::triggerEEPROMSave() {
eeprom_save = ExtUI::safe_millis() + 500;
}
bool DGUSScreenHandler::isPrinterIdle() {
return (!ExtUI::commandsInQueue() && !ExtUI::isMoving());
}
const DGUS_Addr* DGUSScreenHandler::findScreenAddrList(DGUS_ScreenID screen) {
DGUS_ScreenAddrList list;
const DGUS_ScreenAddrList *map = screen_addr_list_map;
do {
memcpy_P(&list, map, sizeof(*map));
if (!list.addr_list) break;
if (list.screen == screen) return list.addr_list;
} while (++map);
return nullptr;
}
bool DGUSScreenHandler::callScreenSetup(DGUS_ScreenID screen) {
DGUS_ScreenSetup setup;
const DGUS_ScreenSetup *list = screen_setup_list;
do {
memcpy_P(&setup, list, sizeof(*list));
if (!setup.setup_fn) break;
if (setup.screen == screen) return setup.setup_fn();
} while (++list);
return true;
}
void DGUSScreenHandler::moveToScreen(DGUS_ScreenID screen, bool abort_wait) {
current_screen = screen;
if (!callScreenSetup(screen)) return;
if (!sendScreenVPData(screen, true)) return;
dgus.switchScreen(current_screen);
}
bool DGUSScreenHandler::sendScreenVPData(DGUS_ScreenID screen, bool complete_update) {
if (complete_update) full_update = false;
const DGUS_Addr *list = findScreenAddrList(screen);
while (true) {
if (!list) return true; // Nothing left to send
const uint16_t addr = pgm_read_word(list++);
if (!addr) return true; // Nothing left to send
DGUS_VP vp;
if (!DGUS_PopulateVP((DGUS_Addr)addr, &vp)) continue; // Invalid VP
if (!vp.tx_handler) continue; // Nothing to send
if (!complete_update
&& !(vp.flags & VPFLAG_AUTOUPLOAD)
&& TERN1(DGUS_SOFTWARE_AUTOSCROLL, !(vp.flags & VPFLAG_TXSTRING_AUTOSCROLL))
) continue; // Unnecessary VP
uint8_t expected_tx = 6 + vp.size; // 6 bytes header + payload.
const millis_t try_until = ExtUI::safe_millis() + 1000;
while (expected_tx > dgus.getFreeTxBuffer()) {
if (ELAPSED(ExtUI::safe_millis(), try_until)) return false; // Stop trying after 1 second
dgus.flushTx(); // Flush the TX buffer
delay(50);
}
vp.tx_handler(vp);
}
}
bool DGUSScreenHandler::refreshVP(DGUS_Addr vpAddr) {
const DGUS_Addr *list = findScreenAddrList(current_screen);
while (list && (uint16_t)*list) {
if (*list == vpAddr) {
DGUS_VP vp;
if (!DGUS_PopulateVP((DGUS_Addr)vpAddr, &vp) || !vp.tx_handler)
return false;
vp.tx_handler(vp);
return true;
}
list++;
}
return false;
}
#endif // DGUS_LCD_UI_E3S1PRO