Skip to content

Commit

Permalink
dmx: work on removing libartnet dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Feb 10, 2025
1 parent 7fa7a48 commit 598d770
Show file tree
Hide file tree
Showing 13 changed files with 594 additions and 385 deletions.
4 changes: 4 additions & 0 deletions src/plugins/score-plugin-protocols/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ set(ARTNET_HDRS
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetProtocolFactory.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetProtocolSettingsWidget.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetSpecificSettings.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/DMXFixtureInstantiation.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/DMXProtocolCreation.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/FixtureDialog.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/LEDDialog.hpp"
)
Expand All @@ -216,6 +218,8 @@ set(ARTNET_SRCS
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetProtocolFactory.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetProtocolSettingsWidget.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/ArtnetSpecificSettingsSerialization.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/DMXFixtureInstantiation.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/DMXProtocolCreation.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/FixtureDialog.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Protocols/Artnet/LEDDialog.cpp"
)
Expand Down
325 changes: 8 additions & 317 deletions src/plugins/score-plugin-protocols/Protocols/Artnet/ArtnetDevice.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
#include <ossia/detail/config.hpp>

#include <QDebug>
#if defined(OSSIA_PROTOCOL_ARTNET)
#include "ArtnetDevice.hpp"
#include "ArtnetSpecificSettings.hpp"

#include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>

#include <Protocols/Artnet/DMXFixtureInstantiation.hpp>
#include <Protocols/Artnet/DMXProtocolCreation.hpp>

#include <score/document/DocumentContext.hpp>

#include <ossia/network/generic/generic_device.hpp>
#include <ossia/protocols/artnet/artnet_protocol.hpp>
#include <ossia/protocols/artnet/dmx_led_parameter.hpp>
#include <ossia/protocols/artnet/dmx_parameter.hpp>
#include <ossia/protocols/artnet/dmxusbpro_protocol.hpp>
#include <ossia/protocols/artnet/e131_protocol.hpp>

#include <QSerialPortInfo>

