mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 00:44:37 -06:00
* Log task name of socket that failed to open port * Give failed to recv the same treatment * Format my changes
366 lines
13 KiB
C++
366 lines
13 KiB
C++
// ======================================================================
|
|
// \title SocketComponentHelper.cpp
|
|
// \author mstarch, crsmith
|
|
// \brief cpp file for SocketComponentHelper implementation class
|
|
//
|
|
// \copyright
|
|
// Copyright 2009-2020, by the California Institute of Technology.
|
|
// ALL RIGHTS RESERVED. United States Government Sponsorship
|
|
// acknowledged.
|
|
//
|
|
// ======================================================================
|
|
|
|
#include <Drv/Ip/SocketComponentHelper.hpp>
|
|
#include <Fw/Logger/Logger.hpp>
|
|
#include <Fw/Types/Assert.hpp>
|
|
#include <cerrno>
|
|
|
|
namespace Drv {
|
|
|
|
SocketComponentHelper::SocketComponentHelper() {}
|
|
|
|
SocketComponentHelper::~SocketComponentHelper() {}
|
|
|
|
void SocketComponentHelper::start(const Fw::ConstStringBase& name,
|
|
const FwTaskPriorityType priority,
|
|
const Os::Task::ParamType stack,
|
|
const Os::Task::ParamType cpuAffinity,
|
|
const FwTaskPriorityType priorityReconnect,
|
|
const Os::Task::ParamType stackReconnect,
|
|
const Os::Task::ParamType cpuAffinityReconnect) {
|
|
// Reconnect Thread
|
|
FW_ASSERT(m_reconnectTask.getState() ==
|
|
Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
|
this->m_reconnectStop = false;
|
|
Fw::String reconnectName;
|
|
reconnectName.format("%s_reconnect", name.toChar());
|
|
Os::Task::Arguments reconnectArguments(reconnectName, SocketComponentHelper::reconnectTask, this, priorityReconnect,
|
|
stackReconnect, cpuAffinityReconnect);
|
|
Os::Task::Status reconnectStat = m_reconnectTask.start(reconnectArguments);
|
|
FW_ASSERT(Os::Task::OP_OK == reconnectStat, static_cast<FwAssertArgType>(reconnectStat));
|
|
|
|
// Read Thread
|
|
FW_ASSERT(m_task.getState() ==
|
|
Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
|
this->m_stop = false;
|
|
// Note: the first step is for the IP socket to open the port
|
|
Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
|
|
Os::Task::Status stat = m_task.start(arguments);
|
|
FW_ASSERT(Os::Task::OP_OK == stat, static_cast<FwAssertArgType>(stat));
|
|
}
|
|
|
|
SocketIpStatus SocketComponentHelper::open() {
|
|
SocketIpStatus status = SOCK_ANOTHER_THREAD_OPENING;
|
|
OpenState local_open = OpenState::OPEN;
|
|
// Scope to guard lock
|
|
{
|
|
Os::ScopeLock scopeLock(m_lock);
|
|
if (this->m_open == OpenState::NOT_OPEN) {
|
|
this->m_open = OpenState::OPENING;
|
|
local_open = this->m_open;
|
|
} else {
|
|
local_open = OpenState::SKIP;
|
|
}
|
|
}
|
|
if (local_open == OpenState::OPENING) {
|
|
FW_ASSERT(this->m_descriptor.fd == -1); // Ensure we are not opening an opened socket
|
|
status = this->getSocketHandler().open(this->m_descriptor);
|
|
// Lock scope
|
|
{
|
|
Os::ScopeLock scopeLock(m_lock);
|
|
if (Drv::SOCK_SUCCESS == status) {
|
|
this->m_open = OpenState::OPEN;
|
|
} else {
|
|
this->m_open = OpenState::NOT_OPEN;
|
|
this->m_descriptor.fd = -1;
|
|
}
|
|
}
|
|
// Notify connection on success outside locked scope
|
|
if (Drv::SOCK_SUCCESS == status) {
|
|
this->connected();
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool SocketComponentHelper::isOpened() {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
bool is_open = this->m_open == OpenState::OPEN;
|
|
return is_open;
|
|
}
|
|
|
|
void SocketComponentHelper::setAutomaticOpen(bool auto_open) {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
this->m_reopen = auto_open;
|
|
}
|
|
|
|
bool SocketComponentHelper::getAutomaticOpen() {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
return this->m_reopen;
|
|
}
|
|
|
|
SocketIpStatus SocketComponentHelper::reopen() {
|
|
SocketIpStatus status = SOCK_SUCCESS;
|
|
if (not this->isOpened()) {
|
|
// Check for auto-open before attempting to reopen
|
|
bool reopen = this->getAutomaticOpen();
|
|
if (not reopen) {
|
|
status = SOCK_AUTO_CONNECT_DISABLED;
|
|
// Open a network connection if it has not already been open
|
|
} else {
|
|
status = this->open();
|
|
if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
|
|
status = SocketIpStatus::SOCK_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
SocketIpStatus SocketComponentHelper::send(const U8* const data, const FwSizeType size) {
|
|
SocketIpStatus status = SOCK_SUCCESS;
|
|
this->m_lock.lock();
|
|
SocketDescriptor descriptor = this->m_descriptor;
|
|
this->m_lock.unlock();
|
|
// Prevent transmission before connection, or after a disconnect
|
|
if (descriptor.fd == -1) {
|
|
this->requestReconnect();
|
|
SocketIpStatus reconnectStat = this->waitForReconnect();
|
|
if (reconnectStat == SOCK_SUCCESS) {
|
|
// Refresh local copy after reopen
|
|
this->m_lock.lock();
|
|
descriptor = this->m_descriptor;
|
|
this->m_lock.unlock();
|
|
} else {
|
|
return reconnectStat;
|
|
}
|
|
}
|
|
status = this->getSocketHandler().send(descriptor, data, size);
|
|
if (status == SOCK_DISCONNECTED) {
|
|
this->close();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void SocketComponentHelper::shutdown() {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
this->getSocketHandler().shutdown(this->m_descriptor);
|
|
}
|
|
|
|
void SocketComponentHelper::close() {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
this->getSocketHandler().close(this->m_descriptor);
|
|
this->m_descriptor.fd = -1;
|
|
this->m_open = OpenState::NOT_OPEN;
|
|
}
|
|
|
|
/* Read Thread */
|
|
|
|
Os::Task::Status SocketComponentHelper::join() {
|
|
Os::Task::Status stat = m_task.join();
|
|
Os::Task::Status reconnectStat = this->joinReconnect();
|
|
if (stat == Os::Task::Status::OP_OK) {
|
|
return reconnectStat;
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
void SocketComponentHelper::stop() {
|
|
// Scope to protect lock
|
|
{
|
|
Os::ScopeLock scopeLock(m_lock);
|
|
this->m_stop = true;
|
|
}
|
|
this->stopReconnect();
|
|
this->shutdown(); // Break out of any receives and fully shutdown
|
|
}
|
|
|
|
bool SocketComponentHelper::running() {
|
|
Os::ScopeLock scopedLock(this->m_lock);
|
|
bool running = not this->m_stop;
|
|
return running;
|
|
}
|
|
|
|
SocketIpStatus SocketComponentHelper::recv(U8* data, FwSizeType& size) {
|
|
SocketIpStatus status = SOCK_SUCCESS;
|
|
// Check for previously disconnected socket
|
|
this->m_lock.lock();
|
|
SocketDescriptor descriptor = this->m_descriptor;
|
|
this->m_lock.unlock();
|
|
if (descriptor.fd == -1) {
|
|
return SOCK_DISCONNECTED;
|
|
}
|
|
status = this->getSocketHandler().recv(descriptor, data, size);
|
|
if (status == SOCK_DISCONNECTED) {
|
|
this->close();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void SocketComponentHelper::readLoop() {
|
|
SocketIpStatus status = SOCK_SUCCESS;
|
|
do {
|
|
// Prevent transmission before connection, or after a disconnect
|
|
if ((not this->isOpened()) and this->running()) {
|
|
this->requestReconnect();
|
|
status = this->waitForReconnect();
|
|
// When reopen is disabled, just break as this is a exit condition for the loop
|
|
if (status == SOCK_AUTO_CONNECT_DISABLED) {
|
|
break;
|
|
}
|
|
}
|
|
// If the network connection is open, read from it
|
|
if (this->isOpened() and this->running()) {
|
|
Fw::Buffer buffer = this->getBuffer();
|
|
U8* data = buffer.getData();
|
|
FW_ASSERT(data);
|
|
FwSizeType size = buffer.getSize();
|
|
// recv blocks, so it may have been a while since its done an isOpened check
|
|
status = this->recv(data, size);
|
|
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) &&
|
|
(status != SOCK_NO_DATA_AVAILABLE)) {
|
|
Fw::Logger::log("[WARNING] %s failed to recv from port with status %d and errno %d\n",
|
|
this->m_task.getName().toChar(), status, errno);
|
|
this->close();
|
|
buffer.setSize(0);
|
|
} else {
|
|
// Send out received data
|
|
buffer.setSize(size);
|
|
}
|
|
this->sendBuffer(buffer, status);
|
|
}
|
|
}
|
|
// This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
|
|
while (this->running());
|
|
// Close the socket
|
|
this->close(); // Close the port entirely
|
|
}
|
|
|
|
void SocketComponentHelper::readTask(void* pointer) {
|
|
FW_ASSERT(pointer);
|
|
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
|
self->readLoop();
|
|
}
|
|
|
|
/* Reconnect Thread */
|
|
|
|
Os::Task::Status SocketComponentHelper::joinReconnect() {
|
|
return m_reconnectTask.join();
|
|
}
|
|
|
|
void SocketComponentHelper::stopReconnect() {
|
|
Os::ScopeLock scopeLock(this->m_reconnectLock);
|
|
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
|
this->m_reconnectStop = true;
|
|
}
|
|
|
|
bool SocketComponentHelper::runningReconnect() {
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
bool running = not this->m_reconnectStop;
|
|
return running;
|
|
}
|
|
|
|
void SocketComponentHelper::reconnectLoop() {
|
|
SocketIpStatus status = SOCK_SUCCESS;
|
|
while (this->runningReconnect()) {
|
|
// Check if we need to reconnect
|
|
bool reconnect = false;
|
|
{
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
|
|
this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
|
|
reconnect = true;
|
|
|
|
}
|
|
// If we were already in or are now in RECONNECT_IN_PROGRESS we
|
|
// need to try to reconnect, again
|
|
else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
|
|
reconnect = true;
|
|
}
|
|
}
|
|
|
|
if (reconnect) {
|
|
status = this->reopen();
|
|
|
|
// Reopen Case 1: Auto Connect is disabled, so no longer
|
|
// try to reconnect
|
|
if (status == SOCK_AUTO_CONNECT_DISABLED) {
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
|
}
|
|
// Reopen Case 2: Success, so no longer
|
|
// try to reconnect
|
|
else if (status == SOCK_SUCCESS) {
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
|
}
|
|
// Reopen Case 3: Keep trying to reconnect - NO reconnect
|
|
// state change
|
|
else {
|
|
Fw::Logger::log("[WARNING] %s failed to open port with status %d and errno %d\n",
|
|
this->m_task.getName().toChar(), status, errno);
|
|
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
|
}
|
|
} else {
|
|
// After a brief delay, we will loop again
|
|
(void)Os::Task::delay(this->m_reconnectCheckInterval);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SocketComponentHelper::reconnectTask(void* pointer) {
|
|
FW_ASSERT(pointer);
|
|
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
|
self->reconnectLoop();
|
|
}
|
|
|
|
void SocketComponentHelper::requestReconnect() {
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
|
|
m_reconnectState = ReconnectState::REQUEST_RECONNECT;
|
|
}
|
|
return;
|
|
}
|
|
|
|
SocketIpStatus SocketComponentHelper::waitForReconnect(Fw::TimeInterval timeout) {
|
|
// Do not attempt to reconnect if auto reconnect config flag is disabled
|
|
if (!this->getAutomaticOpen()) {
|
|
return SOCK_AUTO_CONNECT_DISABLED;
|
|
}
|
|
|
|
Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
|
|
|
|
while (elapsed < timeout) {
|
|
// If the reconnect thread is NOT reconnecting, we are done waiting
|
|
// If we are no longer running the reconnect thread, we are done waiting
|
|
{
|
|
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
|
if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
|
|
break;
|
|
}
|
|
if (this->m_reconnectStop) {
|
|
break;
|
|
}
|
|
}
|
|
// Wait a bit before checking again
|
|
(void)Os::Task::delay(this->m_reconnectWaitInterval);
|
|
elapsed.add(this->m_reconnectWaitInterval.getSeconds(), this->m_reconnectWaitInterval.getUSeconds());
|
|
}
|
|
|
|
// If we have completed our loop, check if we are connected or if
|
|
// auto connect was disabled during our wait
|
|
if (this->isOpened()) {
|
|
return SOCK_SUCCESS;
|
|
}
|
|
|
|
// Check one more time if auto reconnect config flag got disabled
|
|
if (!this->getAutomaticOpen()) {
|
|
return SOCK_AUTO_CONNECT_DISABLED;
|
|
}
|
|
|
|
return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
|
|
}
|
|
|
|
} // namespace Drv
|