mirror of
https://github.com/Sani7/remote-ui.git
synced 2026-10-27 06:01:14 +01:00
Move
Updated submodule client/qwt
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(Simulator-CVS_I10 VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(magic_enum URL https://github.com/Neargye/magic_enum/releases/download/v0.9.6/magic_enum-v0.9.6.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
|
||||
FetchContent_MakeAvailable(magic_enum)
|
||||
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
|
||||
FetchContent_MakeAvailable(json)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets Svg WebSockets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Svg WebSockets)
|
||||
|
||||
add_subdirectory(qwt)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
simulators/sim-cvs_i10.h
|
||||
simulators/sim-cvs_i10.cpp
|
||||
simulators/sim-cvs_i10.ui
|
||||
|
||||
simulators/sim-can_debugger.h
|
||||
simulators/sim-can_debugger.cpp
|
||||
simulators/sim-can_debugger.ui
|
||||
|
||||
simulators/sim-scope_mux_tester.h
|
||||
simulators/sim-scope_mux_tester.cpp
|
||||
simulators/sim-scope_mux_tester.ui
|
||||
|
||||
simulators/sim-test.h
|
||||
simulators/sim-test.cpp
|
||||
simulators/sim-test.ui
|
||||
|
||||
simulator_base.h
|
||||
simulator_base.cpp
|
||||
helpers.h
|
||||
helpers.cpp
|
||||
IterableEnum.hpp
|
||||
hex_spinbox.h
|
||||
hex_spinbox.cpp
|
||||
better_radiobutton.h
|
||||
better_radiobutton.cpp
|
||||
led.h
|
||||
led.cpp
|
||||
|
||||
can_transceive.h
|
||||
can_transceive.cpp
|
||||
can_transceive.ui
|
||||
|
||||
networkerror.h
|
||||
networkerror.cpp
|
||||
networkerror.ui
|
||||
|
||||
web_socket_wrapper.h
|
||||
web_socket_wrapper.cpp
|
||||
|
||||
mainwindow.h
|
||||
mainwindow.cpp
|
||||
mainwindow.ui
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(Simulator-CVS_I10
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define target properties for Android with Qt 6 as:
|
||||
# set_property(TARGET Simulator-CVS_I10 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
|
||||
else()
|
||||
if(ANDROID)
|
||||
add_library(Simulator-CVS_I10 SHARED
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define properties for Android with Qt 5 after find_package() calls as:
|
||||
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
else()
|
||||
add_executable(Simulator-CVS_I10
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(Simulator-CVS_I10
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/simulators
|
||||
${PROJECT_BINARY_DIR}/
|
||||
)
|
||||
|
||||
target_link_libraries(Simulator-CVS_I10 PRIVATE qwt Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebSockets magic_enum::magic_enum nlohmann_json::nlohmann_json)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
# explicit, fixed bundle identifier manually though.
|
||||
if(${QT_VERSION} VERSION_LESS 6.1.0)
|
||||
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.Simulator-CVS_I10)
|
||||
endif()
|
||||
set_target_properties(Simulator-CVS_I10 PROPERTIES
|
||||
${BUNDLE_ID_OPTION}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
MACOSX_BUNDLE TRUE
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS Simulator-CVS_I10
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt_finalize_executable(Simulator-CVS_I10)
|
||||
endif()
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
A way to iterate over enumerations as C++ template.
|
||||
|
||||
Copyright (c) 2023 Sander Speetjens
|
||||
Licensed under the terms of the MIT license (given below).
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ITERABLE_ENUM_BEGIN(name) enum class name : int {
|
||||
#define ITERABLE_ENUM_END ,end, begin = 0 }
|
||||
|
||||
template <class T>
|
||||
class Iterable
|
||||
{
|
||||
public:
|
||||
Iterable(T value = T::begin) : mValue(value) {}
|
||||
Iterable(const Iterable& other) : mValue(other.mValue) {}
|
||||
|
||||
Iterable& operator++()
|
||||
{
|
||||
assert(mValue >= T::begin && mValue < T::end);
|
||||
mValue = static_cast<T>(static_cast<int>(mValue) + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterable operator++(int)
|
||||
{
|
||||
Iterable prev = *this;
|
||||
mValue = static_cast<T>(static_cast<int>(mValue) + 1);
|
||||
return prev;
|
||||
}
|
||||
|
||||
Iterable& operator--()
|
||||
{
|
||||
assert(mValue >= T::end && mValue < T::begin);
|
||||
mValue = static_cast<T>(static_cast<int>(mValue) - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterable operator--(int)
|
||||
{
|
||||
Iterable prev = *this;
|
||||
mValue = static_cast<T>(static_cast<int>(mValue) - 1);
|
||||
return prev;
|
||||
}
|
||||
|
||||
Iterable operator+(int i)
|
||||
{
|
||||
return static_cast<T>(static_cast<int>(mValue) + i);
|
||||
}
|
||||
|
||||
Iterable operator+(T i)
|
||||
{
|
||||
return static_cast<T>(static_cast<int>(mValue) + static_cast<int>(i));
|
||||
}
|
||||
|
||||
Iterable operator-(int i)
|
||||
{
|
||||
return static_cast<T>(static_cast<int>(mValue) - i);
|
||||
}
|
||||
|
||||
Iterable operator-(T i)
|
||||
{
|
||||
return static_cast<T>(static_cast<int>(mValue) - static_cast<int>(i));
|
||||
}
|
||||
|
||||
|
||||
bool operator >(const T& other) const
|
||||
{
|
||||
return static_cast<int>(mValue) > static_cast<int>(other);
|
||||
}
|
||||
|
||||
bool operator >=(const T& other) const
|
||||
{
|
||||
return static_cast<int>(mValue) >= static_cast<int>(other);
|
||||
}
|
||||
|
||||
bool operator <(const T& other) const
|
||||
{
|
||||
return static_cast<int>(mValue) < static_cast<int>(other);
|
||||
}
|
||||
|
||||
bool operator <=(const T& other) const
|
||||
{
|
||||
return static_cast<int>(mValue) <= static_cast<int>(other);
|
||||
}
|
||||
|
||||
bool operator ==(const T& other) const
|
||||
{
|
||||
return static_cast<int>(mValue) == static_cast<int>(other);
|
||||
}
|
||||
|
||||
bool valid() const { return mValue != T::end; }
|
||||
|
||||
operator T() const { return mValue; }
|
||||
operator T&() { return mValue; }
|
||||
T val() const { return mValue; }
|
||||
|
||||
private:
|
||||
T mValue;
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "better_radiobutton.h"
|
||||
|
||||
Better_Radiobutton::Better_Radiobutton(QWidget* parent) :
|
||||
QWidget{parent},
|
||||
m_radiobuttons{}
|
||||
{
|
||||
}
|
||||
|
||||
Better_Radiobutton::Better_Radiobutton(std::vector<QString> names, QWidget* parent) :
|
||||
QWidget{parent},
|
||||
m_v_layout{new QVBoxLayout(this)},
|
||||
m_radiobuttons{},
|
||||
m_checked{""}
|
||||
{
|
||||
for (const auto& name : names) {
|
||||
add_radiobutton(name);
|
||||
}
|
||||
}
|
||||
|
||||
void Better_Radiobutton::add_radiobutton(QString name)
|
||||
{
|
||||
auto radiobutton = new QRadioButton(name, this);
|
||||
m_radiobuttons.push_back(radiobutton);
|
||||
m_v_layout->addWidget(radiobutton);
|
||||
|
||||
connect(radiobutton, &QRadioButton::toggled, this, [this, name, radiobutton] {
|
||||
emit toggled(name);
|
||||
if (radiobutton->isChecked()) {
|
||||
m_checked = name;
|
||||
emit checked(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString Better_Radiobutton::isChecked() const
|
||||
{
|
||||
return m_checked;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef BETTER_RADIOBUTTON_H
|
||||
#define BETTER_RADIOBUTTON_H
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QRadioButton>
|
||||
|
||||
class Better_Radiobutton : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Better_Radiobutton(QWidget* parent = nullptr);
|
||||
explicit Better_Radiobutton(std::vector<QString>names, QWidget* partent = nullptr);
|
||||
|
||||
void add_radiobutton(QString name);
|
||||
QString isChecked() const;
|
||||
|
||||
signals:
|
||||
void toggled(QString name);
|
||||
void checked(QString name);
|
||||
private:
|
||||
QVBoxLayout* m_v_layout;
|
||||
std::vector<QRadioButton*> m_radiobuttons;
|
||||
QString m_checked;
|
||||
};
|
||||
|
||||
#endif // BETTER_RADIOBUTTON_H
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "can_transceive.h"
|
||||
#include "ui_can_transceive.h"
|
||||
|
||||
Can_Transceive::Can_Transceive(QWidget* parent) : QWidget(parent), ui(new Ui::Can_Transceive) {
|
||||
ui->setupUi(this);
|
||||
|
||||
can_send_hexspinboxes = {
|
||||
ui->s_can_send_data0,
|
||||
ui->s_can_send_data1,
|
||||
ui->s_can_send_data2,
|
||||
ui->s_can_send_data3,
|
||||
ui->s_can_send_data4,
|
||||
ui->s_can_send_data5,
|
||||
ui->s_can_send_data6,
|
||||
ui->s_can_send_data7,
|
||||
};
|
||||
|
||||
enable_can_send_data();
|
||||
connect(ui->c_can_send_dlc, &QComboBox::currentIndexChanged, this, [=, this]{enable_can_send_data();});
|
||||
connect(ui->b_can_send_send, &QPushButton::clicked, this, [=, this]{emit send_can_message(ui->s_can_send_sid->value(), ui->c_can_send_dlc->currentIndex(), std::array<uint8_t, 8>({(uint8_t)(can_send_hexspinboxes[0]->value()), (uint8_t)(can_send_hexspinboxes[1]->value()),(uint8_t)(can_send_hexspinboxes[2]->value()),(uint8_t)(can_send_hexspinboxes[3]->value()),(uint8_t)(can_send_hexspinboxes[4]->value()),(uint8_t)(can_send_hexspinboxes[5]->value()),(uint8_t)(can_send_hexspinboxes[6]->value()),(uint8_t)(can_send_hexspinboxes[7]->value())}));});
|
||||
connect(ui->b_clear, &QPushButton::clicked, this, [=, this]{emit can_clear();});
|
||||
}
|
||||
|
||||
Can_Transceive::~Can_Transceive() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void Can_Transceive::enable_can_send_data()
|
||||
{
|
||||
uint8_t selected = atoi(ui->c_can_send_dlc->currentText().toLocal8Bit());
|
||||
for (size_t i = 0; i < can_send_hexspinboxes.size(); i++)
|
||||
{
|
||||
can_send_hexspinboxes[i]->setEnabled(i < selected);
|
||||
if (i >= selected)
|
||||
{
|
||||
can_send_hexspinboxes[i]->setValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Can_Transceive::clear()
|
||||
{
|
||||
ui->recvd_list->setRowCount(0);
|
||||
ui->send_list->setRowCount(0);
|
||||
}
|
||||
|
||||
void Can_Transceive::add_send_item(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload)
|
||||
{
|
||||
size_t rc = ui->send_list->rowCount();
|
||||
ui->send_list->setRowCount(rc + 1);
|
||||
|
||||
ui->send_list->setItem(rc, 0, new QTableWidgetItem(QString().setNum(id, 16)));
|
||||
ui->send_list->setItem(rc, 1, new QTableWidgetItem(QString("%0").arg(dlc)));
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
ui->send_list->setItem(rc, 2 + i, new QTableWidgetItem(QString().setNum(payload[i], 16)));
|
||||
}
|
||||
ui->send_list->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
void Can_Transceive::add_receive_item(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload)
|
||||
{
|
||||
size_t rc = ui->recvd_list->rowCount();
|
||||
ui->recvd_list->setRowCount(rc + 1);
|
||||
|
||||
ui->recvd_list->setItem(rc, 0, new QTableWidgetItem(QString().setNum(id, 16)));
|
||||
ui->recvd_list->setItem(rc, 1, new QTableWidgetItem(QString("%0").arg(dlc)));
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
ui->recvd_list->setItem(rc, 2 + i, new QTableWidgetItem(QString().setNum(payload[i], 16)));
|
||||
}
|
||||
ui->recvd_list->resizeColumnsToContents();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef CAN_TRANSCEIVE_H
|
||||
#define CAN_TRANSCEIVE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "hex_spinbox.h"
|
||||
|
||||
namespace Ui {
|
||||
class Can_Transceive;
|
||||
}
|
||||
|
||||
class Can_Transceive : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Can_Transceive(QWidget* parent = nullptr);
|
||||
~Can_Transceive();
|
||||
void enable_can_send_data();
|
||||
void clear();
|
||||
void add_send_item(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload);
|
||||
void add_receive_item(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload);
|
||||
|
||||
signals:
|
||||
void send_can_message(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload);
|
||||
void can_clear();
|
||||
|
||||
private:
|
||||
Ui::Can_Transceive* ui;
|
||||
std::vector<HexSpinBox*> can_send_hexspinboxes;
|
||||
};
|
||||
|
||||
#endif // CAN_TRANSCEIVE_H
|
||||
@@ -0,0 +1,359 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Can_Transceive</class>
|
||||
<widget class="QWidget" name="Can_Transceive">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>713</width>
|
||||
<height>562</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Received CAN messages</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="recvd_list">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>SID</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>DLC</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 0</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 4</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 5</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 6</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 7</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_8">
|
||||
<property name="title">
|
||||
<string>Send CAN message</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="c_can_send_dlc">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="10">
|
||||
<widget class="QPushButton" name="b_can_send_send">
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="HexSpinBox" name="s_can_send_data1">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="HexSpinBox" name="s_can_send_sid">
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="HexSpinBox" name="s_can_send_data0">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>SID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>DLC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Data 0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Data 1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Data 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Data 3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Data 4</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="7">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Data 5</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="8">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Data 6</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="9">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Data 7</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="HexSpinBox" name="s_can_send_data2">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<widget class="HexSpinBox" name="s_can_send_data3">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="HexSpinBox" name="s_can_send_data4">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<widget class="HexSpinBox" name="s_can_send_data5">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="8">
|
||||
<widget class="HexSpinBox" name="s_can_send_data6">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="9">
|
||||
<widget class="HexSpinBox" name="s_can_send_data7">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Send CAN messages</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="send_list">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>SID</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>DLC</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 0</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 3</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 4</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 5</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 6</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data 7</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="b_clear">
|
||||
<property name="text">
|
||||
<string>clear buffers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HexSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>hex_spinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "helpers.h"
|
||||
|
||||
void create_dial_needle(QwtDial* dial)
|
||||
{
|
||||
QwtDialSimpleNeedle* needle = new QwtDialSimpleNeedle(
|
||||
QwtDialSimpleNeedle::Arrow, true, Qt::yellow,
|
||||
QColor( Qt::gray ).lighter( 130 ) );
|
||||
needle->setWidth(2);
|
||||
dial->setNeedle( needle );
|
||||
}
|
||||
|
||||
void set_widget_color(QWidget* widget, QColor color, QColor bg)
|
||||
{
|
||||
QString style = QString("#%1 { background-color: %2; color: %3;}").arg(widget->objectName(), bg.name(), color.name());
|
||||
|
||||
widget->setStyleSheet(style);
|
||||
}
|
||||
|
||||
void set_widget_color(QWidget* widget, QString color, QString bg)
|
||||
{
|
||||
QString style = QString("#%1 { background-color: %2; color: %3;}").arg(widget->objectName(), bg, color);
|
||||
|
||||
widget->setStyleSheet(style);
|
||||
}
|
||||
|
||||
QColor widget_bg_color(QWidget* widget)
|
||||
{
|
||||
std::string input = widget->styleSheet().toStdString();
|
||||
QColor background_color;
|
||||
|
||||
// Find the position of "background-color:"
|
||||
size_t bg_pos = input.find("background-color:");
|
||||
if (bg_pos != std::string::npos) {
|
||||
// Find the end position of the background color value
|
||||
size_t bg_value_start = bg_pos + 17; // Length of "background-color:" is 17
|
||||
size_t bg_value_end = input.find(";", bg_value_start);
|
||||
if (bg_value_end != std::string::npos) {
|
||||
background_color = QColor(input.substr(bg_value_start, bg_value_end - bg_value_start).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return background_color;
|
||||
}
|
||||
|
||||
QColor widget_color(QWidget* widget)
|
||||
{
|
||||
std::string input = widget->styleSheet().toStdString();
|
||||
QColor color;
|
||||
|
||||
// Find the position of "color:"
|
||||
size_t color_pos = input.find(" color:");
|
||||
if (color_pos != std::string::npos) {
|
||||
// Find the end position of the text color value
|
||||
size_t color_value_start = color_pos + 7; // Length of "color:" is 7
|
||||
size_t color_value_end = input.find(";", color_value_start);
|
||||
if (color_value_end != std::string::npos) {
|
||||
color = QColor(input.substr(color_value_start, color_value_end - color_value_start).c_str());
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
void reset_widget_color(QWidget* widget)
|
||||
{
|
||||
widget->setStyleSheet("");
|
||||
}
|
||||
|
||||
QString network_reply_to_fix(QNetworkReply::NetworkError error)
|
||||
{
|
||||
switch (error) {
|
||||
case QNetworkReply::NetworkError::ConnectionRefusedError:
|
||||
return "Make sure the server is started ($ systemctl status unocan_server)";
|
||||
break;
|
||||
case QNetworkReply::NetworkError::HostNotFoundError:
|
||||
return "Make sure the simulator is turned on";
|
||||
break;
|
||||
case QNetworkReply::NetworkError::InternalServerError:
|
||||
return "Server had an oopsie, restart the client and if it fails again restart the server";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef HELPERS_H
|
||||
#define HELPERS_H
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <qwt_color_map.h>
|
||||
#include <qwt_dial.h>
|
||||
#include <qwt_dial_needle.h>
|
||||
#include <qwt_plot.h>
|
||||
#include <qwt_plot_curve.h>
|
||||
#include <qwt_slider.h>
|
||||
#include <qwt_series_data.h>
|
||||
#include <qwt_thermo.h>
|
||||
#include <QNetworkReply>
|
||||
|
||||
void create_dial_needle(QwtDial* dial);
|
||||
|
||||
void set_widget_color(QWidget* widget, QColor color, QColor bg);
|
||||
void set_widget_color(QWidget* widget, QString color, QString bg);
|
||||
QColor widget_bg_color(QWidget* widget);
|
||||
QColor widget_color(QWidget* widget);
|
||||
void reset_widget_color(QWidget* widget);
|
||||
QString network_reply_to_fix(QNetworkReply::NetworkError error);
|
||||
|
||||
#endif // HELPERS_H
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "hex_spinbox.h"
|
||||
|
||||
HexSpinBox::HexSpinBox(QWidget *parent,
|
||||
bool showPrefix,
|
||||
const QString &format) :
|
||||
QSpinBox(parent),
|
||||
format(format)
|
||||
{
|
||||
// Validates hex strings up to 8 chars with or w/out leading "0x" prefix.
|
||||
// For arbitrary prefix/suffix, the regex could be built dynamically
|
||||
// in validate(), or override setPrefix()/setSuffix() methods.
|
||||
const QRegularExpression rx("(?:0[xX])?[0-9A-Fa-f]{1,8}");
|
||||
validator = new QRegularExpressionValidator(rx, this);
|
||||
setShowPrefix(showPrefix);
|
||||
}
|
||||
|
||||
void HexSpinBox::setShowPrefix(bool show)
|
||||
{
|
||||
if (show)
|
||||
setPrefix(QStringLiteral("0x"));
|
||||
else
|
||||
setPrefix(QString());
|
||||
}
|
||||
|
||||
void HexSpinBox::setFormat(const QString &text)
|
||||
{
|
||||
format = text;
|
||||
this->lineEdit()->setText(textFromValue(value()));
|
||||
}
|
||||
|
||||
QValidator::State HexSpinBox::validate(QString &text, int &pos) const
|
||||
{
|
||||
return validator->validate(text, pos);
|
||||
}
|
||||
|
||||
int HexSpinBox::valueFromText(const QString &text) const
|
||||
{
|
||||
return text.toInt(0, 16);
|
||||
}
|
||||
|
||||
QString HexSpinBox::textFromValue(int value) const
|
||||
{
|
||||
return QString().asprintf(qPrintable(format), value);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _HEXSPINBOX_H_
|
||||
#define _HEXSPINBOX_H_
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QLineEdit>
|
||||
#include <QRegularExpressionValidator>
|
||||
|
||||
// NOTE: Since QSpinBox uses int as the storage type, the effective editing range
|
||||
// is +/- 0x7FFF FFFF, so it can't handle a full unsigned int.
|
||||
// QDoubleSpinBox would be a more suitable base class if a wider range is needed.
|
||||
class HexSpinBox : public QSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HexSpinBox(QWidget *parent = nullptr,
|
||||
bool showPrefix = false,
|
||||
const QString &format = QStringLiteral("%X"));
|
||||
|
||||
public slots:
|
||||
void setShowPrefix(bool show);
|
||||
void setFormat(const QString &text);
|
||||
|
||||
protected:
|
||||
QValidator::State validate(QString &text, int &pos) const override;
|
||||
int valueFromText(const QString &text) const override;
|
||||
QString textFromValue(int value) const override;
|
||||
|
||||
private:
|
||||
QRegularExpressionValidator *validator;
|
||||
QString format;
|
||||
};
|
||||
|
||||
#endif // _HEXSPINBOX_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "led.h"
|
||||
|
||||
Led::Led(QWidget* parrent)
|
||||
: QPushButton(parrent)
|
||||
{
|
||||
set_read_only(this, true);
|
||||
}
|
||||
|
||||
Led::Led(const QString &text, QWidget *parent)
|
||||
{
|
||||
set_read_only(this, true);
|
||||
}
|
||||
|
||||
Led::Led(const QIcon& icon, const QString &text, QWidget *parent)
|
||||
{
|
||||
set_read_only(this, true);
|
||||
}
|
||||
|
||||
Led::~Led()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool Led::is_read_only(QWidget* widget)
|
||||
{
|
||||
return widget->testAttribute(Qt::WA_TransparentForMouseEvents) || (widget->focusPolicy() == Qt::NoFocus);
|
||||
}
|
||||
|
||||
void Led::set_read_only(QWidget* widget, bool read_only)
|
||||
{
|
||||
widget->setAttribute(Qt::WA_TransparentForMouseEvents, read_only);
|
||||
widget->setFocusPolicy(read_only ? Qt::NoFocus : Qt::StrongFocus);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
#include "QPushButton"
|
||||
|
||||
class Led : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Led(QWidget *parent = nullptr);
|
||||
explicit Led(const QString &text, QWidget *parent = nullptr);
|
||||
Led(const QIcon& icon, const QString &text, QWidget *parent = nullptr);
|
||||
~Led();
|
||||
private:
|
||||
bool is_read_only(QWidget* widget);
|
||||
void set_read_only(QWidget* widget, bool read_only);
|
||||
};
|
||||
|
||||
#endif // LED_H
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("sim-us client");
|
||||
parser.addHelpOption();
|
||||
|
||||
QCommandLineOption hostOption(QStringList() << "s"
|
||||
<< "server",
|
||||
QCoreApplication::translate("main", "sim-us server host [default: localhost]."),
|
||||
QCoreApplication::translate("main", "host"), QLatin1String("localhost"));
|
||||
parser.addOption(hostOption);
|
||||
|
||||
QCommandLineOption portOption(QStringList() << "p"
|
||||
<< "port",
|
||||
QCoreApplication::translate("main", "Port for echoserver [default: 9002]."),
|
||||
QCoreApplication::translate("main", "port"), QLatin1String("9002"));
|
||||
parser.addOption(portOption);
|
||||
|
||||
parser.process(a);
|
||||
|
||||
QString host = parser.value(hostOption);
|
||||
uint16_t port = parser.value(portOption).toUShort();
|
||||
MainWindow w(host, port);
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include "sim-scope_mux_tester.h"
|
||||
#include "sim-can_debugger.h"
|
||||
#include "sim-cvs_i10.h"
|
||||
#include "sim-test.h"
|
||||
|
||||
MainWindow::MainWindow(QString host, uint16_t port, QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
m_web_socket(new Web_socket_wrapper(QUrl("ws://" + host + ":" + QString::number(port)), this)),
|
||||
m_error_dialog(new NetworkError(this)),
|
||||
m_sims{{"Can_Debugger", new Can_Debugger(m_web_socket, this)},
|
||||
{"Scope_Mux_Tester", new Scope_Mux_Tester(m_web_socket, this)},
|
||||
{"CVS_I10", new CVS_I10(m_web_socket, this)},
|
||||
{"Test_Sim", new Test_Sim(m_web_socket, this)}}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle("Simulator Selector");
|
||||
|
||||
m_error_dialog->set_error("Connection timed out\nCheck if the server is running");
|
||||
ui->connection->setText("Connected to ws://" + host + ":" + QString::number(port));
|
||||
//Default sim
|
||||
//defaultSim("CVS_I10");
|
||||
|
||||
connect(ui->pushButton, &QPushButton::clicked, this, [=, this]{open_sim(ui->comboBox->currentText());});
|
||||
connect(this, &MainWindow::quit, this, &QCoreApplication::quit, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
delete m_web_socket;
|
||||
delete m_error_dialog;
|
||||
}
|
||||
|
||||
void MainWindow::setup_cb(void)
|
||||
{
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_command_cb, this, [=, this](json& j){on_cmd_cb(j);});
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_connected, this, [=, this]{m_web_socket->send_command(Web_socket_wrapper::Command::get_simulators);});
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_closed, this, [=, this]{m_error_dialog->open(); ui->connection->setText("Could not connect to the server");});
|
||||
}
|
||||
|
||||
void MainWindow::showEvent( QShowEvent* event )
|
||||
{
|
||||
QWidget::showEvent( event );
|
||||
setup_cb();
|
||||
}
|
||||
|
||||
void MainWindow::hideEvent(QHideEvent* event)
|
||||
{
|
||||
QWidget::hideEvent(event);
|
||||
disconnect(m_web_socket, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void MainWindow::defaultSim(QString name)
|
||||
{
|
||||
this->m_selected_sim_name = name;
|
||||
this->m_default_sim = true;
|
||||
}
|
||||
|
||||
void MainWindow::pase_sim_names(json& sims)
|
||||
{
|
||||
for (json& item : sims)
|
||||
{
|
||||
ui->comboBox->addItem(QString::fromStdString(item));
|
||||
}
|
||||
|
||||
QThread::msleep(30);
|
||||
|
||||
if (this->m_default_sim)
|
||||
{
|
||||
ui->comboBox->setCurrentText(this->m_selected_sim_name);
|
||||
open_sim(this->m_selected_sim_name);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::open_sim(QString sim_name)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_selected_sim = m_sims.at(sim_name);
|
||||
this->m_selected_sim_name = sim_name;
|
||||
}
|
||||
catch (const std::out_of_range& ex)
|
||||
{
|
||||
QD << "out_of_range::what(): " << ex.what();
|
||||
return;
|
||||
}
|
||||
|
||||
m_web_socket->send_command(Web_socket_wrapper::Command::get_active_simulator_name);
|
||||
}
|
||||
|
||||
void MainWindow::open_sim_window(void)
|
||||
{
|
||||
this->hide();
|
||||
m_selected_sim->show();
|
||||
}
|
||||
|
||||
void MainWindow::check_active_sim(QString name)
|
||||
{
|
||||
if (name == "")
|
||||
{
|
||||
QD << "Name not set";
|
||||
m_web_socket->send_command(Web_socket_wrapper::Command::switch_simulator, m_selected_sim_name);
|
||||
return;
|
||||
}
|
||||
|
||||
QD << "Current sim:" << name;
|
||||
|
||||
if (name == m_selected_sim_name)
|
||||
{
|
||||
QD << "Name set and the same";
|
||||
open_sim_window();
|
||||
return;
|
||||
}
|
||||
|
||||
QD << "Name set and wrong";
|
||||
m_web_socket->send_command(Web_socket_wrapper::Command::switch_simulator, m_selected_sim_name);
|
||||
}
|
||||
|
||||
void MainWindow::on_cmd_cb(json& j)
|
||||
{
|
||||
auto response = magic_enum::enum_cast<Web_socket_wrapper::Command>(std::string(j.at("type"))).value_or(Web_socket_wrapper::Command::end);
|
||||
switch (response)
|
||||
{
|
||||
case Web_socket_wrapper::Command::get_simulators:
|
||||
pase_sim_names(j.at("simulators"));
|
||||
break;
|
||||
case Web_socket_wrapper::Command::get_active_simulator_name:
|
||||
check_active_sim(QString::fromStdString(j.at("name")));
|
||||
break;
|
||||
case Web_socket_wrapper::Command::switch_simulator:
|
||||
open_sim_window();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <map>
|
||||
#include <QMainWindow>
|
||||
#include "web_socket_wrapper.h"
|
||||
#include "networkerror.h"
|
||||
#include "simulator_base.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QString host, uint16_t port, QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void defaultSim(QString name);
|
||||
void pase_sim_names(json& sims);
|
||||
void open_sim_window(void);
|
||||
void check_active_sim(QString name);
|
||||
void open_sim(QString sim_name);
|
||||
void setup_cb(void);
|
||||
|
||||
signals:
|
||||
void quit(void);
|
||||
private:
|
||||
void showEvent(QShowEvent* event);
|
||||
void hideEvent(QHideEvent* event);
|
||||
|
||||
void on_cmd_cb(json& j);
|
||||
|
||||
bool m_default_sim = false;
|
||||
QString m_selected_sim_name;
|
||||
QMainWindow* m_selected_sim;
|
||||
Ui::MainWindow* ui;
|
||||
Web_socket_wrapper *m_web_socket;
|
||||
NetworkError* m_error_dialog;
|
||||
|
||||
std::map<QString, SimulatorBase*> m_sims;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="connection">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actiontest">
|
||||
<property name="text">
|
||||
<string>test</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "networkerror.h"
|
||||
#include "ui_networkerror.h"
|
||||
|
||||
NetworkError::NetworkError(QWidget* parent) : QDialog(parent), ui(new Ui::NetworkError) {
|
||||
ui->setupUi(this);
|
||||
connect(ui->exit, &QPushButton::clicked, this, &QCoreApplication::quit, Qt::QueuedConnection);
|
||||
connect(ui->close, &QPushButton::clicked, this, [=, this]{this->close();});
|
||||
|
||||
this->setWindowTitle("Error");
|
||||
}
|
||||
|
||||
void NetworkError::set_error(QString error)
|
||||
{
|
||||
ui->Error->setText(error);
|
||||
}
|
||||
|
||||
NetworkError::~NetworkError() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef NETWORKERROR_H
|
||||
#define NETWORKERROR_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class NetworkError;
|
||||
}
|
||||
|
||||
class NetworkError : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NetworkError(QWidget* parent = nullptr);
|
||||
void set_error(QString error);
|
||||
~NetworkError();
|
||||
|
||||
private:
|
||||
Ui::NetworkError* ui;
|
||||
};
|
||||
|
||||
#endif // NETWORKERROR_H
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NetworkError</class>
|
||||
<widget class="QDialog" name="NetworkError">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Oops, an error occured</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="Error">
|
||||
<property name="text">
|
||||
<string>Error...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exit">
|
||||
<property name="text">
|
||||
<string>Exit Application</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,531 @@
|
||||
#include "simulator_base.h"
|
||||
#include "can_transceive.h"
|
||||
|
||||
SimulatorBase::SimulatorBase(Web_socket_wrapper* web_socket, QWidget* parent) :
|
||||
QMainWindow{parent},
|
||||
m_error_dialog(new NetworkError(this)),
|
||||
m_timer_update(new QTimer()),
|
||||
m_web_socket(web_socket)
|
||||
{
|
||||
m_error_dialog->set_error("Connection timed out\nCheck if the server is running");
|
||||
m_ui_lookup.reserve(40);
|
||||
}
|
||||
|
||||
QWidget* SimulatorBase::id_to_ui(size_t id)
|
||||
{
|
||||
return m_ui_lookup.at(id);
|
||||
}
|
||||
|
||||
void SimulatorBase::showEvent( QShowEvent* event )
|
||||
{
|
||||
QWidget::showEvent( event );
|
||||
QD << "Connecting callbacks";
|
||||
setup_cb();
|
||||
m_web_socket->send_command(Web_socket_wrapper::Command::get_UI_elements);
|
||||
this->m_timer_update->start(m_refresh_rate);
|
||||
m_open = true;
|
||||
}
|
||||
|
||||
void SimulatorBase::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
if (m_open == true)
|
||||
{
|
||||
QD << "Disconnecting callbacks";
|
||||
QWidget::closeEvent(event);
|
||||
m_timer_update->stop();
|
||||
disconnect(m_web_socket, nullptr, nullptr, nullptr);
|
||||
m_open = false;
|
||||
this->parentWidget()->show();
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_cb(void)
|
||||
{
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_command_cb, this, [=, this](json& j){on_cmd_cb(j);});
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_event_cb, this, [=, this](json& j){on_event_cb(j);});
|
||||
connect(m_web_socket, &Web_socket_wrapper::on_closed, this, [=, this]{m_error_dialog->open();});
|
||||
}
|
||||
|
||||
void SimulatorBase::on_cmd_cb(json& j)
|
||||
{
|
||||
auto response = magic_enum::enum_cast<Web_socket_wrapper::Command>(std::string(j.at("type"))).value_or(Web_socket_wrapper::Command::end);
|
||||
switch (response)
|
||||
{
|
||||
case Web_socket_wrapper::Command::get_UI_elements:
|
||||
case Web_socket_wrapper::Command::get_UI_element:
|
||||
UI_item_parser(j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::on_event_cb(json& j)
|
||||
{
|
||||
auto response = magic_enum::enum_cast<Web_socket_wrapper::Event>(std::string(j.at("type"))).value_or(Web_socket_wrapper::Event::end);
|
||||
switch (response)
|
||||
{
|
||||
case Web_socket_wrapper::Event::ui_changed:
|
||||
UI_item_parser(j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::push_ui_item(QWidget* item)
|
||||
{
|
||||
m_ui_lookup.emplace_back(item);
|
||||
setup_ui_item(item, m_ui_lookup.size() - 1);
|
||||
}
|
||||
|
||||
void SimulatorBase::UI_item_parser(json& input)
|
||||
{
|
||||
for (auto& uiItem : input["UI_items"])
|
||||
{
|
||||
if (uiItem["type"] == "UI_button")
|
||||
{
|
||||
process_ui_button(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_led")
|
||||
{
|
||||
process_ui_led(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_slider")
|
||||
{
|
||||
process_ui_slider(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_dial")
|
||||
{
|
||||
process_ui_dial(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_thermo")
|
||||
{
|
||||
process_ui_thermo(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_textbox")
|
||||
{
|
||||
process_ui_textbox(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_label")
|
||||
{
|
||||
process_ui_label(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_combobox")
|
||||
{
|
||||
process_ui_combobox(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UIRadioButton")
|
||||
{
|
||||
process_ui_radiobutton(uiItem);
|
||||
continue;
|
||||
}
|
||||
if (uiItem["type"] == "UI_can")
|
||||
{
|
||||
process_ui_can(uiItem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_label(json& uiItem)
|
||||
{
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto label = qobject_cast<QLabel*>(widget);
|
||||
if (label == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QLabel";
|
||||
return;
|
||||
}
|
||||
|
||||
QString value = QString::fromStdString(uiItem["text"]);
|
||||
|
||||
if (label->text() != value)
|
||||
{
|
||||
label->setText(value);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_slider(json& uiItem)
|
||||
{
|
||||
QLabel* label = id_to_label(uiItem["id"]);
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto slider = qobject_cast<QwtSlider*>(widget);
|
||||
if (slider == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QwtSlider";
|
||||
return;
|
||||
}
|
||||
|
||||
double value = uiItem["value"];
|
||||
bool enable = uiItem["enabled"];
|
||||
bool visible = uiItem["visible"];
|
||||
QString unit = QString::fromStdString(uiItem.at("unit"));
|
||||
|
||||
if (slider->value() != value)
|
||||
{
|
||||
if (label != nullptr)
|
||||
{
|
||||
label->setText(QString("%0 %1").arg(value).arg(unit));
|
||||
}
|
||||
slider->setValue(value);
|
||||
}
|
||||
|
||||
if (slider->isReadOnly() != !enable)
|
||||
{
|
||||
slider->setReadOnly(!enable);
|
||||
}
|
||||
|
||||
if (slider->isVisible() != visible)
|
||||
{
|
||||
slider->setVisible(visible);
|
||||
}
|
||||
//TODO: Add color for scale
|
||||
return;
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_dial(json& uiItem)
|
||||
{
|
||||
QLabel* label = id_to_label(uiItem["id"]);
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dial = qobject_cast<QwtDial*>(widget);
|
||||
if (dial == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QwtDial";
|
||||
return;
|
||||
}
|
||||
|
||||
double value = uiItem["value"];
|
||||
bool visible = uiItem["visible"];
|
||||
QString unit = QString::fromStdString(uiItem.at("unit"));
|
||||
|
||||
if (dial->value() != value)
|
||||
{
|
||||
if (label != nullptr)
|
||||
{
|
||||
label->setText(QString("%0 %1").arg(value).arg(unit));
|
||||
}
|
||||
dial->setValue(value);
|
||||
}
|
||||
|
||||
|
||||
if (dial->isVisible() != visible)
|
||||
{
|
||||
dial->setVisible(visible);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_thermo(json& uiItem)
|
||||
{
|
||||
QLabel* label = id_to_label(uiItem["id"]);
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto thermo = qobject_cast<QwtThermo*>(widget);
|
||||
if (thermo == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QwtThermo";
|
||||
return;
|
||||
}
|
||||
|
||||
double value = uiItem["value"];
|
||||
bool visible = uiItem["visible"];
|
||||
QString unit = QString::fromStdString(uiItem.at("unit"));
|
||||
|
||||
if (thermo->value() != value)
|
||||
{
|
||||
if (label != nullptr)
|
||||
{
|
||||
label->setText(QString("%0 %1").arg(value).arg(unit));
|
||||
}
|
||||
thermo->setValue(value);
|
||||
}
|
||||
|
||||
|
||||
if (thermo->isVisible() != visible)
|
||||
{
|
||||
thermo->setVisible(visible);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_textbox(json& uiItem)
|
||||
{
|
||||
QD << "Not implemented";
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_combobox(json& uiItem)
|
||||
{
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto combobox = qobject_cast<QComboBox*>(widget);
|
||||
if (combobox == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QComboBox";
|
||||
return;
|
||||
}
|
||||
|
||||
QString selected = QString::fromStdString(uiItem["selected"]);
|
||||
|
||||
for (json& item : uiItem["options"])
|
||||
{
|
||||
if (combobox->findText(QString::fromStdString(item)) == -1)
|
||||
combobox->addItem(QString::fromStdString(item));
|
||||
}
|
||||
|
||||
if (combobox->currentText() != selected)
|
||||
{
|
||||
combobox->setCurrentText(selected);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_radiobutton(json& uiItem)
|
||||
{
|
||||
QD << "Not implemented";
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_button(json& uiItem)
|
||||
{
|
||||
//TODO: fix bgcolor and color
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto button = qobject_cast<QPushButton*>(widget);
|
||||
if (button == nullptr)
|
||||
{
|
||||
QD << "widget is not of type QPushButton";
|
||||
return;
|
||||
}
|
||||
|
||||
QColor bg_color = QColor(QString::fromStdString(uiItem["bg_color"]));
|
||||
QColor fg_color = QColor(QString::fromStdString(uiItem["fg_color"]));
|
||||
QString text = QString::fromStdString(uiItem["text"]);
|
||||
bool enabled = uiItem["enabled"];
|
||||
|
||||
if (button->text() != text)
|
||||
{
|
||||
button->setText(text);
|
||||
}
|
||||
|
||||
if (button->isEnabled() != enabled)
|
||||
{
|
||||
button->setEnabled(enabled);
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0x83, 0x91, 0x92))
|
||||
{
|
||||
reset_widget_color(button);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0xFF, 0x00, 0x00))
|
||||
{
|
||||
set_widget_color(button, QString("black"), QString("darkred"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0x00, 0xFF, 0x00))
|
||||
{
|
||||
set_widget_color(button, QString("white"), QString("darkgreen"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0xFF8800))
|
||||
{
|
||||
set_widget_color(button, QString("darkred"), QString("darkorange"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color != widget_bg_color(button) || fg_color != widget_color(button))
|
||||
{
|
||||
set_widget_color(button, fg_color, bg_color);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_led(json& uiItem)
|
||||
{
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto led = qobject_cast<Led*>(widget);
|
||||
if (led == nullptr)
|
||||
{
|
||||
QD << "widget is not of type led";
|
||||
return;
|
||||
}
|
||||
|
||||
QColor bg_color = QColor(QString::fromStdString(uiItem["led_color"]));
|
||||
QColor color = QColor(~bg_color.red(), ~bg_color.green(), ~bg_color.blue());
|
||||
QString text = QString::fromStdString(uiItem["text"]);
|
||||
|
||||
if (text != led->text())
|
||||
{
|
||||
led->setText(text);
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0x83, 0x91, 0x92))
|
||||
{
|
||||
reset_widget_color(led);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0xFF, 0x00, 0x00))
|
||||
{
|
||||
set_widget_color(led, QString("black"), QString("darkred"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0x00, 0xFF, 0x00))
|
||||
{
|
||||
set_widget_color(led, QString("white"), QString("darkgreen"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color == QColor(0xFF8800))
|
||||
{
|
||||
set_widget_color(led, QString("darkred"), QString("darkorange"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_color != widget_bg_color(led))
|
||||
{
|
||||
set_widget_color(led, color, bg_color);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorBase::process_ui_can(json& uiItem)
|
||||
{
|
||||
QWidget* widget = id_to_ui(uiItem["id"]);
|
||||
if (widget == nullptr)
|
||||
{
|
||||
QD << "id_to_ui returned null on " << QString::number((size_t)uiItem["id"]);
|
||||
return;
|
||||
}
|
||||
|
||||
auto can_ui = qobject_cast<Can_Transceive*>(widget);
|
||||
if (can_ui == nullptr)
|
||||
{
|
||||
QD << "widget is not of type led";
|
||||
return;
|
||||
}
|
||||
|
||||
can_ui->clear();
|
||||
for (auto& send_item : uiItem["send_msgs"])
|
||||
{
|
||||
can_ui->add_send_item(send_item["id"], send_item["dlc"], send_item["payload"]);
|
||||
}
|
||||
|
||||
for (auto& recvd_item : uiItem["rcvd_msgs"])
|
||||
{
|
||||
can_ui->add_receive_item(recvd_item["id"], recvd_item["dlc"], recvd_item["payload"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_ui_item(QWidget* item, size_t index)
|
||||
{
|
||||
setup_button(item, index);
|
||||
setup_combobox(item, index);
|
||||
setup_dial(item, index);
|
||||
setup_slider(item, index);
|
||||
setup_can_ui(item, index);
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_button(QWidget* item, size_t index)
|
||||
{
|
||||
QPushButton* button = qobject_cast<QPushButton*>(item);
|
||||
if (button == nullptr)
|
||||
return;
|
||||
connect(button, &QPushButton::clicked, this, [=, this]{m_web_socket->send_event(Web_socket_wrapper::Event::clicked, index);});
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_combobox(QWidget* item, size_t index)
|
||||
{
|
||||
QComboBox* combobox = qobject_cast<QComboBox*>(item);
|
||||
if (combobox == nullptr)
|
||||
return;
|
||||
|
||||
connect(combobox, &QComboBox::currentIndexChanged, this, [=, this]{
|
||||
m_web_socket->send_event(Web_socket_wrapper::Event::selected, index,
|
||||
combobox->currentText());});
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_dial(QWidget* item, size_t index)
|
||||
{
|
||||
QwtDial* dial = qobject_cast<QwtDial*>(item);
|
||||
if (dial == nullptr)
|
||||
return;
|
||||
|
||||
create_dial_needle(dial);
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_slider(QWidget* item, size_t index)
|
||||
{
|
||||
QwtSlider* slider = qobject_cast<QwtSlider*>(item);
|
||||
if (slider == nullptr)
|
||||
return;
|
||||
|
||||
connect(slider, &QwtSlider::sliderMoved, this, [=, this]{
|
||||
m_web_socket->send_event(Web_socket_wrapper::Event::value_changed, index,
|
||||
slider->value());});
|
||||
}
|
||||
|
||||
void SimulatorBase::setup_can_ui(QWidget* item, size_t index)
|
||||
{
|
||||
Can_Transceive* can = qobject_cast<Can_Transceive*>(item);
|
||||
if (can == nullptr)
|
||||
return;
|
||||
|
||||
connect(can, &Can_Transceive::send_can_message, this, [=, this](uint32_t id, uint8_t dlc, std::array<uint8_t, 8> payload){
|
||||
m_web_socket->send_event(Web_socket_wrapper::Event::can_send, index, id, dlc, payload);
|
||||
});
|
||||
connect(can, &Can_Transceive::can_clear, this, [=, this]{
|
||||
m_web_socket->send_event(Web_socket_wrapper::Event::can_clear, index);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef SIMULATOR_BASE_H
|
||||
#define SIMULATOR_BASE_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QWidget>
|
||||
#include "web_socket_wrapper.h"
|
||||
#include "networkerror.h"
|
||||
#include "helpers.h"
|
||||
#include "led.h"
|
||||
#include <magic_enum.hpp>
|
||||
#include "IterableEnum.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
#define QD qDebug() << "Debug:" << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":"
|
||||
|
||||
class SimulatorBase : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SimulatorBase(Web_socket_wrapper* api, QWidget* parent = nullptr);
|
||||
void setup_cb(void);
|
||||
void sim_update(void);
|
||||
QWidget* id_to_ui(size_t id);
|
||||
virtual QLabel* id_to_label(size_t name) {return nullptr;}
|
||||
|
||||
signals:
|
||||
void quit();
|
||||
protected:
|
||||
void on_event_cb(json& j);
|
||||
void on_cmd_cb(json& j);
|
||||
|
||||
void push_ui_item(QWidget* item);
|
||||
|
||||
void UI_item_parser(json& input);
|
||||
virtual void process_ui_label(json& uiItem);
|
||||
virtual void process_ui_slider(json& uiItem);
|
||||
virtual void process_ui_dial(json& uiItem);
|
||||
virtual void process_ui_thermo(json& uiItem);
|
||||
virtual void process_ui_textbox(json& uiItem);
|
||||
virtual void process_ui_combobox(json& uiItem);
|
||||
virtual void process_ui_radiobutton(json& uiItem);
|
||||
virtual void process_ui_button(json& uiItem);
|
||||
virtual void process_ui_led(json& uiItem);
|
||||
virtual void process_ui_can(json& uiItem);
|
||||
|
||||
void setup_ui_item(QWidget* item, size_t index);
|
||||
void setup_button(QWidget* item, size_t index);
|
||||
void setup_combobox(QWidget* item, size_t index);
|
||||
void setup_dial(QWidget* item, size_t index);
|
||||
void setup_slider(QWidget* item, size_t index);
|
||||
void setup_can_ui(QWidget* item, size_t index);
|
||||
|
||||
void showEvent(QShowEvent* event);
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
||||
private:
|
||||
Web_socket_wrapper* m_web_socket;
|
||||
|
||||
QTimer* m_timer_update;
|
||||
uint32_t m_refresh_rate;
|
||||
NetworkError* m_error_dialog;
|
||||
bool m_open = false;
|
||||
std::vector<QWidget*>m_ui_lookup;
|
||||
};
|
||||
|
||||
#endif // SIMULATOR_BASE_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "sim-can_debugger.h"
|
||||
#include "ui_sim-can_debugger.h"
|
||||
|
||||
Can_Debugger::Can_Debugger(Web_socket_wrapper* web_socket, QWidget *parent) :
|
||||
SimulatorBase(web_socket, parent),
|
||||
ui(new Ui::Can_Debugger) {
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle("CAN Debugger");
|
||||
|
||||
this->push_ui_item(ui->can_transceive);
|
||||
}
|
||||
|
||||
Can_Debugger::~Can_Debugger() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef SIM_CAN_DEBUGGER_H
|
||||
#define SIM_CAN_DEBUGGER_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "simulator_base.h"
|
||||
|
||||
namespace Ui {
|
||||
class Can_Debugger;
|
||||
}
|
||||
|
||||
class Can_Debugger : public SimulatorBase {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Can_Debugger(Web_socket_wrapper* web_socket, QWidget *parent = nullptr);
|
||||
~Can_Debugger();
|
||||
|
||||
private:
|
||||
Ui::Can_Debugger* ui;
|
||||
};
|
||||
|
||||
#endif // SIM_CAN_DEBUGGER_H
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Can_Debugger</class>
|
||||
<widget class="QMainWindow" name="Can_Debugger">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1430</width>
|
||||
<height>1127</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="0" column="0">
|
||||
<widget class="Can_Transceive" name="can_transceive" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1430</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Can_Transceive</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>can_transceive.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "sim-test.h"
|
||||
#include "ui_sim-test.h"
|
||||
|
||||
Test_Sim::Test_Sim(Web_socket_wrapper* web_socket, QWidget* parent) :
|
||||
SimulatorBase(web_socket, parent),
|
||||
ui(new Ui::Test_Sim)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle("Test Sim");
|
||||
this->setWindowState(Qt::WindowMaximized);
|
||||
|
||||
push_ui_item(ui->button);
|
||||
push_ui_item(ui->combobox);
|
||||
push_ui_item(ui->label);
|
||||
push_ui_item(ui->slider);
|
||||
push_ui_item(ui->dial);
|
||||
push_ui_item(ui->thermo);
|
||||
push_ui_item(ui->led);
|
||||
}
|
||||
|
||||
Test_Sim::~Test_Sim() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QLabel* Test_Sim::id_to_label(size_t id)
|
||||
{
|
||||
QWidget* widget = id_to_ui(id);
|
||||
if (widget == ui->slider)
|
||||
return ui->slider_label;
|
||||
if (widget == ui->thermo)
|
||||
return ui->thermo_label;
|
||||
if (widget == ui->dial)
|
||||
return ui->dial_label;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef SIM_TEST_H
|
||||
#define SIM_TEST_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "simulator_base.h"
|
||||
#include "web_socket_wrapper.h"
|
||||
|
||||
namespace Ui {
|
||||
class Test_Sim;
|
||||
}
|
||||
|
||||
class Test_Sim : public SimulatorBase {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Test_Sim(Web_socket_wrapper* web_socket, QWidget* parent = nullptr);
|
||||
~Test_Sim();
|
||||
|
||||
QLabel* id_to_label(size_t name) override;
|
||||
|
||||
private:
|
||||
Ui::Test_Sim* ui;
|
||||
};
|
||||
|
||||
#endif // SIM_TEST_H
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Test_Sim</class>
|
||||
<widget class="QMainWindow" name="Test_Sim">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="button">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="Led" name="led">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="slider_label">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QwtSlider" name="slider"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="dial_label">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QwtDial" name="dial">
|
||||
<property name="lineWidth">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="minScaleArc">
|
||||
<double>20.000000000000000</double>
|
||||
</property>
|
||||
<property name="maxScaleArc">
|
||||
<double>340.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="thermo_label">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QwtThermo" name="thermo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QComboBox" name="combobox"/>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QwtDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qwt_dial.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QwtSlider</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qwt_slider.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QwtThermo</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qwt_thermo.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Led</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>led.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "web_socket_wrapper.h"
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
Web_socket_wrapper::Web_socket_wrapper(const QUrl &url, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_web_socket(new QWebSocket("", QWebSocketProtocol::VersionLatest, this)),
|
||||
m_ping_timer(new QTimer(this)),
|
||||
m_pong_timer(new QTimer(this))
|
||||
{
|
||||
connect(m_web_socket, &QWebSocket::connected, this, &Web_socket_wrapper::m_on_connected);
|
||||
connect(m_web_socket, &QWebSocket::disconnected, this, &Web_socket_wrapper::on_closed);
|
||||
connect(m_web_socket, &QWebSocket::pong, this, &Web_socket_wrapper::m_on_pong);
|
||||
connect(m_ping_timer, &QTimer::timeout, this, [=, this]{
|
||||
m_web_socket->ping();
|
||||
m_pong_timer->start(500);
|
||||
});
|
||||
connect(m_pong_timer, &QTimer::timeout, this, &Web_socket_wrapper::m_on_pong_timeout);
|
||||
m_ping_timer->setSingleShot(true);
|
||||
m_pong_timer->setSingleShot(true);
|
||||
m_web_socket->open(url);
|
||||
}
|
||||
|
||||
Web_socket_wrapper::~Web_socket_wrapper()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_command(Command command, size_t id)
|
||||
{
|
||||
json j;
|
||||
switch (command)
|
||||
{
|
||||
case Command::get_UI_element:
|
||||
j["command"]["type"] = magic_enum::enum_name(command).data();
|
||||
j["command"]["id"] = id;
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
case Command::get_UI_elements:
|
||||
j["command"]["type"] = magic_enum::enum_name(command).data();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
case Command::get_active_simulator_name:
|
||||
j["command"]["type"] = magic_enum::enum_name(command).data();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
case Command::get_simulators:
|
||||
j["command"]["type"] = magic_enum::enum_name(command).data();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_command(Command command, QString id)
|
||||
{
|
||||
json j;
|
||||
switch (command)
|
||||
{
|
||||
case Command::switch_simulator:
|
||||
j["command"]["type"] = magic_enum::enum_name(command).data();
|
||||
j["command"]["name"] = id.toStdString();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_event(Event event, size_t id)
|
||||
{
|
||||
json j;
|
||||
switch (event)
|
||||
{
|
||||
case Event::clicked:
|
||||
case Event::can_clear:
|
||||
j["event"]["type"] = magic_enum::enum_name(event).data();
|
||||
j["event"]["id"] = id;
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_event(Event event, size_t id, double val)
|
||||
{
|
||||
json j;
|
||||
switch (event)
|
||||
{
|
||||
case Event::value_changed:
|
||||
j["event"]["type"] = magic_enum::enum_name(event).data();
|
||||
j["event"]["id"] = id;
|
||||
j["event"]["value"] = val;
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_event(Event event, size_t id, QString val)
|
||||
{
|
||||
json j;
|
||||
switch (event)
|
||||
{
|
||||
case Event::text_changed:
|
||||
j["event"]["type"] = magic_enum::enum_name(event).data();
|
||||
j["event"]["id"] = id;
|
||||
j["event"]["text"] = val.toStdString();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
case Event::selected:
|
||||
j["event"]["type"] = magic_enum::enum_name(event).data();
|
||||
j["event"]["id"] = id;
|
||||
j["event"]["selected"] = val.toStdString();
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::send_event(Event event, size_t id, uint32_t sid, uint8_t dlc, std::array<uint8_t, 8> payload)
|
||||
{
|
||||
json j;
|
||||
switch (event)
|
||||
{
|
||||
case Event::can_send:
|
||||
j["event"]["type"] = magic_enum::enum_name(event).data();
|
||||
j["event"]["id"] = id;
|
||||
j["event"]["sid"] = sid;
|
||||
j["event"]["dlc"] = dlc;
|
||||
j["event"]["payload"] = payload;
|
||||
m_web_socket->sendTextMessage(QString(j.dump().data()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::close()
|
||||
{
|
||||
if (m_connected)
|
||||
m_web_socket->close();
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::m_on_connected()
|
||||
{
|
||||
m_connected = true;
|
||||
connect(m_web_socket, &QWebSocket::textMessageReceived,
|
||||
this, &Web_socket_wrapper::m_on_received);
|
||||
|
||||
m_ping_timer->start(1000);
|
||||
|
||||
emit on_connected();
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::m_on_received(QString message)
|
||||
{
|
||||
json j = json::parse(message.toStdString());
|
||||
if (j.contains("response"))
|
||||
{
|
||||
emit on_command_cb(j.at("response"));
|
||||
return;
|
||||
}
|
||||
if (j.contains("event"))
|
||||
{
|
||||
emit on_event_cb(j.at("event"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::m_on_pong(quint64 elapsedTime)
|
||||
{
|
||||
if (elapsedTime > 500)
|
||||
{
|
||||
m_web_socket->close();
|
||||
emit on_closed();
|
||||
}
|
||||
|
||||
m_pong_timer->stop();
|
||||
m_ping_timer->start(1000);
|
||||
}
|
||||
|
||||
void Web_socket_wrapper::m_on_pong_timeout()
|
||||
{
|
||||
qDebug() << "Pong timeout";
|
||||
m_web_socket->close();
|
||||
emit on_closed();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef WEB_SOCKET_WRAPPER_H
|
||||
#define WEB_SOCKET_WRAPPER_H
|
||||
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
#include <QTimer>
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
class Web_socket_wrapper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Command
|
||||
{
|
||||
get_UI_element,
|
||||
get_UI_elements,
|
||||
switch_simulator,
|
||||
get_active_simulator_name,
|
||||
get_simulators,
|
||||
end,
|
||||
begin = 0
|
||||
};
|
||||
|
||||
enum class Event
|
||||
{
|
||||
clicked,
|
||||
value_changed,
|
||||
text_changed,
|
||||
selected,
|
||||
can_send,
|
||||
can_clear,
|
||||
ui_changed,
|
||||
end,
|
||||
begin = 0
|
||||
};
|
||||
public:
|
||||
explicit Web_socket_wrapper(const QUrl &url, QObject *parent = nullptr);
|
||||
~Web_socket_wrapper();
|
||||
void send_command(Command command, size_t id = 0);
|
||||
void send_command(Command command, QString id);
|
||||
void send_event(Event event, size_t id);
|
||||
void send_event(Event event, size_t id, double val);
|
||||
void send_event(Event event, size_t id, QString val);
|
||||
void send_event(Event event, size_t id, uint32_t sid, uint8_t dlc, std::array<uint8_t, 8> payload);
|
||||
void close();
|
||||
|
||||
Q_SIGNALS:
|
||||
void on_connected();
|
||||
void on_closed();
|
||||
void on_command_cb(json& data);
|
||||
void on_event_cb(json& data);
|
||||
|
||||
private Q_SLOTS:
|
||||
void m_on_connected();
|
||||
void m_on_received(QString message);
|
||||
void m_on_pong(quint64 elapsedTime);
|
||||
void m_on_pong_timeout();
|
||||
|
||||
private:
|
||||
bool m_connected = false;
|
||||
QWebSocket* m_web_socket;
|
||||
QTimer* m_ping_timer;
|
||||
QTimer* m_pong_timer;
|
||||
};
|
||||
|
||||
#endif // WEB_SOCKET_WRAPPER_H
|
||||
Reference in New Issue
Block a user