LCOV - code coverage report
Current view: top level - src - RestApiProvider.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 76.7 % 60 46
Test Date: 2026-03-03 13:56:25 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                 :         812 : RestApiProvider& RestApiProvider::Get() {
      13   [ +  +  +  - ]:         812 :     static RestApiProvider instance;
      14                 :         812 :     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                 :           2 : void RestApiProvider::RequestSteeringRange(int port) {
      25         [ -  + ]:           2 :     if (m_isRequesting.load()) return;
      26                 :             : 
      27         [ +  - ]:           2 :     std::lock_guard<std::mutex> lock(m_threadMutex);
      28         [ +  + ]:           2 :     if (m_requestThread.joinable()) {
      29         [ +  - ]:           1 :         m_requestThread.join();
      30                 :             :     }
      31                 :             : 
      32                 :           2 :     m_isRequesting = true;
      33         [ +  - ]:           4 :     m_requestThread = std::thread([this, port]() {
      34                 :             :         try {
      35         [ +  - ]:           2 :             this->PerformRequest(port);
      36                 :           0 :         } catch (...) {
      37   [ -  -  -  - ]:           0 :             Logger::Get().Log("RestApiProvider: Unexpected exception in request thread");
      38                 :           0 :         }
      39                 :           2 :         this->m_isRequesting = false;
      40                 :           4 :     });
      41                 :           2 : }
      42                 :             : 
      43                 :         809 : float RestApiProvider::GetFallbackRangeDeg() const {
      44                 :         809 :     return m_fallbackRangeDeg.load();
      45                 :             : }
      46                 :             : 
      47                 :           0 : bool RestApiProvider::IsRequesting() const {
      48                 :           0 :     return m_isRequesting.load();
      49                 :             : }
      50                 :             : 
      51                 :           2 : void RestApiProvider::PerformRequest(int port) {
      52                 :           2 :     std::string response;
      53                 :           2 :     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().Log("RestApiProvider: Failed to open URL (Port %d)", port);
      70                 :             :         }
      71                 :             :         InternetCloseHandle(hInternet);
      72                 :             :     } else {
      73                 :             :         Logger::Get().Log("RestApiProvider: Failed to initialize WinINet");
      74                 :             :     }
      75                 :             : #else
      76                 :             :     // Mock for Linux/Testing
      77                 :             :     // In real tests, we might want to inject this mock behavior
      78   [ +  -  +  - ]:           2 :     Logger::Get().Log("RestApiProvider: Mock request on Linux (Port %d)", port);
      79                 :             :     // For now, do nothing, we will mock ParseSteeringLock in tests
      80                 :             : #endif
      81                 :             : 
      82   [ -  +  -  -  :           2 :     if (success && !response.empty()) {
                   -  + ]
      83         [ #  # ]:           0 :         float range = ParseSteeringLock(response);
      84         [ #  # ]:           0 :         if (range > 0.0f) {
      85                 :           0 :             m_fallbackRangeDeg = range;
      86   [ #  #  #  # ]:           0 :             Logger::Get().Log("RestApiProvider: Retrieved steering range: %.1f deg", range);
      87                 :             :         } else {
      88   [ #  #  #  # ]:           0 :             Logger::Get().Log("RestApiProvider: Could not parse VM_STEER_LOCK from response");
      89                 :             :         }
      90                 :             :     }
      91                 :           2 : }
      92                 :             : 
      93                 :           5 : float RestApiProvider::ParseSteeringLock(const std::string& json) {
      94                 :             :     // Look for "VM_STEER_LOCK" and then "stringValue"
      95                 :             :     // Example: "VM_STEER_LOCK" : { ... "stringValue" : "540 deg" ... }
      96                 :             : 
      97                 :             :     // 1. Find the property
      98                 :           5 :     size_t propPos = json.find("\"VM_STEER_LOCK\"");
      99         [ +  + ]:           5 :     if (propPos == std::string::npos) return 0.0f;
     100                 :             : 
     101                 :             :     // 2. Find "stringValue" within the next few hundred characters
     102                 :           3 :     size_t searchLimit = 512;
     103         [ +  - ]:           3 :     std::string sub = json.substr(propPos, searchLimit);
     104                 :           3 :     size_t valPos = sub.find("\"stringValue\"");
     105         [ -  + ]:           3 :     if (valPos == std::string::npos) return 0.0f;
     106                 :             : 
     107                 :             :     // 3. Extract the value string
     108                 :           3 :     size_t colonPos = sub.find(':', valPos);
     109         [ -  + ]:           3 :     if (colonPos == std::string::npos) return 0.0f;
     110                 :             : 
     111                 :           3 :     size_t startQuote = sub.find('\"', colonPos);
     112         [ -  + ]:           3 :     if (startQuote == std::string::npos) return 0.0f;
     113                 :             : 
     114                 :           3 :     size_t endQuote = sub.find('\"', startQuote + 1);
     115         [ -  + ]:           3 :     if (endQuote == std::string::npos) return 0.0f;
     116                 :             : 
     117         [ +  - ]:           3 :     std::string valueStr = sub.substr(startQuote + 1, endQuote - startQuote - 1);
     118                 :             : 
     119                 :             :     // 4. Extract numeric value using regex
     120         [ +  - ]:           3 :     std::regex re(R"(\d*\.?\d+)");
     121                 :           3 :     std::smatch match;
     122   [ +  -  +  - ]:           3 :     if (std::regex_search(valueStr, match, re)) {
     123                 :             :         try {
     124   [ +  -  +  - ]:           3 :             return std::stof(match.str());
     125                 :           0 :         } catch (...) {
     126                 :           0 :             return 0.0f;
     127         [ -  - ]:           0 :         }
     128                 :             :     }
     129                 :             : 
     130                 :           0 :     return 0.0f;
     131                 :           3 : }
        

Generated by: LCOV version 2.0-1