|
@@ -45,10 +45,6 @@ struct serial_index_t {
|
45
|
45
|
constexpr serial_index_t() : index(-1) {}
|
46
|
46
|
};
|
47
|
47
|
|
48
|
|
-// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is.
|
49
|
|
-CALL_IF_EXISTS_IMPL(void, flushTX);
|
50
|
|
-CALL_IF_EXISTS_IMPL(bool, connected, true);
|
51
|
|
-
|
52
|
48
|
// In order to catch usage errors in code, we make the base to encode number explicit
|
53
|
49
|
// If given a number (and not this enum), the compiler will reject the overload, falling back to the (double, digit) version
|
54
|
50
|
// 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 {
|
59
|
55
|
Bin = 2
|
60
|
56
|
};
|
61
|
57
|
|
62
|
|
-// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than
|
63
|
|
-// double or float. For double or float, a conversion exists so the call will be transparent
|
|
58
|
+// A simple feature list enumeration
|
|
59
|
+enum class SerialFeature {
|
|
60
|
+ None = 0x00,
|
|
61
|
+ MeatPack = 0x01, //!< Enabled when Meatpack is present
|
|
62
|
+ BinaryFileTransfer = 0x02, //!< Enabled for BinaryFile transfer support (in the future)
|
|
63
|
+ Virtual = 0x04, //!< Enabled for virtual serial port (like Telnet / Websocket / ...)
|
|
64
|
+ Hookable = 0x08, //!< Enabled if the serial class supports a setHook method
|
|
65
|
+};
|
|
66
|
+ENUM_FLAGS(SerialFeature);
|
|
67
|
+
|
|
68
|
+// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is.
|
|
69
|
+CALL_IF_EXISTS_IMPL(void, flushTX);
|
|
70
|
+CALL_IF_EXISTS_IMPL(bool, connected, true);
|
|
71
|
+CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None);
|
|
72
|
+
|
|
73
|
+// A simple forward struct to prevent the compiler from selecting print(double, int) as a default overload
|
|
74
|
+// for any type other than double/float. For double/float, a conversion exists so the call will be invisible.
|
64
|
75
|
struct EnsureDouble {
|
65
|
76
|
double a;
|
66
|
77
|
FORCE_INLINE operator double() { return a; }
|
67
|
|
- // 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
|
68
|
|
- // 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:
|
|
78
|
+ // If the compiler breaks on ambiguity here, it's likely because print(X, base) is called with X not a double/float, and
|
|
79
|
+ // a base that's not a PrintBase value. This code is made to detect the error. You MUST set a base explicitly like this:
|
69
|
80
|
// SERIAL_PRINT(v, PrintBase::Hex)
|
70
|
81
|
FORCE_INLINE EnsureDouble(double a) : a(a) {}
|
71
|
82
|
FORCE_INLINE EnsureDouble(float a) : a(a) {}
|
72
|
83
|
};
|
73
|
84
|
|
74
|
|
-// Using Curiously Recurring Template Pattern here to avoid virtual table cost when compiling.
|
|
85
|
+// Using Curiously-Recurring Template Pattern here to avoid virtual table cost when compiling.
|
75
|
86
|
// Since the real serial class is known at compile time, this results in the compiler writing
|
76
|
87
|
// a completely efficient code.
|
77
|
88
|
template <class Child>
|
|
@@ -85,27 +96,44 @@ struct SerialBase {
|
85
|
96
|
SerialBase(const bool) {}
|
86
|
97
|
#endif
|
87
|
98
|
|
|
99
|
+ #define SerialChild static_cast<Child*>(this)
|
|
100
|
+
|
88
|
101
|
// Static dispatch methods below:
|
89
|
102
|
// The most important method here is where it all ends to:
|
90
|
|
- size_t write(uint8_t c) { return static_cast<Child*>(this)->write(c); }
|
|
103
|
+ size_t write(uint8_t c) { return SerialChild->write(c); }
|
|
104
|
+
|
91
|
105
|
// Called when the parser finished processing an instruction, usually build to nothing
|
92
|
|
- void msgDone() { static_cast<Child*>(this)->msgDone(); }
|
93
|
|
- // Called upon initialization
|
94
|
|
- void begin(const long baudRate) { static_cast<Child*>(this)->begin(baudRate); }
|
95
|
|
- // Called upon destruction
|
96
|
|
- void end() { static_cast<Child*>(this)->end(); }
|
|
106
|
+ void msgDone() const { SerialChild->msgDone(); }
|
|
107
|
+
|
|
108
|
+ // Called on initialization
|
|
109
|
+ void begin(const long baudRate) { SerialChild->begin(baudRate); }
|
|
110
|
+
|
|
111
|
+ // Called on destruction
|
|
112
|
+ void end() { SerialChild->end(); }
|
|
113
|
+
|
97
|
114
|
/** Check for available data from the port
|
98
|
115
|
@param index The port index, usually 0 */
|
99
|
|
- int available(serial_index_t index = 0) { return static_cast<Child*>(this)->available(index); }
|
|
116
|
+ int available(serial_index_t index=0) const { return SerialChild->available(index); }
|
|
117
|
+
|
100
|
118
|
/** Read a value from the port
|
101
|
119
|
@param index The port index, usually 0 */
|
102
|
|
- int read(serial_index_t index = 0) { return static_cast<Child*>(this)->read(index); }
|
|
120
|
+ int read(serial_index_t index=0) { return SerialChild->read(index); }
|
|
121
|
+
|
|
122
|
+ /** Combine the features of this serial instance and return it
|
|
123
|
+ @param index The port index, usually 0 */
|
|
124
|
+ SerialFeature features(serial_index_t index=0) const { return static_cast<const Child*>(this)->features(index); }
|
|
125
|
+
|
|
126
|
+ // Check if the serial port has a feature
|
|
127
|
+ bool has_feature(serial_index_t index, SerialFeature flag) const { (features(index) & flag) != SerialFeature::None; }
|
|
128
|
+
|
103
|
129
|
// Check if the serial port is connected (usually bypassed)
|
104
|
|
- bool connected() { return static_cast<Child*>(this)->connected(); }
|
|
130
|
+ bool connected() const { return SerialChild->connected(); }
|
|
131
|
+
|
105
|
132
|
// Redirect flush
|
106
|
|
- void flush() { static_cast<Child*>(this)->flush(); }
|
|
133
|
+ void flush() { SerialChild->flush(); }
|
|
134
|
+
|
107
|
135
|
// Not all implementation have a flushTX, so let's call them only if the child has the implementation
|
108
|
|
- void flushTX() { CALL_IF_EXISTS(void, static_cast<Child*>(this), flushTX); }
|
|
136
|
+ void flushTX() { CALL_IF_EXISTS(void, SerialChild, flushTX); }
|
109
|
137
|
|
110
|
138
|
// Glue code here
|
111
|
139
|
FORCE_INLINE void write(const char *str) { while (*str) write(*str++); }
|