INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataProvider/WiFi/Sensors/ArubaSensor.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 13 187 7.0%
Functions: 4 12 33.3%
Branches: 12 294 4.1%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "ArubaSensor.hpp"
10
11 #include "util/Time/TimeBase.hpp"
12 #include "util/Logger.hpp"
13 #include <libssh/libssh.h>
14 #include <regex>
15 #include <sstream>
16
17 #include "internal/NodeManager.hpp"
18 namespace nm = NAV::NodeManager;
19 #include "internal/FlowManager.hpp"
20
21 #include "NodeData/WiFi/WiFiObs.hpp"
22
23 112 NAV::ArubaSensor::ArubaSensor()
24
6/12
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 112 times.
✗ Branch 10 not taken.
✓ Branch 14 taken 112 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 112 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 112 times.
✗ Branch 21 not taken.
896 : Node(typeStatic())
25 {
26 LOG_TRACE("{}: called", name);
27
28 112 _onlyRealTime = true;
29 112 _hasConfig = true;
30 112 _guiConfigDefaultWindowSize = { 710, 220 };
31
32
4/8
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 112 times.
✓ Branch 10 taken 112 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
448 nm::CreateOutputPin(this, "WiFiObs", Pin::Type::Flow, { NAV::WiFiObs::type() });
33 224 }
34
35 224 NAV::ArubaSensor::~ArubaSensor()
36 {
37 LOG_TRACE("{}: called", nameId());
38 224 }
39
40 224 std::string NAV::ArubaSensor::typeStatic()
41 {
42
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
448 return "ArubaSensor";
43 }
44
45 std::string NAV::ArubaSensor::type() const
46 {
47 return typeStatic();
48 }
49
50 112 std::string NAV::ArubaSensor::category()
51 {
52
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 return "Data Provider";
53 }
54
55 void NAV::ArubaSensor::guiConfig()
56 {
57 if (ImGui::InputTextWithHint("SSH Host", "192.168.0.0", &_sshHost))
58 {
59 LOG_DEBUG("{}: ssh host changed to {}", nameId(), _sshHost);
60 flow::ApplyChanges();
61 doDeinitialize();
62 }
63 if (ImGui::InputTextWithHint("SSH User", "admin", &_sshUser))
64 {
65 LOG_DEBUG("{}: ssh admin changed to {}", nameId(), _sshUser);
66 flow::ApplyChanges();
67 doDeinitialize();
68 }
69 if (ImGui::InputText("SSH Password", &_sshPassword))
70 {
71 LOG_DEBUG("{}: ssh password changed to {}", nameId(), _sshPassword);
72 flow::ApplyChanges();
73 doDeinitialize();
74 }
75 if (ImGui::InputText("SSH Host Keys", &_sshHostkeys))
76 {
77 LOG_DEBUG("{}: ssh host keys changed to {}", nameId(), _sshHostkeys);
78 flow::ApplyChanges();
79 doDeinitialize();
80 }
81 if (ImGui::InputText("SSH Key Exchange", &_sshKeyExchange))
82 {
83 LOG_DEBUG("{}: ssh key exchange changed to {}", nameId(), _sshKeyExchange);
84 flow::ApplyChanges();
85 doDeinitialize();
86 }
87 if (ImGui::InputText("SSH Publickey Accepted Types", &_sshPublickeyAcceptedTypes))
88 {
89 LOG_DEBUG("{}: ssh publickey accepted types changed to {}", nameId(), _sshPublickeyAcceptedTypes);
90 flow::ApplyChanges();
91 doDeinitialize();
92 }
93 ImGui::Spacing(); // Add spacing here
94 ImGui::Separator(); // Add a horizontal line
95 if (ImGui::InputInt("Output interval [ms]", &_outputInterval))
96 {
97 LOG_DEBUG("{}: output interval changed to {}", nameId(), _outputInterval);
98 flow::ApplyChanges();
99 }
100 }
101
102 [[nodiscard]] json NAV::ArubaSensor::save() const
103 {
104 LOG_TRACE("{}: called", nameId());
105
106 json j;
107
108 j["sshHost"] = _sshHost;
109 j["sshUser"] = _sshUser;
110 j["sshPassword"] = _sshPassword;
111 j["sshHostkeys"] = _sshHostkeys;
112 j["sshKeyExchange"] = _sshKeyExchange;
113 j["sshPublickeyAcceptedTypes"] = _sshPublickeyAcceptedTypes;
114 j["outputInterval"] = _outputInterval;
115
116 return j;
117 }
118
119 void NAV::ArubaSensor::restore(json const& j)
120 {
121 LOG_TRACE("{}: called", nameId());
122
123 if (j.contains("sshHost"))
124 {
125 j.at("sshHost").get_to(_sshHost);
126 }
127 if (j.contains("sshUser"))
128 {
129 j.at("sshUser").get_to(_sshUser);
130 }
131 if (j.contains("sshPassword"))
132 {
133 j.at("sshPassword").get_to(_sshPassword);
134 }
135 if (j.contains("sshHostkeys"))
136 {
137 j.at("sshHostkeys").get_to(_sshHostkeys);
138 }
139 if (j.contains("sshKeyExchange"))
140 {
141 j.at("sshKeyExchange").get_to(_sshKeyExchange);
142 }
143 if (j.contains("sshPublickeyAcceptedTypes"))
144 {
145 j.at("sshPublickeyAcceptedTypes").get_to(_sshPublickeyAcceptedTypes);
146 }
147 if (j.contains("outputInterval"))
148 {
149 j.at("outputInterval").get_to(_outputInterval);
150 }
151 }
152
153 bool NAV::ArubaSensor::resetNode()
154 {
155 return true;
156 }
157
158 bool NAV::ArubaSensor::initialize()
159 {
160 LOG_TRACE("{}: called", nameId());
161
162 // SSH session
163 _session = ssh_new();
164 if (_session == nullptr)
165 {
166 LOG_INFO("{}: Failed to create SSH _session", nameId());
167 return false;
168 }
169 LOG_DEBUG("{}: Successfully created SSH _session", nameId());
170
171 ssh_options_set(_session, SSH_OPTIONS_HOST, "192.168.178.45");
172 ssh_options_set(_session, SSH_OPTIONS_USER, "admin");
173 ssh_options_set(_session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa");
174 ssh_options_set(_session, SSH_OPTIONS_KEY_EXCHANGE, "ecdh-sha2-nistp256");
175 ssh_options_set(_session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-rsa");
176
177 // connect
178 if (ssh_connect(_session) != SSH_OK)
179 {
180 LOG_INFO("{}: Failed to connect to the router", nameId());
181 ssh_free(_session);
182 return false;
183 }
184 LOG_DEBUG("{}: Successfully connected to the router", nameId());
185
186 // authenticate
187 if (ssh_userauth_password(_session, nullptr, _sshPassword.c_str()) != SSH_AUTH_SUCCESS)
188 {
189 LOG_INFO("{}: Authentication failed", nameId());
190 ssh_disconnect(_session);
191 ssh_free(_session);
192 return false;
193 }
194 LOG_DEBUG("{}: Authentication succeeded", nameId());
195
196 // channel
197 _channel = ssh_channel_new(_session);
198 if (_channel == nullptr)
199 {
200 LOG_INFO("{}: Failed to create SSH channel", nameId());
201 ssh_disconnect(_session);
202 ssh_free(_session);
203 return false;
204 }
205 LOG_DEBUG("{}: Successfully created SSH channel", nameId());
206
207 // open _session
208 if (ssh_channel_open_session(_channel) != SSH_OK)
209 {
210 LOG_INFO("{}: Failed to open an SSH _session", nameId());
211 ssh_channel_free(_channel);
212 ssh_disconnect(_session);
213 ssh_free(_session);
214 return false;
215 }
216 LOG_DEBUG("{}: Successfully opened an SSH _session", nameId());
217
218 // pty
219 if (ssh_channel_request_pty(_channel) != SSH_OK)
220 {
221 LOG_INFO("{}: Failed to open pty", nameId());
222 ssh_channel_free(_channel);
223 ssh_disconnect(_session);
224 ssh_free(_session);
225 return false;
226 }
227 LOG_DEBUG("{}: Successfully opened pty", nameId());
228
229 // shell
230 if (ssh_channel_request_shell(_channel) != SSH_OK)
231 {
232 LOG_INFO("{}: Failed to open shell", nameId());
233 ssh_channel_free(_channel);
234 ssh_disconnect(_session);
235 ssh_free(_session);
236 return false;
237 }
238 LOG_DEBUG("{}: Successfully opened shell", nameId());
239
240 _timer.start(_outputInterval, readSensorDataThread, this);
241
242 return true;
243 }
244
245 void NAV::ArubaSensor::deinitialize()
246 {
247 LOG_TRACE("{}: called", nameId());
248
249 if (!isInitialized())
250 {
251 return;
252 }
253
254 ssh_channel_write(_channel, "exit\n", strlen("exit\n"));
255 ssh_channel_free(_channel);
256 ssh_disconnect(_session);
257 ssh_free(_session);
258
259 if (_timer.is_running())
260 {
261 _timer.stop();
262 }
263 }
264
265 void NAV::ArubaSensor::readSensorDataThread(void* userData)
266 {
267 auto* node = static_cast<ArubaSensor*>(userData);
268 auto obs = std::make_shared<WiFiObs>();
269
270 std::array<char, 1024> buffer{};
271 std::string receivedData;
272 size_t bytesRead = 0;
273 // Send command to access point
274 ssh_channel_write(node->_channel, "show ap range scanning-results\n", strlen("show ap range scanning-results\n"));
275 // Read output
276 do {
277 bytesRead = static_cast<size_t>(ssh_channel_read_timeout(node->_channel, buffer.data(), sizeof(buffer), 0, 10)); // timeout in ms
278 if (bytesRead > 0)
279 {
280 receivedData.append(buffer.data(), bytesRead);
281 }
282 } while (bytesRead > 0);
283 // Send command to clear output
284 ssh_channel_write(node->_channel, "clear range-scanning-result\n", strlen("clear range-scanning-result\n"));
285
286 // Parse the received data
287 std::istringstream iss(receivedData);
288 std::string line;
289
290 while (std::getline(iss, line) && line.find("Peer-bssid") == std::string::npos) {} // Skip lines until the header "Peer-bssid" is found
291
292 // Skip the header lines
293 std::getline(iss, line);
294 std::getline(iss, line);
295
296 // MAC address validation
297 std::regex macRegex("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$");
298
299 while (std::getline(iss, line) && !line.empty())
300 {
301 std::istringstream lineStream(line);
302 std::string value;
303 std::string macAddress;
304 lineStream >> macAddress;
305
306 int rtt = 0;
307 int rssi = 0;
308 int stdValue = 0;
309
310 lineStream >> rtt >> rssi >> stdValue;
311
312 std::regex timeRegex(R"(\d{4}-\d{2}-\d{2})"); // Time format: YYYY-MM-DD
313 std::string timeStamp1;
314 std::string timeStamp2;
315 lineStream >> timeStamp1;
316 lineStream >> timeStamp2;
317 while (lineStream >> value)
318 {
319 if (std::regex_match(value, timeRegex)) // Check if the value is a time stamp
320 {
321 timeStamp1 = value;
322 break;
323 }
324 }
325 lineStream >> value;
326 timeStamp2 = value;
327 double measuredDistance = static_cast<double>(rtt) * 1e-9 / 2 * InsConst::C_AIR;
328 double measuredDistanceStd = static_cast<double>(stdValue) * 1e-9 / 2 * InsConst::C_AIR;
329 if (std::regex_match(macAddress, macRegex)) // Check if the MAC address is valid
330 {
331 InsTime_YMDHMS yearMonthDayHMS(std::stoi(timeStamp1.substr(0, 4)), std::stoi(timeStamp1.substr(5, 2)), std::stoi(timeStamp1.substr(8, 2)), std::stoi(timeStamp2.substr(0, 2)), std::stoi(timeStamp2.substr(3, 2)), std::stoi(timeStamp2.substr(6, 2)));
332 InsTime timeOfMeasurement(yearMonthDayHMS, UTC);
333 std::ranges::transform(macAddress, macAddress.begin(), ::toupper); // Convert to uppercase
334 obs->distance = measuredDistance;
335 obs->distanceStd = measuredDistanceStd;
336 obs->macAddress = macAddress;
337 obs->insTime = timeOfMeasurement;
338 // obs->insTime = util::time::GetCurrentInsTime(); // if you want to use the instinct time instead
339 node->invokeCallbacks(OUTPUT_PORT_INDEX_WIFI_OBS, obs);
340 }
341 }
342 }
343