GCC Code Coverage Report


Directory: Drv/Ip/
File: SocketComponentHelper.cpp
Date: 2026-09-23 21:11:29
Exec Total Coverage
Lines: 0 223 0.0%
Functions: 0 24 0.0%
Branches: 0 177 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title SocketComponentHelper.cpp
3 // \author mstarch, crsmith
4 // \brief cpp file for SocketComponentHelper implementation class
5 //
6 // \copyright
7 // Copyright 2009-2020, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12
13 #include <Drv/Ip/SocketComponentHelper.hpp>
14 #include <Fw/Logger/Logger.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <cerrno>
17
18 namespace Drv {
19
20 ✗ SocketComponentHelper::SocketComponentHelper() {}
21
22 ✗ SocketComponentHelper::~SocketComponentHelper() {}
23
24 ✗ void SocketComponentHelper::start(const Fw::ConstStringBase& name,
25 const FwTaskPriorityType priority,
26 const Os::Task::ParamType stack,
27 const Os::Task::ParamType cpuAffinity,
28 const FwTaskPriorityType priorityReconnect,
29 const Os::Task::ParamType stackReconnect,
30 const Os::Task::ParamType cpuAffinityReconnect) {
31 // Reconnect Thread
32 ✗ FW_ASSERT(m_reconnectTask.getState() ==
33 Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
34 ✗ this->m_reconnectStop = false;
35 ✗ Fw::String reconnectName;
36 ✗ (void)reconnectName.format("%s_reconnect", name.toChar()); // task name may safely truncate
37 ✗ Os::Task::Arguments reconnectArguments(reconnectName, SocketComponentHelper::reconnectTask, this, priorityReconnect,
38 ✗ stackReconnect, cpuAffinityReconnect);
39 ✗ Os::Task::Status reconnectStat = m_reconnectTask.start(reconnectArguments);
40 ✗ FW_ASSERT(Os::Task::OP_OK == reconnectStat, static_cast<FwAssertArgType>(reconnectStat));
41
42 // Read Thread
43 ✗ FW_ASSERT(m_task.getState() ==
44 Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
45 ✗ this->m_stop = false;
46 // Note: the first step is for the IP socket to open the port
47 ✗ Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
48 ✗ Os::Task::Status stat = m_task.start(arguments);
49 ✗ FW_ASSERT(Os::Task::OP_OK == stat, static_cast<FwAssertArgType>(stat));
50 ✗ }
51
52 ✗ SocketIpStatus SocketComponentHelper::open() {
53 ✗ SocketIpStatus status = SOCK_ANOTHER_THREAD_OPENING;
54 ✗ OpenState local_open = OpenState::OPEN;
55 // Scope to guard lock
56 {
57 ✗ Os::ScopeLock scopeLock(m_lock);
58 ✗ if (this->m_open == OpenState::NOT_OPEN) {
59 ✗ this->m_open = OpenState::OPENING;
60 ✗ local_open = this->m_open;
61 } else {
62 ✗ local_open = OpenState::SKIP;
63 }
64 ✗ }
65 ✗ if (local_open == OpenState::OPENING) {
66 // Open into a local descriptor and publish it under the lock
67 ✗ SocketDescriptor descriptor;
68 {
69 ✗ Os::ScopeLock scopeLock(m_lock);
70 ✗ descriptor = this->m_descriptor;
71 ✗ }
72 ✗ FW_ASSERT(descriptor.fd == -1); // Ensure we are not opening an opened socket
73 ✗ status = this->getSocketHandler().open(descriptor);
74 // Lock scope
75 {
76 ✗ Os::ScopeLock scopeLock(m_lock);
77 ✗ if (Drv::SOCK_SUCCESS == status) {
78 ✗ this->m_descriptor = descriptor;
79 ✗ this->m_open = OpenState::OPEN;
80 } else {
81 ✗ this->m_open = OpenState::NOT_OPEN;
82 ✗ this->m_descriptor.fd = -1;
83 }
84 ✗ }
85 // Notify connection on success outside locked scope
86 ✗ if (Drv::SOCK_SUCCESS == status) {
87 ✗ this->connected();
88 }
89 }
90
91 ✗ return status;
92 }
93
94 ✗ bool SocketComponentHelper::isOpened() {
95 ✗ Os::ScopeLock scopedLock(this->m_lock);
96 ✗ bool is_open = this->m_open == OpenState::OPEN;
97 ✗ return is_open;
98 ✗ }
99
100 ✗ void SocketComponentHelper::setAutomaticOpen(bool auto_open) {
101 ✗ Os::ScopeLock scopedLock(this->m_lock);
102 ✗ this->m_reopen = auto_open;
103 ✗ }
104
105 ✗ bool SocketComponentHelper::getAutomaticOpen() {
106 ✗ Os::ScopeLock scopedLock(this->m_lock);
107 ✗ return this->m_reopen;
108 ✗ }
109
110 ✗ SocketIpStatus SocketComponentHelper::reopen() {
111 ✗ SocketIpStatus status = SOCK_SUCCESS;
112 ✗ if (not this->isOpened()) {
113 // Check for auto-open before attempting to reopen
114 ✗ bool reopen = this->getAutomaticOpen();
115 ✗ if (not reopen) {
116 ✗ status = SOCK_AUTO_CONNECT_DISABLED;
117 // Open a network connection if it has not already been open
118 } else {
119 ✗ status = this->open();
120 ✗ if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
121 ✗ status = SocketIpStatus::SOCK_SUCCESS;
122 }
123 }
124 }
125 ✗ return status;
126 }
127
128 ✗ SocketIpStatus SocketComponentHelper::send(const U8* const data, const FwSizeType size) {
129 ✗ SocketIpStatus status = SOCK_SUCCESS;
130 ✗ this->m_lock.lock();
131 ✗ SocketDescriptor descriptor = this->m_descriptor;
132 ✗ this->m_lock.unlock();
133 // Prevent transmission before connection, or after a disconnect
134 ✗ if (descriptor.fd == -1) {
135 ✗ this->requestReconnect();
136 ✗ SocketIpStatus reconnectStat = this->waitForReconnect();
137 ✗ if (reconnectStat == SOCK_SUCCESS) {
138 // Refresh local copy after reopen
139 ✗ this->m_lock.lock();
140 ✗ descriptor = this->m_descriptor;
141 ✗ this->m_lock.unlock();
142 } else {
143 ✗ return reconnectStat;
144 }
145 }
146 ✗ status = this->getSocketHandler().send(descriptor, data, size);
147 ✗ if (status == SOCK_DISCONNECTED) {
148 ✗ this->close();
149 }
150 ✗ return status;
151 }
152
153 ✗ void SocketComponentHelper::shutdown() {
154 ✗ Os::ScopeLock scopedLock(this->m_lock);
155 ✗ this->getSocketHandler().shutdown(this->m_descriptor);
156 ✗ }
157
158 ✗ void SocketComponentHelper::close() {
159 ✗ Os::ScopeLock scopedLock(this->m_lock);
160 ✗ this->getSocketHandler().close(this->m_descriptor);
161 ✗ this->m_descriptor.fd = -1;
162 ✗ this->m_open = OpenState::NOT_OPEN;
163 ✗ }
164
165 /* Read Thread */
166
167 ✗ Os::Task::Status SocketComponentHelper::join() {
168 ✗ Os::Task::Status stat = m_task.join();
169 ✗ Os::Task::Status reconnectStat = this->joinReconnect();
170 ✗ if (stat == Os::Task::Status::OP_OK) {
171 ✗ return reconnectStat;
172 }
173 ✗ return stat;
174 }
175
176 ✗ void SocketComponentHelper::stop() {
177 // Scope to protect lock
178 {
179 ✗ Os::ScopeLock scopeLock(m_lock);
180 ✗ this->m_stop = true;
181 ✗ }
182 ✗ this->stopReconnect();
183 ✗ this->shutdown(); // Break out of any receives and fully shutdown
184 ✗ }
185
186 ✗ bool SocketComponentHelper::running() {
187 ✗ Os::ScopeLock scopedLock(this->m_lock);
188 ✗ bool running = not this->m_stop;
189 ✗ return running;
190 ✗ }
191
192 ✗ SocketIpStatus SocketComponentHelper::recv(U8* data, FwSizeType& size) {
193 ✗ SocketIpStatus status = SOCK_SUCCESS;
194 // Check for previously disconnected socket
195 ✗ this->m_lock.lock();
196 ✗ SocketDescriptor descriptor = this->m_descriptor;
197 ✗ this->m_lock.unlock();
198 ✗ if (descriptor.fd == -1) {
199 ✗ return SOCK_DISCONNECTED;
200 }
201 ✗ status = this->getSocketHandler().recv(descriptor, data, size);
202 ✗ if (status == SOCK_DISCONNECTED) {
203 ✗ this->close();
204 }
205 ✗ return status;
206 }
207
208 ✗ void SocketComponentHelper::readLoop() {
209 ✗ SocketIpStatus status = SOCK_SUCCESS;
210 do {
211 // Prevent transmission before connection, or after a disconnect
212 ✗ if ((not this->isOpened()) and this->running()) {
213 ✗ this->requestReconnect();
214 ✗ status = this->waitForReconnect();
215 // When reopen is disabled, just break as this is a exit condition for the loop
216 ✗ if (status == SOCK_AUTO_CONNECT_DISABLED) {
217 ✗ break;
218 }
219 }
220 // If the network connection is open, read from it
221 ✗ if (this->isOpened() and this->running()) {
222 ✗ Fw::Buffer buffer = this->getBuffer();
223 ✗ if (buffer.isValid()) {
224 ✗ U8* data = buffer.getData();
225 ✗ FW_ASSERT(data != nullptr);
226 ✗ FwSizeType size = buffer.getSize();
227 // recv blocks, so it may have been a while since its done an isOpened check
228 ✗ status = this->recv(data, size);
229 ✗ if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) &&
230 (status != SOCK_NO_DATA_AVAILABLE)) {
231 ✗ Fw::Logger::log("[WARNING] %s failed to recv from port with status %d and errno %d\n",
232 ✗ this->m_task.getName().toChar(), status, errno);
233 ✗ this->close();
234 ✗ buffer.setSize(0);
235 } else {
236 // Send out received data
237 ✗ buffer.setSize(size);
238 }
239 ✗ this->sendBuffer(buffer, status);
240 } else {
241 ✗ Fw::Logger::log("[WARNING] %s failed to get buffer for recv\n", this->m_task.getName().toChar());
242 ✗ (void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
243 }
244 ✗ }
245 }
246 // This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
247 ✗ while (this->running());
248 // Close the socket
249 ✗ this->close(); // Close the port entirely
250 ✗ }
251
252 ✗ void SocketComponentHelper::readTask(void* pointer) {
253 ✗ FW_ASSERT(pointer != nullptr);
254 ✗ SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
255 ✗ self->readLoop();
256 ✗ }
257
258 /* Reconnect Thread */
259
260 ✗ Os::Task::Status SocketComponentHelper::joinReconnect() {
261 ✗ return m_reconnectTask.join();
262 }
263
264 ✗ void SocketComponentHelper::stopReconnect() {
265 ✗ Os::ScopeLock scopeLock(this->m_reconnectLock);
266 ✗ this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
267 ✗ this->m_reconnectStop = true;
268 ✗ }
269
270 ✗ bool SocketComponentHelper::runningReconnect() {
271 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
272 ✗ bool running = not this->m_reconnectStop;
273 ✗ return running;
274 ✗ }
275
276 ✗ void SocketComponentHelper::reconnectLoop() {
277 ✗ SocketIpStatus status = SOCK_SUCCESS;
278 // @non-terminating@: runs until the reconnect thread is stopped
279 ✗ while (this->runningReconnect()) {
280 // Check if we need to reconnect
281 ✗ bool reconnect = false;
282 {
283 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
284 ✗ FW_ASSERT(this->m_reconnectState == ReconnectState::NOT_RECONNECTING ||
285 this->m_reconnectState == ReconnectState::REQUEST_RECONNECT ||
286 this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS,
287 static_cast<FwAssertArgType>(this->m_reconnectState));
288 ✗ if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
289 ✗ this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
290 ✗ reconnect = true;
291
292 }
293 // If we were already in or are now in RECONNECT_IN_PROGRESS we
294 // need to try to reconnect, again
295 ✗ else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
296 ✗ reconnect = true;
297 }
298 ✗ }
299
300 ✗ if (reconnect) {
301 ✗ status = this->reopen();
302
303 // Reopen Case 1: Auto Connect is disabled, so no longer
304 // try to reconnect
305 ✗ if (status == SOCK_AUTO_CONNECT_DISABLED) {
306 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
307 ✗ this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
308 ✗ }
309 // Reopen Case 2: Success, so no longer
310 // try to reconnect
311 ✗ else if (status == SOCK_SUCCESS) {
312 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
313 ✗ this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
314 ✗ }
315 // Reopen Case 3: Keep trying to reconnect - NO reconnect
316 // state change
317 else {
318 ✗ Fw::Logger::log("[WARNING] %s failed to open port with status %d and errno %d\n",
319 ✗ this->m_task.getName().toChar(), status, errno);
320 ✗ (void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
321 }
322 } else {
323 // After a brief delay, we will loop again
324 ✗ (void)Os::Task::delay(this->m_reconnectCheckInterval);
325 }
326 }
327 ✗ }
328
329 ✗ void SocketComponentHelper::reconnectTask(void* pointer) {
330 ✗ FW_ASSERT(pointer != nullptr);
331 ✗ SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
332 ✗ self->reconnectLoop();
333 ✗ }
334
335 ✗ void SocketComponentHelper::requestReconnect() {
336 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
337 ✗ if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
338 ✗ m_reconnectState = ReconnectState::REQUEST_RECONNECT;
339 }
340 ✗ return;
341 ✗ }
342
343 ✗ SocketIpStatus SocketComponentHelper::waitForReconnect(Fw::TimeInterval timeout) {
344 // Do not attempt to reconnect if auto reconnect config flag is disabled
345 ✗ if (!this->getAutomaticOpen()) {
346 ✗ return SOCK_AUTO_CONNECT_DISABLED;
347 }
348
349 ✗ Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
350
351 ✗ while (elapsed < timeout) {
352 // If the reconnect thread is NOT reconnecting, we are done waiting
353 // If we are no longer running the reconnect thread, we are done waiting
354 {
355 ✗ Os::ScopeLock scopedLock(this->m_reconnectLock);
356 ✗ if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
357 ✗ break;
358 }
359 ✗ if (this->m_reconnectStop) {
360 ✗ break;
361 }
362 ✗ }
363 // Wait a bit before checking again
364 ✗ (void)Os::Task::delay(this->m_reconnectWaitInterval);
365 ✗ elapsed.add(this->m_reconnectWaitInterval.getSeconds(), this->m_reconnectWaitInterval.getUSeconds());
366 }
367
368 // If we have completed our loop, check if we are connected or if
369 // auto connect was disabled during our wait
370 ✗ if (this->isOpened()) {
371 ✗ return SOCK_SUCCESS;
372 }
373
374 // Check one more time if auto reconnect config flag got disabled
375 ✗ if (!this->getAutomaticOpen()) {
376 ✗ return SOCK_AUTO_CONNECT_DISABLED;
377 }
378
379 ✗ return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
380 ✗ }
381
382 } // namespace Drv
383