From e7fc5f337b980895d0f7084a28c3fbe5b6ae9f82 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 22 Nov 2019 23:10:12 +0300 Subject: [PATCH] relay: rework mask handling to use generic u32<->string conversion --- code/espurna/relay.h | 34 +++++++++++++++++++ code/espurna/relay.ino | 70 ++++++++++++++++++++++---------------- code/espurna/utils.h | 5 +-- code/espurna/utils.ino | 76 +++++++++++++++++++----------------------- 4 files changed, 113 insertions(+), 72 deletions(-) diff --git a/code/espurna/relay.h b/code/espurna/relay.h index 34240735..b951ee6f 100644 --- a/code/espurna/relay.h +++ b/code/espurna/relay.h @@ -8,6 +8,9 @@ Copyright (C) 2016-2019 by Xose PĂ©rez #pragma once +#include +#include "utils.h" + constexpr size_t RELAYS_MAX = 32; enum class RelayStatus : unsigned char { @@ -17,6 +20,37 @@ enum class RelayStatus : unsigned char { UNKNOWN = 0xFF }; +struct RelayMask { + + explicit RelayMask(const String& string) : + as_string(string), + as_u32(u32fromString(string)) + {} + + explicit RelayMask(String&& string) : + as_string(std::move(string)), + as_u32(u32fromString(as_string)) + {} + + explicit RelayMask(uint32_t value) : + as_string(std::move(u32toString(value, 2))), + as_u32(value) + {} + + explicit RelayMask(std::bitset bitset) : + RelayMask(bitset.to_ulong()) + {} + + RelayMask(String&& string, uint32_t value) : + as_string(std::move(string)), + as_u32(value) + {} + + const String as_string; + uint32_t as_u32; + +}; + RelayStatus relayParsePayload(const char * payload); bool relayStatus(unsigned char id, bool status, bool report, bool group_report); diff --git a/code/espurna/relay.ino b/code/espurna/relay.ino index e463f156..d4ee177e 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -384,26 +384,42 @@ void setSpeed(unsigned char speed) { // RELAY // ----------------------------------------------------------------------------- -void _relayMaskRtcmem(uint32_t mask) { +// State persistance persistance + +RelayMask INLINE _relayMaskRtcmem() { + return RelayMask(Rtcmem->relay); +} + +void INLINE _relayMaskRtcmem(uint32_t mask) { Rtcmem->relay = mask; } -uint32_t _relayMaskRtcmem() { - return Rtcmem->relay; +void INLINE _relayMaskRtcmem(const RelayMask& mask) { + _relayMaskRtcmem(mask.as_u32); +} + +void INLINE _relayMaskRtcmem(const std::bitset& bitset) { + _relayMaskRtcmem(bitset.to_ulong()); +} + +RelayMask INLINE _relayMaskSettings() { + return RelayMask(getSetting("relayBootMask")); } -void _relayMaskSettings(const String& string) { - setSetting("relayBootMask", string); +void INLINE _relayMaskSettings(uint32_t mask) { + setSetting("relayBootMask", u32toString(mask, 2)); } -void _relayMaskSettings(uint32_t mask) { - _relayMaskSettings(bitsetToString(mask)); +void INLINE _relayMaskSettings(const RelayMask& mask) { + setSetting("relayBootMask", mask.as_string); } -uint32_t _relayMaskSettings() { - return bitsetFromString(getSetting("relayBootMask")); +void INLINE _relayMaskSettings(const std::bitset& bitset) { + _relayMaskSettings(bitset.to_ulong()); } +// Pulse timers (timer after ON or OFF event) + void relayPulse(unsigned char id) { _relays[id].pulseTicker.detach(); @@ -426,6 +442,8 @@ void relayPulse(unsigned char id) { } +// General relay status control + bool relayStatus(unsigned char id, bool status, bool report, bool group_report) { if (id >= _relays.size()) return false; @@ -577,19 +595,18 @@ void relaySync(unsigned char id) { void relaySave(bool eeprom) { - auto mask = std::bitset(0); - const unsigned char count = constrain(relayCount(), 0, RELAYS_MAX); + + auto statuses = std::bitset(0); for (unsigned int id = 0; id < count; ++id) { - mask.set(id, relayStatus(id)); + statuses.set(id, relayStatus(id)); } - const uint32_t mask_value = mask.to_ulong(); - const String mask_string = bitsetToString(mask_value); - DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %s\n"), mask_string.c_str()); + const RelayMask mask(statuses); + DEBUG_MSG_P(PSTR("[RELAY] Setting relay mask: %s\n"), mask.as_string.c_str()); // Persist only to rtcmem, unless requested to save to the eeprom - _relayMaskRtcmem(mask_value); + _relayMaskRtcmem(mask); // The 'eeprom' flag controls whether we are commiting this change or not. // It is useful to set it to 'false' if the relay change triggering the @@ -598,7 +615,7 @@ void relaySave(bool eeprom) { // Nevertheless, we store the value in the EEPROM buffer so it will be written // on the next commit. if (eeprom) { - _relayMaskSettings(mask_string); + _relayMaskSettings(mask); // We are actually enqueuing the commit so it will be // executed on the main loop, in case this is called from a system context callback eepromCommit(); @@ -686,18 +703,14 @@ void _relayBoot() { _relayRecursive = true; bool trigger_save = false; - uint32_t stored_mask = 0; - if (rtcmemStatus()) { - stored_mask = _relayMaskRtcmem(); - } else { - stored_mask = _relayMaskSettings(); - } + const auto stored_mask = rtcmemStatus() + ? _relayMaskRtcmem() + : _relayMaskSettings(); - const String string_mask(bitsetToString(stored_mask)); - DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %s\n"), string_mask.c_str()); + DEBUG_MSG_P(PSTR("[RELAY] Retrieving mask: %s\n"), stored_mask.as_string.c_str()); - auto mask = std::bitset(stored_mask); + auto mask = std::bitset(stored_mask.as_u32); // Walk the relays unsigned char lock; @@ -747,12 +760,11 @@ void _relayBoot() { } - const auto mask_value = mask.to_ulong(); - _relayMaskRtcmem(mask_value); + _relayMaskRtcmem(mask); // Save if there is any relay in the RELAY_BOOT_TOGGLE mode if (trigger_save) { - _relayMaskSettings(mask_value); + _relayMaskSettings(mask); } _relayRecursive = false; diff --git a/code/espurna/utils.h b/code/espurna/utils.h index c6990087..0a8e5e6d 100644 --- a/code/espurna/utils.h +++ b/code/espurna/utils.h @@ -52,5 +52,6 @@ void nice_delay(unsigned long ms); double roundTo(double num, unsigned char positions); -uint32_t bitsetFromString(const String& string); -String bitsetToString(uint32_t bitset); +uint32_t u32fromString(const String& string, int base); +uint32_t u32fromString(const String& string); +String u32toString(uint32_t bitset, int base); diff --git a/code/espurna/utils.ino b/code/espurna/utils.ino index be30190a..b2461713 100644 --- a/code/espurna/utils.ino +++ b/code/espurna/utils.ino @@ -134,22 +134,6 @@ bool haveRelaysOrSensors() { return result; } -// TODO: force getSetting return type to handle settings -uint32_t u32fromString(const String& string, int base = 10) { - - const char *ptr = string.c_str(); - char *value_endptr = nullptr; - - // invalidate the whole string when invalid chars are detected - const auto value = strtoul(ptr, &value_endptr, base); - if (value_endptr == ptr || value_endptr[0] != '\0') { - return 0; - } - - return value; - -} - // ----------------------------------------------------------------------------- // Heartbeat helper // ----------------------------------------------------------------------------- @@ -691,42 +675,52 @@ char* strnstr(const char* buffer, const char* token, size_t n) { return nullptr; } -// Note: -// - when using standard base-2 literal syntax, parse that -// to keep backwards compatibility -// - otherwise, fallback to base-10 numbers -uint32_t bitsetFromString(const String& string) { +// TODO: force getSetting return type to handle settings +uint32_t u32fromString(const String& string, int base) { + + const char *ptr = string.c_str(); + char *value_endptr = nullptr; + + // invalidate the whole string when invalid chars are detected + const auto value = strtoul(ptr, &value_endptr, base); + if (value_endptr == ptr || value_endptr[0] != '\0') { + return 0; + } + + return value; + +} + +uint32_t u32fromString(const String& string) { if (!string.length()) { return 0; } - if (string.startsWith("0b") && (string.length() > 2)) { - return u32fromString(string.substring(2), 2); + int base = 10; + if (string.length() > 2) { + if (string.startsWith("0b")) { + base = 2; + } else if (string.startsWith("0o")) { + base = 8; + } else if (string.startsWith("0x")) { + base = 16; + } else { + return 0; + } + return u32fromString(string.substring(2), base); } - return u32fromString(string); + return u32fromString(string, base); } -// Note: -// - bitset::to_string() will return std::string -// - itoa accepts int, so it will cut the sign bit -String bitsetToString(uint32_t value) { +String u32toString(uint32_t value, int base) { String result; - result.reserve(34); + result.reserve(32 + 2); result += "0b"; - const uint32_t _value { value }; - size_t bits = 0; - - do { - value >>= 1; - bits++; - } while (value); - - int bit = bits - 1; - do { - result += ((_value & (1 << bit)) ? '1' : '0'); - } while (--bit >= 0); + char buffer[33] = {0}; + ultoa(value, buffer, base); + result += buffer; return result; }