2021-05-27 10:59:22 -04:00
|
|
|
/*
|
|
|
|
universe.cpp
|
|
|
|
|
|
|
|
Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com)
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "universe.h"
|
2021-08-09 15:01:41 -04:00
|
|
|
#include "config.h"
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
namespace SACN {
|
|
|
|
|
2021-06-20 09:10:21 -04:00
|
|
|
/**
|
|
|
|
* @brief UniverseSource::UniverseSource
|
|
|
|
*/
|
|
|
|
UniverseSource::UniverseSource()
|
|
|
|
: cid_(UUID::uuid())
|
|
|
|
, description_(std::string())
|
|
|
|
, universe_(0)
|
|
|
|
, priority_(100)
|
|
|
|
, sync_address_(0)
|
|
|
|
, options_(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
/**
|
2021-07-28 10:43:43 -04:00
|
|
|
* @brief UniverseSource::UniverseSource
|
|
|
|
* @param pdu
|
|
|
|
*
|
|
|
|
* Construct a Universe Source from an sACN frame PDU
|
|
|
|
*/
|
2021-06-20 09:10:21 -04:00
|
|
|
UniverseSource::UniverseSource(std::shared_ptr<DATA::Pdu> pdu)
|
|
|
|
{
|
2021-07-28 10:37:33 -04:00
|
|
|
#if defined(RTTI_ENABLED)
|
2021-07-29 19:26:49 -04:00
|
|
|
auto root_header = dynamic_cast<RLP::rlp_header*>(pdu->parent()->header());
|
2021-07-28 10:37:33 -04:00
|
|
|
auto frame_header = dynamic_cast<DATA::frame_header*>(pdu->header());
|
|
|
|
#else
|
2021-07-29 19:26:49 -04:00
|
|
|
auto root_header = static_cast<RLP::rlp_header*>(pdu->parent()->header());
|
2021-07-28 10:37:33 -04:00
|
|
|
auto frame_header = static_cast<DATA::frame_header*>(pdu->header());
|
|
|
|
#endif
|
2021-07-29 19:26:49 -04:00
|
|
|
|
|
|
|
cid_ = root_header->cid;
|
2021-05-27 10:59:22 -04:00
|
|
|
description_ = std::string((char*)frame_header->source_name);
|
|
|
|
universe_ = frame_header->universe;
|
|
|
|
priority_ = frame_header->priority;
|
|
|
|
sync_address_ = frame_header->sync_address;
|
|
|
|
options_ = frame_header->options;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
/**
|
|
|
|
* @brief operator ==
|
|
|
|
* @param a
|
|
|
|
* @param b
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool operator== (const UniverseSource& a, const UniverseSource& b)
|
|
|
|
{
|
|
|
|
return (std::hash<UniverseSource>{}(a) == std::hash<UniverseSource>{}(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Universe::Universe
|
|
|
|
*/
|
|
|
|
Universe::Universe()
|
2021-08-06 12:36:31 -04:00
|
|
|
: DMX::Universe(E131_NETWORK_DATA_LOSS_TIMEOUT)
|
2021-08-02 10:09:14 -04:00
|
|
|
{
|
2021-08-06 12:36:04 -04:00
|
|
|
sync_data_.clear();
|
2021-08-02 10:09:14 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Universe::~Universe
|
|
|
|
*/
|
|
|
|
Universe::~Universe()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
/**
|
|
|
|
Set universe data from a DMP PDU from a UniverseSource.
|
|
|
|
*/
|
|
|
|
void Universe::set(std::shared_ptr<DMP::Pdu> dmp,
|
|
|
|
std::shared_ptr<UniverseSource> source) {
|
|
|
|
// 7.3 Address Type and Data Type
|
|
|
|
// Sources shall set the DMP Layer's Address Type and Data Type to 0xa1.
|
|
|
|
// Receivers shall discard the packet if the received value is not 0xa1.
|
|
|
|
if (!dmp->header())
|
|
|
|
return;
|
2021-08-02 10:09:14 -04:00
|
|
|
#if defined(RTTI_ENABLED)
|
|
|
|
auto type = dynamic_cast<ACN::DMP::address_type*>(dmp->header());
|
|
|
|
#else
|
|
|
|
auto type = static_cast<ACN::DMP::address_type*>(dmp->header());
|
|
|
|
#endif
|
2021-05-27 10:59:22 -04:00
|
|
|
if (!type->z_reserved) return; // needs to be true, but why?
|
|
|
|
if (type->relative) return;
|
|
|
|
if (type->type != ACN::DMP::ARRAY) return;
|
|
|
|
if (type->x_reserved != 0) return;
|
|
|
|
if (type->width != ACN::DMP::TWO) return;
|
|
|
|
|
|
|
|
// only act on the first property pair in the data
|
|
|
|
if (!dmp->data())
|
|
|
|
return;
|
2021-08-02 10:09:14 -04:00
|
|
|
|
|
|
|
#if defined(RTTI_ENABLED)
|
|
|
|
auto set_data = dynamic_cast<ACN::DMP::dmp_set_data*>(dmp->data());
|
|
|
|
#else
|
|
|
|
auto set_data = static_cast<ACN::DMP::dmp_set_data*>(dmp->data());
|
|
|
|
#endif
|
|
|
|
const auto& [range, data] = set_data->properties.front();
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
// 7.4 First Property Address
|
|
|
|
// Sources shall set the DMP Layer's First Property Address to 0x0000.
|
|
|
|
// Receivers shall discard the packet if the received value is not 0x0000.
|
2021-08-02 10:09:14 -04:00
|
|
|
if (range.address != 0)
|
2021-05-27 10:59:22 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
// 7.5 Address Increment
|
|
|
|
// Sources shall set the DMP Layer's Address Increment to 0x0001.
|
|
|
|
// Receivers shall discard the packet if the received value is not 0x0001.
|
2021-08-02 10:09:14 -04:00
|
|
|
if (range.incriment != 1)
|
2021-05-27 10:59:22 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
// 7.6 Property Value Count
|
|
|
|
// The DMP Layer's Property Value Count is used to encode the number of
|
|
|
|
// DMX512-A [DMX] Slots (including the START Code slot).
|
2021-08-02 10:09:14 -04:00
|
|
|
if (range.count < 1 || range.count > 513)
|
2021-05-27 10:59:22 -04:00
|
|
|
return;
|
|
|
|
|
2021-07-31 10:14:26 -04:00
|
|
|
setSource(source);
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
// 7.7 Property Values (DMX512-A Data)
|
2021-07-31 10:14:26 -04:00
|
|
|
// The DMP Layer's Property values field is used to encode the
|
2021-05-27 10:59:22 -04:00
|
|
|
// DMX512-A [DMX] START Code and data.
|
2021-08-06 12:36:04 -04:00
|
|
|
|
|
|
|
// 6.2.4.1 If a receiver is presented with an E1.31 Data Packet containing a
|
|
|
|
// Synchronization Address of 0, it shall discard any data waiting to be
|
|
|
|
// processed and immediately act on that Data Packet.
|
|
|
|
if (source->syncAddress() == 0) {
|
|
|
|
sync_data_.clear();
|
|
|
|
DMX::Universe::setData(data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sync_data_ = data;
|
2021-06-20 09:09:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Universe::setSource
|
|
|
|
* @param source
|
|
|
|
*/
|
|
|
|
void Universe::setSource(std::shared_ptr<UniverseSource> source)
|
|
|
|
{
|
|
|
|
source_ = source;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
|
2021-08-06 12:36:04 -04:00
|
|
|
uint8_t Universe::slot(const uint16_t addr)
|
|
|
|
{
|
|
|
|
// 6.2.6 E1.31 Data Packet: Options
|
|
|
|
// Force_Synchronization: Bit 5
|
|
|
|
// This bit indicates whether to lock or revert to an unsynchronized state
|
|
|
|
// when synchronization is lost. When set to 0, components that had been
|
|
|
|
// operating in a synchronized state shall not update with any new packets
|
|
|
|
// until synchronization resumes. When set to 1, once synchronization has
|
|
|
|
// been lost, components that had been operating in a synchronized state need
|
|
|
|
// not wait for a new E1.31 Synchronization Packet in order to update to the
|
|
|
|
// next E1.31 Data Packet.
|
|
|
|
if (isSyncronized() && source()->isForced() && rxRate() == 0)
|
|
|
|
synchronize();
|
|
|
|
|
|
|
|
return DMX::Universe::slot(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Universe::synchronize
|
|
|
|
*/
|
|
|
|
void Universe::synchronize()
|
|
|
|
{
|
|
|
|
if (!isSyncronized())
|
|
|
|
return;
|
|
|
|
|
|
|
|
DMX::Universe::setData(sync_data_);
|
|
|
|
sync_data_.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::MergeProxyUniverse
|
|
|
|
*/
|
2021-08-02 10:09:14 -04:00
|
|
|
MergeProxyUniverse::MergeProxyUniverse()
|
|
|
|
: SACN::Universe()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-06 12:36:04 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::~MergeProxyUniverse
|
|
|
|
*/
|
2021-08-02 10:09:14 -04:00
|
|
|
MergeProxyUniverse::~MergeProxyUniverse()
|
|
|
|
{
|
|
|
|
for (auto& [_, universe] : sources_)
|
|
|
|
delete universe;
|
|
|
|
}
|
|
|
|
|
2021-08-06 12:36:04 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::sources
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
const std::vector<UniverseSource> MergeProxyUniverse::sources() const
|
|
|
|
{
|
|
|
|
std::vector<UniverseSource> keys;
|
|
|
|
for (const auto& [key, _] : sources_)
|
|
|
|
keys.push_back(key);
|
|
|
|
return keys;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::sourceUniverse
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
Universe* MergeProxyUniverse::sourceUniverse(const UniverseSource &src)
|
|
|
|
{
|
|
|
|
if (!hasSourceUniverse(src))
|
|
|
|
newSource_(src);
|
|
|
|
return sources_.at(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::hasSourceUniverse
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool MergeProxyUniverse::hasSourceUniverse(const UniverseSource& src) const
|
|
|
|
{
|
|
|
|
if (sources_.count(src))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::hassources
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool MergeProxyUniverse::hasSources() const
|
|
|
|
{
|
|
|
|
if (sources_.size())
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::deleteSourceUniverse
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::deleteSourceUniverse(const UniverseSource& src)
|
|
|
|
{
|
|
|
|
if (sources_.count(src)) {
|
|
|
|
delete sources_.at(src);
|
|
|
|
sources_.erase(src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::addSourceUniverse
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::addSourceUniverse(Universe* universe)
|
|
|
|
{
|
|
|
|
sources_.at(*universe->source()) = universe;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::dataChangedNotifier
|
|
|
|
* @param universe
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::dataChangedNotifier(DMX::Universe* dmx)
|
|
|
|
{
|
|
|
|
#if defined(RTTI_ENABLED)
|
|
|
|
auto sacn = static_cast<SACN::Universe*>(dmx);
|
|
|
|
#else
|
|
|
|
auto sacn = static_cast<SACN::Universe*>(dmx);
|
|
|
|
#endif
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe)
|
|
|
|
return;
|
|
|
|
if (sacn->source() == universe->source())
|
|
|
|
for (const auto &cb : callbacks_)
|
|
|
|
cb(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::isSyncronized
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool MergeProxyUniverse::isSyncronized() const
|
|
|
|
{
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe) return false;
|
|
|
|
return universe->isSyncronized();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::source
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
std::shared_ptr<UniverseSource> MergeProxyUniverse::source() const
|
|
|
|
{
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe) return nullptr;
|
|
|
|
return universe->source();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::set
|
|
|
|
* @param pdu
|
|
|
|
* @param source
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::set(std::shared_ptr<DMP::Pdu> pdu,
|
|
|
|
std::shared_ptr<UniverseSource> src)
|
|
|
|
{
|
|
|
|
if (!sources_.count(*src))
|
|
|
|
newSource_(*src);
|
|
|
|
|
|
|
|
sources_.at(*src)->set(pdu, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-06 12:36:04 -04:00
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::synchronize
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::synchronize()
|
|
|
|
{
|
|
|
|
for ( auto& [_, uni] : sources_)
|
|
|
|
uni->synchronize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::data
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
const DMX::DimmerData * MergeProxyUniverse::data() const
|
|
|
|
{
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe) return nullptr;
|
|
|
|
return universe->data();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::slot
|
|
|
|
* @param s
|
|
|
|
* @return
|
|
|
|
*/
|
2021-08-06 12:36:04 -04:00
|
|
|
uint8_t MergeProxyUniverse::slot(const uint16_t s)
|
2021-08-02 10:09:14 -04:00
|
|
|
{
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe) return 0;
|
|
|
|
return universe->slot(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::rxRate
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
double MergeProxyUniverse::rxRate()
|
|
|
|
{
|
|
|
|
auto universe = dominant_();
|
|
|
|
if (!universe) return 0.0;
|
|
|
|
return universe->rxRate();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::dominant_
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
Universe* MergeProxyUniverse::dominant_() const
|
|
|
|
{
|
|
|
|
Universe* ret = nullptr;
|
|
|
|
for (auto& [_, uni] : sources_) {
|
|
|
|
if (uni->rxRate() < 1) // stale universes cannot be dominant
|
|
|
|
continue;
|
|
|
|
if (!ret || uni->source()->priority() > ret->source()->priority())
|
|
|
|
ret = uni;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief MergeProxyUniverse::newSource_
|
|
|
|
* @param src
|
|
|
|
*/
|
|
|
|
void MergeProxyUniverse::newSource_(const UniverseSource &src)
|
|
|
|
{
|
|
|
|
sources_.emplace(src, new Universe());
|
|
|
|
sources_.at(src)->onData(std::bind(&SACN::MergeProxyUniverse::dataChangedNotifier,
|
|
|
|
this, std::placeholders::_1));
|
|
|
|
}
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
}; // SACN
|