270 lines
10 KiB
C++
270 lines
10 KiB
C++
#include <eismultiplexer.h>
|
|
#include <QMessageBox>
|
|
#include <QMessageBox>
|
|
#include <set>
|
|
#include <QFileDialog>
|
|
#include <QTextStream>
|
|
#include <QFile>
|
|
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
#include "triggerwidget.h"
|
|
|
|
MainWindow::MainWindow(QWidget *parent):
|
|
QMainWindow(parent),
|
|
ui(new Ui::MainWindow),
|
|
codeEditor(this),
|
|
pythonOutput(this),
|
|
currentFilePath(""),
|
|
isFileModified(false)
|
|
{
|
|
ui->setupUi(this);
|
|
enumerateDevices();
|
|
|
|
codeEditor.setAutoIndentation(true);
|
|
codeEditor.setAutoParentheses(true);
|
|
codeEditor.setHighlighter(&highligter);
|
|
codeEditor.setCompleter(&completer);
|
|
QFont font("Monospace");
|
|
font.setStyleHint(QFont::TypeWriter);
|
|
codeEditor.setFont(font);
|
|
|
|
ui->codeLayout->addWidget(&codeEditor, 1);
|
|
|
|
pythonOutput.setReadOnly(true);
|
|
ui->codeLayout->addWidget(&pythonOutput);
|
|
|
|
// Set up keyboard shortcuts
|
|
ui->actionOpen->setShortcut(QKeySequence::Open);
|
|
ui->actionSave->setShortcut(QKeySequence::Save);
|
|
ui->actionSave_As->setShortcut(QKeySequence::SaveAs);
|
|
ui->actionQuit->setShortcut(QKeySequence::Quit);
|
|
|
|
connect(ui->actionQuit, &QAction::triggered, this, [this]() {close();});
|
|
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onActionOpenTriggered);
|
|
connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onActionSaveTriggered);
|
|
connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::onActionSaveAsTriggered);
|
|
|
|
// Connect text changed signal to track modifications
|
|
connect(&codeEditor, &QTextEdit::textChanged, this, [this]() {
|
|
isFileModified = true;
|
|
updateStatus();
|
|
});
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void MainWindow::updateStatus()
|
|
{
|
|
if (!currentFilePath.isEmpty()) {
|
|
QString status = "EisMultiplexer-Qt";
|
|
status = QString("%1").arg(currentFilePath);
|
|
if (isFileModified) {
|
|
status += " - [Unsaved Changes]";
|
|
}
|
|
ui->statusbar->showMessage(status);
|
|
}
|
|
}
|
|
|
|
void MainWindow::onActionOpenTriggered()
|
|
{
|
|
QString filePath = QFileDialog::getOpenFileName(this, tr("Open Python Script"),
|
|
"",
|
|
tr("Python Files (*.py);;All Files (*)"));
|
|
|
|
if (filePath.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
QMessageBox::warning(this, tr("Error"), tr("Could not open file: %1").arg(filePath));
|
|
return;
|
|
}
|
|
|
|
QTextStream in(&file);
|
|
QString content = in.readAll();
|
|
file.close();
|
|
|
|
codeEditor.setPlainText(content);
|
|
currentFilePath = filePath;
|
|
isFileModified = false;
|
|
updateStatus();
|
|
}
|
|
|
|
void MainWindow::onActionSaveTriggered()
|
|
{
|
|
if (currentFilePath.isEmpty()) {
|
|
onActionSaveAsTriggered();
|
|
return;
|
|
}
|
|
|
|
QFile file(currentFilePath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
QMessageBox::warning(this, tr("Error"), tr("Could not save file: %1").arg(currentFilePath));
|
|
return;
|
|
}
|
|
|
|
QTextStream out(&file);
|
|
out << codeEditor.toPlainText();
|
|
file.close();
|
|
|
|
isFileModified = false;
|
|
updateStatus();
|
|
}
|
|
|
|
void MainWindow::onActionSaveAsTriggered()
|
|
{
|
|
QString filePath = QFileDialog::getSaveFileName(this, tr("Save Python Script"),
|
|
"",
|
|
tr("Python Files (*.py);;All Files (*)"));
|
|
|
|
if (filePath.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
QMessageBox::warning(this, tr("Error"), tr("Could not save file: %1").arg(filePath));
|
|
return;
|
|
}
|
|
|
|
QTextStream out(&file);
|
|
out << codeEditor.toPlainText();
|
|
file.close();
|
|
|
|
currentFilePath = filePath;
|
|
isFileModified = false;
|
|
updateStatus();
|
|
}
|
|
|
|
void MainWindow::enumerateDevices()
|
|
{
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
int ret = eismultiplexer_connect(multiplexer.get(), serial);
|
|
if (ret == 0)
|
|
{
|
|
uint16_t 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++)
|
|
{
|
|
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
|
|
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
|
|
channels.push_back(widget);
|
|
ui->channelLayout->addWidget(widget.get());
|
|
}
|
|
}
|
|
|
|
// Add trigger widgets
|
|
int triggerCount = eismultiplexer_get_trigger_count(multiplexer.get());
|
|
if (triggerCount > 0)
|
|
{
|
|
qDebug()<<"Adding triggers from device "<<serial;
|
|
for (int trigger = 0; trigger < triggerCount; trigger++)
|
|
{
|
|
std::shared_ptr<TriggerWidget> triggerWidget(new TriggerWidget(serial, trigger, multiplexer));
|
|
qDebug()<<"Added trigger widget from device "<<serial<<" trigger "<<trigger;
|
|
triggers.push_back(triggerWidget);
|
|
ui->channelLayout->addWidget(triggerWidget.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"<<serial<<"eismultiplexer_connect returned"<<ret;
|
|
}
|
|
}
|
|
ui->channelLayout->addStretch();
|
|
|
|
// Second pass: populate gang combos and connect signals for channels
|
|
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);
|
|
generateExample();
|
|
}
|
|
|
|
void MainWindow::generateExample()
|
|
{
|
|
QString example =
|
|
"# This is an example script to show you how\n# to drive eismultiplexer using the python api\n"
|
|
"import eismultiplexer as multi\n"
|
|
"from time import sleep\n\n"
|
|
"# First initalize the device(s)\n";
|
|
|
|
std::set<uint16_t> serials;
|
|
for (size_t i = 0; i < channels.size(); ++i)
|
|
serials.insert(channels[i]->getDeviceSerial());
|
|
|
|
size_t i = 0;
|
|
for (uint16_t serial : serials)
|
|
{
|
|
example.append(QString("multiplexer_") + QString::number(i) + " multi.Multiplexer(serial=" + QString::number(serial) + ")\n");
|
|
++i;
|
|
}
|
|
|
|
example.append("\nprint('\\nListing the nummber of channels per unit')\n");
|
|
for (size_t i = 0; i < serials.size(); ++i)
|
|
{
|
|
QString printLine = "print(f'Found unit with serial number {" + QString::number(channels[i]->getDeviceSerial()) + "} and {multiplexer_" + QString::number(i) + ".getChannelCount()} channels')\n";
|
|
example.append(printLine);
|
|
}
|
|
|
|
example.append("\nprint('Connecting the first and second channel on the first unit')\n");
|
|
example.append("multiplexer_0.connectChannel(multi.Channel.A)\n");
|
|
example.append("multiplexer_0.connectChannel(multi.Channel.B)\n\n");
|
|
example.append("print('Waiting for half a second for something to happen')\n");
|
|
example.append("sleep(0.5)\n\n");
|
|
example.append("print('Disconnect first channel')\n");
|
|
example.append("multiplexer_0.disconnectChannel(multi.Channel.A)\n\n");
|
|
example.append("print('Waiting up to 5000 milliseconds for a trigger')\n");
|
|
example.append("multiplexer_0.setTriggerState(0, multi.TriggerState.INPUT)\n");
|
|
example.append("multiplexer_0.waitTrigger(0, multi.TriggerState.HIGHLEVEL, 5000)\n\n");
|
|
example.append("print('Disconnecting all channels')\n");
|
|
example.append("multiplexer_0.clear()\n");
|
|
codeEditor.setText(example);
|
|
}
|
|
|