diff --git a/FSFWVersion.h b/FSFWVersion.h new file mode 100644 index 00000000..dcb592dc --- /dev/null +++ b/FSFWVersion.h @@ -0,0 +1,11 @@ +#ifndef FSFW_DEFAULTCFG_VERSION_H_ +#define FSFW_DEFAULTCFG_VERSION_H_ + +const char* const FSFW_VERSION_NAME = "fsfw"; + +#define FSFW_VERSION 0 +#define FSFW_SUBVERSION 0 + + + +#endif /* FSFW_DEFAULTCFG_VERSION_H_ */ diff --git a/README.md b/README.md new file mode 100644 index 00000000..fc86fca7 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +Flight Software Framework (FSFW) +====== + +I want to be written! diff --git a/defaultcfg/README.md b/defaultcfg/README.md new file mode 100644 index 00000000..8446cda4 --- /dev/null +++ b/defaultcfg/README.md @@ -0,0 +1,6 @@ +# How to setup configuration folder for FSFW + +It is recommended to copy the content of the defaultcfg folder +into a config folder which is in the same directory as the Flight +Software Framework submodule. After that, the config.mk folder should be +included by the primary Makefile with CURRENTPATH set correctly. diff --git a/defaultcfg/config/FSFWConfig.h b/defaultcfg/config/FSFWConfig.h new file mode 100644 index 00000000..ea86152c --- /dev/null +++ b/defaultcfg/config/FSFWConfig.h @@ -0,0 +1,54 @@ +#ifndef CONFIG_FSFWCONFIG_H_ +#define CONFIG_FSFWCONFIG_H_ + +#include +#include + +//! Used to determine whether C++ ostreams are used +//! Those can lead to code bloat. +#define FSFW_CPP_OSTREAM_ENABLED 1 + +//! Reduced printout to further decrese code size +//! Be careful, this also turns off most diagnostic prinouts! +#define FSFW_REDUCED_PRINTOUT 0 + +//! Can be used to enable debugging printouts for developing the FSFW +#define FSFW_DEBUGGING 0 + +//! Defines the FIFO depth of each commanding service base which +//! also determines how many commands a CSB service can handle in one cycle +//! simulataneously. This will increase the required RAM for +//! each CSB service ! +#define FSFW_CSB_FIFO_DEPTH 6 + +//! If FSFW_OBJ_EVENT_TRANSLATION is set to one, +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiled source files) +#define FSFW_OBJ_EVENT_TRANSLATION 0 + +#if FSFW_OBJ_EVENT_TRANSLATION == 1 +#define FSFW_DEBUG_OUTPUT 1 +//! Specify whether info events are printed too. +#define FSFW_DEBUG_INFO 1 +#include +#include +#else +#define FSFW_DEBUG_OUTPUT 0 +#endif + +//! When using the newlib nano library, C99 support for stdio facilities +//! will not be provided. This define should be set to 1 if this is the case. +#define FSFW_NO_C99_IO 1 + +namespace fsfwconfig { +//! Default timestamp size. The default timestamp will be an eight byte CDC +//! short timestamp. +static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; + +//! Configure the allocated pool sizes for the event manager. +static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; +static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; +static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; +} + +#endif /* CONFIG_FSFWCONFIG_H_ */ diff --git a/defaultcfg/config/OBSWConfig.h b/defaultcfg/config/OBSWConfig.h new file mode 100644 index 00000000..a9f57638 --- /dev/null +++ b/defaultcfg/config/OBSWConfig.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_OBSWCONFIG_H_ +#define CONFIG_OBSWCONFIG_H_ + +#include "OBSWVersion.h" + +#ifdef __cplusplus +namespace config { +#endif + +/* Add mission configuration flags here */ + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_OBSWCONFIG_H_ */ diff --git a/defaultcfg/config/OBSWVersion.h b/defaultcfg/config/OBSWVersion.h new file mode 100644 index 00000000..3c60317c --- /dev/null +++ b/defaultcfg/config/OBSWVersion.h @@ -0,0 +1,9 @@ +#ifndef CONFIG_VERSION_H_ +#define CONFIG_VERSION_H_ + +/* OBSW versioning can be specified in this file */ + +#define OBSW_VERSION 0 +#define OBSW_SUBVERSION 0 + +#endif /* CONFIG_VERSION_H_ */ diff --git a/defaultcfg/config/config.mk b/defaultcfg/config/config.mk new file mode 100644 index 00000000..51543eba --- /dev/null +++ b/defaultcfg/config/config.mk @@ -0,0 +1,15 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/tmtc/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/devices/*.cpp) + +INCLUDES += $(CURRENTPATH) +INCLUDES += $(CURRENTPATH)/objects +INCLUDES += $(CURRENTPATH)/returnvalues +INCLUDES += $(CURRENTPATH)/tmtc +INCLUDES += $(CURRENTPATH)/events +INCLUDES += $(CURRENTPATH)/devices +INCLUDES += $(CURRENTPATH)/pollingsequence +INCLUDES += $(CURRENTPATH)/ipc diff --git a/defaultcfg/config/devices/logicalAddresses.cpp b/defaultcfg/config/devices/logicalAddresses.cpp new file mode 100644 index 00000000..c7ce314d --- /dev/null +++ b/defaultcfg/config/devices/logicalAddresses.cpp @@ -0,0 +1,5 @@ +#include "logicalAddresses.h" + + + + diff --git a/defaultcfg/config/devices/logicalAddresses.h b/defaultcfg/config/devices/logicalAddresses.h new file mode 100644 index 00000000..174fa788 --- /dev/null +++ b/defaultcfg/config/devices/logicalAddresses.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_ +#define CONFIG_DEVICES_LOGICALADDRESSES_H_ + +#include +#include +#include + +/** + * Can be used for addresses for physical devices like I2C adresses. + */ +namespace addresses { + /* Logical addresses have uint32_t datatype */ + enum logicalAddresses: address_t { + }; +} + + +#endif /* CONFIG_DEVICES_LOGICALADDRESSES_H_ */ diff --git a/defaultcfg/config/devices/powerSwitcherList.cpp b/defaultcfg/config/devices/powerSwitcherList.cpp new file mode 100644 index 00000000..343f78d0 --- /dev/null +++ b/defaultcfg/config/devices/powerSwitcherList.cpp @@ -0,0 +1,4 @@ +#include "powerSwitcherList.h" + + + diff --git a/defaultcfg/config/devices/powerSwitcherList.h b/defaultcfg/config/devices/powerSwitcherList.h new file mode 100644 index 00000000..86ddea57 --- /dev/null +++ b/defaultcfg/config/devices/powerSwitcherList.h @@ -0,0 +1,12 @@ +#ifndef CONFIG_DEVICES_POWERSWITCHERLIST_H_ +#define CONFIG_DEVICES_POWERSWITCHERLIST_H_ + +namespace switches { + /* Switches are uint8_t datatype and go from 0 to 255 */ + enum switcherList { + }; + +} + + +#endif /* CONFIG_DEVICES_POWERSWITCHERLIST_H_ */ diff --git a/defaultcfg/config/events/subsystemIdRanges.h b/defaultcfg/config/events/subsystemIdRanges.h new file mode 100644 index 00000000..24eee819 --- /dev/null +++ b/defaultcfg/config/events/subsystemIdRanges.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ +#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ + +#include +#include + +/** + * @brief Custom subsystem IDs can be added here + * @details + * Subsystem IDs are used to create unique events. + */ +namespace SUBSYSTEM_ID { +enum: uint8_t { + SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, +}; +} + +#endif /* CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ */ diff --git a/defaultcfg/config/ipc/missionMessageTypes.cpp b/defaultcfg/config/ipc/missionMessageTypes.cpp new file mode 100644 index 00000000..e2edbf9c --- /dev/null +++ b/defaultcfg/config/ipc/missionMessageTypes.cpp @@ -0,0 +1,11 @@ +#include +#include + +void messagetypes::clearMissionMessage(CommandMessage* message) { + switch(message->getMessageType()) { + default: + break; + } +} + + diff --git a/defaultcfg/config/ipc/missionMessageTypes.h b/defaultcfg/config/ipc/missionMessageTypes.h new file mode 100644 index 00000000..8b2e2fcc --- /dev/null +++ b/defaultcfg/config/ipc/missionMessageTypes.h @@ -0,0 +1,21 @@ +#ifndef CONFIG_IPC_MISSIONMESSAGETYPES_H_ +#define CONFIG_IPC_MISSIONMESSAGETYPES_H_ + +#include +#include + +/** + * Custom command messages are specified here. + * Most messages needed to use FSFW are already located in + * + * @param message Generic Command Message + */ +namespace messagetypes { +enum CustomMessageTypes { + MISSION_MESSAGE_TYPE_START = FW_MESSAGES_COUNT +}; + +void clearMissionMessage(CommandMessage* message); +} + +#endif /* CONFIG_IPC_MISSIONMESSAGETYPES_H_ */ diff --git a/defaultcfg/config/objects/Factory.cpp b/defaultcfg/config/objects/Factory.cpp new file mode 100644 index 00000000..51dd6130 --- /dev/null +++ b/defaultcfg/config/objects/Factory.cpp @@ -0,0 +1,54 @@ +#include "Factory.h" +#include "../tmtc/apid.h" +#include "../tmtc/pusIds.h" +#include "../objects/systemObjectList.h" +#include "../devices/logicalAddresses.h" +#include "../devices/powerSwitcherList.h" + +#include +#include +#include +#include +#include +#include + +#include + +/** + * This class should be used to create all system objects required for + * the on-board software, using the object ID list from the configuration + * folder. + * + * The objects are registered in the internal object manager automatically. + * This is used later to add objects to tasks. + * + * This file also sets static framework IDs. + * + * Framework objects are created first. + * @ingroup init + */ +void Factory::produce(void) { + setStaticFrameworkObjectIds(); + new EventManager(objects::EVENT_MANAGER); + new HealthTable(objects::HEALTH_TABLE); + //new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); +} + +void Factory::setStaticFrameworkObjectIds() { + PusServiceBase::packetSource = objects::PUS_PACKET_DISTRIBUTOR; + PusServiceBase::packetDestination = objects::TM_FUNNEL; + + CommandingServiceBase::defaultPacketSource = objects::PUS_PACKET_DISTRIBUTOR; + CommandingServiceBase::defaultPacketDestination = objects::TM_FUNNEL; + + VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION; + + DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; + DeviceHandlerBase::rawDataReceiverId = objects::PUS_SERVICE_2_DEVICE_ACCESS; + + DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT; + + TmPacketStored::timeStamperId = objects::PUS_TIME; + //TmFunnel::downlinkDestination = objects::NO_OBJECT; +} + diff --git a/defaultcfg/config/objects/Factory.h b/defaultcfg/config/objects/Factory.h new file mode 100644 index 00000000..fe55deff --- /dev/null +++ b/defaultcfg/config/objects/Factory.h @@ -0,0 +1,17 @@ +#ifndef FACTORY_H_ +#define FACTORY_H_ + +#include +#include + +namespace Factory { + /** + * @brief Creates all SystemObject elements which are persistent + * during execution. + */ + void produce(); + void setStaticFrameworkObjectIds(); +} + + +#endif /* FACTORY_H_ */ diff --git a/defaultcfg/config/objects/systemObjectList.h b/defaultcfg/config/objects/systemObjectList.h new file mode 100644 index 00000000..f4292f6d --- /dev/null +++ b/defaultcfg/config/objects/systemObjectList.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ +#define CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ + +#include +#include + +// The objects will be instantiated in the ID order +namespace objects { + enum sourceObjects: uint32_t { + /* All addresses between start and end are reserved for the FSFW */ + FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION, + FSFW_CONFIG_RESERVED_END = TM_STORE + }; +} + +#endif /* BSP_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ */ diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp b/defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp new file mode 100644 index 00000000..f836a746 --- /dev/null +++ b/defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp @@ -0,0 +1,23 @@ +#include "PollingSequenceFactory.h" + +#include +#include +#include + +ReturnValue_t pst::pollingSequenceInitDefault( + FixedTimeslotTaskIF *thisSequence) { + /* Length of a communication cycle */ + uint32_t length = thisSequence->getPeriodMs(); + + /* Add polling sequence table here */ + + if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } +} + diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.h b/defaultcfg/config/pollingsequence/PollingSequenceFactory.h new file mode 100644 index 00000000..c5d41b7d --- /dev/null +++ b/defaultcfg/config/pollingsequence/PollingSequenceFactory.h @@ -0,0 +1,32 @@ +#ifndef POLLINGSEQUENCEFACTORY_H_ +#define POLLINGSEQUENCEFACTORY_H_ + +#include + +class FixedTimeslotTaskIF; + +/** + * All device handlers are scheduled by adding them into Polling Sequence Tables (PST) + * to satisfy stricter timing requirements of device communication, + * A device handler has four different communication steps: + * 1. DeviceHandlerIF::SEND_WRITE -> Send write via interface + * 2. DeviceHandlerIF::GET_WRITE -> Get confirmation for write + * 3. DeviceHandlerIF::SEND_READ -> Send read request + * 4. DeviceHandlerIF::GET_READ -> Read from interface + * The PST specifies precisely when the respective ComIF functions are called + * during the communication cycle time. + * The task is created using the FixedTimeslotTaskIF, + * which utilises the underlying Operating System Abstraction Layer (OSAL) + * + * @param thisSequence FixedTimeslotTaskIF * object is passed inside the Factory class when creating the PST + * @return + */ +namespace pst { + +/* Default PST */ +ReturnValue_t pollingSequenceInitDefault(FixedTimeslotTaskIF *thisSequence); + + +} + +#endif /* POLLINGSEQUENCEINIT_H_ */ diff --git a/defaultcfg/config/returnvalues/classIds.h b/defaultcfg/config/returnvalues/classIds.h new file mode 100644 index 00000000..606cc60b --- /dev/null +++ b/defaultcfg/config/returnvalues/classIds.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_ +#define CONFIG_RETURNVALUES_CLASSIDS_H_ + +#include + +/** + * @brief CLASS_ID defintions which are required for custom returnvalues. + */ +namespace CLASS_ID { +enum { + MISSION_CLASS_ID_START = FW_CLASS_ID_COUNT, +}; +} + + +#endif /* CONFIG_RETURNVALUES_CLASSIDS_H_ */ diff --git a/defaultcfg/config/tmtc/apid.h b/defaultcfg/config/tmtc/apid.h new file mode 100644 index 00000000..c0231bca --- /dev/null +++ b/defaultcfg/config/tmtc/apid.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_TMTC_APID_H_ +#define CONFIG_TMTC_APID_H_ + +#include + +/** + * Application Process Definition: entity, uniquely identified by an + * application process ID (APID), capable of generating telemetry source + * packets and receiving telecommand packets. + * + * Chose APID(s) for mission and define it here. + */ +namespace apid { + static const uint16_t DEFAULT_APID = 0x00; +} + + +#endif /* CONFIG_TMTC_APID_H_ */ diff --git a/defaultcfg/config/tmtc/pusIds.h b/defaultcfg/config/tmtc/pusIds.h new file mode 100644 index 00000000..cc0db9f0 --- /dev/null +++ b/defaultcfg/config/tmtc/pusIds.h @@ -0,0 +1,23 @@ +#ifndef CONFIG_TMTC_PUSIDS_HPP_ +#define CONFIG_TMTC_PUSIDS_HPP_ + +namespace pus { +enum Ids: uint8_t { + PUS_SERVICE_1 = 1, + PUS_SERVICE_2 = 2, + PUS_SERVICE_3 = 3, + PUS_SERVICE_5 = 5, + PUS_SERVICE_6 = 6, + PUS_SERVICE_8 = 8, + PUS_SERVICE_9 = 9, + PUS_SERVICE_11 = 11, + PUS_SERVICE_17 = 17, + PUS_SERVICE_19 = 19, + PUS_SERVICE_20 = 20, + PUS_SERVICE_23 = 23, + PUS_SERVICE_200 = 200, + PUS_SERVICE_201 = 201, +}; +}; + +#endif /* CONFIG_TMTC_PUSIDS_HPP_ */ diff --git a/events/EventManager.cpp b/events/EventManager.cpp index e71951e3..f60a8a66 100644 --- a/events/EventManager.cpp +++ b/events/EventManager.cpp @@ -1,5 +1,7 @@ #include "EventManager.h" #include "EventMessage.h" +#include + #include "../serviceinterface/ServiceInterfaceStream.h" #include "../ipc/QueueFactory.h" #include "../ipc/MutexFactory.h" @@ -12,8 +14,10 @@ const uint16_t EventManager::POOL_SIZES[N_POOLS] = { // objects registering for certain events. // Each listener requires 1 or 2 EventIdMatcher and 1 or 2 ReportRangeMatcher. // So a good guess is 75 to a max of 100 pools required for each, which fits well. -// SHOULDDO: Shouldn't this be in the config folder and passed via ctor? -const uint16_t EventManager::N_ELEMENTS[N_POOLS] = { 240, 120, 120 }; +const uint16_t EventManager::N_ELEMENTS[N_POOLS] = { + fsfwconfig::FSFW_EVENTMGMR_MATCHTREE_NODES , + fsfwconfig::FSFW_EVENTMGMT_EVENTIDMATCHERS, + fsfwconfig::FSFW_EVENTMGMR_RANGEMATCHERS }; EventManager::EventManager(object_id_t setObjectId) : SystemObject(setObjectId), diff --git a/health/HasHealthIF.h b/health/HasHealthIF.h index ac404300..86863ea8 100644 --- a/health/HasHealthIF.h +++ b/health/HasHealthIF.h @@ -1,5 +1,5 @@ -#ifndef HASHEALTHIF_H_ -#define HASHEALTHIF_H_ +#ifndef FSFW_HEALTH_HASHEALTHIF_H_ +#define FSFW_HEALTH_HASHEALTHIF_H_ #include "../events/Event.h" #include "../returnvalues/HasReturnvaluesIF.h" @@ -8,9 +8,13 @@ class HasHealthIF { public: - typedef enum { - HEALTHY = 1, FAULTY = 0, EXTERNAL_CONTROL = 2, NEEDS_RECOVERY = 3, PERMANENT_FAULTY = 4 - } HealthState; + enum HealthState: uint8_t { + HEALTHY = 1, + FAULTY = 0, + EXTERNAL_CONTROL = 2, + NEEDS_RECOVERY = 3, + PERMANENT_FAULTY = 4 + }; static const uint8_t INTERFACE_ID = CLASS_ID::HAS_HEALTH_IF; static const ReturnValue_t OBJECT_NOT_HEALTHY = MAKE_RETURN_CODE(1); @@ -31,20 +35,17 @@ public: virtual MessageQueueId_t getCommandQueue() const = 0; /** - * set the Health State - * + * @brief Set the Health State * The parent will be informed, if the Health changes - * * @param health */ virtual ReturnValue_t setHealth(HealthState health) = 0; /** - * get Health State - * - * @return Health State of the object + * @brief Get Health State + * @return Health State of the object */ virtual HasHealthIF::HealthState getHealth() = 0; }; -#endif /* HASHEALTHIF_H_ */ +#endif /* FSFW_HEALTH_HASHEALTHIF_H_ */ diff --git a/health/HealthHelper.h b/health/HealthHelper.h index d1f1945c..08889fba 100644 --- a/health/HealthHelper.h +++ b/health/HealthHelper.h @@ -12,13 +12,15 @@ #include "../returnvalues/HasReturnvaluesIF.h" /** - * Helper class for Objects that implement HasHealthIF + * @brief Helper class for Objects that implement HasHealthIF + * @details + * It takes care of registering with the Health Table as well as handling + * health commands (including replying to the sender) and updating + * the Health Table. * - * It takes care of registering with the Health Table as well as handling health commands - * (including replying to the sender) and updating the Health Table. - * - * If a parent is set in the ctor, the parent will be informed with a @c HEALTH_INFO message - * about changes in the health state. Note that a @c HEALTH_INFO is only generated if the Health + * If a parent is set in the ctor, the parent will be informed with a + * @c HEALTH_INFO message about changes in the health state. + * Note that a @c HEALTH_INFO is only generated if the Health * changes, not for all @c HEALTH_SET commands received. * * It does NOT handle @c HEALTH_INFO messages @@ -27,10 +29,9 @@ class HealthHelper { public: /** - * ctor - * * @param owner - * @param objectId the object Id to use when communication with the HealthTable + * @param objectId The object Id to use when communication with + * the HealthTable */ HealthHelper(HasHealthIF* owner, object_id_t objectId); @@ -56,8 +57,9 @@ public: * * @param message * @return - * -@c RETURN_OK if the message was handled - * -@c RETURN_FAILED if the message could not be handled (ie it was not a @c HEALTH_SET or @c HEALTH_READ message) + * -@c RETURN_OK if the message was handled + * -@c RETURN_FAILED if the message could not be handled + * (ie it was not a @c HEALTH_SET or @c HEALTH_READ message) */ ReturnValue_t handleHealthCommand(CommandMessage *message); @@ -78,16 +80,19 @@ public: HasHealthIF::HealthState getHealth(); /** - * @param parentQueue the Queue id of the parent object. Set to 0 if no parent present + * @param parentQueue The queue ID of the parent object. + * Set to 0 if no parent present */ void setParentQueue(MessageQueueId_t parentQueue); /** * - * @param parentQueue the Queue id of the parent object. Set to 0 if no parent present + * @param parentQueue The queue ID of the parent object. + * Set to 0 if no parent present * @return - * -@c RETURN_OK if the Health Table was found and the object could be registered - * -@c RETURN_FAILED else + * -@c RETURN_OK if the Health Table was found and the object + * could be registered + * -@c RETURN_FAILED else */ ReturnValue_t initialize(MessageQueueId_t parentQueue ); @@ -110,11 +115,15 @@ private: HasHealthIF* owner; /** - * if the #parentQueue is not NULL, a @c HEALTH_INFO message will be sent to this queue - * @param health the health is passed as parameter so that the number of calls to the health table can be minimized + * if the #parentQueue is not NULL, a @c HEALTH_INFO message + * will be sent to this queue + * @param health + * The health is passed as parameter so that the number of + * calls to the health table can be minimized * @param oldHealth information of the previous health state. */ - void informParent(HasHealthIF::HealthState health, HasHealthIF::HealthState oldHealth); + void informParent(HasHealthIF::HealthState health, + HasHealthIF::HealthState oldHealth); void handleSetHealthCommand(CommandMessage *message); }; diff --git a/health/HealthMessage.cpp b/health/HealthMessage.cpp index 1bb29526..52479c26 100644 --- a/health/HealthMessage.cpp +++ b/health/HealthMessage.cpp @@ -7,11 +7,13 @@ void HealthMessage::setHealthMessage(CommandMessage* message, Command_t command, message->setParameter2(oldHealth); } -void HealthMessage::setHealthMessage(CommandMessage* message, Command_t command) { +void HealthMessage::setHealthMessage(CommandMessage* message, + Command_t command) { message->setCommand(command); } -HasHealthIF::HealthState HealthMessage::getHealth(const CommandMessage* message) { +HasHealthIF::HealthState HealthMessage::getHealth( + const CommandMessage* message) { return (HasHealthIF::HealthState) message->getParameter(); } diff --git a/health/HealthMessage.h b/health/HealthMessage.h index db2a7719..fb979c66 100644 --- a/health/HealthMessage.h +++ b/health/HealthMessage.h @@ -1,5 +1,5 @@ -#ifndef HEALTHMESSAGE_H_ -#define HEALTHMESSAGE_H_ +#ifndef FSFW_HEALTH_HEALTHMESSAGE_H_ +#define FSFW_HEALTH_HEALTHMESSAGE_H_ #include "HasHealthIF.h" #include "../ipc/CommandMessage.h" @@ -7,14 +7,20 @@ class HealthMessage { public: static const uint8_t MESSAGE_ID = messagetypes::HEALTH_COMMAND; - static const Command_t HEALTH_SET = MAKE_COMMAND_ID(1);//REPLY_COMMAND_OK/REPLY_REJECTED - static const Command_t HEALTH_ANNOUNCE = MAKE_COMMAND_ID(3); //NO REPLY! + + static const Command_t HEALTH_SET = MAKE_COMMAND_ID(1); + // No reply expected, health will be announced as event! + static const Command_t HEALTH_ANNOUNCE = MAKE_COMMAND_ID(2); + // Same as before, but all objects in health table will + // announce their health as events. + static const Command_t HEALTH_ANNOUNCE_ALL = MAKE_COMMAND_ID(3); + static const Command_t HEALTH_INFO = MAKE_COMMAND_ID(5); static const Command_t REPLY_HEALTH_SET = MAKE_COMMAND_ID(6); static void setHealthMessage(CommandMessage *message, Command_t command, - HasHealthIF::HealthState health, HasHealthIF::HealthState oldHealth = HasHealthIF::FAULTY); - + HasHealthIF::HealthState health, + HasHealthIF::HealthState oldHealth = HasHealthIF::FAULTY); static void setHealthMessage(CommandMessage *message, Command_t command); static HasHealthIF::HealthState getHealth(const CommandMessage *message); @@ -27,4 +33,4 @@ private: HealthMessage(); }; -#endif /* HEALTHMESSAGE_H_ */ +#endif /* FSFW_HEALTH_HEALTHMESSAGE_H_ */ diff --git a/health/HealthTable.cpp b/health/HealthTable.cpp index 4d2564a3..2b8b6712 100644 --- a/health/HealthTable.cpp +++ b/health/HealthTable.cpp @@ -1,6 +1,7 @@ #include "HealthTable.h" -#include "../serialize/SerializeAdapter.h" +#include "../ipc/MutexHelper.h" #include "../ipc/MutexFactory.h" +#include "../serialize/SerializeAdapter.h" HealthTable::HealthTable(object_id_t objectid) : SystemObject(objectid) { @@ -9,6 +10,12 @@ HealthTable::HealthTable(object_id_t objectid) : mapIterator = healthMap.begin(); } +void HealthTable::setMutexTimeout(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) { + this->timeoutType = timeoutType; + this->mutexTimeoutMs = timeoutMs; +} + HealthTable::~HealthTable() { MutexFactory::instance()->deleteMutex(mutex); } @@ -18,74 +25,63 @@ ReturnValue_t HealthTable::registerObject(object_id_t object, if (healthMap.count(object) != 0) { return HasReturnvaluesIF::RETURN_FAILED; } - healthMap.insert( - std::pair(object, - initilialState)); + healthMap.emplace(object, initilialState); return HasReturnvaluesIF::RETURN_OK; } void HealthTable::setHealth(object_id_t object, HasHealthIF::HealthState newState) { - mutex->lockMutex(MutexIF::BLOCKING); + MutexHelper(mutex, timeoutType, mutexTimeoutMs); HealthMap::iterator iter = healthMap.find(object); if (iter != healthMap.end()) { iter->second = newState; } - mutex->unlockMutex(); } HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) { HasHealthIF::HealthState state = HasHealthIF::HEALTHY; - mutex->lockMutex(MutexIF::BLOCKING); + MutexHelper(mutex, timeoutType, mutexTimeoutMs); HealthMap::iterator iter = healthMap.find(object); if (iter != healthMap.end()) { state = iter->second; } - mutex->unlockMutex(); return state; } -uint32_t HealthTable::getPrintSize() { - mutex->lockMutex(MutexIF::BLOCKING); - uint32_t size = healthMap.size() * 5 + 2; - mutex->unlockMutex(); - return size; -} - bool HealthTable::hasHealth(object_id_t object) { - bool exits = false; - mutex->lockMutex(MutexIF::BLOCKING); + MutexHelper(mutex, timeoutType, mutexTimeoutMs); HealthMap::iterator iter = healthMap.find(object); if (iter != healthMap.end()) { - exits = true; + return true; } - mutex->unlockMutex(); - return exits; + return false; +} + +size_t HealthTable::getPrintSize() { + MutexHelper(mutex, timeoutType, mutexTimeoutMs); + uint32_t size = healthMap.size() * sizeof(object_id_t) + + sizeof(HasHealthIF::HealthState) + sizeof(uint16_t); + return size; } void HealthTable::printAll(uint8_t* pointer, size_t maxSize) { - mutex->lockMutex(MutexIF::BLOCKING); + MutexHelper(mutex, timeoutType, mutexTimeoutMs); size_t size = 0; uint16_t count = healthMap.size(); - ReturnValue_t result = SerializeAdapter::serialize(&count, + SerializeAdapter::serialize(&count, &pointer, &size, maxSize, SerializeIF::Endianness::BIG); - HealthMap::iterator iter; - for (iter = healthMap.begin(); - iter != healthMap.end() && result == HasReturnvaluesIF::RETURN_OK; - ++iter) { - result = SerializeAdapter::serialize(&iter->first, + for (const auto& health: healthMap) { + SerializeAdapter::serialize(&health.first, &pointer, &size, maxSize, SerializeIF::Endianness::BIG); - uint8_t health = iter->second; - result = SerializeAdapter::serialize(&health, &pointer, &size, + uint8_t healthValue = health.second; + SerializeAdapter::serialize(&healthValue, &pointer, &size, maxSize, SerializeIF::Endianness::BIG); } - mutex->unlockMutex(); } -ReturnValue_t HealthTable::iterate( - std::pair *value, bool reset) { +ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - mutex->lockMutex(MutexIF::BLOCKING); + MutexHelper(mutex, timeoutType, mutexTimeoutMs); if (reset) { mapIterator = healthMap.begin(); } @@ -94,7 +90,5 @@ ReturnValue_t HealthTable::iterate( } *value = *mapIterator; mapIterator++; - mutex->unlockMutex(); - return result; } diff --git a/health/HealthTable.h b/health/HealthTable.h index 6f5dd18d..cea34f68 100644 --- a/health/HealthTable.h +++ b/health/HealthTable.h @@ -1,35 +1,47 @@ -#ifndef HEALTHTABLE_H_ -#define HEALTHTABLE_H_ +#ifndef FSFW_HEALTH_HEALTHTABLE_H_ +#define FSFW_HEALTH_HEALTHTABLE_H_ #include "HealthTableIF.h" #include "../objectmanager/SystemObject.h" #include "../ipc/MutexIF.h" #include -typedef std::map HealthMap; class HealthTable: public HealthTableIF, public SystemObject { public: HealthTable(object_id_t objectid); virtual ~HealthTable(); - virtual ReturnValue_t registerObject(object_id_t object, - HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY); + void setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs); - virtual bool hasHealth(object_id_t object); - virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState); - virtual HasHealthIF::HealthState getHealth(object_id_t); + /** HealthTableIF overrides */ + virtual ReturnValue_t registerObject(object_id_t object, + HasHealthIF::HealthState initilialState = + HasHealthIF::HEALTHY) override; + virtual size_t getPrintSize() override; + virtual void printAll(uint8_t *pointer, size_t maxSize) override; - virtual uint32_t getPrintSize(); - virtual void printAll(uint8_t *pointer, size_t maxSize); + /** ManagesHealthIF overrides */ + virtual bool hasHealth(object_id_t object) override; + virtual void setHealth(object_id_t object, + HasHealthIF::HealthState newState) override; + virtual HasHealthIF::HealthState getHealth(object_id_t) override; protected: + using HealthMap = std::map; + using HealthEntry = std::pair; + MutexIF* mutex; + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; + uint32_t mutexTimeoutMs = 20; + HealthMap healthMap; HealthMap::iterator mapIterator; - virtual ReturnValue_t iterate(std::pair *value, bool reset = false); + virtual ReturnValue_t iterate( + HealthEntry* value, + bool reset = false) override; }; -#endif /* HEALTHTABLE_H_ */ +#endif /* FSFW_HEALTH_HEALTHTABLE_H_ */ diff --git a/health/HealthTableIF.h b/health/HealthTableIF.h index 404c03e4..d61e6761 100644 --- a/health/HealthTableIF.h +++ b/health/HealthTableIF.h @@ -1,26 +1,24 @@ -#ifndef HEALTHTABLEIF_H_ -#define HEALTHTABLEIF_H_ +#ifndef FSFW_HEALTH_HEALTHTABLEIF_H_ +#define FSFW_HEALTH_HEALTHTABLEIF_H_ #include "ManagesHealthIF.h" #include "../objectmanager/ObjectManagerIF.h" #include "../returnvalues/HasReturnvaluesIF.h" -#include - class HealthTableIF: public ManagesHealthIF { - friend class HealthCommandingService; public: - virtual ~HealthTableIF() { - } + virtual ~HealthTableIF() {} virtual ReturnValue_t registerObject(object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0; - virtual uint32_t getPrintSize() = 0; + virtual size_t getPrintSize() = 0; virtual void printAll(uint8_t *pointer, size_t maxSize) = 0; protected: - virtual ReturnValue_t iterate(std::pair *value, bool reset = false) = 0; + virtual ReturnValue_t iterate( + std::pair *value, + bool reset = false) = 0; }; -#endif /* HEALTHTABLEIF_H_ */ +#endif /* FRAMEWORK_HEALTH_HEALTHTABLEIF_H_ */ diff --git a/health/ManagesHealthIF.h b/health/ManagesHealthIF.h index 42d4c4f0..2edfceca 100644 --- a/health/ManagesHealthIF.h +++ b/health/ManagesHealthIF.h @@ -1,8 +1,9 @@ -#ifndef FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_ -#define FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_ +#ifndef FSFW_HEALTH_MANAGESHEALTHIF_H_ +#define FSFW_HEALTH_MANAGESHEALTHIF_H_ #include "HasHealthIF.h" #include "../objectmanager/ObjectManagerIF.h" + class ManagesHealthIF { public: virtual ~ManagesHealthIF() { @@ -49,4 +50,4 @@ public: } }; -#endif /* FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_ */ +#endif /* FSFW_HEALTH_MANAGESHEALTHIF_H_ */ diff --git a/osal/host/QueueFactory.cpp b/osal/host/QueueFactory.cpp index da3fea55..1a679c96 100644 --- a/osal/host/QueueFactory.cpp +++ b/osal/host/QueueFactory.cpp @@ -1,6 +1,11 @@ + +#include "MessageQueue.h" + +#include "../../ipc/MessageQueueSenderIF.h" +#include "../../ipc/MessageQueueMessageIF.h" #include "../../ipc/QueueFactory.h" -#include "../../osal/host/MessageQueue.h" #include "../../serviceinterface/ServiceInterfaceStream.h" + #include QueueFactory* QueueFactory::factoryInstance = nullptr; diff --git a/osal/windows/TcWinUdpPollingTask.cpp b/osal/windows/TcWinUdpPollingTask.cpp new file mode 100644 index 00000000..7b54bb2c --- /dev/null +++ b/osal/windows/TcWinUdpPollingTask.cpp @@ -0,0 +1,148 @@ +#include "TcWinUdpPollingTask.h" +#include "../../globalfunctions/arrayprinter.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + +#include +#include + +TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId, + object_id_t tmtcUnixUdpBridge, size_t frameSize, + double timeoutSeconds): SystemObject(objectId), + tmtcBridgeId(tmtcUnixUdpBridge) { + if(frameSize > 0) { + this->frameSize = frameSize; + } + else { + this->frameSize = DEFAULT_MAX_FRAME_SIZE; + } + + // Set up reception buffer with specified frame size. + // For now, it is assumed that only one frame is held in the buffer! + receptionBuffer.reserve(this->frameSize); + receptionBuffer.resize(this->frameSize); + + if(timeoutSeconds == -1) { + receptionTimeout = DEFAULT_TIMEOUT; + } + else { + receptionTimeout = timevalOperations::toTimeval(timeoutSeconds); + } +} + +TcWinUdpPollingTask::~TcWinUdpPollingTask() {} + +ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) { + // Poll for new UDP datagrams in permanent loop. + while(true) { + //! Sender Address is cached here. + struct sockaddr_in senderAddress; + int senderAddressSize = sizeof(senderAddress); + ssize_t bytesReceived = recvfrom(serverUdpSocket, + reinterpret_cast(receptionBuffer.data()), frameSize, + receptionFlags, reinterpret_cast(&senderAddress), + &senderAddressSize); + if(bytesReceived == SOCKET_ERROR) { + // handle error + sif::error << "TcWinUdpPollingTask::performOperation: Reception" + " error." << std::endl; + handleReadError(); + continue; + } + //sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived + // << " bytes received" << std::endl; + + ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); + if(result != HasReturnvaluesIF::RETURN_FAILED) { + + } + tmtcBridge->registerCommConnect(); + tmtcBridge->checkAndSetClientAddress(senderAddress); + } + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { + store_address_t storeId; + ReturnValue_t result = tcStore->addData(&storeId, + receptionBuffer.data(), bytesRead); + // arrayprinter::print(receptionBuffer.data(), bytesRead); + if (result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data " + "storage failed" << std::endl; + sif::error << "Packet size: " << bytesRead << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + TmTcMessage message(storeId); + + result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); + if (result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "Serial Polling: Sending message to queue failed" + << std::endl; + tcStore->deleteData(storeId); + } + return result; +} + +ReturnValue_t TcWinUdpPollingTask::initialize() { + tcStore = objectManager->get(objects::TC_STORE); + if (tcStore == nullptr) { + sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!" + << std::endl; + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + tmtcBridge = objectManager->get(tmtcBridgeId); + if(tmtcBridge == nullptr) { + sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid" + " TMTC bridge object!" << std::endl; + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + serverUdpSocket = tmtcBridge->serverSocket; + //sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket " + // << serverUdpSocket << std::endl; + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() { + // Initialize the destination after task creation. This ensures + // that the destination has already been set in the TMTC bridge. + targetTcDestination = tmtcBridge->getRequestQueue(); + return HasReturnvaluesIF::RETURN_OK; +} + +void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) { + DWORD timeoutMs = timeoutSeconds * 1000.0; + int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeoutMs), sizeof(DWORD)); + if(result == -1) { + sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting " + "receive timeout failed with " << strerror(errno) << std::endl; + } +} + +void TcWinUdpPollingTask::handleReadError() { + int error = WSAGetLastError(); + switch(error) { + case(WSANOTINITIALISED): { + sif::info << "TmTcWinUdpBridge::handleReadError: WSANOTINITIALISED: " + << "WSAStartup(...) call " << "necessary" << std::endl; + break; + } + case(WSAEFAULT): { + sif::info << "TmTcWinUdpBridge::handleReadError: WSADEFAULT: " + << "Bad address " << std::endl; + break; + } + default: { + sif::info << "TmTcWinUdpBridge::handleReadError: Error code: " + << error << std::endl; + break; + } + } + // to prevent spam. + Sleep(1000); +} diff --git a/osal/windows/TcWinUdpPollingTask.h b/osal/windows/TcWinUdpPollingTask.h new file mode 100644 index 00000000..50d39d25 --- /dev/null +++ b/osal/windows/TcWinUdpPollingTask.h @@ -0,0 +1,67 @@ +#ifndef FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ +#define FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ + +#include "TmTcWinUdpBridge.h" +#include "../../objectmanager/SystemObject.h" +#include "../../tasks/ExecutableObjectIF.h" +#include "../../storagemanager/StorageManagerIF.h" + +#include + +/** + * @brief This class can be used to implement the polling of a Unix socket, + * using UDP for now. + * @details + * The task will be blocked while the specified number of bytes has not been + * received, so TC reception is handled inside a separate task. + * This class caches the IP address of the sender. It is assumed there + * is only one sender for now. + */ +class TcWinUdpPollingTask: public SystemObject, + public ExecutableObjectIF { + friend class TmTcWinUdpBridge; +public: + static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048; + //! 0.5 default milliseconds timeout for now. + static constexpr timeval DEFAULT_TIMEOUT = {.tv_sec = 0, .tv_usec = 500}; + + TcWinUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + size_t frameSize = 0, double timeoutSeconds = -1); + virtual~ TcWinUdpPollingTask(); + + /** + * Turn on optional timeout for UDP polling. In the default mode, + * the receive function will block until a packet is received. + * @param timeoutSeconds + */ + void setTimeout(double timeoutSeconds); + + virtual ReturnValue_t performOperation(uint8_t opCode) override; + virtual ReturnValue_t initialize() override; + virtual ReturnValue_t initializeAfterTaskCreation() override; + +protected: + StorageManagerIF* tcStore = nullptr; + +private: + //! TMTC bridge is cached. + object_id_t tmtcBridgeId = objects::NO_OBJECT; + TmTcWinUdpBridge* tmtcBridge = nullptr; + MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; + //! Reception flags: https://linux.die.net/man/2/recvfrom. + int receptionFlags = 0; + + //! Server socket, which is member of TMTC bridge and is assigned in + //! constructor + SOCKET serverUdpSocket = 0; + + std::vector receptionBuffer; + + size_t frameSize = 0; + timeval receptionTimeout; + + ReturnValue_t handleSuccessfullTcRead(size_t bytesRead); + void handleReadError(); +}; + +#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */ diff --git a/osal/windows/TmTcWinUdpBridge.cpp b/osal/windows/TmTcWinUdpBridge.cpp new file mode 100644 index 00000000..7e283c2a --- /dev/null +++ b/osal/windows/TmTcWinUdpBridge.cpp @@ -0,0 +1,176 @@ +#include +#include "TmTcWinUdpBridge.h" + +TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, + object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, + uint16_t serverPort, uint16_t clientPort): + TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { + mutex = MutexFactory::instance()->createMutex(); + + // Initiates Winsock DLL. + WSAData wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge:" + "WSAStartup failed with error: " << err << std::endl; + return; + } + + uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT; + if(serverPort != 0xFFFF) { + setServerPort = serverPort; + } + + uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT; + if(clientPort != 0xFFFF) { + setClientPort = clientPort; + } + + // Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html + //clientSocket = socket(AF_INET, SOCK_DGRAM, 0); + serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(serverSocket == INVALID_SOCKET) { + sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open" + " UDP socket!" << std::endl; + handleSocketError(); + return; + } + + serverAddress.sin_family = AF_INET; + + // Accept packets from any interface. (potentially insecure). + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(setServerPort); + serverAddressLen = sizeof(serverAddress); + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&serverSocketOptions), + sizeof(serverSocketOptions)); + + clientAddress.sin_family = AF_INET; + clientAddress.sin_addr.s_addr = htonl(INADDR_ANY); + clientAddress.sin_port = htons(setClientPort); + clientAddressLen = sizeof(clientAddress); + + int result = bind(serverSocket, + reinterpret_cast(&serverAddress), + serverAddressLen); + if(result != 0) { + sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind " + "local port " << setServerPort << " to server socket!" + << std::endl; + handleBindError(); + } +} + +TmTcWinUdpBridge::~TmTcWinUdpBridge() { + WSACleanup(); +} + +ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { + int flags = 0; + + //clientAddress.sin_addr.s_addr = htons(INADDR_ANY); + //clientAddressLen = sizeof(serverAddress); + +// char ipAddress [15]; +// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, +// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; + + ssize_t bytesSent = sendto(serverSocket, + reinterpret_cast(data), dataLen, flags, + reinterpret_cast(&clientAddress), clientAddressLen); + if(bytesSent == SOCKET_ERROR) { + sif::error << "TmTcWinUdpBridge::sendTm: Send operation failed." + << std::endl; + handleSendError(); + } +// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" +// " sent." << std::endl; + return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; +} + +void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { + MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10); + +// char ipAddress [15]; +// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, +// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; +// sif::debug << "IP Address Old: " << inet_ntop(AF_INET, +// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; + + // Set new IP address if it has changed. + if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) { + clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr; + clientAddressLen = sizeof(clientAddress); + } +} + +void TmTcWinUdpBridge::handleSocketError() { + int errCode = WSAGetLastError(); + switch(errCode) { + case(WSANOTINITIALISED): { + sif::info << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: " + << "WSAStartup(...) call " << "necessary" << std::endl; + break; + } + default: { + /* + https://docs.microsoft.com/en-us/windows/win32/winsock/ + windows-sockets-error-codes-2 + */ + sif::info << "TmTcWinUdpBridge::handleSocketError: Error code: " + << errCode << std::endl; + break; + } + } +} + +void TmTcWinUdpBridge::handleBindError() { + int errCode = WSAGetLastError(); + switch(errCode) { + case(WSANOTINITIALISED): { + sif::info << "TmTcWinUdpBridge::handleBindError: WSANOTINITIALISED: " + << "WSAStartup(...) call " << "necessary" << std::endl; + break; + } + default: { + /* + https://docs.microsoft.com/en-us/windows/win32/winsock/ + windows-sockets-error-codes-2 + */ + sif::info << "TmTcWinUdpBridge::handleBindError: Error code: " + << errCode << std::endl; + break; + } + } +} + +void TmTcWinUdpBridge::handleSendError() { + int errCode = WSAGetLastError(); + switch(errCode) { + case(WSANOTINITIALISED): { + sif::info << "TmTcWinUdpBridge::handleSendError: WSANOTINITIALISED: " + << "WSAStartup(...) call " << "necessary" << std::endl; + break; + } + case(WSAEADDRNOTAVAIL): { + sif::info << "TmTcWinUdpBridge::handleReadError: WSAEADDRNOTAVAIL: " + << "Check target address. " << std::endl; + break; + } + default: { + /* + https://docs.microsoft.com/en-us/windows/win32/winsock/ + windows-sockets-error-codes-2 + */ + sif::info << "TmTcWinUdpBridge::handleSendError: Error code: " + << errCode << std::endl; + break; + } + } +} + diff --git a/osal/windows/TmTcWinUdpBridge.h b/osal/windows/TmTcWinUdpBridge.h new file mode 100644 index 00000000..8188039c --- /dev/null +++ b/osal/windows/TmTcWinUdpBridge.h @@ -0,0 +1,49 @@ +#ifndef FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ +#define FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ + +#include "../../tmtcservices/TmTcBridge.h" + +#include +#include + +class TmTcWinUdpBridge: public TmTcBridge { + friend class TcWinUdpPollingTask; +public: + // The ports chosen here should not be used by any other process. + static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301; + static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302; + + TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination, + object_id_t tmStoreId, object_id_t tcStoreId, + uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF); + virtual~ TmTcWinUdpBridge(); + + void checkAndSetClientAddress(sockaddr_in clientAddress); + +protected: + virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; + +private: + SOCKET serverSocket = 0; + + const int serverSocketOptions = 0; + + struct sockaddr_in clientAddress; + int clientAddressLen = 0; + + struct sockaddr_in serverAddress; + int serverAddressLen = 0; + + //! Access to the client address is mutex protected as it is set + //! by another task. + MutexIF* mutex; + + void handleSocketError(); + void handleBindError(); + void handleSendError(); +}; + + + +#endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */ + diff --git a/returnvalues/HasReturnvaluesIF.h b/returnvalues/HasReturnvaluesIF.h index 3d3e3208..4a3835b6 100644 --- a/returnvalues/HasReturnvaluesIF.h +++ b/returnvalues/HasReturnvaluesIF.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_ -#define FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_ +#ifndef FSFW_RETURNVALUES_HASRETURNVALUESIF_H_ +#define FSFW_RETURNVALUES_HASRETURNVALUESIF_H_ #include "FwClassIds.h" #include @@ -15,9 +15,17 @@ public: static const ReturnValue_t RETURN_FAILED = 1; virtual ~HasReturnvaluesIF() {} - static ReturnValue_t makeReturnCode(uint8_t interfaceId, uint8_t number) { - return (interfaceId << 8) + number; + /** + * It is discouraged to use the input parameters 0,0 and 0,1 as this + * will generate the RETURN_OK and RETURN_FAILED returnvalues. + * @param interfaceId + * @param number + * @return + */ + static constexpr ReturnValue_t makeReturnCode(uint8_t interfaceId, + uint8_t number) { + return (static_cast(interfaceId) << 8) + number; } }; -#endif /* FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_ */ +#endif /* FSFW_RETURNVALUES_HASRETURNVALUESIF_H_ */ diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index f99f8fbb..f137e030 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -1,8 +1,9 @@ -#include "../timemanager/CCSDSTime.h" +#include "CCSDSTime.h" + #include #include #include - +#include CCSDSTime::CCSDSTime() { } @@ -158,15 +159,16 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* } // Newlib nano can't parse uint8, see SCNu8 documentation and https://sourceware.org/newlib/README // Suggestion: use uint16 all the time. This should work on all systems. -#ifdef NEWLIB_NANO_NO_C99_IO +#if FSFW_NO_C99_IO == 1 uint16_t year; uint16_t month; uint16_t day; uint16_t hour; uint16_t minute; float second; - int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, - &month, &day, &hour, &minute, &second); + int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%" + "2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &month, &day, &hour, + &minute, &second); if (count == 6) { to->year = year; to->month = month; @@ -179,12 +181,13 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* } // try Code B (yyyy-ddd) - count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &day, - &hour, &minute, &second); + count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%" + "2" SCNu16 ":%fZ", &year, &day, &hour, &minute, &second); if (count == 5) { uint8_t tempDay; ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, - reinterpret_cast(&month), reinterpret_cast(&tempDay)); + reinterpret_cast(&month), + reinterpret_cast(&tempDay)); if (result != RETURN_OK) { return RETURN_FAILED; } diff --git a/unittest/README.md b/unittest/README.md index e628d43e..8a787c07 100644 --- a/unittest/README.md +++ b/unittest/README.md @@ -7,6 +7,16 @@ The makefile with default settings creates the unit test binary which can be run in the terminal or in eclipse. ### Instructions + +To run the fsfw unittests in the project, perform following steps: + +1. Copy the testcfg folder the project root (folder containing the FSFW). +2. There is a makefile inside the testcfg folder which can be used to have + a starting point to compile the unit tests. Copy that Makefile to the project + root +3. Create a folder named catch2 (can have other name which requires Makefile + adaption) and copy the Catch2 header files there (NOTE: CMake support + not enabled yet!) ### Eclipse CDT settings diff --git a/unittest/testcfg/Makefile-FSFW-Tests b/unittest/testcfg/Makefile-FSFW-Tests index d43a6edc..2017d2bd 100644 --- a/unittest/testcfg/Makefile-FSFW-Tests +++ b/unittest/testcfg/Makefile-FSFW-Tests @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------- -# Makefile for FSFW Test +# Makefile for FSFW Test #------------------------------------------------------------------------------- # User-modifiable options #------------------------------------------------------------------------------- diff --git a/unittest/testcfg/cdatapool/dataPoolInit.h b/unittest/testcfg/cdatapool/dataPoolInit.h index 23a3d01f..9425d767 100644 --- a/unittest/testcfg/cdatapool/dataPoolInit.h +++ b/unittest/testcfg/cdatapool/dataPoolInit.h @@ -1,7 +1,7 @@ #ifndef HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ #define HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ -#include +#include #include #include #include