300 lines
7.3 KiB
C++
300 lines
7.3 KiB
C++
#include "qsacnnode.h"
|
|
|
|
#include <QDebug>
|
|
#include <QMetaEnum>
|
|
#include <QNetworkDatagram>
|
|
#include <QNetworkInterface>
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::QSacnNode
|
|
* @param parent
|
|
* @param cid
|
|
* @param fctn
|
|
*/
|
|
QSacnNode::QSacnNode(QObject *parent, QUuid cid, QString fctn, bool ipv4, bool ipv6)
|
|
: Component(UUID::uuid(cid.toString().toStdString()), fctn.toStdString(), ipv4, ipv6)
|
|
, QUdpSocket(parent)
|
|
{
|
|
bind(QHostAddress::AnyIPv4, sACN::ACN_SDT_MULTICAST_PORT);
|
|
|
|
connect(this, &QUdpSocket::readyRead,
|
|
this, &QSacnNode::udpReceive);
|
|
|
|
sACN::Receiver::onDiscovered([this](){ emit discoveryUpdates(); });
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::~QSacnNode
|
|
*/
|
|
QSacnNode::~QSacnNode()
|
|
{
|
|
if (Receiver::discoveryEnabled())
|
|
QSacnNode::unsubscribe(sACN::E131_DISCOVERY_UNIVERSE);
|
|
|
|
foreach (const auto num, rx_universes.keys())
|
|
QSacnNode::unsubscribe(num);
|
|
|
|
foreach (const auto num, tx_universes.keys())
|
|
QSacnNode::terminate(num);
|
|
}
|
|
|
|
|
|
void QSacnNode::setIPv4(const bool enable)
|
|
{
|
|
Node::setIPv4(enable);
|
|
|
|
QList<QHostAddress> groups;
|
|
foreach (const auto & universe, rx_universes)
|
|
groups.append(IPv4MulticastAddress(universe->number()));
|
|
if (discoveryEnabled())
|
|
groups.append(IPv4MulticastAddress(sACN::E131_DISCOVERY_UNIVERSE));
|
|
|
|
if (enable)
|
|
{
|
|
qDebug() << "Enabling IPv4";
|
|
foreach (const auto & ip, groups)
|
|
{
|
|
qDebug() << "Joining IGMP Group" << ip.toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
joinMulticastGroup(ip, iface);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Disabling IPv4";
|
|
foreach (const auto & ip, groups)
|
|
{
|
|
qDebug() << "Leaving IGMP Group" << ip.toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
leaveMulticastGroup(ip, iface);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QSacnNode::setIPv6(const bool enable)
|
|
{
|
|
Node::setIPv6(enable);
|
|
|
|
QList<QHostAddress> groups;
|
|
foreach (const auto & universe, rx_universes)
|
|
groups.append(IPv6MulticastAddress(universe->number()));
|
|
if (discoveryEnabled())
|
|
groups.append(IPv6MulticastAddress(sACN::E131_DISCOVERY_UNIVERSE));
|
|
|
|
if (enable)
|
|
{
|
|
qDebug() << "Enabling IPv6";
|
|
foreach (const auto & ip, groups)
|
|
{
|
|
qDebug() << "Joining MLD Group" << ip.toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
joinMulticastGroup(ip, iface);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Disabling IPv6";
|
|
foreach (const auto & ip, groups)
|
|
{
|
|
qDebug() << "Leaving MLD Group" << ip.toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
leaveMulticastGroup(ip, iface);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::subscribe
|
|
* @param num
|
|
*/
|
|
void QSacnNode::subscribe(const uint16_t num)
|
|
{
|
|
if (Receiver::universe(num)) // already subscribed
|
|
return;
|
|
|
|
switch (num) {
|
|
case sACN::E131_DISCOVERY_UNIVERSE:
|
|
{
|
|
if (discoveryEnabled())
|
|
return;
|
|
qDebug() << "Enabling sACN Discovery";
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
qDebug() << "Subscribing to universe" << QString::number(num);
|
|
Receiver::subscribe(num);
|
|
rx_universes.emplace(num, new QSacnUniverse(this, Receiver::universe(num)));
|
|
emit subscribing(rx_universes.value(num));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (enable_IPv4)
|
|
{
|
|
qDebug() << "Joining IGMP Group" << IPv4MulticastAddress(num).toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
joinMulticastGroup(IPv4MulticastAddress(num), iface);
|
|
}
|
|
if (enable_IPv6)
|
|
{
|
|
qDebug() << "Joining MLD Group" << IPv6MulticastAddress(num).toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
joinMulticastGroup(IPv6MulticastAddress(num), iface);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::unsubscribe
|
|
* @param num
|
|
*/
|
|
void QSacnNode::unsubscribe(const uint16_t num)
|
|
{
|
|
switch (num) {
|
|
case sACN::E131_DISCOVERY_UNIVERSE:
|
|
{
|
|
if (!discoveryEnabled())
|
|
return;
|
|
qDebug() << "Disabling sACN Discovery";
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (Receiver::universe(num)) {
|
|
qDebug() << "Unsubscribing from universe " << QString::number(num);
|
|
emit unsubscribing(rx_universes.value(num));
|
|
rx_universes.take(num)->deleteLater();
|
|
Receiver::unsubscribe(num);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (enable_IPv4)
|
|
{
|
|
qDebug() << "Leaving IGMP Group" << IPv4MulticastAddress(num).toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
leaveMulticastGroup(IPv4MulticastAddress(num), iface);
|
|
}
|
|
if (enable_IPv6)
|
|
{
|
|
qDebug() << "Leaving MLD Group" << IPv6MulticastAddress(num).toString();
|
|
for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
|
|
leaveMulticastGroup(IPv6MulticastAddress(num), iface);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::create
|
|
* @param num
|
|
*/
|
|
void QSacnNode::create(const uint16_t num)
|
|
{
|
|
if (Source::universe(num)) // already created
|
|
return;
|
|
|
|
qDebug() << "Creating universe " << QString::number(num);
|
|
Source::create(num);
|
|
tx_universes.emplace(num, new QSacnUniverse(this, Source::universe(num)));
|
|
emit creating(tx_universes.value(num));
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::terminate
|
|
* @param num
|
|
*/
|
|
void QSacnNode::terminate(const uint16_t num)
|
|
{
|
|
if (Source::universe(num)) {
|
|
qDebug() << "Terminating universe " << QString::number(num);
|
|
Source::terminate(num);
|
|
emit terminating(tx_universes.value(num));
|
|
tx_universes.take(num)->deleteLater();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::UdpPayloadReceiver
|
|
*/
|
|
void QSacnNode::udpReceive()
|
|
{
|
|
while (hasPendingDatagrams())
|
|
{
|
|
auto datagram = receiveDatagram();
|
|
|
|
// expecting IANA registered Session Data Transport traffic
|
|
if (datagram.destinationPort() != sACN::ACN_SDT_MULTICAST_PORT)
|
|
return;
|
|
|
|
// only receive over configured protocols
|
|
switch (datagram.senderAddress().protocol()) {
|
|
case IPv4Protocol:
|
|
if (!enable_IPv4)
|
|
return;
|
|
break;
|
|
case IPv6Protocol:
|
|
if (!enable_IPv6)
|
|
return;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// wrap a PDU i/o stream around the QNetworkDatagram data buffer
|
|
auto data = datagram.data();
|
|
auto stream = std::make_shared<ACN::PDU::pdu_stream>(
|
|
reinterpret_cast<uint8_t*>(data.data()), data.length());
|
|
UdpPayloadReceiver(stream);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::sendUDP
|
|
* @param stream
|
|
* @param ip
|
|
*/
|
|
void QSacnNode::sendUDP(const ACN::PDU::Stream stream,
|
|
const ACN::SDT::UDP::ipAddress& ip)
|
|
{
|
|
QHostAddress addr;
|
|
switch (ip.type) {
|
|
case ACN::SDT::SDT_ADDR_IPV4:
|
|
if (!enable_IPv4)
|
|
return;
|
|
addr = QHostAddress(ip.address.ipv4.value);
|
|
break;
|
|
case ACN::SDT::SDT_ADDR_IPV6:
|
|
if (!enable_IPv6)
|
|
return;
|
|
addr = QHostAddress(ip.address.ipv6.bytes);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
writeDatagram(reinterpret_cast<char*>(stream->base()), stream->size(), addr, ip.port);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief QSacnNode::sendTCP
|
|
* @param stream
|
|
* @param ip
|
|
*/
|
|
void QSacnNode::sendTCP(const ACN::PDU::Stream stream,
|
|
const ACN::SDT::UDP::ipAddress& ip)
|
|
{
|
|
Q_UNUSED(stream)
|
|
Q_UNUSED(ip)
|
|
qDebug() << "sACN uses UDP only. Stop trying to send TCP!";
|
|
}
|