Skip to content

Commit

Permalink
Fix #1295 - Crash when reloading WebUI while running
Browse files Browse the repository at this point in the history
  • Loading branch information
MitchBradley committed Aug 28, 2024
1 parent 2f7cc0e commit 87126a9
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 26 deletions.
Binary file modified FluidNC/data/index.html.gz
Binary file not shown.
6 changes: 3 additions & 3 deletions FluidNC/src/HashFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void HashFS::delete_file(const std::filesystem::path& path, bool report) {
}
}

bool HashFS::file_is_hashed(const std::filesystem::path& path) {
bool HashFS::file_is_hashable(const std::filesystem::path& path) {
int count = 0;
for (auto it = path.begin(); it != path.end(); ++it) {
++count;
Expand All @@ -70,7 +70,7 @@ bool HashFS::file_is_hashed(const std::filesystem::path& path) {
}

void HashFS::rehash_file(const std::filesystem::path& path, bool report) {
if (file_is_hashed(path)) {
if (file_is_hashable(path)) {
std::string hash;
if (hashFile(path, hash) != Error::Ok) {
delete_file(path, false);
Expand Down Expand Up @@ -108,7 +108,7 @@ void HashFS::hash_all() {
}
}
std::string HashFS::hash(const std::filesystem::path& path) {
if (file_is_hashed(path)) {
if (file_is_hashable(path)) {
std::map<std::string, std::string>::const_iterator it;
it = localFsHashes.find(path.filename());
if (it != localFsHashes.end()) {
Expand Down
2 changes: 1 addition & 1 deletion FluidNC/src/HashFS.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class HashFS {
public:
static std::map<std::string, std::string> localFsHashes;

static bool file_is_hashed(const std::filesystem::path& path);
static bool file_is_hashable(const std::filesystem::path& path);
static void delete_file(const std::filesystem::path& path, bool report = true);
static void rehash_file(const std::filesystem::path& path, bool report = true);
static void rename_file(const std::filesystem::path& ipath, const std::filesystem::path& opath, bool report = true);
Expand Down
6 changes: 3 additions & 3 deletions FluidNC/src/Machine/Macros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ Cmd findOverride(std::string name) {
}

bool Macro::run(Channel* channel) {
if (channel) {
log_debug_to(*channel, "Run " << name());
}
if (_gcode.length()) {
if (channel) {
log_debug_to(*channel, "Run " << name() << ": " << _gcode);
}
Job::save();
Job::nest(new MacroChannel(this), channel);
return true;
Expand Down
2 changes: 1 addition & 1 deletion FluidNC/src/ProcessSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,7 @@ Error execute_line(char* line, Channel& channel, AuthenticationLevel auth_level)
return Error::SystemGcLock;
}
Error result = gc_execute_line(line);
if (result != Error::Ok) {
if (result != Error::Ok && result != Error::Reset) {
log_debug_to(channel, "Bad GCode: " << line);
}
return result;
Expand Down
94 changes: 76 additions & 18 deletions FluidNC/src/WebUI/WebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ namespace WebUI {
_webserver->on("/command", HTTP_ANY, handle_web_command);
_webserver->on("/command_silent", HTTP_ANY, handle_web_command_silent);
_webserver->on("/feedhold_reload", HTTP_ANY, handleFeedholdReload);
_webserver->on("/cyclestart_reload", HTTP_ANY, handleCyclestartReload);
_webserver->on("/restart_reload", HTTP_ANY, handleRestartReload);
_webserver->on("/did_restart", HTTP_ANY, handleDidRestart);

//LocalFS
_webserver->on("/files", HTTP_ANY, handleFileList, LocalFSFileupload);
Expand Down Expand Up @@ -230,6 +233,22 @@ namespace WebUI {
return false;
}

// If you load or reload WebUI while a program is running, there is a high
// risk of stalling the motion because serving a file from
// the local FLASH filesystem takes away a lot of CPU cycles. If we get
// a request for a file when running, reject it to preserve the motion
// integrity.
// This can make it hard to debug ISR IRAM problems, because the easiest
// way to trigger such problems is to refresh WebUI during motion.
if (http_block_during_motion->get() && inMotionState()) {
Web_Server::handleReloadBlocked();
return true;
}
if (state_is(State::Hold)) {
Web_Server::handleFeedholdBlocked();
return true;
}

std::string hash;
// Check for brower cache match

Expand All @@ -241,21 +260,9 @@ namespace WebUI {
}

if (hash.length() && std::string(_webserver->header("If-None-Match").c_str()) == hash) {
log_debug(path << " is cached");
_webserver->send(304);
return true;
}
// If you load or reload WebUI while a program is running, there is a high
// risk of stalling the motion because serving a file from
// the local FLASH filesystem takes away a lot of CPU cycles. If we get
// a request for a file when running, reject it to preserve the motion
// integrity.
// This can make it hard to debug ISR IRAM problems, because the easiest
// way to trigger such problems is to refresh WebUI during motion.
if (http_block_during_motion->get() && inMotionState()) {
Web_Server::handleReloadBlocked();
return true;
}

bool isGzip = false;
FileStream* file;
Expand Down Expand Up @@ -646,25 +653,76 @@ namespace WebUI {

// This page is used when you try to reload WebUI during motion,
// to avoid interrupting that motion. It lets you wait until
// motion is finished or issue a feedhold.
// motion is finished.
void Web_Server::handleReloadBlocked() {
_webserver->send(503,
"text/html",
"<!DOCTYPE html><html><body>"
"<h3>Cannot load WebUI while moving</h3>"
"<button onclick='window.location.reload()'>Retry</button>"
"&nbsp;Retry (you must first wait for motion to finish)<br><br>"
"<button onclick='window.location.replace(\"/feedhold_reload\")'>Feedhold</button>"
"&nbsp;Stop the motion with feedhold and then retry<br>"
"<h3>Cannot load WebUI while GCode Program is Running</h3>"

"<button onclick='window.location.replace(\"/feedhold_reload\")'>Pause</button>"
"&nbsp;Pause the GCode program with feedhold<br><br>"

"<button onclick='window.location.replace(\"/restart_reload\")'>Stop</button>"
"&nbsp;Stop the GCode Program with reset<br><br>"

"<button onclick='window.location.reload()'>Reload WebUI</button>"
"&nbsp;(You must first stop the GCode program or wait for it to finish)<br><br>"

"</body></html>");
}
// This page is used when you try to reload WebUI during feedhold state.
// Reload will not work because commands cannot be executed in feedhold,
// so things like ESP800 will hang.
void Web_Server::handleFeedholdBlocked() {
_webserver->send(503,
"text/html",
"<!DOCTYPE html><html><body>"
"<h3>GCode Program is Paused (in Feedhold state)</h3>"

"<button onclick='window.location.replace(\"/cyclestart_reload\")'>Resume</button>"
"&nbsp;Resume the GCode program with cyclestart<br><br>"

"<button onclick='window.location.replace(\"/restart_reload\")'>Stop</button>"
"&nbsp;Stop the GCode Program with reset<br><br>"

"</body></html>");
}
void Web_Server::handleDidRestart() {
_webserver->send(503,
"text/html",
"<!DOCTYPE html><html><body>"
"<h3>GCode Program has been stopped</h3>"
"<button onclick='window.location.replace(\"/\")'>Reload WebUI</button>"
"</body></html>");
}
// This page issues a feedhold to pause the motion then retries the WebUI reload
void Web_Server::handleFeedholdReload() {
protocol_send_event(&feedHoldEvent);
// delay(100);
// delay(100);
// Go to the main page
_webserver->sendHeader(LOCATION_HEADER, "/");
_webserver->send(302);
}
// This page issues a feedhold to pause the motion then retries the WebUI reload
void Web_Server::handleCyclestartReload() {
protocol_send_event(&cycleStartEvent);
// delay(100);
// delay(100);
// Go to the main page
_webserver->sendHeader(LOCATION_HEADER, "/");
_webserver->send(302);
}
// This page issues a feedhold to pause the motion then retries the WebUI reload
void Web_Server::handleRestartReload() {
protocol_send_event(&rtResetEvent);
// delay(100);
// delay(100);
// Go to the main page
_webserver->sendHeader(LOCATION_HEADER, "/did_restart");
_webserver->send(302);
}

//push error code and message to websocket. Used by upload code
void Web_Server::pushError(int code, const char* st, bool web_error, uint16_t timeout) {
Expand Down
4 changes: 4 additions & 0 deletions FluidNC/src/WebUI/WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ namespace WebUI {
static void handle_Websocket_Event(uint8_t num, uint8_t type, uint8_t* payload, size_t length);
static void handle_Websocketv3_Event(uint8_t num, uint8_t type, uint8_t* payload, size_t length);
static void handleReloadBlocked();
static void handleFeedholdBlocked();
static void handleFeedholdReload();
static void handleCyclestartReload();
static void handleRestartReload();
static void handleDidRestart();
static void LocalFSFileupload();
static void handleFileList();
static void handleUpdate();
Expand Down

0 comments on commit 87126a9

Please sign in to comment.