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 : }
|