F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
SocketComponentHelper.cpp
Go to the documentation of this file.
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 
14 #include <Fw/Logger/Logger.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <cerrno>
17 
18 namespace Drv {
19 
21 
23 
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
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  reconnectName.format("%s_reconnect", name.toChar());
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
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 
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  FW_ASSERT(this->m_descriptor.fd == -1); // Ensure we are not opening an opened socket
67  status = this->getSocketHandler().open(this->m_descriptor);
68  // Lock scope
69  {
70  Os::ScopeLock scopeLock(m_lock);
71  if (Drv::SOCK_SUCCESS == status) {
72  this->m_open = OpenState::OPEN;
73  } else {
74  this->m_open = OpenState::NOT_OPEN;
75  this->m_descriptor.fd = -1;
76  }
77  }
78  // Notify connection on success outside locked scope
79  if (Drv::SOCK_SUCCESS == status) {
80  this->connected();
81  }
82  }
83 
84  return status;
85 }
86 
88  Os::ScopeLock scopedLock(this->m_lock);
89  bool is_open = this->m_open == OpenState::OPEN;
90  return is_open;
91 }
92 
94  Os::ScopeLock scopedLock(this->m_lock);
95  this->m_reopen = auto_open;
96 }
97 
99  Os::ScopeLock scopedLock(this->m_lock);
100  return this->m_reopen;
101 }
102 
103 SocketIpStatus SocketComponentHelper::reopen() {
104  SocketIpStatus status = SOCK_SUCCESS;
105  if (not this->isOpened()) {
106  // Check for auto-open before attempting to reopen
107  bool reopen = this->getAutomaticOpen();
108  if (not reopen) {
110  // Open a network connection if it has not already been open
111  } else {
112  status = this->open();
115  }
116  }
117  }
118  return status;
119 }
120 
121 SocketIpStatus SocketComponentHelper::send(const U8* const data, const FwSizeType size) {
122  SocketIpStatus status = SOCK_SUCCESS;
123  this->m_lock.lock();
124  SocketDescriptor descriptor = this->m_descriptor;
125  this->m_lock.unlock();
126  // Prevent transmission before connection, or after a disconnect
127  if (descriptor.fd == -1) {
128  this->requestReconnect();
129  SocketIpStatus reconnectStat = this->waitForReconnect();
130  if (reconnectStat == SOCK_SUCCESS) {
131  // Refresh local copy after reopen
132  this->m_lock.lock();
133  descriptor = this->m_descriptor;
134  this->m_lock.unlock();
135  } else {
136  return reconnectStat;
137  }
138  }
139  status = this->getSocketHandler().send(descriptor, data, size);
140  if (status == SOCK_DISCONNECTED) {
141  this->close();
142  }
143  return status;
144 }
145 
147  Os::ScopeLock scopedLock(this->m_lock);
148  this->getSocketHandler().shutdown(this->m_descriptor);
149 }
150 
152  Os::ScopeLock scopedLock(this->m_lock);
153  this->getSocketHandler().close(this->m_descriptor);
154  this->m_descriptor.fd = -1;
155  this->m_open = OpenState::NOT_OPEN;
156 }
157 
158 /* Read Thread */
159 
161  Os::Task::Status stat = m_task.join();
162  Os::Task::Status reconnectStat = this->joinReconnect();
163  if (stat == Os::Task::Status::OP_OK) {
164  return reconnectStat;
165  }
166  return stat;
167 }
168 
170  // Scope to protect lock
171  {
172  Os::ScopeLock scopeLock(m_lock);
173  this->m_stop = true;
174  }
175  this->stopReconnect();
176  this->shutdown(); // Break out of any receives and fully shutdown
177 }
178 
180  Os::ScopeLock scopedLock(this->m_lock);
181  bool running = not this->m_stop;
182  return running;
183 }
184 
186  SocketIpStatus status = SOCK_SUCCESS;
187  // Check for previously disconnected socket
188  this->m_lock.lock();
189  SocketDescriptor descriptor = this->m_descriptor;
190  this->m_lock.unlock();
191  if (descriptor.fd == -1) {
192  return SOCK_DISCONNECTED;
193  }
194  status = this->getSocketHandler().recv(descriptor, data, size);
195  if (status == SOCK_DISCONNECTED) {
196  this->close();
197  }
198  return status;
199 }
200 
202  SocketIpStatus status = SOCK_SUCCESS;
203  do {
204  // Prevent transmission before connection, or after a disconnect
205  if ((not this->isOpened()) and this->running()) {
206  this->requestReconnect();
207  status = this->waitForReconnect();
208  // When reopen is disabled, just break as this is a exit condition for the loop
209  if (status == SOCK_AUTO_CONNECT_DISABLED) {
210  break;
211  }
212  }
213  // If the network connection is open, read from it
214  if (this->isOpened() and this->running()) {
215  Fw::Buffer buffer = this->getBuffer();
216  U8* data = buffer.getData();
217  FW_ASSERT(data);
218  FwSizeType size = buffer.getSize();
219  // recv blocks, so it may have been a while since its done an isOpened check
220  status = this->recv(data, size);
221  if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) &&
222  (status != SOCK_NO_DATA_AVAILABLE)) {
223  Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n", status, errno);
224  this->close();
225  buffer.setSize(0);
226  } else {
227  // Send out received data
228  buffer.setSize(size);
229  }
230  this->sendBuffer(buffer, status);
231  }
232  }
233  // This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
234  while (this->running());
235  // Close the socket
236  this->close(); // Close the port entirely
237 }
238 
239 void SocketComponentHelper::readTask(void* pointer) {
240  FW_ASSERT(pointer);
241  SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
242  self->readLoop();
243 }
244 
245 /* Reconnect Thread */
246 
248  return m_reconnectTask.join();
249 }
250 
252  Os::ScopeLock scopeLock(this->m_reconnectLock);
253  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
254  this->m_reconnectStop = true;
255 }
256 
258  Os::ScopeLock scopedLock(this->m_reconnectLock);
259  bool running = not this->m_reconnectStop;
260  return running;
261 }
262 
264  SocketIpStatus status = SOCK_SUCCESS;
265  while (this->runningReconnect()) {
266  // Check if we need to reconnect
267  bool reconnect = false;
268  {
269  Os::ScopeLock scopedLock(this->m_reconnectLock);
270  if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
271  this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
272  reconnect = true;
273 
274  }
275  // If we were already in or are now in RECONNECT_IN_PROGRESS we
276  // need to try to reconnect, again
277  else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
278  reconnect = true;
279  }
280  }
281 
282  if (reconnect) {
283  status = this->reopen();
284 
285  // Reopen Case 1: Auto Connect is disabled, so no longer
286  // try to reconnect
287  if (status == SOCK_AUTO_CONNECT_DISABLED) {
288  Os::ScopeLock scopedLock(this->m_reconnectLock);
289  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
290  }
291  // Reopen Case 2: Success, so no longer
292  // try to reconnect
293  else if (status == SOCK_SUCCESS) {
294  Os::ScopeLock scopedLock(this->m_reconnectLock);
295  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
296  }
297  // Reopen Case 3: Keep trying to reconnect - NO reconnect
298  // state change
299  else {
300  Fw::Logger::log("[WARNING] Failed to open port with status %d and errno %d\n", status, errno);
302  }
303  } else {
304  // After a brief delay, we will loop again
306  }
307  }
308 }
309 
311  FW_ASSERT(pointer);
312  SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
313  self->reconnectLoop();
314 }
315 
317  Os::ScopeLock scopedLock(this->m_reconnectLock);
318  if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
319  m_reconnectState = ReconnectState::REQUEST_RECONNECT;
320  }
321  return;
322 }
323 
325  // Do not attempt to reconnect if auto reconnect config flag is disabled
326  if (!this->getAutomaticOpen()) {
328  }
329 
330  Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
331 
332  while (elapsed < timeout) {
333  // If the reconnect thread is NOT reconnecting, we are done waiting
334  // If we are no longer running the reconnect thread, we are done waiting
335  {
336  Os::ScopeLock scopedLock(this->m_reconnectLock);
337  if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
338  break;
339  }
340  if (this->m_reconnectStop) {
341  break;
342  }
343  }
344  // Wait a bit before checking again
347  }
348 
349  // If we have completed our loop, check if we are connected or if
350  // auto connect was disabled during our wait
351  if (this->isOpened()) {
352  return SOCK_SUCCESS;
353  }
354 
355  // Check one more time if auto reconnect config flag got disabled
356  if (!this->getAutomaticOpen()) {
358  }
359 
360  return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
361 }
362 
363 } // namespace Drv
SocketComponentHelper()
constructs the socket read task
static Status delay(Fw::TimeInterval interval)
delay the current task
Definition: Task.cpp:199
bool m_reopen
Force reopen on disconnect.
bool m_stop
Stops the task when set to true.
Failed to read socket with disconnect.
Definition: IpSocket.hpp:38
void shutdown()
shutdown the socket communications
Interrupted status for retries.
Definition: IpSocket.hpp:36
Operation succeeded.
Definition: Os.hpp:26
PlatformSizeType FwSizeType
SocketIpStatus recv(const SocketDescriptor &fd, U8 *const data, FwSizeType &size)
receive data from the IP socket from the given buffer
Definition: IpSocket.cpp:167
bool isOpened()
check if IP socket has previously been opened
void setSize(FwSizeType size)
Definition: Buffer.cpp:75
SocketIpStatus send(const U8 *const data, const FwSizeType size)
send data to the IP socket from the given buffer
State getState()
get the task&#39;s state
Definition: Task.cpp:76
static void log(const char *format,...)
log a formated string with supplied arguments
Definition: Logger.cpp:21
Automatic connections are disabled.
Definition: IpSocket.hpp:47
U8 * getData() const
Definition: Buffer.cpp:56
void setAutomaticOpen(bool auto_open)
set socket to automatically open connections when true, or not when false
SocketIpStatus waitForReconnect(Fw::TimeInterval timeout=Fw::TimeInterval(1, 0))
wait method for a task to wait for a reconnect request to complete
int fd
Used for all sockets to track the communication file descriptor.
Definition: IpSocket.hpp:22
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:84
SocketIpStatus recv(U8 *data, FwSizeType &size)
receive data from the IP socket from the given buffer
virtual SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size)
send data out the IP socket from the given buffer
Definition: IpSocket.cpp:132
static void reconnectTask(void *pointer)
a task designed for socket reconnection
virtual Fw::Buffer getBuffer()=0
returns a buffer to fill with data
Another thread is opening.
Definition: IpSocket.hpp:46
void requestReconnect()
signal to reconnect task that a reconnect is needed
void shutdown(const SocketDescriptor &socketDescriptor)
shutdown the socket
Definition: IpSocket.cpp:111
void unlock()
alias for unLock to meet BasicLockable requirements
Definition: Mutex.hpp:64
supports a task to read a given socket adaptation
OpenState m_open
Have we successfully opened.
void start(const Fw::StringBase &name, const FwTaskPriorityType priority=Os::Task::TASK_PRIORITY_DEFAULT, const Os::Task::ParamType stack=Os::Task::TASK_DEFAULT, const Os::Task::ParamType cpuAffinity=Os::Task::TASK_DEFAULT, const FwTaskPriorityType priorityReconnect=Os::Task::TASK_PRIORITY_DEFAULT, const Os::Task::ParamType stackReconnect=Os::Task::TASK_DEFAULT, const Os::Task::ParamType cpuAffinityReconnect=Os::Task::TASK_DEFAULT)
start the socket read task to start producing data
Socket operation successful.
Definition: IpSocket.hpp:30
bool getAutomaticOpen()
get socket automatically open connections status
message sent/received okay
Definition: Task.hpp:40
Status join() override
block until the task has ended
Definition: Task.cpp:141
void add(U32 seconds, U32 mseconds)
void close()
close the socket communications
virtual void connected()=0
called when the IPv4 system has been connected
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:55
PlatformTaskPriorityType FwTaskPriorityType
The type of task priorities used.
virtual IpSocket & getSocketHandler()=0
returns a reference to the socket handler
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
FwSizeType ParamType
backwards-compatible parameter type
Definition: Task.hpp:222
FwSizeType getSize() const
Definition: Buffer.cpp:60
U32 getUSeconds() const
virtual ~SocketComponentHelper()
destructor of the socket read task
static void readTask(void *pointer)
a task designed to read from the socket and output incoming data
void stop()
stop the socket read task and close the associated socket.
void close(const SocketDescriptor &socketDescriptor)
closes the socket
Definition: IpSocket.cpp:107
locks a mutex within the current scope
Definition: Mutex.hpp:80
virtual void reconnectLoop()
reconnect TCP socket
static const Fw::TimeInterval SOCKET_RETRY_INTERVAL
Definition: IpCfg.hpp:24
virtual void readLoop()
receive off the TCP socket
SocketIpStatus open(SocketDescriptor &socketDescriptor)
open the IP socket for communications
Definition: IpSocket.cpp:120
Os::Task::Status join()
joins to the stopping read task to wait for it to close
SocketIpStatus
Status enumeration for socket return values.
Definition: IpSocket.hpp:29
SocketIpStatus open()
open the socket for communications
No data available or read operation would block.
Definition: IpSocket.hpp:45
virtual void sendBuffer(Fw::Buffer buffer, SocketIpStatus status)=0
sends a buffer to be filled with data
#define FW_ASSERT(...)
Definition: Assert.hpp:14
bool running()
is the read loop running
virtual const CHAR * toChar() const =0
U32 getSeconds() const
void lock()
lock the mutex and assert success
Definition: Mutex.cpp:34