Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1295 - Crash when reloading WebUI while running #1334

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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
Loading