Implement ganged switches
This commit is contained in:
parent
7a6e674756
commit
81e7e1a5b3
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
#include "channelwidget.h"
|
#include "channelwidget.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -5,93 +6,166 @@
|
||||||
ChannelWidget::ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber,
|
ChannelWidget::ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber,
|
||||||
std::shared_ptr<struct eismultiplexer> multiplexer,
|
std::shared_ptr<struct eismultiplexer> multiplexer,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
:
|
:
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
deviceSerial(deviceSerial),
|
deviceSerial(deviceSerial),
|
||||||
channelNumber(channelNumber),
|
channelNumber(channelNumber),
|
||||||
multiplexer(multiplexer),
|
multiplexer(multiplexer),
|
||||||
checkbox("Enable"),
|
checkbox("Enable"),
|
||||||
devicelabel(QString::asprintf("Device %04u", deviceSerial)),
|
devicelabel(QString::asprintf("Device %04u", deviceSerial)),
|
||||||
channellabel(QString::asprintf("Channel %u", channelNumber)),
|
channellabel(QString::asprintf("Channel %u", channelNumber)),
|
||||||
ganglabel("Ganged:")
|
ganglabel("Ganged:")
|
||||||
{
|
{
|
||||||
hlayout.addLayout(&labellayout);
|
hlayout.addLayout(&labellayout);
|
||||||
vlayout.addLayout(&hlayout);
|
vlayout.addLayout(&hlayout);
|
||||||
|
|
||||||
labellayout.addWidget(&devicelabel);
|
labellayout.addWidget(&devicelabel);
|
||||||
labellayout.addWidget(&channellabel);
|
labellayout.addWidget(&channellabel);
|
||||||
|
|
||||||
line.setGeometry(QRect(320, 150, 118, 3));
|
line.setGeometry(QRect(320, 150, 118, 3));
|
||||||
line.setFrameShape(QFrame::HLine);
|
line.setFrameShape(QFrame::HLine);
|
||||||
line.setFrameShadow(QFrame::Sunken);
|
line.setFrameShadow(QFrame::Sunken);
|
||||||
vlayout.addWidget(&line);
|
vlayout.addWidget(&line);
|
||||||
|
|
||||||
gangcombo.addItem("Unganged");
|
// Add Unganged option first
|
||||||
|
gangcombo.addItem("Unganged");
|
||||||
|
|
||||||
hlayout.addStretch();
|
hlayout.addStretch();
|
||||||
hlayout.addWidget(&ganglabel);
|
hlayout.addWidget(&ganglabel);
|
||||||
hlayout.addWidget(&gangcombo);
|
hlayout.addWidget(&gangcombo);
|
||||||
hlayout.addWidget(&checkbox);
|
hlayout.addWidget(&checkbox);
|
||||||
connect(&checkbox, &QCheckBox::toggled, this, &ChannelWidget::onChannelToggled);
|
|
||||||
|
|
||||||
setFixedHeight(96);
|
// Connect signals
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
connect(&checkbox, &QCheckBox::toggled, this, &ChannelWidget::onChannelToggled);
|
||||||
|
connect(&gangcombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ChannelWidget::onGangComboChanged);
|
||||||
|
|
||||||
setLayout(&vlayout);
|
setFixedHeight(96);
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
setLayout(&vlayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelWidget::~ChannelWidget()
|
ChannelWidget::~ChannelWidget()
|
||||||
{
|
{
|
||||||
// Nothing to clean up
|
// Nothing to clean up
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ChannelWidget::getDeviceSerial() const
|
uint16_t ChannelWidget::getDeviceSerial() const
|
||||||
{
|
{
|
||||||
return deviceSerial;
|
return deviceSerial;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ChannelWidget::getChannelNumber() const
|
uint16_t ChannelWidget::getChannelNumber() const
|
||||||
{
|
{
|
||||||
return channelNumber;
|
return channelNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChannelWidget::isChecked() const
|
bool ChannelWidget::isChecked() const
|
||||||
{
|
{
|
||||||
return checkbox.isChecked();
|
return checkbox.isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelWidget::onChannelToggled(bool checked)
|
void ChannelWidget::onChannelToggled(bool checked)
|
||||||
{
|
{
|
||||||
if (checked)
|
if (checked)
|
||||||
{
|
{
|
||||||
// Emit signal before actually turning on the channel
|
// Emit signal before actually turning on the channel
|
||||||
emit channelAboutToBeTurnedOn(deviceSerial, channelNumber);
|
emit channelAboutToBeTurnedOn(deviceSerial, channelNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_t channelFlag = static_cast<channel_t>(1 << channelNumber);
|
channel_t channelFlag = static_cast<channel_t>(1 << channelNumber);
|
||||||
if (checked)
|
if (checked)
|
||||||
{
|
{
|
||||||
if (eismultiplexer_connect_channel(multiplexer.get(), channelFlag) < 0)
|
if (eismultiplexer_connect_channel(multiplexer.get(), channelFlag) < 0)
|
||||||
{
|
{
|
||||||
QMessageBox::warning(this, tr("Connection Failed"),
|
QMessageBox::warning(this, tr("Connection Failed"),
|
||||||
tr("Failed to connect channel %1 on device %2").arg(channelNumber).arg(deviceSerial));
|
tr("Failed to connect channel %1 on device %2").arg(channelNumber).arg(deviceSerial));
|
||||||
qWarning() << "Failed to connect channel" << channelNumber << "on device" << deviceSerial;
|
qWarning() << "Failed to connect channel" << channelNumber << "on device" << deviceSerial;
|
||||||
checkbox.blockSignals(true);
|
checkbox.blockSignals(true);
|
||||||
checkbox.setChecked(false);
|
checkbox.setChecked(false);
|
||||||
setEnabled(false); // Gray out the widget
|
setEnabled(false); // Gray out the widget
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (eismultiplexer_disconnect_channel(multiplexer.get(), channelFlag) < 0)
|
if (eismultiplexer_disconnect_channel(multiplexer.get(), channelFlag) < 0)
|
||||||
{
|
{
|
||||||
QMessageBox::warning(this, tr("Disconnection Failed"),
|
QMessageBox::warning(this, tr("Disconnection Failed"),
|
||||||
tr("Failed to disconnect channel %1 on device %2").arg(channelNumber).arg(deviceSerial));
|
tr("Failed to disconnect channel %1 on device %2").arg(channelNumber).arg(deviceSerial));
|
||||||
qWarning() << "Failed to disconnect channel" << channelNumber << "on device" << deviceSerial;
|
qWarning() << "Failed to disconnect channel" << channelNumber << "on device" << deviceSerial;
|
||||||
checkbox.blockSignals(true);
|
checkbox.blockSignals(true);
|
||||||
checkbox.setChecked(true);
|
checkbox.setChecked(true);
|
||||||
setEnabled(false); // Gray out the widget
|
setEnabled(false); // Gray out the widget
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit state change signal for other channels that might be ganged to this one
|
||||||
|
emit channelStateChanged(deviceSerial, channelNumber, checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::onGangComboChanged(int index)
|
||||||
|
{
|
||||||
|
if (index == 0) {
|
||||||
|
// Unganged selected - reset ganged channel tracking
|
||||||
|
gangedDeviceSerial = 0;
|
||||||
|
gangedChannelNumber = 0;
|
||||||
|
checkbox.setEnabled(true);
|
||||||
|
checkbox.setChecked(false);
|
||||||
|
checkbox.setChecked(false); // Reset to false to avoid leaving it checked when unganged
|
||||||
|
} else {
|
||||||
|
// A ganged channel was selected
|
||||||
|
QString currentText = gangcombo.currentText();
|
||||||
|
QStringList parts = currentText.split(", ");
|
||||||
|
if (parts.size() == 2) {
|
||||||
|
bool ok1, ok2;
|
||||||
|
uint16_t gangedSerial = parts[0].toUInt(&ok1);
|
||||||
|
uint16_t gangedChannel = parts[1].toUInt(&ok2);
|
||||||
|
|
||||||
|
if (ok1 && ok2) {
|
||||||
|
setGangedChannel(gangedSerial, gangedChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked)
|
||||||
|
{
|
||||||
|
// If this channel is ganged to the channel that changed state
|
||||||
|
if (gangedDeviceSerial == deviceSerial && gangedChannelNumber == channelNumber) {
|
||||||
|
// Update our checkbox state to follow the ganged channel
|
||||||
|
checkbox.blockSignals(true);
|
||||||
|
checkbox.setChecked(checked);
|
||||||
|
checkbox.blockSignals(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::updateGangCombo()
|
||||||
|
{
|
||||||
|
// Clear existing items except "Unganged"
|
||||||
|
while (gangcombo.count() > 1) {
|
||||||
|
gangcombo.removeItem(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::updateCheckboxState()
|
||||||
|
{
|
||||||
|
// If we're ganged, update our state to match the ganged channel
|
||||||
|
if (gangedDeviceSerial != 0 && gangedChannelNumber != 0) {
|
||||||
|
checkbox.setEnabled(false);
|
||||||
|
// We need to check the state of the ganged channel and sync
|
||||||
|
// This would typically be done by querying the actual channel state
|
||||||
|
// but for now we'll assume the ganged channel's state is known
|
||||||
|
} else {
|
||||||
|
checkbox.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::setGangedChannel(uint16_t gangedDeviceSerial, uint16_t gangedChannelNumber)
|
||||||
|
{
|
||||||
|
this->gangedDeviceSerial = gangedDeviceSerial;
|
||||||
|
this->gangedChannelNumber = gangedChannelNumber;
|
||||||
|
|
||||||
|
// Update checkbox state to follow the ganged channel
|
||||||
|
updateCheckboxState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef CHANNELWIDGET_H
|
#ifndef CHANNELWIDGET_H
|
||||||
#define CHANNELWIDGET_H
|
#define CHANNELWIDGET_H
|
||||||
|
|
||||||
|
@ -11,39 +13,54 @@
|
||||||
|
|
||||||
class ChannelWidget : public QWidget
|
class ChannelWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber,
|
explicit ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber,
|
||||||
std::shared_ptr<struct eismultiplexer> multiplexer,
|
std::shared_ptr<struct eismultiplexer> multiplexer,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
~ChannelWidget() override;
|
~ChannelWidget() override;
|
||||||
|
|
||||||
uint16_t getDeviceSerial() const;
|
uint16_t getDeviceSerial() const;
|
||||||
uint16_t getChannelNumber() const;
|
uint16_t getChannelNumber() const;
|
||||||
bool isChecked() const;
|
bool isChecked() const;
|
||||||
|
|
||||||
signals:
|
// Accessors for MainWindow to populate and access the gang combo
|
||||||
void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber);
|
QComboBox* getGangCombo() { return &gangcombo; }
|
||||||
|
void setGangedChannel(uint16_t deviceSerial, uint16_t channelNumber);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onChannelToggled(bool checked);
|
void onChannelToggled(bool checked);
|
||||||
|
void onGangComboChanged(int index);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber);
|
||||||
|
void channelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t deviceSerial;
|
void updateGangCombo();
|
||||||
uint16_t channelNumber;
|
void updateCheckboxState();
|
||||||
std::shared_ptr<struct eismultiplexer> multiplexer;
|
|
||||||
QCheckBox checkbox;
|
uint16_t deviceSerial;
|
||||||
QLabel devicelabel;
|
uint16_t channelNumber;
|
||||||
QLabel channellabel;
|
std::shared_ptr<struct eismultiplexer> multiplexer;
|
||||||
QLabel ganglabel;
|
QCheckBox checkbox;
|
||||||
QComboBox gangcombo;
|
QLabel devicelabel;
|
||||||
QFrame line;
|
QLabel channellabel;
|
||||||
QVBoxLayout vlayout;
|
QLabel ganglabel;
|
||||||
QHBoxLayout hlayout;
|
QComboBox gangcombo;
|
||||||
QVBoxLayout labellayout;
|
QFrame line;
|
||||||
|
QVBoxLayout vlayout;
|
||||||
|
QHBoxLayout hlayout;
|
||||||
|
QVBoxLayout labellayout;
|
||||||
|
|
||||||
|
// Track the channel this one is ganged to (if any)
|
||||||
|
uint16_t gangedDeviceSerial = 0;
|
||||||
|
uint16_t gangedChannelNumber = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHANNELWIDGET_H
|
#endif // CHANNELWIDGET_H
|
||||||
|
|
||||||
|
|
||||||
|
|
125
mainwindow.cpp
125
mainwindow.cpp
|
@ -1,69 +1,96 @@
|
||||||
|
|
||||||
|
|
||||||
#include <eismultiplexer.h>
|
#include <eismultiplexer.h>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow(parent)
|
: QMainWindow(parent)
|
||||||
, ui(new Ui::MainWindow)
|
, ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
enumerateDevices();
|
enumerateDevices();
|
||||||
|
|
||||||
connect(ui->actionQuit, &QAction::triggered, this, [this]()
|
connect(ui->actionQuit, &QAction::triggered, this, [this]()
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::enumerateDevices()
|
void MainWindow::enumerateDevices()
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
uint16_t* serials = eismultiplexer_list_available_devices(&count);
|
uint16_t* serials = eismultiplexer_list_available_devices(&count);
|
||||||
|
|
||||||
if (!serials || count == 0)
|
if (!serials || count == 0)
|
||||||
{
|
{
|
||||||
QMessageBox::warning(nullptr, tr("No Devices Found"),
|
QMessageBox::warning(nullptr, tr("No Devices Found"),
|
||||||
tr("No EIS multiplexer devices were found. Please connect a device and try again."));
|
tr("No EIS multiplexer devices were found. Please connect a device and try again."));
|
||||||
qWarning() << "No EIS multiplexer devices found";
|
qWarning() << "No EIS multiplexer devices found";
|
||||||
exit(0);
|
exit(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < count; i++)
|
// First pass: create all widgets without connecting signals
|
||||||
{
|
for (size_t i = 0; i < count; i++)
|
||||||
uint16_t serial = serials[i];
|
{
|
||||||
std::shared_ptr<struct eismultiplexer> multiplexer(new struct eismultiplexer);
|
uint16_t serial = serials[i];
|
||||||
int ret = eismultiplexer_connect(multiplexer.get(), serial);
|
std::shared_ptr<struct eismultiplexer> multiplexer(new struct eismultiplexer);
|
||||||
if (ret == 0)
|
int ret = eismultiplexer_connect(multiplexer.get(), serial);
|
||||||
{
|
if (ret == 0)
|
||||||
uint16_t channelCount = 0;
|
{
|
||||||
qDebug()<<"Adding channels from device "<<serial;
|
uint16_t channelCount = 0;
|
||||||
if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0)
|
qDebug()<<"Adding channels from device "<<serial;
|
||||||
{
|
if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0)
|
||||||
for (uint16_t channel = 0; channel < channelCount; channel++)
|
{
|
||||||
{
|
for (uint16_t channel = 0; channel < channelCount; channel++)
|
||||||
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
|
{
|
||||||
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
|
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
|
||||||
channels.push_back(widget);
|
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
|
||||||
ui->channelLayout->addWidget(widget.get());
|
channels.push_back(widget);
|
||||||
}
|
ui->channelLayout->addWidget(widget.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
QMessageBox::warning(this, tr("Connection Failed"),
|
{
|
||||||
tr("Failed to connect to device with serial %1").arg(serial));
|
QMessageBox::warning(this, tr("Connection Failed"),
|
||||||
qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret;
|
tr("Failed to connect to device with serial %1").arg(serial));
|
||||||
}
|
qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret;
|
||||||
ui->channelLayout->addStretch();
|
}
|
||||||
}
|
}
|
||||||
ui->statusbar->showMessage("Ready");
|
ui->channelLayout->addStretch();
|
||||||
|
|
||||||
free(serials);
|
// Second pass: populate gang combos and connect signals
|
||||||
|
for (const auto& widget : channels) {
|
||||||
|
// Populate gang combo with all other channels
|
||||||
|
for (const auto& otherWidget : channels) {
|
||||||
|
if (widget->getDeviceSerial() != otherWidget->getDeviceSerial() ||
|
||||||
|
widget->getChannelNumber() != otherWidget->getChannelNumber()) {
|
||||||
|
QString channelText = QString::asprintf("%04u, %u",
|
||||||
|
otherWidget->getDeviceSerial(),
|
||||||
|
otherWidget->getChannelNumber());
|
||||||
|
widget->getGangCombo()->addItem(channelText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect state change signals
|
||||||
|
for (const auto& otherWidget : channels) {
|
||||||
|
if (widget.get() != otherWidget.get()) {
|
||||||
|
connect(otherWidget.get(), &ChannelWidget::channelStateChanged,
|
||||||
|
widget.get(), &ChannelWidget::onOtherChannelStateChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->statusbar->showMessage("Ready");
|
||||||
|
|
||||||
|
free(serials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue