diff --git a/.github/workflows/deploy_jetson.yml b/.github/workflows/deploy_jetson.yml index 0369a9e5..838f4fda 100644 --- a/.github/workflows/deploy_jetson.yml +++ b/.github/workflows/deploy_jetson.yml @@ -27,6 +27,7 @@ jobs: --build-arg password=${{ secrets.PASSWORD }} \ --build-arg user=${{ secrets.USER }} . + - name: Remove Old Local Executable run: | cd digital_cluster @@ -44,4 +45,14 @@ jobs: cd digital_cluster rm -f /home/jetracer/qt_cluster/build/digital_cluster cp ./digital_cluster /home/jetracer/qt_cluster/build/ - ls -lh /home/jetracer/qt_cluster/build/digital_cluster \ No newline at end of file + cp -r ./fonts_icon /home/jetracer/qt_cluster/ + ls -lh /home/jetracer/qt_cluster/build/digital_cluster + ls -lh /home/jetracer/qt_cluster/fonts_icon + + - name: Run Qt App with Docker Compose + run: | + cd /home/jetracer/qt_cluster + docker-compose build + docker-compose up + + diff --git a/.github/workflows/testing.yml b/.github/workflows/documentation_test_coverage.yml similarity index 74% rename from .github/workflows/testing.yml rename to .github/workflows/documentation_test_coverage.yml index 18d425d2..0bc983e1 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/documentation_test_coverage.yml @@ -1,4 +1,4 @@ -name: Google Testing +name: Documentation and running tests on: push: @@ -31,19 +31,17 @@ jobs: rm -rf build mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DUSE_SANITIZER=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON .. make xvfb-run -a ./digital_cluster_test lcov --capture --directory . --output-file coverage.info --ignore-errors mismatch,unused lcov --remove coverage.info '*/build/*' '*/test/*' '/usr/*' --output-file coverage.info genhtml coverage.info --output-directory coverage_report - - name: Create docs directory if it does not exist - run: mkdir -p docs # Gerar documentação - name: Generate Documentation - run: doxygen Doxyfile + run: cd doxyfiles && doxygen Doxyfile - name: Upload Coverage Report uses: actions/upload-artifact@v4 @@ -56,23 +54,18 @@ jobs: uses: actions/upload-artifact@v4 with: name: doxygen-docs - path: docs # Adjust to your Doxygen output directory + path: doxyfiles/docs # retention-days: 7 - name: Move coverage report into docs run: | - mkdir -p docs/coverage - cp -r digital_cluster/build/coverage_report/* docs/coverage/ + mkdir -p doxyfiles/docs/coverage + cp -r digital_cluster/build/coverage_report/* doxyfiles/docs/coverage/ - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.PAT_TOKEN }} - publish_dir: docs - # destination_dir: ./ + publish_dir: doxyfiles/docs force_orphan: true - # user_name: github-actions - # user_email: github-actions@github.com - # keep_files: true - # exclude_assets: '**/*.md' diff --git a/.gitignore b/.gitignore index 4667bf4a..6f175cd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ digital_cluster/build/ digital_cluster/digital_cluster digital_cluster/.env -docs/ -.gitignore \ No newline at end of file +digital_cluster/.vscode/ +digital_cluster/src/object copy.cpp +.vscode/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index bdbf6f43..00000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "/usr/include/x86_64-linux-gnu/qt5/QtCore" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-clang-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f09c0eb1..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "files.associations": { - "iostream": "cpp", - "random": "cpp", - "any": "cpp", - "memory": "cpp", - "future": "cpp", - "optional": "cpp", - "*.tcc": "cpp", - "string": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "cctype": "cpp", - "charconv": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "list": "cpp", - "map": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "ratio": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "numbers": "cpp", - "ostream": "cpp", - "ranges": "cpp", - "span": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "stop_token": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "typeinfo": "cpp", - "variant": "cpp", - "format": "cpp", - "cwctype": "cpp", - "set": "cpp", - "csignal": "cpp", - "deque": "cpp", - "semaphore": "cpp" - }, - "cmake.sourceDirectory": "/home/matilde/Team05_SEA-ME_DES/DistributedEmbeddedSystems/digital_cluster", - "C_Cpp.errorSquiggles": "disabled" -} \ No newline at end of file diff --git a/README.md b/README.md index ca40304e..40252755 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# Digital Cluster + +![Digital Cluster](/digital_cluster/results/cluster.png) ## Project Architecture @@ -13,12 +16,12 @@ The Jetson communicates with the Raspberry via **mqtt**, with a **Cloud Broker** ## Documentation and Results -For more documentation and visual results of our Qt app, go to [Digital Cluster README](/digital_cluster/README.md). +For more documentation and visual results of our Qt app, go to [Digital Cluster documentation](/digital_cluster/README.md). -For more on our **CAN Communication, Arduino and Speed Sensor**, visit our [Arduino README](/arduino/README_arduino.md). +For more on our **CAN Communication, Arduino and Speed Sensor**, visit our [Arduino documentation](/arduino/README_arduino.md). To view a more detailed and structured documentation, click on -[Doxygen Docs](https://seame-pt.github.io/DistributedEmbeddedSystems/). +[Doxygen documentation](https://seame-pt.github.io/DistributedEmbeddedSystems/). ## Test Coverage Report diff --git a/digital_cluster/.vscode/c_cpp_properties.json b/digital_cluster/.vscode/c_cpp_properties.json deleted file mode 100644 index 71e21bdc..00000000 --- a/digital_cluster/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "/usr/include/x86_64-linux-gnu/qt5/QtWidgets" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-clang-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/digital_cluster/.vscode/settings.json b/digital_cluster/.vscode/settings.json deleted file mode 100644 index 5bc015e8..00000000 --- a/digital_cluster/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "files.associations": { - "random": "cpp", - "qapplication": "cpp", - "algorithm": "cpp", - "typeinfo": "cpp", - "chrono": "cpp" - } -} \ No newline at end of file diff --git a/digital_cluster/CMakeLists.txt b/digital_cluster/CMakeLists.txt index f7a2162b..3d331886 100644 --- a/digital_cluster/CMakeLists.txt +++ b/digital_cluster/CMakeLists.txt @@ -26,8 +26,12 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") option(USE_SANITIZER "Enable sanitizer" OFF) if(USE_SANITIZER) - set(SANITIZER_COMPILE_FLAGS -fsanitize=address -fno-omit-frame-pointer) +# set(SANITIZER_COMPILE_FLAGS -fsanitize=address -fno-omit-frame-pointer) set(SANITIZER_LINK_FLAGS -fsanitize=address) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_COMPILE_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_COMPILE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_LINK_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_LINK_FLAGS}") # set(SANITIZER_FLAGS -fsanitize=thread) # Uncomment this line if you want ThreadSanitizer instead: endif() diff --git a/digital_cluster/README.md b/digital_cluster/README.md index 28207f43..78284ed4 100644 --- a/digital_cluster/README.md +++ b/digital_cluster/README.md @@ -1,10 +1,10 @@ # Instrument Cluster -![Cluster](results/cluster.jpeg) +Our Digital Cluster receives updates from the **cloud** via MQTT. -Click to see our Digital Cluster updating the values with **Cloud** [View video](results/cluster.mp4). +![Cluster](results/cluster.gif) -### Compile and run with Cmake +## Compile and run with Cmake To compile locally and view the cluster, you may need to install libraries such as QtMqtt. We recommend using Qt5 and installing QtMqtt from its source repository, the 5.13 version (specific branch). @@ -12,7 +12,6 @@ Furthermore, if you are not in the same network as the jetracer, you should get If you are indeed in the same network, you can set the correct Hostname and port on the file mentioned above. You will also see the implementation and documentation of this matter in said file. - You may have some issues with the thread library so you may need to export it, in my device I do it like this: ```bash @@ -35,7 +34,7 @@ make ./digital_cluster ``` -# Google Test +## Google Test To view our gtests go to the **test** folder, to run them, execute the following: @@ -43,7 +42,7 @@ To view our gtests go to the **test** folder, to run them, execute the following ./automate_test.sh ``` -# Cross-compilation +## Cross-compilation To run this program on our jetracer, you should connect through localhost on mainwindow.cpp. After that, execute the following: diff --git a/digital_cluster/automate_cross.sh b/digital_cluster/automate_cross.sh new file mode 100755 index 00000000..06e71bd1 --- /dev/null +++ b/digital_cluster/automate_cross.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e +docker buildx build --platform linux/arm64 --progress=plain --load -t cross-env --build-arg password=$(grep password .env | cut -d '=' -f2) --build-arg user=$(grep user .env | cut -d '=' -f2) . + + +if [ -f "./digital_cluster" ]; then + + echo "Removing existing executable from local machine" + + rm ./digital_cluster + +fi + + +echo "Copying new executable from Docker container" + + +docker cp $(docker create cross-env):/workspace/build/digital_cluster ./digital_cluster + +echo "Removing existing executable from JetRacer" + + +ssh jetracer@100.123.70.46 "rm -f /home/jetracer/qt_cluster/build/digital_cluster" + + +echo "Copying new executable to Raspberry" + +scp ./digital_cluster jetracer@100.123.70.46:/home/jetracer/qt_cluster/build + +ssh jetracer@100.123.70.46 "cd qt_cluster && docker-compose build && docker-compose up" diff --git a/digital_cluster/automate_env.sh b/digital_cluster/automate_env.sh index 2071e73c..fb35bec3 100755 --- a/digital_cluster/automate_env.sh +++ b/digital_cluster/automate_env.sh @@ -1,13 +1,14 @@ #!/bin/bash export $(grep -v '^#' .env | xargs) set -e -rm -rf build -mkdir build +# rm -rf build +# mkdir build cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make # export QT_WAYLAND_DISABLE_TABLET=1 # export QT_WAYLAND_DISABLE_CLIPBOARD=1 # export QT_QPA_PLATFORM=xcb -LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0 ./digital_cluster +LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0 +./digital_cluster # export QT_LOGGING_RULES="qt5.*=true;qt5.wayland=true" # GSETTINGS_BACKEND=memory QT_WAYLAND_DISABLE_KEYBOARD=1 # valgrind --leak-check=full --track-origins=yes diff --git a/digital_cluster/automate_test.sh b/digital_cluster/automate_test.sh index 7ed6cc46..ac2753ee 100755 --- a/digital_cluster/automate_test.sh +++ b/digital_cluster/automate_test.sh @@ -1,18 +1,17 @@ #!/bin/bash export $(grep -v '^#' .env | xargs) set -e -# rm -rf build -# mkdir build +rm -rf build +mkdir build cd build -cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON .. # -DUSE_SANITIZER=ON +cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON .. make LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0 ./digital_cluster_test -# gdb --args ./digital_cluster_test -lcov --capture --directory . --output-file coverage.info -lcov --remove coverage.info '*/build/*' '*/test/*' '/usr/*' '*/googletest/*' --output-file coverage.info -genhtml coverage.info --output-directory coverage_report -xdg-open coverage_report/index.html +# lcov --capture --directory . --output-file coverage.info +# lcov --remove coverage.info '*/build/*' '*/test/*' '/usr/*' '*/googletest/*' --output-file coverage.info +# genhtml coverage.info --output-directory coverage_report +# xdg-open coverage_report/index.html #/lib/x86_64-linux-gnu/libasan.so.8 # valgrind --leak-check=full ./digital_cluster_test diff --git a/digital_cluster/coverage.info b/digital_cluster/coverage.info deleted file mode 100644 index e69de29b..00000000 diff --git a/digital_cluster/fonts_icon/speed50.png b/digital_cluster/fonts_icon/speed50.png new file mode 100644 index 00000000..d64418b5 Binary files /dev/null and b/digital_cluster/fonts_icon/speed50.png differ diff --git a/digital_cluster/fonts_icon/speed80.png b/digital_cluster/fonts_icon/speed80.png new file mode 100644 index 00000000..8e875550 Binary files /dev/null and b/digital_cluster/fonts_icon/speed80.png differ diff --git a/digital_cluster/include/autonomy.h b/digital_cluster/include/autonomy.h index ef985cc0..4ee3aa43 100644 --- a/digital_cluster/include/autonomy.h +++ b/digital_cluster/include/autonomy.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -17,6 +19,7 @@ class Autonomy : public QWidget ~Autonomy(); void set_autonomy(int i); int get_nbsections(); + int get_autonomy(); QVector get_sections(); QHBoxLayout* get_layout(); QVBoxLayout* get_mainlayout(); @@ -25,10 +28,11 @@ class Autonomy : public QWidget private: int nb_sections; + int autonomy; QVector sections; QHBoxLayout *layout; QVBoxLayout *main_layout; QLabel *label; }; -#endif // Autonomy_H +#endif diff --git a/digital_cluster/include/battery.h b/digital_cluster/include/battery.h index 4b04820a..edb8cc76 100644 --- a/digital_cluster/include/battery.h +++ b/digital_cluster/include/battery.h @@ -110,4 +110,4 @@ class Battery : public QWidget void setTestPainter(TestPainter* painter) { test_painter = painter; } }; -#endif // BATTERY_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/digital_cluster/include/lane.h b/digital_cluster/include/lane.h index 06c5731a..c56a0551 100644 --- a/digital_cluster/include/lane.h +++ b/digital_cluster/include/lane.h @@ -15,6 +15,8 @@ #include #include #include +#include + class Lane: public QWidget { Q_OBJECT @@ -28,7 +30,8 @@ class Lane: public QWidget { QPen rightGrayPen; QPen leftRedPen; QPen rightRedPen; - + QPixmap pixmap_original; + qreal m_leftDashOffset = 0.0; qreal m_rightDashOffset = 0.0; qreal m_leftOpacity = 1.0; @@ -41,6 +44,7 @@ class Lane: public QWidget { QPixmap pixmap; QGraphicsOpacityEffect *opacityEffect; void paintEvent(QPaintEvent *) override; + public: Lane(QWidget *parent = nullptr); diff --git a/digital_cluster/include/mainwindow.h b/digital_cluster/include/mainwindow.h index 487a701b..a80aa3ad 100644 --- a/digital_cluster/include/mainwindow.h +++ b/digital_cluster/include/mainwindow.h @@ -34,8 +34,9 @@ class MainWindow : public QMainWindow Battery *get_battery(); Autonomy *get_autonomy(); Temperature *get_temperature(); - Lane* get_lane(); Object* get_object(); + Lane* get_lane(); + Speed* get_speed(); private slots: void connected(); @@ -46,6 +47,7 @@ private slots: Speed *left_dial = nullptr; Battery *right_dial = nullptr; Lane *center_dial = nullptr; + Object *object = nullptr; QMqttClient *client; Temperature *temp; Autonomy *autonomy; diff --git a/digital_cluster/include/object.h b/digital_cluster/include/object.h index dfee682c..25a67785 100644 --- a/digital_cluster/include/object.h +++ b/digital_cluster/include/object.h @@ -5,21 +5,22 @@ #include #include -class Object : public Lane { +class Object : public QWidget { Q_OBJECT private: QWidget *popup = nullptr; int object; - QString digital_path; + QString speed50_path; + QString speed80_path; public: Object(QWidget *parent = nullptr); ~Object(); - // void showObjectPopup(); - void setting_pens(int i, QPainter *painter); int get_object(); - void set_object(int i); + QString get_speed50_path() const { return speed50_path; } + QString get_speed80_path() const { return speed80_path; } + void set_object(int i, const QString &topicName); protected: void paintEvent(QPaintEvent *) override; diff --git a/digital_cluster/include/temperature.h b/digital_cluster/include/temperature.h index 29e1a477..26c65cb7 100644 --- a/digital_cluster/include/temperature.h +++ b/digital_cluster/include/temperature.h @@ -21,13 +21,15 @@ class Temperature : public QWidget QHBoxLayout* get_layout(); QVBoxLayout* get_mainlayout(); QLabel *get_label(); + int get_temperature(); private: int nb_sections; + int temperature; QVector sections; QHBoxLayout *layout; QVBoxLayout* main_layout; QLabel *label = nullptr; }; -#endif // Temperature_H +#endif diff --git a/digital_cluster/main.cpp b/digital_cluster/main.cpp index 623a969d..4e023aeb 100644 --- a/digital_cluster/main.cpp +++ b/digital_cluster/main.cpp @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); app = &a; a.setAttribute(Qt::AA_UseHighDpiPixmaps); - a.setOverrideCursor(QCursor(Qt::BlankCursor)); + QApplication::setOverrideCursor(Qt::BlankCursor); MainWindow window; window.setFixedSize(1280, 400); window.setWindowState(Qt::WindowFullScreen); @@ -22,6 +22,7 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, cleanup); std::signal(SIGTSTP, cleanup); int result = a.exec(); + QApplication::restoreOverrideCursor(); app = nullptr; return result; } \ No newline at end of file diff --git a/digital_cluster/results/cluster.gif b/digital_cluster/results/cluster.gif new file mode 100644 index 00000000..9d567371 Binary files /dev/null and b/digital_cluster/results/cluster.gif differ diff --git a/digital_cluster/results/cluster.jpeg b/digital_cluster/results/cluster.jpeg deleted file mode 100644 index 10ab349e..00000000 Binary files a/digital_cluster/results/cluster.jpeg and /dev/null differ diff --git a/digital_cluster/results/cluster.mp4 b/digital_cluster/results/cluster.mp4 deleted file mode 100644 index 722fb088..00000000 Binary files a/digital_cluster/results/cluster.mp4 and /dev/null differ diff --git a/digital_cluster/results/cluster.png b/digital_cluster/results/cluster.png new file mode 100644 index 00000000..bc7c19f1 Binary files /dev/null and b/digital_cluster/results/cluster.png differ diff --git a/digital_cluster/src/autonomy.cpp b/digital_cluster/src/autonomy.cpp index a2381675..4889d2cb 100644 --- a/digital_cluster/src/autonomy.cpp +++ b/digital_cluster/src/autonomy.cpp @@ -4,59 +4,71 @@ Autonomy::Autonomy(QWidget *parent) : QWidget{parent} { - if (parent) - { - setMinimumSize(parent->width() * 0.2, parent->height() * 0.16); - setMaximumSize(parent->width() * 0.2, parent->height() * 0.16); - } + label = new QLabel(this); main_layout = new QVBoxLayout(this); - main_layout->setSpacing(height() * 0.05); layout = new QHBoxLayout(); - layout->setSpacing(width() * 0.0155); + + label->setAlignment(Qt::AlignCenter); + label->setMinimumWidth(120); + main_layout->setSpacing(6); + main_layout->setContentsMargins(0, 0, 0, 0); // Remove margins + + layout->setSpacing(2); + layout->setContentsMargins(0, 0, 0, 0); + nb_sections = 6; for (int i = 0; i < nb_sections; ++i) { QWidget *section = new QWidget(this); - section->setFixedHeight(height() * 0.3); - section->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + section->setFixedSize(22, 32); layout->addWidget(section); sections.append(section); } + main_layout->addLayout(layout); - label = new QLabel(this); - set_autonomy(8); + set_autonomy(7); + main_layout->addWidget(label); // Add label to the layout } //destructor Autonomy::~Autonomy() { std::cout << "Remove Autonomy" << std::endl; - delete layout; } //setter void Autonomy::set_autonomy(int aut) { - int sections_color = static_cast((aut / 10.0) * nb_sections); + autonomy = aut; + int sections_color = std::max(1, static_cast(std::ceil((aut / 10.0) * nb_sections))); for (int i = nb_sections -1; i >= 0; i--) { if (i >= nb_sections - sections_color) { - QColor section_color; - if (aut > 6) + QColor endColor; + QColor startColor(0, 80, 60); // dark cyan-green + + if (aut >= 5) { - int red = std::max(0, 120 - ((nb_sections - 1 - i) * (255 / nb_sections))); - int green = std::min(255, 80 + ((nb_sections - 1 - i) * (20 / nb_sections))); - int blue = std::min(255, 50 + ((nb_sections - 1 - i) * (50 / nb_sections))); - section_color.setRgb(red, green, blue); - } else + endColor = QColor(0, 60, 40); // slightly brighter cyan-green + } + else { - int red = std::max(0, 120 - ((nb_sections - 1 - i) * (255 / nb_sections))); - int green = std::min(255, 80 + ((nb_sections - 1 - i) * (20 / nb_sections))); - int blue = std::min(255, 50 + ((nb_sections - 1 - i) * (50 / nb_sections))); - section_color.setRgb(red, green, blue); - } + startColor = QColor(80, 20, 0); + endColor = QColor(50, 10, 0); + } + // Interpolate based on **active section index** (0 = leftmost active) + int activeIndex = i - (nb_sections - sections_color); // 0..sections_active-1 + float t = (sections_color > 1) ? float(activeIndex) / (sections_color - 1) : 0.0f; // 0..1 + + int red = startColor.red() + t * (endColor.red() - startColor.red()); + int green = startColor.green() + t * (endColor.green() - startColor.green()); + int blue = startColor.blue() + t * (endColor.blue() - startColor.blue()); + + QColor section_color(red, green, blue); // correct: QColor object sections[i]->setStyleSheet(QString("background-color: %1").arg(section_color.name())); + + } else { QColor inactive_color(22, 32, 60); @@ -64,12 +76,11 @@ void Autonomy::set_autonomy(int aut) } } label->setTextFormat(Qt::RichText); // Enable rich text - label->setText("" + QString::number(aut) + - " km"); + label->setText("" + QString::number(aut) + + " km"); label->setStyleSheet("color: rgb(0, 120, 140);"); - label->setAlignment(Qt::AlignTop | Qt::AlignRight); + label->setAlignment(Qt::AlignRight); label->setContentsMargins(0, 0, 5, 0); - main_layout->addWidget(label); } // getters @@ -78,6 +89,11 @@ int Autonomy::get_nbsections() return nb_sections; } +int Autonomy::get_autonomy() +{ + return autonomy; +} + QVector Autonomy::get_sections() { return sections; diff --git a/digital_cluster/src/battery.cpp b/digital_cluster/src/battery.cpp index d12f304d..c97177b0 100644 --- a/digital_cluster/src/battery.cpp +++ b/digital_cluster/src/battery.cpp @@ -6,7 +6,7 @@ Battery::Battery(QWidget *parent) if (parent) { setMinimumSize(parent->width() * 0.5, parent->height() * 0.7); - setMaximumSize(parent->width() * 0.5, parent->height() * 0.7); + // setMaximumSize(parent->width() * 0.5, parent->height() * 0.7); } } @@ -69,13 +69,13 @@ void Battery::draw_arcs(TestPainter *painter) } float angle_progress = (static_cast(current) * 270.0f) / max; segment_angle = angle_progress / segments; - QColor start_color(0, 65, 74); + QColor start_color(0, 80, 60); QColor end_color(0, 255, 200); if (current < 40) { segments = 200; - start_color = QColor(0, 65, 74); - end_color = QColor(0, 140, 150, 255); + start_color = QColor(0, 80, 60); + end_color = QColor(0, 100, 150, 255); } for (int i = 0; i < segments; ++i) { @@ -95,7 +95,7 @@ void Battery::draw_arcs(TestPainter *painter) void Battery::draw_pixmap(TestPainter *painter) { - painter->setPen(QPen(QColor(0, 250, 195))); + painter->setPen(QPen(QColor(0, 255, 190))); painter->setFont(QFont("Digital-7", width() / 5, QFont::Bold)); QRect currentTextRect = painter->boundingRect(rect(), Qt::AlignCenter, QString::number(current)); painter->drawText(currentTextRect, Qt::AlignCenter, QString::number(current)); @@ -104,7 +104,7 @@ void Battery::draw_pixmap(TestPainter *painter) QString digital_path = QDir(path).filePath("../fonts_icon/battery.png"); digital_path = QDir::cleanPath(digital_path); QPixmap pixmap(digital_path); - pixmap = pixmap.scaled(width() / 10, width() / 10, Qt::KeepAspectRatio); + pixmap = pixmap.scaled(width() / 8, width() / 8, Qt::KeepAspectRatio); QRect rectBottom = this->rect(); int xIcon = (width() - pixmap.width()) / 2; QRect bottomRect = QRect(xIcon, currentTextRect.bottom() + 10, pixmap.width(), pixmap.height()); @@ -114,7 +114,7 @@ void Battery::draw_pixmap(TestPainter *painter) void Battery::draw_text(TestPainter *painter, QRect bottomRect) { - QFont font("Calculator", width() / 16); + QFont font("Calculator", width() / 12); painter->setFont(font); painter->setPen(QPen(QColor(0, 120, 100))); QRectF textRect(bottomRect.right() + 5, bottomRect.bottom() - 23, 30, 30); // Adjust size as needed diff --git a/digital_cluster/src/lane.cpp b/digital_cluster/src/lane.cpp index 8c2e854e..2e729b2c 100644 --- a/digital_cluster/src/lane.cpp +++ b/digital_cluster/src/lane.cpp @@ -4,14 +4,13 @@ Lane::Lane(QWidget *parent) : QWidget(parent), lane(1) { if (parent) { - setMinimumSize(parent->width() * 0.4, parent->height() * 0.4); - setMaximumSize(parent->width() * 0.4, parent->height() * 0.4); + setMinimumSize(parent->width() * 0.5, parent->height() * 0.5); } QString path = QCoreApplication::applicationDirPath(); QString digital_path = QDir(path).filePath("../fonts_icon/sports.png"); digital_path = QDir::cleanPath(digital_path); pixmap = QPixmap(digital_path); - pixmap = pixmap.scaled(this->width() * 0.4, height() * 0.7, Qt::KeepAspectRatio, Qt::SmoothTransformation); + pixmap = pixmap.scaled(this->width() * 0.4 , height() * 0.5, Qt::KeepAspectRatio, Qt::SmoothTransformation); animationTimer = new QTimer(this); connect(animationTimer, &QTimer::timeout, this, [this]() { m_leftDashOffset = int(m_leftDashOffset + 1) % 30; @@ -30,14 +29,14 @@ void Lane::setters() laneGradient = QLinearGradient(0, height() * 0.9, 0, 0); laneGradient.setColorAt(0.0, QColor(20, 20, 20, 20)); - laneGradient.setColorAt(0.4, QColor(100, 100, 100, 100)); - laneGradient.setColorAt(0.5, QColor(100, 100, 100, 100)); + laneGradient.setColorAt(0.4, QColor(150, 150, 150, 150)); + laneGradient.setColorAt(0.5, QColor(150, 150, 150, 150)); laneGradient.setColorAt(1.0, QColor(20, 20, 20, 1)); redGradient = QLinearGradient(0, height() * 0.9, 0, 0); redGradient.setColorAt(0.0, QColor(50, 5, 0, 50)); - redGradient.setColorAt(0.4, QColor(180, 20, 0, 150)); - redGradient.setColorAt(0.6, QColor(180, 20, 0, 150)); + redGradient.setColorAt(0.4, QColor(200, 30, 0, 150)); + redGradient.setColorAt(0.6, QColor(200, 30, 0, 150)); redGradient.setColorAt(1.0, QColor(50, 5, 0, 50)); leftGrayPen = QPen(QBrush(laneGradient), 5, Qt::DashLine); @@ -59,10 +58,10 @@ void Lane::showNoLanePopup() QHBoxLayout *layout = new QHBoxLayout(popup); QLabel *iconLabel = new QLabel(popup); QPixmap warningIcon(digital_path); - iconLabel->setPixmap(warningIcon.scaled(22, 22, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + iconLabel->setPixmap(warningIcon.scaled(30, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation)); QLabel *textLabel = new QLabel(" Take manual control", popup); - textLabel->setStyleSheet("color: gray; font-size: 16px;"); + textLabel->setStyleSheet("color: gray; font-size: 20px;"); layout->addWidget(iconLabel, 0, Qt::AlignTop | Qt::AlignCenter); layout->addWidget(textLabel, 0, Qt::AlignCenter); popup->setLayout(layout); @@ -87,6 +86,7 @@ void Lane::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); + QRect rect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, pixmap.size(), this->rect()); painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.drawPixmap(rect, pixmap); diff --git a/digital_cluster/src/mainwindow.cpp b/digital_cluster/src/mainwindow.cpp index 88da4806..cae45cfb 100644 --- a/digital_cluster/src/mainwindow.cpp +++ b/digital_cluster/src/mainwindow.cpp @@ -4,31 +4,58 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), client(new QMqttClient(this)) { - setStyleSheet("background-color: rgb(0, 0, 20);"); left_dial = new Speed(this); right_dial = new Battery(this); - QHBoxLayout* layout = new QHBoxLayout(); - layout->addWidget(left_dial, 1, Qt::AlignTop | Qt::AlignLeft); - + object = new Object(this); + temp = new Temperature(this); + autonomy = new Autonomy(this); center_dial = new Lane(this); - QVBoxLayout* centerLayout = new QVBoxLayout(); - centerLayout->addWidget(center_dial, 0, Qt::AlignCenter); - layout->addLayout(centerLayout, 1); + QVBoxLayout* mainlayout = new QVBoxLayout(); + QHBoxLayout* layout = new QHBoxLayout(); + QVBoxLayout* lane_layout = new QVBoxLayout(); + QHBoxLayout* centerbar = new QHBoxLayout(); + QWidget* centralWidget = new QWidget(this); + layout->addWidget(left_dial, 1, Qt::AlignTop | Qt::AlignLeft); + lane_layout->addWidget(center_dial, 0, Qt::AlignCenter); + lane_layout->setStretch(0, 1); + layout->addLayout(lane_layout, 1); layout->addWidget(right_dial, 1, Qt::AlignTop | Qt::AlignRight); - QVBoxLayout* mainlayout = new QVBoxLayout(); + + // Create layouts + object->setFixedSize(60, 60); + + // Create a main container for the entire bar + QWidget *barContainer = new QWidget(); + QHBoxLayout *barLayout = new QHBoxLayout(barContainer); + barLayout->setContentsMargins(20, 0, 80, 0); // Left/right margins + + // Left section with object + barLayout->addWidget(object, 0, Qt::AlignLeft); + + // Center section with temp and autonomy + QWidget *centerWidget = new QWidget(); + QHBoxLayout *centerLayout = new QHBoxLayout(centerWidget); + centerLayout->setContentsMargins(0, 0, 0, 0); + centerLayout->addStretch(1); + temp->setFixedHeight(80); // or whatever looks right + autonomy->setFixedHeight(80); // match temp + // temp->setStyleSheet("border: 1px solid red;"); + // autonomy->setStyleSheet("border: 1px solid blue;"); + + centerLayout->addWidget(temp, 0, Qt::AlignTop); + centerLayout->addSpacing(30); + centerLayout->addWidget(autonomy, 0, Qt::AlignTop); + centerLayout->addStretch(1); + + barLayout->addWidget(centerWidget, 1); // Take remaining space + mainlayout->addLayout(layout, 2); + mainlayout->addWidget(barContainer, 1); // Add the container widget - temp = new Temperature(this); - autonomy = new Autonomy(this); - QHBoxLayout* layoutbar = new QHBoxLayout(); - layoutbar->setSpacing(width() / 20); - layoutbar->addWidget(temp, 0, Qt::AlignBottom | Qt::AlignRight); - layoutbar->addWidget(autonomy, 0, Qt::AlignBottom | Qt::AlignLeft); - mainlayout->addLayout(layoutbar, 1); - QWidget* centralWidget = new QWidget(this); centralWidget->setLayout(mainlayout); setCentralWidget(centralWidget); + setStyleSheet("background-color: rgb(0, 0, 20);"); init_mqtt(); } @@ -42,14 +69,14 @@ MainWindow::~MainWindow() //connecting to mqtt via cloud or localhost or to jetracer via network void MainWindow::init_mqtt() { - client->setHostname("972e24210b544ba49bfb9c1d3164d02b.s1.eu.hivemq.cloud"); //cloud - client->setPort(8883); + // client->setHostname("972e24210b544ba49bfb9c1d3164d02b.s1.eu.hivemq.cloud"); //cloud + // client->setPort(8883); QString user = qgetenv("user"); client->setUsername(user); QString pass = qgetenv("password"); client->setPassword(pass); - // client->setHostname("10.21.221.67"); //when on the same network - // client->setPort(1883); //cross compiling + client->setHostname("10.21.221.67"); //when on the same network + client->setPort(1883); //cross compiling // client->setHostname("127.0.0.1"); //when cross-compiling with jetracer connect(client, &QMqttClient::connected, this, &MainWindow::connected); @@ -73,9 +100,11 @@ void MainWindow::connected() auto autono_sub = client->subscribe(autono); QMqttTopicFilter lane("jetracer/lane_touch"); auto lane_sub = client->subscribe(lane); - // QMqttTopicFilter obj("jetracer/object"); - // auto object = client->subscribe(obj); - if (!speed_sub || !bat_sub | !autono_sub || !temp_sub || !lane_sub) { // || !object + QMqttTopicFilter speed50("jetracer/speed_50"); + auto speed50_sub = client->subscribe(speed50); + QMqttTopicFilter speed80("jetracer/speed_80"); + auto speed80_sub = client->subscribe(speed80); + if (!speed_sub || !bat_sub | !autono_sub || !temp_sub || !lane_sub || !speed50_sub || !speed80_sub) { qDebug() << "Failed to subscribe to topic"; } } @@ -112,11 +141,11 @@ void MainWindow::message_received(const QByteArray &message, const QMqttTopic center_dial->set_lane(msg); }, Qt::AutoConnection); } - // else if (topic.name() == "jetracer/object") { - // QMetaObject::invokeMethod(this, [this, msg]() { - // center_dial->set_object(msg); - // }, Qt::AutoConnection); - // } + else if (topic.name() == "jetracer/speed_50" || topic.name() == "jetracer/speed_80") { + QMetaObject::invokeMethod(this, [this, msg, topic]() { + object->set_object(msg, topic.name()); + }, Qt::AutoConnection); + } } else { qDebug() << "Invalid data received"; } @@ -132,6 +161,11 @@ Battery* MainWindow::get_battery() return right_dial; } +Speed* MainWindow::get_speed() +{ + return left_dial; +} + Autonomy* MainWindow::get_autonomy() { std::cout << "Getting autonomy\n"; @@ -146,4 +180,9 @@ Temperature* MainWindow::get_temperature() Lane* MainWindow::get_lane() { return center_dial; +} + +Object* MainWindow::get_object() +{ + return object; } \ No newline at end of file diff --git a/digital_cluster/src/object.cpp b/digital_cluster/src/object.cpp index 40247746..33d35fe6 100644 --- a/digital_cluster/src/object.cpp +++ b/digital_cluster/src/object.cpp @@ -1,133 +1,48 @@ #include "object.h" -Object::Object(QWidget *parent) +Object::Object(QWidget *parent): QWidget(parent) { - // QString path = QCoreApplication::applicationDirPath(); - // digital_path = QDir(path).filePath("../fonts_icon/warning1.png"); - // digital_path = QDir::cleanPath(digital_path); - set_object(1); + QString path = QCoreApplication::applicationDirPath(); + speed50_path = QDir(path).filePath("../fonts_icon/speed50.png"); + speed50_path = QDir::cleanPath(speed50_path); + speed80_path = QDir(path).filePath("../fonts_icon/speed80.png"); + speed80_path = QDir::cleanPath(speed80_path); + object = 0; + set_object(1, "jetracer/speed_50"); } -// void Object::showObjectPopup() -// { -// if (!popup && object > 3) -// { -// popup = new QWidget(this); -// popup->setAttribute(Qt::WA_DeleteOnClose); -// popup->setStyleSheet("background-color: transparent;"); -// QHBoxLayout* layout = new QHBoxLayout(popup); - -// QLabel* textLabel = new QLabel("Colision danger", popup); -// textLabel->setStyleSheet("color: gray; font-size: 18px;"); -// layout->addWidget(textLabel, 0, Qt::AlignCenter); -// popup->setLayout(layout); -// popup->adjustSize(); -// popup->move((width() - popup->width()) / 2, 0); - -// opacityEffect = new QGraphicsOpacityEffect(popup); -// opacityEffect->setOpacity(0.0); -// popup->setGraphicsEffect(opacityEffect); -// popup->show(); - -// QPropertyAnimation* fadeIn = new QPropertyAnimation(opacityEffect, -// "opacity"); fadeIn->setDuration(300); fadeIn->setStartValue(0.0); -// fadeIn->setEndValue(1.0); -// fadeIn->setEasingCurve(QEasingCurve::InOutSine); -// fadeIn->start(QAbstractAnimation::DeleteWhenStopped); -// } -// if (lane == 0 || object > 3) -// { -// QPixmap warningIcon(digital_path); -// int size = 35; -// int x = (width() - size) / 2; -// int y = (height() - size) / 2; - -// painter.drawPixmap(x, y, warningIcon.scaled(size, size, -// Qt::KeepAspectRatio, Qt::SmoothTransformation)); -// } -// } - -// void Object::setting_pens(int i, QPainter *painter) -// { -// QColor rectColor = (objects[i] > 10) ? Qt::darkRed : Qt::darkGray; -// // std::cout << "Object " << i << ": " << objects[i] << std::endl; -// // std::cout << "Color: " << rectColor.red() << std::endl; -// QPen rectPen(rectColor, 5); -// QBrush rectBrush(rectColor, Qt::SolidPattern); -// painter->setPen(rectPen); -// painter->setBrush(rectBrush); -// // painter.setOpacity(1.0); -// } - void Object::paintEvent(QPaintEvent *event) { -// Lane::paintEvent(event); -// QPainter painter(this); -// painter.setRenderHint(QPainter::Antialiasing); -// int BottomLeft = width() * 0.35; -// int BottomRight = width() * 0.65; -// int TopLeft = width() * 0.4; -// int TopRight = width() * 0.6; - -// int rectWidth = 9; -// int margin = 10; -// int centerX = (width() - rectWidth) / 2; - -// if (objects[0] != 0) -// { -// setting_pens(0, &painter); -// painter.drawLine(QPoint(BottomLeft, height() * 0.15), QPoint(TopLeft - margin, height() * 0.1)); -// } -// if (objects[1] != 0) -// { -// setting_pens(1, &painter); -// painter.drawLine(QPoint(centerX, height() * 0.05), QPoint((width() + rectWidth) / 2, height() * 0.05)); -// } -// if (objects[2] != 0) -// { -// setting_pens(2, &painter); -// painter.drawLine(QPoint(BottomRight, height() * 0.15), QPoint(TopRight + margin, height() * 0.1)); -// } + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + QPixmap pixmap; + if (object == 11) { + pixmap.load(speed50_path); + } else if (object == 1) { + pixmap.load(speed80_path); + } + if (!pixmap.isNull()) { + QPixmap scaled = pixmap.scaled(60, 60, Qt::KeepAspectRatio, Qt::SmoothTransformation); + painter.drawPixmap(0, 0, scaled); // margin + } + else { + std::cout << "Object: no image found" << std::endl; + } } //parsing -void Object::set_object(int i) +void Object::set_object(int i, const QString &topicName) { - if (object == i or (i != 1 && i != 2)) + if (i != 1) return; - object = i; - std::cout << "Object: " << object << std::endl; - // objects.clear(); - // objects.resize(3, 0); - // objects[0] = i % 10; // left - // objects[1] = (i / 10) % 10; // center - // objects[2] = (i / 100) % 10; // right - // std::cout << "Object: " << objects[0] << " " << objects[1] << " " << objects[2] << std::endl; - // update(); + if (topicName == "jetracer/speed_50") { + object = i + 10; + } else { + object = i; + } + update(); } -// // std::vector temp(3, 0); -// // int j = -1; -// // for (int i = 0; i < str.size(); i++) -// // { -// // if (str[i] == ',' || i == str.size() - 1) -// // { -// // int n = i; -// // while (n - 1 >= 0 && str[n - 1] != ',') -// // n--; -// // if (i == str.size() - 1) -// // i++; -// // temp[++j] = std::stoi(str.substr(n, i - n)); -// // } -// // } - -// // if (objects == temp) -// // return; -// // objects = temp; -// // std::cout << "Object: " << objects[0] << " " << objects[1] << " " << objects[2] << std::endl; -// update(); - - int Object::get_object() { return object; @@ -135,11 +50,5 @@ int Object::get_object() Object::~Object() { - // if (popup) - // { - // popup->close(); - // popup->deleteLater(); - // popup = nullptr; - // } std::cout << "Remove Object" << std::endl; } \ No newline at end of file diff --git a/digital_cluster/src/speed.cpp b/digital_cluster/src/speed.cpp index fc812aa8..06b62f36 100644 --- a/digital_cluster/src/speed.cpp +++ b/digital_cluster/src/speed.cpp @@ -5,7 +5,7 @@ Speed::Speed(QWidget *parent) { if (parent) { setMinimumSize(parent->width() * 0.5, parent->height() * 0.7); - setMaximumSize(parent->width() * 0.5, parent->height() * 0.7); + // setMaximumSize(parent->width() * 0.5, parent->height() * 0.7); } QString path = QCoreApplication::applicationDirPath(); QString digital_path = QDir(path).filePath("../fonts_icon/digital-7.ttf"); @@ -62,7 +62,7 @@ void Speed::paint_text(QPainter &painter) { QRect currentTextRect = painter.boundingRect(rect(), Qt::AlignCenter, QString::number(current)); painter.drawText(currentTextRect, Qt::AlignCenter, QString::number(current)); painter.setPen(QPen(Qt::darkCyan)); - painter.setFont(QFont("Calculator", width() / 14)); + painter.setFont(QFont("Calculator", width() / 12)); QRect kmhRect = painter.boundingRect(rect(), Qt::AlignCenter, "km/h"); int yPosition = currentTextRect.bottom() + kmhRect.height(); int xPosition = (width() - kmhRect.width()) / 2; @@ -70,6 +70,7 @@ void Speed::paint_text(QPainter &painter) { } void Speed::set_current(float n) { + current = n * 3.6; float new_target = (current * 270.0f) / max; new_target = std::min(new_target, 270.0f); diff --git a/digital_cluster/src/temperature.cpp b/digital_cluster/src/temperature.cpp index 7abd87f8..ae7a07f9 100644 --- a/digital_cluster/src/temperature.cpp +++ b/digital_cluster/src/temperature.cpp @@ -3,24 +3,31 @@ Temperature::Temperature(QWidget *parent) : QWidget{parent} { - setMinimumSize(parent->width() * 0.2, parent->height() * 0.16); - setMaximumSize(parent->width() * 0.2, parent->height() * 0.16); - main_layout = new QVBoxLayout(this); - main_layout->setSpacing(height() * 0.05); layout = new QHBoxLayout(); - layout->setSpacing(width() * 0.0155); + main_layout = new QVBoxLayout(this); + label = new QLabel(this); + + // setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + label->setAlignment(Qt::AlignCenter); + label->setMinimumWidth(120); + main_layout->setSpacing(6); + main_layout->setContentsMargins(0, 0, 0, 0); // Remove margins for precise control + + layout->setSpacing(2); + layout->setContentsMargins(0, 0, 0, 0); + nb_sections = 6; for (int i = 0; i < nb_sections; ++i) { QWidget *section = new QWidget(this); - section->setFixedHeight(height() * 0.3); - section->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + section->setFixedSize(22, 32); + // section->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); layout->addWidget(section); sections.append(section); } + main_layout->addLayout(layout); - label = new QLabel(this); - set_temperature(50); - setLayout(main_layout); //setting layout for qwidget (no need to delete them) + set_temperature(40); + main_layout->addWidget(label); // Ensure label is added to the layout } Temperature::~Temperature() @@ -31,24 +38,24 @@ Temperature::~Temperature() void Temperature::set_text(int temp) { label->setTextFormat(Qt::RichText); - label->setText("🌡️ " - "" + + label->setText("🌡️ " + "" + QString::number(temp) + "" - " °C"); + " °C"); label->setContentsMargins(5, 0, 0, 0); - label->setAlignment(Qt::AlignTop | Qt::AlignLeft); - main_layout->addWidget(label); + label->setAlignment(Qt::AlignLeft); } void Temperature::set_temperature(int temp) { + temperature = temp; int sections_color = static_cast((temp / 80.0) * nb_sections); for (int i = 0; i < nb_sections; ++i) { if (i < sections_color) { QColor sectionColor; if (temp < 60) { - int green = std::min(255, 60 + (i * (50 / nb_sections))); //from dim cyan to regular cyan - int blue = std::min(255, 80 + (i * (50 / nb_sections))); //dim blueincrease to bright blue + int green = std::min(255, 80 + (i * (50 / nb_sections))); //from dim cyan to regular cyan + int blue = std::min(255, 100 + (i * (50 / nb_sections))); //dim blueincrease to bright blue sectionColor.setRgb(0, green, blue); } else { int red = (i * (200 / nb_sections)); //increase red component @@ -70,6 +77,11 @@ int Temperature::get_nbsections() return nb_sections; } +int Temperature::get_temperature() +{ + return temperature; +} + QVector Temperature::get_sections() { return sections; diff --git a/digital_cluster/test/autonomy.cpp b/digital_cluster/test/autonomy.cpp index 01e38294..6bed20aa 100644 --- a/digital_cluster/test/autonomy.cpp +++ b/digital_cluster/test/autonomy.cpp @@ -22,10 +22,11 @@ class AutonomyT : public testing::Test TEST_F(AutonomyT, TestVar) { - // autonomy->set_autonomy(7); + // autonomy->set_autonomy(8); EXPECT_EQ(autonomy->get_nbsections(), 6); EXPECT_EQ(autonomy->get_sections().size(), 6); EXPECT_TRUE(autonomy->get_layout() != nullptr); EXPECT_TRUE(autonomy->get_mainlayout() != nullptr); EXPECT_TRUE(autonomy->get_label() != nullptr); + // QApplication::quit() } \ No newline at end of file diff --git a/digital_cluster/test/battery.cpp b/digital_cluster/test/battery.cpp index 8780eb60..2b204baa 100644 --- a/digital_cluster/test/battery.cpp +++ b/digital_cluster/test/battery.cpp @@ -44,10 +44,3 @@ TEST_F(BatteryT, TestBatteryState) QApplication::processEvents(); QTest::qWait(500); } - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/digital_cluster/test/lane.cpp b/digital_cluster/test/lane.cpp index b621abee..c96e8f80 100644 --- a/digital_cluster/test/lane.cpp +++ b/digital_cluster/test/lane.cpp @@ -25,7 +25,7 @@ class LaneT : public testing::Test TEST_F(LaneT, TestVar) { lane->set_lane(0); - QTest::qWait(450); + QTest::qWait(500); EXPECT_EQ(lane->get_lane(), 0); EXPECT_NE(lane->get_popup(), nullptr); EXPECT_NEAR(lane->leftOpacity(), 0.0, 0.01); @@ -49,12 +49,12 @@ TEST_F(LaneT, TestVar) TEST_F(LaneT, SetLaneEdgeCases) { lane->set_lane(-1); - QTest::qWait(450); + QTest::qWait(500); EXPECT_NEAR(lane->leftOpacity(), 0.0, 0.01); EXPECT_NEAR(lane->rightOpacity(), 0.0, 0.01); lane->set_lane(std::numeric_limits::max()); // Large value - QTest::qWait(450); + QTest::qWait(500); EXPECT_NEAR(lane->leftOpacity(), 0.0, 0.01); EXPECT_NEAR(lane->rightOpacity(), 0.0, 0.01); } diff --git a/digital_cluster/test/object.cpp b/digital_cluster/test/object.cpp index 858e11fc..91b849c1 100644 --- a/digital_cluster/test/object.cpp +++ b/digital_cluster/test/object.cpp @@ -1,24 +1,35 @@ #include "../include/mainwindow.h" #include -#include #include #include -// class ObjectT : public testing::Test { -// protected: -// MainWindow *mw; -// Object *object; +class ObjectT : public testing::Test { +protected: + MainWindow *mw; + Object *object; -// void SetUp() override { -// mw = new MainWindow(); -// mw->show(); -// object = mw->get_lane(); -// } -// void TearDown() override { delete mw; } -// }; + void SetUp() override { + mw = new MainWindow(); + mw->show(); + object = mw->get_object(); + } + void TearDown() override { delete mw; } +}; -// TEST_F(ObjectT, TestVar) { -// object->set_object("0); -// QTest::qWait(450); -// EXPECT_EQ(object->get_object(), 0); -// } \ No newline at end of file +TEST_F(ObjectT, TestVar) { + object->set_object(1, "jetracer/speed_50"); + EXPECT_EQ(object->get_object(), 11); + + QPixmap pixmap; + QString path = object->get_speed50_path(); + QVERIFY(pixmap.load(path)); + + path = object->get_speed80_path(); + QVERIFY(pixmap.load(path)); + + object->set_object(1, "jetracer/speed_80"); + EXPECT_EQ(object->get_object(), 1); + + object->set_object(2, "jetracer/speed_80"); + EXPECT_EQ(object->get_object(), 1); +} diff --git a/digital_cluster/test/window.cpp b/digital_cluster/test/window.cpp index b0922210..aed23d57 100644 --- a/digital_cluster/test/window.cpp +++ b/digital_cluster/test/window.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "../include/mainwindow.h" @@ -11,7 +12,7 @@ class Window : public testing::Test MainWindow* window; void SetUp() override { // new before each test - window = new MainWindow(); + window = new MainWindow; } void TearDown() override { //delete after each test delete window; @@ -20,10 +21,37 @@ class Window : public testing::Test TEST_F(Window, Mqtt) { - QTest::qWait(1000); - EXPECT_EQ(window->get_client()->state(), QMqttClient::Connected); - //check signal connection const QMetaObject *meta = window->get_client()->metaObject(); EXPECT_TRUE(meta->indexOfSignal("connected()") >= 0); EXPECT_TRUE(meta->indexOfSignal(QMetaObject::normalizedSignature("messageReceived(QByteArray,QMqttTopicName)")) >= 0); -} \ No newline at end of file + + window->get_client()->messageReceived("5", QMqttTopicName("jetracer/speed")); + window->get_client()->messageReceived("1", QMqttTopicName("jetracer/speed_50")); + window->get_client()->messageReceived("80", QMqttTopicName("jetracer/battery")); + window->get_client()->messageReceived("35", QMqttTopicName("jetracer/temperature")); + window->get_client()->messageReceived("8", QMqttTopicName("jetracer/autonomy")); + window->get_client()->messageReceived("82", QMqttTopicName("jetracer/lane_touch")); + + EXPECT_EQ(window->get_speed()->get_current(), static_cast(5 * 3.6)); + EXPECT_EQ(window->get_battery()->get_current(), 80); + EXPECT_EQ(window->get_temperature()->get_temperature(), 35); + EXPECT_EQ(window->get_autonomy()->get_autonomy(), 8); + EXPECT_EQ(window->get_lane()->get_lane(), 82); + EXPECT_EQ(window->get_object()->get_object(), 11); // speed_50 + + window->get_client()->messageReceived("1", QMqttTopicName("jetracer/speed_80")); + EXPECT_EQ(window->get_object()->get_object(), 1); // speed_80 + +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + + // Gracefully exit Qt after tests are done + QTimer::singleShot(0, &app, &QApplication::quit); + + return ret; +} diff --git a/doxyfile.css b/doxyfile.css deleted file mode 100644 index 3b90a029..00000000 --- a/doxyfile.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 10px; - padding: 10px; - margin-left: 100px; - margin-right: 100px; - font-family: Arial, sans-serif; -} - -/* img { - display: block; - margin-left: auto; - margin-right: auto; -} */ diff --git a/doxyfile_readme.md b/doxyfile_readme.md deleted file mode 100644 index bafb4144..00000000 --- a/doxyfile_readme.md +++ /dev/null @@ -1,54 +0,0 @@ - -## Project Architecture - -We implemented **Tensor Regulators** so that the servo motors of our **JetRacer** and our **Raspberry Pi** have the neccesary Voltage to function properly. - -\image html ADR/structure.png "Project Structure" width=50% - -## Communication Structure - -The Jetson communicates with the Raspberry via **mqtt**, with a **Cloud Broker** (HiveMQ) and a local Broker. We chose mqtt since its latency is pretty small, and it seems to get the job done well. The Raspberry is the one to display the information in the **LCD**, with informations of Jetson's temperature and battery, and also information of the speed directly from the speed sensor, with arduino via CAN. - -\image html ADR/mqtt.png "Communication Structure" width=50% - -## CAN Communication: Raspberry Pi and Arduino -This project demonstrates how to establish a **CAN (Controller Area Network)** communication between an **Arduino** and a **Raspberry Pi**. The Arduino reads speed data from a sensor, calculates the speed, and sends it to the Raspberry Pi over a CAN bus using an **MCP2515 CAN module**. The Raspberry Pi processes and displays the received data using Python. - -## Overview -Controller Area Network (CAN) is a robust communication protocol commonly used in automotive and industrial applications. This project involves: - Using an Arduino to send speed data via a CAN bus. - Configuring a Raspberry Pi to receive and process the data using Python. - -## Hardware Requirements -- **Arduino Uno/Nano/Mega** or similar. -- **Raspberry Pi** (any version with SPI support, e.g., Raspberry Pi 4). -- **MCP2515 CAN Module** (two units: one for the Arduino and one for the Raspberry Pi). -- Speed sensor (connected to Arduino). ---- - -### Raspberry Pi to MCP2515 CAN Module Connection -| MCP2515 Pin | Raspberry Pi GPIO Pin | -|---------------|------------------------| -| **VCC** | 3.3V or 5V | -| **GND** | GND | -| **CS** | GPIO 8 (SPI0_CS0) | -| **SCK** | GPIO 11 (SPI0_SCLK) | -| **MOSI** | GPIO 10 (SPI0_MOSI) | -| **MISO** | GPIO 9 (SPI0_MISO) | -| **INT** | GPIO 25 (optional) | - ---- - -### Arduino Setup -1. Install the **MCP_CAN** library in the Arduino IDE: - - Go to **Sketch > Include Library > Manage Libraries**. - - Search for `MCP_CAN`, CAN-BUS Shield and install it. - -2. Upload the Arduino code (provided in our arduino directory). - -3. Connect the speed sensor to the Arduino and verify it calculates speed correctly. - ---- - -### Raspberry Pi Setup -(https://forums.raspberrypi.com/viewtopic.php?t=353451) diff --git a/Doxyfile b/doxyfiles/Doxyfile similarity index 99% rename from Doxyfile rename to doxyfiles/Doxyfile index f1f1064f..74ff4a22 100644 --- a/Doxyfile +++ b/doxyfiles/Doxyfile @@ -41,7 +41,7 @@ DOXYFILE_ENCODING = UTF-8 # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Instrument Cluster" +PROJECT_NAME = "Instrument doxyCluster" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This @@ -943,13 +943,16 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = \ - digital_cluster/src/ \ - digital_cluster/README.md \ - digital_cluster/include/ \ - digital_cluster/test/ \ - ADR/ \ - doxyfile_readme.md \ +INPUT = ../digital_cluster/src \ + ../digital_cluster/test \ + ../digital_cluster/include \ + ../digital_cluster/results \ + ../digital_cluster/README.md \ + ../ADR/ \ + ../README.md \ + ../arduino/ + + # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1060,7 +1063,8 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = ADR +IMAGE_PATH = ../ADR \ + ../digital_cluster/results/ \ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -1121,7 +1125,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = doxyfile_readme.md +USE_MDFILE_AS_MAINPAGE = ../README.md # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common diff --git a/doxyfiles/doxyfile.css b/doxyfiles/doxyfile.css new file mode 100644 index 00000000..f30d382b --- /dev/null +++ b/doxyfiles/doxyfile.css @@ -0,0 +1,14 @@ +body { + margin: 10px; + padding: 10px; + margin-left: 100px; + margin-right: 100px; + font-family: Arial, sans-serif; +} + +img { + max-width: 100%; /* ensures images never overflow container */ + height: auto; /* keeps aspect ratio */ + display: block; + margin: 10px auto; /* centers the image with margins */ +}