Skip to content

Commit

Permalink
Merge branch 'main' into WebUI3
Browse files Browse the repository at this point in the history
  • Loading branch information
MitchBradley authored Jan 26, 2024
2 parents 1e26fc0 + 09fd572 commit e8394a4
Show file tree
Hide file tree
Showing 74 changed files with 1,721 additions and 833 deletions.
Binary file modified FluidNC/data/index.html.gz
Binary file not shown.
5 changes: 4 additions & 1 deletion FluidNC/esp32/StartupLog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ void StartupLog::dump(Channel& out) {
std::string line;
while (i < _len) {
char c = _messages[i++];
if (c == '\r') {
continue;
}
if (c == '\n') {
break;
}
line += c;
}
log_to(out, line);
log_stream(out, line);
}
}

Expand Down
228 changes: 202 additions & 26 deletions FluidNC/src/Channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include "Channel.h"
#include "Report.h" // report_gcode_modes
#include "Machine/MachineConfig.h" // config
#include "Serial.h" // execute_realtime_command
#include "RealtimeCmd.h" // execute_realtime_command
#include "Limits.h"
#include "Logging.h"
#include <string_view>

void Channel::flushRx() {
_linelen = 0;
Expand Down Expand Up @@ -101,17 +103,19 @@ void Channel::autoReportGCodeState() {
}
void Channel::autoReport() {
if (_reportInterval) {
auto limitState = limits_get_state();
auto probeState = config->_probe->get_state();
if (_reportWco || sys.state != _lastState || limitState != _lastLimits || probeState != _lastProbe ||
if (probeState != _lastProbe) {
report_recompute_pin_string();
}
if (_reportWco || sys.state != _lastState || probeState != _lastProbe || _lastPinString != report_pin_string ||
(motionState() && (int32_t(xTaskGetTickCount()) - _nextReportTime) >= 0)) {
if (_reportWco) {
report_wco_counter = 0;
}
_reportWco = false;
_lastState = sys.state;
_lastLimits = limitState;
_lastProbe = probeState;
_reportWco = false;
_lastState = sys.state;
_lastProbe = probeState;
_lastPinString = report_pin_string;

_nextReportTime = xTaskGetTickCount() + _reportInterval;
report_realtime_status(*this);
Expand All @@ -124,43 +128,130 @@ void Channel::autoReport() {
}
}

void Channel::pin_event(uint32_t pinnum, bool active) {
try {
auto event_pin = _events.at(pinnum);
*_pin_values[pinnum] = active;
event_pin->trigger(active);
} catch (std::exception& ex) {}
}

void Channel::handleRealtimeCharacter(uint8_t ch) {
uint32_t cmd;

int res = _utf8.decode(ch, cmd);
if (res == -1) {
// This can be caused by line noise on an unpowered pendant
log_debug("UTF8 decoding error");
_active = false;
return;
}
if (res == 0) {
return;
}
// Otherwise res==1 and we have decoded a sequence so proceed

_active = true;
if (cmd == PinACK) {
log_debug("ACK");
_ackwait = false;
return;
}
if (cmd == PinNAK) {
log_error("Channel device rejected config");
log_debug("NAK");
_ackwait = false;
return;
}

if (cmd >= PinLowFirst && cmd < PinLowLast) {
pin_event(cmd - PinLowFirst, false);
return;
}
if (cmd >= PinHighFirst && cmd < PinHighLast) {
pin_event(cmd - PinHighFirst, true);
return;
}
execute_realtime_command(static_cast<Cmd>(cmd), *this);
}

void Channel::push(uint8_t byte) {
if (is_realtime_command(byte)) {
handleRealtimeCharacter(byte);
} else {
_queue.push(byte);
}
}

Channel* Channel::pollLine(char* line) {
handle();
while (1) {
int ch;
int ch = -1;
if (line && _queue.size()) {
ch = _queue.front();
_queue.pop();
} else {
ch = read();
if (ch < 0) {
break;
}
if (realtimeOkay(ch) && is_realtime_command(ch)) {
handleRealtimeCharacter((uint8_t)ch);
continue;
}
if (!line) {
_queue.push(ch);
continue;
}
// Fall through if line is non-null and it is not a realtime character
}

// ch will only be negative if read() was called and returned -1
// The _queue path will return only nonnegative character values
if (ch < 0) {
break;
}
if (realtimeOkay(ch) && is_realtime_command(ch)) {
execute_realtime_command(static_cast<Cmd>(ch), *this);
continue;
}
if (!line) {
// If we are not able to handle a line we save the character
// until later
_queue.push(uint8_t(ch));
continue;
}
if (line && lineComplete(line, ch)) {
if (lineComplete(line, ch)) {
return this;
}
}
autoReport();
if (_active) {
autoReport();
}
return nullptr;
}

void Channel::setAttr(int index, bool* value, const std::string& attrString, const char* tag) {
if (value) {
_pin_values[index] = value;
}
out_acked(attrString, tag);
}

void Channel::out(const char* s, const char* tag) {
sendLine(MsgLevelNone, s);
}

void Channel::out(const std::string& s, const char* tag) {
sendLine(MsgLevelNone, s);
}

void Channel::out_acked(const std::string& s, const char* tag) {
out(s, tag);
}

void Channel::ready() {
#if 0
// At the moment this is unnecessary because initializing
// an input pin triggers an initial value event
if (!_pin_values.empty()) {
out("GET: io.*");
}
#endif
}

void Channel::registerEvent(uint8_t code, EventPin* obj) {
_events[code] = obj;
}

void Channel::ack(Error status) {
if (status == Error::Ok) {
log_to(*this, "ok");
sendLine(MsgLevelNone, "ok");
return;
}
// With verbose errors, the message text is displayed instead of the number.
Expand All @@ -173,3 +264,88 @@ void Channel::ack(Error status) {
msg << static_cast<int>(status);
}
}

void Channel::print_msg(MsgLevel level, const char* msg) {
if (_message_level >= level) {
println(msg);
}
}

// This overload is used primarily with fixed string
// values. It sends a pointer to the string whose
// memory does not need to be reclaimed later.
// This is the most efficient form, but it only works
// with fixed messages.
void Channel::sendLine(MsgLevel level, const char* line) {
if (outputTask) {
LogMessage msg { this, (void*)line, level, false };
while (!xQueueSend(message_queue, &msg, 10)) {}
} else {
print_msg(level, line);
}
}

// This overload is used primarily with log_*() where
// a std::string is dynamically allocated with "new",
// and then extended to construct the message. Its
// pointer is sent to the output task, which sends
// the message to the output channel and then "delete"s
// the pointer to reclaim the memory.
// This form has intermediate efficiency, as the string
// is allocated once and freed once.
void Channel::sendLine(MsgLevel level, const std::string* line) {
if (outputTask) {
LogMessage msg { this, (void*)line, level, true };
while (!xQueueSend(message_queue, &msg, 10)) {}
} else {
print_msg(level, line->c_str());
delete line;
}
}

// This overload is used for many miscellaneous messages
// where the std::string is allocated in a code block and
// then extended with various information. This send_line()
// copies that string to a newly allocated one and sends that
// via the std::string* version of send_line(). The original
// string is freed by the caller sometime after send_line()
// returns, while the new string is freed by the output task
// after the message is forwared to the output channel.
// This is the least efficient form, requiring two strings
// to be allocated and freed, with an intermediate copy.
// It is used only rarely.
void Channel::sendLine(MsgLevel level, const std::string& line) {
if (outputTask) {
sendLine(level, new std::string(line));
} else {
print_msg(level, line.c_str());
}
}

bool Channel::is_visible(const std::string& stem, const std::string& extension, bool isdir) {
if (stem.length() && stem[0] == '.') {
// Exclude hidden files and directories
return false;
}
if (stem == "System Volume Information") {
// Exclude a common SD card metadata subdirectory
return false;
}
if (isdir) {
return true;
}
std::string_view extensions(_gcode_extensions);
int pos = 0;
while (extensions.length()) {
auto next_pos = extensions.find_first_of(' ', pos);
std::string_view next_extension = extensions.substr(0, next_pos);
if (extension == next_extension) {
return true;
}
if (next_pos == extensions.npos) {
break;
}
extensions.remove_prefix(next_pos + 1);
}
return false;
}
Loading

0 comments on commit e8394a4

Please sign in to comment.