Add support for reading state

This commit is contained in:
Carl Philipp Klemm 2025-10-14 12:59:55 +02:00
parent 1eac3f6a83
commit 4c2a4790c0
12 changed files with 163 additions and 103 deletions

View file

@ -42,8 +42,6 @@ add_executable(${PROJECT_NAME}
mainwindow.h mainwindow.h
mainwindow.cpp mainwindow.cpp
mainwindow.ui mainwindow.ui
multiplexer.h
multiplexer.cpp
triggerwidget.cpp triggerwidget.cpp
triggerwidget.h triggerwidget.h
pythonrunner.cpp pythonrunner.cpp

View file

@ -108,7 +108,6 @@ void ChannelWidget::onGangComboChanged(int index)
gangedChannelNumber = -1; gangedChannelNumber = -1;
checkbox.setEnabled(true); checkbox.setEnabled(true);
checkbox.setChecked(false); checkbox.setChecked(false);
checkbox.setChecked(false);
} else { } else {
// A ganged channel was selected // A ganged channel was selected
QString currentText = gangcombo.currentText(); QString currentText = gangcombo.currentText();
@ -128,7 +127,7 @@ void ChannelWidget::onGangComboChanged(int index)
void ChannelWidget::onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked) void ChannelWidget::onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked)
{ {
// If this channel is ganged to the channel that changed state // If this channel is ganged to the channel that changed state
if (gangedDeviceSerial == deviceSerial && gangedChannelNumber == channelNumber) { if (this->deviceSerial == deviceSerial && this->channelNumber == channelNumber) {
// Update our checkbox state to follow the ganged channel // Update our checkbox state to follow the ganged channel
checkbox.blockSignals(true); checkbox.blockSignals(true);
checkbox.setChecked(checked); checkbox.setChecked(checked);
@ -163,3 +162,9 @@ void ChannelWidget::setGangedChannel(uint16_t gangedDeviceSerial, uint16_t gange
updateCheckboxState(); updateCheckboxState();
} }
void ChannelWidget::replaceMultiplexer(std::shared_ptr<struct eismultiplexer> multiplexer, int serial)
{
if(serial == deviceSerial || serial == -1)
this->multiplexer = multiplexer;
}

View file

@ -33,6 +33,7 @@ public slots:
private slots: private slots:
void onChannelToggled(bool checked); void onChannelToggled(bool checked);
void onGangComboChanged(int index); void onGangComboChanged(int index);
void replaceMultiplexer(std::shared_ptr<struct eismultiplexer> multiplexer, int serial = -1);
signals: signals:
void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber); void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber);

View file

@ -1,4 +1,3 @@
#include <eismultiplexer.h>
#include <QMessageBox> #include <QMessageBox>
#include <QMessageBox> #include <QMessageBox>
#include <set> #include <set>
@ -19,7 +18,6 @@ MainWindow::MainWindow(QWidget *parent):
isFileModified(false) isFileModified(false)
{ {
ui->setupUi(this); ui->setupUi(this);
enumerateDevices();
codeEditor.setAutoIndentation(true); codeEditor.setAutoIndentation(true);
codeEditor.setAutoParentheses(true); codeEditor.setAutoParentheses(true);
@ -55,6 +53,8 @@ MainWindow::MainWindow(QWidget *parent):
connect(ui->pushButtonRun, &QPushButton::clicked, this, &MainWindow::runScript); connect(ui->pushButtonRun, &QPushButton::clicked, this, &MainWindow::runScript);
connect(ui->pushButtonStop, &QPushButton::clicked, this, &MainWindow::stopScript); connect(ui->pushButtonStop, &QPushButton::clicked, this, &MainWindow::stopScript);
connect(&pythonRunner, &PythonRunner::scriptFinished, this, &MainWindow::stopScript); connect(&pythonRunner, &PythonRunner::scriptFinished, this, &MainWindow::stopScript);
enumerateDevices();
generateExample();
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -62,6 +62,30 @@ MainWindow::~MainWindow()
delete ui; delete ui;
} }
void MainWindow::readState()
{
for (auto& multiplexer : multiplexers) {
channel_t channelstate = eismultiplexer_get_connected(multiplexer.second.get());
for (auto& channel : channels) {
for(size_t i = 0; i < 16; ++i) {
channel_t mask = static_cast<channel_t>(1 << i);
channel->onOtherChannelStateChanged(multiplexer.first, i, channelstate & mask);
}
}
int triggerCount = eismultiplexer_get_trigger_count(multiplexer.second.get());
for(int i = 0; i < triggerCount; ++i) {
bool level;
trigger_state_t state;
int ret = eismultiplexer_get_trigger_state(multiplexer.second.get(), i, &state, &level);
if (ret < 0) {
for (auto& trigger : triggers)
trigger->updateSate(multiplexer.first, i, state);
}
}
}
}
void MainWindow::updateStatus() void MainWindow::updateStatus()
{ {
if (!currentFilePath.isEmpty()) { if (!currentFilePath.isEmpty()) {
@ -150,6 +174,9 @@ 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 (ui->scrollArea->layout())
delete ui->scrollArea->layout();
QVBoxLayout* channelLayout = new QVBoxLayout(ui->scrollArea);
if (!serials || count == 0) if (!serials || count == 0)
{ {
@ -168,6 +195,7 @@ void MainWindow::enumerateDevices()
int ret = eismultiplexer_connect(multiplexer.get(), serial); int ret = eismultiplexer_connect(multiplexer.get(), serial);
if (ret == 0) if (ret == 0)
{ {
multiplexers.push_back({serial, multiplexer});
uint16_t channelCount = 0; uint16_t channelCount = 0;
qDebug()<<"Adding channels from device "<<serial; qDebug()<<"Adding channels from device "<<serial;
if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0) if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0)
@ -177,7 +205,7 @@ void MainWindow::enumerateDevices()
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer)); std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel; qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
channels.push_back(widget); channels.push_back(widget);
ui->channelLayout->addWidget(widget.get()); channelLayout->addWidget(widget.get());
} }
} }
@ -191,7 +219,7 @@ void MainWindow::enumerateDevices()
std::shared_ptr<TriggerWidget> triggerWidget(new TriggerWidget(serial, trigger, multiplexer)); std::shared_ptr<TriggerWidget> triggerWidget(new TriggerWidget(serial, trigger, multiplexer));
qDebug()<<"Added trigger widget from device "<<serial<<" trigger "<<trigger; qDebug()<<"Added trigger widget from device "<<serial<<" trigger "<<trigger;
triggers.push_back(triggerWidget); triggers.push_back(triggerWidget);
ui->channelLayout->addWidget(triggerWidget.get()); channelLayout->addWidget(triggerWidget.get());
} }
} }
} }
@ -202,7 +230,8 @@ void MainWindow::enumerateDevices()
qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret; qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret;
} }
} }
ui->channelLayout->addStretch();
channelLayout->addStretch();
// Second pass: populate gang combos and connect signals for channels // Second pass: populate gang combos and connect signals for channels
for (const auto& widget : channels) { for (const auto& widget : channels) {
@ -226,10 +255,11 @@ void MainWindow::enumerateDevices()
} }
} }
readState();
ui->statusbar->showMessage("Ready"); ui->statusbar->showMessage("Ready");
free(serials); free(serials);
generateExample();
} }
void MainWindow::runScript() { void MainWindow::runScript() {
@ -239,14 +269,25 @@ void MainWindow::runScript() {
ui->pushButtonStop->setEnabled(true); ui->pushButtonStop->setEnabled(true);
codeEditor.setEnabled(false); codeEditor.setEnabled(false);
ui->scrollArea->setEnabled(false); ui->scrollArea->setEnabled(false);
disconnectDevices();
} }
void MainWindow::stopScript() { void MainWindow::stopScript() {
pythonRunner.stopScript(); pythonRunner.stopScript();
channels.clear();
triggers.clear();
ui->pushButtonRun->setEnabled(true); ui->pushButtonRun->setEnabled(true);
ui->pushButtonStop->setEnabled(false); ui->pushButtonStop->setEnabled(false);
codeEditor.setEnabled(true); codeEditor.setEnabled(true);
ui->scrollArea->setEnabled(true); ui->scrollArea->setEnabled(true);
enumerateDevices();
}
void MainWindow::disconnectDevices()
{
for(auto& multiplexer : multiplexers)
eismultiplexer_disconnect(multiplexer.second.get());
multiplexers.clear();
} }
void MainWindow::generateExample() void MainWindow::generateExample()

View file

@ -1,13 +1,13 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <eismultiplexer.h>
#include <QMainWindow> #include <QMainWindow>
#include <memory> #include <memory>
#include <QCodeEditor> #include <QCodeEditor>
#include <QPythonCompleter> #include <QPythonCompleter>
#include <QPythonHighlighter> #include <QPythonHighlighter>
#include <QProgressBar> #include <QProgressBar>
#include "channelwidget.h" #include "channelwidget.h"
#include "triggerwidget.h" #include "triggerwidget.h"
#include "pythonrunner.h" #include "pythonrunner.h"
@ -19,10 +19,11 @@ class MainWindow;
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
std::vector<std::shared_ptr<ChannelWidget>> channels; std::vector<std::shared_ptr<TriggerWidget>> triggers;
std::vector<std::shared_ptr<TriggerWidget>> triggers; std::vector<std::shared_ptr<ChannelWidget>> channels;
Ui::MainWindow *ui; std::vector<std::pair<uint16_t, std::shared_ptr<struct eismultiplexer>>> multiplexers;
Ui::MainWindow *ui;
QCodeEditor codeEditor; QCodeEditor codeEditor;
QPythonHighlighter highligter; QPythonHighlighter highligter;
QPythonCompleter completer; QPythonCompleter completer;
@ -42,11 +43,13 @@ private slots:
void onActionSaveAsTriggered(); void onActionSaveAsTriggered();
void runScript(); void runScript();
void stopScript(); void stopScript();
void readState();
private: private:
void enumerateDevices(); void enumerateDevices();
void generateExample(); void generateExample();
void updateStatus(); void updateStatus();
void disconnectDevices();
QString currentFilePath; QString currentFilePath;
bool isFileModified; bool isFileModified;
}; };

