- #include <SFML/Graphics.hpp>
- #include <SFML/Network.hpp>
- #include <cjson/cJSON.h>
- #include <iostream>
- #include <string>
- #include <thread>
- #include <chrono>
- #include <atomic>
- // ============================================================
- // KONFIGURACJA
- // ============================================================
- const std::string URL_HOST = "192.168.1.2";
- const std::string URL_PATH = "/api";
- // Jak często sprawdzamy serwer.
- // 100 ms daje maksymalnie ~100 ms oczekiwania na wykrycie zmiany.
- constexpr int POLL_MS = 100;
- // Jak często traktujemy dane jako pełny cykl odświeżenia.
- constexpr int DATA_REFRESH_MS = 1000;
- // Timeout HTTP.
- // Nie może być kilka sekund, bo wtedy GUI może długo czekać
- // na kolejną próbę.
- constexpr int HTTP_TIMEOUT_MS = 150;
- // GUI
- constexpr unsigned int FPS = 120;
- // ============================================================
- // DANE
- // ============================================================
- struct RadarData
- {
- bool presence = false;
- int distance = 0;
- bool valid = false;
- // Numer wersji danych.
- // Zwiększany po każdym poprawnym odebraniu JSON.
- unsigned long long version = 0;
- };
- // ============================================================
- // WSPÓŁDZIELONE DANE
- // ============================================================
- // Każde pole jest atomowe.
- // Dzięki temu GUI nie musi blokować się mutexem.
- std::atomic<bool> radarPresence{false};
- std::atomic<int> radarDistance{0};
- std::atomic<bool> radarValid{false};
- // Zwiększane po otrzymaniu nowych danych.
- std::atomic<unsigned long long> radarVersion{0};
- // Sterowanie programem.
- std::atomic<bool> running{true};
- // ============================================================
- // THREAD HTTP
- // ============================================================
- void fetchLoop()
- {
- sf::Http http(URL_HOST);
- sf::Http::Request request(
- URL_PATH,
- sf::Http::Request::Get
- );
- request.setField(
- "User-Agent",
- "radar-monitor/2.0"
- );
- request.setField(
- "Accept",
- "application/json"
- );
- // --------------------------------------------------------
- // Następny request
- // --------------------------------------------------------
- auto nextPoll =
- std::chrono::steady_clock::now();
- while (running)
- {
- // ====================================================
- // Czekamy tylko do następnego requestu.
- // ====================================================
- auto now =
- std::chrono::steady_clock::now();
- if (now < nextPoll)
- {
- std::this_thread::sleep_until(nextPoll);
- }
- if (!running)
- break;
- // Następne odpytywanie za 100 ms.
- nextPoll +=
- std::chrono::milliseconds(POLL_MS);
- // ====================================================
- // REQUEST
- // ====================================================
- sf::Http::Response response =
- http.sendRequest(
- request,
- sf::milliseconds(HTTP_TIMEOUT_MS)
- );
- // ====================================================
- // JEŚLI SERWER ODPOWIEDZIAŁ
- // ====================================================
- if (response.getStatus() ==
- sf::Http::Response::Ok)
- {
- const std::string& body =
- response.getBody();
- cJSON* json =
- cJSON_Parse(body.c_str());
- if (json != nullptr)
- {
- cJSON* presence =
- cJSON_GetObjectItemCaseSensitive(
- json,
- "presence"
- );
- cJSON* distance =
- cJSON_GetObjectItemCaseSensitive(
- json,
- "detectionDistance"
- );
- bool validPresence =
- cJSON_IsBool(presence);
- bool validDistance =
- cJSON_IsNumber(distance);
- // --------------------------------------------
- // POPRAWNY JSON
- // --------------------------------------------
- if (validPresence &&
- validDistance)
- {
- bool newPresence =
- cJSON_IsTrue(presence);
- int newDistance =
- distance->valueint;
- // ----------------------------------------
- // NAJPIERW zapisujemy dane
- // ----------------------------------------
- radarPresence.store(
- newPresence,
- std::memory_order_relaxed
- );
- radarDistance.store(
- newDistance,
- std::memory_order_relaxed
- );
- radarValid.store(
- true,
- std::memory_order_relaxed
- );
- // ----------------------------------------
- // NA KOŃCU zwiększamy VERSION.
- //
- // GUI widząc nową wersję wie, że komplet
- // danych jest już zapisany.
- // ----------------------------------------
- radarVersion.fetch_add(
- 1,
- std::memory_order_release
- );
- }
- cJSON_Delete(json);
- }
- }
- // ====================================================
- // JEŚLI REQUEST SIĘ NIE UDAŁ
- // ====================================================
- // Nie zerujemy od razu danych.
- //
- // Dzięki temu chwilowy błąd sieci nie powoduje
- // migania GUI.
- //
- // Dopiero kolejne poprawne dane je zastąpią.
- }
- }
- // ============================================================
- // MAIN
- // ============================================================
- int main()
- {
- // ========================================================
- // OKNO
- // ========================================================
- sf::RenderWindow window(
- sf::VideoMode(800, 480),
- "Radar Monitor",
- sf::Style::Titlebar |
- sf::Style::Close
- );
- // 120 FPS.
- //
- // Nowe dane mogą zostać pokazane maksymalnie
- // po około 8 ms od następnej klatki GUI.
- window.setFramerateLimit(FPS);
- // ========================================================
- // CZCIONKA
- // ========================================================
- sf::Font font;
- if (!font.loadFromFile(
- "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"))
- {
- if (!font.loadFromFile(
- "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"))
- {
- std::cerr
- << "Nie można załadować czcionki!\n";
- return 1;
- }
- }
- // ========================================================
- // TEKSTY
- // ========================================================
- sf::Text title(
- "RADAR MONITOR",
- font,
- 48
- );
- title.setFillColor(
- sf::Color::White
- );
- title.setPosition(
- 200,
- 20
- );
- // --------------------------------------------------------
- sf::Text presenceLabel(
- "PRESENCE",
- font,
- 36
- );
- presenceLabel.setFillColor(
- sf::Color(180, 180, 180)
- );
- presenceLabel.setPosition(
- 80,
- 140
- );
- // --------------------------------------------------------
- sf::Text distanceLabel(
- "DISTANCE",
- font,
- 36
- );
- distanceLabel.setFillColor(
- sf::Color(180, 180, 180)
- );
- distanceLabel.setPosition(
- 80,
- 280
- );
- // --------------------------------------------------------
- sf::Text distanceValue(
- "---",
- font,
- 72
- );
- distanceValue.setFillColor(
- sf::Color::White
- );
- distanceValue.setPosition(
- 400,
- 260
- );
- // --------------------------------------------------------
- sf::Text statusText(
- "Laczenie...",
- font,
- 24
- );
- statusText.setFillColor(
- sf::Color(150, 150, 150)
- );
- statusText.setPosition(
- 20,
- 430
- );
- // ========================================================
- // LED
- // ========================================================
- sf::CircleShape led(
- 50.f
- );
- led.setPosition(
- 420,
- 120
- );
- led.setOutlineThickness(
- 4.f
- );
- led.setOutlineColor(
- sf::Color(40, 40, 40)
- );
- led.setFillColor(
- sf::Color(80, 80, 80)
- );
- // ========================================================
- // START HTTP
- // ========================================================
- std::thread fetcher(
- fetchLoop
- );
- // ========================================================
- // LOKALNY CACHE GUI
- // ========================================================
- unsigned long long lastVersion = 0;
- RadarData displayedData;
- // ========================================================
- // PĘTLA GUI
- // ========================================================
- while (window.isOpen())
- {
- // ====================================================
- // EVENTY
- // ====================================================
- sf::Event event;
- while (window.pollEvent(event))
- {
- if (event.type ==
- sf::Event::Closed)
- {
- window.close();
- }
- }
- // ====================================================
- // SPRAWDZAMY, CZY PRZYSZŁY NOWE DANE
- // ====================================================
- unsigned long long version =
- radarVersion.load(
- std::memory_order_acquire
- );
- if (version != lastVersion)
- {
- lastVersion = version;
- // -----------------------------------------------
- // Odczytujemy komplet danych
- // -----------------------------------------------
- displayedData.presence =
- radarPresence.load(
- std::memory_order_relaxed
- );
- displayedData.distance =
- radarDistance.load(
- std::memory_order_relaxed
- );
- displayedData.valid =
- radarValid.load(
- std::memory_order_relaxed
- );
- // =================================================
- // LED
- // =================================================
- if (displayedData.valid)
- {
- if (displayedData.presence)
- {
- led.setFillColor(
- sf::Color(0, 220, 0)
- );
- }
- else
- {
- led.setFillColor(
- sf::Color(220, 0, 0)
- );
- }
- // =================================================
- // DISTANCE
- // =================================================
- distanceValue.setString(
- std::to_string(
- displayedData.distance
- ) + " cm"
- );
- // =================================================
- // STATUS
- // =================================================
- statusText.setString(
- "OK"
- );
- statusText.setFillColor(
- sf::Color(0, 200, 0)
- );
- }
- else
- {
- led.setFillColor(
- sf::Color(80, 80, 80)
- );
- distanceValue.setString(
- "---"
- );
- statusText.setString(
- "Brak danych"
- );
- statusText.setFillColor(
- sf::Color(200, 80, 80)
- );
- }
- }
- // ====================================================
- // RENDER
- // ====================================================
- window.clear(
- sf::Color(25, 25, 30)
- );
- window.draw(title);
- window.draw(presenceLabel);
- window.draw(led);
- window.draw(distanceLabel);
- window.draw(distanceValue);
- window.draw(statusText);
- window.display();
- }
- // ========================================================
- // ZATRZYMANIE
- // ========================================================
- running.store(
- false
- );
- if (fetcher.joinable())
- {
- fetcher.join();
- }
- return 0;
- }

Pobierz program!