#include <wobjectimpl.h>
W_OBJECT_IMPL(Protocols::ArtnetDevice)
Expand All @@ -39,190 +34,6 @@ ArtnetDevice::ArtnetDevice(

ArtnetDevice::~ArtnetDevice() { }

namespace
{
struct fixture_setup_visitor
{
const Artnet::Fixture& fix;
const Artnet::Channel& chan;
ossia::net::node_base& fixt_node;
ossia::net::dmx_buffer& buffer;
int dmx_channel;
int operator()(const Artnet::SingleCapability& v) const noexcept
{
auto chan_node = fixt_node.create_child(chan.name.toStdString());
auto chan_param
= std::make_unique<ossia::net::dmx_parameter>(*chan_node, buffer, dmx_channel);

auto& node = *chan_node;
auto& p = *chan_param;

// FIXME this only works if the channels are joined for now
int bytes = 1;

for(auto& name : chan.fineChannels)
{
if(ossia::contains(fix.mode.channelNames, name))
{
bytes++;
}
}
p.m_bytes = bytes;

chan_node->set_parameter(std::move(chan_param));
p.set_default_value(chan.defaultValue);
p.set_value(chan.defaultValue);

if(!v.comment.isEmpty())
ossia::net::set_description(node, v.comment.toStdString());

return bytes;
}

int operator()(const std::vector<Artnet::RangeCapability>& v) const noexcept
{
std::vector<std::pair<std::string, uint8_t>> values;
std::string comment;
std::string default_preset;

// Parse all capabilities
for(auto& capa : v)
{
std::string name;
if(!capa.effectName.isEmpty())
name = capa.effectName.toStdString();
else
name = capa.type.toStdString();

if(chan.defaultValue >= capa.range.first && chan.defaultValue < capa.range.second)
default_preset = name;
values.push_back({name, capa.range.first});
}

// Make sure that all values have an unique name
{
std::vector<int> counts;
for(int i = 0; i < std::ssize(values); i++)
{
int n = 1;
for(int j = 0; j < i; j++)
{
if(values[j].first == values[i].first)
n++;
}
counts.push_back(n);
}
for(int i = 0; i < std::ssize(values); i++)
{
if(counts[i] > 1)
values[i].first += fmt::format(" {}", counts[i]);
}
}

// Write the comment
for(int i = 0; i < std::ssize(values); i++)
{
if(!v[i].comment.isEmpty())
{
comment += values[i].first + ": " + v[i].comment.toStdString() + "\n";
}
}

if(values.empty())
return 0;

auto chan_node = fixt_node.create_child(chan.name.toStdString());
auto chan_param
= std::make_unique<ossia::net::dmx_parameter>(*chan_node, buffer, dmx_channel);

chan_param->set_default_value(chan.defaultValue);
chan_param->set_value(chan.defaultValue);
auto& chan_param_ref = *chan_param;

if(!comment.empty())
ossia::net::set_description(*chan_node, comment);

chan_node->set_parameter(std::move(chan_param));
{
auto chan_enumnode = chan_node->create_child("preset");
auto chan_enumparam = std::make_unique<ossia::net::dmx_enum_parameter>(
*chan_enumnode, chan_param_ref, values);

auto& node = *chan_enumnode;
auto& p = *chan_enumparam;

if(default_preset.empty())
default_preset = values.front().first;
p.set_default_value(default_preset);
p.set_value(default_preset);

if(!comment.empty())
ossia::net::set_description(node, std::move(comment));

chan_enumnode->set_parameter(std::move(chan_enumparam));
}
return 1;
}
};
struct led_visitor
{
ossia::net::node_base& fixt_node;
ossia::net::dmx_buffer& buffer;
int dmx_channel;
int operator()(const Artnet::LEDStripLayout& v) const noexcept
{
auto chan_param = std::make_unique<ossia::net::dmx_led_parameter>(
fixt_node, buffer, dmx_channel, v.diodes.size(), v.length);

fixt_node.set_parameter(std::move(chan_param));
return v.channels();
}
int operator()(const Artnet::LEDPaneLayout& v) const noexcept { SCORE_ABORT; }
int operator()(const Artnet::LEDVolumeLayout& v) const noexcept { SCORE_ABORT; }
int operator()(ossia::monostate) { SCORE_ABORT; }
};

static void addArtnetFixture(
ossia::net::generic_device& dev, ossia::net::dmx_buffer& buffer,
const Artnet::Fixture& fix, int channels_per_universe)
{
int dmx_channel = 0;
int size = 0;
// For each fixture, we'll create a node.
auto fixt_node = dev.create_child(fix.fixtureName.toStdString());
if(!fixt_node)
return;

// For each channel, a sub-node that goes [0-255] or more depending on bit depth
for(auto& chan : fix.controls)
{
// Get the dmx offset of this channel:
const int channel_offset
= ossia::index_in_container(fix.mode.channelNames, chan.name);
if(channel_offset == -1)
continue;
dmx_channel = fix.address + channel_offset;

// Then for each range-based subchannels, sub-nodes with the relevant domains.
fixture_setup_visitor vis{fix, chan, *fixt_node, buffer, dmx_channel};

size = ossia::visit(vis, chan.capabilities);
}

if(fix.led)
{
dmx_channel = fix.address;
led_visitor vis{*fixt_node, buffer, dmx_channel};

size = ossia::visit(vis, fix.led);
}

int max = dmx_channel + size;
int max_universe_count = 1 + max / channels_per_universe;
if(buffer.universes() < max_universe_count)
buffer.set_universe_count(max_universe_count);
}
}
bool ArtnetDevice::reconnect()
{
disconnect();
Expand All @@ -231,146 +42,26 @@ bool ArtnetDevice::reconnect()
{
const auto& set = m_settings.deviceSpecificSettings.value<ArtnetSpecificSettings>();

// Convert the settings to the ossia format
ossia::net::dmx_config conf;
conf.autocreate = ossia::net::dmx_config::no_auto;
if(set.fixtures.empty())
{
if(set.transport == ArtnetSpecificSettings::ArtNet_MultiUniverse
|| set.transport == ArtnetSpecificSettings::E131_MultiUniverse)
{
conf.autocreate = ossia::net::dmx_config::just_universes;
}
else if(set.transport == ArtnetSpecificSettings::ArtNet)
{
conf.autocreate = ossia::net::dmx_config::channel_index;
}
else
{
conf.autocreate = ossia::net::dmx_config::just_index;
}
}
conf.frequency = set.rate;
conf.universe = set.universe;
conf.multicast = set.multicast;
conf.channels_per_universe = set.channels_per_universe;
conf.mode = set.mode == ArtnetSpecificSettings::Source
? ossia::net::dmx_config::source
: ossia::net::dmx_config::sink;

// Create the protocol
std::unique_ptr<ossia::net::dmx_protocol_base> artnet_proto;
switch(set.transport)
{
case ArtnetSpecificSettings::ArtNet:
case ArtnetSpecificSettings::ArtNetV2:
case ArtnetSpecificSettings::ArtNet_MultiUniverse: {
auto host = set.host.toStdString();
if(host.empty())
host = "0.0.0.0";

if(set.mode == ArtnetSpecificSettings::Source)
artnet_proto
= std::make_unique<ossia::net::artnet_protocol>(m_ctx, conf, host);
else
artnet_proto
= std::make_unique<ossia::net::artnet_input_protocol>(m_ctx, conf, host);
break;
}
case ArtnetSpecificSettings::E131:
case ArtnetSpecificSettings::E131_MultiUniverse: {
auto host = set.host.toStdString();
if(host.empty())
host = "0.0.0.0";

if(set.mode == ArtnetSpecificSettings::Source)
{
ossia::net::outbound_socket_configuration sock_conf;
sock_conf.host = host;
sock_conf.port = ossia::net::e131_protocol::default_port;

artnet_proto
= std::make_unique<ossia::net::e131_protocol>(m_ctx, conf, sock_conf);
}
else
{
ossia::net::inbound_socket_configuration sock_conf;
sock_conf.bind = host;
sock_conf.port = ossia::net::e131_protocol::default_port;

artnet_proto = std::make_unique<ossia::net::e131_input_protocol>(
m_ctx, conf, sock_conf);
}

break;
}
case ArtnetSpecificSettings::DMXUSBPRO:
case ArtnetSpecificSettings::DMXUSBPRO_Mk2:
case ArtnetSpecificSettings::OpenDMX_USB: {
ossia::net::serial_configuration sock_conf;

for(auto& p : QSerialPortInfo::availablePorts())
{
if(p.portName() == set.host)
{
sock_conf.port = p.systemLocation().toStdString();
break;
}
}
if(sock_conf.port.empty())
sock_conf.port = set.host.toStdString();

int version = -1;
switch(set.transport)
{
case ArtnetSpecificSettings::DMXUSBPRO:
version = 1;
sock_conf.baud_rate = 115200;
sock_conf.stop_bits = ossia::net::serial_configuration::two;
break;

case ArtnetSpecificSettings::DMXUSBPRO_Mk2:
version = 2;
sock_conf.baud_rate = 115200;
sock_conf.stop_bits = ossia::net::serial_configuration::two;
break;

case ArtnetSpecificSettings::OpenDMX_USB:
version = 3;
sock_conf.baud_rate = 250000;
sock_conf.stop_bits = ossia::net::serial_configuration::two;
break;

default:
break;
}

artnet_proto = std::make_unique<ossia::net::dmxusbpro_protocol>(
m_ctx, conf, sock_conf, version);
break;
}
}

// Create the device
if(artnet_proto)
if(auto artnet_proto = Protocols::instantiateDMXProtocol(m_ctx, set))
{
auto& proto = *artnet_proto;
SCORE_ASSERT(proto.buffer().universes() >= 1);

auto dev = std::make_unique<ossia::net::generic_device>(
std::move(artnet_proto), settings().name.toStdString());

if(!set.fixtures.empty())
SCORE_ASSERT(proto.buffer().universes() == 1);
for(auto& fixt : set.fixtures)
{
addArtnetFixture(*dev, proto.buffer(), fixt, conf.channels_per_universe);
addArtnetFixture(*dev, proto.buffer(), fixt, set.channels_per_universe);
}

if(set.mode == ArtnetSpecificSettings::Sink)
if(auto p = dynamic_cast<ossia::net::dmx_input_protocol_base*>(&proto))
p->create_channel_map();
m_dev = std::move(dev);
deviceChanged(nullptr, m_dev.get());
}
deviceChanged(nullptr, m_dev.get());
}
catch(const std::runtime_error& e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Protocols
{
class ArtnetDevice final : public Device::OwningDeviceInterface
{

W_OBJECT(ArtnetDevice)
public:
ArtnetDevice(
Expand Down
Loading

0 comments on commit 598d770

Please sign in to comment.