LCOV - code coverage report
Current view: top level - io - RestApiProvider.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 76.7 % 60 46
Test Date: 2026-03-20 13:12:51 Functions: 87.5 % 8 7
Branches: 40.0 % 70 28

             Branch data     Line data    Source code
       1                 :             : #include "RestApiProvider.h"
       2                 :             : #include "Logger.h"
       3                 :             : #include <iostream>
       4                 :             : #include <regex>
       5                 :             : 
       6                 :             : #ifdef _WIN32
       7                 :             : #include <windows.h>
       8                 :             : #include <wininet.h>
       9                 :             : #pragma comment(lib, "wininet.lib")
      10                 :             : #endif
      11                 :             : 
      12                 :         617 : RestApiProvider& RestApiProvider::Get() {
      13   [ +  +  +  - ]:         617 :     static RestApiProvider instance;
      14                 :         617 :     return instance;
      15                 :             : }
      16                 :             : 
      17                 :           1 : RestApiProvider::~RestApiProvider() {
      18                 :           1 :     std::lock_guard<std::mutex> lock(m_threadMutex);
      19         [ +  - ]:           1 :     if (m_requestThread.joinable()) {
      20                 :           1 :         m_requestThread.join();
      21                 :             :     }
      22                 :           1 : }
      23                 :             : 
      24                 :           4 : void RestApiProvider::RequestSteeringRange(int port) {
      25         [ -  + ]:           4 :     if (m_isRequesting.load()) return;
      26                 :             : 
      27         [ +  - ]:           4 :     std::lock_guard<std::mutex> lock(m_threadMutex);
      28         [ +  + ]:           4 :     if (m_requestThread.joinable()) {
      29         [ +  - ]:           3 :         m_requestThread.join();
      30                 :             :     }
      31                 :             : 
      32                 :           4 :     m_isRequesting = true;
      33         [ +  - ]:           8 :     m_requestThread = std::thread([this, port]() {
      34                 :             :         try {
      35         [ +  - ]:           4 :             this->PerformRequest(port);
      36                 :           0 :         } catch (...) {
      37   [ -  -  -  - ]:           0 :             Logger::Get().LogFile("RestApiProvider: Unexpected exception in request thread");
      38                 :           0 :         }
      39                 :           4 :         this->m_isRequesting = false;
      40                 :           8 :     });
      41                 :           4 : }
      42                 :             : 
      43                 :         488 : float RestApiProvider::GetFallbackRangeDeg() const {
      44                 :         488 :     return m_fallbackRangeDeg.load();
      45                 :             : }
      46                 :             : 
      47                 :           0 : bool RestApiProvider::IsRequesting() const {
      48                 :           0 :     return m_isRequesting.load();
      49                 :             : }
      50                 :             : 
      51                 :           4 : void RestApiProvider::PerformRequest(int port) {
      52                 :           4 :     std::string response;
      53                 :           4 :     bool success = false;
      54                 :             : 
      55                 :             : #ifdef _WIN32
      56                 :             :     HINTERNET hInternet = InternetOpenA("lmuFFB", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
      57                 :             :     if (hInternet) {
      58                 :             :         std::string url = "http://localhost:" + std::to_string(port) + "/rest/garage/getPlayerGarageData";
      59                 :             :         HINTERNET hConnect = InternetOpenUrlA(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0);
      60                 :             :         if (hConnect) {
      61                 :             :             char buffer[4096];
      62                 :             :             DWORD bytesRead;
      63                 :             :             while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
      64                 :             :                 response.append(buffer, bytesRead);
      65                 :             :             }
      66                 :             :             InternetCloseHandle(hConnect);
      67                 :             :             success = true;
      68                 :             :         } else {
      69                 :             :             Logger::Get().LogFile("RestApiProvider: Failed to open URL (Port %d)", port);
      70                 :             :         }
      71                 :             :         InternetCloseHandle(hInternet);
      72                 :             :     } else {
      73                 :             :         Logger::Get().LogFile("RestApiProvider: Failed to initialize WinINet");
      74                 :             :     }
      75                 :             : #else
      76   [ +  -  +  - ]:           4 :     Logger::Get().LogFile("RestApiProvider: Mock request on Linux (Port %d)", port);
      77                 :             : #endif
      78                 :             : 
      79   [ -  +  -  -  :           4 :     if (success && !response.empty()) {
                   -  + ]
      80         [ #  # ]:           0 :         float range = ParseSteeringLock(response);
      81         [ #  # ]:           0 :         if (range > 0.0f) {
      82                 :           0 :             m_fallbackRangeDeg = range;
      83   [ #  #  #  # ]:           0 :             Logger::Get().LogFile("RestApiProvider: Retrieved steering range: %.1f deg", range);
      84                 :             :         } else {
      85   [ #  #  #  # ]:           0 :             Logger::Get().LogFile("RestApiProvider: Could not parse VM_STEER_LOCK from response");
      86                 :             :         }
      87                 :             :     }
      88                 :           4 : }
      89                 :             : 
      90                 :           5 : float RestApiProvider::ParseSteeringLock(const std::string& json) {
      91                 :             :     // Look for "VM_STEER_LOCK" and then "stringValue"
      92                 :             :     // Example: "VM_STEER_LOCK" : { ... "stringValue" : "540 deg" ... }
      93                 :             : 
      94                 :             :     // 1. Find the property
      95                 :           5 :     size_t propPos = json.find("\"VM_STEER_LOCK\"");
      96         [ +  + ]:           5 :     if (propPos == std::string::npos) return 0.0f;
      97                 :             : 
      98                 :             :     // 2. Find "stringValue" within the next few hundred characters
      99                 :           3 :     size_t searchLimit = 512;
     100         [ +  - ]:           3 :     std::string sub = json.substr(propPos, searchLimit);
     101                 :           3 :     size_t valPos = sub.find("\"stringValue\"");
     102         [ -  + ]:           3 :     if (valPos == std::string::npos) return 0.0f;
     103                 :             : 
     104                 :             :     // 3. Extract the value string
     105                 :           3 :     size_t colonPos = sub.find(':', valPos);
     106         [ -  + ]:           3 :     if (colonPos == std::string::npos) return 0.0f;
     107                 :             : 
     108                 :           3 :     size_t startQuote = sub.find('\"', colonPos);
     109         [ -  + ]:           3 :     if (startQuote == std::string::npos) return 0.0f;
     110                 :             : 
     111                 :           3 :     size_t endQuote = sub.find('\"', startQuote + 1);
     112         [ -  + ]:           3 :     if (endQuote == std::string::npos) return 0.0f;
     113                 :             : 
     114         [ +  - ]:           3 :     std::string valueStr = sub.substr(startQuote + 1, endQuote - startQuote - 1);
     115                 :             : 
     116                 :             :     // 4. Extract numeric value using regex
     117         [ +  - ]:           3 :     std::regex re(R"(\d*\.?\d+)");
     118                 :           3 :     std::smatch match;
     119   [ +  -  +  - ]:           3 :     if (std::regex_search(valueStr, match, re)) {
     120                 :             :         try {
     121   [ +  -  +  - ]:           3 :             return std::stof(match.str());
     122                 :           0 :         } catch (...) {
     123                 :           0 :             return 0.0f;
     124         [ -  - ]:           0 :         }
     125                 :             :     }
     126                 :             : 
     127                 :           0 :     return 0.0f;
     128                 :           3 : }
        

Generated by: LCOV version 2.0-1