worker thread for streaming sACN
This commit is contained in:
parent
b3256be388
commit
5c0cc41af5
14
README.md
14
README.md
|
@ -43,7 +43,7 @@ may not be inherited from sibling `PDU` in the data.
|
|||
| :- | :-: | :-: |
|
||||
| 0 to 10 V Analog Control | E1.3 | |
|
||||
| USITT DMX512-A | E1.11 | Data Abstraction |
|
||||
| ACN Root Layer Protocol (RLP) | E1.17 | Rx |
|
||||
| ACN Root Layer Protocol (RLP) | E1.17 | ✓ |
|
||||
| ACN Session Data Transport Protocol (SDT) | E1.17 | |
|
||||
| ACN Device Management Protocol (DMP) | E1.17 | Limited |
|
||||
| ACN Device Description Language (DDL) | E1.17 | |
|
||||
|
@ -53,9 +53,9 @@ may not be inherited from sibling `PDU` in the data.
|
|||
| EPI 13 - Allocation of Internet Protocol Version 4 Addresses to ACN Hosts | E1.17 | |
|
||||
| EPI 15 - ACN Allocation of Multicast Addresses on IPv4 Networks | E1.17 | |
|
||||
| EPI 16 - ESTA Registered Names and Identifiers | E1.17 | |
|
||||
| EPI 17 - ACN Root Layer Protocol Operation on UDP | E1.17 | Rx |
|
||||
| EPI 17 - ACN Root Layer Protocol Operation on UDP | E1.17 | ✓ |
|
||||
| EPI 18 - Operation of SDT on UDP Networks | E1.17 | |
|
||||
| EPI 19 - ACN Discovery on IP Networks | E1.17 | |
|
||||
| EPI 19 - ACN Discovery on IP Networks | E1.17 | Names |
|
||||
| EPI 20 - MTU Size for ACN on IPv4 Networks | E1.17 | |
|
||||
| EPI 22 - DDL Core Modules for ACN Devices | E1.17 | |
|
||||
| Remote Device Management (RDM) | E1.20 | Responder |
|
||||
|
@ -68,12 +68,12 @@ may not be inherited from sibling `PDU` in the data.
|
|||
| EPI 25 - Time Reference in ACN Systems Using SNTP and NTP | E1.30-3 | |
|
||||
| EPI 26 - DDL Extensions for DMX and RDM Devices | E1.30-4 | |
|
||||
| EPI 32 - Identification of Draft DDL Modules | E1.30-10 | |
|
||||
| EPI 33 - ACN RLP Operation on TCP | E1.30-11 | |
|
||||
| Streaming ACN (sACN) | E1.31 | Partial |
|
||||
| EPI 33 - ACN RLP Operation on TCP | E1.30-11 | ✓ |
|
||||
| Streaming ACN (sACN) | E1.31 | Data/Discovery |
|
||||
| sACN Receiver | E1.31 | ✓ |
|
||||
| sACN Source | E1.31 | Limited |
|
||||
| sACN Source | E1.31 | ✓ |
|
||||
| sACN Data | E1.31 | ✓ |
|
||||
| sACN Sync | E1.31 | ✓ |
|
||||
| sACN Sync | E1.31 | Rx |
|
||||
| sACN Discovery | E1.31 | ✓ |
|
||||
| sACN Preview | E1.31 | - |
|
||||
| RDMNet | E1.33 | |
|
||||
|
|
|
@ -36,6 +36,7 @@ Universe::Universe(Source* src)
|
|||
, active_data_slots(0)
|
||||
, provenance_(std::make_shared<DATA::data_header>())
|
||||
, source_(src)
|
||||
, tx_worker_(&Universe::tx_loop_, this)
|
||||
{
|
||||
destination.type = ACN::SDT::SDT_ADDR_NULL;
|
||||
}
|
||||
|
@ -46,6 +47,17 @@ Universe::Universe(Source* src)
|
|||
*/
|
||||
Universe::~Universe()
|
||||
{
|
||||
// shut down tx worker thread
|
||||
if (tx_worker_.joinable())
|
||||
{
|
||||
tx_control_mutex_.lock();
|
||||
tx_enable_ = false;
|
||||
tx_control_mutex_.unlock();
|
||||
tx_request_.notify_all();
|
||||
tx_worker_.join();
|
||||
}
|
||||
|
||||
// delete sync data pointer
|
||||
delete sync_data_;
|
||||
}
|
||||
|
||||
|
@ -169,6 +181,9 @@ void Universe::setValue (const uint16_t address, const uint8_t value)
|
|||
|
||||
// set the value
|
||||
DMX::Universe::setValue(address, value);
|
||||
|
||||
// request sACN message to be sent
|
||||
tx_request_.notify_all();
|
||||
}
|
||||
|
||||
|
||||
|
@ -196,6 +211,9 @@ void Universe::setValue (const uint16_t start, const uint16_t footprint,
|
|||
|
||||
// set the values
|
||||
DMX::Universe::setValue(start, footprint, data);
|
||||
|
||||
// request sACN message to be sent
|
||||
tx_request_.notify_all();
|
||||
}
|
||||
|
||||
|
||||
|
@ -304,4 +322,91 @@ void Universe::sACNsend() const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Universe::tx_loop_
|
||||
*/
|
||||
void Universe::tx_loop_()
|
||||
{
|
||||
// rx universes, by definition, won't have a source set
|
||||
if (!source_)
|
||||
return;
|
||||
|
||||
// run at least 1 loop
|
||||
bool enable = true;
|
||||
|
||||
/// > \cite sACN 6.6.1 Transmission Rate
|
||||
/// >
|
||||
/// > E1.31 sources shall not transmit packets for a given universe number
|
||||
/// > at a rate which exceeds the maximum refresh rate specified in
|
||||
/// > E1.11 \cite DMX
|
||||
///
|
||||
/// > \cite DMX Table 6 - Timing Diagram Values - output of transmitting UART
|
||||
/// >
|
||||
/// > Minimum Update Time for 513 slots : 22.7ms
|
||||
std::chrono::milliseconds minimum_update_time(23);
|
||||
|
||||
/// > \cite sACN 6.6.2 Null START Code Transmission Requirements in E1.31
|
||||
/// > Data Packets
|
||||
/// >
|
||||
/// > 1. Three packets containing the non-changing Property Values
|
||||
/// > (corresponding to DMX512-A slot data) shall be sent before the
|
||||
/// > initiation of transmission suppression.
|
||||
DMX::DimmerData last_null_data({0});
|
||||
uint retransmission_count = 0;
|
||||
|
||||
/// > 2. Thereafter, a single keep-alive packet shall be transmitted at
|
||||
/// > intervals of between 800mS and 1000mS.
|
||||
std::chrono::milliseconds keep_alive_interval(800);
|
||||
|
||||
std::chrono::system_clock::time_point last_sent;
|
||||
|
||||
// I don't fully understand the semantics of std::conditional_variable,
|
||||
// but it requires this mutex in the sleeping thread.
|
||||
std::mutex mtx;
|
||||
|
||||
while (enable)
|
||||
{
|
||||
// enforce strict minimum update times
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(std::chrono::system_clock::now() - last_sent);
|
||||
if (elapsed < minimum_update_time)
|
||||
std::this_thread::sleep_for(minimum_update_time - elapsed);
|
||||
|
||||
// check for control permission to continue looping
|
||||
tx_control_mutex_.lock();
|
||||
enable = tx_enable_;
|
||||
tx_control_mutex_.unlock();
|
||||
|
||||
// if shutting down, send stream_terminated in this last update
|
||||
if (!enable)
|
||||
provenance_->options.stream_terminated = true;
|
||||
|
||||
// see if this is new data or re-transmitting
|
||||
auto sleep = std::chrono::milliseconds(minimum_update_time);
|
||||
null_start_mutex.lock();
|
||||
if (null_start_data != last_null_data)
|
||||
{
|
||||
retransmission_count = 0;
|
||||
last_null_data = null_start_data;
|
||||
}
|
||||
else
|
||||
if (++retransmission_count >= 2)
|
||||
sleep = std::chrono::milliseconds(keep_alive_interval);
|
||||
null_start_mutex.unlock();
|
||||
|
||||
// send the sACN message
|
||||
sACNsend();
|
||||
last_sent = std::chrono::system_clock::now();
|
||||
|
||||
// sleep before the next cycle
|
||||
if (enable)
|
||||
{
|
||||
mtx.lock();
|
||||
tx_request_.wait_for(mtx, sleep);
|
||||
mtx.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace SACN
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
#include "dmx/universe.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace sACN {
|
||||
|
@ -97,6 +99,12 @@ private:
|
|||
/// > between the sequence number of an E1.31 Synchronization Packet and
|
||||
/// > the sequence number of an E1.31 Data Packet on that same universe.
|
||||
uint8_t sync_sequence_ = 0;
|
||||
|
||||
std::condition_variable_any tx_request_;
|
||||
std::mutex tx_control_mutex_;
|
||||
bool tx_enable_ = true;
|
||||
std::thread tx_worker_;
|
||||
void tx_loop_();
|
||||
};
|
||||
|
||||
} // SACN namespace
|
||||
|
|
Loading…
Reference in New Issue