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] %s failed to recv from port with status %d and errno %d\n",
224  this->m_task.getName().toChar(), status, errno);
225  this->close();
226  buffer.setSize(0);
227  } else {
228  // Send out received data
229  buffer.setSize(size);
230  }
231  this->sendBuffer(buffer, status);
232  }
233  }
234  // This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
235  while (this->running());
236  // Close the socket
237  this->close(); // Close the port entirely
238 }
239 
240 void SocketComponentHelper::readTask(void* pointer) {
241  FW_ASSERT(pointer);
242  SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
243  self->readLoop();
244 }
245 
246 /* Reconnect Thread */
247 
249  return m_reconnectTask.join();
250 }
251 
253  Os::ScopeLock scopeLock(this->m_reconnectLock);
254  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
255  this->m_reconnectStop = true;
256 }
257 
259  Os::ScopeLock scopedLock(this->m_reconnectLock);
260  bool running = not this->m_reconnectStop;
261  return running;
262 }
263 
265  SocketIpStatus status = SOCK_SUCCESS;
266  while (this->runningReconnect()) {
267  // Check if we need to reconnect
268  bool reconnect = false;
269  {
270  Os::ScopeLock scopedLock(this->m_reconnectLock);
271  if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
272  this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
273  reconnect = true;
274 
275  }
276  // If we were already in or are now in RECONNECT_IN_PROGRESS we
277  // need to try to reconnect, again
278  else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
279  reconnect = true;
280  }
281  }
282 
283  if (reconnect) {
284  status = this->reopen();
285 
286  // Reopen Case 1: Auto Connect is disabled, so no longer
287  // try to reconnect
288  if (status == SOCK_AUTO_CONNECT_DISABLED) {
289  Os::ScopeLock scopedLock(this->m_reconnectLock);
290  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
291  }
292  // Reopen Case 2: Success, so no longer
293  // try to reconnect
294  else if (status == SOCK_SUCCESS) {
295  Os::ScopeLock scopedLock(this->m_reconnectLock);
296  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
297  }
298  // Reopen Case 3: Keep trying to reconnect - NO reconnect
299  // state change
300  else {
301  Fw::Logger::log("[WARNING] %s failed to open port with status %d and errno %d\n",
302  this->m_task.getName().toChar(), status, errno);
304  }
305  } else {
306  // After a brief delay, we will loop again
308  }
309  }
310 }
311 
313  FW_ASSERT(pointer);
314  SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
315  self->reconnectLoop();
316 }
317 
319  Os::ScopeLock scopedLock(this->m_reconnectLock);
320  if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
321  m_reconnectState = ReconnectState::REQUEST_RECONNECT;
322  }
323  return;
324 }
325 
327  // Do not attempt to reconnect if auto reconnect config flag is disabled
328  if (!this->getAutomaticOpen()) {
330  }
331 
332  Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
333 
334  while (elapsed < timeout) {
335  // If the reconnect thread is NOT reconnecting, we are done waiting
336  // If we are no longer running the reconnect thread, we are done waiting
337  {
338  Os::ScopeLock scopedLock(this->m_reconnectLock);
339  if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
340  break;
341  }
342  if (this->m_reconnectStop) {
343  break;
344  }
345  }
346  // Wait a bit before checking again
349  }
350 
351  // If we have completed our loop, check if we are connected or if
352  // auto connect was disabled during our wait
353  if (this->isOpened()) {
354  return SOCK_SUCCESS;
355  }
356 
357  // Check one more time if auto reconnect config flag got disabled
358  if (!this->getAutomaticOpen()) {
360  }
361 
362  return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
363 }
364 
365 } // namespace Drv
SocketComponentHelper()
constructs the socket read task
static Status delay(Fw::TimeInterval interval)
delay the current task
Definition: Task.cpp:204
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
const char * toChar() const
Convert to a C-style char*.
Definition: TaskString.hpp:45
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
virtual const CHAR * toChar() const =0
Convert to a C-style char*.
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.
Socket operation successful.
Definition: IpSocket.hpp:30
bool getAutomaticOpen()
get socket automatically open connections status
message sent/received okay
Definition: Task.hpp:50
Status join() override
block until the task has ended
Definition: Task.cpp:141
void add(U32 seconds, U32 mseconds)
TaskString getName()
get the task name
Definition: Task.cpp:177
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:39
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:232
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
A read-only abstract superclass for StringBase.
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
void start(const Fw::ConstStringBase &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
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
U32 getSeconds() const
void lock()
lock the mutex and assert success
Definition: Mutex.cpp:34