#pragma once #include namespace diller { namespace utils { enum class tty_status : uint8_t { NEED_MORE, TIMEOUT, EMPTY_LINE, FULL_LINE, LINE_OVERFLOW, }; class serial_io { public: static auto available() -> decltype(Serial.available()) { return Serial.available(); } static auto read() -> decltype(Serial.read()) { return Serial.read(); } template static auto print(A a) -> decltype(Serial.print(a)) { return Serial.print(a); } template static auto println(A a) -> decltype(Serial.println(a)) { return Serial.println(a); } }; template class software_serial_io { public: static auto available() -> decltype(instance::software_serial.available()) { return instance::software_serial.available(); } static auto read() -> decltype(instance::software_serial.read()) { return instance::software_serial.read(); } template static auto print(A a) -> decltype(instance::software_serial.print(a)) { return instance::software_serial.print(a); } template static auto println(A a) -> decltype(instance::software_serial.println(a)) { return instance::software_serial.println(a); } }; template class tty { public: char line[buffer_size]; uint8_t size = 0; const bool debug = debug_; const unsigned long timeout_ms = timeout_ms_; void init(bool reset = false) { if (reset) { line[0] = '\0'; } size = 0; last_char = 0; } void quote(const String &s) { Serial.print(s.c_str()); } void quote(const char *str) { Serial.print(str); } void escape(const char *str) { Serial.print(str); } tty_status readline() { while (io::available()) { int c = io::read(); if (debug) { Serial.print("read: c="); Serial.println(c, HEX); } if (c == '\n') { // ignore last_char = millis(); } else if (c == '\r') { line[size] = '\0'; auto status = size > 0 ? tty_status::FULL_LINE : tty_status::EMPTY_LINE; init(); if (debug) { Serial.print("debug src=tty state=\"new line\" size="); Serial.print(size, DEC); Serial.print(" line="); quote(line); Serial.println(); } return status; } else { if (size == buffer_size - 1) { if (debug) { Serial.print("debug src=tty state=overflow size="); Serial.print(size, DEC); Serial.print(" line="); quote(line); Serial.println(); } init(); return tty_status::LINE_OVERFLOW; } line[size++] = static_cast(c); last_char = millis(); } } unsigned long now = millis(); if (last_char > 0 && now >= (last_char + timeout_ms)) { if (debug) { line[size] = '\0'; Serial.print("debug src=tty state=\"timeout\" line="); quote(line); Serial.println(); } init(true); return tty_status::TIMEOUT; } return tty_status::NEED_MORE; } private: unsigned long last_char = 0; }; class key_value_map { public: virtual void clear(); virtual void put(char *key, char *value) = 0; virtual const char *key(uint8_t index) const = 0; virtual const char *value(uint8_t index) const = 0; virtual const char *find(const char *key) const = 0; virtual uint8_t size() const = 0; virtual bool is_empty() const = 0; virtual bool is_full() const = 0; }; template class fixed_size_key_value_map : public key_value_map { public: uint8_t size_; static_assert(max_args <= 256, "max_args is too big"); char *keys[max_args]; char *values[max_args]; void clear() { size_ = 0; } void put(char *key, char *value) { if (size_ < max_args) { keys[size_] = key; values[size_] = value; size_++; } } const char *key(uint8_t index) const { return keys[index]; } const char *value(uint8_t index) const { return values[index]; } const char *find(const char *key) const { for (int i = 0; i < size_; i++) { if (strcmp(keys[i], key) == 0) { return values[i]; } } return nullptr; } uint8_t size() const { return size_; } bool is_empty() const { return size_ >= max_args; } bool is_full() const { return size_ >= max_args; } }; class diller_parser { public: diller_parser(key_value_map &map) : map(map) { } void parse(char *line) { char *c = line; map.clear(); while (*c != '\0') { while (*c == ' ') { c++; } if (*c == '\0') { break; } char *key = c, *value = nullptr; while (*c != ' ' && *c != '\0') { if (*c == '=') { *c = '\0'; c++; value = c; } c++; } map.put(key, value); if (*c == '\0' || map.is_full()) { break; } *c++ = '\0'; } } protected: key_value_map ↦ }; class property { public: property() : new_(true) { } const String &id() const { return id_; } const String &value() const { return value_; } const String &name() const { return name_; } const String &description() const { return description_; } bool dirty() const { return dirty_; } void init(const String &id) { id_ = id; dirty_ = false; } bool is_new() const { return new_; } void set_old() { new_ = false; } private: String id_; boolean dirty_; boolean new_; String value_; String name_; String description_; }; template class properties { public: properties() : size_(0) { } property* operator[](uint8_t i) { if (i >= size_) { return nullptr; } return &properties_[i]; } property* find(const char *id) { for (int i = 0; i < size_; i++) { if (properties_[i].id() == id) { return &properties_[i]; } } if (size_ < max_property_count) { auto p = &properties_[size_]; size_++; p->init(id); return p; } return nullptr; } uint8_t size() { return size_; } private: property properties_[max_property_count]; uint8_t size_; }; } // namespace utils } // namespace diller