diff --git a/protocol/esta/dmx/universe.cpp b/protocol/esta/dmx/universe.cpp index 33877b1..a290f3d 100644 --- a/protocol/esta/dmx/universe.cpp +++ b/protocol/esta/dmx/universe.cpp @@ -39,10 +39,47 @@ Universe::Universe(int timeout_period) /** - * @brief Universe::~Universe + * @brief milliseconds since the last update + * @return */ -Universe::~Universe() +long Universe::age() { + if (!last_updated_.time_since_epoch().count()) + return -1; // universe has never been seen + + auto now = std::chrono::system_clock::now(); + auto elapsed = std::chrono::duration_cast(now - last_updated_); + + return elapsed.count(); +} + + +/** + * @brief Universe::rxRate Rate (in Hz) at which the universe is being recv'd. + * @return Hz + * + * Calculated as the rolling mean of (the number of frames in + * the last rx_timeout_peroid ms) / rx_timeout_period seconds. + */ +double Universe::rxRate() +{ + rx_timeout_(); + // updates per second + return rx_times_.size() / (rx_timeout_period_ / 1000.0); +} + + +/** + * @brief Universe::status + * @return + * + * Like Schrödinger's cat, the status of the universe may exist in a superpostion + * of states. Only by observing it will it collaps into a single status. + */ +uint8_t Universe::status() +{ + rx_timeout_(); // many things may have happened since the last observation + return status_; } @@ -63,33 +100,53 @@ uint8_t Universe::slot(const uint16_t address) /** - * @brief Universe::rxRate Rate (in Hz) at which the universe is being recv'd. - * @return Hz - * - * Calculated as the rolling mean of (the number of frames in - * the last rx_timeout_peroid ms) / rx_timeout_period seconds. + * @brief Universe::setStatus + * @param val */ -double Universe::rxRate() +void Universe::setStatus(uint8_t val) { - rx_timeout_(); - // updates per second - return rx_times_.size() / (rx_timeout_period_ / 1000.0); + if (val == RX_TIMEOUT) + val = DMX_LOST; + + if (val == status_) + return; + + status_ = val; + do_callbacks_(cb_statusChange); } /** - * @brief milliseconds since the last update - * @return + * @brief Universe::setValue + * @param start + * @param footprint + * @param data */ -long Universe::age() +void Universe::setValue(const uint16_t start, const uint16_t footprint, + const uint8_t * const data) { - if (!last_updated_.time_since_epoch().count()) - return -1; // universe has never been seen + // start and footprint valid? + if (start < 1 || start + footprint > null_start_data.size()) + return; - auto now = std::chrono::system_clock::now(); - auto elapsed = std::chrono::duration_cast(now - last_updated_); + null_start_mutex.lock(); + std::copy_n(data, footprint, null_start_data.begin() + start); + null_start_mutex.unlock(); - return elapsed.count(); + setStatus(DMX_ACTIVE); + last_updated_ = std::chrono::system_clock::now(); + do_callbacks_(cb_dataChange); +} + + +/** + * @brief Universe::setValue + * @param address + * @param value + */ +void Universe::setValue(const uint16_t address, const uint8_t value) +{ + setValue(address, 1, &value); } @@ -101,17 +158,16 @@ long Universe::age() */ void Universe::setData(const std::vector& data) { - // no data? if (data.empty()) - return; + return; // no data - // Alternate Start Code if (data.front() != E111_NULL_START) - return altSCdata(data); + return altSCdata(data); // Alternate Start Code // accept variable lenth input, but never more than the limit const auto max_length = null_start_data.size(); const auto length = data.size() < max_length ? data.size() : max_length; + null_start_mutex.lock(); // take the lock if (length < max_length) null_start_data.fill(0); // wipe old data @@ -147,87 +203,6 @@ void Universe::altSCdata(const std::vector & data) } -/** - * @brief Universe::status - * @return - * - * Like Schrödinger's cat, the status of the universe may exist in a superpostion - * of states. Only by observing it will it collaps into a single status. - */ -uint8_t Universe::status() -{ - rx_timeout_(); // many things may have happened since the last observation - return status_; -} - - -/** - * @brief Universe::setStatus - * @param val - */ -void Universe::setStatus(uint8_t val) -{ - if (val == RX_TIMEOUT) - val = DMX_LOST; - - if (val == status_) - return; - - status_ = val; - do_callbacks_(cb_statusChange); -} - - -/** - * @brief Universe::onStatusChange - * @param cb - * @return - */ -std::shared_ptr Universe::onStatusChange(const std::function cb) -{ - // wrap the callback with a shared pointer - auto sp = std::make_shared>(std::move(cb)); - // add callback to list (as a weak pointer) - cb_statusChange.push_back(sp); - // return token that caller must keep throughout it's scope - return sp; -} - - -/** - * @brief Universe::setValue - * @param address - * @param value - */ -void Universe::setValue(const uint16_t address, const uint8_t value) -{ - setValue(address, 1, &value); -} - - -/** - * @brief Universe::setValue - * @param start - * @param footprint - * @param data - */ -void Universe::setValue(const uint16_t start, const uint16_t footprint, - const uint8_t* data) -{ - // start and footprint valid? - if (start < 1 || start + footprint > null_start_data.size()) - return; - - null_start_mutex.lock(); - std::copy_n(data, footprint, null_start_data.begin() + start); - null_start_mutex.unlock(); - - setStatus(DMX_ACTIVE); - last_updated_ = std::chrono::system_clock::now(); - do_callbacks_(cb_dataChange); -} - - /** * @brief Universe::onData * @param cb @@ -245,23 +220,18 @@ std::shared_ptr Universe::onDataChange(const std::function>> & callbacks) +std::shared_ptr Universe::onStatusChange(const std::function cb) { - for (auto it = callbacks.begin(); it != callbacks.end();) - { - if (auto sp = it->lock()) - { // the owner is still holding the token - (*sp)(this); - ++it; - } - else - { // the owner has released the token - it = callbacks.erase(it); - } - } + // wrap the callback with a shared pointer + auto sp = std::make_shared>(std::move(cb)); + // add callback to list (as a weak pointer) + cb_statusChange.push_back(sp); + // return token that caller must keep throughout it's scope + return sp; } @@ -301,4 +271,24 @@ void Universe::rx_timeout_(bool add_now) } } + +/** + * @brief Universe::doCallbacks + * @param callbacks + */ +void Universe::do_callbacks_(std::vector>> & callbacks) +{ + for (auto it = callbacks.begin(); it != callbacks.end();) + { + if (auto sp = it->lock()) + { // the owner is still holding the token + (*sp)(this); + ++it; + } + else + // the owner has released the token + it = callbacks.erase(it); + } +} + } // namespace DMX diff --git a/protocol/esta/dmx/universe.h b/protocol/esta/dmx/universe.h index c4f715d..5d52085 100644 --- a/protocol/esta/dmx/universe.h +++ b/protocol/esta/dmx/universe.h @@ -35,7 +35,7 @@ namespace DMX { -using DimmerData = std::array; //!< Array of 513 bytes for basic data. +using DimmerData = std::array; //!< Array of 513 bytes for basic data. /** * @brief The Universe class @@ -47,33 +47,29 @@ using DimmerData = std::array; //!< Array of 513 b class Universe { public: Universe (int timeout_period = E111_DATA_LOSS_TIMEOUT); - virtual ~Universe (); - virtual uint8_t slot (const uint16_t); - virtual double rxRate(); virtual long age(); + virtual double rxRate(); + virtual uint8_t status(); + virtual uint8_t slot(const uint16_t); - virtual void setValue (const uint16_t address, const uint8_t value); - virtual void setValue (const uint16_t start, const uint16_t footprint, - const uint8_t* data); - - void setData (const std::vector &); - + virtual void setStatus(uint8_t); + virtual void setValue(const uint16_t start, const uint16_t footprint, + const uint8_t* const data); + virtual void setValue(const uint16_t address, const uint8_t value); + virtual void setData(const std::vector &); virtual void altSCdata(const std::vector &); std::shared_ptr onDataChange(const std::function); - /** - * @brief The Status enum - */ std::shared_ptr onStatusChange(const std::function); + + /// Known states of the universe Status value enum Status : uint8_t { DMX_NULL = 0, //!< uninitialized DMX_ACTIVE = 1, //!< actively sending/receiving data DMX_LOST = 2, //!< no activity in E111_DATA_LOSS_TIMEOUT RX_TIMEOUT = 250 //!< no activity in the constructed timeout period }; - virtual void setStatus(uint8_t); - virtual uint8_t status(); protected: void doDataCallbacks() {do_callbacks_(cb_dataChange);} //!< Run Data Change Callbacks @@ -84,13 +80,12 @@ class Universe { std::chrono::system_clock::time_point last_updated_; //!< time of the latest update private: - std::queue> rx_times_; - void rx_timeout_(bool add_now = false); void do_callbacks_(std::vector>>&); - const int rx_timeout_period_; + const int rx_timeout_period_; uint8_t status_; //!< the operating state of the universe + std::queue> rx_times_; std::vector>> cb_dataChange; std::vector>> cb_statusChange; };