diff --git a/RoutesGenereren/RoutesGenereren.pro b/RoutesGenereren/RoutesGenereren.pro
new file mode 100644
index 0000000..203165a
--- /dev/null
+++ b/RoutesGenereren/RoutesGenereren.pro
@@ -0,0 +1,35 @@
+QT += core gui serialport
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++14
+QT += concurrent
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ console.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ routegenerator.cpp \
+ settingsdialog.cpp
+
+HEADERS += \
+ console.h \
+ mainwindow.h \
+ routegenerator.h \
+ settingsdialog.h
+
+FORMS += \
+ mainwindow.ui \
+ settingsdialog.ui
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+RESOURCES += \
+ terminal.qrc
diff --git a/RoutesGenereren/RoutesGenereren.pro.user b/RoutesGenereren/RoutesGenereren.pro.user
new file mode 100644
index 0000000..edb4a0f
--- /dev/null
+++ b/RoutesGenereren/RoutesGenereren.pro.user
@@ -0,0 +1,433 @@
+
+
+
+
+
+ EnvironmentId
+ {2de608a0-7d4e-4466-820b-332fa0018787}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ 0
+ false
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ false
+ 1
+ true
+ true
+ true
+ *.md, *.MD, Makefile
+ false
+ true
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+
+
+ 0
+ true
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 6
+ true
+
+
+
+ true
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop Qt 5.15.2 MinGW 32-bit
+ Desktop Qt 5.15.2 MinGW 32-bit
+ qt.qt5.5152.win32_mingw81_kit
+ 0
+ 0
+ 0
+
+ 0
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+
+
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Release
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Release
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+
+
+ 0
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp - Copy\build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Profile
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Profile
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+ 0
+
+ 3
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+ true
+ true
+ 0
+ true
+
+ 2
+
+ false
+ RoutesGenereren2
+ Qt4ProjectManager.Qt4RunConfiguration:C:/Users/NPC/Documents/School/_ProjBlok11/QtApp2/RoutesGenereren/RoutesGenereren.pro
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp2/RoutesGenereren/RoutesGenereren.pro
+ true
+ true
+ true
+ true
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/build-RoutesGenereren-Desktop_Qt_5_15_2_MinGW_32_bit-Debug
+
+ 1
+
+
+
+ ProjectExplorer.Project.Target.1
+
+ Desktop
+ Qt 5.15.2 (mingw81_32)
+ Qt 5.15.2 (mingw81_32)
+ {db23b840-4fa8-4369-81d4-5c33831dd635}
+ 0
+ 0
+ 0
+
+ 0
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Debug
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Debug
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+
+
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Release
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Release
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+
+
+ 0
+ C:\Users\NPC\Documents\School\_ProjBlok11\QtApp\build-RoutesGenereren-Qt_5_15_2_mingw81_32-Profile
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp/build-RoutesGenereren-Qt_5_15_2_mingw81_32-Profile
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+ 0
+
+ 3
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+ true
+ true
+ 0
+ true
+
+ 2
+
+ false
+ RoutesGenereren2
+ Qt4ProjectManager.Qt4RunConfiguration:C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/RoutesGenereren/RoutesGenereren.pro
+ C:/Users/NPC/Documents/School/_ProjBlok11/QtApp - Copy/RoutesGenereren/RoutesGenereren.pro
+ true
+ true
+ true
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 2
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/RoutesGenereren/RoutesGenereren.pro.user.6737c63 b/RoutesGenereren/RoutesGenereren.pro.user.6737c63
new file mode 100644
index 0000000..98a8268
--- /dev/null
+++ b/RoutesGenereren/RoutesGenereren.pro.user.6737c63
@@ -0,0 +1,264 @@
+
+
+
+
+
+ EnvironmentId
+ {6737c63b-8d94-4d04-854a-df0b2ddb36db}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ false
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ false
+ 1
+ true
+ true
+ true
+ *.md, *.MD, Makefile
+ false
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+
+
+ 0
+ true
+
+ -fno-delayed-template-parsing
+
+ true
+ Builtin.BuildSystem
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 4
+
+
+
+ true
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop Qt 6.1.3 MSVC2019 64bit
+ Desktop Qt 6.1.3 MSVC2019 64bit
+ qt.qt6.613.win64_msvc2019_64_kit
+ 0
+ 0
+ 0
+
+ 0
+ C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug
+ C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+
+
+ C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Release
+ C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Release
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+
+
+ 0
+ C:\Users\lars\OneDrive\Documenten\Avans Elektrotechniek\TechnischeInformatica2025\ProjectBlok11-12\build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Profile
+ C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Profile
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+ 0
+
+ 3
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+ true
+ true
+ true
+
+ 2
+
+ Qt4ProjectManager.Qt4RunConfiguration:C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/RoutesGenereren/RoutesGenereren.pro
+ C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/RoutesGenereren/RoutesGenereren.pro
+ false
+ true
+ true
+ false
+ true
+ C:/Users/lars/OneDrive/Documenten/Avans Elektrotechniek/TechnischeInformatica2025/ProjectBlok11-12/build-RoutesGenereren-Desktop_Qt_6_1_3_MSVC2019_64bit-Debug
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/RoutesGenereren/console.cpp b/RoutesGenereren/console.cpp
new file mode 100644
index 0000000..b4c1ee1
--- /dev/null
+++ b/RoutesGenereren/console.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "console.h"
+
+#include
+
+Console::Console(QWidget *parent) :
+ QPlainTextEdit(parent)
+{
+ document()->setMaximumBlockCount(100);
+ QPalette p = palette();
+ p.setColor(QPalette::Base, Qt::black);
+ p.setColor(QPalette::Text, Qt::green);
+ setPalette(p);
+}
+
+void Console::putData(const QByteArray &data)
+{
+ insertPlainText(data);
+
+ QScrollBar *bar = verticalScrollBar();
+ bar->setValue(bar->maximum());
+}
+
+void Console::setLocalEchoEnabled(bool set)
+{
+ m_localEchoEnabled = set;
+}
+
+void Console::keyPressEvent(QKeyEvent *e)
+{
+ switch (e->key()) {
+ case Qt::Key_Backspace:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ break;
+ default:
+ if (m_localEchoEnabled)
+ QPlainTextEdit::keyPressEvent(e);
+ emit getData(e->text().toLocal8Bit());
+ }
+}
+
+void Console::mousePressEvent(QMouseEvent *e)
+{
+ Q_UNUSED(e);
+ setFocus();
+}
+
+void Console::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_UNUSED(e);
+}
+
+void Console::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_UNUSED(e);
+}
diff --git a/RoutesGenereren/console.h b/RoutesGenereren/console.h
new file mode 100644
index 0000000..d5a39ef
--- /dev/null
+++ b/RoutesGenereren/console.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include
+
+class Console : public QPlainTextEdit
+{
+ Q_OBJECT
+
+signals:
+ void getData(const QByteArray &data);
+
+public:
+ explicit Console(QWidget *parent = nullptr);
+
+ void putData(const QByteArray &data);
+ void setLocalEchoEnabled(bool set);
+
+protected:
+ void keyPressEvent(QKeyEvent *e) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseDoubleClickEvent(QMouseEvent *e) override;
+ void contextMenuEvent(QContextMenuEvent *e) override;
+
+private:
+ bool m_localEchoEnabled = false;
+};
+
+#endif // CONSOLE_H
diff --git a/RoutesGenereren/images/application-exit.png b/RoutesGenereren/images/application-exit.png
new file mode 100644
index 0000000..32be6b3
Binary files /dev/null and b/RoutesGenereren/images/application-exit.png differ
diff --git a/RoutesGenereren/images/clear.png b/RoutesGenereren/images/clear.png
new file mode 100644
index 0000000..aa612f1
Binary files /dev/null and b/RoutesGenereren/images/clear.png differ
diff --git a/RoutesGenereren/images/connect.png b/RoutesGenereren/images/connect.png
new file mode 100644
index 0000000..dd5a51e
Binary files /dev/null and b/RoutesGenereren/images/connect.png differ
diff --git a/RoutesGenereren/images/disconnect.png b/RoutesGenereren/images/disconnect.png
new file mode 100644
index 0000000..fd58f7a
Binary files /dev/null and b/RoutesGenereren/images/disconnect.png differ
diff --git a/RoutesGenereren/images/settings.png b/RoutesGenereren/images/settings.png
new file mode 100644
index 0000000..3d1042e
Binary files /dev/null and b/RoutesGenereren/images/settings.png differ
diff --git a/RoutesGenereren/locations.json b/RoutesGenereren/locations.json
new file mode 100644
index 0000000..efc0cd5
--- /dev/null
+++ b/RoutesGenereren/locations.json
@@ -0,0 +1,200 @@
+[
+ {
+ "cost": 2,
+ "mg_id": 0,
+ "x": 51688573,
+ "y": 5287210
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 101,
+ "x": 51690224,
+ "y": 5296625
+ },
+ {
+ "cost": 2,
+ "mg_id": 112,
+ "x": 51688460,
+ "y": 5303150
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 1,
+ "x": 51686200,
+ "y": 5304500
+ },
+ {
+ "cost": 2,
+ "mg_id": 9,
+ "x": 51685051,
+ "y": 5289156
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 111,
+ "x": 51684258,
+ "y": 5302611
+ },
+ {
+ "cost": 2,
+ "mg_id": 5,
+ "x": 51691299,
+ "y": 5303950
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 100,
+ "x": 51689428,
+ "y": 5310484
+ },
+ {
+ "cost": 2,
+ "mg_id": 113,
+ "x": 51695984,
+ "y": 5299074
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 108,
+ "x": 51689124,
+ "y": 5303969
+ },
+ {
+ "cost": 2,
+ "mg_id": 114,
+ "x": 51689619,
+ "y": 5299065
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 115,
+ "x": 51693002,
+ "y": 5301264
+ },
+ {
+ "cost": 2,
+ "mg_id": 102,
+ "x": 51697021,
+ "y": 5299328
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 116,
+ "x": 51689724,
+ "y": 5300408
+ },
+ {
+ "cost": 2,
+ "mg_id": 110,
+ "x": 51686471,
+ "y": 5304106
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 109,
+ "x": 51689471,
+ "y": 5303200
+ },
+ {
+ "cost": 2,
+ "mg_id": 117,
+ "x": 51689302,
+ "y": 5303396
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 118,
+ "x": 51695457,
+ "y": 5297448
+ },
+ {
+ "cost": 2,
+ "mg_id": 119,
+ "x": 51694463,
+ "y": 5302862
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 8,
+ "x": 51690467,
+ "y": 5294925
+ },
+ {
+ "cost": 2,
+ "mg_id": 11,
+ "x": 51683776,
+ "y": 5317938
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 120,
+ "x": 51687561,
+ "y": 5305911
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 121,
+ "x": 51698630,
+ "y": 5292803
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 7,
+ "x": 51688691,
+ "y": 5309001
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 122,
+ "x": 51687344,
+ "y": 5305871
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 10,
+ "x": 51696264,
+ "y": 5307460
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 123,
+ "x": 51696501,
+ "y": 5312884
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 2,
+ "x": 51691911,
+ "y": 5286594
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 124,
+ "x": 51690402,
+ "y": 5291740
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 3,
+ "x": 51685400,
+ "y": 5289354
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 106,
+ "x": 51690484,
+ "y": 5296206
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 125,
+ "x": 51686618,
+ "y": 5308459
+ },
+ {
+ "cost": 1.5,
+ "mg_id": 6,
+ "x": 51686697,
+ "y": 5303137
+ }
+]
diff --git a/RoutesGenereren/main.cpp b/RoutesGenereren/main.cpp
new file mode 100644
index 0000000..fd3e533
--- /dev/null
+++ b/RoutesGenereren/main.cpp
@@ -0,0 +1,11 @@
+#include "mainwindow.h"
+
+#include
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/RoutesGenereren/mainwindow.cpp b/RoutesGenereren/mainwindow.cpp
new file mode 100644
index 0000000..eec08d1
--- /dev/null
+++ b/RoutesGenereren/mainwindow.cpp
@@ -0,0 +1,688 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include "console.h"
+#include "routegenerator.h"
+#include "settingsdialog.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+static constexpr std::chrono::seconds kWriteTimeout = std::chrono::seconds{5};
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ m_ui(new Ui::MainWindow),
+ m_status(new QLabel),
+ m_console(new Console),
+ m_settings(new SettingsDialog(this)),
+ m_serial(new QSerialPort(this))
+{
+ m_ui->setupUi(this);
+
+ m_ui->tabWidget->setCurrentIndex(0);
+
+ loadLocationsFromFile();
+ // if there was nothing in the file, fall back to defaults
+ if (allPoints.empty())
+ loadDefaultPoints();
+ populateLocationsTable();
+
+ refreshRouteList();
+ refreshSerialPorts();
+
+ scene = new QGraphicsScene(this);
+ m_ui->graphicsView->setScene(scene);
+ m_ui->graphicsView->setRenderHint(QPainter::Antialiasing, true);
+
+ // SerialPort + console instellingen
+ //m_console->setEnabled(false);
+ //QWidget* tab1 = m_console;
+ //m_ui->tabWidget->addTab(tab1, "Developer mode");
+
+ m_ui->actionConnect->setEnabled(true);
+ m_ui->actionDisconnect->setEnabled(false);
+ m_ui->actionQuit->setEnabled(true);
+ m_ui->actionConfigure->setEnabled(true);
+
+ m_ui->statusBar->addWidget(m_status);
+
+ initActionsConnections();
+
+ connect(m_serial, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
+ connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::readData);
+ connect(m_console, &Console::getData, this, &MainWindow::writeData);
+
+ //QObject::connect(m_ui->pushButton_2, SIGNAL(pressed()), this, SLOT(pushButton_2_clicked()));
+}
+
+MainWindow::~MainWindow()
+{
+ delete m_settings;
+ delete m_ui;
+}
+
+void MainWindow::drawRoutes()
+{
+ scene->clear();
+
+ int64_t minX = std::numeric_limits::max(), maxX = std::numeric_limits::lowest();
+ int64_t minY = std::numeric_limits::max(), maxY = std::numeric_limits::lowest();
+ for (auto& pts : selectedPoints)
+ for (auto& p : pts) {
+ minX = std::min(minX, p.x);
+ maxX = std::max(maxX, p.x);
+ minY = std::min(minY, p.y);
+ maxY = std::max(maxY, p.y);
+ }
+
+ QSizeF viewSz = m_ui->graphicsView->viewport()->size();
+ constexpr qreal M = 20.0; // marge in pixels
+ qreal scaleX = (viewSz.width() - 2*M) / qreal(maxX - minX);
+ qreal scaleY = (viewSz.height() - 2*M) / qreal(maxY - minY);
+ qreal scale = std::min(scaleX, scaleY);
+
+ auto mapPt = [&](const Point& p) {
+ qreal x = (p.x - minX)*scale + M;
+ qreal y = (p.y - minY)*scale + M;
+ return QPointF(x,y);
+ };
+
+ const int radius = 4;
+ const QColor colors[] = {
+ Qt::red, Qt::blue, Qt::green, Qt::darkYellow,
+ Qt::magenta, Qt::cyan, Qt::darkGreen, Qt::darkRed,
+ Qt::gray, Qt::black
+ };
+
+ for (int r = 0; r < routeIndices.size(); ++r) {
+ QPen pen(colors[r%10]);
+ pen.setCosmetic(true);
+ pen.setWidth(0);
+ const auto& route = routeIndices[r];
+ const auto& pts = selectedPoints[r];
+
+ // lijnen
+ for (int i = 0; i + 1 < route.size(); ++i) {
+ QPointF p1 = mapPt(pts[ route[i] ]);
+ QPointF p2 = mapPt(pts[ route[i+1] ]);
+ scene->addLine(p1.x(), p1.y(), p2.x(), p2.y(), pen);
+ }
+ // punten
+ for (auto& p : pts) {
+ QPointF c = mapPt(p);
+ scene->addEllipse(c.x()-radius, c.y()-radius,
+ radius*2, radius*2,
+ QPen(Qt::black),
+ QBrush(colors[r%10]));
+ }
+ }
+
+ // 4) Scene instellen en fitten
+ scene->setSceneRect(0, 0, viewSz.width(), viewSz.height());
+ m_ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
+}
+
+void MainWindow::resizeEvent(QResizeEvent* event)
+{
+ QMainWindow::resizeEvent(event);
+ if (scene && !scene->items().isEmpty()) {
+ m_ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
+ }
+}
+
+void MainWindow::on_pushButton_clicked()
+{
+ m_ui->statusLabel->setText("Bezig met genereren...");
+ QApplication::processEvents();
+
+ QFuture future = QtConcurrent::run([this]() {
+ generate_routes(allPoints);
+ QMetaObject::invokeMethod(this, [this]() {
+ m_ui->statusLabel->setText("Routes succesvol gegenereerd!");
+ drawRoutes();
+ }, Qt::QueuedConnection);
+ });
+}
+
+void MainWindow::pushButton_2_clicked()
+{
+
+}
+
+void MainWindow::openSerialPort()
+{
+ const SettingsDialog::Settings p = m_settings->settings();
+ m_serial->setPortName(p.name);
+ m_serial->setBaudRate(p.baudRate);
+ m_serial->setDataBits(p.dataBits);
+ m_serial->setParity(p.parity);
+ m_serial->setStopBits(p.stopBits);
+ m_serial->setFlowControl(p.flowControl);
+ if (m_serial->open(QIODevice::ReadWrite)) {
+ m_console->setEnabled(true);
+ m_console->setLocalEchoEnabled(p.localEchoEnabled);
+ m_ui->actionConnect->setEnabled(false);
+ m_ui->actionDisconnect->setEnabled(true);
+ m_ui->actionConfigure->setEnabled(false);
+ showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6")
+ .arg(p.name, p.stringBaudRate, p.stringDataBits,
+ p.stringParity, p.stringStopBits, p.stringFlowControl));
+ } else {
+ QMessageBox::critical(this, tr("Error"), m_serial->errorString());
+ showStatusMessage(tr("Open error"));
+ }
+}
+
+void MainWindow::closeSerialPort()
+{
+ if (m_serial->isOpen())
+ m_serial->close();
+ m_console->setEnabled(false);
+ m_ui->actionConnect->setEnabled(true);
+ m_ui->actionDisconnect->setEnabled(false);
+ m_ui->actionConfigure->setEnabled(true);
+ showStatusMessage(tr("Disconnected"));
+}
+
+void MainWindow::about()
+{
+ QMessageBox::about(this, tr("About Serial Terminal"),
+ tr("The Serial Terminal example demonstrates how to "
+ "use the Qt Serial Port module in modern GUI applications "
+ "using Qt, with a menu bar, toolbars, and a status bar."));
+}
+
+void MainWindow::writeData(const QByteArray &data)
+{
+ m_serial->write(data);
+}
+
+void MainWindow::readData()
+{
+ const QByteArray data = m_serial->readAll();
+ m_console->putData(data);
+}
+
+void MainWindow::handleError(QSerialPort::SerialPortError error)
+{
+ if (error == QSerialPort::ResourceError) {
+ QMessageBox::critical(this, tr("Critical Error"), m_serial->errorString());
+ closeSerialPort();
+ }
+}
+
+void MainWindow::handleBytesWritten(qint64 bytes)
+{
+ m_bytesToWrite -= bytes;
+ if (m_bytesToWrite == 0)
+ m_timer->stop();
+}
+
+void MainWindow::handleWriteTimeout()
+{
+ const QString error = tr("Write operation timed out for port %1.\n"
+ "Error: %2").arg(m_serial->portName(),
+ m_serial->errorString());
+ showWriteError(error);
+}
+
+void MainWindow::initActionsConnections()
+{
+ connect(m_ui->actionConnect, &QAction::triggered, this, &MainWindow::openSerialPort);
+ connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::closeSerialPort);
+ connect(m_ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
+ connect(m_ui->actionConfigure, &QAction::triggered, m_settings, &SettingsDialog::show);
+ connect(m_ui->actionClear, &QAction::triggered, m_console, &Console::clear);
+ connect(m_ui->actionAbout, &QAction::triggered, this, &MainWindow::about);
+ connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
+}
+
+void MainWindow::showStatusMessage(const QString &message)
+{
+ m_status->setText(message);
+}
+
+void MainWindow::showWriteError(const QString &message)
+{
+ QMessageBox::warning(this, tr("Warning"), message);
+}
+
+void MainWindow::refreshRouteList()
+{
+ QDir buildDir(QCoreApplication::applicationDirPath());
+ buildDir.setNameFilters(QStringList{"*.json"});
+ buildDir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
+
+ auto files = buildDir.entryList();
+ m_ui->comboRouteFiles->clear();
+ if (files.isEmpty()) {
+ m_ui->comboRouteFiles->addItem(tr("[geen routes]"));
+ } else {
+ m_ui->comboRouteFiles->addItems(files);
+ }
+}
+
+void MainWindow::refreshSerialPorts()
+{
+ auto ports = QSerialPortInfo::availablePorts();
+ m_ui->comboPorts->clear();
+ for (const auto &info : ports)
+ m_ui->comboPorts->addItem(info.portName());
+ if (ports.isEmpty())
+ m_ui->comboPorts->addItem(tr("[geen poorten]"));
+}
+
+void MainWindow::on_btnRefreshRoutes_clicked()
+{
+ refreshRouteList();
+}
+
+void MainWindow::on_btnRefreshPorts_clicked()
+{
+ refreshSerialPorts();
+}
+
+void MainWindow::on_btnUploadSerial_clicked()
+{
+ // Read json file
+ QString fileName = m_ui->comboRouteFiles->currentText();
+ if (fileName.startsWith("[")) {
+ QMessageBox::warning(this, tr("Geen route"),
+ tr("Genereer eerst routes op tab 1."));
+ return;
+ }
+ QDir buildDir(QCoreApplication::applicationDirPath());
+ QString src = buildDir.filePath(fileName);
+ QFile f(src);
+ if (!f.open(QIODevice::ReadOnly)) {
+ QMessageBox::critical(this, tr("Fout"),
+ tr("Kan %1 niet openen").arg(src));
+ return;
+ }
+ const QByteArray jsonData = f.readAll();
+ f.close();
+
+ // Open serial port
+ QString portName = m_ui->comboPorts->currentText();
+ if (portName.startsWith("[")) {
+ QMessageBox::warning(this, tr("Geen poort"),
+ tr("Sluit koffer aan en ververs poorten."));
+ return;
+ }
+ QSerialPort port(portName, this);
+ port.setBaudRate(QSerialPort::Baud115200);
+ port.setDataBits(QSerialPort::Data8);
+ port.setParity(QSerialPort::NoParity);
+ port.setStopBits(QSerialPort::OneStop);
+ port.setFlowControl(QSerialPort::NoFlowControl);
+ if (!port.open(QIODevice::ReadWrite)) {
+ QMessageBox::critical(this, tr("Fout"),
+ tr("Kan poort %1 niet openen:\n%2")
+ .arg(portName, port.errorString()));
+ return;
+ }
+
+
+ // Delete loc.txt, progress.txt and score.txt
+ QByteArray resetCmd = "fs rm /SD:/loc.txt\r\n";
+ port.write(resetCmd);
+ port.waitForBytesWritten(1000);
+
+ QThread::msleep(300);
+
+ resetCmd = "fs rm /SD:/progress.txt\r\n";
+ port.write(resetCmd);
+ port.waitForBytesWritten(1000);
+
+ QThread::msleep(300);
+
+ resetCmd = "fs rm /SD:/score.txt\r\n";
+ port.write(resetCmd);
+ port.waitForBytesWritten(1000);
+
+ QThread::msleep(300);
+
+ // upload to /SD:/loc.txt
+ const qint64 CHUNK_SIZE = 16;
+ const qint64 totalLen = jsonData.size();
+ qint64 offset = 0;
+ const QByteArray remotePath("/SD:/loc.txt");
+
+ while (offset < totalLen) {
+ qint64 chunkLen = qMin(CHUNK_SIZE, totalLen - offset);
+ QByteArray chunk = jsonData.mid(offset, chunkLen);
+
+ QString offsetHex = QString("%1")
+ .arg(offset, 2, 16, QChar('0'));
+
+ // build hex-strings
+ QStringList hexBytes;
+ hexBytes.reserve(chunkLen);
+ for (uchar b : chunk) {
+ hexBytes << QString("%1")
+ .arg(b, 2, 16, QChar('0'));
+ }
+ QByteArray spacedHex = hexBytes.join(' ').toUtf8();
+
+ // build fs command
+ QByteArray cmd = "fs write "
+ + remotePath + " "
+ + spacedHex
+ + "\r\n";
+
+ qDebug() << ">>" << cmd.trimmed();
+
+ qint64 written = port.write(cmd);
+ if (written != cmd.size() ||
+ !port.waitForBytesWritten(5000)) {
+ QMessageBox::critical(this, tr("Fout"),
+ tr("Chunk offset %1 niet verzonden").arg(offsetHex));
+ port.close();
+ return;
+ }
+
+ QThread::msleep(300);
+ offset += chunkLen;
+ }
+
+ // Reset progress.txt (ASCII ‘0’ = 0x30)
+ {
+ QByteArray resetCmd = "fs write /SD:/progress.txt 30\r\n";
+ port.write(resetCmd);
+ port.waitForBytesWritten(500);
+ }
+
+ // Reset score.txt
+ {
+ QByteArray resetCmd = "fs write /SD:/score.txt 30\r\n";
+ port.write(resetCmd);
+ port.waitForBytesWritten(500);
+ }
+
+ // (Optional feedback message)
+ if (port.waitForReadyRead(500)) {
+ QByteArray fb;
+ do { fb += port.readAll(); }
+ while (port.waitForReadyRead(100));
+ qDebug() << "Feedback:" << fb.trimmed();
+ QMessageBox::information(this, tr("Feedback"),
+ QString::fromUtf8(fb));
+ }
+
+ port.close();
+ QMessageBox::information(this, tr("Klaar"),
+ tr("JSON (%1 bytes) in %2 chunks geüpload naar %3")
+ .arg(totalLen)
+ .arg((totalLen + CHUNK_SIZE - 1)/CHUNK_SIZE)
+ .arg(portName));
+}
+
+void MainWindow::on_btnAddLoc_clicked()
+{
+ bool ok = false;
+ // 1) Vraag X en Y (int64_t)
+ QString xs = QInputDialog::getText(this, tr("Nieuw punt"), tr("X (microdegrees):"), QLineEdit::Normal,
+ QString(), &ok);
+ if (!ok) return;
+ int64_t x = xs.toLongLong(&ok);
+ if (!ok) {
+ QMessageBox::warning(this, tr("Ongeldige invoer"), tr("Geef een geldig getal op."));
+ return;
+ }
+
+ QString ys = QInputDialog::getText(this, tr("Nieuw punt"), tr("Y (microdegrees):"), QLineEdit::Normal,
+ QString(), &ok);
+ if (!ok) return;
+ int64_t y = ys.toLongLong(&ok);
+ if (!ok) {
+ QMessageBox::warning(this, tr("Ongeldige invoer"), tr("Geef een geldig getal op."));
+ return;
+ }
+
+ // cost (float)
+ double cost = QInputDialog::getDouble(this, tr("Nieuw punt"), tr("Cost:"),
+ 1.0, 0.0, 1e6, 2, &ok);
+ if (!ok) return;
+
+ // mg_id (int)
+ int mg = QInputDialog::getInt(this, tr("Nieuw punt"), tr("mg_id:"),
+ 0, 0, std::numeric_limits::max(), 1, &ok);
+ if (!ok) return;
+
+ allPoints.emplace_back(x, y, static_cast(cost), mg);
+
+ populateLocationsTable();
+ saveLocationsToFile();
+}
+
+void MainWindow::on_btnEditLoc_clicked()
+{
+ auto *tbl = m_ui->tableLocations;
+ int row = tbl->currentRow();
+ if (row < 0 || row >= int(allPoints.size())) {
+ QMessageBox::warning(this, tr("Bewerken"), tr("Selecteer eerst een rij."));
+ return;
+ }
+
+ Point &p = allPoints[row];
+ bool ok = false;
+ int64_t newX = 0, newY = 0;
+ double newCost = 0.0;
+ int newMg = 0;
+
+ do {
+ QString xs = QInputDialog::getText(
+ this,
+ tr("Bewerk punt"),
+ tr("X (microdegrees):"),
+ QLineEdit::Normal,
+ QString::number(p.x),
+ &ok
+ );
+ if (!ok) return;
+ newX = xs.toLongLong(&ok);
+ if (!ok) {
+ QMessageBox::warning(this,
+ tr("Ongeldige invoer"),
+ tr("Voer een geldig geheel getal in voor X."));
+ }
+ } while (!ok);
+
+ do {
+ QString ys = QInputDialog::getText(
+ this,
+ tr("Bewerk punt"),
+ tr("Y (microdegrees):"),
+ QLineEdit::Normal,
+ QString::number(p.y),
+ &ok
+ );
+ if (!ok) return;
+ newY = ys.toLongLong(&ok);
+ if (!ok) {
+ QMessageBox::warning(this,
+ tr("Ongeldige invoer"),
+ tr("Voer een geldig geheel getal in voor Y."));
+ }
+ } while (!ok);
+
+ do {
+ double c = QInputDialog::getDouble(
+ this,
+ tr("Bewerk punt"),
+ tr("Cost:"),
+ double(p.cost),
+ 0.0, 1e6, 2,
+ &ok
+ );
+ if (!ok) return;
+ newCost = c;
+ } while (!ok);
+
+ do {
+ int m = QInputDialog::getInt(
+ this,
+ tr("Bewerk punt"),
+ tr("mg_id:"),
+ p.mg_id,
+ 0, std::numeric_limits::max(),
+ 1,
+ &ok
+ );
+ if (!ok) return;
+ newMg = m;
+ } while (!ok);
+
+ p.x = newX;
+ p.y = newY;
+ p.cost = static_cast(newCost);
+ p.mg_id = newMg;
+
+ populateLocationsTable();
+ saveLocationsToFile();
+}
+
+
+void MainWindow::on_btnRemoveLoc_clicked()
+{
+ auto *tbl = m_ui->tableLocations;
+ int row = tbl->currentRow();
+ if (row < 0 || row >= int(allPoints.size())) {
+ QMessageBox::warning(this, tr("Verwijderen"), tr("Selecteer eerst een rij."));
+ return;
+ }
+
+ if (QMessageBox::question(this, tr("Verwijderen"),
+ tr("Weet je zeker dat je punt %1 wilt verwijderen?")
+ .arg(row+1))
+ != QMessageBox::Yes)
+ {
+ return;
+ }
+
+ allPoints.erase(allPoints.begin() + row);
+ populateLocationsTable();
+ saveLocationsToFile();
+}
+
+void MainWindow::loadDefaultPoints()
+{
+ allPoints = {
+ {51688573, 5287210, 2.0f, 0}, // Avans
+ {51690224, 5296625, 1.5f, 101}, // Jan de Groot
+ {51688460, 5303150, 2.0f, 112}, // Stadhuis
+ {51686200, 5304500, 1.5f, 1}, // Sint Jans Kathedraal
+ {51685051, 5289156, 2.0f, 9}, // Paleisbrug
+ {51684258, 5302611, 1.5f, 111}, // Zuidwal
+ {51691299, 5303950, 2.0f, 5}, // Arena
+ {51689428, 5310484, 1.5f, 100}, // Nationaal Carnavalsmuseum
+ {51695984, 5299074, 2.0f, 113}, // Tramkade
+ {51689124, 5303969, 1.5f, 108}, // De Markt
+ {51689619, 5299065, 2.0f, 114}, // Bolwerk
+ {51693002, 5301264, 1.5f, 115}, // VUE Cinema
+ {51697021, 5299328, 2.0f, 102}, // Bossche Brouwers
+ {51689724, 5300408, 1.5f, 116}, // Café Bar le Duc
+ {51686471, 5304106, 2.0f, 110}, // Museumkwartier
+ {51689471, 5303200, 1.5f, 109}, // Moriaan
+ {51689302, 5303396, 2.0f, 117}, // ’t Opkikkertje
+ {51695457, 5297448, 1.5f, 118}, // Verkadefabriek
+ {51694463, 5302862, 2.0f, 119}, // BHIC
+ {51690467, 5294925, 1.5f, 8}, // Station
+ {51683776, 5317938, 2.0f, 11}, // Zuiderpark
+ {51687561, 5305911, 1.5f, 120}, // Korte Putstraat
+ {51698630, 5292803, 1.5f, 121}, // Brabanthallen
+ {51688691, 5309001, 1.5f, 7}, // Café de Palm
+ {51687344, 5305871, 1.5f, 122}, // Bistro Tante Pietje
+ {51696264, 5307460, 1.5f, 10}, // Taxandriaplein Park
+ {51696501, 5312884, 1.5f, 123}, // IJzeren Vrouw Park
+ {51691911, 5286594, 1.5f, 2}, // Simon Stevinweg
+ {51690402, 5291740, 1.5f, 124}, // Hugo de Grootplein
+ {51685400, 5289354, 1.5f, 3}, // Kinepolis
+ {51690484, 5296206, 1.5f, 106}, // Gouden Draak
+ {51686618, 5308459, 1.5f, 125}, // Theater aan de Parade
+ {51686697, 5303137, 1.5f, 6} // Gemeentehuis
+ };
+}
+
+void MainWindow::populateLocationsTable()
+{
+ auto *tbl = m_ui->tableLocations;
+ tbl->clearContents();
+ tbl->setRowCount(int(allPoints.size()));
+ for (int i = 0; i < int(allPoints.size()); ++i) {
+ const Point &p = allPoints[i];
+ tbl->setItem(i, 0, new QTableWidgetItem(QString::number(p.x)));
+ tbl->setItem(i, 1, new QTableWidgetItem(QString::number(p.y)));
+ tbl->setItem(i, 2, new QTableWidgetItem(QString::number(p.cost)));
+ tbl->setItem(i, 3, new QTableWidgetItem(QString::number(p.mg_id)));
+ }
+}
+
+void MainWindow::loadLocationsFromFile()
+{
+ QString path = QCoreApplication::applicationDirPath() + "/locations.json";
+
+ QFile f(path);
+ if (!f.open(QIODevice::ReadOnly))
+ return;
+ auto raw = f.readAll();
+ f.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(raw);
+ if (!doc.isArray())
+ return;
+
+ allPoints.clear();
+ for (auto v : doc.array()) {
+ if (!v.isObject()) continue;
+ auto o = v.toObject();
+ int64_t x = qint64(o.value("x").toDouble());
+ int64_t y = qint64(o.value("y").toDouble());
+ float cost = float(o.value("cost").toDouble());
+ int mg = o.value("mg_id").toInt();
+ allPoints.emplace_back(x,y,cost,mg);
+ }
+}
+
+void MainWindow::saveLocationsToFile()
+{
+ QString path = QCoreApplication::applicationDirPath() + "/locations.json";
+
+
+ QJsonArray arr;
+ for (auto &p : allPoints) {
+ QJsonObject o;
+ o["x"] = double(p.x);
+ o["y"] = double(p.y);
+ o["cost"] = double(p.cost);
+ o["mg_id"]= p.mg_id;
+ arr.append(o);
+ }
+ QJsonDocument doc(arr);
+
+ QFile f(path);
+ if (!f.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
+ QMessageBox::warning(this, tr("Error"),
+ tr("Kan locaties niet opslaan in %1").arg(path));
+ return;
+ }
+ f.write(doc.toJson());
+ f.close();
+}
+
diff --git a/RoutesGenereren/mainwindow.h b/RoutesGenereren/mainwindow.h
new file mode 100644
index 0000000..de28b76
--- /dev/null
+++ b/RoutesGenereren/mainwindow.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "routegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLabel;
+class QTimer;
+
+namespace Ui {
+class MainWindow;
+}
+
+QT_END_NAMESPACE
+
+class Console;
+class SettingsDialog;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private slots:
+ void on_pushButton_clicked();
+ void pushButton_2_clicked();
+
+ void openSerialPort();
+ void closeSerialPort();
+ void about();
+ void writeData(const QByteArray &data);
+ void readData();
+
+ void handleError(QSerialPort::SerialPortError error);
+ void handleBytesWritten(qint64 bytes);
+ void handleWriteTimeout();
+
+ void on_btnRefreshRoutes_clicked();
+ void on_btnRefreshPorts_clicked();
+ void on_btnUploadSerial_clicked();
+
+ void on_btnAddLoc_clicked();
+ void on_btnEditLoc_clicked();
+ void on_btnRemoveLoc_clicked();
+
+private:
+ void initActionsConnections();
+ void showStatusMessage(const QString &message);
+ void showWriteError(const QString &message);
+ void drawRoutes();
+ void refreshRouteList();
+ void refreshSerialPorts();
+ void loadDefaultPoints();
+ void populateLocationsTable();
+ void loadLocationsFromFile();
+ void saveLocationsToFile();
+
+protected:
+ void resizeEvent(QResizeEvent* event) override;
+
+private:
+ Ui::MainWindow *m_ui = nullptr;
+ QLabel *m_status = nullptr;
+ Console *m_console = nullptr;
+ SettingsDialog *m_settings = nullptr;
+ qint64 m_bytesToWrite = 0;
+ QTimer *m_timer = nullptr;
+ QSerialPort *m_serial = nullptr;
+ QString dataRead;
+ QString selectedNode;
+ QGraphicsScene *scene = nullptr;
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/RoutesGenereren/mainwindow.ui b/RoutesGenereren/mainwindow.ui
new file mode 100644
index 0000000..f79d2c2
--- /dev/null
+++ b/RoutesGenereren/mainwindow.ui
@@ -0,0 +1,346 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 886
+ 539
+
+
+
+ Serial Terminal
+
+
+
+ -
+
+
+ 0
+
+
+
+ Genereer routes
+
+
+
+
+ 10
+ 370
+ 191
+ 29
+
+
+
+ Genereer Routes
+
+
+
+
+
+ 210
+ 370
+ 191
+ 31
+
+
+
+ status
+
+
+
+
+
+ 10
+ 10
+ 821
+ 351
+
+
+
+
+
+
+ Overzetten naar koffer
+
+
+
+
+ 60
+ 170
+ 711
+ 27
+
+
+
+
-
+
+
+ Laad Routes in
+
+
+
+ -
+
+
+ -
+
+
+ Refresh Poort
+
+
+
+ -
+
+
+ -
+
+
+ Upload Route
+
+
+
+
+
+
+
+
+ Beheer locaties
+
+
+
+
+ 60
+ 50
+ 431
+ 301
+
+
+
+ 4
+
+
+ 99
+
+
+
+ X
+
+
+
+
+ Y
+
+
+
+
+ Cost
+
+
+
+
+ mg_id
+
+
+
+
+
+
+ 590
+ 90
+ 80
+ 25
+
+
+
+ Toevoegen
+
+
+
+
+
+ 590
+ 160
+ 80
+ 25
+
+
+
+ Bewerken
+
+
+
+
+
+ 590
+ 240
+ 80
+ 25
+
+
+
+ Verwijderen
+
+
+
+
+
+
+
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+
+
+ toolBar
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+ &About
+
+
+ About program
+
+
+ Alt+A
+
+
+
+
+ About Qt
+
+
+
+
+
+ :/images/connect.png:/images/connect.png
+
+
+ C&onnect
+
+
+ Connect to serial port
+
+
+ Ctrl+O
+
+
+
+
+
+ :/images/disconnect.png:/images/disconnect.png
+
+
+ &Disconnect
+
+
+ Disconnect from serial port
+
+
+ Ctrl+D
+
+
+
+
+
+ :/images/settings.png:/images/settings.png
+
+
+ &Configure
+
+
+ Configure serial port
+
+
+ Alt+C
+
+
+
+
+
+ :/images/clear.png:/images/clear.png
+
+
+ C&lear
+
+
+ Clear data
+
+
+ Alt+L
+
+
+
+
+
+ :/images/application-exit.png:/images/application-exit.png
+
+
+ &Quit
+
+
+ Ctrl+Q
+
+
+
+
+
+
+
+
+
diff --git a/RoutesGenereren/routegenerator.cpp b/RoutesGenereren/routegenerator.cpp
new file mode 100644
index 0000000..fc4e537
--- /dev/null
+++ b/RoutesGenereren/routegenerator.cpp
@@ -0,0 +1,226 @@
+#include "routegenerator.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+const int NUM_HOTPOINTS = 5;
+const int NUM_ROUTES = 10;
+const float MIN_ROUTE_TIME_MINUTES = 1.0f * 60.0f; // 60 min
+const float MAX_ROUTE_TIME_MINUTES = 2.0f * 60.0f; // 120 min
+
+constexpr double METERS_PER_DEGREE = 111320.0; // gemiddeld
+constexpr double WALKING_SPEED_M_PER_S = 1.4; // meter per seconde
+
+
+Point::Point(int64_t x, int64_t y, float cost, int mg_id)
+ : x(x), y(y), cost(cost), mg_id(mg_id)
+{}
+
+bool Point::operator==(const Point& other) const {
+ return x == other.x
+ && y == other.y
+ && cost == other.cost
+ && mg_id == other.mg_id;
+}
+
+vector> routeIndices;
+vector> selectedPoints;
+vector allPoints;
+
+struct Edge {
+ int u, v;
+ float weight;
+};
+
+// Calculate distance in metres between two microdegree points
+double geoDistanceMeters(const Point& a, const Point& b) {
+ double dx_deg = (a.x - b.x) * 1e-6;
+ double dy_deg = (a.y - b.y) * 1e-6;
+ double dx_m = dx_deg * METERS_PER_DEGREE;
+ double dy_m = dy_deg * METERS_PER_DEGREE;
+ return sqrt(dx_m*dx_m + dy_m*dy_m);
+}
+
+// Calculate time in minutes between two points
+double stepTimeMinutes(const Point& a, const Point& b) {
+ double dist_m = geoDistanceMeters(a, b);
+ return (dist_m / WALKING_SPEED_M_PER_S) / 60.0;
+}
+
+int findSet(int x, vector& parent) {
+ return parent[x] == x ? x : parent[x] = findSet(parent[x], parent);
+}
+
+float totalRouteCost(const vector& route, const vector& points) {
+ float total = 0.0f;
+ for (size_t i = 0; i + 1 < route.size(); ++i) {
+ total += static_cast(stepTimeMinutes(points[route[i]], points[route[i+1]]))
+ + points[route[i]].cost;
+ }
+ total += points[route.back()].cost;
+ return total;
+}
+
+vector computeMST(vector& points) {
+ size_t n = points.size();
+ vector edges, mst;
+ vector parent(n);
+ for (size_t i = 0; i < n; ++i) parent[i] = i;
+
+ for (size_t i = 0; i < n; ++i) {
+ for (size_t j = i + 1; j < n; ++j) {
+ double d = geoDistanceMeters(points[i], points[j]);
+ edges.push_back({ static_cast(i), static_cast(j), static_cast(d) });
+ }
+ }
+ sort(edges.begin(), edges.end(), [](auto& a, auto& b){ return a.weight < b.weight; });
+ for (auto& e : edges) {
+ int u = findSet(e.u, parent);
+ int v = findSet(e.v, parent);
+ if (u != v) {
+ mst.push_back(e);
+ parent[u] = v;
+ if (mst.size() == n - 1) break;
+ }
+ }
+ return mst;
+}
+
+void optimizeRoute(vector& route, vector& points) {
+ bool improved = true;
+ while (improved) {
+ improved = false;
+ for (size_t i = 1; i + 2 < route.size(); ++i) {
+ for (size_t j = i + 1; j + 1 < route.size(); ++j) {
+ double d1 = geoDistanceMeters(points[route[i-1]], points[route[i]])
+ + geoDistanceMeters(points[route[j]], points[route[j+1]]);
+ double d2 = geoDistanceMeters(points[route[i-1]], points[route[j]])
+ + geoDistanceMeters(points[route[i]], points[route[j+1]]);
+ if (d2 < d1) {
+ reverse(route.begin()+i, route.begin()+j+1);
+ improved = true;
+ }
+ }
+ }
+ }
+}
+
+vector christofides(vector& points) {
+ auto mst = computeMST(points);
+ vector degree(points.size(), 0);
+ for (auto& e : mst) {
+ degree[e.u]++; degree[e.v]++;
+ }
+ vector odd;
+ for (size_t i = 0; i < points.size(); ++i)
+ if (degree[i] % 2) odd.push_back(static_cast(i));
+
+ vector matching;
+ while (odd.size() >= 2) {
+ int u = odd.back(); odd.pop_back();
+ int v = odd.back(); odd.pop_back();
+ matching.push_back({u, v, static_cast(geoDistanceMeters(points[u], points[v]))});
+ }
+
+ vector eulerian = mst;
+ eulerian.insert(eulerian.end(), matching.begin(), matching.end());
+
+ vector route;
+ vector used(points.size(), false);
+ route.push_back(0);
+ used[0] = true;
+ for (auto& e : eulerian) {
+ if (!used[e.v]) {
+ route.push_back(e.v);
+ used[e.v] = true;
+ }
+ }
+ route.push_back(route.front());
+ optimizeRoute(route, points);
+ return route;
+}
+
+void saveRoutesToFiles(const vector>& routes,
+ const vector>& selPts,
+ const vector& allPts)
+{
+ for (size_t i = 0; i < routes.size(); ++i) {
+ string filename = "Route_" + to_string(i+1) + ".json";
+ ofstream file(filename);
+ if (!file) {
+ cerr << "Kon bestand niet openen: " << filename << "\n";
+ continue;
+ }
+ file << "[\n";
+ for (size_t j = 0; j < routes[i].size(); ++j) {
+ const Point& p = selPts[i][routes[i][j]];
+ file << " { \"x\": " << p.x
+ << ", \"y\": " << p.y
+ << ", \"mg_id\": " << p.mg_id << " }"
+ << (j+1 < routes[i].size() ? "," : "") << "\n";
+ }
+ file << "]\n";
+ cout << "Opgeslagen in '" << filename << "'.\n";
+ }
+}
+
+void generate_routes(const std::vector &inputPoints) {
+
+ std::vector allPoints = inputPoints;
+
+ vector hotpoints(allPoints.begin(), allPoints.begin() + NUM_HOTPOINTS);
+ vector waypoints(allPoints.begin() + NUM_HOTPOINTS, allPoints.end());
+
+ random_device rd;
+ mt19937 g(rd());
+
+ selectedPoints.clear();
+ routeIndices.clear();
+ vector routeTimes;
+ bool valid = false;
+
+ while (!valid) {
+ selectedPoints = vector>(NUM_ROUTES, hotpoints);
+ routeIndices.clear();
+ routeTimes.clear();
+
+ shuffle(waypoints.begin(), waypoints.end(), g);
+ for (size_t i = 0; i < waypoints.size(); ++i) {
+ int rid = static_cast(i % NUM_ROUTES);
+ selectedPoints[rid].push_back(waypoints[i]);
+ for (int r = 0; r < NUM_ROUTES; ++r) {
+ if (rand() % 10 < 3)
+ selectedPoints[r].push_back(waypoints[i]);
+ }
+ }
+
+ for (int i = 0; i < NUM_ROUTES; ++i) {
+ routeIndices.push_back(christofides(selectedPoints[i]));
+ routeTimes.push_back(totalRouteCost(routeIndices[i], selectedPoints[i]));
+ }
+
+ valid = true;
+ for (auto t : routeTimes) {
+ if (t < MIN_ROUTE_TIME_MINUTES || t > MAX_ROUTE_TIME_MINUTES) {
+ valid = false;
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < NUM_ROUTES; ++i) {
+ cout << "Route " << i+1 << " tijd: "
+ << fixed << setprecision(1) << routeTimes[i] << " min\n";
+ }
+
+ saveRoutesToFiles(routeIndices, selectedPoints, allPoints);
+}
diff --git a/RoutesGenereren/routegenerator.h b/RoutesGenereren/routegenerator.h
new file mode 100644
index 0000000..d0f8dc7
--- /dev/null
+++ b/RoutesGenereren/routegenerator.h
@@ -0,0 +1,32 @@
+// routegenerator.h
+
+
+#ifndef ROUTEGENERATOR_H
+#define ROUTEGENERATOR_H
+
+#include
+#include
+
+struct Point {
+ int64_t x;
+ int64_t y;
+ int mg_id;
+
+ float cost;
+
+ Point(int64_t x = 0, int64_t y = 0, float cost = 1.0f, int mg_id = -1);
+
+ bool operator==(const Point& other) const;
+
+};
+
+
+extern std::vector> routeIndices;
+extern std::vector> selectedPoints;
+extern std::vector allPoints;
+
+
+void generate_routes(const std::vector& inputPoints);
+
+
+#endif // ROUTEGENERATOR_H
diff --git a/RoutesGenereren/settingsdialog.cpp b/RoutesGenereren/settingsdialog.cpp
new file mode 100644
index 0000000..4935c18
--- /dev/null
+++ b/RoutesGenereren/settingsdialog.cpp
@@ -0,0 +1,177 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "settingsdialog.h"
+#include "ui_settingsdialog.h"
+
+#include
+#include
+#include
+
+static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A");
+
+SettingsDialog::SettingsDialog(QWidget *parent) :
+ QDialog(parent),
+ m_ui(new Ui::SettingsDialog),
+ m_intValidator(new QIntValidator(0, 4000000, this))
+{
+ m_ui->setupUi(this);
+
+ m_ui->baudRateBox->setInsertPolicy(QComboBox::NoInsert);
+
+ connect(m_ui->applyButton, &QPushButton::clicked,
+ this, &SettingsDialog::apply);
+ connect(m_ui->serialPortInfoListBox, QOverload::of(&QComboBox::currentIndexChanged),
+ this, &SettingsDialog::showPortInfo);
+ connect(m_ui->baudRateBox, QOverload::of(&QComboBox::currentIndexChanged),
+ this, &SettingsDialog::checkCustomBaudRatePolicy);
+ connect(m_ui->serialPortInfoListBox, QOverload::of(&QComboBox::currentIndexChanged),
+ this, &SettingsDialog::checkCustomDevicePathPolicy);
+
+ fillPortsParameters();
+ fillPortsInfo();
+
+ updateSettings();
+}
+
+
+SettingsDialog::~SettingsDialog()
+{
+ delete m_ui;
+}
+
+SettingsDialog::Settings SettingsDialog::settings() const
+{
+ return m_currentSettings;
+}
+
+void SettingsDialog::showPortInfo(int idx)
+{
+ if (idx == -1)
+ return;
+
+ const QString blankString = tr(::blankString);
+
+ const QStringList list = m_ui->serialPortInfoListBox->itemData(idx).toStringList();
+ m_ui->descriptionLabel->setText(tr("Description: %1").arg(list.value(1, blankString)));
+ m_ui->manufacturerLabel->setText(tr("Manufacturer: %1").arg(list.value(2, blankString)));
+ m_ui->serialNumberLabel->setText(tr("Serial number: %1").arg(list.value(3, blankString)));
+ m_ui->locationLabel->setText(tr("Location: %1").arg(list.value(4, blankString)));
+ m_ui->vidLabel->setText(tr("Vendor Identifier: %1").arg(list.value(5, blankString)));
+ m_ui->pidLabel->setText(tr("Product Identifier: %1").arg(list.value(6, blankString)));
+}
+
+void SettingsDialog::apply()
+{
+ updateSettings();
+ hide();
+}
+
+void SettingsDialog::checkCustomBaudRatePolicy(int idx)
+{
+ const bool isCustomBaudRate = !m_ui->baudRateBox->itemData(idx).isValid();
+ m_ui->baudRateBox->setEditable(isCustomBaudRate);
+ if (isCustomBaudRate) {
+ m_ui->baudRateBox->clearEditText();
+ QLineEdit *edit = m_ui->baudRateBox->lineEdit();
+ edit->setValidator(m_intValidator);
+ }
+}
+
+void SettingsDialog::checkCustomDevicePathPolicy(int idx)
+{
+ const bool isCustomPath = !m_ui->serialPortInfoListBox->itemData(idx).isValid();
+ m_ui->serialPortInfoListBox->setEditable(isCustomPath);
+ if (isCustomPath)
+ m_ui->serialPortInfoListBox->clearEditText();
+}
+
+void SettingsDialog::fillPortsParameters()
+{
+ m_ui->baudRateBox->addItem(QStringLiteral("9600"), QSerialPort::Baud9600);
+ m_ui->baudRateBox->addItem(QStringLiteral("19200"), QSerialPort::Baud19200);
+ m_ui->baudRateBox->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);
+ m_ui->baudRateBox->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);
+ m_ui->baudRateBox->addItem(tr("Custom"));
+
+ m_ui->dataBitsBox->addItem(QStringLiteral("5"), QSerialPort::Data5);
+ m_ui->dataBitsBox->addItem(QStringLiteral("6"), QSerialPort::Data6);
+ m_ui->dataBitsBox->addItem(QStringLiteral("7"), QSerialPort::Data7);
+ m_ui->dataBitsBox->addItem(QStringLiteral("8"), QSerialPort::Data8);
+ m_ui->dataBitsBox->setCurrentIndex(3);
+
+ m_ui->parityBox->addItem(tr("None"), QSerialPort::NoParity);
+ m_ui->parityBox->addItem(tr("Even"), QSerialPort::EvenParity);
+ m_ui->parityBox->addItem(tr("Odd"), QSerialPort::OddParity);
+ m_ui->parityBox->addItem(tr("Mark"), QSerialPort::MarkParity);
+ m_ui->parityBox->addItem(tr("Space"), QSerialPort::SpaceParity);
+
+ m_ui->stopBitsBox->addItem(QStringLiteral("1"), QSerialPort::OneStop);
+#ifdef Q_OS_WIN
+ m_ui->stopBitsBox->addItem(tr("1.5"), QSerialPort::OneAndHalfStop);
+#endif
+ m_ui->stopBitsBox->addItem(QStringLiteral("2"), QSerialPort::TwoStop);
+
+ m_ui->flowControlBox->addItem(tr("None"), QSerialPort::NoFlowControl);
+ m_ui->flowControlBox->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl);
+ m_ui->flowControlBox->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl);
+}
+
+void SettingsDialog::fillPortsInfo()
+{
+ m_ui->serialPortInfoListBox->clear();
+ const QString blankString = tr(::blankString);
+ const auto infos = QSerialPortInfo::availablePorts();
+
+ for (const QSerialPortInfo &info : infos) {
+ QStringList list;
+ const QString description = info.description();
+ const QString manufacturer = info.manufacturer();
+ const QString serialNumber = info.serialNumber();
+ const auto vendorId = info.vendorIdentifier();
+ const auto productId = info.productIdentifier();
+ list << info.portName()
+ << (!description.isEmpty() ? description : blankString)
+ << (!manufacturer.isEmpty() ? manufacturer : blankString)
+ << (!serialNumber.isEmpty() ? serialNumber : blankString)
+ << info.systemLocation()
+ << (vendorId ? QString::number(vendorId, 16) : blankString)
+ << (productId ? QString::number(productId, 16) : blankString);
+
+ m_ui->serialPortInfoListBox->addItem(list.constFirst(), list);
+ }
+
+ m_ui->serialPortInfoListBox->addItem(tr("Custom"));
+}
+
+void SettingsDialog::updateSettings()
+{
+ m_currentSettings.name = m_ui->serialPortInfoListBox->currentText();
+
+ if (m_ui->baudRateBox->currentIndex() == 4) {
+ m_currentSettings.baudRate = m_ui->baudRateBox->currentText().toInt();
+ } else {
+ const auto baudRateData = m_ui->baudRateBox->currentData();
+ m_currentSettings.baudRate = baudRateData.value();
+ }
+ m_currentSettings.stringBaudRate = QString::number(m_currentSettings.baudRate);
+
+ const auto dataBitsData = m_ui->dataBitsBox->currentData();
+ m_currentSettings.dataBits = dataBitsData.value();
+ m_currentSettings.stringDataBits = m_ui->dataBitsBox->currentText();
+
+ const auto parityData = m_ui->parityBox->currentData();
+ m_currentSettings.parity = parityData.value();
+ m_currentSettings.stringParity = m_ui->parityBox->currentText();
+
+ const auto stopBitsData = m_ui->stopBitsBox->currentData();
+ m_currentSettings.stopBits = stopBitsData.value();
+ m_currentSettings.stringStopBits = m_ui->stopBitsBox->currentText();
+
+ const auto flowControlData = m_ui->flowControlBox->currentData();
+ m_currentSettings.flowControl = flowControlData.value();
+ m_currentSettings.stringFlowControl = m_ui->flowControlBox->currentText();
+
+ m_currentSettings.localEchoEnabled = m_ui->localEchoCheckBox->isChecked();
+}
diff --git a/RoutesGenereren/settingsdialog.h b/RoutesGenereren/settingsdialog.h
new file mode 100644
index 0000000..fac74c4
--- /dev/null
+++ b/RoutesGenereren/settingsdialog.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2012 Denis Shienkov
+// Copyright (C) 2012 Laszlo Papp
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+namespace Ui {
+class SettingsDialog;
+}
+
+class QIntValidator;
+
+QT_END_NAMESPACE
+
+class SettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ struct Settings {
+ QString name;
+ qint32 baudRate;
+ QString stringBaudRate;
+ QSerialPort::DataBits dataBits;
+ QString stringDataBits;
+ QSerialPort::Parity parity;
+ QString stringParity;
+ QSerialPort::StopBits stopBits;
+ QString stringStopBits;
+ QSerialPort::FlowControl flowControl;
+ QString stringFlowControl;
+ bool localEchoEnabled;
+ };
+
+ explicit SettingsDialog(QWidget *parent = nullptr);
+ ~SettingsDialog();
+
+ Settings settings() const;
+
+private slots:
+ void showPortInfo(int idx);
+ void apply();
+ void checkCustomBaudRatePolicy(int idx);
+ void checkCustomDevicePathPolicy(int idx);
+
+private:
+ void fillPortsParameters();
+ void fillPortsInfo();
+ void updateSettings();
+
+private:
+ Ui::SettingsDialog *m_ui = nullptr;
+ Settings m_currentSettings;
+ QIntValidator *m_intValidator = nullptr;
+};
+
+#endif // SETTINGSDIALOG_H
diff --git a/RoutesGenereren/settingsdialog.ui b/RoutesGenereren/settingsdialog.ui
new file mode 100644
index 0000000..bf7753a
--- /dev/null
+++ b/RoutesGenereren/settingsdialog.ui
@@ -0,0 +1,177 @@
+
+
+ SettingsDialog
+
+
+
+ 0
+ 0
+ 308
+ 322
+
+
+
+ Settings
+
+
+ -
+
+
+ Select Parameters
+
+
+
-
+
+
+ BaudRate:
+
+
+
+ -
+
+
+ -
+
+
+ Data bits:
+
+
+
+ -
+
+
+ -
+
+
+ Parity:
+
+
+
+ -
+
+
+ -
+
+
+ Stop bits:
+
+
+
+ -
+
+
+ -
+
+
+ Flow control:
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Select Serial Port
+
+
+
-
+
+
+ -
+
+
+ Description:
+
+
+
+ -
+
+
+ Manufacturer:
+
+
+
+ -
+
+
+ Serial number:
+
+
+
+ -
+
+
+ Location:
+
+
+
+ -
+
+
+ Vendor ID:
+
+
+
+ -
+
+
+ Product ID:
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 96
+ 20
+
+
+
+
+ -
+
+
+ Apply
+
+
+
+
+
+ -
+
+
+ Additional options
+
+
+
-
+
+
+ Local echo
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RoutesGenereren/terminal.qrc b/RoutesGenereren/terminal.qrc
new file mode 100644
index 0000000..0b49879
--- /dev/null
+++ b/RoutesGenereren/terminal.qrc
@@ -0,0 +1,9 @@
+
+
+ images/connect.png
+ images/disconnect.png
+ images/application-exit.png
+ images/settings.png
+ images/clear.png
+
+
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..4ba9d38
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,310 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+using namespace sf;
+
+const int WIDTH = 800;
+const int HEIGHT = 600;
+const int NUM_HOTPOINTS = 5;
+const int NUM_ROUTES = 10;
+const float MAX_DISTANCE_RATIO = 1.10f;
+
+struct Point {
+ float x, y;
+ float cost = 1.0f; // standaard tijd op locatie in minuten
+};
+
+bool operator==(const Point& a, const Point& b) {
+ return a.x == b.x && a.y == b.y;
+}
+
+struct Edge {
+ int u, v;
+ float weight;
+};
+
+int find(int x, vector& parent) {
+ if (parent[x] == x) return x;
+ return parent[x] = find(parent[x], parent);
+}
+
+float distance(const Point& a, const Point& b) {
+ return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
+}
+
+float totalRouteCost(const vector& route, const vector& points) {
+ float total = 0.0f;
+ for (size_t i = 0; i < route.size() - 1; ++i) {
+ float dist = distance(points[route[i]], points[route[i + 1]]);
+ float walkTime = dist / 84.0f;
+ total += walkTime + points[route[i]].cost;
+ }
+ total += points[route.back()].cost;
+ return total;
+}
+
+vector computeMST(vector& points) {
+ int n = points.size();
+ vector edges, mst;
+ vector parent(n);
+ for (int i = 0; i < n; i++) parent[i] = i;
+
+ for (int i = 0; i < n; i++) {
+ for (int j = i + 1; j < n; j++) {
+ edges.push_back({ i, j, distance(points[i], points[j]) });
+ }
+ }
+
+ sort(edges.begin(), edges.end(), [](Edge a, Edge b) {
+ return a.weight < b.weight;
+ });
+
+ for (const auto& edge : edges) {
+ int setU = find(edge.u, parent);
+ int setV = find(edge.v, parent);
+ if (setU != setV) {
+ mst.push_back(edge);
+ parent[setU] = setV;
+ if (mst.size() == n - 1) break;
+ }
+ }
+
+ return mst;
+}
+
+void optimizeRoute(vector& route, vector& points) {
+ bool improved = true;
+ while (improved) {
+ improved = false;
+ for (size_t i = 1; i < route.size() - 2; ++i) {
+ for (size_t j = i + 1; j < route.size() - 1; ++j) {
+ float d1 = distance(points[route[i - 1]], points[route[i]]) +
+ distance(points[route[j]], points[route[j + 1]]);
+ float d2 = distance(points[route[i - 1]], points[route[j]]) +
+ distance(points[route[i]], points[route[j + 1]]);
+ if (d2 < d1) {
+ reverse(route.begin() + i, route.begin() + j + 1);
+ improved = true;
+ }
+ }
+ }
+ }
+}
+
+vector christofides(vector& points) {
+ vector mst = computeMST(points);
+ vector degree(points.size(), 0);
+
+ for (auto& edge : mst) {
+ degree[edge.u]++;
+ degree[edge.v]++;
+ }
+
+ vector oddNodes;
+ for (int i = 0; i < points.size(); i++) {
+ if (degree[i] % 2 == 1) oddNodes.push_back(i);
+ }
+
+ vector matching;
+ while (!oddNodes.empty()) {
+ int u = oddNodes.back(); oddNodes.pop_back();
+ int v = oddNodes.back(); oddNodes.pop_back();
+ matching.push_back({ u, v, distance(points[u], points[v]) });
+ }
+
+ vector eulerianGraph = mst;
+ eulerianGraph.insert(eulerianGraph.end(), matching.begin(), matching.end());
+
+ vector route;
+ vector visited(points.size(), false);
+
+ route.push_back(0);
+ visited[0] = true;
+
+ for (auto& edge : eulerianGraph) {
+ if (!visited[edge.v]) {
+ route.push_back(edge.v);
+ visited[edge.v] = true;
+ }
+ }
+
+ for (int i = 0; i < NUM_HOTPOINTS; i++) {
+ if (find(route.begin(), route.end(), i) == route.end()) {
+ int bestInsertPos = 1;
+ float bestIncrease = FLT_MAX;
+ for (size_t j = 1; j < route.size(); j++) {
+ float increase = distance(points[route[j - 1]], points[i]) +
+ distance(points[i], points[route[j]]) -
+ distance(points[route[j - 1]], points[route[j]]);
+ if (increase < bestIncrease) {
+ bestIncrease = increase;
+ bestInsertPos = j;
+ }
+ }
+ route.insert(route.begin() + bestInsertPos, i);
+ }
+ }
+
+ if (route.front() != 0) route.insert(route.begin(), 0);
+ if (route.back() != 0) route.push_back(0);
+
+ optimizeRoute(route, points);
+
+ return route;
+}
+
+void saveRoutesToFile(const vector>& routes, const vector>& selectedPoints, const vector& allPoints, const string& filename) {
+ ofstream file(filename);
+ if (!file.is_open()) {
+ cerr << "Kon bestand niet openen: " << filename << endl;
+ return;
+ }
+
+ for (int i = 0; i < routes.size(); ++i) {
+ file << "Route " << i + 1 << ": ";
+ for (int index : routes[i]) {
+ auto it = find(allPoints.begin(), allPoints.end(), selectedPoints[i][index]);
+ if (it != allPoints.end()) {
+ int locationID = distance(allPoints.begin(), it);
+ file << locationID << " ";
+ }
+ else {
+ file << "X ";
+ }
+ }
+ file << endl;
+ }
+
+ file.close();
+ cout << "Routes opgeslagen in '" << filename << "'." << endl;
+}
+
+void drawGraph(RenderWindow& window, vector& allPoints, vector>& routes, vector>& selectedPoints) {
+ window.clear(Color::Black);
+
+ for (auto& p : allPoints) {
+ CircleShape shape(5);
+ shape.setPosition(p.x - 3, p.y - 3);
+ shape.setFillColor(Color::White);
+ window.draw(shape);
+ }
+
+ vector routeColors = {
+ Color::Red, Color::Green, Color::Blue, Color::Cyan,
+ Color::Magenta, Color::Yellow, Color(255, 165, 0),
+ Color(128, 0, 128), Color(0, 255, 127), Color(255, 105, 180)
+ };
+
+ for (int i = 0; i < NUM_ROUTES; ++i) {
+ auto& route = routes[i];
+ auto& points = selectedPoints[i];
+ Color color = routeColors[i % routeColors.size()];
+
+ for (size_t j = 0; j < points.size(); ++j) {
+ CircleShape shape(6);
+ shape.setPosition(points[j].x - 3, points[j].y - 3);
+ shape.setFillColor(j < NUM_HOTPOINTS ? Color::Blue : color);
+ window.draw(shape);
+ }
+
+ for (size_t j = 0; j < route.size() - 1; ++j) {
+ Vertex line[] = {
+ Vertex(Vector2f(points[route[j]].x, points[route[j]].y), color),
+ Vertex(Vector2f(points[route[j + 1]].x, points[route[j + 1]].y), color)
+ };
+ window.draw(line, 2, Lines);
+ }
+ }
+
+ window.display();
+}
+
+int main() {
+ RenderWindow window(VideoMode(WIDTH, HEIGHT), "Christofides Algorithm Visualization");
+
+ vector allPoints = {
+ {100, 100, 2.0}, {300, 200, 1.5}, {500, 400, 3.5}, {200, 500, 2.5}, {600, 100, 4.0},
+ {150, 300, 1.0}, {400, 350, 1.0}, {250, 50, 3.0}, {550, 250, 2.0}, {700, 150, 2.0},
+ {120, 500, 1.5}, {320, 270, 1.0}, {530, 420, 2.5}, {260, 430, 2.0}, {580, 130, 3.0},
+ {110, 220, 1.0}, {350, 180, 1.5}, {480, 380, 2.5}, {230, 510, 1.0}, {630, 170, 3.0},
+ {50, 450, 1.5}, {450, 100, 2.0}, {370, 320, 1.0}, {670, 220, 2.5}, {280, 370, 1.0},
+ {90, 290, 1.0}, {490, 470, 3.5}, {310, 150, 1.0}, {590, 290, 1.5}, {710, 410, 3.0},
+ {400, 500, 2.5}, {540, 350, 2.0}, {620, 200, 1.0}, {260, 260, 1.0}, {150, 400, 1.0},
+ {340, 530, 2.0}, {680, 300, 2.5}, {130, 140, 1.5}, {470, 240, 1.0}, {700, 500, 3.0},
+ {330, 440, 1.0}, {210, 360, 1.0}, {570, 450, 2.0}, {90, 190, 1.0}, {610, 110, 3.0},
+ {250, 480, 1.5}, {370, 210, 1.0}, {560, 310, 2.5}, {720, 180, 1.0}, {430, 280, 1.0}
+ };
+
+ vector hotpoints(allPoints.begin(), allPoints.begin() + NUM_HOTPOINTS);
+ vector waypoints(allPoints.begin() + NUM_HOTPOINTS, allPoints.end());
+
+ random_device rd;
+ mt19937 g(rd());
+
+ vector> selectedPoints;
+ vector> routes;
+ vector routeTimes;
+ bool valid = false;
+
+ while (!valid) {
+ selectedPoints = vector>(NUM_ROUTES, vector(hotpoints));
+ routes.clear();
+ routeTimes.clear();
+
+ shuffle(waypoints.begin(), waypoints.end(), g);
+
+ for (size_t i = 0; i < waypoints.size(); ++i) {
+ int routeIndex = i % NUM_ROUTES;
+ selectedPoints[routeIndex].push_back(waypoints[i]);
+ for (int r = 0; r < NUM_ROUTES; ++r) {
+ if (rand() % 10 < 3) {
+ selectedPoints[r].push_back(waypoints[i]);
+ }
+ }
+ }
+
+ for (int i = 0; i < NUM_ROUTES; ++i) {
+ // Zorg dat schoolpunt (100, 100) in elke lijst zit
+ if (find(selectedPoints[i].begin(), selectedPoints[i].end(), allPoints[0]) == selectedPoints[i].end()) {
+ selectedPoints[i].insert(selectedPoints[i].begin(), allPoints[0]);
+ }
+
+ sort(selectedPoints[i].begin(), selectedPoints[i].end(), [](const Point& a, const Point& b) {
+ return tie(a.x, a.y) < tie(b.x, b.y);
+ });
+ selectedPoints[i].erase(unique(selectedPoints[i].begin(), selectedPoints[i].end(), [](const Point& a, const Point& b) {
+ return a == b && !(a.x == 100 && a.y == 100);
+ }), selectedPoints[i].end());
+
+ routes.push_back(christofides(selectedPoints[i]));
+ routeTimes.push_back(totalRouteCost(routes.back(), selectedPoints[i]));
+ }
+
+ float minTime = *min_element(routeTimes.begin(), routeTimes.end());
+ float maxTime = *max_element(routeTimes.begin(), routeTimes.end());
+ valid = (maxTime / minTime) <= MAX_DISTANCE_RATIO;
+ }
+
+ for (int i = 0; i < NUM_ROUTES; ++i) {
+ cout << "Route " << i + 1 << " tijd: " << routeTimes[i] << " min" << endl;
+ }
+
+ saveRoutesToFile(routes, selectedPoints, allPoints, "routes.txt");
+
+ while (window.isOpen()) {
+ Event event;
+ while (window.pollEvent(event)) {
+ if (event.type == Event::Closed)
+ window.close();
+ }
+ drawGraph(window, allPoints, routes, selectedPoints);
+ }
+
+ return 0;
+}