diff --git a/channelwidget.cpp b/channelwidget.cpp index ae259fc..f3cc2f7 100644 --- a/channelwidget.cpp +++ b/channelwidget.cpp @@ -1,3 +1,4 @@ + #include "channelwidget.h" #include #include @@ -5,93 +6,166 @@ ChannelWidget::ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber, std::shared_ptr multiplexer, QWidget *parent) - : - QWidget(parent), - deviceSerial(deviceSerial), - channelNumber(channelNumber), - multiplexer(multiplexer), - checkbox("Enable"), - devicelabel(QString::asprintf("Device %04u", deviceSerial)), - channellabel(QString::asprintf("Channel %u", channelNumber)), - ganglabel("Ganged:") + : + QWidget(parent), + deviceSerial(deviceSerial), + channelNumber(channelNumber), + multiplexer(multiplexer), + checkbox("Enable"), + devicelabel(QString::asprintf("Device %04u", deviceSerial)), + channellabel(QString::asprintf("Channel %u", channelNumber)), + ganglabel("Ganged:") { - hlayout.addLayout(&labellayout); - vlayout.addLayout(&hlayout); + hlayout.addLayout(&labellayout); + vlayout.addLayout(&hlayout); - labellayout.addWidget(&devicelabel); - labellayout.addWidget(&channellabel); + labellayout.addWidget(&devicelabel); + labellayout.addWidget(&channellabel); - line.setGeometry(QRect(320, 150, 118, 3)); - line.setFrameShape(QFrame::HLine); - line.setFrameShadow(QFrame::Sunken); - vlayout.addWidget(&line); + line.setGeometry(QRect(320, 150, 118, 3)); + line.setFrameShape(QFrame::HLine); + line.setFrameShadow(QFrame::Sunken); + vlayout.addWidget(&line); - gangcombo.addItem("Unganged"); + // Add Unganged option first + gangcombo.addItem("Unganged"); - hlayout.addStretch(); - hlayout.addWidget(&ganglabel); - hlayout.addWidget(&gangcombo); - hlayout.addWidget(&checkbox); - connect(&checkbox, &QCheckBox::toggled, this, &ChannelWidget::onChannelToggled); + hlayout.addStretch(); + hlayout.addWidget(&ganglabel); + hlayout.addWidget(&gangcombo); + hlayout.addWidget(&checkbox); - setFixedHeight(96); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + // Connect signals + connect(&checkbox, &QCheckBox::toggled, this, &ChannelWidget::onChannelToggled); + connect(&gangcombo, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelWidget::onGangComboChanged); - setLayout(&vlayout); + setFixedHeight(96); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + setLayout(&vlayout); } ChannelWidget::~ChannelWidget() { - // Nothing to clean up + // Nothing to clean up } uint16_t ChannelWidget::getDeviceSerial() const { - return deviceSerial; + return deviceSerial; } uint16_t ChannelWidget::getChannelNumber() const { - return channelNumber; + return channelNumber; } bool ChannelWidget::isChecked() const { - return checkbox.isChecked(); + return checkbox.isChecked(); } void ChannelWidget::onChannelToggled(bool checked) { - if (checked) - { - // Emit signal before actually turning on the channel - emit channelAboutToBeTurnedOn(deviceSerial, channelNumber); - } + if (checked) + { + // Emit signal before actually turning on the channel + emit channelAboutToBeTurnedOn(deviceSerial, channelNumber); + } - channel_t channelFlag = static_cast(1 << channelNumber); - if (checked) - { - if (eismultiplexer_connect_channel(multiplexer.get(), channelFlag) < 0) - { - QMessageBox::warning(this, tr("Connection Failed"), - tr("Failed to connect channel %1 on device %2").arg(channelNumber).arg(deviceSerial)); - qWarning() << "Failed to connect channel" << channelNumber << "on device" << deviceSerial; - checkbox.blockSignals(true); - checkbox.setChecked(false); - setEnabled(false); // Gray out the widget - } - } - else - { - if (eismultiplexer_disconnect_channel(multiplexer.get(), channelFlag) < 0) - { - QMessageBox::warning(this, tr("Disconnection Failed"), - tr("Failed to disconnect channel %1 on device %2").arg(channelNumber).arg(deviceSerial)); - qWarning() << "Failed to disconnect channel" << channelNumber << "on device" << deviceSerial; - checkbox.blockSignals(true); - checkbox.setChecked(true); - setEnabled(false); // Gray out the widget - } - } + channel_t channelFlag = static_cast(1 << channelNumber); + if (checked) + { + if (eismultiplexer_connect_channel(multiplexer.get(), channelFlag) < 0) + { + QMessageBox::warning(this, tr("Connection Failed"), + tr("Failed to connect channel %1 on device %2").arg(channelNumber).arg(deviceSerial)); + qWarning() << "Failed to connect channel" << channelNumber << "on device" << deviceSerial; + checkbox.blockSignals(true); + checkbox.setChecked(false); + setEnabled(false); // Gray out the widget + } + } + else + { + if (eismultiplexer_disconnect_channel(multiplexer.get(), channelFlag) < 0) + { + QMessageBox::warning(this, tr("Disconnection Failed"), + tr("Failed to disconnect channel %1 on device %2").arg(channelNumber).arg(deviceSerial)); + qWarning() << "Failed to disconnect channel" << channelNumber << "on device" << deviceSerial; + checkbox.blockSignals(true); + checkbox.setChecked(true); + 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(); } diff --git a/channelwidget.h b/channelwidget.h index 08762a6..0c00e3f 100644 --- a/channelwidget.h +++ b/channelwidget.h @@ -1,3 +1,5 @@ + + #ifndef CHANNELWIDGET_H #define CHANNELWIDGET_H @@ -11,39 +13,54 @@ class ChannelWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber, - std::shared_ptr multiplexer, - QWidget *parent = nullptr); - ~ChannelWidget() override; + explicit ChannelWidget(uint16_t deviceSerial, uint16_t channelNumber, + std::shared_ptr multiplexer, + QWidget *parent = nullptr); + ~ChannelWidget() override; - uint16_t getDeviceSerial() const; - uint16_t getChannelNumber() const; - bool isChecked() const; + uint16_t getDeviceSerial() const; + uint16_t getChannelNumber() const; + bool isChecked() const; -signals: - void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber); + // Accessors for MainWindow to populate and access the gang combo + 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: - 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: - uint16_t deviceSerial; - uint16_t channelNumber; - std::shared_ptr multiplexer; - QCheckBox checkbox; - QLabel devicelabel; - QLabel channellabel; - QLabel ganglabel; - QComboBox gangcombo; - QFrame line; - QVBoxLayout vlayout; - QHBoxLayout hlayout; - QVBoxLayout labellayout; + void updateGangCombo(); + void updateCheckboxState(); + + uint16_t deviceSerial; + uint16_t channelNumber; + std::shared_ptr multiplexer; + QCheckBox checkbox; + QLabel devicelabel; + QLabel channellabel; + QLabel ganglabel; + QComboBox gangcombo; + 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 - diff --git a/mainwindow.cpp b/mainwindow.cpp index 0b7ae5d..bb51f00 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,69 +1,96 @@ + + #include #include #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) + : QMainWindow(parent) + , ui(new Ui::MainWindow) { - ui->setupUi(this); - enumerateDevices(); + ui->setupUi(this); + enumerateDevices(); - connect(ui->actionQuit, &QAction::triggered, this, [this]() - { - close(); - }); + connect(ui->actionQuit, &QAction::triggered, this, [this]() + { + close(); + }); } MainWindow::~MainWindow() { - delete ui; + delete ui; } void MainWindow::enumerateDevices() { - size_t count = 0; - uint16_t* serials = eismultiplexer_list_available_devices(&count); + size_t count = 0; + uint16_t* serials = eismultiplexer_list_available_devices(&count); - if (!serials || count == 0) - { - QMessageBox::warning(nullptr, tr("No Devices Found"), - tr("No EIS multiplexer devices were found. Please connect a device and try again.")); - qWarning() << "No EIS multiplexer devices found"; - exit(0); - return; - } + if (!serials || count == 0) + { + QMessageBox::warning(nullptr, tr("No Devices Found"), + tr("No EIS multiplexer devices were found. Please connect a device and try again.")); + qWarning() << "No EIS multiplexer devices found"; + exit(0); + return; + } - for (size_t i = 0; i < count; i++) - { - uint16_t serial = serials[i]; - std::shared_ptr multiplexer(new struct eismultiplexer); - int ret = eismultiplexer_connect(multiplexer.get(), serial); - if (ret == 0) - { - uint16_t channelCount = 0; - qDebug()<<"Adding channels from device "<= 0) - { - for (uint16_t channel = 0; channel < channelCount; channel++) - { - std::shared_ptr widget(new ChannelWidget(serial, channel, multiplexer)); - qDebug()<<"Added widget from device "<channelLayout->addWidget(widget.get()); - } - } - } - else - { - QMessageBox::warning(this, tr("Connection Failed"), - tr("Failed to connect to device with serial %1").arg(serial)); - qWarning()<<"Failed to connect to device with serial"<channelLayout->addStretch(); - } - ui->statusbar->showMessage("Ready"); + // First pass: create all widgets without connecting signals + for (size_t i = 0; i < count; i++) + { + uint16_t serial = serials[i]; + std::shared_ptr multiplexer(new struct eismultiplexer); + int ret = eismultiplexer_connect(multiplexer.get(), serial); + if (ret == 0) + { + uint16_t channelCount = 0; + qDebug()<<"Adding channels from device "<= 0) + { + for (uint16_t channel = 0; channel < channelCount; channel++) + { + std::shared_ptr widget(new ChannelWidget(serial, channel, multiplexer)); + qDebug()<<"Added widget from device "<channelLayout->addWidget(widget.get()); + } + } + } + else + { + QMessageBox::warning(this, tr("Connection Failed"), + tr("Failed to connect to device with serial %1").arg(serial)); + qWarning()<<"Failed to connect to device with serial"<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); } +