mirror of
https://github.com/sudoxnym/open-hyperion.git
synced 2026-04-14 11:36:31 +00:00
Initial E1.31 receiver plugin is functional, automatically maps controllers to universes
This commit is contained in:
parent
3d2d86d129
commit
b7fbb150fb
11 changed files with 944 additions and 123 deletions
|
|
@ -144,7 +144,63 @@ typedef struct
|
||||||
|
|
||||||
typedef void (*RGBControllerCallback)(void *);
|
typedef void (*RGBControllerCallback)(void *);
|
||||||
|
|
||||||
class RGBController
|
class RGBControllerInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void SetupColors() = 0;
|
||||||
|
|
||||||
|
virtual RGBColor GetLED(unsigned int led) = 0;
|
||||||
|
virtual void SetLED(unsigned int led, RGBColor color) = 0;
|
||||||
|
virtual void SetAllLEDs(RGBColor color) = 0;
|
||||||
|
virtual void SetAllZoneLEDs(int zone, RGBColor color) = 0;
|
||||||
|
|
||||||
|
virtual int GetMode() = 0;
|
||||||
|
virtual void SetMode(int mode) = 0;
|
||||||
|
|
||||||
|
virtual unsigned char * GetDeviceDescription(unsigned int protocol_version) = 0;
|
||||||
|
virtual void ReadDeviceDescription(unsigned char* data_buf, unsigned int protocol_version) = 0;
|
||||||
|
|
||||||
|
virtual unsigned char * GetModeDescription(int mode) = 0;
|
||||||
|
virtual void SetModeDescription(unsigned char* data_buf) = 0;
|
||||||
|
|
||||||
|
virtual unsigned char * GetColorDescription() = 0;
|
||||||
|
virtual void SetColorDescription(unsigned char* data_buf) = 0;
|
||||||
|
|
||||||
|
virtual unsigned char * GetZoneColorDescription(int zone) = 0;
|
||||||
|
virtual void SetZoneColorDescription(unsigned char* data_buf) = 0;
|
||||||
|
|
||||||
|
virtual unsigned char * GetSingleLEDColorDescription(int led) = 0;
|
||||||
|
virtual void SetSingleLEDColorDescription(unsigned char* data_buf) = 0;
|
||||||
|
|
||||||
|
virtual void RegisterUpdateCallback(RGBControllerCallback new_callback, void * new_callback_arg) = 0;
|
||||||
|
virtual void UnregisterUpdateCallback(void * callback_arg) = 0;
|
||||||
|
virtual void SignalUpdate() = 0;
|
||||||
|
|
||||||
|
virtual void UpdateLEDs() = 0;
|
||||||
|
//virtual void UpdateZoneLEDs(int zone) = 0;
|
||||||
|
//virtual void UpdateSingleLED(int led) = 0;
|
||||||
|
|
||||||
|
virtual void UpdateMode() = 0;
|
||||||
|
|
||||||
|
virtual void DeviceCallThreadFunction() = 0;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Functions to be implemented in device implementation |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
virtual void SetupZones() = 0;
|
||||||
|
|
||||||
|
virtual void ResizeZone(int zone, int new_size) = 0;
|
||||||
|
|
||||||
|
virtual void DeviceUpdateLEDs() = 0;
|
||||||
|
virtual void UpdateZoneLEDs(int zone) = 0;
|
||||||
|
virtual void UpdateSingleLED(int led) = 0;
|
||||||
|
|
||||||
|
virtual void DeviceUpdateMode() = 0;
|
||||||
|
|
||||||
|
virtual void SetCustomMode() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RGBController : public RGBControllerInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string name; /* controller name */
|
std::string name; /* controller name */
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ public:
|
||||||
|
|
||||||
virtual std::vector<RGBController*> & GetRGBControllers() = 0;
|
virtual std::vector<RGBController*> & GetRGBControllers() = 0;
|
||||||
|
|
||||||
|
virtual unsigned int GetDetectionPercent() = 0;
|
||||||
|
|
||||||
virtual std::string GetConfigurationDirectory() = 0;
|
virtual std::string GetConfigurationDirectory() = 0;
|
||||||
|
|
||||||
virtual std::vector<NetworkClient*>& GetClients() = 0;
|
virtual std::vector<NetworkClient*>& GetClients() = 0;
|
||||||
|
|
|
||||||
236
OpenRGBE131ReceiverDialog.cpp
Normal file
236
OpenRGBE131ReceiverDialog.cpp
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
#include "OpenRGBE131ReceiverDialog.h"
|
||||||
|
#include "ui_OpenRGBE131ReceiverDialog.h"
|
||||||
|
|
||||||
|
#include <e131.h>
|
||||||
|
|
||||||
|
#define MAX_LEDS_PER_UNIVERSE 170
|
||||||
|
|
||||||
|
void DeviceListChanged_Callback(void * this_ptr)
|
||||||
|
{
|
||||||
|
((OpenRGBE131ReceiverDialog *)this_ptr)->DeviceListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RGBController* controller;
|
||||||
|
unsigned int start_channel;
|
||||||
|
unsigned int start_led;
|
||||||
|
unsigned int num_leds;
|
||||||
|
bool update;
|
||||||
|
} universe_member;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int universe;
|
||||||
|
std::vector<universe_member> members;
|
||||||
|
} universe_entry;
|
||||||
|
|
||||||
|
static std::vector<universe_entry> universe_list;
|
||||||
|
|
||||||
|
OpenRGBE131ReceiverDialog::OpenRGBE131ReceiverDialog(ResourceManager* manager, QWidget *parent) : QWidget(parent), ui(new Ui::OpenRGBE131ReceiverDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
resource_manager = manager;
|
||||||
|
|
||||||
|
/*-------------------------------------------------*\
|
||||||
|
| Register device list change callback |
|
||||||
|
\*-------------------------------------------------*/
|
||||||
|
resource_manager->RegisterDeviceListChangeCallback(DeviceListChanged_Callback, this);
|
||||||
|
resource_manager->RegisterDetectionProgressCallback(DeviceListChanged_Callback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenRGBE131ReceiverDialog::~OpenRGBE131ReceiverDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenRGBE131ReceiverDialog::DeviceListChanged()
|
||||||
|
{
|
||||||
|
if(resource_manager->GetDetectionPercent() == 100)
|
||||||
|
{
|
||||||
|
universe_list.clear();
|
||||||
|
|
||||||
|
for(unsigned int controller_idx = 0; controller_idx < resource_manager->GetRGBControllers().size(); controller_idx++)
|
||||||
|
{
|
||||||
|
RGBController* controller = resource_manager->GetRGBControllers()[controller_idx];
|
||||||
|
|
||||||
|
unsigned int num_universes = 1 + ((controller->leds.size() * 3) / 512);
|
||||||
|
unsigned int remaining_leds = controller->leds.size();
|
||||||
|
unsigned int start_led = 0;
|
||||||
|
|
||||||
|
for(unsigned int universe_idx = 0; universe_idx < num_universes; universe_idx++)
|
||||||
|
{
|
||||||
|
// For now, create universes sequentially per controller
|
||||||
|
universe_entry new_entry;
|
||||||
|
|
||||||
|
new_entry.universe = universe_list.size() + 1;
|
||||||
|
|
||||||
|
// Add members as needed
|
||||||
|
universe_member new_member;
|
||||||
|
|
||||||
|
new_member.controller = resource_manager->GetRGBControllers()[controller_idx];
|
||||||
|
new_member.start_channel = 1;
|
||||||
|
new_member.start_led = start_led;
|
||||||
|
new_member.num_leds = remaining_leds;
|
||||||
|
new_member.update = 1;
|
||||||
|
|
||||||
|
// Limit the number of LEDs
|
||||||
|
if(new_member.num_leds > MAX_LEDS_PER_UNIVERSE)
|
||||||
|
{
|
||||||
|
new_member.num_leds = MAX_LEDS_PER_UNIVERSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update start LED
|
||||||
|
start_led = start_led + new_member.num_leds;
|
||||||
|
|
||||||
|
// Update remaining LED count
|
||||||
|
remaining_leds = remaining_leds - new_member.num_leds;
|
||||||
|
|
||||||
|
new_entry.members.push_back(new_member);
|
||||||
|
|
||||||
|
universe_list.push_back(new_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->E131TreeView->clear();
|
||||||
|
|
||||||
|
ui->E131TreeView->setColumnCount(5);
|
||||||
|
ui->E131TreeView->header()->setStretchLastSection(false);
|
||||||
|
ui->E131TreeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
|
ui->E131TreeView->setHeaderLabels(QStringList() << "Universe" << "Start Channel" << "Start LED" << "LED Count" << "Update");
|
||||||
|
|
||||||
|
for(unsigned int universe_idx = 0; universe_idx < universe_list.size(); universe_idx++)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem* new_universe_entry = new QTreeWidgetItem(ui->E131TreeView);
|
||||||
|
|
||||||
|
new_universe_entry->setText(0, QString::fromStdString("Universe " + std::to_string(universe_list[universe_idx].universe)));
|
||||||
|
|
||||||
|
for(unsigned int member_idx = 0; member_idx < universe_list[universe_idx].members.size(); member_idx++)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem* new_member_entry = new QTreeWidgetItem(new_universe_entry);
|
||||||
|
|
||||||
|
new_member_entry->setText(0, QString::fromStdString((universe_list[universe_idx].members[member_idx].controller->name)));
|
||||||
|
new_member_entry->setText(1, QString::number(universe_list[universe_idx].members[member_idx].start_channel));
|
||||||
|
new_member_entry->setText(2, QString::number(universe_list[universe_idx].members[member_idx].start_led));
|
||||||
|
new_member_entry->setText(3, QString::number(universe_list[universe_idx].members[member_idx].num_leds));
|
||||||
|
new_member_entry->setText(4, QString::number(universe_list[universe_idx].members[member_idx].update));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->E131TreeView->expandAll();
|
||||||
|
|
||||||
|
// Start the receiver thread
|
||||||
|
E131ReceiverThread = new std::thread(&OpenRGBE131ReceiverDialog::E131ReceiverThreadFunction, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenRGBE131ReceiverDialog::E131ReceiverThreadFunction()
|
||||||
|
{
|
||||||
|
int sockfd;
|
||||||
|
e131_packet_t packet;
|
||||||
|
e131_error_t error;
|
||||||
|
uint8_t last_seq = 0x00;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Create a socket for E1.31 |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
sockfd = e131_socket();
|
||||||
|
|
||||||
|
if(sockfd < 0)
|
||||||
|
{
|
||||||
|
printf("Socket error\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Bind the socket to the default E1.31 port |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(e131_bind(sockfd, E131_DEFAULT_PORT) < 0)
|
||||||
|
{
|
||||||
|
printf("Bind error\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Join the configured universes |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
for(unsigned int universe_idx = 0; universe_idx < universe_list.size(); universe_idx++)
|
||||||
|
{
|
||||||
|
if(e131_multicast_join(sockfd, universe_list[universe_idx].universe) < 0)
|
||||||
|
{
|
||||||
|
printf("Join error\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Loop to receive E1.31 packets |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if(e131_recv(sockfd, &packet) < 0)
|
||||||
|
{
|
||||||
|
printf("Receive error\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((error = e131_pkt_validate(&packet)) != E131_ERR_NONE)
|
||||||
|
{
|
||||||
|
printf("Validation error: %s\r\n", e131_strerror(error));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(e131_pkt_discard(&packet, last_seq))
|
||||||
|
// {
|
||||||
|
// //printf("Warning: packet out of order received\r\n");
|
||||||
|
// last_seq = packet.frame.seq_number;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
last_seq = packet.frame.seq_number;
|
||||||
|
|
||||||
|
/*-------------------------------------------------*\
|
||||||
|
| If received packet is from a valid universe, |
|
||||||
|
| update the corresponding devices |
|
||||||
|
\*-------------------------------------------------*/
|
||||||
|
unsigned int universe = ntohs(packet.frame.universe) - 1;
|
||||||
|
|
||||||
|
for(unsigned int universe_idx = 0; universe_idx < universe_list.size(); universe_idx++)
|
||||||
|
{
|
||||||
|
if(universe_list[universe_idx].universe == universe)
|
||||||
|
{
|
||||||
|
for(unsigned int member_idx = 0; member_idx < universe_list[universe_idx].members.size(); member_idx++)
|
||||||
|
{
|
||||||
|
RGBController* controller = universe_list[universe_idx].members[member_idx].controller;
|
||||||
|
unsigned int channel = universe_list[universe_idx].members[member_idx].start_channel;
|
||||||
|
unsigned int start_led = universe_list[universe_idx].members[member_idx].start_led;
|
||||||
|
unsigned int num_leds = universe_list[universe_idx].members[member_idx].num_leds;
|
||||||
|
bool update = universe_list[universe_idx].members[member_idx].update;
|
||||||
|
|
||||||
|
for(unsigned int led_idx = start_led; led_idx < (start_led + num_leds); led_idx++)
|
||||||
|
{
|
||||||
|
// Calculate channels for this LED
|
||||||
|
unsigned int red_idx = channel + 0;
|
||||||
|
unsigned int grn_idx = channel + 1;
|
||||||
|
unsigned int blu_idx = channel + 2;
|
||||||
|
|
||||||
|
// Get color out of E1.31 packet
|
||||||
|
RGBColor led_color = ToRGBColor(packet.dmp.prop_val[red_idx],
|
||||||
|
packet.dmp.prop_val[grn_idx],
|
||||||
|
packet.dmp.prop_val[blu_idx]);
|
||||||
|
|
||||||
|
// Set LED color in controller
|
||||||
|
controller->colors[led_idx] = led_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If configured to update the device, update
|
||||||
|
if(update)
|
||||||
|
{
|
||||||
|
controller->UpdateLEDs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
OpenRGBE131ReceiverDialog.h
Normal file
30
OpenRGBE131ReceiverDialog.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef OPENRGBE131RECEIVERDIALOG_H
|
||||||
|
#define OPENRGBE131RECEIVERDIALOG_H
|
||||||
|
|
||||||
|
#include "ResourceManager.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class OpenRGBE131ReceiverDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenRGBE131ReceiverDialog : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OpenRGBE131ReceiverDialog(ResourceManager* manager, QWidget *parent = nullptr);
|
||||||
|
~OpenRGBE131ReceiverDialog();
|
||||||
|
|
||||||
|
void DeviceListChanged();
|
||||||
|
void E131ReceiverThreadFunction();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ResourceManager* resource_manager;
|
||||||
|
Ui::OpenRGBE131ReceiverDialog *ui;
|
||||||
|
|
||||||
|
std::thread* E131ReceiverThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPENRGBE131RECEIVERDIALOG_H
|
||||||
36
OpenRGBE131ReceiverDialog.ui
Normal file
36
OpenRGBE131ReceiverDialog.ui
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>OpenRGBE131ReceiverDialog</class>
|
||||||
|
<widget class="QWidget" name="OpenRGBE131ReceiverDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>700</width>
|
||||||
|
<height>275</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QTreeWidget" name="E131TreeView">
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
66
OpenRGBE131ReceiverPlugin.cpp
Normal file
66
OpenRGBE131ReceiverPlugin.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*-----------------------------------------*\
|
||||||
|
| OpenRGBE131ReceiverPlugin.cpp |
|
||||||
|
| |
|
||||||
|
| OpenRGB E1.31 Receiver Plugin |
|
||||||
|
| |
|
||||||
|
| herosilas12 (CoffeeIsLife) 12/11/2020 |
|
||||||
|
| Adam Honse (CalcProgrammer1) 1/5/2021 |
|
||||||
|
\*-----------------------------------------*/
|
||||||
|
|
||||||
|
#include "OpenRGBE131ReceiverPlugin.h"
|
||||||
|
#include "OpenRGBE131ReceiverDialog.h"
|
||||||
|
|
||||||
|
/*-----------------------------------------*\
|
||||||
|
| Initialize |
|
||||||
|
| |
|
||||||
|
| This function must be present in all |
|
||||||
|
| OpenRGB plugins. It defines the plugin |
|
||||||
|
| name, description, location, and other |
|
||||||
|
| plugin information. It creates the tab |
|
||||||
|
| label and is the entry point for plugin |
|
||||||
|
| code |
|
||||||
|
\*-----------------------------------------*/
|
||||||
|
OpenRGBPluginInfo OpenRGBPlugin::Initialize(bool dark_theme, ResourceManager* resource_manager_ptr)
|
||||||
|
{
|
||||||
|
info.PluginName = "E1.31 Receiver";
|
||||||
|
info.PluginDescription = "OpenRGB E1.31 Receiver Plugin";
|
||||||
|
info.PluginLocation = "TopTabBar";
|
||||||
|
info.HasCustom = false;
|
||||||
|
info.SettingName = "";
|
||||||
|
info.PluginLabel = new QLabel();
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Set the label text |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
info.PluginLabel->setText("E1.31 Receiver");
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Save the arguments to Initialize based on what you |
|
||||||
|
| need for this plugin's functionality. In this example|
|
||||||
|
| we will need the Resource Manager to access the device|
|
||||||
|
| list, so save the Resource Manager pointer locally. |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
resource_manager = resource_manager_ptr;
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------*\
|
||||||
|
| CreateGUI |
|
||||||
|
| |
|
||||||
|
| This function must be present in all |
|
||||||
|
| OpenRGB plugins. It creates the QWidget |
|
||||||
|
| that represents the plugin tab's content |
|
||||||
|
\*-----------------------------------------*/
|
||||||
|
QWidget* OpenRGBPlugin::CreateGUI(QWidget* parent)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Create the main widget for this plugin tab |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
QWidget* plugin_widget = new OpenRGBE131ReceiverDialog(resource_manager, parent);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| The CreateGUI function must return the main widget |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
return plugin_widget;
|
||||||
|
}
|
||||||
|
|
@ -30,11 +30,6 @@ public:
|
||||||
|
|
||||||
virtual QWidget *CreateGUI(QWidget* parent) override;
|
virtual QWidget *CreateGUI(QWidget* parent) override;
|
||||||
|
|
||||||
void TimerThreadFunction();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ResourceManager* resource_manager;
|
ResourceManager* resource_manager;
|
||||||
QLabel* plugin_label;
|
|
||||||
|
|
||||||
std::thread* TimerThread;
|
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
# OpenRGB Plugin Template QMake Project #
|
# OpenRGB E1.31 Receiver Plugin QMake Project #
|
||||||
# #
|
# #
|
||||||
# herosilas12 (CoffeeIsLife) 12/11/2020 #
|
# herosilas12 (CoffeeIsLife) 12/11/2020 #
|
||||||
# Adam Honse (CalcProgrammer1) 1/5/2021 #
|
# Adam Honse (CalcProgrammer1) 1/5/2021 #
|
||||||
|
|
@ -26,11 +26,18 @@ CONFIG += c++11
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
# Plugin Project Files #
|
# Plugin Project Files #
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
|
INCLUDEPATH += \
|
||||||
|
dependencies/libe131/src/ \
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
OpenRGBPlugin.cpp \
|
dependencies/libe131/src/e131.c \
|
||||||
|
OpenRGBE131ReceiverDialog.cpp \
|
||||||
|
OpenRGBE131ReceiverPlugin.cpp \
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
OpenRGBPlugin.h \
|
dependencies/libe131/src/e131.h \
|
||||||
|
OpenRGBE131ReceiverDialog.h \
|
||||||
|
OpenRGBE131ReceiverPlugin.h \
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
# OpenRGB Plugin SDK #
|
# OpenRGB Plugin SDK #
|
||||||
|
|
@ -55,6 +62,29 @@ HEADERS +=
|
||||||
OpenRGB/net_port/net_port.h \
|
OpenRGB/net_port/net_port.h \
|
||||||
OpenRGB/RGBController/RGBController.h \
|
OpenRGB/RGBController/RGBController.h \
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------#
|
||||||
|
# Windows-specific Configuration #
|
||||||
|
#-----------------------------------------------------------------------------------------------#
|
||||||
|
win32:contains(QMAKE_TARGET.arch, x86_64) {
|
||||||
|
LIBS += \
|
||||||
|
-lws2_32 \
|
||||||
|
}
|
||||||
|
|
||||||
|
win32:contains(QMAKE_TARGET.arch, x86) {
|
||||||
|
LIBS += \
|
||||||
|
-lws2_32 \
|
||||||
|
}
|
||||||
|
|
||||||
|
win32:DEFINES -= \
|
||||||
|
UNICODE
|
||||||
|
|
||||||
|
win32:DEFINES += \
|
||||||
|
_MBCS \
|
||||||
|
WIN32 \
|
||||||
|
_CRT_SECURE_NO_WARNINGS \
|
||||||
|
_WINSOCK_DEPRECATED_NO_WARNINGS \
|
||||||
|
WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
# Default rules for deployment. #
|
# Default rules for deployment. #
|
||||||
#-----------------------------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------------------------#
|
||||||
|
|
@ -63,3 +93,6 @@ unix {
|
||||||
}
|
}
|
||||||
|
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
OpenRGBE131ReceiverDialog.ui
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
/*-----------------------------------------*\
|
|
||||||
| OpenRGBPlugin.cpp |
|
|
||||||
| |
|
|
||||||
| OpenRGB Plugin template with example |
|
|
||||||
| |
|
|
||||||
| herosilas12 (CoffeeIsLife) 12/11/2020 |
|
|
||||||
| Adam Honse (CalcProgrammer1) 1/5/2021 |
|
|
||||||
\*-----------------------------------------*/
|
|
||||||
|
|
||||||
#include "OpenRGBPlugin.h"
|
|
||||||
|
|
||||||
/*-----------------------------------------*\
|
|
||||||
| Initialize |
|
|
||||||
| |
|
|
||||||
| This function must be present in all |
|
|
||||||
| OpenRGB plugins. It defines the plugin |
|
|
||||||
| name, description, location, and other |
|
|
||||||
| plugin information. It creates the tab |
|
|
||||||
| label and is the entry point for plugin |
|
|
||||||
| code |
|
|
||||||
\*-----------------------------------------*/
|
|
||||||
OpenRGBPluginInfo OpenRGBPlugin::Initialize(bool dark_theme, ResourceManager* resource_manager_ptr)
|
|
||||||
{
|
|
||||||
info.PluginName = "OpenRGB Plugin Template";
|
|
||||||
info.PluginDescription = "An example plugin for OpenRGB";
|
|
||||||
info.PluginLocation = "InformationTab";
|
|
||||||
info.HasCustom = false;
|
|
||||||
info.SettingName = "";
|
|
||||||
info.PluginLabel = new QLabel();
|
|
||||||
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| Set the label text |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
info.PluginLabel->setText("OpenRGB Example Plugin");
|
|
||||||
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| Save the arguments to Initialize based on what you |
|
|
||||||
| need for this plugin's functionality. In this example|
|
|
||||||
| we will need the Resource Manager to access the device|
|
|
||||||
| list, so save the Resource Manager pointer locally. |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
resource_manager = resource_manager_ptr;
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------*\
|
|
||||||
| CreateGUI |
|
|
||||||
| |
|
|
||||||
| This function must be present in all |
|
|
||||||
| OpenRGB plugins. It creates the QWidget |
|
|
||||||
| that represents the plugin tab's content |
|
|
||||||
\*-----------------------------------------*/
|
|
||||||
QWidget* OpenRGBPlugin::CreateGUI(QWidget* parent)
|
|
||||||
{
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| Create the main widget for this plugin tab |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
QWidget* plugin_widget = new QWidget(parent);
|
|
||||||
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| In this example, we will create a label showing the |
|
|
||||||
| number of RGBController devices. This will be shown |
|
|
||||||
| in a QLabel, updated at 1Hz by a background thread. |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
plugin_label = new QLabel(plugin_widget);
|
|
||||||
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| With the label created, start the worker thread |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
TimerThread = new std::thread(&OpenRGBPlugin::TimerThreadFunction, this);
|
|
||||||
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| The CreateGUI function must return the main widget |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
return plugin_widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------*\
|
|
||||||
| TimerThreadFunction |
|
|
||||||
| |
|
|
||||||
| This function is part of the example code|
|
|
||||||
| and is not a required part of an OpenRGB |
|
|
||||||
| plugin. This function is an example of |
|
|
||||||
| how a plugin can run a background thread |
|
|
||||||
| and interact with the Resource Manager |
|
|
||||||
\*-----------------------------------------*/
|
|
||||||
void OpenRGBPlugin::TimerThreadFunction()
|
|
||||||
{
|
|
||||||
/*-----------------------------------------------------*\
|
|
||||||
| Begin infinite loop |
|
|
||||||
\*-----------------------------------------------------*/
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
/*-------------------------------------------------*\
|
|
||||||
| Print the number of devices to a string |
|
|
||||||
\*-------------------------------------------------*/
|
|
||||||
std::string text;
|
|
||||||
|
|
||||||
text.append("Number of devices detected: ");
|
|
||||||
text.append(std::to_string(resource_manager->GetRGBControllers().size()));
|
|
||||||
text.append("\r\n");
|
|
||||||
|
|
||||||
/*-------------------------------------------------*\
|
|
||||||
| Update the label |
|
|
||||||
\*-------------------------------------------------*/
|
|
||||||
plugin_label->setText(QString::fromStdString(text));
|
|
||||||
|
|
||||||
/*-------------------------------------------------*\
|
|
||||||
| Sleep for 1 second |
|
|
||||||
\*-------------------------------------------------*/
|
|
||||||
Sleep(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
313
dependencies/libe131/src/e131.c
vendored
Normal file
313
dependencies/libe131/src/e131.c
vendored
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
/**
|
||||||
|
* E1.31 (sACN) library for C/C++
|
||||||
|
* Hugo Hromic - http://github.com/hhromic
|
||||||
|
*
|
||||||
|
* Some content of this file is based on:
|
||||||
|
* https://github.com/forkineye/E131/blob/master/E131.h
|
||||||
|
* https://github.com/forkineye/E131/blob/master/E131.cpp
|
||||||
|
*
|
||||||
|
* Copyright 2016 Hugo Hromic
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <ws2ipdef.h>
|
||||||
|
#else
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "e131.h"
|
||||||
|
|
||||||
|
/* E1.31 Public Constants */
|
||||||
|
const uint16_t E131_DEFAULT_PORT = 5568;
|
||||||
|
const uint8_t E131_DEFAULT_PRIORITY = 0x64;
|
||||||
|
|
||||||
|
/* E1.31 Private Constants */
|
||||||
|
const uint16_t _E131_PREAMBLE_SIZE = 0x0010;
|
||||||
|
const uint16_t _E131_POSTAMBLE_SIZE = 0x0000;
|
||||||
|
const uint8_t _E131_ACN_PID[] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00};
|
||||||
|
const uint32_t _E131_ROOT_VECTOR = 0x00000004;
|
||||||
|
const uint32_t _E131_FRAME_VECTOR = 0x00000002;
|
||||||
|
const uint8_t _E131_DMP_VECTOR = 0x02;
|
||||||
|
const uint8_t _E131_DMP_TYPE = 0xa1;
|
||||||
|
const uint16_t _E131_DMP_FIRST_ADDR = 0x0000;
|
||||||
|
const uint16_t _E131_DMP_ADDR_INC = 0x0001;
|
||||||
|
|
||||||
|
/* Create a socket file descriptor suitable for E1.31 communication */
|
||||||
|
int e131_socket(void) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA WsaData;
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &WsaData);
|
||||||
|
#endif
|
||||||
|
return socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind a socket file descriptor to a port number for E1.31 communication */
|
||||||
|
int e131_bind(int sockfd, const uint16_t port) {
|
||||||
|
e131_addr_t addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
memset(addr.sin_zero, 0, sizeof addr.sin_zero);
|
||||||
|
return bind(sockfd, (struct sockaddr *)&addr, sizeof addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a unicast E1.31 destination using a host and port number */
|
||||||
|
int e131_unicast_dest(e131_addr_t *dest, const char *host, const uint16_t port) {
|
||||||
|
if (dest == NULL || host == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct hostent *he = gethostbyname(host);
|
||||||
|
if (he == NULL) {
|
||||||
|
errno = EADDRNOTAVAIL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dest->sin_family = AF_INET;
|
||||||
|
dest->sin_addr = *(struct in_addr *)he->h_addr;
|
||||||
|
dest->sin_port = htons(port);
|
||||||
|
memset(dest->sin_zero, 0, sizeof dest->sin_zero);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a multicast E1.31 destination using a universe and port number */
|
||||||
|
int e131_multicast_dest(e131_addr_t *dest, const uint16_t universe, const uint16_t port) {
|
||||||
|
if (dest == NULL || universe < 1 || universe > 63999) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dest->sin_family = AF_INET;
|
||||||
|
dest->sin_addr.s_addr = htonl(0xefff0000 | universe);
|
||||||
|
dest->sin_port = htons(port);
|
||||||
|
memset(dest->sin_zero, 0, sizeof dest->sin_zero);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Describe an E1.31 destination into a string */
|
||||||
|
int e131_dest_str(char *str, const e131_addr_t *dest) {
|
||||||
|
if (str == NULL || dest == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sprintf(str, "%s:%d", inet_ntoa(dest->sin_addr), ntohs(dest->sin_port));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Join a socket file descriptor to an E1.31 multicast group using a universe */
|
||||||
|
int e131_multicast_join(int sockfd, const uint16_t universe) {
|
||||||
|
if (universe < 1 || universe > 63999) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct ip_mreq mreq;
|
||||||
|
mreq.imr_multiaddr.s_addr = htonl(0xefff0000 | universe);
|
||||||
|
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||||
|
#else
|
||||||
|
struct ip_mreqn mreq;
|
||||||
|
mreq.imr_multiaddr.s_addr = htonl(0xefff0000 | universe);
|
||||||
|
mreq.imr_address.s_addr = htonl(INADDR_ANY);
|
||||||
|
mreq.imr_ifindex = 0;
|
||||||
|
#endif
|
||||||
|
return setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof mreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize an E1.31 packet using a universe and a number of slots */
|
||||||
|
int e131_pkt_init(e131_packet_t *packet, const uint16_t universe, const uint16_t num_slots) {
|
||||||
|
if (packet == NULL || universe < 1 || universe > 63999 || num_slots < 1 || num_slots > 512) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute packet layer lengths
|
||||||
|
uint16_t prop_val_cnt = num_slots + 1;
|
||||||
|
uint16_t dmp_length = prop_val_cnt +
|
||||||
|
sizeof packet->dmp - sizeof packet->dmp.prop_val;
|
||||||
|
uint16_t frame_length = sizeof packet->frame + dmp_length;
|
||||||
|
uint16_t root_length = sizeof packet->root.flength +
|
||||||
|
sizeof packet->root.vector + sizeof packet->root.cid + frame_length;
|
||||||
|
|
||||||
|
// clear packet
|
||||||
|
memset(packet, 0, sizeof *packet);
|
||||||
|
|
||||||
|
// set Root Layer values
|
||||||
|
packet->root.preamble_size = htons(_E131_PREAMBLE_SIZE);
|
||||||
|
packet->root.postamble_size = htons(_E131_POSTAMBLE_SIZE);
|
||||||
|
memcpy(packet->root.acn_pid, _E131_ACN_PID, sizeof packet->root.acn_pid);
|
||||||
|
packet->root.flength = htons(0x7000 | root_length);
|
||||||
|
packet->root.vector = htonl(_E131_ROOT_VECTOR);
|
||||||
|
|
||||||
|
// set Framing Layer values
|
||||||
|
packet->frame.flength = htons(0x7000 | frame_length);
|
||||||
|
packet->frame.vector = htonl(_E131_FRAME_VECTOR);
|
||||||
|
packet->frame.priority = E131_DEFAULT_PRIORITY;
|
||||||
|
packet->frame.universe = htons(universe);
|
||||||
|
|
||||||
|
// set Device Management Protocol (DMP) Layer values
|
||||||
|
packet->dmp.flength = htons(0x7000 | dmp_length);
|
||||||
|
packet->dmp.vector = _E131_DMP_VECTOR;
|
||||||
|
packet->dmp.type = _E131_DMP_TYPE;
|
||||||
|
packet->dmp.first_addr = htons(_E131_DMP_FIRST_ADDR);
|
||||||
|
packet->dmp.addr_inc = htons(_E131_DMP_ADDR_INC);
|
||||||
|
packet->dmp.prop_val_cnt = htons(prop_val_cnt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the state of a framing option in an E1.31 packet */
|
||||||
|
bool e131_get_option(const e131_packet_t *packet, const e131_option_t option) {
|
||||||
|
if (packet != NULL && packet->frame.options & (1 << (option % 8)))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the state of a framing option in an E1.31 packet */
|
||||||
|
int e131_set_option(e131_packet_t *packet, const e131_option_t option, const bool state) {
|
||||||
|
if (packet == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
packet->frame.options ^= (-state ^ packet->frame.options) & (1 << (option % 8));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send an E1.31 packet to a socket file descriptor using a destination */
|
||||||
|
ssize_t e131_send(int sockfd, const e131_packet_t *packet, const e131_addr_t *dest) {
|
||||||
|
if (packet == NULL || dest == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const size_t packet_length = sizeof packet->raw -
|
||||||
|
sizeof packet->dmp.prop_val + htons(packet->dmp.prop_val_cnt);
|
||||||
|
return sendto(sockfd, packet->raw, packet_length, 0,
|
||||||
|
(const struct sockaddr *)dest, sizeof *dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive an E1.31 packet from a socket file descriptor */
|
||||||
|
ssize_t e131_recv(int sockfd, e131_packet_t *packet) {
|
||||||
|
if (packet == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return recv(sockfd, packet->raw, sizeof packet->raw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate that an E1.31 packet is well-formed */
|
||||||
|
e131_error_t e131_pkt_validate(const e131_packet_t *packet) {
|
||||||
|
if (packet == NULL)
|
||||||
|
return E131_ERR_NULLPTR;
|
||||||
|
if (ntohs(packet->root.preamble_size) != _E131_PREAMBLE_SIZE)
|
||||||
|
return E131_ERR_PREAMBLE_SIZE;
|
||||||
|
if (ntohs(packet->root.postamble_size) != _E131_POSTAMBLE_SIZE)
|
||||||
|
return E131_ERR_POSTAMBLE_SIZE;
|
||||||
|
if (memcmp(packet->root.acn_pid, _E131_ACN_PID, sizeof packet->root.acn_pid) != 0)
|
||||||
|
return E131_ERR_ACN_PID;
|
||||||
|
if (ntohl(packet->root.vector) != _E131_ROOT_VECTOR)
|
||||||
|
return E131_ERR_VECTOR_ROOT;
|
||||||
|
if (ntohl(packet->frame.vector) != _E131_FRAME_VECTOR)
|
||||||
|
return E131_ERR_VECTOR_FRAME;
|
||||||
|
if (packet->dmp.vector != _E131_DMP_VECTOR)
|
||||||
|
return E131_ERR_VECTOR_DMP;
|
||||||
|
if (packet->dmp.type != _E131_DMP_TYPE)
|
||||||
|
return E131_ERR_TYPE_DMP;
|
||||||
|
if (htons(packet->dmp.first_addr) != _E131_DMP_FIRST_ADDR)
|
||||||
|
return E131_ERR_FIRST_ADDR_DMP;
|
||||||
|
if (htons(packet->dmp.addr_inc) != _E131_DMP_ADDR_INC)
|
||||||
|
return E131_ERR_ADDR_INC_DMP;
|
||||||
|
return E131_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if an E1.31 packet should be discarded (sequence number out of order) */
|
||||||
|
bool e131_pkt_discard(const e131_packet_t *packet, const uint8_t last_seq_number) {
|
||||||
|
if (packet == NULL)
|
||||||
|
return true;
|
||||||
|
int8_t seq_num_diff = packet->frame.seq_number - last_seq_number;
|
||||||
|
if (seq_num_diff > -20 && seq_num_diff <= 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump an E1.31 packet to a stream (i.e. stdout, stderr) */
|
||||||
|
int e131_pkt_dump(FILE *stream, const e131_packet_t *packet) {
|
||||||
|
if (stream == NULL || packet == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fprintf(stream, "[Root Layer]\n");
|
||||||
|
fprintf(stream, " Preamble Size .......... %" PRIu16 "\n", ntohs(packet->root.preamble_size));
|
||||||
|
fprintf(stream, " Post-amble Size ........ %" PRIu16 "\n", ntohs(packet->root.postamble_size));
|
||||||
|
fprintf(stream, " ACN Packet Identifier .. %s\n", packet->root.acn_pid);
|
||||||
|
fprintf(stream, " Flags & Length ......... %" PRIu16 "\n", ntohs(packet->root.flength));
|
||||||
|
fprintf(stream, " Layer Vector ........... %" PRIu32 "\n", ntohl(packet->root.vector));
|
||||||
|
fprintf(stream, " Component Identifier ... ");
|
||||||
|
for (size_t pos=0, total=sizeof packet->root.cid; pos<total; pos++)
|
||||||
|
fprintf(stream, "%02x", packet->root.cid[pos]);
|
||||||
|
fprintf(stream, "\n");
|
||||||
|
fprintf(stream, "[Framing Layer]\n");
|
||||||
|
fprintf(stream, " Flags & Length ......... %" PRIu16 "\n", ntohs(packet->frame.flength));
|
||||||
|
fprintf(stream, " Layer Vector ........... %" PRIu32 "\n", ntohl(packet->frame.vector));
|
||||||
|
fprintf(stream, " Source Name ............ %s\n", packet->frame.source_name);
|
||||||
|
fprintf(stream, " Packet Priority ........ %" PRIu8 "\n", packet->frame.priority);
|
||||||
|
fprintf(stream, " Reserved ............... %" PRIu16 "\n", ntohs(packet->frame.reserved));
|
||||||
|
fprintf(stream, " Sequence Number ........ %" PRIu8 "\n", packet->frame.seq_number);
|
||||||
|
fprintf(stream, " Options Flags .......... %" PRIu8 "\n", packet->frame.options);
|
||||||
|
fprintf(stream, " DMX Universe Number .... %" PRIu16 "\n", ntohs(packet->frame.universe));
|
||||||
|
fprintf(stream, "[Device Management Protocol (DMP) Layer]\n");
|
||||||
|
fprintf(stream, " Flags & Length ......... %" PRIu16 "\n", ntohs(packet->dmp.flength));
|
||||||
|
fprintf(stream, " Layer Vector ........... %" PRIu8 "\n", packet->dmp.vector);
|
||||||
|
fprintf(stream, " Address & Data Type .... %" PRIu8 "\n", packet->dmp.type);
|
||||||
|
fprintf(stream, " First Address .......... %" PRIu16 "\n", ntohs(packet->dmp.first_addr));
|
||||||
|
fprintf(stream, " Address Increment ...... %" PRIu16 "\n", ntohs(packet->dmp.addr_inc));
|
||||||
|
fprintf(stream, " Property Value Count ... %" PRIu16 "\n", ntohs(packet->dmp.prop_val_cnt));
|
||||||
|
fprintf(stream, "[DMP Property Values]\n ");
|
||||||
|
for (size_t pos=0, total=ntohs(packet->dmp.prop_val_cnt); pos<total; pos++)
|
||||||
|
fprintf(stream, " %02x", packet->dmp.prop_val[pos]);
|
||||||
|
fprintf(stream, "\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return a string describing an E1.31 error */
|
||||||
|
const char *e131_strerror(const e131_error_t error) {
|
||||||
|
switch (error) {
|
||||||
|
case E131_ERR_NONE:
|
||||||
|
return "Success";
|
||||||
|
case E131_ERR_PREAMBLE_SIZE:
|
||||||
|
return "Invalid Preamble Size";
|
||||||
|
case E131_ERR_POSTAMBLE_SIZE:
|
||||||
|
return "Invalid Post-amble Size";
|
||||||
|
case E131_ERR_ACN_PID:
|
||||||
|
return "Invalid ACN Packet Identifier";
|
||||||
|
case E131_ERR_VECTOR_ROOT:
|
||||||
|
return "Invalid Root Layer Vector";
|
||||||
|
case E131_ERR_VECTOR_FRAME:
|
||||||
|
return "Invalid Framing Layer Vector";
|
||||||
|
case E131_ERR_VECTOR_DMP:
|
||||||
|
return "Invalid Device Management Protocol (DMP) Layer Vector";
|
||||||
|
case E131_ERR_TYPE_DMP:
|
||||||
|
return "Invalid DMP Address & Data Type";
|
||||||
|
case E131_ERR_FIRST_ADDR_DMP:
|
||||||
|
return "Invalid DMP First Address";
|
||||||
|
case E131_ERR_ADDR_INC_DMP:
|
||||||
|
return "Invalid DMP Address Increment";
|
||||||
|
default:
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
168
dependencies/libe131/src/e131.h
vendored
Normal file
168
dependencies/libe131/src/e131.h
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* E1.31 (sACN) library for C/C++
|
||||||
|
* Hugo Hromic - http://github.com/hhromic
|
||||||
|
*
|
||||||
|
* Some content of this file is based on:
|
||||||
|
* https://github.com/forkineye/E131/blob/master/E131.h
|
||||||
|
* https://github.com/forkineye/E131/blob/master/E131.cpp
|
||||||
|
*
|
||||||
|
* Copyright 2016 Hugo Hromic
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _E131_H
|
||||||
|
#define _E131_H
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#else
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <BaseTsd.h>
|
||||||
|
typedef SSIZE_T ssize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* E1.31 Public Constants */
|
||||||
|
extern const uint16_t E131_DEFAULT_PORT;
|
||||||
|
extern const uint8_t E131_DEFAULT_PRIORITY;
|
||||||
|
|
||||||
|
/* E1.31 Socket Address Type */
|
||||||
|
typedef struct sockaddr_in e131_addr_t;
|
||||||
|
|
||||||
|
/* E1.31 Packet Type */
|
||||||
|
/* All packet contents shall be transmitted in network byte order (big endian) */
|
||||||
|
typedef union {
|
||||||
|
PACK(struct {
|
||||||
|
PACK(struct { /* ACN Root Layer: 38 bytes */
|
||||||
|
uint16_t preamble_size; /* Preamble Size */
|
||||||
|
uint16_t postamble_size; /* Post-amble Size */
|
||||||
|
uint8_t acn_pid[12]; /* ACN Packet Identifier */
|
||||||
|
uint16_t flength; /* Flags (high 4 bits) & Length (low 12 bits) */
|
||||||
|
uint32_t vector; /* Layer Vector */
|
||||||
|
uint8_t cid[16]; /* Component Identifier (UUID) */
|
||||||
|
}) root;
|
||||||
|
|
||||||
|
PACK(struct { /* Framing Layer: 77 bytes */
|
||||||
|
uint16_t flength; /* Flags (high 4 bits) & Length (low 12 bits) */
|
||||||
|
uint32_t vector; /* Layer Vector */
|
||||||
|
uint8_t source_name[64]; /* User Assigned Name of Source (UTF-8) */
|
||||||
|
uint8_t priority; /* Packet Priority (0-200, default 100) */
|
||||||
|
uint16_t reserved; /* Reserved (should be always 0) */
|
||||||
|
uint8_t seq_number; /* Sequence Number (detect duplicates or out of order packets) */
|
||||||
|
uint8_t options; /* Options Flags (bit 7: preview data, bit 6: stream terminated) */
|
||||||
|
uint16_t universe; /* DMX Universe Number */
|
||||||
|
}) frame;
|
||||||
|
|
||||||
|
PACK(struct { /* Device Management Protocol (DMP) Layer: 523 bytes */
|
||||||
|
uint16_t flength; /* Flags (high 4 bits) / Length (low 12 bits) */
|
||||||
|
uint8_t vector; /* Layer Vector */
|
||||||
|
uint8_t type; /* Address Type & Data Type */
|
||||||
|
uint16_t first_addr; /* First Property Address */
|
||||||
|
uint16_t addr_inc; /* Address Increment */
|
||||||
|
uint16_t prop_val_cnt; /* Property Value Count (1 + number of slots) */
|
||||||
|
uint8_t prop_val[513]; /* Property Values (DMX start code + slots data) */
|
||||||
|
}) dmp;
|
||||||
|
});
|
||||||
|
|
||||||
|
uint8_t raw[638]; /* raw buffer view: 638 bytes */
|
||||||
|
} e131_packet_t;
|
||||||
|
|
||||||
|
/* E1.31 Framing Options Type */
|
||||||
|
typedef enum {
|
||||||
|
E131_OPT_TERMINATED = 6,
|
||||||
|
E131_OPT_PREVIEW = 7,
|
||||||
|
} e131_option_t;
|
||||||
|
|
||||||
|
/* E1.31 Validation Errors Type */
|
||||||
|
typedef enum {
|
||||||
|
E131_ERR_NONE,
|
||||||
|
E131_ERR_NULLPTR,
|
||||||
|
E131_ERR_PREAMBLE_SIZE,
|
||||||
|
E131_ERR_POSTAMBLE_SIZE,
|
||||||
|
E131_ERR_ACN_PID,
|
||||||
|
E131_ERR_VECTOR_ROOT,
|
||||||
|
E131_ERR_VECTOR_FRAME,
|
||||||
|
E131_ERR_VECTOR_DMP,
|
||||||
|
E131_ERR_TYPE_DMP,
|
||||||
|
E131_ERR_FIRST_ADDR_DMP,
|
||||||
|
E131_ERR_ADDR_INC_DMP,
|
||||||
|
} e131_error_t;
|
||||||
|
|
||||||
|
/* Create a socket file descriptor suitable for E1.31 communication */
|
||||||
|
extern int e131_socket(void);
|
||||||
|
|
||||||
|
/* Bind a socket file descriptor to a port number for E1.31 communication */
|
||||||
|
extern int e131_bind(int sockfd, const uint16_t port);
|
||||||
|
|
||||||
|
/* Initialize a unicast E1.31 destination using a host and port number */
|
||||||
|
extern int e131_unicast_dest(e131_addr_t *dest, const char *host, const uint16_t port);
|
||||||
|
|
||||||
|
/* Initialize a multicast E1.31 destination using a universe and port number */
|
||||||
|
extern int e131_multicast_dest(e131_addr_t *dest, const uint16_t universe, const uint16_t port);
|
||||||
|
|
||||||
|
/* Describe an E1.31 destination into a string (must be at least 22 bytes) */
|
||||||
|
extern int e131_dest_str(char *str, const e131_addr_t *dest);
|
||||||
|
|
||||||
|
/* Join a socket file descriptor to an E1.31 multicast group using a universe */
|
||||||
|
extern int e131_multicast_join(int sockfd, const uint16_t universe);
|
||||||
|
|
||||||
|
/* Initialize an E1.31 packet using a universe and a number of slots */
|
||||||
|
extern int e131_pkt_init(e131_packet_t *packet, const uint16_t universe, const uint16_t num_slots);
|
||||||
|
|
||||||
|
/* Get the state of a framing option in an E1.31 packet */
|
||||||
|
extern bool e131_get_option(const e131_packet_t *packet, const e131_option_t option);
|
||||||
|
|
||||||
|
/* Set the state of a framing option in an E1.31 packet */
|
||||||
|
extern int e131_set_option(e131_packet_t *packet, const e131_option_t option, const bool state);
|
||||||
|
|
||||||
|
/* Send an E1.31 packet to a socket file descriptor using a destination */
|
||||||
|
extern ssize_t e131_send(int sockfd, const e131_packet_t *packet, const e131_addr_t *dest);
|
||||||
|
|
||||||
|
/* Receive an E1.31 packet from a socket file descriptor */
|
||||||
|
extern ssize_t e131_recv(int sockfd, e131_packet_t *packet);
|
||||||
|
|
||||||
|
/* Validate that an E1.31 packet is well-formed */
|
||||||
|
extern e131_error_t e131_pkt_validate(const e131_packet_t *packet);
|
||||||
|
|
||||||
|
/* Check if an E1.31 packet should be discarded (sequence number out of order) */
|
||||||
|
extern bool e131_pkt_discard(const e131_packet_t *packet, const uint8_t last_seq_number);
|
||||||
|
|
||||||
|
/* Dump an E1.31 packet to a stream (i.e. stdout, stderr) */
|
||||||
|
extern int e131_pkt_dump(FILE *stream, const e131_packet_t *packet);
|
||||||
|
|
||||||
|
/* Return a string describing an E1.31 error */
|
||||||
|
extern const char *e131_strerror(const e131_error_t error);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
Loading…
Reference in a new issue