open-hyperion/OpenRGBE131ReceiverDialog.cpp

518 lines
19 KiB
C++
Raw Normal View History

#include "OpenRGBE131ReceiverDialog.h"
#include "ui_OpenRGBE131ReceiverDialog.h"
#include <e131.h>
#include <QLineEdit>
#include <QSignalMapper>
#define MAX_LEDS_PER_UNIVERSE 170
void DeviceListChanged_Callback(void * this_ptr)
{
OpenRGBE131ReceiverDialog * this_obj = (OpenRGBE131ReceiverDialog *)this_ptr;
QMetaObject::invokeMethod(this_obj, "DeviceListChanged", Qt::QueuedConnection);
}
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;
typedef unsigned int LineEditParameter;
enum
{
LINEEDIT_PARAMETER_START_CHANNEL,
LINEEDIT_PARAMETER_START_LED,
LINEEDIT_PARAMETER_NUM_LEDS
};
class LineEditArgument : public QObject
{
public:
QWidget * widget;
unsigned int universe_idx;
unsigned int member_idx;
LineEditParameter parameter;
};
typedef unsigned int CheckBoxParameter;
enum
{
CHECKBOX_PARAMETER_UPDATE
};
class CheckBoxArgument : public QObject
{
public:
QWidget * widget;
unsigned int universe_idx;
unsigned int member_idx;
CheckBoxParameter parameter;
};
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);
online = false;
received_count = 0;
UpdateOnlineStatus();
}
OpenRGBE131ReceiverDialog::~OpenRGBE131ReceiverDialog()
{
delete ui;
}
void OpenRGBE131ReceiverDialog::DeviceListChanged()
{
if(resource_manager->GetDetectionPercent() == 100)
{
ui->E131TreeView->clear();
universe_list.clear();
for(unsigned int controller_idx = 0; controller_idx < resource_manager->GetRGBControllers().size(); controller_idx++)
{
RGBController* controller = resource_manager->GetRGBControllers()[controller_idx];
// Determine if the controller has a Direct mode
bool has_direct = false;
for(unsigned int mode_idx = 0; mode_idx < controller->modes.size(); mode_idx++)
{
if(controller->modes[mode_idx].name == "Direct")
{
has_direct = true;
break;
}
}
// Only map controllers that have a Direct mode
if(has_direct)
{
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 = false;
// 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;
// Only set the update flag for the last universe of the controller
2021-01-07 05:59:14 +00:00
if(universe_idx == (num_universes - 1))
{
new_member.update = true;
}
new_entry.members.push_back(new_member);
universe_list.push_back(new_entry);
}
}
}
UpdateTreeView();
}
else
{
ui->E131TreeView->clear();
universe_list.clear();
}
}
void OpenRGBE131ReceiverDialog::UpdateTreeView()
{
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");
/*-----------------------------------------------------*\
| Create a signal mapper for LineEdit fields |
\*-----------------------------------------------------*/
QSignalMapper* LineEditMapper = new QSignalMapper(this);
connect(LineEditMapper, SIGNAL(mapped(QObject *)), this, SLOT(on_LineEdit_updated(QObject *)));
/*-----------------------------------------------------*\
| Create a signal mapper for CheckBox fields |
\*-----------------------------------------------------*/
QSignalMapper* CheckBoxMapper = new QSignalMapper(this);
connect(CheckBoxMapper, SIGNAL(mapped(QObject *)), this, SLOT(on_CheckBox_updated(QObject *)));
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)));
QLineEdit* start_channel_edit = new QLineEdit(ui->E131TreeView);
start_channel_edit->setText(QString::number(universe_list[universe_idx].members[member_idx].start_channel));
ui->E131TreeView->setItemWidget(new_member_entry, 1, start_channel_edit);
LineEditArgument* start_channel_argument = new LineEditArgument();
start_channel_argument->widget = start_channel_edit;
start_channel_argument->universe_idx = universe_idx;
start_channel_argument->member_idx = member_idx;
start_channel_argument->parameter = LINEEDIT_PARAMETER_START_CHANNEL;
connect(start_channel_edit, SIGNAL(editingFinished()), LineEditMapper, SLOT(map()));
LineEditMapper->setMapping(start_channel_edit, start_channel_argument);
QLineEdit* start_led_edit = new QLineEdit(ui->E131TreeView);
start_led_edit->setText(QString::number(universe_list[universe_idx].members[member_idx].start_led));
ui->E131TreeView->setItemWidget(new_member_entry, 2, start_led_edit);
LineEditArgument* start_led_argument = new LineEditArgument();
start_led_argument->widget = start_led_edit;
start_led_argument->universe_idx = universe_idx;
start_led_argument->member_idx = member_idx;
start_led_argument->parameter = LINEEDIT_PARAMETER_START_LED;
connect(start_led_edit, SIGNAL(editingFinished()), LineEditMapper, SLOT(map()));
LineEditMapper->setMapping(start_led_edit, start_led_argument);
QLineEdit* num_leds_edit = new QLineEdit(ui->E131TreeView);
num_leds_edit->setText(QString::number(universe_list[universe_idx].members[member_idx].num_leds));
ui->E131TreeView->setItemWidget(new_member_entry, 3, num_leds_edit);
LineEditArgument* num_leds_argument = new LineEditArgument();
num_leds_argument->widget = num_leds_edit;
num_leds_argument->universe_idx = universe_idx;
num_leds_argument->member_idx = member_idx;
num_leds_argument->parameter = LINEEDIT_PARAMETER_NUM_LEDS;
connect(num_leds_edit, SIGNAL(editingFinished()), LineEditMapper, SLOT(map()));
LineEditMapper->setMapping(num_leds_edit, num_leds_argument);
QCheckBox* update_checkbox = new QCheckBox(ui->E131TreeView);
update_checkbox->setChecked(universe_list[universe_idx].members[member_idx].update);
ui->E131TreeView->setItemWidget(new_member_entry, 4, update_checkbox);
CheckBoxArgument* update_argument = new CheckBoxArgument();
update_argument->widget = update_checkbox;
update_argument->universe_idx = universe_idx;
update_argument->member_idx = member_idx;
update_argument->parameter = CHECKBOX_PARAMETER_UPDATE;
connect(update_checkbox, SIGNAL(clicked()), CheckBoxMapper, SLOT(map()));
CheckBoxMapper->setMapping(update_checkbox, update_argument);
}
}
ui->E131TreeView->setStyleSheet("QLineEdit { border: none }");
ui->E131TreeView->expandAll();
}
void OpenRGBE131ReceiverDialog::E131ReceiverThreadFunction()
{
int sockfd;
e131_packet_t packet;
e131_error_t error;
uint8_t last_seq = 0x00;
2021-01-06 19:48:49 +00:00
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
/*-----------------------------------------------------*\
| Initialize update time |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
/*-----------------------------------------------------*\
| Clear online status |
\*-----------------------------------------------------*/
online = false;
/*-----------------------------------------------------*\
| 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 if multicast enabled |
\*-----------------------------------------------------*/
bool multicast = ui->EnableMulticastBox->checkState() == Qt::Checked;
for(unsigned int universe_idx = 0; universe_idx < universe_list.size(); universe_idx++)
{
if(multicast)
{
if(e131_multicast_join(sockfd, universe_list[universe_idx].universe) < 0)
{
printf("Join error\r\n");
return;
}
}
}
/*-----------------------------------------------------*\
| Set online status |
\*-----------------------------------------------------*/
online = true;
UpdateOnlineStatus();
/*-----------------------------------------------------*\
| Loop to receive E1.31 packets |
\*-----------------------------------------------------*/
while(online)
{
if(e131_recv(sockfd, &packet) < 0)
{
printf("Receive error\r\n");
online = false;
}
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;
// }
received_count++;
2021-01-06 19:48:49 +00:00
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(500))
{
ui->PacketsReceivedValue->setText(QString::number(received_count));
2021-01-07 06:19:13 +00:00
ui->ReceiverSourceValue->setText(QString::fromLocal8Bit((const char *)packet.frame.source_name, 64));
2021-01-06 19:48:49 +00:00
last_update_time = std::chrono::steady_clock::now();
}
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;
// Channel range is 1-512, so break if channel is zero
if(channel == 0)
{
break;
}
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;
// Verify last channel (blue) is within range 1-512
if(blu_idx > 512)
{
break;
}
// 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]);
// Verify LED index
if(led_idx >= controller->colors.size())
{
break;
}
// Set LED color in controller
controller->colors[led_idx] = led_color;
2021-01-08 18:26:34 +00:00
// Increment the channel by 3 to account for red, green, and blue channels
channel = channel + 3;
}
// If configured to update the device, update
if(update)
{
controller->UpdateLEDs();
}
}
}
}
}
closesocket(sockfd);
UpdateOnlineStatus();
}
void OpenRGBE131ReceiverDialog::UpdateOnlineStatus()
{
if(online)
{
ui->ReceiverStatusValue->setText("Online");
ui->ButtonStartReceiver->setEnabled(false);
ui->ButtonStopReceiver->setEnabled(true);
}
else
{
ui->ReceiverStatusValue->setText("Offline");
received_count = 0;
ui->PacketsReceivedValue->setText(QString::number(received_count));
ui->ButtonStartReceiver->setEnabled(true);
ui->ButtonStopReceiver->setEnabled(false);
}
}
void OpenRGBE131ReceiverDialog::on_ButtonStartReceiver_clicked()
{
if(!online)
{
// Start the receiver thread
E131ReceiverThread = new std::thread(&OpenRGBE131ReceiverDialog::E131ReceiverThreadFunction, this);
}
}
void OpenRGBE131ReceiverDialog::on_ButtonStopReceiver_clicked()
{
online = false;
UpdateOnlineStatus();
}
void OpenRGBE131ReceiverDialog::on_LineEdit_updated(QObject* lineedit_argument)
{
/*-----------------------------------------------------*\
| Update the parameter |
\*-----------------------------------------------------*/
unsigned int universe_idx = ((LineEditArgument*)lineedit_argument)->universe_idx;
unsigned int member_idx = ((LineEditArgument*)lineedit_argument)->member_idx;
LineEditParameter parameter = ((LineEditArgument*)lineedit_argument)->parameter;
QWidget* widget = ((LineEditArgument*)lineedit_argument)->widget;
unsigned int value = ((QLineEdit*)widget)->text().toInt();
switch(parameter)
{
case LINEEDIT_PARAMETER_START_CHANNEL:
universe_list[universe_idx].members[member_idx].start_channel = value;
break;
case LINEEDIT_PARAMETER_START_LED:
universe_list[universe_idx].members[member_idx].start_led = value;
break;
case LINEEDIT_PARAMETER_NUM_LEDS:
universe_list[universe_idx].members[member_idx].num_leds = value;
break;
}
}
void OpenRGBE131ReceiverDialog::on_CheckBox_updated(QObject* checkbox_argument)
{
/*-----------------------------------------------------*\
| Update the parameter |
\*-----------------------------------------------------*/
unsigned int universe_idx = ((CheckBoxArgument*)checkbox_argument)->universe_idx;
unsigned int member_idx = ((CheckBoxArgument*)checkbox_argument)->member_idx;
CheckBoxParameter parameter = ((CheckBoxArgument*)checkbox_argument)->parameter;
QWidget* widget = ((CheckBoxArgument*)checkbox_argument)->widget;
bool value = ((QCheckBox*)widget)->isChecked();
switch(parameter)
{
case CHECKBOX_PARAMETER_UPDATE:
universe_list[universe_idx].members[member_idx].update = value;
break;
}
}