commit e87470d14ebe9800acc5e4d63cfe667caa9efbcc Author: Carl Philipp Klemm Date: Mon Aug 25 17:01:34 2025 +0200 inial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e86a2c3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.14) + +project(eismuliplexer-qt) + +set(CMAKE_PROJECT_VERSION_MAJOR 0) +set(CMAKE_PROJECT_VERSION_MINOR 9) +set(CMAKE_PROJECT_VERSION_PATCH 0) + +add_compile_definitions(VERSION_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}) +add_compile_definitions(VERSION_MINOR=${CMAKE_PROJECT_VERSION_MINOR}) +add_compile_definitions(VERSION_PATCH=${CMAKE_PROJECT_VERSION_PATCH}) + +if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + message(FATAL_ERROR "Windows builds have to be cross compiled on UNIX") +endif() + +message("Platform " ${CMAKE_SYSTEM_NAME}) +if(WIN32) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/release-win.sh ${CMAKE_CURRENT_BINARY_DIR}/release.sh @ONLY) + add_custom_target(package + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/release.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Createing release archive" + VERBATIM) +endif(WIN32) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer) +find_package(Qt6 REQUIRED COMPONENTS Widgets) +find_package(Qt6 REQUIRED COMPONENTS Core) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +add_executable(${PROJECT_NAME} + main.cpp + channelwidget.cpp + channelwidget.h + mainwindow.h + mainwindow.cpp + mainwindow.ui + multiplexer.h + multiplexer.cpp +) +target_compile_options(${PROJECT_NAME} PUBLIC "-Wall") +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES}) +#target_include_directories(${PROJECT_NAME} PUBLIC ${EISMULIPLEXER_INCLUDE_DIRS}) diff --git a/channelwidget.cpp b/channelwidget.cpp new file mode 100644 index 0000000..ae259fc --- /dev/null +++ b/channelwidget.cpp @@ -0,0 +1,97 @@ +#include "channelwidget.h" +#include +#include + +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:") +{ + hlayout.addLayout(&labellayout); + vlayout.addLayout(&hlayout); + + labellayout.addWidget(&devicelabel); + labellayout.addWidget(&channellabel); + + line.setGeometry(QRect(320, 150, 118, 3)); + line.setFrameShape(QFrame::HLine); + line.setFrameShadow(QFrame::Sunken); + vlayout.addWidget(&line); + + gangcombo.addItem("Unganged"); + + hlayout.addStretch(); + hlayout.addWidget(&ganglabel); + hlayout.addWidget(&gangcombo); + hlayout.addWidget(&checkbox); + connect(&checkbox, &QCheckBox::toggled, this, &ChannelWidget::onChannelToggled); + + setFixedHeight(96); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + setLayout(&vlayout); +} + +ChannelWidget::~ChannelWidget() +{ + // Nothing to clean up +} + +uint16_t ChannelWidget::getDeviceSerial() const +{ + return deviceSerial; +} + +uint16_t ChannelWidget::getChannelNumber() const +{ + return channelNumber; +} + +bool ChannelWidget::isChecked() const +{ + return checkbox.isChecked(); +} + +void ChannelWidget::onChannelToggled(bool checked) +{ + 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 + } + } +} + diff --git a/channelwidget.h b/channelwidget.h new file mode 100644 index 0000000..08762a6 --- /dev/null +++ b/channelwidget.h @@ -0,0 +1,49 @@ +#ifndef CHANNELWIDGET_H +#define CHANNELWIDGET_H + +#include +#include +#include +#include +#include +#include +#include + +class ChannelWidget : public QWidget +{ + Q_OBJECT + +public: + 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; + +signals: + void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber); + +private slots: + void onChannelToggled(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; +}; + +#endif // CHANNELWIDGET_H + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fb4d5b4 --- /dev/null +++ b/main.cpp @@ -0,0 +1,10 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow window; + window.show(); + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..99a5739 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,68 @@ +#include +#include +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + enumerateDevices(); + + connect(ui->actionQuit, &QAction::triggered, this, [this]() + { + close(); + }); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +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; + } + + for (size_t i = 0; i < count; i++) + { + uint16_t serial = serials[i]; + std::shared_ptr multiplexer(new struct eismultiplexer); + if (eismultiplexer_connect(multiplexer.get(), serial) >= 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" << serial; + } + ui->channelLayout->addStretch(); + } + ui->statusbar->showMessage("Ready"); + + free(serials); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..cb1a19c --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,31 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +#include "channelwidget.h" + +namespace Ui +{ +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + std::vector> channels; + Ui::MainWindow *ui; + +signals: + void channelStateChanged(uint16_t device, uint16_t channel); + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + void enumerateDevices(); +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..53aca8f --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,68 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + EisMultiplexer-Qt + + + + + + + true + + + + + 0 + 0 + 788 + 537 + + + + + + + + + + + + + + + + 0 + 0 + 800 + 29 + + + + + File + + + + + + + + + Quit + + + + + + diff --git a/multiplexer.cpp b/multiplexer.cpp new file mode 100644 index 0000000..3e1c8e0 --- /dev/null +++ b/multiplexer.cpp @@ -0,0 +1,16 @@ +#include "multiplexer.h" + +Multiplexer::Multiplexer(QObject *parent) + : QObject{parent} +{} + +Multiplexer::~Multiplexer() +{ + for(auto& multiplexer : multiplexers) + eismultiplexer_disconnect(multiplexer.get()); +} + +void Multiplexer::probe() +{ + +} diff --git a/multiplexer.h b/multiplexer.h new file mode 100644 index 0000000..32a04ec --- /dev/null +++ b/multiplexer.h @@ -0,0 +1,22 @@ +#ifndef MULTIPLEXER_H +#define MULTIPLEXER_H + +#include +#include "eismultiplexer.h" + +class Multiplexer : public QObject +{ + Q_OBJECT + std::vector> multiplexers; + std::vector channelStates; + +public: + explicit Multiplexer(QObject *parent = nullptr); + ~Multiplexer(); + void probe(); + +signals: + void foundDevice(std::shared_ptr); +}; + +#endif // MULTIPLEXER_H diff --git a/scripts/release-win.sh b/scripts/release-win.sh new file mode 100755 index 0000000..f987f25 --- /dev/null +++ b/scripts/release-win.sh @@ -0,0 +1,61 @@ +#!/bin/bash -e +# +# libkissinference - an inference libary for kiss networks +# Copyright (C) 2024 Carl Philipp Klemm +# +# This file is part of libkissinference. +# +# libkissinference is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# libkissinference is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with libkissinference. If not, see . +# + +PROJECTNAME=@PROJECT_NAME@ +SYSTEMPROC=@CMAKE_SYSTEM_PROCESSOR@ +ROOTPATH=@CMAKE_FIND_ROOT_PATH@ +VERSION="@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@" +BINARYDIR="@CMAKE_CURRENT_BINARY_DIR@" +SRCDIR="@CMAKE_CURRENT_SOURCE_DIR@" +RELDIRECTORY="$BINARYDIR/packaged/$VERSION/release" +ZIPNAME=$PROJECTNAME-$SYSTEMPROC-$VERSION + +rm $BINARYDIR/packaged/$ZIPNAME.zip || true +cd $BINARYDIR +install -d $RELDIRECTORY +cp eismuliplexer-qt.exe $RELDIRECTORY +cp $ROOTPATH/bin/libwinpthread-1.dll $RELDIRECTORY +cp $ROOTPATH/bin/libusb-1.0.dll $RELDIRECTORY +cp $ROOTPATH/bin/libssp-0.dll $RELDIRECTORY +cp $ROOTPATH/bin/libgcc_s_seh-1.dll $RELDIRECTORY +cp $ROOTPATH/bin/libstdc++-6.dll $RELDIRECTORY +cp $ROOTPATH/bin/Qt6Core.dll $RELDIRECTORY +cp $ROOTPATH/bin/Qt6Gui.dll $RELDIRECTORY +cp $ROOTPATH/bin/Qt6Widgets.dll $RELDIRECTORY +cp -r $ROOTPATH/lib/qt6/plugins/platforms $RELDIRECTORY +cp $ROOTPATH/lib/libeismultiplexer.dll $RELDIRECTORY +cp $ROOTPATH/bin/libpcre2-*-0.dll $RELDIRECTORY +cp $ROOTPATH/bin/zlib1.dll $RELDIRECTORY +cp $ROOTPATH/bin/libfreetype-6.dll $RELDIRECTORY +cp $ROOTPATH/bin/libpng16-16.dll $RELDIRECTORY +cp $ROOTPATH/bin/libzstd.dll $RELDIRECTORY +cp $ROOTPATH/bin/libharfbuzz-0.dll $RELDIRECTORY +cp $ROOTPATH/bin/libintl-8.dll $RELDIRECTORY +cp $ROOTPATH/bin/libgraphite2.dll $RELDIRECTORY +cp $ROOTPATH/bin/libglib-2.0-0.dll $RELDIRECTORY +cp $ROOTPATH/bin/libbz2-1.dll $RELDIRECTORY +cp $ROOTPATH/bin/libbrotlidec.dll $RELDIRECTORY +cp $ROOTPATH/bin/libbrotlicommon.dll $RELDIRECTORY +cp $ROOTPATH/bin/libiconv-2.dll $RELDIRECTORY +#cp $SRCDIR/README.md $RELDIRECTORY +cd $RELDIRECTORY/.. +rm $BINARYDIR/packaged/$ZIPNAME.zip || true +zip -r $BINARYDIR/packaged/$ZIPNAME.zip release