├── .github └── workflows │ ├── ci.yml │ └── pre-commit.yml ├── .gitignore ├── .pre-commit-config.yaml ├── 3rdparty ├── LICENSE_1_0.txt └── catch.hpp ├── CMakeLists.txt ├── CONTRIBUTING.md ├── COPYING ├── README.md ├── assets ├── appinfo.rc ├── icon │ ├── appicon.ico │ ├── gen_icns.sh │ ├── gen_icon.sh │ ├── icon-1024.png │ ├── icon-128.png │ ├── icon-144.png │ ├── icon-16.png │ ├── icon-180.png │ ├── icon-192.png │ ├── icon-24.png │ ├── icon-256.png │ ├── icon-32.png │ ├── icon-48.png │ ├── icon-512.png │ ├── icon-64.png │ ├── icon-inkscape.svg │ ├── icon.svg │ └── valeronoi.icns ├── screenshot.png └── valeronoi.desktop ├── cmake-format.py ├── src ├── config.h.in ├── gui │ ├── dialog │ │ ├── about.cpp │ │ ├── about.h │ │ ├── about.ui │ │ ├── export.cpp │ │ ├── export.h │ │ ├── export.ui │ │ ├── log.cpp │ │ ├── log.h │ │ ├── log.ui │ │ ├── robot_config.cpp │ │ ├── robot_config.h │ │ ├── robot_config.ui │ │ ├── settings.cpp │ │ ├── settings.h │ │ ├── settings.ui │ │ ├── update.cpp │ │ ├── update.h │ │ └── update.ui │ ├── graphics_item │ │ ├── entity_item.cpp │ │ ├── entity_item.h │ │ ├── floor_item.cpp │ │ ├── floor_item.h │ │ ├── map_based_item.cpp │ │ ├── map_based_item.h │ │ ├── map_item.cpp │ │ ├── map_item.h │ │ ├── measurement_item.cpp │ │ └── measurement_item.h │ └── widget │ │ ├── display_widget.cpp │ │ └── display_widget.h ├── main.cpp ├── res │ ├── SourceCodePro-Regular.otf │ ├── colormaps.json │ └── valeronoi.png ├── robot │ ├── api │ │ ├── sse.cpp │ │ ├── sse.h │ │ ├── valetudo_v2.cpp │ │ └── valetudo_v2.h │ ├── commands.h │ ├── connection_configuration.cpp │ ├── connection_configuration.h │ ├── robot.cpp │ ├── robot.h │ ├── robot_information.cpp │ ├── robot_information.h │ ├── wifi_information.cpp │ └── wifi_information.h ├── state │ ├── measurements.cpp │ ├── measurements.h │ ├── robot_map.cpp │ ├── robot_map.h │ ├── state.cpp │ ├── state.h │ ├── wifi_collection.cpp │ └── wifi_collection.h ├── util │ ├── colormap.h │ ├── compat.h │ ├── log_helper.cpp │ ├── log_helper.h │ ├── segment_generator.cpp │ └── segment_generator.h ├── valeronoi.cpp ├── valeronoi.h ├── valeronoi.qrc └── valeronoi.ui ├── tests ├── test_colormap.cpp └── test_main.cpp └── tools ├── pkgbuild └── valeronoi-git │ └── PKGBUILD └── windows ├── copy_dlls.py └── install.nsi /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | release: 6 | types: 7 | - "published" 8 | 9 | jobs: 10 | ci-macos: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - os: macos-14 16 | qt: "6.8.0" 17 | cgal: "6.0.1" 18 | variant: "current" 19 | - os: macos-13 20 | qt: "6.5.3" 21 | cgal: "5.6.2" 22 | variant: "legacy" 23 | name: CI macOS ${{ matrix.variant }} (${{ matrix.os }}/Qt ${{ matrix.qt }}/CGAL ${{ matrix.cgal }}) 24 | runs-on: ${{ matrix.os }} 25 | env: 26 | HOMEBREW_NO_ANALYTICS: 1 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Install dependencies 30 | run: | 31 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 32 | brew analytics off 33 | brew install cmake create-dmg mpfr boost openssl@1.1 34 | - name: Install Qt 35 | uses: jurplel/install-qt-action@v4 36 | with: 37 | version: ${{ matrix.qt }} 38 | - name: Install CGAL 39 | env: 40 | CGAL_VERSION: ${{ matrix.cgal }} 41 | run: | 42 | mkdir /tmp/cgal 43 | cd /tmp/cgal 44 | wget https://github.com/CGAL/cgal/releases/download/v$CGAL_VERSION/CGAL-$CGAL_VERSION.tar.xz 45 | tar xf CGAL-$CGAL_VERSION.tar.xz 46 | mkdir CGAL-$CGAL_VERSION/build 47 | cd CGAL-$CGAL_VERSION/build 48 | cmake .. -DCMAKE_BUILD_TYPE=Release 49 | sudo make install 50 | - name: Build on macOS 51 | env: 52 | OS: ${{ matrix.os }} 53 | QT_VERSION: ${{ matrix.qt }} 54 | VARIANT: ${{ matrix.variant }} 55 | run: | 56 | mkdir build 57 | cd build 58 | cmake .. -DCMAKE_BUILD_TYPE=Release -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 59 | make -j 60 | ./valeronoi-tests 61 | macdeployqt valeronoi.app 62 | mkdir dist 63 | mv valeronoi.app dist/Valeronoi.app 64 | create-dmg --hdiutil-verbose --no-internet-enable --skip-jenkins --volname "Valeronoi Installer" --volicon "../assets/icon/valeronoi.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "Valeronoi.app" 200 190 --hide-extension "Valeronoi.app" --app-drop-link 600 185 "Valeronoi-macOS-Intel-$VARIANT.dmg" "dist/" 65 | - name: Upload artifact 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: valeronoi-macos-${{ matrix.variant }} 69 | path: build/Valeronoi-macOS-Intel-${{ matrix.variant }}.dmg 70 | - name: Upload release binary 71 | if: ${{ github.event_name == 'release' }} 72 | uses: alexellis/upload-assets@0.4.1 73 | env: 74 | GITHUB_TOKEN: ${{ github.token }} 75 | with: 76 | asset_paths: '["build/Valeronoi-macOS-Intel-${{ matrix.variant }}.dmg"]' 77 | ci-windows: 78 | strategy: 79 | fail-fast: false 80 | matrix: 81 | sys: [mingw64] # mingw32, ucrt64 - Experimental 82 | name: CI Windows/MSYS2 83 | runs-on: windows-2022 84 | steps: 85 | - uses: actions/checkout@v4 86 | - name: Install msys2 87 | uses: msys2/setup-msys2@v2 88 | with: 89 | msystem: ${{ matrix.sys }} 90 | install: >- 91 | base-devel 92 | git 93 | make 94 | zip 95 | pacboy: >- 96 | cmake:p 97 | ninja:p 98 | gcc:p 99 | cc:p 100 | qt6-tools:p 101 | qt6-base:p 102 | qt6-imageformats:p 103 | qt6-svg:p 104 | openssl:p 105 | cgal:p 106 | eigen3:p 107 | intel-tbb:p 108 | nsis:p 109 | location: C:\msys2 110 | - name: Build on Windows 111 | shell: msys2 {0} 112 | run: | 113 | mkdir build 114 | cd build 115 | cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release 116 | cmake --build . 117 | ./valeronoi-tests.exe 118 | mkdir dist 119 | mv valeronoi.exe dist/valeronoi.exe 120 | cp ../COPYING dist 121 | cp ../assets/appinfo.rc dist 122 | mkdir dist/icon 123 | cp ../assets/icon/appicon.ico dist/icon 124 | cd dist 125 | python ../../tools/windows/copy_dlls.py 126 | zip -r ../Valeronoi-Windows-portable-x86_64.zip . 127 | - name: Upload portable archive 128 | uses: actions/upload-artifact@v4 129 | with: 130 | name: valeronoi-windows-portable-${{ matrix.sys }} 131 | path: build/dist 132 | - name: Upload release portable archive 133 | if: ${{ github.event_name == 'release' }} 134 | uses: alexellis/upload-assets@0.4.1 135 | env: 136 | GITHUB_TOKEN: ${{ github.token }} 137 | with: 138 | asset_paths: '["build/Valeronoi-Windows-portable-x86_64.zip"]' 139 | - name: Build installer 140 | shell: msys2 {0} 141 | run: | 142 | cd build/dist 143 | cp ../../tools/windows/install.nsi . 144 | makensis install.nsi 145 | - name: Upload installer 146 | uses: actions/upload-artifact@v4 147 | with: 148 | name: valeronoi-windows-installer-${{ matrix.sys }} 149 | path: build/dist/Valeronoi-installer-x86_64.exe 150 | - name: Upload release portable archive 151 | if: ${{ github.event_name == 'release' }} 152 | uses: alexellis/upload-assets@0.4.1 153 | env: 154 | GITHUB_TOKEN: ${{ github.token }} 155 | with: 156 | asset_paths: '["build/dist/Valeronoi-installer-x86_64.exe"]' 157 | ci-linux: 158 | strategy: 159 | fail-fast: false 160 | matrix: 161 | include: 162 | - os: ubuntu-24.04 163 | qt: "6.8.0" 164 | cgal: "6.0.1" 165 | variant: "current" 166 | - os: ubuntu-22.04 167 | qt: "6.5.3" 168 | cgal: "5.6.2" 169 | variant: "legacy" 170 | name: CI Linux/AppImage ${{ matrix.variant }} (${{ matrix.os }}/Qt ${{ matrix.qt }}/CGAL ${{ matrix.cgal }}) 171 | runs-on: ${{ matrix.os }} 172 | steps: 173 | - uses: actions/checkout@v4 174 | - name: Install Qt 175 | uses: jurplel/install-qt-action@v4 176 | with: 177 | version: ${{ matrix.qt }} 178 | - name: Install dependencies 179 | run: | 180 | sudo apt-get install libfuse2 openssl libxcb-xkb-dev libxcb-cursor-dev libxkbcommon-x11-0 cmake xz-utils libmpfr-dev libboost-dev -y 181 | - name: Install CGAL 182 | env: 183 | CGAL_VERSION: ${{ matrix.cgal }} 184 | run: | 185 | mkdir -p /tmp/cgal 186 | cd /tmp/cgal 187 | wget https://github.com/CGAL/cgal/releases/download/v$CGAL_VERSION/CGAL-$CGAL_VERSION.tar.xz 188 | tar xf CGAL-$CGAL_VERSION.tar.xz 189 | mkdir CGAL-$CGAL_VERSION/build 190 | cd CGAL-$CGAL_VERSION/build 191 | cmake .. -DCMAKE_BUILD_TYPE=Release 192 | sudo make install 193 | - name: Build on Linux 194 | env: 195 | OS: ${{ matrix.os }} 196 | QT_VERSION: ${{ matrix.qt }} 197 | VARIANT: ${{ matrix.variant }} 198 | run: | 199 | mkdir build 200 | cd build 201 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release 202 | make -j install DESTDIR=AppDir 203 | ./valeronoi-tests 204 | mkdir tools 205 | cd tools 206 | wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage 207 | wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage 208 | chmod +x * 209 | cd .. 210 | export PATH=$Qt6_DIR/bin:$PATH 211 | tools/linuxdeploy-x86_64.AppImage --appdir AppDir -i ../assets/icon/icon-256.png -d AppDir/usr/share/applications/valeronoi.desktop --output appimage --plugin qt 212 | mv Valeronoi-*.AppImage "Valeronoi-${VARIANT}-x86_64.AppImage" 213 | shell: bash 214 | - name: Upload artifact 215 | uses: actions/upload-artifact@v4 216 | with: 217 | name: valeronoi-linux-appimage-${{ matrix.variant }} 218 | path: build/Valeronoi-${{ matrix.variant }}-x86_64.AppImage 219 | - name: Upload release AppImage 220 | if: ${{ github.event_name == 'release' }} 221 | uses: alexellis/upload-assets@0.4.1 222 | env: 223 | GITHUB_TOKEN: ${{ github.token }} 224 | with: 225 | asset_paths: '["build/Valeronoi-${{ matrix.variant }}-x86_64.AppImage"]' 226 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: [push] 4 | 5 | jobs: 6 | pre-commit: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-python@v5 11 | - uses: actions/setup-go@v5 12 | with: 13 | go-version: "stable" 14 | - name: Install dependencies 15 | run: | 16 | sudo apt-get install cppcheck -y 17 | go install mvdan.cc/sh/v3/cmd/shfmt@latest 18 | - uses: pre-commit/action@v3.0.1 19 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | repos: 3 | - repo: https://github.com/timothycrosley/isort 4 | rev: 5.13.2 5 | hooks: 6 | - id: isort 7 | - repo: https://github.com/ambv/black 8 | rev: 24.10.0 9 | hooks: 10 | - id: black 11 | - repo: https://github.com/pre-commit/pre-commit-hooks 12 | rev: v5.0.0 13 | hooks: 14 | - id: check-merge-conflict 15 | - id: check-yaml 16 | - id: check-json 17 | - id: check-xml 18 | - id: destroyed-symlinks 19 | - id: end-of-file-fixer 20 | - id: mixed-line-ending 21 | - id: trailing-whitespace 22 | - id: fix-byte-order-marker 23 | - repo: https://github.com/pre-commit/mirrors-prettier 24 | rev: v4.0.0-alpha.8 25 | hooks: 26 | - id: prettier 27 | - repo: https://github.com/jumanjihouse/pre-commit-hooks 28 | rev: 3.0.0 29 | hooks: 30 | - id: shellcheck 31 | - id: shfmt 32 | - id: script-must-have-extension 33 | - repo: https://gitlab.com/daverona/pre-commit/cpp 34 | rev: 0.8.0 35 | hooks: 36 | - id: clang-format 37 | - repo: https://github.com/cheshirekow/cmake-format-precommit 38 | rev: v0.6.13 39 | hooks: 40 | - id: cmake-format 41 | - id: cmake-lint 42 | 43 | exclude: (3rdparty/.*) 44 | -------------------------------------------------------------------------------- /3rdparty/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project( 3 | Valeronoi 4 | VERSION 0.2.2 5 | LANGUAGES CXX 6 | ) 7 | 8 | set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE TRUE) 9 | 10 | execute_process( 11 | COMMAND git log -1 --format=%h 12 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 13 | OUTPUT_VARIABLE GIT_COMMIT 14 | OUTPUT_STRIP_TRAILING_WHITESPACE 15 | ) 16 | message(STATUS "Git commit is ${GIT_COMMIT}") 17 | 18 | find_package(Threads REQUIRED) 19 | find_package(OpenSSL REQUIRED) 20 | find_package(CGAL REQUIRED) 21 | 22 | find_package( 23 | Qt6 24 | COMPONENTS Core Widgets Network Svg OpenGLWidgets 25 | REQUIRED 26 | ) 27 | include(CheckIPOSupported) 28 | 29 | check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) 30 | if(ipo_supported) 31 | message(STATUS "Enabling interprocedural optimization") 32 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 33 | else() 34 | message(STATUS "Interprocedural optimization not supported: ${ipo_error}") 35 | endif() 36 | 37 | set(CMAKE_CXX_STANDARD 17) 38 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 39 | 40 | include_directories(. "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/3rdparty") 41 | 42 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 43 | message(STATUS "Setting build type to 'Release' as none was specified.") 44 | set(CMAKE_BUILD_TYPE 45 | "Release" 46 | CACHE STRING "Choose the type of build." FORCE 47 | ) 48 | endif() 49 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 50 | 51 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES 52 | "Clang" 53 | ) 54 | add_compile_options(-Wall -Wextra -pedantic -Werror) 55 | 56 | option(COMPILE_NATIVE 57 | "Compile for 'native' architecture (non-portable, but faster binary)" 58 | OFF 59 | ) 60 | if(COMPILE_NATIVE) 61 | message(STATUS "Building binary for native architecture") 62 | add_compile_options(-march=native) 63 | endif() 64 | endif() 65 | 66 | configure_file(src/config.h.in config.h) 67 | 68 | set(CMAKE_AUTOGEN_PARALLEL 1) 69 | set(CMAKE_AUTOMOC ON) 70 | set(CMAKE_AUTORCC ON) 71 | set(CMAKE_AUTOUIC ON) 72 | 73 | set(SOURCE_FILES 74 | src/main.cpp 75 | src/valeronoi.ui 76 | src/valeronoi.qrc 77 | src/valeronoi.cpp 78 | src/util/segment_generator.cpp 79 | src/util/log_helper.cpp 80 | src/robot/robot.cpp 81 | src/robot/connection_configuration.cpp 82 | src/robot/robot_information.cpp 83 | src/robot/wifi_information.cpp 84 | src/robot/api/sse.cpp 85 | src/robot/api/valetudo_v2.cpp 86 | src/state/state.cpp 87 | src/state/robot_map.cpp 88 | src/state/measurements.cpp 89 | src/state/wifi_collection.cpp 90 | src/gui/dialog/robot_config.ui 91 | src/gui/dialog/robot_config.cpp 92 | src/gui/dialog/about.ui 93 | src/gui/dialog/about.cpp 94 | src/gui/dialog/update.ui 95 | src/gui/dialog/update.cpp 96 | src/gui/dialog/settings.ui 97 | src/gui/dialog/settings.cpp 98 | src/gui/dialog/export.ui 99 | src/gui/dialog/export.cpp 100 | src/gui/dialog/log.ui 101 | src/gui/dialog/log.cpp 102 | src/gui/widget/display_widget.cpp 103 | src/gui/graphics_item/map_based_item.cpp 104 | src/gui/graphics_item/map_item.cpp 105 | src/gui/graphics_item/floor_item.cpp 106 | src/gui/graphics_item/entity_item.cpp 107 | src/gui/graphics_item/measurement_item.cpp 108 | ) 109 | 110 | set(TEST_FILES tests/test_main.cpp tests/test_colormap.cpp) 111 | 112 | set(MACOSX_BUNDLE_BUNDLE_NAME "Valeronoi") 113 | set(MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION}") 114 | set(MACOSX_BUNDLE_COPYRIGHT 115 | "2021-2024 Christian Friedrich Coors, released under GPLv3" 116 | ) 117 | set(MACOSX_BUNDLE_ICON_FILE "valeronoi.icns") 118 | set(MACOSX_BUNDLE_LONG_VERSION_STRING "${CMAKE_PROJECT_VERSION}") 119 | set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION}") 120 | 121 | add_executable( 122 | valeronoi WIN32 MACOSX_BUNDLE 123 | ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/assets/icon/valeronoi.icns 124 | ${CMAKE_SOURCE_DIR}/assets/appinfo.rc 125 | ) 126 | target_compile_options(valeronoi PRIVATE -fPIC) 127 | add_executable(valeronoi-tests ${TEST_FILES}) 128 | 129 | target_link_libraries( 130 | valeronoi 131 | PRIVATE Threads::Threads 132 | Qt6::Core 133 | Qt6::Widgets 134 | Qt6::Network 135 | Qt6::Svg 136 | Qt6::OpenGLWidgets 137 | CGAL::CGAL 138 | OpenSSL::SSL 139 | ) 140 | 141 | if(APPLE) 142 | install(TARGETS valeronoi BUNDLE DESTINATION Valeronoi) 143 | set_source_files_properties( 144 | ${CMAKE_SOURCE_DIR}/assets/icon/valeronoi.icns 145 | PROPERTIES MACOSX_PACKAGE_LOCATION "Resources" 146 | ) 147 | elseif(NOT WIN32) 148 | install( 149 | TARGETS valeronoi 150 | RUNTIME DESTINATION bin 151 | LIBRARY DESTINATION lib 152 | ARCHIVE DESTINATION lib/static 153 | ) 154 | install(FILES ${CMAKE_SOURCE_DIR}/assets/valeronoi.desktop 155 | DESTINATION share/applications 156 | ) 157 | install(FILES ${CMAKE_SOURCE_DIR}/COPYING 158 | DESTINATION share/licenses/valeronoi/LICENSE 159 | ) 160 | foreach( 161 | icon_size 162 | 16 163 | 24 164 | 32 165 | 48 166 | 64 167 | 128 168 | 144 169 | 180 170 | 192 171 | 256 172 | 512 173 | 1024 174 | ) 175 | install( 176 | FILES ${CMAKE_SOURCE_DIR}/assets/icon/icon-${icon_size}.png 177 | DESTINATION share/icons/hicolor/${icon_size}x${icon_size}/apps 178 | RENAME valeronoi.png 179 | ) 180 | endforeach() 181 | install( 182 | FILES ${CMAKE_SOURCE_DIR}/assets/icon/icon.svg 183 | DESTINATION share/icons/hicolor/scalable/apps 184 | RENAME valeronoi.svg 185 | ) 186 | endif() 187 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions to Valeronoi are welcome! Please note that this project is licensed under the GPLv3, so your contributions will also fall under that license. 4 | 5 | ## Development 6 | 7 | Make sure to follow the existing project structure. The Qt-Dialog files (\*.ui) are generated using the Qt 5 Designer, so they should be compatible with both Qt 5 and Qt 6. This project has a [pre-commit](https://pre-commit.com/) config, so make sure any PRs pass the pre-commit checks. 8 | 9 | By default, IPO Optimizations are enabled. This can lead to highly increased linking times. During development it is advised to disable IPO (e.g. remove `set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)` from CMakeLists.txt). 10 | 11 | ### Coding conventions 12 | 13 | - Include guards: `#ifndef VALERONOI_$PATH$_$FILENAME$_H` 14 | - Variables: `snake_case` 15 | - Members: `m_name` 16 | - Qt Signals: `signal_name` 17 | - Qt Slots: `slot_name` 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Valeronoi 2 | 3 | Valeronoi (Valetudo + Voronoi) is a companion for [Valetudo](https://valetudo.cloud) for generating WiFi signal strength maps. It visualizes them using a [Voronoi diagram](https://en.wikipedia.org/wiki/Voronoi_diagram). 4 | 5 | ![Screenshot](assets/screenshot.png) 6 | 7 | ## Installation 8 | 9 | Binary distributions are available for Linux, macOS and Windows (x86_64). They can be found in the [releases](https://github.com/ccoors/Valeronoi/releases). 10 | 11 | - Linux: [AppImages](https://appimage.org/). The "current" AppImage is built on Ubuntu 24.04 LTS (Noble Numbat) and Qt 6, so it probably won't run on anything older than that. In that case you may want to test the "legacy" AppImage, which is built on Ubuntu 22.04 LTS (Jammy Jellyfish) instead. 12 | - macOS: "current" is built on macOS 14 (Sonoma) on Intel Macs. It also works with Rosetta 2 on ARM-based Macs. "legacy" is built on macOS 13 (Ventura). 13 | - Windows: Compiled on Windows with mingw64. Available as an installer and a portable zip. Using the uninstaller generated by the installer also removes the registry keys used for the settings stored by Valeronoi. 14 | 15 | ## Basic Usage 16 | 17 | 1. Make sure you have a supported robot running a recent Valetudo version (API v2). Having persistent maps is highly recommended. 18 | 2. Set up the robot connection in Valeronoi (Robot -> Setup) 19 | 3. Connect Valeronoi to the robot using the "Connect" button 20 | 4. Verify that the complete, correct map is displayed (!) See below for an explanation. 21 | 5. Start recording WiFi measurements by clicking the "Begin recording" button 22 | 6. Start a cleanup, either using the controls in the "Control" tab or Valetudo/Home Assistant/... If you don't have persistent maps, make sure to not start a full cleanup! Major map updates can not be handled in Valeronoi. 23 | 7. While cleaning, watch the map update 24 | 8. After the robot returned to the dock, stop the recording and/or disconnect Valeronoi 25 | 26 | ### Persistent maps 27 | 28 | If you have a robot that does not support persistent maps (Roborock V1) or don't have that feature enabled, make sure to only record when doing partial cleanups, as a new map will always be generated on a full cleanup. Major map changes during recording can change the internal map coordinates and mess up the recording. 29 | 30 | ## Support 31 | 32 | ### Graphics 33 | 34 | If the graphics are slow and unresponsive, try the following things in the "Display" tab: 35 | 36 | - Disable drawing the floor 37 | - Disabling the "Restrict to ..." checkboxes 38 | - Increase the "Simplify" slider 39 | - Enabling drawing using OpenGL 40 | 41 | **Warning:** Using OpenGL may increase or decrease performance, depending on your system. Enabling OpenGL can also lead to various issues, like garbage text or inverted graphics. Valeronoi requests OpenGL 3.2, which should be available on most systems, but may not be available in VMs or Remote Desktop scenarios. 42 | 43 | ## Building from source 44 | 45 | Install the required Libraries using the method of your choice: 46 | 47 | - Qt 6 48 | - CGAL (4.x/5.x/6.x should work) 49 | 50 | You also need a C++-Compiler capable of C++17. g++ 7 or later works. 51 | 52 | Then use the CMake-Project as you would in any other project. 53 | 54 | ``` 55 | mkdir build && cd build 56 | cmake .. -DCMAKE_BUILD_TYPE=Release 57 | make -j 58 | ``` 59 | 60 | ### Arch Linux 61 | 62 | A `PKGBUILD` for the `valeronoi-git` package is provided in `tools/pkgbuild/valeronoi-git`. To build and install it run `makepkg -si`. 63 | 64 | ## License 65 | 66 | GPLv3 67 | -------------------------------------------------------------------------------- /assets/appinfo.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "icon/appicon.ico" 2 | -------------------------------------------------------------------------------- /assets/icon/appicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/appicon.ico -------------------------------------------------------------------------------- /assets/icon/gen_icns.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir valeronoi.iconset 4 | sips -z 16 16 icon-1024.png --out valeronoi.iconset/icon_16x16.png 5 | sips -z 32 32 icon-1024.png --out valeronoi.iconset/icon_16x16@2x.png 6 | sips -z 32 32 icon-1024.png --out valeronoi.iconset/icon_32x32.png 7 | sips -z 64 64 icon-1024.png --out valeronoi.iconset/icon_32x32@2x.png 8 | sips -z 128 128 icon-1024.png --out valeronoi.iconset/icon_128x128.png 9 | sips -z 256 256 icon-1024.png --out valeronoi.iconset/icon_128x128@2x.png 10 | sips -z 256 256 icon-1024.png --out valeronoi.iconset/icon_256x256.png 11 | sips -z 512 512 icon-1024.png --out valeronoi.iconset/icon_256x256@2x.png 12 | sips -z 512 512 icon-1024.png --out valeronoi.iconset/icon_512x512.png 13 | cp icon-1024.png valeronoi.iconset/icon_512x512@2x.png 14 | iconutil -c icns valeronoi.iconset 15 | rm -r valeronoi.iconset 16 | -------------------------------------------------------------------------------- /assets/icon/gen_icon.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | cd "$(dirname "${BASH_SOURCE[0]}")" 6 | sizes=(16 24 32 48 64 128 144 180 192 256 512 1024) 7 | input="icon" 8 | 9 | for size in "${sizes[@]}"; do 10 | outfile="${input}-${size}.png" 11 | inkscape --export-filename "$outfile" -w "$size" -h "$size" "${input}.svg" 12 | optipng -o7 "$outfile" 13 | done 14 | 15 | magick convert icon-16.png icon-32.png icon-256.png appicon.ico 16 | cp icon-256.png ../../src/res/valeronoi.png 17 | -------------------------------------------------------------------------------- /assets/icon/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-1024.png -------------------------------------------------------------------------------- /assets/icon/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-128.png -------------------------------------------------------------------------------- /assets/icon/icon-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-144.png -------------------------------------------------------------------------------- /assets/icon/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-16.png -------------------------------------------------------------------------------- /assets/icon/icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-180.png -------------------------------------------------------------------------------- /assets/icon/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-192.png -------------------------------------------------------------------------------- /assets/icon/icon-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-24.png -------------------------------------------------------------------------------- /assets/icon/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-256.png -------------------------------------------------------------------------------- /assets/icon/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-32.png -------------------------------------------------------------------------------- /assets/icon/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-48.png -------------------------------------------------------------------------------- /assets/icon/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-512.png -------------------------------------------------------------------------------- /assets/icon/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/icon-64.png -------------------------------------------------------------------------------- /assets/icon/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon/valeronoi.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/icon/valeronoi.icns -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/assets/screenshot.png -------------------------------------------------------------------------------- /assets/valeronoi.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Valeronoi 4 | Comment=Valeronoi is a companion app for Valetudo for generating WiFi heat maps. 5 | Exec=valeronoi 6 | Icon=valeronoi 7 | Categories=Network;Utility; 8 | Terminal=false 9 | -------------------------------------------------------------------------------- /cmake-format.py: -------------------------------------------------------------------------------- 1 | with section("format"): 2 | # How wide to allow formatted cmake files 3 | line_width = 80 4 | 5 | # How many spaces to tab for indent 6 | tab_size = 4 7 | 8 | # If true, separate flow control names from their parentheses with a space 9 | separate_ctrl_name_with_space = False 10 | 11 | # If true, separate function names from parentheses with a space 12 | separate_fn_name_with_space = False 13 | 14 | # If a statement is wrapped to more than one line, than dangle the closing 15 | # parenthesis on its own line. 16 | dangle_parens = True 17 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_CONFIG_H 19 | #define VALERONOI_CONFIG_H 20 | 21 | // Configured by CMake 22 | 23 | #define VALERONOI_GIT_COMMIT "@GIT_COMMIT@" 24 | #define VALERONOI_VERSION_MAJOR "@Valeronoi_VERSION_MAJOR@" 25 | #define VALERONOI_VERSION_MINOR "@Valeronoi_VERSION_MINOR@" 26 | #define VALERONOI_VERSION_PATCH "@Valeronoi_VERSION_PATCH@" 27 | 28 | #define VALERONOI_VERSION VALERONOI_VERSION_MAJOR "." VALERONOI_VERSION_MINOR "." VALERONOI_VERSION_PATCH 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/gui/dialog/about.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "about.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "config.h" 25 | #include "ui_about.h" 26 | 27 | namespace Valeronoi::gui::dialog { 28 | 29 | AboutDialog::AboutDialog(QWidget *parent) 30 | : QDialog(parent), ui(new Ui::AboutDialog) { 31 | ui->setupUi(this); 32 | ui->version->setText(VALERONOI_VERSION); 33 | ui->gitCommit->setText(VALERONOI_GIT_COMMIT); 34 | 35 | connect(ui->aboutQt, &QPushButton::clicked, this, 36 | [=]() { QMessageBox::aboutQt(this); }); 37 | } 38 | 39 | AboutDialog::~AboutDialog() { delete ui; } 40 | 41 | } // namespace Valeronoi::gui::dialog 42 | -------------------------------------------------------------------------------- /src/gui/dialog/about.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_ABOUT_H 19 | #define VALERONOI_GUI_DIALOG_ABOUT_H 20 | 21 | #include 22 | 23 | QT_BEGIN_NAMESPACE 24 | namespace Ui { 25 | class AboutDialog; 26 | } 27 | QT_END_NAMESPACE 28 | 29 | namespace Valeronoi::gui::dialog { 30 | 31 | class AboutDialog : public QDialog { 32 | Q_OBJECT 33 | public: 34 | explicit AboutDialog(QWidget *parent = nullptr); 35 | 36 | ~AboutDialog() override; 37 | 38 | private: 39 | Ui::AboutDialog *ui; 40 | }; 41 | 42 | } // namespace Valeronoi::gui::dialog 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/gui/dialog/export.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "export.h" 19 | 20 | #include "ui_export.h" 21 | 22 | namespace Valeronoi::gui::dialog { 23 | ExportDialog::ExportDialog(QWidget *parent) 24 | : QDialog(parent), ui(new Ui::ExportDialog) { 25 | ui->setupUi(this); 26 | setWindowFlags(Qt::Sheet); 27 | 28 | connect(ui->width, QOverload::of(&QSpinBox::valueChanged), this, 29 | [=](int width) { 30 | int new_height = static_cast(width / m_ratio); 31 | if (ui->height->value() != new_height) { 32 | const bool block = ui->height->blockSignals(true); 33 | ui->height->setValue(new_height); 34 | ui->height->blockSignals(block); 35 | } 36 | }); 37 | 38 | connect(ui->height, QOverload::of(&QSpinBox::valueChanged), this, 39 | [=](int height) { 40 | int new_width = static_cast(height * m_ratio); 41 | if (ui->width->value() != new_width) { 42 | const bool block = ui->width->blockSignals(true); 43 | ui->width->setValue(new_width); 44 | ui->width->blockSignals(block); 45 | } 46 | }); 47 | } 48 | 49 | ExportDialog::~ExportDialog() { delete ui; } 50 | 51 | void ExportDialog::set_size(const QSize &size) { 52 | ui->width->setMaximum(10 * size.width()); 53 | ui->height->setMaximum(10 * size.height()); 54 | const bool block_w = ui->width->blockSignals(true); 55 | const bool block_h = ui->height->blockSignals(true); 56 | ui->width->setValue(size.width()); 57 | ui->height->setValue(size.height()); 58 | ui->width->blockSignals(block_w); 59 | ui->height->blockSignals(block_h); 60 | m_ratio = 61 | static_cast(size.width()) / static_cast(size.height()); 62 | ui->transparent->setChecked(true); 63 | } 64 | 65 | QSize ExportDialog::get_size() const { 66 | return QSize(ui->width->value(), ui->height->value()); 67 | } 68 | 69 | bool ExportDialog::get_transparent() const { 70 | return ui->transparent->isChecked(); 71 | } 72 | 73 | } // namespace Valeronoi::gui::dialog 74 | -------------------------------------------------------------------------------- /src/gui/dialog/export.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_EXPORT_H 19 | #define VALERONOI_GUI_DIALOG_EXPORT_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "../../robot/robot.h" 27 | 28 | QT_BEGIN_NAMESPACE 29 | namespace Ui { 30 | class ExportDialog; 31 | } 32 | QT_END_NAMESPACE 33 | 34 | namespace Valeronoi::gui::dialog { 35 | class ExportDialog : public QDialog { 36 | Q_OBJECT 37 | public: 38 | explicit ExportDialog(QWidget *parent = nullptr); 39 | 40 | ~ExportDialog() override; 41 | 42 | void set_size(const QSize &size); 43 | 44 | [[nodiscard]] QSize get_size() const; 45 | 46 | [[nodiscard]] bool get_transparent() const; 47 | 48 | private: 49 | Ui::ExportDialog *ui; 50 | double m_ratio; 51 | }; 52 | } // namespace Valeronoi::gui::dialog 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/gui/dialog/export.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExportDialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 360 13 | 240 14 | 15 | 16 | 17 | Export image 18 | 19 | 20 | 21 | 22 | 23 | Select image resolution 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Height 33 | 34 | 35 | 36 | 37 | 38 | 39 | Width 40 | 41 | 42 | 43 | 44 | 45 | 46 | Pixels 47 | 48 | 49 | 50 | 51 | 52 | 53 | Pixels 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 150 62 | 0 63 | 64 | 65 | 66 | 1 67 | 68 | 69 | 99999 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 150 78 | 0 79 | 80 | 81 | 82 | 1 83 | 84 | 85 | 99999 86 | 87 | 88 | 89 | 90 | 91 | 92 | Qt::Horizontal 93 | 94 | 95 | 96 | 40 97 | 20 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Transparent background 108 | 109 | 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | Qt::Vertical 118 | 119 | 120 | 121 | 20 122 | 35 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | Qt::Horizontal 131 | 132 | 133 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | buttonBox 143 | accepted() 144 | ExportDialog 145 | accept() 146 | 147 | 148 | 248 149 | 254 150 | 151 | 152 | 157 153 | 274 154 | 155 | 156 | 157 | 158 | buttonBox 159 | rejected() 160 | ExportDialog 161 | reject() 162 | 163 | 164 | 316 165 | 260 166 | 167 | 168 | 286 169 | 274 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/gui/dialog/log.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "log.h" 19 | 20 | #include "ui_log.h" 21 | 22 | namespace Valeronoi::gui::dialog { 23 | 24 | LogDialog::LogDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LogDialog) { 25 | ui->setupUi(this); 26 | 27 | ui->logText->setFontFamily("Source Code Pro"); 28 | } 29 | 30 | LogDialog::~LogDialog() { delete ui; } 31 | 32 | void LogDialog::log_message(const QString &msg) { ui->logText->append(msg); } 33 | 34 | } // namespace Valeronoi::gui::dialog 35 | -------------------------------------------------------------------------------- /src/gui/dialog/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_LOG_H 19 | #define VALERONOI_GUI_DIALOG_LOG_H 20 | 21 | #include 22 | 23 | QT_BEGIN_NAMESPACE 24 | namespace Ui { 25 | class LogDialog; 26 | } 27 | QT_END_NAMESPACE 28 | 29 | namespace Valeronoi::gui::dialog { 30 | 31 | class LogDialog : public QDialog { 32 | Q_OBJECT 33 | public: 34 | explicit LogDialog(QWidget *parent = nullptr); 35 | 36 | ~LogDialog() override; 37 | 38 | void log_message(const QString &msg); 39 | 40 | private: 41 | Ui::LogDialog *ui; 42 | }; 43 | 44 | } // namespace Valeronoi::gui::dialog 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/gui/dialog/log.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LogDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 491 10 | 317 11 | 12 | 13 | 14 | Log 15 | 16 | 17 | 18 | 19 | 20 | Valeronoi log: 21 | 22 | 23 | 24 | 25 | 26 | 27 | true 28 | 29 | 30 | 31 | 32 | 33 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 34 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 35 | p, li { white-space: pre-wrap; } 36 | </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> 37 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> 38 | 39 | 40 | 41 | 42 | 43 | 44 | Qt::Horizontal 45 | 46 | 47 | QDialogButtonBox::Close 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | buttonBox 57 | accepted() 58 | LogDialog 59 | accept() 60 | 61 | 62 | 248 63 | 254 64 | 65 | 66 | 157 67 | 274 68 | 69 | 70 | 71 | 72 | buttonBox 73 | rejected() 74 | LogDialog 75 | reject() 76 | 77 | 78 | 316 79 | 260 80 | 81 | 82 | 286 83 | 274 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/gui/dialog/robot_config.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "robot_config.h" 19 | 20 | #include 21 | #include 22 | 23 | #include "../../robot/connection_configuration.h" 24 | #include "ui_robot_config.h" 25 | 26 | namespace Valeronoi::gui::dialog { 27 | RobotConfigDialog::RobotConfigDialog(QWidget *parent) 28 | : QDialog(parent), 29 | ui(new Ui::RobotConfigDialog), 30 | m_progress_dialog(tr("Testing connection..."), tr("Cancel"), 0, 100, 31 | this) { 32 | ui->setupUi(this); 33 | setWindowFlags(Qt::Sheet); 34 | 35 | m_progress_dialog.setWindowModality(Qt::ApplicationModal); 36 | m_progress_dialog.setAutoClose(true); 37 | m_progress_dialog.close(); 38 | connect(&m_progress_dialog, &QProgressDialog::canceled, this, 39 | &RobotConfigDialog::slot_end_test); 40 | 41 | connect(this, &QDialog::accepted, this, &RobotConfigDialog::slot_save); 42 | connect(ui->buttonTestConnection, &QPushButton::clicked, this, 43 | &RobotConfigDialog::slot_test_connection); 44 | 45 | connect(&m_robot, &Valeronoi::robot::Robot::signal_connecting_step, this, 46 | [=](float value) { 47 | qDebug().nospace() << "Received connection step " << value; 48 | if (!m_test_cancelled) { 49 | m_progress_dialog.setValue(10 + static_cast(80 * value)); 50 | } 51 | }); 52 | connect(&m_robot, &Valeronoi::robot::Robot::signal_connected, this, 53 | &RobotConfigDialog::slot_robot_connected); 54 | connect(&m_robot, &Valeronoi::robot::Robot::signal_connection_error, this, 55 | &RobotConfigDialog::slot_robot_connection_failed); 56 | 57 | load_settings(); 58 | } 59 | 60 | RobotConfigDialog::~RobotConfigDialog() { delete ui; } 61 | 62 | void RobotConfigDialog::slot_save() { 63 | QSettings settings; 64 | ensure_http(); 65 | 66 | settings.setValue("robot/url", ui->valetudoAddress->text()); 67 | settings.setValue("robot/auth/enabled", ui->groupBoxAuth->isChecked()); 68 | settings.setValue("robot/auth/username", ui->authUsername->text()); 69 | settings.setValue("robot/auth/password", ui->authPassword->text()); 70 | 71 | emit signal_config_changed(); 72 | } 73 | 74 | void RobotConfigDialog::load_settings() { 75 | QSettings settings; 76 | ui->valetudoAddress->setText( 77 | settings.value("robot/url", "http://").toString()); 78 | ui->groupBoxAuth->setChecked( 79 | settings.value("robot/auth/enabled", false).toBool()); 80 | ui->authUsername->setText( 81 | settings.value("robot/auth/username", "").toString()); 82 | ui->authPassword->setText( 83 | settings.value("robot/auth/password", "").toString()); 84 | 85 | ensure_http(); 86 | } 87 | 88 | void RobotConfigDialog::slot_end_test() { 89 | m_test_cancelled = true; 90 | m_robot.slot_disconnect(); 91 | m_progress_dialog.close(); 92 | } 93 | 94 | void RobotConfigDialog::slot_test_connection() { 95 | ensure_http(); 96 | const QString url_input = ui->valetudoAddress->text().trimmed(); 97 | if (url_input.isEmpty()) { 98 | QMessageBox::warning(this, tr("Error"), 99 | tr("Please enter the Valetudo address")); 100 | return; 101 | } 102 | 103 | QUrl base_url = QUrl::fromUserInput(url_input); 104 | if (!base_url.isValid()) { 105 | QMessageBox::warning( 106 | this, tr("Error"), 107 | tr("Invalid URL: %1: %2").arg(url_input, base_url.errorString())); 108 | return; 109 | } 110 | 111 | m_test_cancelled = false; 112 | m_progress_dialog.setValue(10); 113 | Valeronoi::robot::ConnectionConfiguration c; 114 | c.m_url = base_url; 115 | c.m_auth = ui->groupBoxAuth->isChecked(); 116 | c.m_username = ui->authUsername->text(); 117 | c.m_password = ui->authPassword->text(); 118 | m_robot.set_connection_configuration(c); 119 | m_robot.slot_connect(); 120 | } 121 | 122 | void RobotConfigDialog::slot_robot_connected() { 123 | m_progress_dialog.close(); 124 | const auto robot_information = m_robot.get_information(); 125 | QString robot_description = QString("%1 / %2 (%3)") 126 | .arg(robot_information->m_manufacturer) 127 | .arg(robot_information->m_model_name) 128 | .arg(robot_information->m_implementation); 129 | 130 | bool has_wifi_config = 131 | robot_information->m_capabilities.contains("WifiConfigurationCapability"); 132 | 133 | QString has_wifi_config_description; 134 | if (has_wifi_config) { 135 | has_wifi_config_description = 136 | tr("✅ The robot has the WifiConfigurationCapability."); 137 | } else { 138 | has_wifi_config_description = 139 | tr("❌ The robot does not have the WifiConfigurationCapability."); 140 | } 141 | 142 | m_robot.slot_disconnect(); 143 | 144 | QString test_result = 145 | tr("Connection successful. Detected:\n%1\nRunning Valetudo %2\n\n%3") 146 | .arg(robot_description) 147 | .arg(robot_information->m_valetudo_version) 148 | .arg(has_wifi_config_description); 149 | 150 | if (has_wifi_config) { 151 | QMessageBox::information(this, tr("Test successful"), test_result); 152 | } else { 153 | QMessageBox::warning(this, tr("Test failed"), test_result); 154 | } 155 | } 156 | 157 | void RobotConfigDialog::slot_robot_connection_failed() { 158 | m_progress_dialog.close(); 159 | QMessageBox::warning(this, tr("Error"), 160 | tr("Test failed:\n%1").arg(m_robot.get_error())); 161 | } 162 | 163 | void RobotConfigDialog::ensure_http() { 164 | const auto current_text = ui->valetudoAddress->text().trimmed(); 165 | if (!current_text.startsWith("http://")) { 166 | ui->valetudoAddress->setText(QString("http://").append(current_text)); 167 | } 168 | } 169 | } // namespace Valeronoi::gui::dialog 170 | -------------------------------------------------------------------------------- /src/gui/dialog/robot_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_ROBOT_CONFIG_H 19 | #define VALERONOI_GUI_DIALOG_ROBOT_CONFIG_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "../../robot/robot.h" 32 | 33 | QT_BEGIN_NAMESPACE 34 | namespace Ui { 35 | class RobotConfigDialog; 36 | } 37 | QT_END_NAMESPACE 38 | 39 | namespace Valeronoi::gui::dialog { 40 | class RobotConfigDialog : public QDialog { 41 | Q_OBJECT 42 | public: 43 | explicit RobotConfigDialog(QWidget *parent = nullptr); 44 | 45 | ~RobotConfigDialog() override; 46 | 47 | void load_settings(); 48 | 49 | signals: 50 | void signal_config_changed(); 51 | 52 | private slots: 53 | void slot_save(); 54 | 55 | void slot_test_connection(); 56 | 57 | void slot_end_test(); 58 | 59 | void slot_robot_connected(); 60 | 61 | void slot_robot_connection_failed(); 62 | 63 | private: 64 | void ensure_http(); 65 | 66 | Ui::RobotConfigDialog *ui; 67 | QProgressDialog m_progress_dialog; 68 | bool m_test_cancelled{false}; 69 | 70 | Valeronoi::robot::Robot m_robot; 71 | }; 72 | } // namespace Valeronoi::gui::dialog 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/gui/dialog/robot_config.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | RobotConfigDialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 420 13 | 300 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 420 25 | 300 26 | 27 | 28 | 29 | Robot Setup 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Valetudo address 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | HTTP Authentication 60 | 61 | 62 | true 63 | 64 | 65 | false 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Username 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 0 82 | 0 83 | 84 | 85 | 86 | valetudo 87 | 88 | 89 | 90 | 91 | 92 | 93 | Password 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 0 102 | 0 103 | 104 | 105 | 106 | QLineEdit::Password 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | Qt::Horizontal 122 | 123 | 124 | 125 | 271 126 | 20 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 0 136 | 0 137 | 138 | 139 | 140 | Test connection 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Qt::Vertical 151 | 152 | 153 | 154 | 20 155 | 40 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | Qt::Horizontal 164 | 165 | 166 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 167 | 168 | 169 | 170 | 171 | 172 | 173 | valetudoAddress 174 | groupBoxAuth 175 | authUsername 176 | authPassword 177 | buttonTestConnection 178 | 179 | 180 | 181 | 182 | buttonBox 183 | accepted() 184 | RobotConfigDialog 185 | accept() 186 | 187 | 188 | 248 189 | 254 190 | 191 | 192 | 157 193 | 274 194 | 195 | 196 | 197 | 198 | buttonBox 199 | rejected() 200 | RobotConfigDialog 201 | reject() 202 | 203 | 204 | 316 205 | 260 206 | 207 | 208 | 286 209 | 274 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /src/gui/dialog/settings.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "settings.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../../util/compat.h" 25 | #include "ui_settings.h" 26 | 27 | namespace Valeronoi::gui::dialog { 28 | SettingsDialog::SettingsDialog(QWidget *parent) 29 | : QDialog(parent), ui(new Ui::SettingsDialog) { 30 | ui->setupUi(this); 31 | setWindowFlags(Qt::Sheet); 32 | 33 | connect(ui->checkUpdates, &CHECKBOX_SIGNAL_CHANGED, this, [=]() { 34 | QSettings settings; 35 | settings.setValue("app/autoUpdateCheck", ui->checkUpdates->isChecked()); 36 | }); 37 | 38 | QSettings settings; 39 | ui->checkUpdates->setChecked( 40 | settings.value("app/autoUpdateCheck", false).toBool()); 41 | } 42 | 43 | SettingsDialog::~SettingsDialog() { delete ui; } 44 | 45 | bool SettingsDialog::should_auto_check_for_updates(QWidget *parent) { 46 | QSettings settings; 47 | bool check_for_updates{false}; 48 | auto update_check = settings.value("app/autoUpdateCheck"); 49 | if (update_check.isNull()) { 50 | auto start_count = settings.value("app/startCount", 0).toInt(); 51 | if (start_count >= 3) { 52 | const auto clicked_button = QMessageBox::question( 53 | parent, tr("Auto Updates"), 54 | tr("Do you want Valeronoi to automatically check for updates?")); 55 | check_for_updates = (clicked_button == QMessageBox::StandardButton::Yes); 56 | settings.setValue("app/autoUpdateCheck", check_for_updates); 57 | ui->checkUpdates->setChecked(check_for_updates); 58 | } 59 | } else { 60 | check_for_updates = update_check.toBool(); 61 | } 62 | return check_for_updates; 63 | } 64 | 65 | } // namespace Valeronoi::gui::dialog 66 | -------------------------------------------------------------------------------- /src/gui/dialog/settings.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_SETTINGS_H 19 | #define VALERONOI_GUI_DIALOG_SETTINGS_H 20 | 21 | #include 22 | 23 | QT_BEGIN_NAMESPACE 24 | namespace Ui { 25 | class SettingsDialog; 26 | } 27 | QT_END_NAMESPACE 28 | 29 | namespace Valeronoi::gui::dialog { 30 | class SettingsDialog : public QDialog { 31 | Q_OBJECT 32 | public: 33 | explicit SettingsDialog(QWidget *parent = nullptr); 34 | 35 | ~SettingsDialog() override; 36 | 37 | [[nodiscard]] bool should_auto_check_for_updates(QWidget *parent = nullptr); 38 | 39 | private: 40 | Ui::SettingsDialog *ui; 41 | }; 42 | } // namespace Valeronoi::gui::dialog 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/gui/dialog/settings.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SettingsDialog 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 400 13 | 150 14 | 15 | 16 | 17 | Settings 18 | 19 | 20 | 21 | 22 | 23 | Automatically check for updates 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Vertical 31 | 32 | 33 | 34 | 20 35 | 67 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Qt::Horizontal 44 | 45 | 46 | QDialogButtonBox::Close 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | buttonBox 56 | accepted() 57 | SettingsDialog 58 | accept() 59 | 60 | 61 | 248 62 | 254 63 | 64 | 65 | 157 66 | 274 67 | 68 | 69 | 70 | 71 | buttonBox 72 | rejected() 73 | SettingsDialog 74 | reject() 75 | 76 | 77 | 316 78 | 260 79 | 80 | 81 | 286 82 | 274 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/gui/dialog/update.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "update.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "config.h" 30 | #include "ui_update.h" 31 | 32 | namespace Valeronoi::gui::dialog { 33 | 34 | UpdateDialog::UpdateDialog(QWidget *parent) 35 | : QDialog(parent), ui(new Ui::UpdateDialog) { 36 | ui->setupUi(this); 37 | ui->currentVersion->setText(VALERONOI_VERSION); 38 | 39 | connect(ui->goToRelease, &QPushButton::clicked, this, 40 | [=]() { QDesktopServices::openUrl(m_goto_url); }); 41 | } 42 | 43 | UpdateDialog::~UpdateDialog() { delete ui; } 44 | 45 | void UpdateDialog::check_update(bool silent, QWidget *error_parent) { 46 | qDebug() << "Checking for updates"; 47 | 48 | auto r = QNetworkRequest( 49 | QUrl("https://api.github.com/repos/ccoors/Valeronoi/releases")); 50 | r.setTransferTimeout(10000); 51 | r.setMaximumRedirectsAllowed(10); 52 | r.setAttribute(QNetworkRequest::CacheLoadControlAttribute, 53 | QNetworkRequest::AlwaysNetwork); 54 | r.setHeader(QNetworkRequest::UserAgentHeader, "Valeronoi/" VALERONOI_VERSION); 55 | auto reply = m_qnam.get(r); 56 | connect(reply, &QNetworkReply::finished, this, [=]() { 57 | if (reply->error() != QNetworkReply::NoError) { 58 | QMessageBox::warning( 59 | error_parent, "Valeronoi", 60 | tr("Update check failed: %1").arg(reply->errorString())); 61 | } else { 62 | auto data = reply->readAll(); 63 | QJsonParseError error; 64 | auto json = QJsonDocument::fromJson(data, &error); 65 | if (json.isNull()) { 66 | QMessageBox::warning( 67 | error_parent, "Valeronoi", 68 | tr("Error parsing update data: %1").arg(error.errorString())); 69 | } else { 70 | auto newest_release = json.array()[0].toObject(); 71 | auto current_tag = newest_release["tag_name"].toString(); 72 | m_goto_url = newest_release["html_url"].toString(); 73 | if (current_tag.isEmpty() || m_goto_url.isEmpty()) { 74 | QMessageBox::information(error_parent, "Valeronoi", 75 | tr("Could not load update information")); 76 | return; 77 | } 78 | ui->updatedVersion->setText(current_tag); 79 | if (current_tag != "v" VALERONOI_VERSION) { 80 | open(); 81 | raise(); 82 | } else if (!silent) { 83 | QMessageBox::information(error_parent, "Valeronoi", 84 | tr("No new version available")); 85 | } 86 | } 87 | } 88 | reply->deleteLater(); 89 | }); 90 | } 91 | 92 | } // namespace Valeronoi::gui::dialog 93 | -------------------------------------------------------------------------------- /src/gui/dialog/update.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_DIALOG_UPDATE_H 19 | #define VALERONOI_GUI_DIALOG_UPDATE_H 20 | 21 | #include 22 | #include 23 | 24 | QT_BEGIN_NAMESPACE 25 | namespace Ui { 26 | class UpdateDialog; 27 | } 28 | QT_END_NAMESPACE 29 | 30 | namespace Valeronoi::gui::dialog { 31 | 32 | class UpdateDialog : public QDialog { 33 | Q_OBJECT 34 | public: 35 | explicit UpdateDialog(QWidget *parent = nullptr); 36 | 37 | ~UpdateDialog() override; 38 | 39 | void check_update(bool silent = false, QWidget *error_parent = nullptr); 40 | 41 | private: 42 | Ui::UpdateDialog *ui; 43 | 44 | QUrl m_goto_url; 45 | 46 | QNetworkAccessManager m_qnam; 47 | }; 48 | 49 | } // namespace Valeronoi::gui::dialog 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/gui/dialog/update.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | UpdateDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 450 10 | 225 11 | 12 | 13 | 14 | Update 15 | 16 | 17 | 18 | 19 | 20 | A new version of Valeronoi is available: 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | You are running version 30 | 31 | 32 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 33 | 34 | 35 | 36 | 37 | 38 | 39 | CURRENT_VERSION 40 | 41 | 42 | 43 | 44 | 45 | 46 | New version 47 | 48 | 49 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 50 | 51 | 52 | 53 | 54 | 55 | 56 | UPDATED_VERSION 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Qt::Horizontal 69 | 70 | 71 | 72 | 40 73 | 20 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Go to release page 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Qt::Vertical 92 | 93 | 94 | 95 | 20 96 | 44 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Qt::Horizontal 105 | 106 | 107 | QDialogButtonBox::Close 108 | 109 | 110 | false 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | buttonBox 120 | accepted() 121 | UpdateDialog 122 | accept() 123 | 124 | 125 | 248 126 | 254 127 | 128 | 129 | 157 130 | 274 131 | 132 | 133 | 134 | 135 | buttonBox 136 | rejected() 137 | UpdateDialog 138 | reject() 139 | 140 | 141 | 316 142 | 260 143 | 144 | 145 | 286 146 | 274 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/gui/graphics_item/entity_item.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "entity_item.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace Valeronoi::gui::graphics_item { 25 | EntityItem::EntityItem(const Valeronoi::state::RobotMap &robot_map, 26 | QGraphicsItem *parent) 27 | : MapBasedItem(robot_map, parent) {} 28 | 29 | void EntityItem::paint(QPainter *painter, 30 | const QStyleOptionGraphicsItem *option, 31 | QWidget *widget) { 32 | (void)widget; 33 | (void)option; 34 | if (m_robot_map.is_valid()) { 35 | const auto &map = m_robot_map.get_map(); 36 | // Draw path(s) first 37 | std::for_each( 38 | map.entities.begin(), map.entities.end(), [=](const auto &entity) { 39 | if ((entity.type == "path" || entity.cls == "PathMapEntity") && 40 | entity.points.size() > 1) { 41 | QPen pen; 42 | QColor path_color = Qt::white; 43 | path_color.setAlphaF(0.5); 44 | pen.setColor(path_color); 45 | pen.setWidth(2); 46 | painter->setPen(pen); 47 | painter->setBrush(Qt::transparent); 48 | QPainterPath path; 49 | path.moveTo(entity.points[0].x, entity.points[0].y); 50 | for (const auto &p : entity.points) { 51 | path.lineTo(p.x, p.y); 52 | } 53 | painter->drawPath(path); 54 | } 55 | }); 56 | 57 | std::for_each(map.entities.begin(), map.entities.end(), 58 | [=](const auto &entity) { 59 | if (entity.points.empty()) { 60 | return; 61 | } 62 | if (entity.type == "charger_location") { 63 | paint_charger(painter, entity); 64 | } else if (entity.type == "robot_position") { 65 | paint_robot(painter, entity); 66 | } 67 | }); 68 | } 69 | } 70 | 71 | void EntityItem::paint_robot(QPainter *painter, 72 | const Valeronoi::state::Entity &entity) { 73 | QPen pen; 74 | pen.setColor(Qt::darkGray); 75 | pen.setWidthF(1.5); 76 | painter->setPen(pen); 77 | painter->setBrush(Qt::white); 78 | QPointF robot_center(entity.points[0].x, entity.points[0].y); 79 | painter->drawEllipse(robot_center, 10, 10); 80 | QPointF tower_head(entity.points[0].x, entity.points[0].y); 81 | tower_head += 3 * QPointF(std::cos(entity.angle), std::sin(entity.angle)); 82 | pen.setWidthF(1); 83 | painter->setPen(pen); 84 | painter->drawEllipse(tower_head, 3, 3); 85 | } 86 | 87 | void EntityItem::paint_charger(QPainter *painter, 88 | const Valeronoi::state::Entity &entity) { 89 | QPen pen; 90 | pen.setColor(QColor(50, 50, 50)); 91 | pen.setWidthF(1.5); 92 | painter->setPen(pen); 93 | painter->setBrush(Qt::lightGray); 94 | QPointF charger_center(entity.points[0].x, entity.points[0].y); 95 | painter->drawEllipse(charger_center, 12, 12); 96 | 97 | QPainterPath path; 98 | path.moveTo(charger_center.x() + 2, charger_center.y() - 6); 99 | path.lineTo(charger_center.x() - 3.5, charger_center.y() + 1); 100 | path.lineTo(charger_center.x() - 1, charger_center.y() + 1); 101 | path.lineTo(charger_center.x() - 2, charger_center.y() + 6); 102 | 103 | path.lineTo(charger_center.x() + 3.5, charger_center.y() - 1); 104 | path.lineTo(charger_center.x() + 1, charger_center.y() - 1); 105 | path.closeSubpath(); 106 | 107 | painter->setPen( 108 | QPen(QColor(0, 120, 255), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); 109 | painter->setBrush(Qt::white); 110 | painter->drawPath(path); 111 | } 112 | 113 | } // namespace Valeronoi::gui::graphics_item 114 | -------------------------------------------------------------------------------- /src/gui/graphics_item/entity_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_GRAPHICS_ITEM_ENTITY_ITEM_H 19 | #define VALERONOI_GUI_GRAPHICS_ITEM_ENTITY_ITEM_H 20 | 21 | #include "../../state/robot_map.h" 22 | #include "map_based_item.h" 23 | 24 | namespace Valeronoi::gui::graphics_item { 25 | class EntityItem : public MapBasedItem { 26 | public: 27 | explicit EntityItem(const Valeronoi::state::RobotMap &robot_map, 28 | QGraphicsItem *parent = nullptr); 29 | 30 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 31 | QWidget *widget) override; 32 | 33 | private: 34 | static void paint_charger(QPainter *painter, 35 | const Valeronoi::state::Entity &entity); 36 | 37 | static void paint_robot(QPainter *painter, 38 | const Valeronoi::state::Entity &entity); 39 | }; 40 | 41 | } // namespace Valeronoi::gui::graphics_item 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/gui/graphics_item/floor_item.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "floor_item.h" 19 | 20 | namespace Valeronoi::gui::graphics_item { 21 | FloorItem::FloorItem(const Valeronoi::state::RobotMap &robot_map, 22 | QGraphicsItem *parent) 23 | : MapBasedItem(robot_map, parent) {} 24 | 25 | void FloorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 26 | QWidget *widget) { 27 | (void)option; 28 | (void)widget; 29 | painter->setPen(Qt::transparent); 30 | 31 | // Anti aliasing the floor leads to a thin grid 32 | painter->setRenderHints(QPainter::Antialiasing, false); 33 | 34 | painter->setBrush(m_floor_color); 35 | painter->drawPath(m_floor_path); 36 | } 37 | 38 | void FloorItem::set_floor_color(QColor color) { 39 | m_floor_color = color; 40 | update(); 41 | } 42 | 43 | void FloorItem::map_updated() { 44 | m_floor_path.clear(); 45 | m_floor_path.setFillRule(Qt::WindingFill); 46 | if (m_robot_map.is_valid()) { 47 | const auto &map = m_robot_map.get_map(); 48 | const auto floor = map.layers.find("floor"); 49 | if (floor != map.layers.end()) { 50 | for (const auto &block : floor->second.blocks) { 51 | m_floor_path.addRect(block.x, block.y, map.pixel_size, map.pixel_size); 52 | } 53 | } 54 | } 55 | MapBasedItem::map_updated(); 56 | } 57 | 58 | const QPainterPath &FloorItem::get_floor_path() const { return m_floor_path; } 59 | 60 | } // namespace Valeronoi::gui::graphics_item 61 | -------------------------------------------------------------------------------- /src/gui/graphics_item/floor_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_GRAPHICS_ITEM_FLOOR_ITEM_H 19 | #define VALERONOI_GUI_GRAPHICS_ITEM_FLOOR_ITEM_H 20 | 21 | #include 22 | 23 | #include "../../state/robot_map.h" 24 | #include "map_based_item.h" 25 | 26 | namespace Valeronoi::gui::graphics_item { 27 | class FloorItem : public MapBasedItem { 28 | public: 29 | explicit FloorItem(const Valeronoi::state::RobotMap &robot_map, 30 | QGraphicsItem *parent = nullptr); 31 | 32 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 33 | QWidget *widget) override; 34 | 35 | void set_floor_color(QColor color); 36 | 37 | void map_updated() override; 38 | 39 | [[nodiscard]] const QPainterPath &get_floor_path() const; 40 | 41 | private: 42 | QColor m_floor_color; 43 | 44 | QPainterPath m_floor_path; 45 | }; 46 | 47 | } // namespace Valeronoi::gui::graphics_item 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/gui/graphics_item/map_based_item.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "map_based_item.h" 19 | 20 | namespace Valeronoi::gui::graphics_item { 21 | MapBasedItem::MapBasedItem(const Valeronoi::state::RobotMap &robot_map, 22 | QGraphicsItem *parent) 23 | : QGraphicsItem(parent), m_robot_map(robot_map) {} 24 | 25 | QRectF MapBasedItem::boundingRect() const { return m_map_bounds; } 26 | 27 | void MapBasedItem::map_updated() { 28 | QRectF new_bounds; 29 | if (m_robot_map.is_valid()) { 30 | new_bounds = QRectF(0, 0, m_robot_map.get_map().size_x, 31 | m_robot_map.get_map().size_y); 32 | } else { 33 | new_bounds = QRectF(0, 0, 400.0, 50.0); 34 | } 35 | if (new_bounds != m_map_bounds) { 36 | m_map_bounds = new_bounds; 37 | prepareGeometryChange(); 38 | } 39 | update(); 40 | } 41 | 42 | } // namespace Valeronoi::gui::graphics_item 43 | -------------------------------------------------------------------------------- /src/gui/graphics_item/map_based_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_GRAPHICS_ITEM_MAP_BASED_ITEM_H 19 | #define VALERONOI_GUI_GRAPHICS_ITEM_MAP_BASED_ITEM_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "../../state/robot_map.h" 31 | 32 | namespace Valeronoi::gui::graphics_item { 33 | 34 | class MapBasedItem : public QGraphicsItem { 35 | public: 36 | explicit MapBasedItem(const Valeronoi::state::RobotMap &robot_map, 37 | QGraphicsItem *parent = nullptr); 38 | 39 | [[nodiscard]] QRectF boundingRect() const override; 40 | 41 | virtual void map_updated(); 42 | 43 | protected: 44 | const Valeronoi::state::RobotMap &m_robot_map; 45 | 46 | QRectF m_map_bounds; 47 | }; 48 | 49 | } // namespace Valeronoi::gui::graphics_item 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/gui/graphics_item/map_item.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "map_item.h" 19 | 20 | #include 21 | 22 | namespace Valeronoi::gui::graphics_item { 23 | MapItem::MapItem(const Valeronoi::state::RobotMap &robot_map, 24 | std::function relocate, QGraphicsItem *parent) 25 | : MapBasedItem(robot_map, parent), 26 | m_font("Source Code Pro", 12), 27 | m_relocate(std::move(relocate)) {} 28 | 29 | void MapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 30 | QWidget *widget) { 31 | (void)option; 32 | (void)widget; 33 | painter->setPen(Qt::transparent); 34 | painter->setFont(m_font); 35 | 36 | if (m_robot_map.is_valid()) { 37 | const auto &map = m_robot_map.get_map(); 38 | 39 | painter->setBrush(m_wall_color); 40 | painter->setRenderHints( 41 | QPainter::Antialiasing, 42 | false); // Antialiasing the walls leads to a thin grid 43 | const auto walls = map.layers.find("wall"); 44 | if (walls != map.layers.end()) { 45 | for (const auto &block : walls->second.blocks) { 46 | painter->drawRect(block.x, block.y, map.pixel_size, map.pixel_size); 47 | } 48 | } 49 | } else { 50 | painter->setBrush(Qt::black); 51 | painter->drawRect(0, 0, 400, 50); 52 | painter->setPen(Qt::lightGray); 53 | painter->drawText(0, 0, 400, 50, 54 | Qt::AlignCenter | Qt::AlignVCenter | Qt::TextWordWrap, 55 | m_robot_map.error_msg()); 56 | } 57 | } 58 | 59 | void MapItem::set_wall_color(QColor color) { 60 | m_wall_color = color; 61 | update(); 62 | } 63 | 64 | #ifndef QT_NO_CONTEXTMENU 65 | void MapItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { 66 | const auto &map = m_robot_map.get_map(); 67 | QMenu menu; 68 | QAction *go_to_action = menu.addAction(QObject::tr("Relocate here")); 69 | if (!m_robot_map.is_valid()) { 70 | go_to_action->setEnabled(false); 71 | } 72 | QAction *selected_action = menu.exec(event->screenPos()); 73 | if (selected_action == go_to_action) { 74 | m_relocate(event->pos().x() + map.crop_x, event->pos().y() + map.crop_y); 75 | } 76 | } 77 | #endif 78 | 79 | } // namespace Valeronoi::gui::graphics_item 80 | -------------------------------------------------------------------------------- /src/gui/graphics_item/map_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_GRAPHICS_ITEM_MAP_ITEM_H 19 | #define VALERONOI_GUI_GRAPHICS_ITEM_MAP_ITEM_H 20 | 21 | #ifndef QT_NO_CONTEXTMENU 22 | #include 23 | #include 24 | #include 25 | #endif 26 | 27 | #include 28 | 29 | #include "../../state/robot_map.h" 30 | #include "map_based_item.h" 31 | 32 | namespace Valeronoi::gui::graphics_item { 33 | class MapItem : public MapBasedItem { 34 | public: 35 | explicit MapItem(const Valeronoi::state::RobotMap &robot_map, 36 | std::function relocate, 37 | QGraphicsItem *parent = nullptr); 38 | 39 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 40 | QWidget *widget) override; 41 | 42 | void set_wall_color(QColor color); 43 | 44 | #ifndef QT_NO_CONTEXTMENU 45 | void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; 46 | #endif 47 | 48 | private: 49 | QFont m_font; 50 | QColor m_wall_color; 51 | std::function 52 | m_relocate; // This is a bit hacky, but since MapItem is not a QObject, 53 | // we can't use signals/slots 54 | }; 55 | 56 | } // namespace Valeronoi::gui::graphics_item 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/gui/graphics_item/measurement_item.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "measurement_item.h" 19 | 20 | #include 21 | #include 22 | 23 | constexpr const int SCALE_FONT_SIZE{15}; 24 | constexpr const int SCALE_HISTOGRAM_HEIGHT{70}; 25 | constexpr const int SCALE_BAR_HEIGHT{30}; 26 | constexpr const int SCALE_WIDTH{200}; 27 | constexpr const int SCALE_MARGIN{5}; 28 | constexpr const int PATH_DISTANCE{35}; 29 | constexpr const QSize SCALE_SIZE{ 30 | SCALE_WIDTH, SCALE_BAR_HEIGHT + SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT}; 31 | 32 | namespace Valeronoi::gui::graphics_item { 33 | MeasurementItem::MeasurementItem(const Valeronoi::state::RobotMap &robot_map, 34 | QGraphicsItem *parent) 35 | : MapBasedItem(robot_map, parent), m_font("Source Code Pro") { 36 | m_font.setPixelSize(SCALE_FONT_SIZE); 37 | } 38 | 39 | void MeasurementItem::paint(QPainter *painter, 40 | const QStyleOptionGraphicsItem *option, 41 | QWidget *widget) { 42 | (void)option; 43 | (void)widget; 44 | (void)painter; 45 | painter->setPen(Qt::transparent); 46 | if (m_display_mode == Valeronoi::state::DISPLAY_MODE::None || 47 | m_color_map == nullptr || m_min >= m_max) { 48 | return; 49 | } 50 | if (m_robot_map.is_valid()) { 51 | if (m_display_mode == Valeronoi::state::DISPLAY_MODE::Voronoi) { 52 | // Rendering Voronoi segments with antialiasing leads to artifacts 53 | painter->setRenderHints(QPainter::Antialiasing, false); 54 | painter->setClipRect(MapBasedItem::boundingRect(), Qt::ReplaceClip); 55 | if (m_restrict_path) { 56 | painter->setClipPath(m_path, Qt::IntersectClip); 57 | } 58 | if (m_restrict_points) { 59 | painter->setClipPath(m_points_path, Qt::IntersectClip); 60 | } 61 | 62 | for (const auto &p : m_data_segments) { 63 | painter->setBrush(p.color); 64 | painter->drawPolygon(p.polygon); 65 | } 66 | } else if (m_display_mode == Valeronoi::state::DISPLAY_MODE::DataPoints) { 67 | for (const auto &p : m_data_segments) { 68 | painter->setBrush(p.color); 69 | painter->drawEllipse(p.x - 2, p.y - 2, 4, 4); 70 | } 71 | } 72 | 73 | painter->setClipRect(boundingRect(), Qt::ReplaceClip); 74 | const auto &map = m_robot_map.get_map(); 75 | const auto base_y = map.size_y + SCALE_MARGIN; 76 | if (!m_legend.isNull()) { 77 | painter->drawPicture(QPoint(SCALE_MARGIN, base_y), m_legend); 78 | } 79 | } 80 | } 81 | 82 | void MeasurementItem::set_display_mode( 83 | Valeronoi::state::DISPLAY_MODE display_mode) { 84 | m_display_mode = display_mode; 85 | calculate_colors(); 86 | } 87 | 88 | void MeasurementItem::set_color_map( 89 | const Valeronoi::util::RGBColorMap *color_map) { 90 | m_color_map = color_map; 91 | calculate_colors(); 92 | } 93 | 94 | QColor MeasurementItem::color_value(double normalized_value) const { 95 | auto color = m_color_map->get_color(normalized_value); 96 | return {static_cast(255 * color[0]), static_cast(255 * color[1]), 97 | static_cast(255 * color[2])}; 98 | } 99 | 100 | QColor MeasurementItem::get_color(double value) const { 101 | if (m_min == m_max || m_max - m_min == 0 || m_min > m_max || !m_color_map) { 102 | return Qt::black; 103 | } 104 | double normalized = (value - m_min) / (m_max - m_min); 105 | return color_value(normalized); 106 | } 107 | 108 | void MeasurementItem::set_data_segments(const state::DataSegments &segments) { 109 | m_points_path.clear(); 110 | m_points_path.setFillRule(Qt::WindingFill); 111 | m_data_segments = segments; 112 | m_min = 0.0; 113 | m_max = -100.0; 114 | m_histogram.clear(); 115 | m_histogram_max = 0; 116 | for (const auto &s : m_data_segments) { 117 | m_min = std::min(m_min, s.value); 118 | m_max = std::max(m_max, s.value); 119 | const auto int_value = static_cast(s.value); 120 | m_histogram[int_value] += 1; 121 | m_histogram_max = std::max(m_histogram_max, m_histogram[int_value]); 122 | m_points_path.addRect(s.x - PATH_DISTANCE, s.y - PATH_DISTANCE, 123 | 2 * PATH_DISTANCE, 2 * PATH_DISTANCE); 124 | } 125 | calculate_colors(); 126 | } 127 | 128 | void MeasurementItem::calculate_colors() { 129 | for (auto &s : m_data_segments) { 130 | s.color = get_color(s.value); 131 | } 132 | if (m_color_map && m_max > m_min && m_robot_map.is_valid()) { 133 | QPainter painter; 134 | painter.begin(&m_legend); 135 | painter.setFont(m_font); 136 | 137 | painter.setBrush(Qt::black); 138 | painter.setPen(Qt::transparent); 139 | painter.drawRect(0, 0, SCALE_WIDTH + 2 * SCALE_MARGIN, 140 | SCALE_SIZE.height() + 2 * SCALE_MARGIN); 141 | 142 | if (m_histogram_max > 0) { 143 | painter.setPen(Qt::transparent); 144 | const auto int_max = static_cast(m_max); 145 | const auto int_min = static_cast(m_min); 146 | const auto bar_width = 147 | static_cast(SCALE_WIDTH) / (int_max - int_min + 1); 148 | std::for_each( 149 | m_histogram.begin(), m_histogram.end(), [&](const auto values) { 150 | const auto bin = values.first; 151 | const auto count = values.second; 152 | const auto height = SCALE_HISTOGRAM_HEIGHT * 153 | static_cast(count) / m_histogram_max; 154 | const auto x = SCALE_MARGIN + bar_width * (bin - int_min); 155 | auto bin_rect = 156 | QRectF(x, SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT - height, 157 | bar_width, height); 158 | painter.setBrush(get_color(bin)); 159 | painter.drawRect(bin_rect); 160 | }); 161 | painter.setBrush(Qt::transparent); 162 | painter.setPen(QPen(Qt::white, 1)); 163 | painter.drawRect(SCALE_MARGIN, SCALE_MARGIN, SCALE_WIDTH, 164 | SCALE_HISTOGRAM_HEIGHT); 165 | } 166 | 167 | painter.setPen(Qt::transparent); 168 | 169 | for (int x = 0; x < SCALE_WIDTH; x++) { 170 | painter.setBrush(color_value(static_cast(x) / SCALE_WIDTH)); 171 | painter.drawRect(x + SCALE_MARGIN, 172 | 2 * SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT, 1, 173 | SCALE_BAR_HEIGHT / 3); 174 | } 175 | painter.setBrush(Qt::transparent); 176 | painter.setPen(QPen(Qt::white, 1)); 177 | painter.drawRect(SCALE_MARGIN, 2 * SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT, 178 | SCALE_WIDTH, SCALE_BAR_HEIGHT / 3); 179 | painter.drawText( 180 | SCALE_MARGIN, 181 | 2 * SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT + SCALE_BAR_HEIGHT / 3, 182 | SCALE_WIDTH / 2, (SCALE_BAR_HEIGHT / 3) * 2, Qt::AlignTop, 183 | QString::number(m_min, 'f', 1).append(" dBm")); 184 | painter.drawText( 185 | SCALE_MARGIN + SCALE_WIDTH / 2, 186 | 2 * SCALE_MARGIN + SCALE_HISTOGRAM_HEIGHT + SCALE_BAR_HEIGHT / 3, 187 | SCALE_WIDTH / 2, (SCALE_BAR_HEIGHT / 3) * 2, 188 | Qt::AlignTop | Qt::AlignRight, 189 | QString::number(m_max, 'f', 1).append(" dBm")); 190 | 191 | painter.end(); 192 | } 193 | update(); 194 | } 195 | 196 | void MeasurementItem::set_restrict_path(bool enabled) { 197 | m_restrict_path = enabled; 198 | update(); 199 | } 200 | 201 | void MeasurementItem::set_restrict_path(const QPainterPath &path) { 202 | m_path = path; 203 | m_path.setFillRule(Qt::WindingFill); 204 | m_restrict_path = true; 205 | update(); 206 | } 207 | 208 | void MeasurementItem::set_restrict_points(bool enabled) { 209 | m_restrict_points = enabled; 210 | update(); 211 | } 212 | 213 | QRectF MeasurementItem::boundingRect() const { 214 | auto rect = MapBasedItem::boundingRect(); 215 | if (!m_robot_map.is_valid()) { 216 | return rect; 217 | } 218 | if (rect.width() < SCALE_WIDTH + SCALE_MARGIN) { 219 | rect += QMarginsF(0, 0, SCALE_WIDTH + SCALE_MARGIN, 0); 220 | } 221 | return rect + QMarginsF(0, 0, 0, SCALE_SIZE.height() + 4 * SCALE_MARGIN); 222 | } 223 | 224 | } // namespace Valeronoi::gui::graphics_item 225 | -------------------------------------------------------------------------------- /src/gui/graphics_item/measurement_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_GRAPHICS_ITEM_MEASUREMENT_ITEM_H 19 | #define VALERONOI_GUI_GRAPHICS_ITEM_MEASUREMENT_ITEM_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "../../state/measurements.h" 26 | #include "../../state/robot_map.h" 27 | #include "../../state/state.h" 28 | #include "../../util/colormap.h" 29 | #include "map_based_item.h" 30 | 31 | namespace Valeronoi::gui::graphics_item { 32 | 33 | class MeasurementItem : public MapBasedItem { 34 | public: 35 | explicit MeasurementItem(const Valeronoi::state::RobotMap &robot_map, 36 | QGraphicsItem *parent = nullptr); 37 | 38 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 39 | QWidget *widget) override; 40 | 41 | void set_data_segments(const Valeronoi::state::DataSegments &segments); 42 | 43 | void set_display_mode(Valeronoi::state::DISPLAY_MODE display_mode); 44 | 45 | void set_color_map(const Valeronoi::util::RGBColorMap *color_map); 46 | 47 | void set_restrict_path(bool enabled); 48 | 49 | void set_restrict_path(const QPainterPath &path); 50 | 51 | void set_restrict_points(bool enabled); 52 | 53 | [[nodiscard]] QRectF boundingRect() const override; 54 | 55 | private: 56 | void calculate_colors(); 57 | 58 | [[nodiscard]] QColor get_color(double value) const; 59 | 60 | [[nodiscard]] QColor color_value(double normalized_value) const; 61 | 62 | double m_min{0.0}, m_max{0.0}; 63 | std::unordered_map m_histogram; 64 | int m_histogram_max{0}; 65 | 66 | const Valeronoi::util::RGBColorMap *m_color_map{nullptr}; 67 | 68 | Valeronoi::state::DISPLAY_MODE m_display_mode{ 69 | Valeronoi::state::DISPLAY_MODE::Voronoi}; 70 | 71 | Valeronoi::state::DataSegments m_data_segments; 72 | 73 | bool m_restrict_path{true}, m_restrict_points{true}; 74 | QPainterPath m_path, m_points_path; 75 | QFont m_font; 76 | QPicture m_legend; 77 | }; 78 | 79 | } // namespace Valeronoi::gui::graphics_item 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/gui/widget/display_widget.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_GUI_WIDGET_DISPLAY_WIDGET_H 19 | #define VALERONOI_GUI_WIDGET_DISPLAY_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../../state/measurements.h" 28 | #include "../../state/robot_map.h" 29 | #include "../../util/colormap.h" 30 | #include "../../util/segment_generator.h" 31 | #include "../graphics_item/entity_item.h" 32 | #include "../graphics_item/floor_item.h" 33 | #include "../graphics_item/map_item.h" 34 | #include "../graphics_item/measurement_item.h" 35 | 36 | namespace Valeronoi::gui::widget { 37 | 38 | class DisplayWidget : public QGraphicsView { 39 | Q_OBJECT 40 | public: 41 | explicit DisplayWidget(const Valeronoi::state::RobotMap &robot_map, 42 | const Valeronoi::state::Measurements &measurements, 43 | QWidget *parent); 44 | 45 | [[nodiscard]] QColor get_background_color() const; 46 | 47 | void set_background_color(QColor color); 48 | 49 | [[nodiscard]] QColor get_wall_color() const; 50 | 51 | void set_wall_color(QColor color); 52 | 53 | void set_color_map(const Valeronoi::util::RGBColorMap *color_map); 54 | 55 | void set_floor(bool enabled, QColor color); 56 | 57 | [[nodiscard]] bool get_floor_enabled() const; 58 | 59 | [[nodiscard]] QColor get_floor_color() const; 60 | 61 | void set_entities(bool enabled); 62 | 63 | [[nodiscard]] bool get_entities_enabled() const; 64 | 65 | [[nodiscard]] qreal get_zoom_factor() const; 66 | 67 | void set_opengl(bool enabled); 68 | 69 | [[nodiscard]] bool get_opengl() const; 70 | 71 | void set_antialiasing(bool enabled); 72 | 73 | [[nodiscard]] bool get_antialiasing() const; 74 | 75 | void set_restrict_floor(bool enabled); 76 | 77 | [[nodiscard]] bool get_restrict_floor() const; 78 | 79 | void set_restrict_path(bool enabled); 80 | 81 | [[nodiscard]] bool get_restrict_path() const; 82 | 83 | [[nodiscard]] int get_simplify() const; 84 | 85 | [[nodiscard]] int get_wifi_id_filter() const; 86 | 87 | signals: 88 | void signal_relocate(int x, int y); 89 | 90 | public slots: 91 | void slot_map_updated(); 92 | 93 | void slot_measurements_updated(); 94 | 95 | void slot_set_display_mode(int display_mode); 96 | 97 | void slot_update_map_rect(); 98 | 99 | void slot_set_simplify(int value); 100 | 101 | void slot_set_wifi_id_filter(int wifi_id_filter); 102 | 103 | protected: 104 | void wheelEvent(QWheelEvent *event) override; 105 | void paintEvent(QPaintEvent *event) override; 106 | 107 | private: 108 | void zoom_by(qreal factor); 109 | 110 | QColor m_background_color, m_wall_color, m_floor_color; 111 | bool m_draw_floor{true}, m_draw_entities{true}, m_use_opengl{false}, 112 | m_antialiasing{true}, m_restrict_floor{true}, m_restrict_path{true}; 113 | int m_simplify{1}, m_wifi_id_filter{-1}; 114 | const Valeronoi::util::RGBColorMap *m_color_map{nullptr}; 115 | Valeronoi::util::SegmentGenerator m_segment_generator; 116 | 117 | Valeronoi::state::DISPLAY_MODE m_display_mode{ 118 | Valeronoi::state::DISPLAY_MODE::Voronoi}; 119 | 120 | const Valeronoi::state::Measurements &m_measurements; 121 | 122 | Valeronoi::gui::graphics_item::MapItem *m_map_item; 123 | Valeronoi::gui::graphics_item::FloorItem *m_floor_item; 124 | Valeronoi::gui::graphics_item::EntityItem *m_entity_item; 125 | Valeronoi::gui::graphics_item::MeasurementItem *m_measurement_item; 126 | 127 | QPainterPath m_floor_path; 128 | }; 129 | 130 | } // namespace Valeronoi::gui::widget 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "config.h" 24 | #include "state/state.h" 25 | #include "util/log_helper.h" 26 | #include "valeronoi.h" 27 | 28 | int main(int argc, char **argv) { 29 | qInstallMessageHandler(Valeronoi::util::log_handler); 30 | 31 | QApplication app(argc, argv); 32 | QCoreApplication::setOrganizationName("ccoors"); 33 | QCoreApplication::setOrganizationDomain("ccoors.de"); 34 | QCoreApplication::setApplicationName("Valeronoi"); 35 | QCoreApplication::setApplicationVersion(VALERONOI_VERSION); 36 | 37 | Q_INIT_RESOURCE(valeronoi); 38 | QFontDatabase::addApplicationFont(":/res/SourceCodePro-Regular.otf"); 39 | QApplication::setWindowIcon(QIcon(":/res/valeronoi.png")); 40 | 41 | qRegisterMetaType(); 42 | 43 | QCommandLineParser parser; 44 | parser.setApplicationDescription( 45 | "A WiFi signal strength mapping companion for Valetudo"); 46 | parser.addHelpOption(); 47 | parser.addVersionOption(); 48 | parser.addPositionalArgument("file", "The file to open."); 49 | parser.process(app); 50 | 51 | Valeronoi::ValeronoiWindow valeronoi; 52 | if (!parser.positionalArguments().isEmpty()) { 53 | if (!valeronoi.load_file(parser.positionalArguments().value(0))) { 54 | return 1; 55 | } 56 | } 57 | valeronoi.show(); 58 | 59 | return app.exec(); 60 | } 61 | -------------------------------------------------------------------------------- /src/res/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/src/res/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /src/res/valeronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoors/Valeronoi/d4ffe5d01598f2c792580b5d06937028bf624cab/src/res/valeronoi.png -------------------------------------------------------------------------------- /src/robot/api/sse.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "sse.h" 19 | 20 | namespace Valeronoi::robot::api { 21 | 22 | SSEConnection::SSEConnection() { 23 | m_reconnect_timer.setSingleShot(true); 24 | connect(&m_reconnect_timer, &QTimer::timeout, this, 25 | &SSEConnection::slot_make_request); 26 | } 27 | 28 | void SSEConnection::set_connection_configuration( 29 | const ConnectionConfiguration &conf) { 30 | m_connection_configuration = conf; 31 | } 32 | 33 | void SSEConnection::set_initial_url(const QUrl &url) { m_initial_url = url; } 34 | 35 | void SSEConnection::set_url(const QUrl &url) { m_url = url; } 36 | 37 | QString SSEConnection::current_data() const { return m_current_data; } 38 | 39 | void SSEConnection::slot_connect() { 40 | if (m_connected) { 41 | return; 42 | } 43 | 44 | if (!m_initial_url.isEmpty()) { 45 | qDebug() << "Making initial request"; 46 | m_initial_current_data = ""; 47 | m_current_data = ""; 48 | QNetworkRequest request = prepare_request(m_initial_url); 49 | 50 | auto reply = m_qnam.get(request); 51 | connect(reply, &QNetworkReply::readyRead, this, [=]() { 52 | qDebug() << "Received initial map data"; 53 | auto buffer = reply->readAll(); 54 | m_initial_current_data.append(buffer); 55 | }); 56 | connect(reply, &QNetworkReply::errorOccurred, this, 57 | [=](QNetworkReply::NetworkError error) { 58 | if (error != QNetworkReply::NetworkError::NoError) { 59 | qDebug().nospace() 60 | << "Initial request error: " << reply->errorString(); 61 | } 62 | }); 63 | connect(reply, &QNetworkReply::finished, this, [=]() { 64 | if (!m_initial_current_data.isEmpty() && m_current_data.isEmpty()) { 65 | m_current_data = m_initial_current_data; 66 | emit signal_data_updated(); 67 | } 68 | 69 | reply->deleteLater(); 70 | }); 71 | } 72 | 73 | qDebug() << "Starting SSE request"; 74 | m_connected = true; 75 | slot_make_request(); 76 | } 77 | 78 | void SSEConnection::slot_ready_read() { 79 | if (!m_reply) { 80 | // Should not happen 81 | return; 82 | } 83 | // m_reply->readAll returns chunked data, so we put it back together and 84 | // parse it according to https://www.w3.org/TR/eventsource/ 85 | const auto buffer = m_reply->readAll(); 86 | qDebug().nospace() << "Received SSE segment: size " << buffer.size(); 87 | QStringList lines = QString(buffer).split("\n"); 88 | 89 | for (const QString &raw_line : lines) { 90 | QString line = raw_line.simplified(); 91 | 92 | switch (m_parser_state) { 93 | case ParserState::IDLE: 94 | if (line.startsWith(":")) { 95 | continue; 96 | } else if (line.startsWith("event: ")) { 97 | m_event_type = line.right(line.size() - 7); 98 | } else if (line.startsWith("data: ") && m_event_type == m_event) { 99 | m_data_buffer = line.right(line.size() - 6); 100 | m_parser_state = ParserState::DATA; 101 | } 102 | break; 103 | case ParserState::DATA: 104 | if (line.isEmpty()) { 105 | m_parser_state = ParserState::IDLE; 106 | m_current_data = m_data_buffer; 107 | emit signal_data_updated(); 108 | } else { 109 | m_data_buffer.append(line); 110 | } 111 | } 112 | } 113 | } 114 | 115 | void SSEConnection::slot_disconnect() { 116 | if (!m_connected) { 117 | return; 118 | } 119 | qDebug() << "SSE connection closed"; 120 | m_connected = false; 121 | if (m_reply) { 122 | m_reply->abort(); 123 | m_reply = nullptr; 124 | } 125 | } 126 | 127 | void SSEConnection::slot_stream_finished() { 128 | qDebug() << "SSE connection finished"; 129 | m_reply->deleteLater(); 130 | m_reply = nullptr; 131 | if (m_connected) { 132 | m_reconnect_timer.start(RECONNECT_TIMER); 133 | } 134 | } 135 | 136 | void SSEConnection::slot_make_request() { 137 | if (!m_connected) { 138 | return; 139 | } 140 | QNetworkRequest request = prepare_request(m_url); 141 | 142 | m_reply = m_qnam.get(request); 143 | connect(m_reply, &QNetworkReply::readyRead, this, 144 | &SSEConnection::slot_ready_read); 145 | connect(m_reply, &QNetworkReply::finished, this, 146 | &SSEConnection::slot_stream_finished); 147 | } 148 | 149 | QNetworkRequest SSEConnection::prepare_request(const QUrl &url) const { 150 | QNetworkRequest request(m_connection_configuration.m_url.resolved(url)); 151 | m_connection_configuration.prepare_request(request); 152 | request.setRawHeader("Accept", "text/event-stream"); 153 | return request; 154 | } 155 | 156 | void SSEConnection::set_event(const QString &event) { m_event = event; } 157 | 158 | } // namespace Valeronoi::robot::api 159 | -------------------------------------------------------------------------------- /src/robot/api/sse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_ROBOT_API_SSE_H 19 | #define VALERONOI_ROBOT_API_SSE_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "../connection_configuration.h" 34 | 35 | namespace Valeronoi::robot::api { 36 | 37 | constexpr const std::chrono::milliseconds RECONNECT_TIMER{2000}; 38 | 39 | enum class ParserState { IDLE = 0, DATA }; 40 | 41 | class SSEConnection : public QObject { 42 | Q_OBJECT 43 | public: 44 | SSEConnection(); 45 | 46 | void set_connection_configuration(const ConnectionConfiguration &conf); 47 | 48 | void set_initial_url(const QUrl &url); 49 | 50 | void set_url(const QUrl &url); 51 | 52 | void set_event(const QString &event); 53 | 54 | [[nodiscard]] QString current_data() const; 55 | 56 | public slots: 57 | void slot_connect(); 58 | 59 | void slot_disconnect(); 60 | 61 | signals: 62 | void signal_data_updated(); 63 | 64 | private slots: 65 | void slot_ready_read(); 66 | 67 | void slot_stream_finished(); 68 | 69 | void slot_make_request(); 70 | 71 | private: 72 | QNetworkRequest prepare_request(const QUrl &url) const; 73 | 74 | bool m_connected{false}; 75 | QString m_event, m_data_buffer, m_initial_current_data, m_current_data, 76 | m_event_type; 77 | ParserState m_parser_state{ParserState::IDLE}; 78 | QTimer m_reconnect_timer; 79 | ConnectionConfiguration m_connection_configuration; 80 | QUrl m_initial_url, m_url; 81 | QNetworkReply *m_reply{nullptr}; 82 | QNetworkAccessManager m_qnam; 83 | }; 84 | 85 | } // namespace Valeronoi::robot::api 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/robot/api/valetudo_v2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "valetudo_v2.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace Valeronoi::robot::api::v2 { 26 | 27 | const QUrl VALETUDO_VERSION = QUrl("/api/v2/valetudo/version"); 28 | const QUrl ROBOT_INFO = QUrl("/api/v2/robot"); 29 | const QUrl ROBOT_STATE = QUrl("/api/v2/robot/state"); 30 | const QUrl ROBOT_CAPABILITIES_INFO = QUrl("/api/v2/robot/capabilities"); 31 | const QUrl ROBOT_MAP = QUrl("/api/v2/robot/state/map"); 32 | const QUrl ROBOT_MAP_SSE = QUrl("/api/v2/robot/state/map/sse"); 33 | 34 | const QList ROBOT_INIT_URLS = {VALETUDO_VERSION, ROBOT_INFO, ROBOT_STATE, 35 | ROBOT_CAPABILITIES_INFO}; 36 | 37 | ValetudoAPI::ValetudoAPI() { 38 | m_map_connection.set_initial_url(ROBOT_MAP); 39 | m_map_connection.set_url(ROBOT_MAP_SSE); 40 | m_map_connection.set_event("MapUpdated"); 41 | connect(&m_map_connection, 42 | &Valeronoi::robot::api::SSEConnection::signal_data_updated, this, 43 | &ValetudoAPI::slot_map_updated); 44 | } 45 | 46 | void ValetudoAPI::set_connection_configuration( 47 | const ConnectionConfiguration &conf) { 48 | m_connection_configuration = conf; 49 | m_map_connection.set_connection_configuration(conf); 50 | } 51 | 52 | const RobotInformation *ValetudoAPI::get_information() const { 53 | return &m_robot_information; 54 | } 55 | 56 | QString ValetudoAPI::get_error() const { return m_error_message; } 57 | 58 | bool ValetudoAPI::is_connected() const { return m_connected; } 59 | 60 | bool ValetudoAPI::is_connecting() const { return m_connecting; } 61 | 62 | void ValetudoAPI::slot_connect() { 63 | if (m_connected || m_connecting) { 64 | return; 65 | } 66 | m_connecting = true; 67 | m_connecting_step = 0; 68 | m_connection_responses.clear(); 69 | m_error_message = ""; 70 | emit signal_connecting(); 71 | next_connection_step(); 72 | } 73 | 74 | void ValetudoAPI::slot_disconnect() { 75 | if (!m_connected && !m_connecting) { 76 | return; 77 | } 78 | m_connected = false; 79 | m_connecting = false; 80 | m_map_connection.slot_disconnect(); 81 | emit signal_connection_ended(); 82 | } 83 | 84 | void ValetudoAPI::next_connection_step() { 85 | if (m_connecting_step == ROBOT_INIT_URLS.size()) { 86 | // We're done 87 | m_connecting = false; 88 | m_connected = true; 89 | 90 | // Parse responses from connection sequence 91 | const auto valetudo_version = 92 | m_connection_responses[0].object()["release"].toString(); 93 | m_robot_information.m_valetudo_version = valetudo_version; 94 | 95 | const auto robot_info = m_connection_responses[1].object(); 96 | m_robot_information.m_manufacturer = robot_info["manufacturer"].toString(); 97 | m_robot_information.m_model_name = robot_info["modelName"].toString(); 98 | m_robot_information.m_implementation = 99 | robot_info["implementation"].toString(); 100 | 101 | const auto robot_state = m_connection_responses[2].object(); 102 | m_robot_information.m_attributes = robot_state["attributes"].toArray(); 103 | 104 | const auto robot_capabilities = m_connection_responses[3].array(); 105 | m_robot_information.m_capabilities.clear(); 106 | for (const auto &&c : robot_capabilities) { 107 | m_robot_information.m_capabilities.push_back(c.toString()); 108 | } 109 | 110 | m_map_connection.slot_connect(); 111 | emit signal_connected(); 112 | return; 113 | } 114 | 115 | emit signal_connecting_step(static_cast(m_connecting_step) / 116 | ROBOT_INIT_URLS.size()); 117 | const auto next_url = ROBOT_INIT_URLS[m_connecting_step]; 118 | auto request = 119 | QNetworkRequest(m_connection_configuration.m_url.resolved(next_url)); 120 | m_connection_configuration.prepare_request(request); 121 | auto reply = m_qnam.get(request); 122 | connect(reply, &QNetworkReply::finished, this, [=]() { 123 | if (m_connecting) { 124 | if (reply->error() != QNetworkReply::NetworkError::NoError) { 125 | m_error_message = reply->errorString(); 126 | emit signal_connection_error(); 127 | slot_disconnect(); 128 | } else { 129 | auto resp_data = reply->readAll(); 130 | QJsonParseError error; 131 | auto json = QJsonDocument::fromJson(resp_data, &error); 132 | if (json.isNull()) { 133 | // Invalid JSON received 134 | m_error_message = error.errorString(); 135 | emit signal_connection_error(); 136 | slot_disconnect(); 137 | } 138 | m_connection_responses.push_back(json); 139 | next_connection_step(); 140 | } 141 | } 142 | reply->deleteLater(); 143 | }); 144 | 145 | m_connecting_step++; 146 | } 147 | 148 | QNetworkReply *ValetudoAPI::request(const QString &verb, const QUrl &url, 149 | bool disconnect_on_failure, bool gc, 150 | const QByteArray *data) { 151 | qDebug().nospace() << "Robot request: " << verb << " to " << url; 152 | if (!m_connected) { 153 | return nullptr; 154 | } 155 | auto request = 156 | QNetworkRequest(m_connection_configuration.m_url.resolved(url)); 157 | m_connection_configuration.prepare_request(request); 158 | if (data != nullptr) { 159 | request.setRawHeader("Content-Type", "application/json"); 160 | } 161 | auto response = data != nullptr 162 | ? m_qnam.sendCustomRequest(request, verb.toUtf8(), *data) 163 | : m_qnam.sendCustomRequest(request, verb.toUtf8()); 164 | if (disconnect_on_failure) { 165 | connect(response, &QNetworkReply::errorOccurred, this, 166 | [=](QNetworkReply::NetworkError error) { 167 | if (error != QNetworkReply::NetworkError::NoError) { 168 | m_error_message = response->errorString(); 169 | emit signal_connection_error(); 170 | slot_disconnect(); 171 | } 172 | }); 173 | } 174 | if (gc) { 175 | connect(response, &QNetworkReply::finished, this, 176 | [=]() { response->deleteLater(); }); 177 | } 178 | return response; 179 | } 180 | 181 | void ValetudoAPI::slot_map_updated() { emit signal_map_updated(); } 182 | 183 | QString ValetudoAPI::get_map_data() const { 184 | return m_map_connection.current_data(); 185 | } 186 | 187 | static QJsonDocument gen_document(const QString &action) { 188 | auto ret = QJsonDocument(); 189 | auto object = QJsonObject(); 190 | object.insert("action", action); 191 | ret.setObject(object); 192 | return ret; 193 | } 194 | 195 | void ValetudoAPI::send_command(Valeronoi::robot::BASIC_COMMANDS command) { 196 | if (!m_connected) { 197 | return; 198 | } 199 | switch (command) { 200 | case BASIC_COMMANDS::START: { 201 | auto data = gen_document("start").toJson(); 202 | request("PUT", QUrl("/api/v2/robot/capabilities/BasicControlCapability"), 203 | false, true, &data); 204 | } break; 205 | case BASIC_COMMANDS::PAUSE: { 206 | auto data = gen_document("pause").toJson(); 207 | request("PUT", QUrl("/api/v2/robot/capabilities/BasicControlCapability"), 208 | false, true, &data); 209 | } break; 210 | case BASIC_COMMANDS::STOP: { 211 | auto data = gen_document("stop").toJson(); 212 | request("PUT", QUrl("/api/v2/robot/capabilities/BasicControlCapability"), 213 | false, true, &data); 214 | } break; 215 | case BASIC_COMMANDS::HOME: { 216 | auto data = gen_document("home").toJson(); 217 | request("PUT", QUrl("/api/v2/robot/capabilities/BasicControlCapability"), 218 | false, true, &data); 219 | } break; 220 | case BASIC_COMMANDS::LOCATE: { 221 | auto data = gen_document("locate").toJson(); 222 | request("PUT", QUrl("/api/v2/robot/capabilities/LocateCapability"), false, 223 | true, &data); 224 | } 225 | } 226 | } 227 | 228 | void ValetudoAPI::relocate(int x, int y) { 229 | if (!m_connected) { 230 | QMessageBox::information(nullptr, "Valeronoi", 231 | tr("Not connected to a robot.")); 232 | return; 233 | } 234 | if (!m_robot_information.m_capabilities.contains("GoToLocationCapability")) { 235 | QMessageBox::information( 236 | nullptr, "Valeronoi", 237 | tr("This robot does not support the GoToLocationCapability.")); 238 | return; 239 | } 240 | auto document = QJsonDocument(); 241 | auto object = QJsonObject(); 242 | object.insert("action", "goto"); 243 | auto coords = QJsonObject(); 244 | coords.insert("x", x); 245 | coords.insert("y", y); 246 | object.insert("coordinates", coords); 247 | document.setObject(object); 248 | auto data = document.toJson(); 249 | request("PUT", QUrl("/api/v2/robot/capabilities/GoToLocationCapability"), 250 | false, true, &data); 251 | } 252 | 253 | } // namespace Valeronoi::robot::api::v2 254 | -------------------------------------------------------------------------------- /src/robot/api/valetudo_v2.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_ROBOT_API_VALETUDO_V2_H 19 | #define VALERONOI_ROBOT_API_VALETUDO_V2_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "../commands.h" 33 | #include "../connection_configuration.h" 34 | #include "../robot_information.h" 35 | #include "sse.h" 36 | 37 | namespace Valeronoi::robot::api::v2 { 38 | 39 | const QUrl ROBOT_WIFI_CAPABILITY = 40 | QUrl("/api/v2/robot/capabilities/WifiConfigurationCapability"); 41 | 42 | class ValetudoAPI : public QObject { 43 | Q_OBJECT 44 | public: 45 | ValetudoAPI(); 46 | 47 | void set_connection_configuration(const ConnectionConfiguration &conf); 48 | 49 | [[nodiscard]] const RobotInformation *get_information() const; 50 | 51 | [[nodiscard]] QString get_error() const; 52 | 53 | [[nodiscard]] bool is_connected() const; 54 | 55 | [[nodiscard]] bool is_connecting() const; 56 | 57 | QNetworkReply *request(const QString &verb, const QUrl &url, 58 | bool disconnect_on_failure = false, bool gc = false, 59 | const QByteArray *data = nullptr); 60 | 61 | [[nodiscard]] QString get_map_data() const; 62 | 63 | void send_command(Valeronoi::robot::BASIC_COMMANDS command); 64 | 65 | void relocate(int x, int y); 66 | 67 | public slots: 68 | void slot_connect(); 69 | 70 | void slot_disconnect(); 71 | 72 | signals: 73 | void signal_connecting(); 74 | 75 | void signal_connecting_step(float value); 76 | 77 | void signal_connected(); 78 | 79 | void signal_connection_error(); 80 | 81 | void signal_connection_ended(); 82 | 83 | void signal_map_updated(); 84 | 85 | private slots: 86 | void slot_map_updated(); 87 | 88 | private: 89 | void next_connection_step(); 90 | 91 | bool m_connected{false}, m_connecting{false}; 92 | int m_connecting_step{0}; 93 | RobotInformation m_robot_information; 94 | ConnectionConfiguration m_connection_configuration; 95 | 96 | QString m_error_message; 97 | 98 | QNetworkAccessManager m_qnam; 99 | QList m_connection_responses; 100 | 101 | Valeronoi::robot::api::SSEConnection m_map_connection; 102 | }; 103 | 104 | } // namespace Valeronoi::robot::api::v2 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/robot/commands.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_ROBOT_COMMANDS_H 19 | #define VALERONOI_ROBOT_COMMANDS_H 20 | 21 | namespace Valeronoi::robot { 22 | 23 | enum class BASIC_COMMANDS { START, PAUSE, STOP, HOME, LOCATE }; 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/robot/connection_configuration.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "connection_configuration.h" 19 | 20 | #include 21 | 22 | #include "config.h" 23 | 24 | namespace Valeronoi::robot { 25 | 26 | void ConnectionConfiguration::read_settings() { 27 | QSettings settings; 28 | m_url = settings.value("robot/url", "").toUrl(); 29 | m_auth = settings.value("robot/auth/enabled", false).toBool(); 30 | m_username = settings.value("robot/auth/username", "").toString(); 31 | m_password = settings.value("robot/auth/password", "").toString(); 32 | } 33 | 34 | ConnectionConfiguration &ConnectionConfiguration::operator=( 35 | const ConnectionConfiguration &other) { 36 | if (this != &other) { 37 | m_url = other.m_url; 38 | m_auth = other.m_auth; 39 | m_username = other.m_username; 40 | m_password = other.m_password; 41 | } 42 | return *this; 43 | } 44 | 45 | bool ConnectionConfiguration::is_valid() const { 46 | return !m_url.isEmpty() && m_url.isValid(); 47 | } 48 | 49 | void ConnectionConfiguration::prepare_request(QNetworkRequest &r) const { 50 | r.setMaximumRedirectsAllowed(10); // Should not be needed anyway 51 | r.setAttribute(QNetworkRequest::CacheLoadControlAttribute, 52 | QNetworkRequest::AlwaysNetwork); 53 | r.setTransferTimeout(5000); // TODO configurable? 54 | r.setHeader(QNetworkRequest::UserAgentHeader, "Valeronoi/" VALERONOI_VERSION); 55 | if (m_auth) { 56 | QString user_pw = m_username + ":" + m_password; 57 | QByteArray data = user_pw.toLocal8Bit().toBase64(); 58 | QString header_data = "Basic " + data; 59 | r.setRawHeader("Authorization", header_data.toLocal8Bit()); 60 | } 61 | } 62 | 63 | } // namespace Valeronoi::robot 64 | -------------------------------------------------------------------------------- /src/robot/connection_configuration.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_ROBOT_CONNECTION_INFORMATION_H 19 | #define VALERONOI_ROBOT_CONNECTION_INFORMATION_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace Valeronoi::robot { 26 | 27 | struct ConnectionConfiguration { 28 | QUrl m_url; 29 | bool m_auth; 30 | QString m_username, m_password; 31 | 32 | void read_settings(); 33 | 34 | [[nodiscard]] bool is_valid() const; 35 | 36 | ConnectionConfiguration& operator=(const ConnectionConfiguration& other); 37 | 38 | void prepare_request(QNetworkRequest& r) const; 39 | }; 40 | 41 | } // namespace Valeronoi::robot 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/robot/robot.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "robot.h" 19 | 20 | #include "../gui/dialog/robot_config.h" 21 | 22 | namespace Valeronoi::robot { 23 | 24 | Robot::Robot() { 25 | // Reroute signals from API 26 | connect(&m_api, &Valeronoi::robot::api::v2::ValetudoAPI::signal_connecting, 27 | this, [=]() { emit signal_connecting(); }); 28 | connect(&m_api, 29 | &Valeronoi::robot::api::v2::ValetudoAPI::signal_connecting_step, this, 30 | [=](float value) { emit signal_connecting_step(value); }); 31 | connect(&m_api, &Valeronoi::robot::api::v2::ValetudoAPI::signal_connected, 32 | this, [=]() { emit signal_connected(); }); 33 | connect(&m_api, 34 | &Valeronoi::robot::api::v2::ValetudoAPI::signal_connection_error, 35 | this, [=]() { emit signal_connection_error(); }); 36 | connect(&m_api, 37 | &Valeronoi::robot::api::v2::ValetudoAPI::signal_connection_ended, 38 | this, [=]() { emit signal_connection_ended(); }); 39 | connect(&m_api, &Valeronoi::robot::api::v2::ValetudoAPI::signal_map_updated, 40 | this, [=]() { emit signal_map_updated(); }); 41 | connect(&m_wifi_timer, &QTimer::timeout, this, &Robot::slot_get_wifi); 42 | m_wifi_timer.setTimerType(Qt::CoarseTimer); 43 | m_wifi_timer.setSingleShot(false); 44 | } 45 | 46 | void Robot::set_connection_configuration(const ConnectionConfiguration &conf) { 47 | m_api.set_connection_configuration(conf); 48 | } 49 | 50 | QString Robot::get_error() const { return m_api.get_error(); } 51 | 52 | bool Robot::is_connected() const { return m_api.is_connected(); } 53 | 54 | bool Robot::is_connecting() const { return m_api.is_connecting(); } 55 | 56 | const RobotInformation *Robot::get_information() const { 57 | return m_api.get_information(); 58 | } 59 | 60 | void Robot::slot_connect() { 61 | m_current_wifi_connection = WifiInformation(); 62 | m_api.slot_connect(); 63 | } 64 | 65 | void Robot::slot_disconnect() { 66 | m_api.slot_disconnect(); 67 | m_current_wifi_connection = WifiInformation(); 68 | } 69 | 70 | void Robot::slot_subscribe_wifi(double interval) { 71 | m_wifi_timer.start(static_cast(1000 * interval)); 72 | } 73 | 74 | void Robot::slot_stop_wifi_subscription() { m_wifi_timer.stop(); } 75 | 76 | void Robot::slot_get_wifi() { 77 | if (is_connected()) { 78 | qDebug() << "Making WiFi Request"; 79 | auto reply = 80 | m_api.request("GET", Valeronoi::robot::api::v2::ROBOT_WIFI_CAPABILITY); 81 | connect(reply, &QNetworkReply::finished, this, [=]() { 82 | auto resp_data = reply->readAll(); 83 | reply->deleteLater(); 84 | QJsonParseError error{}; 85 | const auto json = QJsonDocument::fromJson(resp_data, &error); 86 | if (json.isNull()) { 87 | qDebug() << "Invalid JSON:" << error.errorString(); 88 | return; 89 | } 90 | 91 | const auto response_class = json.object()["__class"].toString(); 92 | if (response_class == "ValetudoWifiStatus" || 93 | response_class == "ValetudoWifiConfiguration") { 94 | const QJsonObject details_object = json.object()["details"].toObject(); 95 | const WifiInformation wifi_info(details_object); 96 | if (wifi_info.bssid() != m_current_wifi_connection.bssid()) { 97 | m_current_wifi_connection = wifi_info; 98 | emit signal_current_wifi_updated(wifi_info); 99 | } 100 | if (wifi_info.has_valid_signal()) { 101 | emit signal_wifi_info_updated(wifi_info); 102 | } else { 103 | qDebug() 104 | << "Message did not contain a valid signal strength, ignoring"; 105 | } 106 | } else { 107 | qDebug().nospace() << "Received unexpected JSON object of class '" 108 | << response_class << "', ignoring"; 109 | } 110 | }); 111 | } 112 | } 113 | 114 | QString Robot::get_map_data() const { return m_api.get_map_data(); } 115 | 116 | void Robot::send_command(BASIC_COMMANDS command) { 117 | m_api.send_command(command); 118 | } 119 | 120 | void Robot::slot_relocate(int x, int y) { m_api.relocate(x, y); } 121 | 122 | } // namespace Valeronoi::robot 123 | -------------------------------------------------------------------------------- /src/robot/robot.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_ROBOT_ROBOT_H 19 | #define VALERONOI_ROBOT_ROBOT_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "api/valetudo_v2.h" 29 | #include "commands.h" 30 | #include "connection_configuration.h" 31 | #include "robot_information.h" 32 | #include "wifi_information.h" 33 | 34 | namespace Valeronoi::robot { 35 | 36 | class Robot : public QObject { 37 | Q_OBJECT 38 | public: 39 | Robot(); 40 | 41 | void set_connection_configuration(const ConnectionConfiguration &conf); 42 | 43 | [[nodiscard]] const RobotInformation *get_information() const; 44 | 45 | [[nodiscard]] QString get_error() const; 46 | 47 | [[nodiscard]] bool is_connected() const; 48 | 49 | [[nodiscard]] bool is_connecting() const; 50 | 51 | [[nodiscard]] QString get_map_data() const; 52 | 53 | void send_command(BASIC_COMMANDS command); 54 | 55 | public slots: 56 | void slot_connect(); 57 | 58 | void slot_disconnect(); 59 | 60 | void slot_subscribe_wifi(double interval); 61 | 62 | void slot_stop_wifi_subscription(); 63 | 64 | void slot_relocate(int x, int y); 65 | 66 | signals: 67 | void signal_connecting(); 68 | 69 | void signal_connecting_step(float value); // 0.0 - 1.0 70 | 71 | void signal_connected(); 72 | 73 | void signal_connection_error(); 74 | 75 | void signal_connection_ended(); 76 | 77 | void signal_map_updated(); 78 | 79 | void signal_wifi_info_updated(WifiInformation wifi_info); 80 | 81 | void signal_current_wifi_updated(WifiInformation wifi_info); 82 | 83 | private slots: 84 | void slot_get_wifi(); 85 | 86 | private: 87 | WifiInformation m_current_wifi_connection; 88 | QTimer m_wifi_timer; 89 | Valeronoi::robot::api::v2::ValetudoAPI m_api; 90 | }; 91 | 92 | } // namespace Valeronoi::robot 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/robot/robot_information.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "robot_information.h" 20 | 21 | namespace Valeronoi::robot { 22 | 23 | QJsonObject RobotInformation::get_attribute(const QString &class_name, 24 | const QString &type, 25 | const QString &sub_type) const { 26 | for (const auto &&attr : m_attributes) { 27 | auto attribute = attr.toObject(); 28 | if (!class_name.isEmpty() && 29 | class_name != attribute["__class"].toString()) { 30 | continue; 31 | } 32 | if (!type.isEmpty() && type != attribute["type"].toString()) { 33 | continue; 34 | } 35 | if (!sub_type.isEmpty() && type != attribute["subType"].toString()) { 36 | continue; 37 | } 38 | return attribute; 39 | } 40 | return QJsonObject(); 41 | } 42 | 43 | } // namespace Valeronoi::robot 44 | -------------------------------------------------------------------------------- /src/robot/robot_information.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALETUDO_ROBOT_ROBOT_INFORMATION_H 19 | #define VALETUDO_ROBOT_ROBOT_INFORMATION_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace Valeronoi::robot { 27 | 28 | struct RobotInformation { 29 | QString m_valetudo_version; 30 | QString m_manufacturer, m_model_name, m_implementation; 31 | QStringList m_capabilities; 32 | QJsonArray m_attributes; 33 | 34 | [[nodiscard]] QJsonObject get_attribute(const QString &class_name, 35 | const QString &type = "", 36 | const QString &sub_type = "") const; 37 | }; 38 | 39 | } // namespace Valeronoi::robot 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/robot/wifi_information.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "wifi_information.h" 19 | 20 | namespace Valeronoi::robot { 21 | 22 | class WifiInformationData : public QSharedData { 23 | public: 24 | WifiInformationData() { 25 | m_bssid = "00:00:00:00:00:00"; 26 | m_ssid = "Unknown WiFi"; 27 | m_signal = 0.0; 28 | } 29 | 30 | WifiInformationData(const WifiInformationData &other) 31 | : QSharedData(other), 32 | m_ssid(other.m_ssid), 33 | m_bssid(other.m_bssid), 34 | m_signal(other.m_signal) {} 35 | 36 | ~WifiInformationData() = default; 37 | 38 | QString m_ssid; 39 | QString m_bssid; 40 | 41 | double m_signal; // measurement 42 | 43 | // // Optionally this could be expanded 44 | // QString m_frequency; 45 | // QString m_ip; 46 | // QString m_ipv6; 47 | // double m_upspeed; // Measurement 48 | }; 49 | 50 | WifiInformation::WifiInformation() : m_data(new WifiInformationData) {} 51 | 52 | WifiInformation::WifiInformation(const QJsonObject &jsonObj) 53 | : m_data(new WifiInformationData) { 54 | set_json(jsonObj); 55 | } 56 | 57 | WifiInformation::WifiInformation(const double signal) 58 | : m_data(new WifiInformationData) { 59 | set_signal(signal); 60 | } 61 | 62 | WifiInformation::WifiInformation(double signal, const QString &ssid, 63 | const QString &bssid) 64 | : m_data(new WifiInformationData) { 65 | set_signal(signal); 66 | set_ssid(ssid); 67 | set_bssid(bssid); 68 | } 69 | 70 | WifiInformation::WifiInformation(const WifiInformation &other) 71 | : m_data(other.m_data) {} 72 | 73 | WifiInformation &WifiInformation::operator=(const WifiInformation &rhs) { 74 | if (this != &rhs) m_data.operator=(rhs.m_data); 75 | return *this; 76 | } 77 | 78 | WifiInformation::~WifiInformation() { m_data.reset(); } 79 | 80 | void WifiInformation::set_ssid(const QString &ssid) { m_data->m_ssid = ssid; } 81 | 82 | void WifiInformation::set_bssid(const QString &bssid) { 83 | m_data->m_bssid = bssid; 84 | } 85 | 86 | void WifiInformation::set_signal(double signal) { m_data->m_signal = signal; } 87 | 88 | QString WifiInformation::ssid() const { return m_data->m_ssid; } 89 | 90 | QString WifiInformation::bssid() const { return m_data->m_bssid; } 91 | 92 | double WifiInformation::signal() const { return m_data->m_signal; } 93 | 94 | QJsonObject WifiInformation::get_json() const { 95 | QJsonObject retObj; 96 | 97 | retObj.insert("bssid", m_data->m_bssid); 98 | retObj.insert("ssid", m_data->m_ssid); 99 | retObj.insert("signal", m_data->m_signal); 100 | 101 | return retObj; 102 | } 103 | 104 | void WifiInformation::set_json(QJsonObject jsonObj) { 105 | if (jsonObj.contains("signal") && jsonObj["signal"].isDouble()) { 106 | m_data->m_signal = jsonObj["signal"].toDouble(); 107 | } 108 | 109 | if (jsonObj.contains("bssid") && jsonObj["bssid"].isString()) { 110 | m_data->m_bssid = jsonObj["bssid"].toString(); 111 | } 112 | 113 | if (jsonObj.contains("ssid") && jsonObj["ssid"].isString()) { 114 | m_data->m_ssid = jsonObj["ssid"].toString(); 115 | } 116 | } 117 | 118 | bool WifiInformation::has_valid_signal() const { 119 | return (m_data->m_signal < -1); 120 | } 121 | 122 | } // namespace Valeronoi::robot 123 | -------------------------------------------------------------------------------- /src/robot/wifi_information.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALETUDO_ROBOT_WIFI_INFORMATION_H 19 | #define VALETUDO_ROBOT_WIFI_INFORMATION_H 20 | 21 | #include 22 | #include 23 | 24 | namespace Valeronoi::robot { 25 | 26 | class WifiInformationData; 27 | 28 | class WifiInformation { 29 | public: 30 | WifiInformation(); 31 | explicit WifiInformation(const QJsonObject &jsonObj); 32 | explicit WifiInformation(double signal); 33 | WifiInformation(double signal, const QString &ssid, const QString &bssid); 34 | 35 | WifiInformation(const WifiInformation &); 36 | WifiInformation &operator=(const WifiInformation &); 37 | ~WifiInformation(); 38 | 39 | void set_ssid(const QString &ssid); 40 | void set_bssid(const QString &bssid); 41 | void set_signal(double signal); 42 | 43 | [[nodiscard]] QString ssid() const; 44 | [[nodiscard]] QString bssid() const; 45 | [[nodiscard]] double signal() const; 46 | 47 | [[nodiscard]] QJsonObject get_json() const; 48 | void set_json(QJsonObject jsonObj); 49 | 50 | [[nodiscard]] bool has_valid_signal() const; 51 | 52 | private: 53 | QSharedDataPointer m_data; 54 | }; 55 | 56 | } // namespace Valeronoi::robot 57 | #endif // VALETUDO_ROBOT_WIFI_INFORMATION_H 58 | -------------------------------------------------------------------------------- /src/state/measurements.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "measurements.h" 19 | 20 | namespace Valeronoi::state { 21 | 22 | void Measurements::set_map(const RobotMap &map) { m_map = ↦ } 23 | 24 | void Measurements::slot_add_measurement(double signal, int wifi_id) { 25 | if (m_map != nullptr && m_map->is_valid()) { 26 | if (auto robot_position = m_map->get_map().get_robot_position()) { 27 | add_measurement(robot_position.value().x, robot_position.value().y, 28 | signal, wifi_id); 29 | emit signal_measurements_updated(); 30 | } else { 31 | qDebug() << "Could not find robot on map"; 32 | } 33 | } 34 | } 35 | 36 | QJsonArray Measurements::get_json() const { 37 | QJsonArray arr; 38 | for (const auto &d : m_data) { 39 | auto obj = QJsonObject(); 40 | obj.insert("x", d.x); 41 | obj.insert("y", d.y); 42 | obj.insert("wifi", d.wifi_id); 43 | auto data = QJsonArray(); 44 | for (auto v : d.data) { 45 | data.append(v); 46 | } 47 | obj.insert("data", data); 48 | arr.append(obj); 49 | } 50 | return arr; 51 | } 52 | 53 | void Measurements::reset() { 54 | m_data.clear(); 55 | emit signal_measurements_updated(); 56 | } 57 | 58 | void Measurements::set_json(const QJsonArray &json) { 59 | m_data.clear(); 60 | for (auto v : json) { 61 | const auto obj = v.toObject(); 62 | int x = obj["x"].toInt(); 63 | int y = obj["y"].toInt(); 64 | int wifiId = unknown_wifi_id; 65 | if (obj.contains("wifi")) { 66 | wifiId = obj["wifi"].toInt(unknown_wifi_id); 67 | } 68 | for (auto m : obj["data"].toArray()) { 69 | add_measurement(x, y, m.toDouble(), wifiId); 70 | } 71 | } 72 | emit signal_measurements_updated(); 73 | } 74 | 75 | void Measurements::add_measurement(int x, int y, double value, int wifi_id) { 76 | for (auto &d : m_data) { 77 | if (d.x == x && d.y == y && d.wifi_id == wifi_id) { 78 | d.data.push_back(value); 79 | double avg = 0; 80 | for (const auto &m : d.data) { 81 | avg += m; 82 | } 83 | d.average = avg / d.data.size(); 84 | return; 85 | } 86 | } 87 | m_data.push_back(Measurement{x, y, wifi_id, {value}, value}); 88 | } 89 | 90 | MeasurementStatistics Measurements::get_statistics() const { 91 | MeasurementStatistics ret{}; 92 | ret.measurements = 0; 93 | ret.unique_places = m_data.size(); 94 | ret.weakest = 0; 95 | ret.strongest = -1000; 96 | ret.unique_wifi_APs = 0; 97 | QVector temp_wifi_count; 98 | for (auto &m : m_data) { 99 | if (!temp_wifi_count.contains(m.wifi_id)) { 100 | temp_wifi_count.push_back(m.wifi_id); 101 | ret.unique_wifi_APs++; 102 | } 103 | for (auto &d : m.data) { 104 | ret.measurements++; 105 | ret.weakest = std::min(ret.weakest, d); 106 | ret.strongest = std::max(ret.strongest, d); 107 | } 108 | } 109 | return ret; 110 | } 111 | 112 | const RawMeasurements &Measurements::get_measurements() const { return m_data; } 113 | 114 | } // namespace Valeronoi::state 115 | -------------------------------------------------------------------------------- /src/state/measurements.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_STATE_MEASUREMENTS_H 19 | #define VALERONOI_STATE_MEASUREMENTS_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "robot_map.h" 29 | #include "state.h" 30 | 31 | namespace Valeronoi::state { 32 | 33 | class Measurements : public QObject { 34 | Q_OBJECT 35 | public: 36 | void reset(); 37 | 38 | void set_json(const QJsonArray &json); 39 | 40 | void set_map(const RobotMap &map); 41 | 42 | [[nodiscard]] const RawMeasurements &get_measurements() const; 43 | 44 | [[nodiscard]] QJsonArray get_json() const; 45 | 46 | [[nodiscard]] MeasurementStatistics get_statistics() const; 47 | 48 | int unknown_wifi_id = 0; 49 | 50 | signals: 51 | void signal_measurements_updated(); 52 | 53 | public slots: 54 | void slot_add_measurement(double signal, int wifi_id); 55 | 56 | private: 57 | void add_measurement(int x, int y, double value, int wifi_id); 58 | 59 | const RobotMap *m_map{nullptr}; 60 | 61 | std::vector m_data; 62 | }; 63 | 64 | } // namespace Valeronoi::state 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/state/robot_map.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "robot_map.h" 19 | 20 | #include 21 | 22 | #ifndef M_PI 23 | #define M_PI 3.14159265358979323846 24 | #endif 25 | 26 | namespace Valeronoi::state { 27 | RobotMap::RobotMap(QObject *parent) : QObject(parent) { reset(); } 28 | 29 | void RobotMap::update_map_json(const QString &json) { 30 | m_error = ""; 31 | m_valid = false; 32 | if (json == "null") { 33 | // Edge case, because QJsonDocument::fromJson also returns null if it 34 | // detects a parsing error 35 | m_error = tr("No map data"); 36 | emit signal_map_updated(); 37 | return; 38 | } 39 | 40 | QJsonParseError error; 41 | auto json_document = QJsonDocument::fromJson(json.toUtf8(), &error); 42 | if (json_document.isNull()) { 43 | m_error = error.errorString(); 44 | emit signal_map_updated(); 45 | return; 46 | } 47 | if (json_document.isEmpty()) { 48 | m_error = tr("No map data"); 49 | emit signal_map_updated(); 50 | return; 51 | } 52 | update_map_json(json_document.object()); 53 | } 54 | 55 | void RobotMap::update_map_json(const QJsonObject &json_object) { 56 | if (json_object["__class"].toString() != "ValetudoMap") { 57 | m_error = tr("Did not receive ValetudoMap"); 58 | emit signal_map_updated(); 59 | return; 60 | } 61 | 62 | m_map_version = json_object["metaData"].toObject()["version"].toInt(); 63 | if (m_map_version < 1 || m_map_version > 2) { 64 | m_error = tr("Unknown map version"); 65 | emit signal_map_updated(); 66 | return; 67 | } 68 | 69 | m_map_json = json_object; 70 | generate_map(); 71 | m_valid = true; 72 | emit signal_map_updated(); 73 | } 74 | 75 | template 76 | static void map_pixels(const QJsonArray &pixels, T &blocks, int pixel_size, 77 | int *min_x, int *max_x, int *min_y, int *max_y) { 78 | blocks.reserve(pixels.size() / 2); 79 | for (qsizetype i = 0; i + 1 < pixels.size(); i += 2) { 80 | int block_x = pixels[i].toInt() * pixel_size; 81 | int block_y = pixels[i + 1].toInt() * pixel_size; 82 | *min_x = std::min(*min_x, block_x); 83 | *max_x = std::max(*max_x, block_x + pixel_size); 84 | *min_y = std::min(*min_y, block_y); 85 | *max_y = std::max(*max_y, block_y + pixel_size); 86 | blocks.push_back({block_x, block_y}); 87 | } 88 | } 89 | 90 | template 91 | static void map_pixels(const std::unordered_set &pixels, T &blocks, 92 | int pixel_size, int *min_x, int *max_x, int *min_y, 93 | int *max_y) { 94 | blocks.reserve(pixels.size() / 2); 95 | for (const auto &block : pixels) { 96 | int block_x = block.x * pixel_size; 97 | int block_y = block.y * pixel_size; 98 | *min_x = std::min(*min_x, block_x); 99 | *max_x = std::max(*max_x, block_x + pixel_size); 100 | *min_y = std::min(*min_y, block_y); 101 | *max_y = std::max(*max_y, block_y + pixel_size); 102 | blocks.push_back({block_x, block_y}); 103 | } 104 | } 105 | 106 | template 107 | static void move_pixels(T &blocks, int move_x, int move_y) { 108 | for (auto &block : blocks) { 109 | block.x += move_x; 110 | block.y += move_y; 111 | } 112 | } 113 | 114 | void RobotMap::generate_map() { 115 | m_map = Map(); 116 | m_map.size_x = m_map_json["size"].toObject()["x"].toInt(); 117 | m_map.size_y = m_map_json["size"].toObject()["y"].toInt(); 118 | m_map.pixel_size = m_map_json["pixelSize"].toInt(); 119 | int min_x{m_map.size_x}, max_x{0}, min_y{m_map.size_y}, max_y{0}; 120 | 121 | // Because we merge layers together, pixels/blocks may occur multiple times in 122 | // a Layer. As layers are already a performance problem, we have to remove 123 | // duplicates by using an unordered_set. 124 | std::unordered_map> floor_pixels; 125 | for (const auto &&layer : m_map_json["layers"].toArray()) { 126 | const auto &layer_obj = layer.toObject(); 127 | auto layer_type = layer_obj["type"].toString().toStdString(); 128 | if (layer_type == "segment") { 129 | // Pretend segments are floor as well, because future Valetudo Versions 130 | // will not have "floor". Dreame Robots don't have a floor layer at all. 131 | layer_type = "floor"; 132 | } 133 | if (layer_type == "floor" || layer_type == "wall") { 134 | if (m_map_version == 1) { 135 | const auto &pixels = layer_obj["pixels"].toArray(); 136 | if ((pixels.size() % 2) != 0 || pixels.size() < 2) { 137 | qDebug().nospace() 138 | << "Invalid layer, has " << pixels.size() << " points"; 139 | continue; 140 | } 141 | 142 | floor_pixels[layer_type].reserve(pixels.size() / 2); 143 | for (qsizetype i = 0; i + 1 < pixels.size(); i += 2) { 144 | const int block_x = pixels[i].toInt(); 145 | const int block_y = pixels[i + 1].toInt(); 146 | floor_pixels[layer_type].insert({block_x, block_y}); 147 | } 148 | } else if (m_map_version == 2) { 149 | const auto &compressed_pixels = layer_obj["compressedPixels"].toArray(); 150 | if ((compressed_pixels.size() % 3) != 0 || 151 | compressed_pixels.size() < 3) { 152 | qDebug().nospace() 153 | << "Invalid layer, has " << compressed_pixels.size() << " points"; 154 | continue; 155 | } 156 | 157 | for (qsizetype i = 0; i + 1 < compressed_pixels.size(); i += 3) { 158 | const int block_x = compressed_pixels[i].toInt(); 159 | const int block_y = compressed_pixels[i + 1].toInt(); 160 | const int block_count = compressed_pixels[i + 2].toInt(); 161 | for (qsizetype j = 0; j < block_count; j++) { 162 | const auto pixel_x = static_cast(block_x + j); 163 | floor_pixels[layer_type].insert({pixel_x, block_y}); 164 | } 165 | } 166 | } 167 | } 168 | } 169 | for (const auto &layer : floor_pixels) { 170 | map_pixels(layer.second, m_map.layers[layer.first].blocks, m_map.pixel_size, 171 | &min_x, &max_x, &min_y, &max_y); 172 | } 173 | 174 | for (const auto &&entity : m_map_json["entities"].toArray()) { 175 | const auto &entity_obj = entity.toObject(); 176 | const auto entity_class = entity_obj["__class"].toString(); 177 | const auto entity_type = entity_obj["type"].toString(); 178 | const auto entity_metadata = entity_obj["metaData"].toObject(); 179 | const auto &points = entity_obj["points"].toArray(); 180 | if (entity_class == "PointMapEntity" && points.size() != 2) { 181 | qDebug().nospace() << "Found PointMapEntity with not 2 points but " 182 | << points.size() << ", ignoring"; 183 | continue; 184 | } 185 | if (entity_type.endsWith("_position") && entity_class != "PointMapEntity") { 186 | qDebug() << entity_type << "entity must be a PointMapEntity, but was" 187 | << entity_class; 188 | continue; 189 | } 190 | const auto entity_type_str = entity_type.toStdString(); 191 | Entity map_entity; 192 | map_entity.type = entity_type_str; 193 | map_entity.cls = entity_class.toStdString(); 194 | map_pixels(points, map_entity.points, 1, &min_x, &max_x, &min_y, &max_y); 195 | map_entity.angle = 196 | (entity_metadata["angle"].toDouble() - 90.0) * M_PI / 180.0; 197 | 198 | if (map_entity.type == "robot_position") { 199 | // Insert robot position last to ensure the robot is drawn last 200 | m_map.entities.push_back(map_entity); 201 | } else { 202 | m_map.entities.insert(m_map.entities.begin(), map_entity); 203 | } 204 | } 205 | 206 | for (auto &layer : m_map.layers) { 207 | move_pixels(layer.second.blocks, -min_x, -min_y); 208 | } 209 | 210 | for (auto &layer : m_map.entities) { 211 | move_pixels(layer.points, -min_x, -min_y); 212 | } 213 | m_map.size_x = max_x - min_x; 214 | m_map.size_y = max_y - min_y; 215 | m_map.crop_x = min_x; 216 | m_map.crop_y = min_y; 217 | qDebug() << "Generated map with size" << m_map.size_x << "x" << m_map.size_y 218 | << "- cropped" << m_map.crop_x << "x" << m_map.crop_y; 219 | } 220 | 221 | bool RobotMap::is_valid() const { return m_valid; } 222 | 223 | QString RobotMap::error_msg() const { return m_error; } 224 | 225 | const Map &RobotMap::get_map() const { return m_map; } 226 | 227 | const QJsonObject &RobotMap::get_map_json() const { return m_map_json; } 228 | 229 | void RobotMap::reset() { 230 | m_valid = false; 231 | m_error = "No map data"; 232 | emit signal_map_updated(); 233 | } 234 | 235 | } // namespace Valeronoi::state 236 | -------------------------------------------------------------------------------- /src/state/robot_map.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_STATE_ROBOT_MAP_H 19 | #define VALERONOI_STATE_ROBOT_MAP_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "state.h" 28 | 29 | namespace Valeronoi::state { 30 | 31 | class RobotMap : public QObject { 32 | Q_OBJECT 33 | public: 34 | explicit RobotMap(QObject *parent = nullptr); 35 | 36 | void update_map_json(const QString &json); 37 | 38 | void update_map_json(const QJsonObject &json); 39 | 40 | void reset(); 41 | 42 | [[nodiscard]] bool is_valid() const; 43 | 44 | [[nodiscard]] QString error_msg() const; 45 | 46 | [[nodiscard]] const Map &get_map() const; 47 | 48 | [[nodiscard]] const QJsonObject &get_map_json() const; 49 | 50 | signals: 51 | void signal_map_updated(); 52 | 53 | private: 54 | void generate_map(); 55 | 56 | QJsonObject m_map_json{}; 57 | int m_map_version{0}; 58 | bool m_valid{false}; 59 | QString m_error; 60 | Map m_map; 61 | }; 62 | } // namespace Valeronoi::state 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/state/state.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "state.h" 19 | 20 | #include 21 | 22 | namespace Valeronoi::state { 23 | 24 | std::optional Map::get_robot_position() const { 25 | auto res = std::find_if(entities.begin(), entities.end(), [=](const auto &e) { 26 | return e.type == "robot_position"; 27 | }); 28 | if (res == entities.end()) { 29 | return std::nullopt; 30 | } 31 | return {res->points[0]}; 32 | } 33 | 34 | } // namespace Valeronoi::state 35 | -------------------------------------------------------------------------------- /src/state/state.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_STATE_STATE_H 19 | #define VALERONOI_STATE_STATE_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace Valeronoi::state { 32 | 33 | struct Point { 34 | int x, y; 35 | }; 36 | 37 | struct Block { 38 | int x, y; 39 | }; 40 | 41 | inline bool operator==(const Block &lhs, const Block &rhs) { 42 | return lhs.x == rhs.x && lhs.y == rhs.y; 43 | } 44 | 45 | inline bool operator<(const Block &lhs, const Block &rhs) { 46 | return (lhs.x + lhs.y) < (rhs.x + rhs.y); 47 | } 48 | 49 | struct Entity { 50 | std::string type, cls; 51 | double angle; 52 | std::vector points; 53 | }; 54 | 55 | struct Layer { 56 | std::vector blocks; 57 | }; 58 | 59 | struct Map { 60 | int pixel_size; 61 | int size_x, size_y; 62 | int crop_x, crop_y; 63 | std::unordered_map layers; 64 | std::vector entities; 65 | 66 | std::optional get_robot_position() const; 67 | }; 68 | 69 | struct Measurement { 70 | int x, y; 71 | int wifi_id; 72 | std::vector data; 73 | double average; 74 | }; 75 | 76 | typedef std::vector RawMeasurements; 77 | 78 | struct MeasurementStatistics { 79 | int measurements, unique_places, unique_wifi_APs; 80 | double strongest, weakest; 81 | }; 82 | 83 | enum class DISPLAY_MODE { 84 | Voronoi = 0, 85 | DataPoints = 1, 86 | None = 2 87 | }; // Keep in sync with valeronoi.ui 88 | 89 | struct DataSegment { 90 | int x{0}, y{0}; 91 | double value{0.0}; 92 | QPolygon polygon; 93 | QColor color; 94 | }; 95 | 96 | typedef QList DataSegments; 97 | 98 | } // namespace Valeronoi::state 99 | 100 | namespace std { 101 | template <> 102 | struct hash { 103 | std::size_t operator()(Valeronoi::state::Block const &s) const noexcept { 104 | std::size_t h1 = std::hash{}(s.x); 105 | std::size_t h2 = std::hash{}(s.y); 106 | return h1 ^ (h2 << 1); 107 | } 108 | }; 109 | } // namespace std 110 | 111 | #ifndef Q_DECLARE_METATYPE 112 | // To make cppcheck happy 113 | #define Q_DECLARE_METATYPE(TYPE) // TYPE 114 | #endif 115 | Q_DECLARE_METATYPE(Valeronoi::state::DataSegments) 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/state/wifi_collection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "wifi_collection.h" 20 | 21 | #include "qjsonarray.h" 22 | 23 | namespace Valeronoi::state { 24 | 25 | wifi_collection::wifi_collection() { clear(); } 26 | 27 | void wifi_collection::clear() { m_known_wifis.clear(); } 28 | 29 | int wifi_collection::get_or_create_wifi_id( 30 | const Valeronoi::robot::WifiInformation& wifi_info) { 31 | int nRet = get_wifi_id(wifi_info.bssid()); 32 | 33 | if (nRet < 0) { 34 | nRet = add_wifi(wifi_info); 35 | } 36 | 37 | return nRet; 38 | } 39 | 40 | int wifi_collection::add_wifi( 41 | const Valeronoi::robot::WifiInformation& wifi_info) { 42 | m_known_wifis.append(wifi_info); 43 | 44 | emit signal_wifi_list_updated(); 45 | emit signal_new_wifi_added(wifi_info); 46 | 47 | return m_known_wifis.size() - 1; 48 | } 49 | 50 | QVector wifi_collection::get_known_wifis() 51 | const { 52 | return m_known_wifis; 53 | } 54 | 55 | QJsonArray wifi_collection::get_json() const { 56 | QJsonArray ret; 57 | 58 | for (const auto& m_known_wifi : m_known_wifis) { 59 | ret.append(m_known_wifi.get_json()); 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | void wifi_collection::set_json(const QJsonArray& json) { 66 | clear(); 67 | 68 | for (const auto& wifi_access_point_value : json) { 69 | if (!wifi_access_point_value.isObject()) { 70 | // Wrong format. 71 | return; 72 | } 73 | QJsonObject wifi_access_point = wifi_access_point_value.toObject(); 74 | m_known_wifis.append(Valeronoi::robot::WifiInformation(wifi_access_point)); 75 | } 76 | 77 | emit signal_wifi_list_updated(); 78 | } 79 | 80 | int wifi_collection::get_wifi_id(const QString& bssid) const { 81 | for (int index = 0; m_known_wifis.size() > index; ++index) { 82 | if (m_known_wifis.at(index).bssid() == bssid) { 83 | return index; 84 | } 85 | } 86 | 87 | return -1; 88 | } 89 | 90 | } // namespace Valeronoi::state 91 | -------------------------------------------------------------------------------- /src/state/wifi_collection.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_STATE_WIFI_COLLECTION_H 19 | #define VALERONOI_STATE_WIFI_COLLECTION_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "../robot/wifi_information.h" 26 | 27 | namespace Valeronoi::state { 28 | 29 | class wifi_collection : public QObject { 30 | Q_OBJECT 31 | public: 32 | wifi_collection(); 33 | 34 | void clear(); 35 | 36 | int get_or_create_wifi_id(const Valeronoi::robot::WifiInformation& wifi_info); 37 | 38 | [[nodiscard]] int get_wifi_id(const QString& bssid) const; 39 | 40 | int add_wifi(const Valeronoi::robot::WifiInformation& wifi_info); 41 | 42 | [[nodiscard]] QVector get_known_wifis() 43 | const; 44 | 45 | [[nodiscard]] QJsonArray get_json() const; 46 | 47 | void set_json(const QJsonArray& json); 48 | 49 | signals: 50 | void signal_wifi_list_updated(); 51 | 52 | void signal_new_wifi_added(Valeronoi::robot::WifiInformation wifi_info); 53 | 54 | private: 55 | QVector m_known_wifis; 56 | }; 57 | 58 | } // namespace Valeronoi::state 59 | 60 | #endif // VALERONOI_STATE_WIFI_COLLECTION_H 61 | -------------------------------------------------------------------------------- /src/util/colormap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_UTIL_COLORMAP_H 19 | #define VALERONOI_UTIL_COLORMAP_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace Valeronoi::util { 27 | template 28 | class ColorMap { 29 | public: 30 | ColorMap(const char *name, std::vector> colors) 31 | : colormap_name{name}, colormap_colors{std::move(colors)} { 32 | if (colormap_colors.size() == 0) { 33 | throw std::runtime_error("Can not initialize ColorMap without colors"); 34 | } 35 | }; 36 | 37 | [[nodiscard]] std::string name() const { return colormap_name; }; 38 | 39 | [[nodiscard]] std::array get_color(T f) const { 40 | if (f <= 0) return colormap_colors[0]; 41 | if (f >= 1) return colormap_colors[colormap_colors.size() - 1]; 42 | double place = (colormap_colors.size() - 1) * f; 43 | double int_part, fract_part; 44 | fract_part = std::modf(place, &int_part); 45 | std::size_t index = int_part; 46 | if (index == colormap_colors.size() - 1) { 47 | // VERY close to end 48 | return colormap_colors[colormap_colors.size() - 1]; 49 | } 50 | assert((index < colormap_colors.size() - 1)); 51 | 52 | std::array ret{}; 53 | for (std::size_t i = 0; i < components; i++) { 54 | ret[i] = (1.0 - fract_part) * colormap_colors[index][i]; 55 | } 56 | 57 | for (std::size_t i = 0; i < components; i++) { 58 | ret[i] += fract_part * colormap_colors[index + 1][i]; 59 | } 60 | 61 | return ret; 62 | } 63 | 64 | private: 65 | std::string colormap_name; 66 | std::vector> colormap_colors; 67 | }; 68 | 69 | typedef ColorMap RGBColorMap; 70 | typedef std::array RGBColorMapColors; 71 | 72 | } // namespace Valeronoi::util 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/util/compat.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_UTIL_COMPAT_H 19 | #define VALERONOI_UTIL_COMPAT_H 20 | 21 | #include 22 | #include 23 | 24 | #if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) 25 | #define CHECKBOX_SIGNAL_CHANGED QCheckBox::stateChanged 26 | #else 27 | #define CHECKBOX_SIGNAL_CHANGED QCheckBox::checkStateChanged 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/util/log_helper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "log_helper.h" 20 | 21 | #include 22 | 23 | namespace Valeronoi::util { 24 | 25 | LogHelper &LogHelper::instance() { 26 | static LogHelper _instance; 27 | return _instance; 28 | } 29 | 30 | void LogHelper::log(const QString &msg) { 31 | if (!m_log_dialog) { 32 | m_buffer.append(msg); 33 | } else { 34 | m_log_dialog->log_message(msg); 35 | } 36 | } 37 | 38 | void LogHelper::set_log_dialog(Valeronoi::gui::dialog::LogDialog *log_dialog) { 39 | m_log_dialog = log_dialog; 40 | m_log_dialog->log_message(m_buffer); 41 | m_buffer.clear(); 42 | } 43 | 44 | void log_handler(QtMsgType type, const QMessageLogContext &context, 45 | const QString &msg) { 46 | (void)context; 47 | auto &helper = LogHelper::instance(); 48 | QString level; 49 | switch (type) { 50 | case QtDebugMsg: 51 | level = "DEBUG"; 52 | break; 53 | case QtWarningMsg: 54 | level = "WARN "; 55 | break; 56 | case QtCriticalMsg: 57 | level = "CRIT "; 58 | break; 59 | case QtFatalMsg: 60 | level = "FATAL"; 61 | break; 62 | case QtInfoMsg: 63 | level = "INFO "; 64 | break; 65 | } 66 | const auto time = QDateTime::currentDateTime(); 67 | helper.log("[ " + level + " ] " + time.toString("yyyy-MM-dd hh:mm:ss.zzz") + 68 | " " + msg); 69 | } 70 | 71 | } // namespace Valeronoi::util 72 | -------------------------------------------------------------------------------- /src/util/log_helper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_UTIL_LOG_HELPER_H 19 | #define VALERONOI_UTIL_LOG_HELPER_H 20 | 21 | #include 22 | #include 23 | 24 | #include "../gui/dialog/log.h" 25 | 26 | namespace Valeronoi::util { 27 | 28 | class LogHelper { 29 | public: 30 | static LogHelper &instance(); 31 | 32 | LogHelper(const LogHelper &) = delete; 33 | 34 | LogHelper &operator=(const LogHelper &) = delete; 35 | 36 | void set_log_dialog(Valeronoi::gui::dialog::LogDialog *log_dialog); 37 | 38 | void log(const QString &msg); 39 | 40 | private: 41 | LogHelper() = default; 42 | 43 | QString m_buffer; 44 | Valeronoi::gui::dialog::LogDialog *m_log_dialog{nullptr}; 45 | }; 46 | 47 | void log_handler(QtMsgType type, const QMessageLogContext &context, 48 | const QString &msg); 49 | 50 | } // namespace Valeronoi::util 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/util/segment_generator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "segment_generator.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 29 | typedef CGAL::Delaunay_triangulation_2 DT; 30 | typedef CGAL::Delaunay_triangulation_adaptation_traits_2
AT; 31 | typedef CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2
AP; 32 | typedef CGAL::Voronoi_diagram_2 VD; 33 | 34 | typedef AT::Site_2 Site_2; 35 | typedef AT::Point_2 Point_2; 36 | typedef VD::Locate_result Locate_result; 37 | typedef VD::Face_handle Face_handle; 38 | typedef VD::Halfedge_handle Halfedge_handle; 39 | typedef VD::Ccb_halfedge_circulator Ccb_halfedge_circulator; 40 | 41 | #if CGAL_VERSION_NR < CGAL_VERSION_NUMBER(6, 0, 0) 42 | #define GET_IF boost::get 43 | #else 44 | #define GET_IF std::get_if 45 | #endif 46 | 47 | namespace Valeronoi::util { 48 | 49 | SegmentGenerator::~SegmentGenerator() { 50 | m_mutex.lock(); 51 | m_abort = true; 52 | m_condition.wakeOne(); 53 | m_mutex.unlock(); 54 | 55 | wait(); 56 | } 57 | 58 | void SegmentGenerator::generate( 59 | const Valeronoi::state::RawMeasurements &measurements, 60 | Valeronoi::state::DISPLAY_MODE display_mode, int simplify, 61 | int wifi_id_filter) { 62 | QMutexLocker locker(&m_mutex); 63 | 64 | m_measurements = measurements; 65 | m_display_mode = display_mode; 66 | m_simplify = simplify; 67 | m_wifi_id_filter = wifi_id_filter; 68 | 69 | if (!isRunning()) { 70 | start(LowPriority); 71 | } else { 72 | m_restart = true; 73 | m_condition.wakeOne(); 74 | } 75 | } 76 | 77 | void SegmentGenerator::run() { 78 | while (true) { 79 | m_mutex.lock(); 80 | auto measurements = m_measurements; 81 | const auto display_mode = m_display_mode; 82 | const auto simplify = m_simplify; 83 | const auto wifi_id_filter = m_wifi_id_filter; 84 | m_mutex.unlock(); 85 | if (m_abort) { 86 | return; 87 | } 88 | 89 | Valeronoi::state::RawMeasurements processed_measurements; 90 | if (simplify > 1 || wifi_id_filter != -1) { 91 | for (auto &m : measurements) { 92 | bool saveValue = true; 93 | 94 | if (wifi_id_filter != -1 && m.wifi_id != wifi_id_filter) { 95 | continue; // Skip and probe next one 96 | } 97 | 98 | if (simplify > 1) { 99 | m.x = (m.x / simplify) * simplify; 100 | m.y = (m.y / simplify) * simplify; 101 | 102 | for (auto &sm : processed_measurements) { 103 | if (sm.x == m.x && sm.y == m.y) { 104 | // I'd like to use std::transform with std::back_inserter instead, 105 | // but that doesn't work for doubles 106 | for (const auto d : m.data) { 107 | sm.data.push_back(d); 108 | } 109 | saveValue = false; 110 | } 111 | } 112 | } 113 | 114 | if (saveValue) { 115 | processed_measurements.push_back(m); 116 | } 117 | } 118 | // Update averages 119 | for (auto &sm : processed_measurements) { 120 | double avg = 0; 121 | for (const auto &m : sm.data) { 122 | avg += m; 123 | } 124 | sm.average = avg / sm.data.size(); 125 | } 126 | } else { 127 | processed_measurements = std::move(measurements); 128 | } 129 | 130 | Valeronoi::state::DataSegments segments; 131 | 132 | switch (display_mode) { 133 | case state::DISPLAY_MODE::Voronoi: 134 | generate_voronoi(processed_measurements, segments); 135 | break; 136 | case state::DISPLAY_MODE::DataPoints: 137 | for (const auto &m : processed_measurements) { 138 | Valeronoi::state::DataSegment s; 139 | s.x = m.x; 140 | s.y = m.y; 141 | s.value = m.average; 142 | segments.push_back(s); 143 | } 144 | break; 145 | case state::DISPLAY_MODE::None: 146 | break; 147 | } 148 | 149 | emit generated_segments(segments); 150 | 151 | m_mutex.lock(); 152 | if (!m_restart) { 153 | m_condition.wait(&m_mutex); 154 | } 155 | m_restart = false; 156 | m_mutex.unlock(); 157 | } 158 | } 159 | 160 | void SegmentGenerator::generate_voronoi( 161 | const Valeronoi::state::RawMeasurements &measurements, 162 | Valeronoi::state::DataSegments &segments) { 163 | if (measurements.size() < 2) { 164 | return; 165 | } 166 | VD vd; 167 | int x_max{0}, y_max{0}; 168 | for (const auto &m : measurements) { 169 | Site_2 t(m.x, m.y); 170 | vd.insert(t); 171 | x_max = std::max(x_max, m.x); 172 | y_max = std::max(y_max, m.y); 173 | } 174 | const int x_center{x_max / 2}; 175 | const int y_center{y_max / 2}; 176 | 177 | // Insert dummy points to prevent infinite points in our vertices. 178 | // You could also do a ton of math magic to calculate the infinite points 179 | // in drawing space, but this is just easier and less likely to contain bugs. 180 | for (int y = -1; y < 2; y++) { 181 | for (int x = -1; x < 2; x++) { 182 | if (x || y) { // No center point 183 | Site_2 t(x_center + x * (x_center * 10), 184 | y_center + y * (y_center * 10)); 185 | vd.insert(t); 186 | } 187 | } 188 | } 189 | 190 | if (!vd.is_valid()) { 191 | return; 192 | } 193 | for (const auto &m : measurements) { 194 | Point_2 p(m.x, m.y); 195 | auto result = vd.locate(p); 196 | if (auto *v = GET_IF(&result)) { 197 | Valeronoi::state::DataSegment s; 198 | s.x = m.x; 199 | s.y = m.y; 200 | Ccb_halfedge_circulator ec_start = (*v)->ccb(); 201 | Ccb_halfedge_circulator ec = ec_start; 202 | do { 203 | if (ec->has_source()) { 204 | const auto point = ec->source()->point(); 205 | s.polygon << QPoint(point.x(), point.y()); 206 | } 207 | } while (++ec != ec_start); 208 | s.value = m.average; 209 | segments.push_back(s); 210 | } else { 211 | // Point is not on a face 212 | } 213 | } 214 | } 215 | 216 | } // namespace Valeronoi::util 217 | -------------------------------------------------------------------------------- /src/util/segment_generator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_UTIL_SEGMENT_GENERATOR_H 19 | #define VALERONOI_UTIL_SEGMENT_GENERATOR_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../state/state.h" 28 | 29 | namespace Valeronoi::util { 30 | 31 | class SegmentGenerator : public QThread { 32 | Q_OBJECT 33 | public: 34 | ~SegmentGenerator() override; 35 | 36 | void generate(const Valeronoi::state::RawMeasurements &measurements, 37 | Valeronoi::state::DISPLAY_MODE display_mode, int simplify, 38 | int wifi_id_filter = -1); 39 | 40 | signals: 41 | void generated_segments(const Valeronoi::state::DataSegments &segments); 42 | 43 | protected: 44 | void run() override; 45 | 46 | private: 47 | static void generate_voronoi( 48 | const Valeronoi::state::RawMeasurements &measurements, 49 | Valeronoi::state::DataSegments &segments); 50 | 51 | bool m_abort{false}, m_restart{false}; 52 | QMutex m_mutex; 53 | QWaitCondition m_condition; 54 | 55 | Valeronoi::state::RawMeasurements m_measurements{}; 56 | Valeronoi::state::DISPLAY_MODE m_display_mode{}; 57 | int m_simplify; 58 | int m_wifi_id_filter; 59 | }; 60 | 61 | } // namespace Valeronoi::util 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/valeronoi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef VALERONOI_H 19 | #define VALERONOI_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "gui/dialog/about.h" 30 | #include "gui/dialog/export.h" 31 | #include "gui/dialog/log.h" 32 | #include "gui/dialog/robot_config.h" 33 | #include "gui/dialog/settings.h" 34 | #include "gui/dialog/update.h" 35 | #include "gui/widget/display_widget.h" 36 | #include "robot/connection_configuration.h" 37 | #include "robot/robot.h" 38 | #include "state/measurements.h" 39 | #include "state/robot_map.h" 40 | #include "state/wifi_collection.h" 41 | #include "util/colormap.h" 42 | #include "util/log_helper.h" 43 | 44 | namespace Valeronoi { 45 | constexpr const char *VALERONOI_FILE_EXTENSION = "vwm"; // Valeronoi WiFi Map 46 | constexpr const int FILE_FORMAT_VERSION = 1; 47 | 48 | QT_BEGIN_NAMESPACE 49 | namespace Ui { 50 | class ValeronoiWindow; 51 | } 52 | QT_END_NAMESPACE 53 | 54 | class ValeronoiWindow : public QMainWindow { 55 | Q_OBJECT 56 | public: 57 | explicit ValeronoiWindow(QWidget *parent = nullptr); 58 | 59 | ~ValeronoiWindow() override; 60 | 61 | bool load_file(const QString &path); 62 | 63 | void closeEvent(QCloseEvent *event) override; 64 | 65 | private slots: 66 | void newFile(); 67 | 68 | void openFile(); 69 | 70 | void saveFile(); 71 | 72 | void saveAsFile(); 73 | 74 | void slot_begin_recording(); 75 | 76 | void slot_end_recording(); 77 | 78 | void slot_toggle_recording(); 79 | 80 | private: 81 | void update_title(); 82 | 83 | [[nodiscard]] bool maybe_save(); 84 | 85 | [[nodiscard]] bool save(); 86 | 87 | void load_colormaps(); 88 | 89 | void set_modified(bool is_modified); 90 | 91 | [[nodiscard]] QString get_open_save_dir() const; 92 | 93 | void set_open_save_dir(const QString &dir); 94 | 95 | [[nodiscard]] QString get_open_save_filter() const; 96 | 97 | void update_display_settings(); 98 | 99 | void connect_display_widget(); 100 | 101 | void connect_wifi_widget(); 102 | 103 | void connect_actions(); 104 | 105 | void set_selected_wifi_item(const QListWidgetItem *widget_item); 106 | 107 | Ui::ValeronoiWindow *ui; 108 | Valeronoi::gui::widget::DisplayWidget *m_display_widget; 109 | 110 | Valeronoi::gui::dialog::ExportDialog m_export_dialog; 111 | Valeronoi::gui::dialog::RobotConfigDialog m_robot_config_dialog; 112 | Valeronoi::gui::dialog::SettingsDialog m_settings_dialog; 113 | Valeronoi::gui::dialog::AboutDialog m_about_dialog; 114 | Valeronoi::gui::dialog::UpdateDialog m_update_dialog; 115 | Valeronoi::gui::dialog::LogDialog m_log_dialog; 116 | 117 | bool m_modified; 118 | QString m_current_file; 119 | 120 | QList m_color_maps; 121 | 122 | Valeronoi::robot::ConnectionConfiguration m_connection_configuration; 123 | Valeronoi::robot::Robot m_robot; 124 | 125 | Valeronoi::state::RobotMap m_robot_map; 126 | 127 | Valeronoi::state::wifi_collection m_wifi_collection; 128 | 129 | Valeronoi::state::Measurements m_wifi_measurements; 130 | 131 | bool m_recording{false}; 132 | void connect_robot_signals(); 133 | }; 134 | 135 | } // namespace Valeronoi 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/valeronoi.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | res/valeronoi.png 5 | res/SourceCodePro-Regular.otf 6 | res/colormaps.json 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/test_colormap.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | 22 | #include "src/util/colormap.h" 23 | 24 | void assert_color(const Valeronoi::util::RGBColorMap &m, double pos, double e1, 25 | double e2, double e3) { 26 | const auto c = m.get_color(pos); 27 | CHECK(c[0] == Approx(e1).margin(0.001)); 28 | CHECK(c[1] == Approx(e2).margin(0.001)); 29 | CHECK(c[2] == Approx(e3).margin(0.001)); 30 | } 31 | 32 | TEST_CASE("ColorMap without colors throws", "[colormap]") { 33 | std::vector> colors; 34 | CHECK_THROWS(Valeronoi::util::RGBColorMap("Foo", colors)); 35 | } 36 | 37 | TEST_CASE("ColorMap works with single color", "[colormap]") { 38 | std::vector> colors; 39 | CHECK_THROWS(Valeronoi::util::RGBColorMap("Foo", colors)); 40 | colors.push_back({0.0, 1.0, 0.5}); 41 | auto const c = Valeronoi::util::RGBColorMap("Foo", colors); 42 | CHECK(c.name() == "Foo"); 43 | assert_color(c, 0.0, 0.0, 1.0, 0.5); 44 | assert_color(c, 1.0, 0.0, 1.0, 0.5); 45 | assert_color(c, -1.0, 0.0, 1.0, 0.5); 46 | assert_color(c, 2.0, 0.0, 1.0, 0.5); 47 | } 48 | 49 | TEST_CASE("ColorMap works with two colors", "[colormap]") { 50 | std::vector> colors; 51 | CHECK_THROWS(Valeronoi::util::RGBColorMap("Foo", colors)); 52 | colors.push_back({0.0, 1.0, 0.5}); 53 | colors.push_back({1.0, 0.5, 0.5}); 54 | auto const c = Valeronoi::util::RGBColorMap("Foo", colors); 55 | assert_color(c, 0.0, 0.0, 1.0, 0.5); 56 | assert_color(c, 0.5, 0.5, 0.75, 0.5); 57 | assert_color(c, 1.0, 1.0, 0.5, 0.5); 58 | assert_color(c, -1.0, 0.0, 1.0, 0.5); 59 | assert_color(c, 2.0, 1.0, 0.5, 0.5); 60 | } 61 | 62 | TEST_CASE("ColorMap works with multiple colors", "[colormap]") { 63 | std::vector> colors; 64 | CHECK_THROWS(Valeronoi::util::RGBColorMap("Foo", colors)); 65 | colors.push_back({0.0, 1.0, 0.5}); 66 | colors.push_back({0.5, 0.5, 0.5}); 67 | colors.push_back({0.6, 1.0, 0.5}); 68 | auto const c = Valeronoi::util::RGBColorMap("Foo", colors); 69 | assert_color(c, 0.0, 0.0, 1.0, 0.5); 70 | assert_color(c, 0.000000001, 0.0, 1.0, 0.5); 71 | assert_color(c, 0.25, 0.25, 0.75, 0.5); 72 | assert_color(c, 0.5, 0.5, 0.5, 0.5); 73 | assert_color(c, 0.75, 0.55, 0.75, 0.5); 74 | assert_color(c, 0.999999999, 0.6, 1.0, 0.5); 75 | assert_color(c, 1.0, 0.6, 1.0, 0.5); 76 | } 77 | -------------------------------------------------------------------------------- /tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Valeronoi is an app for generating WiFi signal strength maps 3 | * Copyright (C) 2021-2024 Christian Friedrich Coors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #define CATCH_CONFIG_MAIN 19 | #include 20 | -------------------------------------------------------------------------------- /tools/pkgbuild/valeronoi-git/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Christian Friedrich Coors 2 | pkgname=valeronoi-git 3 | pkgver=r38.b239f2e 4 | pkgrel=1 5 | pkgdesc="A companion app for Valetudo for creating WiFi maps" 6 | arch=('any') 7 | url="https://github.com/ccoors/Valeronoi" 8 | license=('GPL3') 9 | depends=("qt6-base" "qt6-imageformats" "qt6-svg" "cgal") 10 | conflicts=("valeronoi") 11 | source=("$pkgname"::"git+https://github.com/ccoors/Valeronoi") 12 | sha256sums=('SKIP') 13 | 14 | pkgver() { 15 | cd "$srcdir/$pkgname" 16 | printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 17 | } 18 | 19 | build() { 20 | mkdir -p "${srcdir}/$pkgname/build" 21 | cd "${srcdir}/$pkgname/build" 22 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release 23 | make 24 | } 25 | 26 | check() { 27 | cd "${srcdir}/$pkgname/build" 28 | ./valeronoi-tests 29 | } 30 | 31 | package() { 32 | cd "${srcdir}/$pkgname/build" 33 | make DESTDIR="$pkgdir" install 34 | } 35 | -------------------------------------------------------------------------------- /tools/windows/copy_dlls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This tools copies the required DLLs 5 | 6 | Alternative solution to the options mentioned on https://www.msys2.org/wiki/Distributing/ 7 | """ 8 | 9 | import glob 10 | import os 11 | import shutil 12 | import subprocess 13 | 14 | NOTFOUND = [] 15 | 16 | 17 | def dll_names(binary): 18 | out = subprocess.check_output( 19 | [ 20 | "sh", 21 | "-c", 22 | "objdump -p %s | grep dll | awk '{print $3}'" % binary, 23 | ] 24 | ) 25 | return [x for x in out.decode("utf-8").split("\n") if x and "dll" in x] 26 | 27 | 28 | def copy_dlls(binary, target="./"): 29 | print("Copying DLLs for {}".format(binary)) 30 | res = dll_names(binary) + ["libssl-1_1-x64.dll"] 31 | for dll in res: 32 | if os.path.exists(dll) or dll in NOTFOUND: 33 | continue 34 | dll_files = list(glob.iglob("C:/msys2/**/{}".format(dll), recursive=True)) 35 | if not dll_files: 36 | print("File {} not found".format(dll)) 37 | NOTFOUND.append(dll) 38 | else: 39 | print(dll_files) 40 | shutil.copy(dll_files[0], "{}{}".format(target, dll)) 41 | copy_dlls(dll) 42 | 43 | 44 | def copy_additional_dlls(dir, dlls): 45 | os.makedirs(dir) 46 | # Qt 6.2 requires more magic for TLS 47 | for dll in dlls: 48 | dll_files = list(glob.iglob("C:/msys2/**/{}".format(dll), recursive=True)) 49 | if not dll_files: 50 | print("File {} not found".format(dll)) 51 | continue 52 | print(dll_files) 53 | shutil.copy(dll_files[0], "{}/{}".format(dir, dll)) 54 | 55 | 56 | copy_dlls("valeronoi.exe") 57 | copy_additional_dlls( 58 | "tls", ["qcertonlybackend.dll", "qopensslbackend.dll", "qschannelbackend.dll"] 59 | ) 60 | copy_additional_dlls("styles", ["qwindowsvistastyle.dll"]) 61 | copy_additional_dlls( 62 | "platforms", ["qdirect2d.dll", "qminimal.dll", "qoffscreen.dll", "qwindows.dll"] 63 | ) 64 | -------------------------------------------------------------------------------- /tools/windows/install.nsi: -------------------------------------------------------------------------------- 1 | !include "MUI2.nsh" 2 | 3 | Name "Valeronoi" 4 | OutFile "Valeronoi-installer-x86_64.exe" 5 | Unicode True 6 | 7 | InstallDir "$LOCALAPPDATA\Valeronoi" 8 | 9 | InstallDirRegKey HKCU "Software\ccoors\Valeronoi" "" 10 | 11 | RequestExecutionLevel user 12 | 13 | Var StartMenuFolder 14 | 15 | !define MUI_ABORTWARNING 16 | 17 | !insertmacro MUI_PAGE_WELCOME 18 | !insertmacro MUI_PAGE_LICENSE "COPYING" 19 | !insertmacro MUI_PAGE_COMPONENTS 20 | !insertmacro MUI_PAGE_DIRECTORY 21 | !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" 22 | !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\ccoors\Valeronoi" 23 | !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" 24 | 25 | !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder 26 | !insertmacro MUI_PAGE_INSTFILES 27 | !insertmacro MUI_PAGE_FINISH 28 | 29 | !insertmacro MUI_UNPAGE_CONFIRM 30 | !insertmacro MUI_UNPAGE_INSTFILES 31 | 32 | !insertmacro MUI_LANGUAGE "English" 33 | 34 | Section "Valeronoi" SecValeronoi 35 | SectionIn RO 36 | SetOutPath "$INSTDIR" 37 | 38 | File /r *.exe 39 | File /r *.dll 40 | 41 | SetShellVarContext current 42 | CreateShortCut "$DESKTOP\Valeronoi.lnk" "$INSTDIR\valeronoi.exe" 43 | 44 | !insertmacro MUI_STARTMENU_WRITE_BEGIN Application 45 | CreateDirectory "$SMPROGRAMS\$StartMenuFolder" 46 | CreateShortcut "$SMPROGRAMS\$StartMenuFolder\Valeronoi.lnk" "$INSTDIR\valeronoi.exe" 47 | CreateShortcut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" 48 | !insertmacro MUI_STARTMENU_WRITE_END 49 | 50 | WriteRegStr HKCU "Software\ccoors\Valeronoi" "" $INSTDIR 51 | WriteUninstaller "$INSTDIR\Uninstall.exe" 52 | SectionEnd 53 | 54 | LangString DESC_SecValeronoi ${LANG_ENGLISH} "Valeronoi main application." 55 | 56 | !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN 57 | !insertmacro MUI_DESCRIPTION_TEXT ${SecValeronoi} $(DESC_SecValeronoi) 58 | !insertmacro MUI_FUNCTION_DESCRIPTION_END 59 | 60 | Section "Uninstall" 61 | Delete "$INSTDIR\Uninstall.exe" 62 | RMDir /r "$INSTDIR" 63 | !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder 64 | 65 | Delete "$DESKTOP\Valeronoi.lnk" 66 | Delete "$SMPROGRAMS\$StartMenuFolder\Valeronoi.lnk" 67 | Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" 68 | RMDir /r "$SMPROGRAMS\$StartMenuFolder" 69 | DeleteRegKey HKCU "Software\ccoors\Valeronoi" 70 | SectionEnd 71 | --------------------------------------------------------------------------------