236 lines
6.6 KiB
C++
236 lines
6.6 KiB
C++
/*
|
|
sender.cpp
|
|
|
|
Copyright (c) 2021 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 "extended.h"
|
|
#include "source.h"
|
|
|
|
namespace sACN {
|
|
|
|
/**
|
|
* @brief Source::Source
|
|
* @param cid
|
|
*/
|
|
Source::Source(UUID::uuid cid)
|
|
: Component(cid)
|
|
, discovery_future_(discovery_exitSignal_.get_future())
|
|
, discovery_worker_(&Source::discovery_loop_, this)
|
|
{
|
|
fctn_ = "OpenLCP sACN Source";
|
|
}
|
|
|
|
|
|
Source::~Source()
|
|
{
|
|
discovery_exitSignal_.set_value();
|
|
discovery_worker_.join();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::create
|
|
* @param num
|
|
*/
|
|
void Source::create(const uint16_t num)
|
|
{
|
|
if (universes_.count(num))
|
|
return;
|
|
|
|
auto metadata = std::make_shared<DATA::data_header>();
|
|
metadata->source_name = this->name();
|
|
metadata->universe = num;
|
|
|
|
universes_mutext_.lock();
|
|
universes_.emplace(num, new Universe(this));
|
|
universes_[num]->setProvenance(metadata);
|
|
universes_mutext_.unlock();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::terminate
|
|
* @param num
|
|
*/
|
|
void Source::terminate(const uint16_t num)
|
|
{
|
|
if (!universes_.count(num))
|
|
return;
|
|
|
|
universes_mutext_.lock();
|
|
auto metadata = universes_[num]->provenance();
|
|
metadata->options.stream_terminated = true;
|
|
universes_[num]->setProvenance(metadata);
|
|
universes_mutext_.unlock();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::end
|
|
* @param num
|
|
*/
|
|
void Source::end(const uint16_t num)
|
|
{
|
|
if (!universes_.count(num))
|
|
return;
|
|
|
|
universes_mutext_.lock();
|
|
universes_.erase(num);
|
|
universes_mutext_.unlock();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief @cite sACN 12 Universe Discovery
|
|
*
|
|
* > E1.31 Universe Discovery enables other components on a network to know
|
|
* > which universes are being used to transmit data or synchronization
|
|
* > information. ... this specification requires any source intending to
|
|
* > comply with E1.31 to implement Universe Discovery...
|
|
*/
|
|
void Source::discoveryAnnounce()
|
|
{
|
|
/// > \cite sACN 12.1 Universe Discovery and Termination
|
|
/// >
|
|
/// > Any source that is no longer sending any universe data may stop
|
|
/// > sending E1.31 Universe Discovery Packets until such time that it
|
|
/// > resumes transmission of E1.31 Data and/or Synchronization information.
|
|
if (universes_.empty())
|
|
return;
|
|
|
|
// framing layer header
|
|
auto frmheader = std::make_shared<EXTENDED::discovery_header>();
|
|
frmheader->source_name = name();
|
|
|
|
// universe discovery layer
|
|
auto header = std::make_shared<EXTENDED::DISCOVERY::discovery_list_header>();
|
|
|
|
// known universes
|
|
std::vector<EXTENDED::DISCOVERY::discoveredUniverse> list;
|
|
universes_mutext_.lock();
|
|
for (const auto & [num, univ] : universes_)
|
|
{
|
|
if (univ->provenance()->options.stream_terminated)
|
|
continue;
|
|
if (univ->destination.type != ACN::SDT::SDT_ADDR_NULL)
|
|
continue;
|
|
list.emplace_back(EXTENDED::DISCOVERY::discoveredUniverse());
|
|
list.back().universe = num;
|
|
}
|
|
universes_mutext_.unlock();
|
|
|
|
/// > \cite sACN 8.3 Page
|
|
/// >
|
|
/// > A single source may be transmitting on so many universes that the
|
|
/// > total number of universes it must include in its List of Universes
|
|
/// > will span multiple packets. Each one of these packets acts as a
|
|
/// > “page” of those universes.
|
|
///
|
|
/// > \cite sACN 8.4 Last Page
|
|
/// >
|
|
/// > The Universe Discovery Layer's Last Page field is an 8-bit field
|
|
/// > indicating the number of the final page being to be transmitted. ...
|
|
/// > Page numbers are indexed starting at 0.
|
|
header->last_page = list.size() / 512;
|
|
for (size_t i = 0; i <= header->last_page; i++)
|
|
{
|
|
/// > The Universe Discovery Layer's Page field is an 8-bit field
|
|
/// > indicating the page number of this E1.31 Universe Discovery Packet.
|
|
/// > Page numbers are indexed, starting at 0.
|
|
header->page = i;
|
|
|
|
// universe discover layer data
|
|
auto data = std::make_shared<EXTENDED::DISCOVERY::discovery_list_data>();
|
|
for (size_t n = i * 512; n < (i * 512) + 512; n++)
|
|
{
|
|
if (n > list.size())
|
|
break;
|
|
data->found.push_back(list.at(n));
|
|
}
|
|
|
|
// framing layer data
|
|
auto dlistpdu = std::make_shared<EXTENDED::DISCOVERY::Pdu>();
|
|
dlistpdu->setVector(VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST);
|
|
dlistpdu->setHeader(header);
|
|
dlistpdu->setData(data);
|
|
|
|
// framing layer
|
|
sendExtendedFrame(VECTOR_E131_EXTENDED_DISCOVERY, frmheader, dlistpdu,
|
|
sACN::IPv4MulticastAddress(E131_DISCOVERY_UNIVERSE));
|
|
sendExtendedFrame(VECTOR_E131_EXTENDED_DISCOVERY, frmheader, dlistpdu,
|
|
sACN::IPv6MulticastAddress(E131_DISCOVERY_UNIVERSE));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::sendExtendedFrame
|
|
* @param vector
|
|
* @param header
|
|
* @param data
|
|
* @param ip
|
|
*/
|
|
void Source::sendExtendedFrame(const uint16_t vector,
|
|
std::shared_ptr<ACN::PDU::pdu_header> header,
|
|
std::shared_ptr<ACN::PDU::pdu_data> data,
|
|
const ACN::SDT::UDP::ipAddress& ip)
|
|
{
|
|
if (!(vector == VECTOR_E131_EXTENDED_DISCOVERY ||
|
|
vector == VECTOR_E131_EXTENDED_SYNCHRONIZATION))
|
|
return;
|
|
|
|
auto framepdu = std::make_shared<EXTENDED::Pdu>();
|
|
framepdu->setVector(vector);
|
|
framepdu->setHeader(header);
|
|
framepdu->setData(data);
|
|
rlpSendUdp(VECTOR_ROOT_E131_EXTENDED, framepdu, ip);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::universe
|
|
* @param num
|
|
* @return
|
|
*/
|
|
Universe * Source::universe(const uint16_t num)
|
|
{
|
|
if (!universes_.count(num))
|
|
return nullptr;
|
|
|
|
return universes_.at(num);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Source::discovery_loop_
|
|
*/
|
|
void Source::discovery_loop_()
|
|
{
|
|
while (discovery_future_.wait_for(
|
|
std::chrono::milliseconds(E131_UNIVERSE_DISCOVER_INTERVAL))
|
|
== std::future_status::timeout)
|
|
discoveryAnnounce();
|
|
}
|
|
|
|
|
|
} // SACN
|