Flags for MarlinSerial instance features (#21318)
This commit is contained in:
@@ -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,19 +55,34 @@ enum class PrintBase {
|
||||
Bin = 2
|
||||
};
|
||||
|
||||
// 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 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 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; }
|
||||
// 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 <class Child>
|
||||
@@ -85,27 +96,44 @@ struct SerialBase {
|
||||
SerialBase(const bool) {}
|
||||
#endif
|
||||
|
||||
#define SerialChild static_cast<Child*>(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<Child*>(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<Child*>(this)->msgDone(); }
|
||||
// Called upon initialization
|
||||
void begin(const long baudRate) { static_cast<Child*>(this)->begin(baudRate); }
|
||||
// Called upon destruction
|
||||
void end() { static_cast<Child*>(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<Child*>(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<Child*>(this)->read(index); }
|
||||
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<const Child*>(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<Child*>(this)->connected(); }
|
||||
bool connected() const { return SerialChild->connected(); }
|
||||
|
||||
// Redirect flush
|
||||
void flush() { static_cast<Child*>(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<Child*>(this), flushTX); }
|
||||
void flushTX() { CALL_IF_EXISTS(void, SerialChild, flushTX); }
|
||||
|
||||
// Glue code here
|
||||
FORCE_INLINE void write(const char *str) { while (*str) write(*str++); }
|
||||
|
||||
Reference in New Issue
Block a user