View file

@ -16,58 +16,65 @@
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<layout class="QVBoxLayout" name="codeLayout"> <widget class="QSplitter" name="splitter">
<property name="leftMargin"> <property name="orientation">
<number>10</number> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<item> <widget class="QWidget" name="">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QVBoxLayout" name="codeLayout">
<property name="leftMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QPushButton" name="pushButtonStop"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="enabled"> <item>
<bool>false</bool> <widget class="QPushButton" name="pushButtonStop">
</property> <property name="enabled">
<property name="text"> <bool>false</bool>
<string>Stop</string> </property>
</property> <property name="text">
<property name="icon"> <string>Stop</string>
<iconset theme="QIcon::ThemeIcon::MediaPlaybackStop"/> </property>
</property> <property name="icon">
</widget> <iconset theme="QIcon::ThemeIcon::MediaPlaybackStop"/>
</item> </property>
<item> </widget>
<widget class="QPushButton" name="pushButtonRun"> </item>
<property name="text"> <item>
<string>Run</string> <widget class="QPushButton" name="pushButtonRun">
</property> <property name="text">
<property name="icon"> <string>Run</string>
<iconset theme="QIcon::ThemeIcon::MediaPlaybackStart"/> </property>
</property> <property name="icon">
</widget> <iconset theme="QIcon::ThemeIcon::MediaPlaybackStart"/>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </widget>
</layout> <widget class="QScrollArea" name="scrollArea">
</item> <property name="minimumSize">
<item> <size>
<widget class="QScrollArea" name="scrollArea"> <width>400</width>
<property name="widgetResizable"> <height>0</height>
<bool>true</bool> </size>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>591</width>
<height>533</height>
</rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <property name="widgetResizable">
<item> <bool>true</bool>
<layout class="QVBoxLayout" name="channelLayout"/> </property>
</item> <widget class="QWidget" name="scrollAreaWidgetContents">
</layout> <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>504</width>
<height>537</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3"/>
</widget>
</widget> </widget>
</widget> </widget>
</item> </item>

