From eb1ac889a512d232941d82078769ce910c57035e Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Thu, 11 Mar 2021 15:27:35 +0100 Subject: [PATCH 1/7] Add support for per-serial feature querying --- Marlin/src/core/macros.h | 11 ++++++++++ Marlin/src/core/serial_base.h | 24 +++++++++++++++++---- Marlin/src/core/serial_hook.h | 39 ++++++++++++++++++++++++---------- Marlin/src/feature/meatpack.h | 2 ++ Marlin/src/gcode/host/M115.cpp | 6 ++++-- 5 files changed, 65 insertions(+), 17 deletions(-) diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index d7043ba5234d..16539b1ffd35 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -318,6 +318,17 @@ #endif + // Allow manipulating enumeration value like flags without ugly cast everywhere + #define ENUM_FLAGS(T) \ + FORCE_INLINE constexpr T operator&(T x, T y) { return static_cast(static_cast(x) & static_cast(y)); } \ + FORCE_INLINE constexpr T operator|(T x, T y) { return static_cast(static_cast(x) | static_cast(y)); } \ + FORCE_INLINE constexpr T operator^(T x, T y) { return static_cast(static_cast(x) ^ static_cast(y)); } \ + FORCE_INLINE constexpr T operator~(T x) { return static_cast(~static_cast(x)); } \ + FORCE_INLINE T & operator&=(T &x, T y) { return x = x & y; } \ + FORCE_INLINE T & operator|=(T &x, T y) { return x = x | y; } \ + FORCE_INLINE T & operator^=(T &x, T y) { return x = x ^ y; } + + // C++11 solution that is standard compliant. is not available on all platform namespace Private { template struct enable_if { }; diff --git a/Marlin/src/core/serial_base.h b/Marlin/src/core/serial_base.h index f8fe3a181d5f..bc7390d5908d 100644 --- a/Marlin/src/core/serial_base.h +++ b/Marlin/src/core/serial_base.h @@ -45,10 +45,6 @@ struct serial_index_t { constexpr serial_index_t() : index(-1) {} }; -// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is. -CALL_IF_EXISTS_IMPL(void, flushTX); -CALL_IF_EXISTS_IMPL(bool, connected, true); - // In order to catch usage errors in code, we make the base to encode number explicit // If given a number (and not this enum), the compiler will reject the overload, falling back to the (double, digit) version // We don't want hidden conversion of the first parameter to double, so it has to be as hard to do for the compiler as creating this enum @@ -59,6 +55,23 @@ enum class PrintBase { Bin = 2 }; +// A simple feature list enumeration +enum class SerialFeature { + None = 0x00, + MeatPack = 0x01, //!< Enabled when Meatpack is present + BinaryFileTransfer = 0x02, //!< Enabled for BinaryFile transfer support (in the future) + Virtual = 0x04, //!< Enabled for virtual serial port (like Telnet / Websocket / ...) + Hookable = 0x08, //!< Enabled if the serial class supports a setHook method +}; +ENUM_FLAGS(SerialFeature); + +// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is. +CALL_IF_EXISTS_IMPL(void, flushTX); +CALL_IF_EXISTS_IMPL(bool, connected, true); +CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None); + + + // A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than // double or float. For double or float, a conversion exists so the call will be transparent struct EnsureDouble { @@ -100,6 +113,9 @@ struct SerialBase { /** Read a value from the port @param index The port index, usually 0 */ int read(serial_index_t index = 0) { return static_cast(this)->read(index); } + /** Combine the feature of this serial instance and returns it + @param index The port index, usually 0 */ + SerialFeature features(serial_index_t index = 0) const { return static_cast(this)->features(index); } // Check if the serial port is connected (usually bypassed) bool connected() { return static_cast(this)->connected(); } // Redirect flush diff --git a/Marlin/src/core/serial_hook.h b/Marlin/src/core/serial_hook.h index c687891fb407..1eed18bc319d 100644 --- a/Marlin/src/core/serial_hook.h +++ b/Marlin/src/core/serial_hook.h @@ -60,10 +60,13 @@ struct BaseSerial : public SerialBase< BaseSerial >, public SerialT { void msgDone() {} // We don't care about indices here, since if one can call us, it's the right index anyway - int available(serial_index_t) { return (int)SerialT::available(); } - int read(serial_index_t) { return (int)SerialT::read(); } - bool connected() { return CALL_IF_EXISTS(bool, static_cast(this), connected);; } - void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + int available(serial_index_t) { return (int)SerialT::available(); } + int read(serial_index_t) { return (int)SerialT::read(); } + bool connected() { return CALL_IF_EXISTS(bool, static_cast(this), connected);; } + void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + + SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, static_cast(this), features, index); } + // We have 2 implementation of the same method in both base class, let's say which one we want using SerialT::available; @@ -102,6 +105,7 @@ struct ConditionalSerial : public SerialBase< ConditionalSerial > { int read(serial_index_t ) { return (int)out.read(); } int available() { return (int)out.available(); } int read() { return (int)out.read(); } + SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); } ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {} }; @@ -119,13 +123,14 @@ struct ForwardSerial : public SerialBase< ForwardSerial > { void msgDone() {} // Existing instances implement Arduino's operator bool, so use that if it's available - bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } - void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } + bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } + void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } - int available(serial_index_t) { return (int)out.available(); } - int read(serial_index_t) { return (int)out.read(); } - int available() { return (int)out.available(); } - int read() { return (int)out.read(); } + int available(serial_index_t) { return (int)out.available(); } + int read(serial_index_t) { return (int)out.read(); } + int available() { return (int)out.available(); } + int read() { return (int)out.read(); } + SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); } ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {} }; @@ -165,7 +170,10 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial >, public Seria bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, static_cast(this), connected) : static_cast(this)->operator bool(); } - void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + // Append Hookable for this class + SerialFeature features(serial_index_t index) const { return SerialFeature::Hookable | CALL_IF_EXISTS(SerialFeature, static_cast(this), features, index); } + void setHook(WriteHook writeHook = 0, EndOfMessageHook eofHook = 0, void * userPointer = 0) { // Order is important here as serial code can be called inside interrupts @@ -251,6 +259,15 @@ struct MultiSerial : public SerialBase< MultiSerial> { // Existing instances implement Arduino's operator bool, so use that if it's available bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } + SerialFeature features(serial_index_t index) const { return SerialFeature::MeatPack | CALL_IF_EXISTS(SerialFeature, &out, features, index); } + int available(serial_index_t index) { if (charCount) return charCount; // The buffer still has data diff --git a/Marlin/src/gcode/host/M115.cpp b/Marlin/src/gcode/host/M115.cpp index cd6fcbaf8642..8560de6b168b 100644 --- a/Marlin/src/gcode/host/M115.cpp +++ b/Marlin/src/gcode/host/M115.cpp @@ -22,6 +22,8 @@ #include "../gcode.h" #include "../../inc/MarlinConfig.h" +#include "../queue.h" // for getting the command port + #if ENABLED(M115_GEOMETRY_REPORT) #include "../../module/motion.h" @@ -69,7 +71,7 @@ void GcodeSuite::M115() { cap_line(PSTR("SERIAL_XON_XOFF"), ENABLED(SERIAL_XON_XOFF)); // BINARY_FILE_TRANSFER (M28 B1) - cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER)); + cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER)); // TODO: Replace by (SERIAL_IMPL.features(queue.ring_buffer.command_port()) & SerialFeature::BinaryFileTransfer) == SerialFeature::BinaryFileTransfer once it'll be implemented // EEPROM (M500, M501) cap_line(PSTR("EEPROM"), ENABLED(EEPROM_SETTINGS)); @@ -145,7 +147,7 @@ void GcodeSuite::M115() { cap_line(PSTR("COOLER_TEMPERATURE"), ENABLED(HAS_COOLER)); // MEATPACK Compression - cap_line(PSTR("MEATPACK"), ENABLED(HAS_MEATPACK)); + cap_line(PSTR("MEATPACK"), (SERIAL_IMPL.features(queue.ring_buffer.command_port()) & SerialFeature::MeatPack) == SerialFeature::MeatPack); // Machine Geometry #if ENABLED(M115_GEOMETRY_REPORT) From 6528751d4f921703dd1da98d74464ff34da9c056 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 14 Mar 2021 12:57:06 -0500 Subject: [PATCH 2/7] Update macros.h --- Marlin/src/core/macros.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 16539b1ffd35..865b3fb2e708 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -328,7 +328,6 @@ FORCE_INLINE T & operator|=(T &x, T y) { return x = x | y; } \ FORCE_INLINE T & operator^=(T &x, T y) { return x = x ^ y; } - // C++11 solution that is standard compliant. is not available on all platform namespace Private { template struct enable_if { }; From 29b100674236515af0a2fc6631d3438d4d794caf Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 14 Mar 2021 13:00:00 -0500 Subject: [PATCH 3/7] Update serial_base.h --- Marlin/src/core/serial_base.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Marlin/src/core/serial_base.h b/Marlin/src/core/serial_base.h index bc7390d5908d..9d26f2c4b33c 100644 --- a/Marlin/src/core/serial_base.h +++ b/Marlin/src/core/serial_base.h @@ -70,21 +70,19 @@ CALL_IF_EXISTS_IMPL(void, flushTX); CALL_IF_EXISTS_IMPL(bool, connected, true); CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None); - - -// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than -// double or float. For double or float, a conversion exists so the call will be transparent +// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for +// any type other than double/float. For double/float, a conversion exists so the call will be invisible. struct EnsureDouble { double a; FORCE_INLINE operator double() { return a; } - // If the compiler breaks on ambiguity here, it's likely because you're calling print(X, base) with X not a double or a float, and a - // base that's not one of PrintBase's value. This exact code is made to detect such error, you NEED to set a base explicitely like this: + // If the compiler breaks on ambiguity here, it's likely because print(X, base) is called with X not a double/float, and + // a base that's not a PrintBase value. This code is made to detect the error. You MUST set a base explicitly like this: // SERIAL_PRINT(v, PrintBase::Hex) FORCE_INLINE EnsureDouble(double a) : a(a) {} FORCE_INLINE EnsureDouble(float a) : a(a) {} }; -// Using Curiously Recurring Template Pattern here to avoid virtual table cost when compiling. +// Using Curiously-Recurring Template Pattern here to avoid virtual table cost when compiling. // Since the real serial class is known at compile time, this results in the compiler writing // a completely efficient code. template From 89cb3abd9a506e84448ec5391ba5e9f8007b2d0a Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Fri, 19 Mar 2021 20:37:24 +0100 Subject: [PATCH 4/7] Add support for dumping the current serial configuration on the serial port --- Marlin/src/core/bug_on.h | 6 ++++-- Marlin/src/core/macros.h | 34 +++++++++++++++++++++++++++------- Marlin/src/gcode/gcode_d.cpp | 5 +++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Marlin/src/core/bug_on.h b/Marlin/src/core/bug_on.h index 8869be8d284f..3c9dec0f96e0 100644 --- a/Marlin/src/core/bug_on.h +++ b/Marlin/src/core/bug_on.h @@ -27,10 +27,12 @@ // Useful macro for stopping the CPU on an unexpected condition // This is used like SERIAL_ECHOPAIR, that is: a key-value call of the local variables you want // to dump to the serial port before stopping the CPU. - #define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": "); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0) + // \/ Don't replace by SERIAL_ECHOPAIR since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building + #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLN(": "); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0) #elif ENABLED(MARLIN_DEV_MODE) // Don't stop the CPU here, but at least dump the bug on the serial port - #define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": BUG!\n"); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); } while(0) + // \/ Don't replace by SERIAL_ECHOPAIR since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building + #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLN(": BUG!"); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); } while(0) #else // Release mode, let's ignore the bug #define BUG_ON(V...) NOOP diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 865b3fb2e708..9e0ba740b507 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -367,23 +367,43 @@ return *str ? findStringEnd(str + 1) : str; } - // Check whether a string contains a slash - constexpr bool containsSlash(const char *str) { - return *str == '/' ? true : (*str ? containsSlash(str + 1) : false); + // Check whether a string contains a specific character + constexpr bool contains(const char *str, const char ch) { + return *str == ch ? true : (*str ? contains(str + 1, ch) : false); } - // Find the last position of the slash - constexpr const char* findLastSlashPos(const char* str) { - return *str == '/' ? (str + 1) : findLastSlashPos(str - 1); + // Find the last position of the specific character (should be called with findStringEnd) + constexpr const char* findLastPos(const char* str, const char ch) { + return *str == ch ? (str + 1) : findLastPos(str - 1, ch); } // Compile-time evaluation of the last part of a file path // Typically used to shorten the path to file in compiled strings // CompileTimeString::baseName(__FILE__) returns "macros.h" and not /path/to/Marlin/src/core/macros.h constexpr const char* baseName(const char* str) { - return containsSlash(str) ? findLastSlashPos(findStringEnd(str)) : str; + return contains(str, '/') ? findLastPos(findStringEnd(str), '/') : str; + } + + // Find the first occurence of a character in a string (or return the last position in the string) + constexpr const char* findFirst(const char* str, const char ch) { + return *str == ch || *str == 0 ? (str + 1) : findFirst(str + 1, ch); + } + // Compute the string length at compile time + constexpr unsigned stringLen(const char * str) { + return *str == 0 ? 0 : 1 + stringLen(str+1); } } #define ONLY_FILENAME CompileTimeString::baseName(__FILE__) + /** Get the templated type name. This does not depends on RTTI, but on the preprocessor, so it should be quite safe to use even on old compilers. + WARNING: DO NOT RENAME THIS FUNCTION (or change the text inside the function to match what the preprocessor will generate) + The name is chosen very short since the binary will store "const char* gtn(T*) [with T = YourTypeHere]" so avoid long function name here */ + template + inline const char * gtn(T*) { + // It works on GCC by instantiating __PRETTY_FUNCTION__ and parsing the result. So the syntax here is very limited to GCC output + constexpr unsigned verboseChatLen = sizeof("const char* gtn(T*) [with T = ") - 1; + static char templateType[sizeof(__PRETTY_FUNCTION__) - verboseChatLen] = {}; + __builtin_memcpy(templateType, __PRETTY_FUNCTION__ + verboseChatLen, sizeof(__PRETTY_FUNCTION__) - verboseChatLen - 2); + return templateType; + } #else diff --git a/Marlin/src/gcode/gcode_d.cpp b/Marlin/src/gcode/gcode_d.cpp index a683a330f10d..a41f09b33650 100644 --- a/Marlin/src/gcode/gcode_d.cpp +++ b/Marlin/src/gcode/gcode_d.cpp @@ -167,6 +167,11 @@ dump_delay_accuracy_check(); break; + case 7: // D7 dump the current serial port type (hence configuration) + SERIAL_ECHOLNPGM("Current serial configuration"); + SERIAL_ECHOLN(gtn(&SERIAL_IMPL)); + break; + case 100: { // D100 Disable heaters and attempt a hard hang (Watchdog Test) SERIAL_ECHOLNPGM("Disabling heaters and attempting to trigger Watchdog"); SERIAL_ECHOLNPGM("(USE_WATCHDOG " TERN(USE_WATCHDOG, "ENABLED", "DISABLED") ")"); From 11225214e8c0d28c3a4d0085be3d7db8e8ae2d5d Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Fri, 19 Mar 2021 20:47:59 +0100 Subject: [PATCH 5/7] Add buffer size to the dump --- Marlin/src/gcode/gcode_d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/gcode/gcode_d.cpp b/Marlin/src/gcode/gcode_d.cpp index a41f09b33650..8941523a16b9 100644 --- a/Marlin/src/gcode/gcode_d.cpp +++ b/Marlin/src/gcode/gcode_d.cpp @@ -168,7 +168,7 @@ break; case 7: // D7 dump the current serial port type (hence configuration) - SERIAL_ECHOLNPGM("Current serial configuration"); + SERIAL_ECHOLNPAIR("Current serial configuration RX_BS:", RX_BUFFER_SIZE, ", TX_BS:", TX_BUFFER_SIZE); SERIAL_ECHOLN(gtn(&SERIAL_IMPL)); break; From 3a86faf832d0f6decd9bc75e31bb0aa24ca1cdaa Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 29 Mar 2021 16:37:21 -0500 Subject: [PATCH 6/7] post-merge tweaks --- Marlin/src/core/macros.h | 8 +++---- Marlin/src/core/serial.h | 2 +- Marlin/src/core/serial_base.h | 44 ++++++++++++++++++++++------------ Marlin/src/core/serial_hook.h | 36 +++++++++++++++------------- Marlin/src/gcode/host/M115.cpp | 7 ++++-- 5 files changed, 58 insertions(+), 39 deletions(-) diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 9e0ba740b507..529fae1bf73b 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -324,9 +324,9 @@ FORCE_INLINE constexpr T operator|(T x, T y) { return static_cast(static_cast(x) | static_cast(y)); } \ FORCE_INLINE constexpr T operator^(T x, T y) { return static_cast(static_cast(x) ^ static_cast(y)); } \ FORCE_INLINE constexpr T operator~(T x) { return static_cast(~static_cast(x)); } \ - FORCE_INLINE T & operator&=(T &x, T y) { return x = x & y; } \ - FORCE_INLINE T & operator|=(T &x, T y) { return x = x | y; } \ - FORCE_INLINE T & operator^=(T &x, T y) { return x = x ^ y; } + FORCE_INLINE T & operator&=(T &x, T y) { return x &= y; } \ + FORCE_INLINE T & operator|=(T &x, T y) { return x |= y; } \ + FORCE_INLINE T & operator^=(T &x, T y) { return x ^= y; } // C++11 solution that is standard compliant. is not available on all platform namespace Private { @@ -388,7 +388,7 @@ } // Compute the string length at compile time constexpr unsigned stringLen(const char * str) { - return *str == 0 ? 0 : 1 + stringLen(str+1); + return *str == 0 ? 0 : 1 + stringLen(str + 1); } } diff --git a/Marlin/src/core/serial.h b/Marlin/src/core/serial.h index 2f23e4e3c230..43137f71d739 100644 --- a/Marlin/src/core/serial.h +++ b/Marlin/src/core/serial.h @@ -146,7 +146,7 @@ inline void SERIAL_ECHO(serial_char_t x) { SERIAL_IMPL.write(x.c); } #define AS_CHAR(C) serial_char_t(C) // SERIAL_ECHO_F prints a floating point value with optional precision -inline void SERIAL_ECHO_F(EnsureDouble x, int digit = 2) { SERIAL_IMPL.print(x, digit); } +inline void SERIAL_ECHO_F(EnsureDouble x, int digit=2) { SERIAL_IMPL.print(x, digit); } template void SERIAL_ECHOLN(T x) { SERIAL_IMPL.println(x); } diff --git a/Marlin/src/core/serial_base.h b/Marlin/src/core/serial_base.h index 9d26f2c4b33c..0baad4debfb7 100644 --- a/Marlin/src/core/serial_base.h +++ b/Marlin/src/core/serial_base.h @@ -70,8 +70,8 @@ CALL_IF_EXISTS_IMPL(void, flushTX); CALL_IF_EXISTS_IMPL(bool, connected, true); CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None); -// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for -// any type other than double/float. For double/float, a conversion exists so the call will be invisible. +// A simple forward struct to prevent the compiler from selecting print(double, int) as a default overload +// for any type other than double/float. For double/float, a conversion exists so the call will be invisible. struct EnsureDouble { double a; FORCE_INLINE operator double() { return a; } @@ -96,30 +96,44 @@ struct SerialBase { SerialBase(const bool) {} #endif + #define SerialChild static_cast(this) + // Static dispatch methods below: // The most important method here is where it all ends to: - size_t write(uint8_t c) { return static_cast(this)->write(c); } + size_t write(uint8_t c) { return SerialChild->write(c); } + // Called when the parser finished processing an instruction, usually build to nothing - void msgDone() { static_cast(this)->msgDone(); } - // Called upon initialization - void begin(const long baudRate) { static_cast(this)->begin(baudRate); } - // Called upon destruction - void end() { static_cast(this)->end(); } + void msgDone() const { SerialChild->msgDone(); } + + // Called on initialization + void begin(const long baudRate) { SerialChild->begin(baudRate); } + + // Called on destruction + void end() { SerialChild->end(); } + /** Check for available data from the port @param index The port index, usually 0 */ - int available(serial_index_t index = 0) { return static_cast(this)->available(index); } + int available(serial_index_t index=0) const { return SerialChild->available(index); } + /** Read a value from the port @param index The port index, usually 0 */ - int read(serial_index_t index = 0) { return static_cast(this)->read(index); } - /** Combine the feature of this serial instance and returns it + int read(serial_index_t index=0) { return SerialChild->read(index); } + + /** Combine the features of this serial instance and return it @param index The port index, usually 0 */ - SerialFeature features(serial_index_t index = 0) const { return static_cast(this)->features(index); } + SerialFeature features(serial_index_t index=0) const { return static_cast(this)->features(index); } + + // Check if the serial port has a feature + bool has_feature(serial_index_t index, SerialFeature flag) const { (features(index) & flag) != SerialFeature::None; } + // Check if the serial port is connected (usually bypassed) - bool connected() { return static_cast(this)->connected(); } + bool connected() const { return SerialChild->connected(); } + // Redirect flush - void flush() { static_cast(this)->flush(); } + void flush() { SerialChild->flush(); } + // Not all implementation have a flushTX, so let's call them only if the child has the implementation - void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + void flushTX() { CALL_IF_EXISTS(void, SerialChild, flushTX); } // Glue code here FORCE_INLINE void write(const char* str) { while (*str) write(*str++); } diff --git a/Marlin/src/core/serial_hook.h b/Marlin/src/core/serial_hook.h index 1eed18bc319d..7bc04a5e8865 100644 --- a/Marlin/src/core/serial_hook.h +++ b/Marlin/src/core/serial_hook.h @@ -60,13 +60,12 @@ struct BaseSerial : public SerialBase< BaseSerial >, public SerialT { void msgDone() {} // We don't care about indices here, since if one can call us, it's the right index anyway - int available(serial_index_t) { return (int)SerialT::available(); } - int read(serial_index_t) { return (int)SerialT::read(); } - bool connected() { return CALL_IF_EXISTS(bool, static_cast(this), connected);; } - void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } - - SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, static_cast(this), features, index); } + int available(serial_index_t) { return (int)SerialT::available(); } + int read(serial_index_t) { return (int)SerialT::read(); } + bool connected() { return CALL_IF_EXISTS(bool, static_cast(this), connected);; } + void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, static_cast(this), features, index); } // We have 2 implementation of the same method in both base class, let's say which one we want using SerialT::available; @@ -101,8 +100,8 @@ struct ConditionalSerial : public SerialBase< ConditionalSerial > { bool connected() { return CALL_IF_EXISTS(bool, &out, connected); } void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } - int available(serial_index_t ) { return (int)out.available(); } - int read(serial_index_t ) { return (int)out.read(); } + int available(serial_index_t) { return (int)out.available(); } + int read(serial_index_t) { return (int)out.read(); } int available() { return (int)out.available(); } int read() { return (int)out.read(); } SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); } @@ -123,13 +122,13 @@ struct ForwardSerial : public SerialBase< ForwardSerial > { void msgDone() {} // Existing instances implement Arduino's operator bool, so use that if it's available - bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } - void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } + bool connected() { return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } + void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); } - int available(serial_index_t) { return (int)out.available(); } - int read(serial_index_t) { return (int)out.read(); } - int available() { return (int)out.available(); } - int read() { return (int)out.read(); } + int available(serial_index_t) { return (int)out.available(); } + int read(serial_index_t) { return (int)out.read(); } + int available() { return (int)out.available(); } + int read() { return (int)out.read(); } SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); } ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {} @@ -168,13 +167,16 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial >, public Seria // Underlying implementation might use Arduino's bool operator bool connected() { - return Private::HasMember_connected::value ? CALL_IF_EXISTS(bool, static_cast(this), connected) : static_cast(this)->operator bool(); + return Private::HasMember_connected::value + ? CALL_IF_EXISTS(bool, static_cast(this), connected) + : static_cast(this)->operator bool(); } - void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + + void flushTX() { CALL_IF_EXISTS(void, static_cast(this), flushTX); } + // Append Hookable for this class SerialFeature features(serial_index_t index) const { return SerialFeature::Hookable | CALL_IF_EXISTS(SerialFeature, static_cast(this), features, index); } - void setHook(WriteHook writeHook = 0, EndOfMessageHook eofHook = 0, void * userPointer = 0) { // Order is important here as serial code can be called inside interrupts // When setting a hook, the user pointer must be set first so if writeHook is called as soon as it's set, it'll be valid diff --git a/Marlin/src/gcode/host/M115.cpp b/Marlin/src/gcode/host/M115.cpp index 741a0b3f89ed..4f18e5504d05 100644 --- a/Marlin/src/gcode/host/M115.cpp +++ b/Marlin/src/gcode/host/M115.cpp @@ -61,6 +61,9 @@ void GcodeSuite::M115() { #if ENABLED(EXTENDED_CAPABILITIES_REPORT) + // The port that sent M115 + serial_index_t port = queue.ring_buffer.command_port(); + // PAREN_COMMENTS TERN_(PAREN_COMMENTS, cap_line(PSTR("PAREN_COMMENTS"), true)); @@ -71,7 +74,7 @@ void GcodeSuite::M115() { cap_line(PSTR("SERIAL_XON_XOFF"), ENABLED(SERIAL_XON_XOFF)); // BINARY_FILE_TRANSFER (M28 B1) - cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER)); // TODO: Replace by (SERIAL_IMPL.features(queue.ring_buffer.command_port()) & SerialFeature::BinaryFileTransfer) == SerialFeature::BinaryFileTransfer once it'll be implemented + cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER)); // TODO: Use SERIAL_IMPL.has_feature(port, SerialFeature::BinaryFileTransfer) once implemented // EEPROM (M500, M501) cap_line(PSTR("EEPROM"), ENABLED(EEPROM_SETTINGS)); @@ -150,7 +153,7 @@ void GcodeSuite::M115() { cap_line(PSTR("COOLER_TEMPERATURE"), ENABLED(HAS_COOLER)); // MEATPACK Compression - cap_line(PSTR("MEATPACK"), (SERIAL_IMPL.features(queue.ring_buffer.command_port()) & SerialFeature::MeatPack) == SerialFeature::MeatPack); + cap_line(PSTR("MEATPACK"), SERIAL_IMPL.has_feature(port, SerialFeature::MeatPack)); // Machine Geometry #if ENABLED(M115_GEOMETRY_REPORT) From 2df5eb40fac0ca15b550c730cf49413b94af4e07 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 29 Mar 2021 20:57:47 -0500 Subject: [PATCH 7/7] space --- Marlin/src/core/macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 9e5fb8dfcfb1..fb129cd9396a 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -397,7 +397,7 @@ WARNING: DO NOT RENAME THIS FUNCTION (or change the text inside the function to match what the preprocessor will generate) The name is chosen very short since the binary will store "const char* gtn(T*) [with T = YourTypeHere]" so avoid long function name here */ template - inline const char * gtn(T*) { + inline const char* gtn(T*) { // It works on GCC by instantiating __PRETTY_FUNCTION__ and parsing the result. So the syntax here is very limited to GCC output constexpr unsigned verboseChatLen = sizeof("const char* gtn(T*) [with T = ") - 1; static char templateType[sizeof(__PRETTY_FUNCTION__) - verboseChatLen] = {};