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  (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
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  // 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 
95  Os::ScopeLock scopedLock(this->m_lock);
96  bool is_open = this->m_open == OpenState::OPEN;
97  return is_open;
98 }
99 
101  Os::ScopeLock scopedLock(this->m_lock);
102  this->m_reopen = auto_open;
103 }
104 
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) {
117  // Open a network connection if it has not already been open
118  } else {
119  status = this->open();
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 
154  Os::ScopeLock scopedLock(this->m_lock);
155  this->getSocketHandler().shutdown(this->m_descriptor);
156 }
157 
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 
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 
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 
187  Os::ScopeLock scopedLock(this->m_lock);
188  bool running = not this->m_stop;
189  return running;
190 }
191 
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 
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());
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 
261  return m_reconnectTask.join();
262 }
263 
265  Os::ScopeLock scopeLock(this->m_reconnectLock);
266  this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
267  this->m_reconnectStop = true;
268 }
269 
271  Os::ScopeLock scopedLock(this->m_reconnectLock);
272  bool running = not this->m_reconnectStop;
273  return running;
274 }
275 
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);
321  }
322  } else {
323  // After a brief delay, we will loop again
325  }
326  }
327 }
328 
330  FW_ASSERT(pointer != nullptr);
331  SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
332  self->reconnectLoop();
333 }
334 
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 
344  // Do not attempt to reconnect if auto reconnect config flag is disabled
345  if (!this->getAutomaticOpen()) {
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
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()) {
377  }
378 
379  return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
380 }
381 
382 } // namespace Drv
SocketComponentHelper()
constructs the socket read task
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:27
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:185
bool isOpened()
check if IP socket has previously been opened
void setSize(FwSizeType size)
Definition: Buffer.cpp:131
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
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:82
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:147
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:126
void unlock()
alias for unLock to meet BasicLockable requirements
Definition: Mutex.hpp:64
State getState() const
get the task&#39;s state
Definition: Task.cpp:76
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
bool isValid() const
Definition: Buffer.cpp:78
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:58
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:54
FwSizeType ParamType
backwards-compatible parameter type
Definition: Task.hpp:233
FwSizeType getSize() const
Definition: Buffer.cpp:90
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 shut down the associated socket.
void close(const SocketDescriptor &socketDescriptor)
closes the socket
Definition: IpSocket.cpp:120
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:40
virtual void readLoop()
receive off the TCP socket
static Status delay(const Fw::TimeInterval &interval)
delay the current task
Definition: Task.cpp:204
SocketIpStatus open(SocketDescriptor &socketDescriptor)
open the IP socket for communications
Definition: IpSocket.cpp:135
Os::Task::Status join()
joins the stopping read and reconnect tasks to wait for them 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