View file

@ -1,16 +0,0 @@
#include "multiplexer.h"
Multiplexer::Multiplexer(QObject *parent)
: QObject{parent}
{}
Multiplexer::~Multiplexer()
{
for(auto& multiplexer : multiplexers)
eismultiplexer_disconnect(multiplexer.get());
}
void Multiplexer::probe()
{
}

View file

@ -1,22 +0,0 @@
#ifndef MULTIPLEXER_H
#define MULTIPLEXER_H
#include <QObject>
#include "eismultiplexer.h"
class Multiplexer : public QObject
{
Q_OBJECT
std::vector<std::shared_ptr<struct eismultiplexer>> multiplexers;
std::vector<channel_t> channelStates;
public:
explicit Multiplexer(QObject *parent = nullptr);
~Multiplexer();
void probe();
signals:
void foundDevice(std::shared_ptr<struct eismultiplexer>);
};
#endif // MULTIPLEXER_H

View file

@ -16,7 +16,7 @@ PythonRunner::~PythonRunner() {
} }
} }
void PythonRunner::runScript(const QString& scriptContent) { bool PythonRunner::runScript(const QString& scriptContent) {
if (m_process->state() == QProcess::Running) { if (m_process->state() == QProcess::Running) {
m_process->terminate(); m_process->terminate();
m_process->waitForFinished(1000); m_process->waitForFinished(1000);
@ -24,6 +24,8 @@ void PythonRunner::runScript(const QString& scriptContent) {
m_outputWidget->clear(); m_outputWidget->clear();
m_process->start("python3", QStringList() << "-u" << "-c" << scriptContent); m_process->start("python3", QStringList() << "-u" << "-c" << scriptContent);
return true;
} }
void PythonRunner::stopScript() { void PythonRunner::stopScript() {

View file

@ -3,6 +3,7 @@
#include <QTextEdit> #include <QTextEdit>
#include <QProcess> #include <QProcess>
#include <QObject> #include <QObject>
#include <QTemporaryDir>
class PythonRunner : public QObject { class PythonRunner : public QObject {
Q_OBJECT Q_OBJECT
@ -11,7 +12,7 @@ public:
PythonRunner(QTextEdit* outputWidget, QObject* parent = nullptr); PythonRunner(QTextEdit* outputWidget, QObject* parent = nullptr);
~PythonRunner(); ~PythonRunner();
void runScript(const QString& scriptContent); bool runScript(const QString& scriptContent);
void stopScript(); void stopScript();
private slots: private slots:
@ -25,4 +26,6 @@ signals:
private: private:
QTextEdit* m_outputWidget; QTextEdit* m_outputWidget;
QProcess* m_process; QProcess* m_process;
bool ready;
QTemporaryDir dir;
}; };

View file

@ -70,9 +70,46 @@ void TriggerWidget::onLevelToggled(bool checked)
updateTriggerState(); updateTriggerState();
} }
void TriggerWidget::updateSate(uint16_t serial, uint16_t trigger, trigger_state_t state)
{
if (serial == deviceSerial && trigger == triggerNumber) {
switch (state)
{
case TRIGGER_INPUT:
levelCheckbox.blockSignals(true);
levelCheckbox.setChecked(false);
levelCheckbox.setEnabled(false);
levelCheckbox.blockSignals(false);
inputCheckbox.blockSignals(true);
inputCheckbox.setChecked(true);
inputCheckbox.blockSignals(false);
break;
case TRIGGER_HIGH:
inputCheckbox.blockSignals(true);
inputCheckbox.setChecked(false);
inputCheckbox.blockSignals(false);
levelCheckbox.blockSignals(true);
levelCheckbox.setChecked(true);
levelCheckbox.setEnabled(true);
levelCheckbox.blockSignals(false);
break;
case TRIGGER_LOW:
inputCheckbox.blockSignals(true);
inputCheckbox.setChecked(false);
inputCheckbox.blockSignals(false);
levelCheckbox.blockSignals(true);
levelCheckbox.setChecked(true);
levelCheckbox.setEnabled(false);
levelCheckbox.blockSignals(false);
default:
break;
}
}
}
void TriggerWidget::updateTriggerState() void TriggerWidget::updateTriggerState()
{ {
trigger_state_t state; trigger_state_t state = TRIGGER_INPUT;
if(inputCheckbox.isChecked()) if(inputCheckbox.isChecked())
{ {
levelCheckbox.blockSignals(true); levelCheckbox.blockSignals(true);

View file

@ -22,6 +22,7 @@ public:
uint16_t getDeviceSerial() const; uint16_t getDeviceSerial() const;
uint16_t getTriggerNumber() const; uint16_t getTriggerNumber() const;
void updateSate(uint16_t serial, uint16_t trigger, trigger_state_t state);
private slots: private slots:
void onInputToggled(bool checked); void onInputToggled(bool checked);