#include "qsacnnode.h" #include #include #include #include /** * @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) , QObject(parent) , rx_socket_(new QUdpSocket(this)) , tx_socket_(new QUdpSocket(this)) { rx_socket_->bind(QHostAddress::AnyIPv4, sACN::ACN_SDT_MULTICAST_PORT); tx_socket_->bind(QHostAddress::AnyIPv4); connect(rx_socket_, &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 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()) rx_socket_->joinMulticastGroup(ip, iface); } } else { qDebug() << "Disabling IPv4"; foreach (const auto & ip, groups) { qDebug() << "Leaving IGMP Group" << ip.toString(); for (QNetworkInterface &iface : QNetworkInterface::allInterfaces()) rx_socket_->leaveMulticastGroup(ip, iface); } } } void QSacnNode::setIPv6(const bool enable) { Node::setIPv6(enable); QList 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()) rx_socket_->joinMulticastGroup(ip, iface); } } else { qDebug() << "Disabling IPv6"; foreach (const auto & ip, groups) { qDebug() << "Leaving MLD Group" << ip.toString(); for (QNetworkInterface &iface : QNetworkInterface::allInterfaces()) rx_socket_->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()) rx_socket_->joinMulticastGroup(IPv4MulticastAddress(num), iface); } if (enable_IPv6) { qDebug() << "Joining MLD Group" << IPv6MulticastAddress(num).toString(); for (QNetworkInterface &iface : QNetworkInterface::allInterfaces()) rx_socket_->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()) rx_socket_->leaveMulticastGroup(IPv4MulticastAddress(num), iface); } if (enable_IPv6) { qDebug() << "Leaving MLD Group" << IPv6MulticastAddress(num).toString(); for (QNetworkInterface &iface : QNetworkInterface::allInterfaces()) rx_socket_->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)); delete tx_universes.take(num); } } /** * @brief QSacnNode::UdpPayloadReceiver */ void QSacnNode::udpReceive() { while (rx_socket_->hasPendingDatagrams()) { auto datagram = rx_socket_->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 QUdpSocket::IPv4Protocol: if (!enable_IPv4) return; break; case QUdpSocket::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( reinterpret_cast(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) const { 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; } tx_socket_->writeDatagram(reinterpret_cast(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) const { Q_UNUSED(stream) Q_UNUSED(ip) qDebug() << "sACN uses UDP only. Stop trying to send TCP!"; }