Dodany przez: klima, 12:24 30-08-2026

Nowy Pobierz
  1. #include <SFML/Graphics.hpp>
  2. #include <SFML/Network.hpp>
  3. #include <cjson/cJSON.h>
  4.  
  5. #include <iostream>
  6. #include <string>
  7. #include <thread>
  8. #include <chrono>
  9. #include <atomic>
  10.  
  11. // ============================================================
  12. // KONFIGURACJA
  13. // ============================================================
  14.  
  15. const std::string URL_HOST = "192.168.1.2";
  16. const std::string URL_PATH = "/api";
  17.  
  18. // Jak często sprawdzamy serwer.
  19. // 100 ms daje maksymalnie ~100 ms oczekiwania na wykrycie zmiany.
  20. constexpr int POLL_MS = 100;
  21.  
  22. // Jak często traktujemy dane jako pełny cykl odświeżenia.
  23. constexpr int DATA_REFRESH_MS = 1000;
  24.  
  25. // Timeout HTTP.
  26. // Nie może być kilka sekund, bo wtedy GUI może długo czekać
  27. // na kolejną próbę.
  28. constexpr int HTTP_TIMEOUT_MS = 150;
  29.  
  30. // GUI
  31. constexpr unsigned int FPS = 120;
  32.  
  33. // ============================================================
  34. // DANE
  35. // ============================================================
  36.  
  37. struct RadarData
  38. {
  39.     bool presence = false;
  40.     int distance = 0;
  41.     bool valid = false;
  42.  
  43.     // Numer wersji danych.
  44.     // Zwiększany po każdym poprawnym odebraniu JSON.
  45.     unsigned long long version = 0;
  46. };
  47.  
  48. // ============================================================
  49. // WSPÓŁDZIELONE DANE
  50. // ============================================================
  51.  
  52. // Każde pole jest atomowe.
  53. // Dzięki temu GUI nie musi blokować się mutexem.
  54.  
  55. std::atomic<bool> radarPresence{false};
  56. std::atomic<int> radarDistance{0};
  57. std::atomic<bool> radarValid{false};
  58.  
  59. // Zwiększane po otrzymaniu nowych danych.
  60. std::atomic<unsigned long long> radarVersion{0};
  61.  
  62. // Sterowanie programem.
  63. std::atomic<bool> running{true};
  64.  
  65. // ============================================================
  66. // THREAD HTTP
  67. // ============================================================
  68.  
  69. void fetchLoop()
  70. {
  71.     sf::Http http(URL_HOST);
  72.  
  73.     sf::Http::Request request(
  74.         URL_PATH,
  75.         sf::Http::Request::Get
  76.     );
  77.  
  78.     request.setField(
  79.         "User-Agent",
  80.         "radar-monitor/2.0"
  81.     );
  82.  
  83.     request.setField(
  84.         "Accept",
  85.         "application/json"
  86.     );
  87.  
  88.     // --------------------------------------------------------
  89.     // Następny request
  90.     // --------------------------------------------------------
  91.  
  92.     auto nextPoll =
  93.         std::chrono::steady_clock::now();
  94.  
  95.     while (running)
  96.     {
  97.         // ====================================================
  98.         // Czekamy tylko do następnego requestu.
  99.         // ====================================================
  100.  
  101.         auto now =
  102.             std::chrono::steady_clock::now();
  103.  
  104.         if (now < nextPoll)
  105.         {
  106.             std::this_thread::sleep_until(nextPoll);
  107.         }
  108.  
  109.         if (!running)
  110.             break;
  111.  
  112.         // Następne odpytywanie za 100 ms.
  113.         nextPoll +=
  114.             std::chrono::milliseconds(POLL_MS);
  115.  
  116.         // ====================================================
  117.         // REQUEST
  118.         // ====================================================
  119.  
  120.         sf::Http::Response response =
  121.             http.sendRequest(
  122.                 request,
  123.                 sf::milliseconds(HTTP_TIMEOUT_MS)
  124.             );
  125.  
  126.         // ====================================================
  127.         // JEŚLI SERWER ODPOWIEDZIAŁ
  128.         // ====================================================
  129.  
  130.         if (response.getStatus() ==
  131.             sf::Http::Response::Ok)
  132.         {
  133.             const std::string& body =
  134.                 response.getBody();
  135.  
  136.             cJSON* json =
  137.                 cJSON_Parse(body.c_str());
  138.  
  139.             if (json != nullptr)
  140.             {
  141.                 cJSON* presence =
  142.                     cJSON_GetObjectItemCaseSensitive(
  143.                         json,
  144.                         "presence"
  145.                     );
  146.  
  147.                 cJSON* distance =
  148.                     cJSON_GetObjectItemCaseSensitive(
  149.                         json,
  150.                         "detectionDistance"
  151.                     );
  152.  
  153.                 bool validPresence =
  154.                     cJSON_IsBool(presence);
  155.  
  156.                 bool validDistance =
  157.                     cJSON_IsNumber(distance);
  158.  
  159.                 // --------------------------------------------
  160.                 // POPRAWNY JSON
  161.                 // --------------------------------------------
  162.  
  163.                 if (validPresence &&
  164.                     validDistance)
  165.                 {
  166.                     bool newPresence =
  167.                         cJSON_IsTrue(presence);
  168.  
  169.                     int newDistance =
  170.                         distance->valueint;
  171.  
  172.                     // ----------------------------------------
  173.                     // NAJPIERW zapisujemy dane
  174.                     // ----------------------------------------
  175.  
  176.                     radarPresence.store(
  177.                         newPresence,
  178.                         std::memory_order_relaxed
  179.                     );
  180.  
  181.                     radarDistance.store(
  182.                         newDistance,
  183.                         std::memory_order_relaxed
  184.                     );
  185.  
  186.                     radarValid.store(
  187.                         true,
  188.                         std::memory_order_relaxed
  189.                     );
  190.  
  191.                     // ----------------------------------------
  192.                     // NA KOŃCU zwiększamy VERSION.
  193.                     //
  194.                     // GUI widząc nową wersję wie, że komplet
  195.                     // danych jest już zapisany.
  196.                     // ----------------------------------------
  197.  
  198.                     radarVersion.fetch_add(
  199.                         1,
  200.                         std::memory_order_release
  201.                     );
  202.                 }
  203.  
  204.                 cJSON_Delete(json);
  205.             }
  206.         }
  207.  
  208.         // ====================================================
  209.         // JEŚLI REQUEST SIĘ NIE UDAŁ
  210.         // ====================================================
  211.  
  212.         // Nie zerujemy od razu danych.
  213.         //
  214.         // Dzięki temu chwilowy błąd sieci nie powoduje
  215.         // migania GUI.
  216.         //
  217.         // Dopiero kolejne poprawne dane je zastąpią.
  218.     }
  219. }
  220.  
  221. // ============================================================
  222. // MAIN
  223. // ============================================================
  224.  
  225. int main()
  226. {
  227.     // ========================================================
  228.     // OKNO
  229.     // ========================================================
  230.  
  231.     sf::RenderWindow window(
  232.         sf::VideoMode(800, 480),
  233.         "Radar Monitor",
  234.         sf::Style::Titlebar |
  235.         sf::Style::Close
  236.     );
  237.  
  238.     // 120 FPS.
  239.     //
  240.     // Nowe dane mogą zostać pokazane maksymalnie
  241.     // po około 8 ms od następnej klatki GUI.
  242.  
  243.     window.setFramerateLimit(FPS);
  244.  
  245.     // ========================================================
  246.     // CZCIONKA
  247.     // ========================================================
  248.  
  249.     sf::Font font;
  250.  
  251.     if (!font.loadFromFile(
  252.             "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"))
  253.     {
  254.         if (!font.loadFromFile(
  255.                 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"))
  256.         {
  257.             std::cerr
  258.                 << "Nie można załadować czcionki!\n";
  259.  
  260.             return 1;
  261.         }
  262.     }
  263.  
  264.     // ========================================================
  265.     // TEKSTY
  266.     // ========================================================
  267.  
  268.     sf::Text title(
  269.         "RADAR MONITOR",
  270.         font,
  271.         48
  272.     );
  273.  
  274.     title.setFillColor(
  275.         sf::Color::White
  276.     );
  277.  
  278.     title.setPosition(
  279.         200,
  280.         20
  281.     );
  282.  
  283.     // --------------------------------------------------------
  284.  
  285.     sf::Text presenceLabel(
  286.         "PRESENCE",
  287.         font,
  288.         36
  289.     );
  290.  
  291.     presenceLabel.setFillColor(
  292.         sf::Color(180, 180, 180)
  293.     );
  294.  
  295.     presenceLabel.setPosition(
  296.         80,
  297.         140
  298.     );
  299.  
  300.     // --------------------------------------------------------
  301.  
  302.     sf::Text distanceLabel(
  303.         "DISTANCE",
  304.         font,
  305.         36
  306.     );
  307.  
  308.     distanceLabel.setFillColor(
  309.         sf::Color(180, 180, 180)
  310.     );
  311.  
  312.     distanceLabel.setPosition(
  313.         80,
  314.         280
  315.     );
  316.  
  317.     // --------------------------------------------------------
  318.  
  319.     sf::Text distanceValue(
  320.         "---",
  321.         font,
  322.         72
  323.     );
  324.  
  325.     distanceValue.setFillColor(
  326.         sf::Color::White
  327.     );
  328.  
  329.     distanceValue.setPosition(
  330.         400,
  331.         260
  332.     );
  333.  
  334.     // --------------------------------------------------------
  335.  
  336.     sf::Text statusText(
  337.         "Laczenie...",
  338.         font,
  339.         24
  340.     );
  341.  
  342.     statusText.setFillColor(
  343.         sf::Color(150, 150, 150)
  344.     );
  345.  
  346.     statusText.setPosition(
  347.         20,
  348.         430
  349.     );
  350.  
  351.     // ========================================================
  352.     // LED
  353.     // ========================================================
  354.  
  355.     sf::CircleShape led(
  356.         50.f
  357.     );
  358.  
  359.     led.setPosition(
  360.         420,
  361.         120
  362.     );
  363.  
  364.     led.setOutlineThickness(
  365.         4.f
  366.     );
  367.  
  368.     led.setOutlineColor(
  369.         sf::Color(40, 40, 40)
  370.     );
  371.  
  372.     led.setFillColor(
  373.         sf::Color(80, 80, 80)
  374.     );
  375.  
  376.     // ========================================================
  377.     // START HTTP
  378.     // ========================================================
  379.  
  380.     std::thread fetcher(
  381.         fetchLoop
  382.     );
  383.  
  384.     // ========================================================
  385.     // LOKALNY CACHE GUI
  386.     // ========================================================
  387.  
  388.     unsigned long long lastVersion = 0;
  389.  
  390.     RadarData displayedData;
  391.  
  392.     // ========================================================
  393.     // PĘTLA GUI
  394.     // ========================================================
  395.  
  396.     while (window.isOpen())
  397.     {
  398.         // ====================================================
  399.         // EVENTY
  400.         // ====================================================
  401.  
  402.         sf::Event event;
  403.  
  404.         while (window.pollEvent(event))
  405.         {
  406.             if (event.type ==
  407.                 sf::Event::Closed)
  408.             {
  409.                 window.close();
  410.             }
  411.         }
  412.  
  413.         // ====================================================
  414.         // SPRAWDZAMY, CZY PRZYSZŁY NOWE DANE
  415.         // ====================================================
  416.  
  417.         unsigned long long version =
  418.             radarVersion.load(
  419.                 std::memory_order_acquire
  420.             );
  421.  
  422.         if (version != lastVersion)
  423.         {
  424.             lastVersion = version;
  425.  
  426.             // -----------------------------------------------
  427.             // Odczytujemy komplet danych
  428.             // -----------------------------------------------
  429.  
  430.             displayedData.presence =
  431.                 radarPresence.load(
  432.                     std::memory_order_relaxed
  433.                 );
  434.  
  435.             displayedData.distance =
  436.                 radarDistance.load(
  437.                     std::memory_order_relaxed
  438.                 );
  439.  
  440.             displayedData.valid =
  441.                 radarValid.load(
  442.                     std::memory_order_relaxed
  443.                 );
  444.  
  445.             // =================================================
  446.             // LED
  447.             // =================================================
  448.  
  449.             if (displayedData.valid)
  450.             {
  451.                 if (displayedData.presence)
  452.                 {
  453.                     led.setFillColor(
  454.                         sf::Color(0, 220, 0)
  455.                     );
  456.                 }
  457.                 else
  458.                 {
  459.                     led.setFillColor(
  460.                         sf::Color(220, 0, 0)
  461.                     );
  462.                 }
  463.  
  464.                 // =================================================
  465.                 // DISTANCE
  466.                 // =================================================
  467.  
  468.                 distanceValue.setString(
  469.                     std::to_string(
  470.                         displayedData.distance
  471.                     ) + " cm"
  472.                 );
  473.  
  474.                 // =================================================
  475.                 // STATUS
  476.                 // =================================================
  477.  
  478.                 statusText.setString(
  479.                     "OK"
  480.                 );
  481.  
  482.                 statusText.setFillColor(
  483.                     sf::Color(0, 200, 0)
  484.                 );
  485.             }
  486.             else
  487.             {
  488.                 led.setFillColor(
  489.                     sf::Color(80, 80, 80)
  490.                 );
  491.  
  492.                 distanceValue.setString(
  493.                     "---"
  494.                 );
  495.  
  496.                 statusText.setString(
  497.                     "Brak danych"
  498.                 );
  499.  
  500.                 statusText.setFillColor(
  501.                     sf::Color(200, 80, 80)
  502.                 );
  503.             }
  504.         }
  505.  
  506.         // ====================================================
  507.         // RENDER
  508.         // ====================================================
  509.  
  510.         window.clear(
  511.             sf::Color(25, 25, 30)
  512.         );
  513.  
  514.         window.draw(title);
  515.         window.draw(presenceLabel);
  516.         window.draw(led);
  517.         window.draw(distanceLabel);
  518.         window.draw(distanceValue);
  519.         window.draw(statusText);
  520.  
  521.         window.display();
  522.     }
  523.  
  524.     // ========================================================
  525.     // ZATRZYMANIE
  526.     // ========================================================
  527.  
  528.     running.store(
  529.         false
  530.     );
  531.  
  532.     if (fetcher.joinable())
  533.     {
  534.         fetcher.join();
  535.     }
  536.  
  537.     return 0;
  538. }
  539.  

Źródło:

Ostatnie wpisy

Linki

Funkcje