diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index bdad3dac..ba98991c 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -1,3 +1,4 @@ +#include #include "ObjectFactory.h" #include "OBSWConfig.h" #include "devConf.h" @@ -23,8 +24,6 @@ #include "linux/devices/SusHandler.h" #include "linux/csp/CspCookie.h" #include "linux/csp/CspComIF.h" -#include "linux/obc/CCSDSIPCoreBridge.h" - #include "mission/core/GenericFactory.h" #include "mission/devices/PDU1Handler.h" #include "mission/devices/PDU2Handler.h" @@ -83,6 +82,9 @@ #include "linux/boardtest/LibgpiodTest.h" #endif +#include +#include + ResetArgs resetArgsGnss0; ResetArgs resetArgsGnss1; @@ -171,6 +173,26 @@ void ObjectFactory::produce(void* args){ #endif /* TE7020 != 0 */ +#if OBSW_USE_PTME_IP_CORE == 1 + GpioCookie* gpioCookiePtmeIp = new GpioCookie; + GpiodRegular* vc0PapbBusyN = new GpiodRegular(std::string("gpiochip0"), 0, std::string("PAPBBusy_N")); + gpioCookiePtmeIp->addGpio(gpioIds::PAPB_BUSY_N, papbBusyN); + GpiodRegular* vc0PapbEmpty = new GpiodRegular(st_d::string("gpiochip0"), 1, + std::string("PAPBEmpty_VC0")); + gpioCookiePtmeIp->addGpio(gpioIds::PAPB_EMPTY, papbEmpty); + gpioComIF->addGpios(gpioCookiePtmeIp); + PapbVcInterface* vc0 = new PapbVcInterface(objects::PAPB_VC0, gpioComIF, ) + Ptme* ptme = new Ptme(objects::PTME); + ptme->addVcInterface(0, vc0); + ptme->addVcInterface(1, vc1); + ptme->addVcInterface(2, vc2); + ptme->addVcInterface(3, vc3); + + new CCSDSIPCoreBridge(objects::CCSDS_IP_CORE_BRIDGE, objects::CCSDS_PACKET_DISTRIBUTOR, + objects::TM_STORE, objects::TC_STORE, gpioComIF, std::string("/dev/uio0"), + gpioIds::PAPB_BUSY_N, gpioIds::PAPB_EMPTY); +#endif /* OBSW_USE_TMTC_IP_CORE_BRIDGE == 1 */ + #if OBSW_USE_TMTC_TCP_BRIDGE == 0 auto udpBridge = new UdpTmTcBridge(objects::TMTC_BRIDGE, objects::CCSDS_PACKET_DISTRIBUTOR); new UdpTcPollingTask(objects::TMTC_POLLING_TASK, objects::TMTC_BRIDGE); @@ -846,10 +868,10 @@ void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) { #if BOARD_TE0720 == 1 && OBSW_TEST_CCSDS_BRIDGE == 1 GpioCookie* gpioCookieCcsdsIp = new GpioCookie; - GpiodRegular* papbBusyN = new GpiodRegular(std::string("gpiochip0"), 0, std::string("PAPBBusy_N")); + GpiodRegular* papbBusyN = new GpiodRegular(std::string("gpiochip0"), 0, std::string("PAPBBusy_VC0")); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_BUSY_N, papbBusyN); GpiodRegular* papbEmpty = new GpiodRegular(std::string("gpiochip0"), 1, - std::string("Chip Select Sus Sensor")); + std::string("PAPBEmpty_VC0")); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_EMPTY, papbEmpty); gpioComIF->addGpios(gpioCookieCcsdsIp); diff --git a/common/config/commonClassIds.h b/common/config/commonClassIds.h index 793cb1ba..71a7d312 100644 --- a/common/config/commonClassIds.h +++ b/common/config/commonClassIds.h @@ -19,6 +19,7 @@ enum commonClassIds: uint8_t { PLOC_SUPERVISOR_HANDLER, //PLSV SUS_HANDLER, //SUSS CCSDS_IP_CORE_BRIDGE, //IPCI + PTME, //PTME PLOC_UPDATER, //PLUD GOM_SPACE_HANDLER, //GOMS PLOC_MEMORY_DUMPER, //PLMEMDUMP diff --git a/common/config/commonConfig.h.in b/common/config/commonConfig.h.in index 52b5dd45..55af54e3 100644 --- a/common/config/commonConfig.h.in +++ b/common/config/commonConfig.h.in @@ -6,5 +6,12 @@ // Use TCP instead of UDP for the TMTC bridge. This allows using the TMTC client locally // because UDP packets are not allowed in the VPN #define OBSW_USE_TMTC_TCP_BRIDGE 1 +// This will cause the OBSW to initialize the TMTC bridge responsible for exchanging data with the +// CCSDS IP Cores. +#define OBSW_USE_TMTC_IP_CORE_BRIDGE 0 +// Set to 1 if all telemetry should be sent to the PTME IP Core +#define OBSW_TM_TO_PTME 0 +// Set to 1 if telecommands are received via the PDEC IP Core +#define OBSW_TC_FROM_PDEC 0 #endif /* COMMON_CONFIG_COMMONCONFIG_H_ */ diff --git a/common/config/commonObjects.h b/common/config/commonObjects.h index e44e0a1a..5fc61bd4 100644 --- a/common/config/commonObjects.h +++ b/common/config/commonObjects.h @@ -11,6 +11,11 @@ enum commonObjects: uint32_t { TMTC_BRIDGE = 0x50000300, TMTC_POLLING_TASK = 0x50000400, FILE_SYSTEM_HANDLER = 0x50000500, + PTME = 0x50000600, + PAPB_VC0 = 0x50000700, + PAPB_VC0 = 0x500007001, + PAPB_VC0 = 0x500007002, + PAPB_VC0 = 0x500007003, /* 0x43 ('C') for Controllers */ THERMAL_CONTROLLER = 0x43400001, diff --git a/linux/obc/CCSDSIPCoreBridge.cpp b/linux/archive/tmtc/CCSDSIPCoreBridge.cpp similarity index 98% rename from linux/obc/CCSDSIPCoreBridge.cpp rename to linux/archive/tmtc/CCSDSIPCoreBridge.cpp index fe5a7007..982f7fc2 100644 --- a/linux/obc/CCSDSIPCoreBridge.cpp +++ b/linux/archive/tmtc/CCSDSIPCoreBridge.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include CCSDSIPCoreBridge::CCSDSIPCoreBridge(object_id_t objectId, object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, LinuxLibgpioIF* gpioComIF, diff --git a/linux/obc/CCSDSIPCoreBridge.h b/linux/archive/tmtc/CCSDSIPCoreBridge.h similarity index 100% rename from linux/obc/CCSDSIPCoreBridge.h rename to linux/archive/tmtc/CCSDSIPCoreBridge.h diff --git a/linux/fsfwconfig/OBSWConfig.h.in b/linux/fsfwconfig/OBSWConfig.h.in index dc397635..83564c0c 100644 --- a/linux/fsfwconfig/OBSWConfig.h.in +++ b/linux/fsfwconfig/OBSWConfig.h.in @@ -96,6 +96,7 @@ namespace config { /* Add mission configuration flags here */ static constexpr uint32_t OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE = 50; static constexpr uint32_t PLOC_UPDATER_QUEUE_SIZE = 50; +static constexpr uint8_t NUMBER_OF_VIRTUAL_CHANNELS = 4; #ifdef __cplusplus } diff --git a/linux/obc/CMakeLists.txt b/linux/obc/CMakeLists.txt index 79d9ba9b..315a0d33 100644 --- a/linux/obc/CMakeLists.txt +++ b/linux/obc/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${TARGET_NAME} PUBLIC - CCSDSIPCoreBridge.cpp + PapbVcInterface.cpp + Ptme.cpp ) diff --git a/linux/obc/PapbVcInterface.cpp b/linux/obc/PapbVcInterface.cpp new file mode 100644 index 00000000..736c8b24 --- /dev/null +++ b/linux/obc/PapbVcInterface.cpp @@ -0,0 +1,104 @@ +#include +#include "fsfw/serviceinterface/ServiceInterface.h" + +PapbVcInterface::PapbVcInterface(object_id_t objectId, LinuxLibgpioIF* gpioComIF, + gpioId_t papbBusyId, gpioId_t papbEmptyId, uint32_t vcOffset) : + SystemObject(objectId), gpioComIF(gpioComIF), uioVcInterface(uioVcInterface), papbBusyId( + papbBusyId), papbEmptyId(papbEmptyId), vcOffset(vcOffset) { +} + +PapbVcInterface::~PapbVcInterface() { +} + + +void PapbVcInterface::setRegisterAddress(uint32_t* ptmeBaseAddress) { + vcBaseReg = ptmeBaseAddress + vcOffset; +} + +ReturnValue_t PapbVcInterface::write(const uint8_t * data, size_t dataLen) { + + if(pollPapbBusySignal() == RETURN_OK) { + startPacketTransfer(); + } + + for(size_t idx = 0; idx < dataLen; idx++) { + if(pollPapbBusySignal() == RETURN_OK) { + *(vcBaseReg + DATA_REG_OFFSET) = static_cast(*(data + idx)); + } + else { + sif::warning << "PapbVcInterface::sendTm: Only written " << idx - 1 << " of " + << dataLen << " data" << std::endl; + return RETURN_FAILED; + } + } + + if(pollPapbBusySignal() == RETURN_OK) { + endPacketTransfer(); + } + return RETURN_OK; +} + +void PapbVcInterface::startPacketTransfer() { + *vcBaseReg = CONFIG_START; +} + +void PapbVcInterface::endPacketTransfer() { + *vcBaseReg = CONFIG_END; +} + +ReturnValue_t PapbVcInterface::pollPapbBusySignal() { + int papbBusyState = 0; + ReturnValue_t result = RETURN_OK; + + /** Check if PAPB interface is ready to receive data */ + result = gpioComIF->readGpio(papbBusyId, &papbBusyState); + if (result != RETURN_OK) { + sif::debug << "PapbVcInterface::pollPapbBusySignal: Failed to read papb busy signal" + << std::endl; + return RETURN_FAILED; + } + if (!papbBusyState) { + sif::debug << "PapbVcInterface::pollPapbBusySignal: PAPB busy" << std::endl; + return PAPB_BUSY; + } + + return RETURN_OK; +} + +void PapbVcInterface::isVcInterfaceBufferEmpty() { + ReturnValue_t result = RETURN_OK; + int papbEmptyState = 1; + + result = gpioComIF->readGpio(papbEmptyId, &papbEmptyState); + + if (result != RETURN_OK) { + sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal" + << std::endl; + return; + } + + if (papbEmptyState == 1) { + sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is empty" << std::endl; + } + else { + sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is not empty" << std::endl; + } + return; +} + +ReturnValue_t PapbVcInterface::sendTestFrame() { + /** Size of one complete transfer frame data field amounts to 1105 bytes */ + uint8_t testPacket[1105]; + + /** Fill one test packet */ + for(int idx = 0; idx < 1105; idx++) { + testPacket[idx] = static_cast(idx & 0xFF); + } + + ReturnValue_t result = sendTm(testPacket, 1105); + if(result != RETURN_OK) { + return result; + } + + return RETURN_OK; +} diff --git a/linux/obc/PapbVcInterface.h b/linux/obc/PapbVcInterface.h new file mode 100644 index 00000000..05947ac6 --- /dev/null +++ b/linux/obc/PapbVcInterface.h @@ -0,0 +1,106 @@ +#ifndef LINUX_OBC_PAPBVCINTERFACE_H_ +#define LINUX_OBC_PAPBVCINTERFACE_H_ + +#include "OBSWConfig.h" +#include "linux/obc/VcInterfaceIF.h" +#include +#include +#include +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + +/** + * @brief This class handles the transmission of data to a virtual channel of the PTME IP Core + * via the PAPB interface. + * + * @author J. Meier + */ +class PapbVcInterface : VcInterfaceIF, HasReturnvaluesIF { +public: + /** + * @brief Constructor + * + * @param objectId + * @param papbBusyId The ID of the GPIO which is connected to the PAPBBusy_N signal of the + * VcInterface IP Core. A low logic level indicates the VcInterface is not + * ready to receive more data. + * @param papbEmptyId The ID of the GPIO which is connected to the PAPBEmpty signal of the + * VcInterface IP Core. The signal is high when there are no packets in the + * external buffer memory (BRAM). + */ + PapbVcInterface(object_id_t objectId, LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, + gpioId_t papbEmptyId, uint32_t vcOffset); + virtual ~PapbVcInterface(); + +private: + + static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_IP_CORE_BRIDGE; + + static const ReturnValue_t PAPB_BUSY = MAKE_RETURN_CODE(0xA0); + + /** + * Configuration bits: + * bit[1:0]: Size of data (1,2,3 or 4 bytes). 1 Byte <=> b00 + * bit[2]: Set this bit to 1 to abort a transfered packet + * bit[3]: Signals to VcInterface the start of a new telemetry packet + */ + static const uint32_t CONFIG_START = 0x8; + + /** + * Writing this word to the VcInterface base address signals to the virtual channel interface + * that a complete tm packet has been transferred. + */ + static const uint32_t CONFIG_END = 0x0; + + /** + * Writing to this offset within the memory space of a virtual channel will insert data for + * encoding to the external buffer memory of the PTME IP Core. + * The address offset is 0x400 (= 4 * 256) + */ + static const int DATA_REG_OFFSET = 256; + + LinuxLibgpioIF* gpioComIF = nullptr; + + /** Pulled to low when virtual channel not ready to receive data */ + gpioId_t papbBusyId = gpio::NO_GPIO; + /** High when external buffer memory of virtual channel is empty */ + gpioId_t papbEmptyId = gpio::NO_GPIO; + + uint32_t* vcBaseReg = nullptr; + + uint32_t vcOffset = 0; + + /** + * @brief This function sends the config byte to the virtual channel of the PTME IP Core + * to initiate a packet transfer. + */ + void startPacketTransfer(); + + /** + * @brief This function sends the config byte to the virtual channel interface of the PTME + * IP Core to signal the end of a packet transfer. + */ + void endPacketTransfer(); + + /** + * @brief This function reads the papb busy signal indicating whether the virtual channel + * interface is ready to receive more data or not. PAPB is ready when + * PAPB_Busy_N == '1'. + * + * @return RETURN_OK when ready to receive data else PAPB_BUSY. + */ + ReturnValue_t pollPapbBusySignal(); + + /** + * @brief This function can be used for debugging to check whether there are packets in + * the packet buffer of the virtual channel or not. + */ + void isVcInterfaceBufferEmpty(); + + /** + * @brief This function sends a complete telemetry transfer frame data field (1105 bytes) + * to the papb interface of the PTME IP Core. Can be used to test the implementation. + */ + ReturnValue_t sendTestFrame(); +}; + +#endif /* LINUX_OBC_PAPBVCINTERFACE_H_ */ diff --git a/linux/obc/Ptme.cpp b/linux/obc/Ptme.cpp new file mode 100644 index 00000000..480b5836 --- /dev/null +++ b/linux/obc/Ptme.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include +#include "fsfw/serviceinterface/ServiceInterface.h" + +Ptme::Ptme(object_id_t objectId) : + SystemObject(objectId) { +} + +Ptme::~Ptme() { +} + +ReturnValue_t Ptme::initialize() { + ReturnValue_t result = TmTcBridge::initialize(); + + fd = open(PtmeConfig::UIO_DEVICE_FILE, O_RDWR); + if (fd < 1) { + sif::warning << "Ptme::initialize: Invalid UIO device file" << std::endl; + return RETURN_FAILED; + } + + /** + * Map uio device in virtual address space + * PROT_WRITE: Map uio device in writable only mode + */ + ptmeBaseAddress = static_cast(mmap(NULL, MAP_SIZE, PROT_WRITE, + MAP_SHARED, fd, 0)); + + if (ptmeBaseAddress == MAP_FAILED) { + sif::error << "Ptme::initialize: Failed to map uio address" << std::endl; + return RETURN_FAILED; + } + + return result; +} + +ReturnValue_t Ptme::writeToVc(uint8_t vcId, const uint8_t * data, size_t dataLen) { + ReturnValue_t result = RETURN_OK; + VcInterfaceMapIter vcInterfaceMapIter = vcInterfaceMap.find(vcId); + if (vcInterfaceMapIter == vcInterfaceMap.end()) { + sif::warning << "Ptme::writeToVc: No virtual channel interface found for the virtual " + "channel with id " << static_cast(vcId) << std::endl; + return UNKNOWN_VC_ID; + } + result = vcInterfaceMapIter->second.write(data, size); + return result; +} + +void Ptme::addVcInterface(VcId_t vcId, VcInterfaceIF* vc) { + + if (vcId > config::MAX_VIRTUAL_CHANNEL_ID) { + sif::warning << "Ptme::addVcInterface: Invalid virtual channel ID" << std::endl; + return; + } + + if (vc == nullptr) { + sif::warning << "Ptme::addVcInterface: Invalid virtual channel interface" << std::endl; + return; + } + + auto status = vcInterfaceMap.emplace(vcId, vc); + if (status.second == false) { + sif::warning << "Ptme::addVcInterface: Failed to add virtual channel interface to " + "virtual channel map" << std::endl; + return; + } +} diff --git a/linux/obc/Ptme.h b/linux/obc/Ptme.h new file mode 100644 index 00000000..6ef118ab --- /dev/null +++ b/linux/obc/Ptme.h @@ -0,0 +1,84 @@ +#ifndef LINUX_OBC_PTME_H_ +#define LINUX_OBC_PTME_H_ + +#include "OBSWConfig.h" +#include "linux/obc/PtmeIF.h" +#include "linux/obc/VcInterfaceIF.h" +#include +#include +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + +#include +#include + +/** + * @brief This class handles the interfacing to the telemetry (PTME) IP core responsible for the + * encoding of telemetry packets according to the CCSDS standards CCSDS 131.0-B-3 (TM Synchro- + * nization and channel coding) and CCSDS 132.0-B-2 (TM Space Data Link Protocoll). + * The IP cores are implemented on the programmable logic and are accessible through the + * linux UIO driver. + */ +class Ptme : PtmeIF, SystemObject, HasReturnvaluesIF { +public: + /** + * @brief Constructor + * + * @param objectId + */ + Ptme(object_id_t objectId); + virtual ~Ptme(); + + ReturnValue_t initialize() override; + ReturnValue_t writeToVc(uint8_t vcId, uint8_t* data, size_t size) override; + + /** + * @brief This function adds the reference to a virtual channel interface to the vcInterface + * map. + */ + void addVcInterface(VcId_t vcId, VcInterfaceIF* vc); + +private: + + static const uint8_t INTERFACE_ID = CLASS_ID::PTME; + + static const ReturnValue_t UNKNOWN_VC_ID = MAKE_RETURN_CODE(0xA0); + + + /** Size of mapped address space */ + static const int MAP_SIZE = 0x40000; + + /** + * Configuration bits: + * bit[1:0]: Size of data (1,2,3 or 4 bytes). 1 Byte <=> b00 + * bit[2]: Set this bit to 1 to abort a transfered packet + * bit[3]: Signals to PTME the start of a new telemetry packet + */ + static const uint32_t PTME_CONFIG_START = 0x8; + + /** + * Writing this word to the ptme base address signals to the PTME that a complete tm packet has + * been transferred. + */ + static const uint32_t PTME_CONFIG_END = 0x0; + + /** + * Writing to this offset within the PTME memory space will insert data for encoding to the + * PTME IP core. + * The address offset is 0x400 (= 4 * 256) + */ + static const int PTME_DATA_REG_OFFSET = 256; + + /** The file descriptor of the UIO driver */ + int fd; + + uint32_t* ptmeBaseAddress = nullptr; + + using VcId_t = uint8_t; + + using VcInterfaceMap = std::unordered_map; + using VcInterfaceMapIter = VcInterfaceMap::iterator; + + VcInterfaceMap vcInterfaceMap; +}; + +#endif /* LINUX_OBC_PTME_H_ */ diff --git a/linux/obc/PtmeConfig.h b/linux/obc/PtmeConfig.h new file mode 100644 index 00000000..34fd8876 --- /dev/null +++ b/linux/obc/PtmeConfig.h @@ -0,0 +1,26 @@ +#ifndef LINUX_OBC_PTMECONFIG_H_ +#define LINUX_OBC_PTMECONFIG_H_ + +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include + + +/** + * @brief Configuration parameters derived from FPGA design and device tree. + * + * @author J. Meier + */ +namespace PtmeConfig { + /** + * Offset of virtual channels mapped into address space + * 0x10000 = (0x4000 * 4) + */ + static const uint32_t VC0_OFFSETT = 0; + static const uint32_t VC1_OFFSETT = 0x4000; + static const uint32_t VC2_OFFSETT = 0x8000; + static const uint32_t VC3_OFFSETT = 0xC000; + + static const std::string UIO_DEVICE_FILE = "/dev/uio0"; +}; + +#endif /* LINUX_OBC_PTMECONFIG_H_ */ diff --git a/linux/obc/PtmeIF.h b/linux/obc/PtmeIF.h new file mode 100644 index 00000000..c89d25f0 --- /dev/null +++ b/linux/obc/PtmeIF.h @@ -0,0 +1,28 @@ +#ifndef LINUX_OBC_PTMEIF_H_ +#define LINUX_OBC_PTMEIF_H_ + +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + + +/** + * @brief Interface class for managing the PTME IP Core implemented in the programmable logic. + * + * @details PTME IP Core: https://www.esa.int/Enabling_Support/Space_Engineering_Technology/ + * Microelectronics/PTME + * @author J. Meier + */ +class PtmeIF { +public: + virtual ~PtmeIF(){}; + + /** + * @brief Implements to function to write to a specific virtual channel. + * + * @param vcId Virtual channel to write to + * @param data Pointer to buffer holding the data to write + * @param size Number of bytes to write + */ + virtual ReturnValue_t writeToVc(uint8_t vcId, uint8_t* data, size_t size) = 0; +}; + +#endif /* LINUX_OBC_PTMEIF_H_ */ diff --git a/linux/obc/VcInterfaceIF.h b/linux/obc/VcInterfaceIF.h new file mode 100644 index 00000000..ce643b48 --- /dev/null +++ b/linux/obc/VcInterfaceIF.h @@ -0,0 +1,26 @@ +#ifndef LINUX_OBC_VCINTERFACEIF_H_ +#define LINUX_OBC_VCINTERFACEIF_H_ + +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + +/** + * @brief Interface class for managing different virtual channels of the PTME IP core implemented + * in the programmable logic. + * + * @author J. Meier + */ +class VcInterfaceIF { +public: + virtual ~VcInterfaceIF(){}; + + /** + * @brief Implememts the functionality to write data in the virtual channel of the PTME IP + * Core. + * + * @param data Pointer to buffer holding the data to write + * @param size Number of bytes to write + */ + virtual ReturnValue_t write(uint8_t* data, size_t size) = 0; +}; + +#endif /* LINUX_OBC_VCINTERFACEIF_H_ */ diff --git a/mission/tmtc/CCSDSHandler.cpp b/mission/tmtc/CCSDSHandler.cpp new file mode 100644 index 00000000..38428c14 --- /dev/null +++ b/mission/tmtc/CCSDSHandler.cpp @@ -0,0 +1,605 @@ +/******************************* + * FLP Flight Software Framework (FSFW) + * (c) 2016 IRS, Uni Stuttgart + *******************************/ +/* + * CCSDSHandler.cpp + * + * Created on: Feb 9, 2012 + * Author: baetz + */ +#include +#include +#include +#include +#include +#include +#include +#include + +const float CCSDSHandler::DEFAULT_RATES[BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD] = + { CCSDSHandler::DEFAULT_BYTES_PER_SECOND, CCSDSHandler::DEFAULT_BYTES_PER_SECOND, + CCSDSHandler::DEFAULT_BYTES_PER_SECOND, CCSDSHandler::DEFAULT_BYTES_PER_SECOND }; + +CCSDSHandler::CCSDSHandler(object_id_t setObjectId, uint8_t setSwitchId1, + uint8_t setSwitchId2, object_id_t setChannel, uint16_t setSCID, + BoardHandler::DataPoolIds setPool) : + SystemObject(setObjectId), tmPartHealth(setObjectId + 1, 0), commandQueue(), state( + STATE_IDLE), commState(SEND_WRITE), mode(MODE_OFF), submode( + SUBMODE_NONE), boardHandler((uint8_t*) frameBuffer, + sizeof(frameBuffer), setSCID, setPool), dataLinkLayer( + this->frameBuffer, boardHandler.getClcw(), 0, setSCID), frameLength( + 0), channelId(setChannel), memoryHelper(this, &commandQueue), pendingWrite( + false), pendingRead(false), modeHelper(this), healthHelper(this, + setObjectId), parameterHelper(this), powerSwitcher(setSwitchId1, + setSwitchId2), switchOffWasReported(false), fdir(setObjectId, + setChannel) { + memset(this->frameBuffer, 0, sizeof(frameBuffer)); + DataSet mySet; + PoolVector rateRatio( + datapool::DATA_RATE_ASSIGN, &mySet, PoolVariableIF::VAR_WRITE); + for (uint8_t i = 0; i < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++i) { + virtualChannels[i] = new VCGeneration(i, + BoardHandler::TM_PACKETS_PER_CALL_PER_CHANNEL); + rateLimitOverload[i] = 0; + rateRatio[i] = DEFAULT_RATES[i]; + } + mySet.commit(PoolVariableIF::VALID); + //Set real-time channel to high responsiveness. + virtualChannels[0]->setIdlePacketIntervalMs( + VCGeneration::IDLE_INTERVAL_RT_CHANNEL); +} + +CCSDSHandler::~CCSDSHandler() { + for (uint8_t i = 0; i < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++i) { + delete virtualChannels[i]; + } +} + +void CCSDSHandler::printFrameBuffer(void) { + debug << "frame_buffer contains: " << std::endl; + for (uint32_t i = 0; i < this->frameLength; ++i) { + debug << "frame_buffer[" << std::dec << i << "]: " << std::hex + << std::showbase << (uint16_t) this->frameBuffer[i] << std::dec + << std::endl; + } +} + +ReturnValue_t CCSDSHandler::packetProcessing(void) { + rateSet.read(); + const float* usedRateRatios = NULL; + if (rateSet.dataRates.isValid()) { + usedRateRatios = rateSet.dataRates.value; + } else { + usedRateRatios = DEFAULT_RATES; + } + for (uint8_t i = 0; i < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++i) { + //Set current cycle Limit first + uint32_t newLimit = (usedRateRatios[i]) / SENDS_PER_SECOND; + if (newLimit <= rateLimitOverload[i]) { + //Calculate new overload. + rateLimitOverload[i] -= newLimit; + //Use newLimit as current rate, VC is utilized to its current limit. + rateSet.dataRate[i] = usedRateRatios[i]; //equal to newLimit * SENDS_PER_SECOND + //Don't send anything new. + continue; + } + newLimit -= rateLimitOverload[i]; + virtualChannels[i]->tmSendLimitPerCycle = newLimit; + //Send new packets. + uint32_t tempRate = virtualChannels[i]->packetProcessing(); + if (tempRate > virtualChannels[i]->tmSendLimitPerCycle) { + rateLimitOverload[i] = tempRate - virtualChannels[i]->tmSendLimitPerCycle; + //VC is fully utilized, but not more. Overload will be accounted for in next cycles. + rateSet.dataRate[i] = virtualChannels[i]->tmSendLimitPerCycle * SENDS_PER_SECOND; + } else { + //Data rate extended to a Bytes/s range. Filtering is performed in controller. + rateSet.dataRate[i] = tempRate * SENDS_PER_SECOND; + rateLimitOverload[i] = 0; + } + } + rateSet.commit(PoolVariableIF::VALID); + return RETURN_OK; +} + +ReturnValue_t CCSDSHandler::packetProcessingCheck(void) { + ReturnValue_t status = RETURN_FAILED; + for (uint8_t vc = 0; vc < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++vc) { + status = this->virtualChannels[vc]->packetProcessingCheck(); + if (status != RETURN_OK) { + error << "CCSDSHandler " << std::hex << this->getObjectId() + << " ::packetProcessingCheck: Error on VC " << (uint16_t) vc + << ". Error code: " << status << std::dec << std::endl; + } + } + return RETURN_OK; +} + +ReturnValue_t CCSDSHandler::performOperation(void) { + readCommandQueue(); + return RETURN_OK; +} + +void CCSDSHandler::searchFrame() { + frameLength = this->boardHandler.findFrame(); + while (frameLength != 0) { + ReturnValue_t frame_status = this->dataLinkLayer.processFrame( + frameLength); + if (frame_status != RETURN_OK) { + triggerEvent(DataLinkLayer::FRAME_PROCESSING_FAILED, frame_status, + 0); + } + boardHandler.resetFrameBuffer(); + frameLength = boardHandler.findFrame(); + } +} + +ReturnValue_t CCSDSHandler::initialize() { + PtmeIF *ptme = objectManager->get(ptmeId); + if (ptme == nullptr) { + return ObjectManagerIF::CHILD_INIT_FAILED; + } + status = boardHandler.initialize(channel); + if (status != RETURN_OK) { + return status; + } + status = modeHelper.initialize(); + if (status != RETURN_OK) { + return status; + } + status = healthHelper.initialize(); + if (status != HasReturnvaluesIF::RETURN_OK) { + return status; + } + status = memoryHelper.initialize(); + if (status != HasReturnvaluesIF::RETURN_OK) { + return status; + } + status = parameterHelper.initialize(); + if (status != HasReturnvaluesIF::RETURN_OK) { + return status; + } + status = powerSwitcher.initialize(objects::PCDU_HANDLER); + if (status != HasReturnvaluesIF::RETURN_OK) { + return status; + } + StorageManagerIF* tmStore = objectManager->get( + objects::TM_STORE); + status = dataLinkLayer.initialize(); + if ((tmStore != NULL) && status == RETURN_OK) { + status = RETURN_OK; + for (uint8_t i = 0; i < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++i) { +// this->virtualChannels[i] = objectManager->get( +// objects::CCSDS_VC_BASE + i); + if (this->virtualChannels[i] != NULL) { + this->virtualChannels[i]->setPacketStore(tmStore); + this->virtualChannels[i]->setBoardHandler(&this->boardHandler); + } else { + status = RETURN_FAILED; + } + } + } else { + status = RETURN_FAILED; + error << "CCSDSHandler " << std::hex << this->getObjectId() << std::dec + << " ::CCSDSHandler: Configuration failed." << std::endl; + } + status = fdir.initialize(); + if (status != HasReturnvaluesIF::RETURN_OK) { + return status; + } + return status; +} + +void CCSDSHandler::readCommandQueue(void) { + CommandMessage commandMessage; + ReturnValue_t result = RETURN_FAILED; + while (commandQueue.receiveMessage(&commandMessage) == RETURN_OK) { + + result = parameterHelper.handleParameterMessage(&commandMessage); + if (result == RETURN_OK) { + return; + } + CommandMessage reply; + reply.setReplyRejected(CommandMessage::UNKNOW_COMMAND, + commandMessage.getCommand()); + commandQueue.reply(&reply); + } +} + +MessageQueueId_t CCSDSHandler::getCommandQueue() const { + return commandQueue.getId(); +} + +void CCSDSHandler::flushTmChannels() { + for (uint8_t i = 0; i < BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD; + ++i) { + virtualChannels[i]->flush(); + } +} + +ReturnValue_t CCSDSHandler::handleMemoryLoad(uint32_t address, + const uint8_t* data, uint32_t size, uint8_t** dataPointer) { + if (size != 4) { + return INVALID_SIZE; + } + ReturnValue_t result = boardHandler.manualWriteToRegister(address, data); + if (result == RETURN_OK) { + pendingWrite = true; + return DO_IT_MYSELF; + } + return result; +} + +ReturnValue_t CCSDSHandler::handleMemoryDump(uint32_t address, uint32_t size, + uint8_t** dataPointer, uint8_t* dumpTarget) { + if (size != 4) { + return INVALID_SIZE; + } + ReturnValue_t result = boardHandler.sendRegisterReadCommand(address); + if (result == RETURN_OK) { + pendingRead = true; + return DO_IT_MYSELF; + } + return result; +} + +ReturnValue_t CCSDSHandler::checkModeCommand(Mode_t commandedMode, + Submode_t commandedSubmode, uint32_t* msToReachTheMode) { + if (state != STATE_IDLE) { + return IN_TRANSITION; + } + switch (commandedMode) { + case MODE_ON: + if ((commandedSubmode == SUBMODE_ACTIVE) + || (commandedSubmode == SUBMODE_PASSIVE)) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + break; + case MODE_OFF: + if (commandedSubmode == SUBMODE_NONE) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + default: + return INVALID_MODE; + } +} + +void CCSDSHandler::startTransition(Mode_t commandedMode, + Submode_t commandedSubmode) { + if (commandedMode == mode) { + if (mode == MODE_ON) { + state = STATE_SELECT_SUBMODE; + } else { + //Turn off switch again anyway + state = STATE_TURN_OFF; + } + } else { + switch (commandedMode) { + case MODE_ON: + state = STATE_TURN_ON; + break; + case MODE_OFF: + state = STATE_TURN_OFF; + break; + default: + //Error case. Cannot happen? + error << "CCSDSHandler::startTransition: Invalid commanded mode: " + << commandedMode << std::endl; + return; + } + } + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); +} + +void CCSDSHandler::getMode(Mode_t* modeReturn, Submode_t* submodeReturn) { + *modeReturn = mode; + *submodeReturn = submode; +} + +void CCSDSHandler::setToExternalControl() { + healthHelper.setHealth(EXTERNAL_CONTROL); +} + +void CCSDSHandler::announceMode(bool recursive) { + triggerEvent(MODE_INFO, mode, submode); +} + +void CCSDSHandler::setParentQueue(MessageQueueId_t parentQueueId) { + modeHelper.setParentQueue(parentQueueId); + healthHelper.setParentQeueue(parentQueueId); + tmPartHealth.setParentQueue(parentQueueId); +} + +void CCSDSHandler::doStateMachine() { + ReturnValue_t status = RETURN_FAILED; + powerSwitcher.doStateMachine(); + switch (state) { + case STATE_IDLE: + //Do nothing? Do perform operation stuff? + break; + case STATE_TURN_ON: + powerSwitcher.turnOn(); + modeHelper.startTimer(powerSwitcher.getSwitchDelay()); + state = STATE_WAIT_ON; + break; + case STATE_WAIT_ON: + if (powerSwitcher.checkSwitchState() == RETURN_OK) { + modeHelper.startTimer(LINK_UP_DELAY_MS); + state = STATE_WAIT_LINK; + } + if (boardHandler.checkChannel() == RETURN_OK) { + boardHandler.startStateTranstion(); + state = STATE_INITIALIZE_BOARD; + } else { + if (modeHelper.isTimedOut()) { + triggerEvent(MODE_TRANSITION_FAILED, + PowerSwitchIF::SWITCH_TIMEOUT, state); + setMode(MODE_OFF, SUBMODE_NONE); + } + } + break; + case STATE_WAIT_LINK: + if (boardHandler.checkAndResetChannel() == RETURN_OK) { + boardHandler.startStateTranstion(); + state = STATE_INITIALIZE_BOARD; + } else { + if (modeHelper.isTimedOut()) { + triggerEvent(MODE_TRANSITION_FAILED, DeviceHandlerIF::TIMEOUT, + state); + setMode(MODE_OFF, SUBMODE_NONE); + } + } + break; + case STATE_INITIALIZE_BOARD: + status = boardHandler.initializeBoard(); + switch (status) { + case RETURN_OK: + state = STATE_SELECT_SUBMODE; + break; + case BoardHandler::IN_TRANSITION: + //cannot last forever, so just wait. + break; + default: + triggerEvent(MODE_TRANSITION_FAILED, status, state); + state = STATE_TURN_OFF; + break; + } + break; + case STATE_SELECT_SUBMODE: + boardHandler.startStateTranstion(); + if (modeHelper.commandedSubmode == SUBMODE_PASSIVE) { + state = STATE_WAIT_SUBMODE_PASSIVE; + } else { + state = STATE_WAIT_SUBMODE_ACTIVE; + } + break; + case STATE_WAIT_SUBMODE_PASSIVE: + //Just one wait cycle to get rid of pending TM. + state = STATE_DO_PASSIVATE; + break; + case STATE_DO_PASSIVATE: + status = boardHandler.tmPassivate(); + switch (status) { + case RETURN_OK: + setMode(MODE_ON, SUBMODE_PASSIVE); + break; + case BoardHandler::IN_TRANSITION: + //cannot last forever, so just wait. + break; + default: + //If it comes here and fails then, there might be something wrong with the board. + triggerEvent(MODE_TRANSITION_FAILED, status, state); + state = STATE_TURN_OFF; + break; + } + break; + case STATE_WAIT_SUBMODE_ACTIVE: + status = boardHandler.tmActivate(); + switch (status) { + case RETURN_OK: + setMode(MODE_ON, SUBMODE_ACTIVE); + break; + case BoardHandler::IN_TRANSITION: + //cannot last forever, so just wait. + break; + default: + //If it comes here and fails then, there might be something wrong with the board. + triggerEvent(MODE_TRANSITION_FAILED, status, state); + state = STATE_TURN_OFF; + break; + } + break; + case STATE_TURN_OFF: + //Passivate first. Mainly optimization for System Testbed. + boardHandler.startStateTranstion(); + state = STATE_DO_PASSIVATE_OFF; + //No break + case STATE_DO_PASSIVATE_OFF: + status = boardHandler.tmPassivate(); + switch (status) { + case RETURN_OK: + state = STATE_TURN_SWITCH_OFF; + break; + case BoardHandler::IN_TRANSITION: + //cannot last forever, so just wait. + break; + default: + //If it comes here and fails then, there might be something wrong with the board. + //Just turn it off. + state = STATE_TURN_SWITCH_OFF; + break; + } + break; + case STATE_TURN_SWITCH_OFF: + powerSwitcher.turnOff(); + modeHelper.startTimer(powerSwitcher.getSwitchDelay()); + state = STATE_WAIT_OFF; + break; + case STATE_WAIT_OFF: + if ((boardHandler.checkAndResetChannel() == RMAPChannelIF::LINK_DOWN) + || (powerSwitcher.checkSwitchState() == RETURN_OK)) { + setMode(MODE_OFF, SUBMODE_NONE); + state = STATE_IDLE; + } else { + if (modeHelper.isTimedOut()) { + triggerEvent(MODE_TRANSITION_FAILED, status, state); + setMode(mode, submode); + } + } + break; + default: + break; + } +} + +ReturnValue_t CCSDSHandler::addVirtualChannel(uint8_t virtualChannelId, + VirtualChannelReceptionIF* object) { + return this->dataLinkLayer.addVirtualChannel(virtualChannelId, object); +} + +void CCSDSHandler::setMode(Mode_t newMode, Submode_t newSubmode) { + triggerEvent(MODE_INFO, newMode, newSubmode); + if (newMode == MODE_OFF) { + boardHandler.setDataPoolVaraiblesInvalid(); + } + modeHelper.modeChanged(newMode, newSubmode); + state = STATE_IDLE; + mode = newMode; + submode = newSubmode; +} + +Mode_t CCSDSHandler::getMode() const { + return mode; +} + +Submode_t CCSDSHandler::getSubmode() const { + return submode; +} + +MessageQueueId_t CCSDSHandler::getReportReceptionQueue(uint8_t virtualChannel) { + if (virtualChannel < Ptme::NUM_OF_VIRTUAL_CHANNELS) { + return virtualChannels[virtualChannel]->getReportReceptionQueue(); + } else { + sif::debug << "CCSDSHandler::getReportReceptionQueue: Invalid virtual channel requested"; + } +} + +void CCSDSHandler::executeAllWriteCommands() { + ReturnValue_t returnValue = boardHandler.sendWriteCommands(); + if (returnValue != RETURN_OK) { + triggerEvent(DeviceHandlerIF::DEVICE_SENDING_COMMAND_FAILED, + returnValue, 0); + } + if (submode == SUBMODE_ACTIVE) { + returnValue = this->packetProcessing(); + if (returnValue != RETURN_OK) { + triggerEvent(DeviceHandlerIF::DEVICE_SENDING_COMMAND_FAILED, + returnValue, 1); + } + } +} + +void CCSDSHandler::handleAllWriteReplies() { + ReturnValue_t returnValue = boardHandler.getWriteReplys(); + if (returnValue != RETURN_OK) { + triggerEvent(DeviceHandlerIF::DEVICE_SENDING_COMMAND_FAILED, + returnValue, 0); + } + if (pendingWrite) { + memoryHelper.completeLoad(returnValue); + pendingWrite = false; + } + if (submode == SUBMODE_ACTIVE) { + returnValue = packetProcessingCheck(); + } else { + this->flushTmChannels(); + } +} + +void CCSDSHandler::executeAllReadCommands() { + ReturnValue_t returnValue = boardHandler.sendReadCommands(); + if (returnValue != RETURN_OK) { + triggerEvent(DeviceHandlerIF::DEVICE_REQUESTING_REPLY_FAILED, + returnValue, 0); + } +} + +void CCSDSHandler::handleAllReadReplies() { + if ((mode == MODE_ON) && (state == STATE_IDLE)) { + ReturnValue_t returnValue = boardHandler.getReadReplys(); + if (returnValue == RETURN_OK) { + searchFrame(); + } else { + triggerEvent(DeviceHandlerIF::DEVICE_REQUESTING_REPLY_FAILED, + returnValue, 0); + //do nothing. + } + if (pendingRead) { + uint8_t* tempBuffer; + returnValue = boardHandler.getRegisterReadReply(&tempBuffer); + memoryHelper.completeDump(returnValue, tempBuffer, + BoardHandler::REGISTER_LENGTH); + pendingRead = false; + } + } +} + +ReturnValue_t CCSDSHandler::getParameter(uint8_t domainId, uint16_t parameterId, + ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, + uint16_t startAtIndex) { + ReturnValue_t result = fdir.getParameter(domainId, parameterId, + parameterWrapper, newValues, startAtIndex); + if (result != INVALID_DOMAIN_ID) { + return result; + } + if (domainId != DOMAIN_ID_BASE) { + return INVALID_DOMAIN_ID; + } + switch (parameterId) { + case 0: + parameterWrapper->set(boardHandler.tmPhysicalLayerRegisterValue); + break; + case 1: + parameterWrapper->set(boardHandler.tmCodingSublayerRegisterValue); + break; + default: + return INVALID_MATRIX_ID; + } + return RETURN_OK; +} + +ReturnValue_t CCSDSHandler::setHealth(HealthState health) { + healthHelper.setHealth(health); + return RETURN_OK; +} + +HasHealthIF::HealthState CCSDSHandler::getHealth() { + return healthHelper.getHealth(); +} + +CCSDSHandler::DataRateSet::DataRateSet() : + ControllerSet(), dataRate(datapool::VC_DATA_RATE_RAW, this, + PoolVariableIF::VAR_WRITE), dataRates( + datapool::DATA_RATE_ASSIGN, this, PoolVariableIF::VAR_READ) { +} + +CCSDSHandler::DataRateSet::~DataRateSet() { +} + +void CCSDSHandler::DataRateSet::setToDefault() { + dataRate.value = {0.0, 0.0, 0.0, 0.0}; +} + +void CCSDSHandler::triggerEvent(Event event, uint32_t parameter1, + uint32_t parameter2) { + fdir.triggerEvent(event, parameter1, parameter2); +} diff --git a/mission/tmtc/CCSDSHandler.h b/mission/tmtc/CCSDSHandler.h new file mode 100644 index 00000000..77c8b2c1 --- /dev/null +++ b/mission/tmtc/CCSDSHandler.h @@ -0,0 +1,215 @@ +#ifndef CCSDSHANDLER_H_ +#define CCSDSHANDLER_H_ + +#include "fsfw/serviceinterface/ServiceInterface.h" +#include "fsfw/serviceinterface/serviceInterfaceDefintions.h" +#include "fsfw/objectmanager/SystemObject.h" +#include "fsfw/tasks/ExecutableObjectIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include "fsfw/parameters/ParameterHelper.h" + +/** + * @brief This class handles the data exchange with the CCSDS IP cores implemented in the + * programmable logic of the Q7S. + * + * @author J. Meier + */ +class CCSDSHandler: public SystemObject, + public ExecutableObjectIF, + public HasModesIF, + public AcceptsTelemetryIF, + public HasReturnvaluesIF, + public ReceivesParameterMessagesIF { +public: + static const uint32_t LINK_UP_DELAY_MS = 2000; //!< The maximum time it takes to reconfigure the CCSDS Board. + static const uint32_t MAX_FRAME_SIZE = 1024; + static const Submode_t SUBMODE_ACTIVE = 1; //!< Submode where the TM part of the board is on. + static const Submode_t SUBMODE_PASSIVE = 0; //!< Submode where the TM part of the board is off. + + static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CCSDS_BOARD; + static const Event CCSDS_BOARD_RESET_FAILED = MAKE_EVENT(0, SEVERITY::LOW); //!> Resetting the communication channel failed. Severity LOW, par1: returnCode, par2: 0 + static const Event CCSDS_BOARD_SWITCHED = MAKE_EVENT(1, SEVERITY::INFO); //!> Switched active CCSDS-Board. Par1: objectId of now active board, Par2: objectId of now passive board. + + static const ActionId_t SET_DATA_RATE_RATIO = 1; + static const float SENDS_PER_SECOND = 2.5; + static const uint32_t MINIMUM_BYTES_PER_SECOND = + (VCGeneration::IDLE_PACKET_SIZE + / (VCGeneration::DEFAULT_IDLE_INTERVAL / 1000)) + 1; //!< +1 to be on the safe side. + static const float DEFAULT_RATES[BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD]; + static const uint32_t DEFAULT_BYTES_PER_SECOND = 1300; + + HealthDevice tmPartHealth; //!< A helper device to dedicatetly set the telemetry part to not healthy. + /** + * Main Constructor of the class. + * Initializes all attributes with default values. Sets the frame buffer to zero. + * Creates all TM Virtual Channels (with \c new). Initialization of communication partners + * is done in the #initialize routine. + * @param setObjectId Object identifier of the class. + * @param setSwitchId1 Switch id of the first switch of the board. + * @param setSwitchId2 Switch id of the second switch of the board. + * @param set_channel RMAP channel identifier. This is forwarded to the #boardHandler class. + * @param set_scid Configured SpaceCraft Identifier. Is forwarded to #boardHandler and #dataLinkLayer class. + */ + CCSDSHandler(object_id_t setObjectId, uint8_t setSwitchId1, + uint8_t setSwitchId2, object_id_t setChannel, uint16_t setSCID, + BoardHandler::DataPoolIds setPool); + /** + * The destructor deletes all TM Virtual Channels. + */ + ~CCSDSHandler(); + /** + * Main executing method of the CCSDS Board handling. + * The method coordinates reading and writing of buffers and registers on the CCSDS Board + * as well as checking communication states and looking for frames. The state machine is + * executed here as well. To ensure a smooth communication without much delay, the last read + * and write calls are checked in the beginning and new requests as well as the state machine + * are issued in the end. + * @return Always returns #RETURN_OK. + */ + ReturnValue_t performOperation(void); + + ReturnValue_t handleMemoryLoad(uint32_t address, const uint8_t* data, + uint32_t size, uint8_t** dataPointer); + ReturnValue_t handleMemoryDump(uint32_t address, uint32_t size, + uint8_t** dataPointer, uint8_t* dumpTarget); + /** + * Initialization routine of the class. + * It initializes connections to the power unit and to the Telemetry store which is set for + * every TM Virtual Channel. + * @return + */ + ReturnValue_t initialize(); + MessageQueueId_t getCommandQueue() const; + /** + * Method to configure the TC Virtual Channels in the TC Data Link Layer. + * The call is forwarded to the #dataLinkLayer class. + * @param virtualChannelId VCID to add. + * @param object Pointer to the Virtual Channel object that is added. + * @return See the #dataLinkLayer class. + */ + ReturnValue_t addVirtualChannel(uint8_t virtualChannelId, + VirtualChannelReceptionIF* object); + + Mode_t getMode() const; + Submode_t getSubmode() const; + void setParentQueue(MessageQueueId_t parentQueueId); + MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0); + ReturnValue_t setHealth(HealthState health); + HasHealthIF::HealthState getHealth(); + ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, + ParameterWrapper *parameterWrapper, + const ParameterWrapper *newValues, uint16_t startAtIndex); + void triggerEvent(Event event, uint32_t parameter1 = 0, + uint32_t parameter2 = 0); +protected: + MessageQueue commandQueue; //!< Queue to receive control commands. + VCGeneration* virtualChannels[BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD];//!< An array of VCGeneration classes which each manage on TM Virtual Channel. + uint32_t rateLimitOverload[BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD];//!< Used to smooth data rate in case large packets are sent. + /** + * This method reads the command queue and initializes a state transition if a command of this type was found. + */ + void readCommandQueue(void); + + /** + * This method flushes all pending messages in the TM queue by calling the \c flush method of + * all #virtualChannels. + * This is necessary to avoid doubled TM packets if the TM sender issues its messages to both the + * nominal and redundant CCSDS Board. + */ + void flushTmChannels(); + /** + * This is a helper method to print the current content of the frame buffers. + */ + void printFrameBuffer(void); + /** + * This is an important method which triggers searching for and handling found frames. + * It first calls the boardHandler method to find a frame in the received data. If a + * well-formed frame was found it calls the #dataLinkLayer class to process the frame + */ + void searchFrame(); + /** + * Calls the packet processing routine of each TM Virtual Channel. + * This triggers reception of incoming packets from the OBSW and forwarding to the CCSDS Board. + * There are different sending strategies for forwarding the packets to the board. + * @return Returns #RETURN_OK or the status of the failed virtual channel method. + */ + ReturnValue_t packetProcessing(void); + /** + * With this method the successful forwarding of Telemetry to the TM Virtual Channels is checked. + * The method calls the check routine of each virtual channel. + * @return Returns #RETURN_OK or the status of the failed virtual channel method. + */ + ReturnValue_t packetProcessingCheck(void); + + ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode); + void startTransition(Mode_t mode, Submode_t submode); + void getMode(Mode_t *mode, Submode_t *submode); + void setToExternalControl(); + void announceMode(bool recursive); +private: + enum CcsdsState_t { + STATE_IDLE, + STATE_TURN_ON, + STATE_TURN_OFF, + STATE_DO_PASSIVATE_OFF, + STATE_TURN_SWITCH_OFF, + STATE_WAIT_ON, + STATE_WAIT_LINK, + STATE_WAIT_OFF, + STATE_INITIALIZE_BOARD, + STATE_SELECT_SUBMODE, + STATE_WAIT_SUBMODE_ACTIVE, + STATE_DO_PASSIVATE, + STATE_WAIT_SUBMODE_PASSIVE, + STATE_HANDLE_STARTUP, + }; + CcsdsState_t state; + enum CommState { + SEND_WRITE, GET_WRITE, SEND_READ, GET_READ + }; + CommState commState; + Mode_t mode; + Submode_t submode; + BoardHandler boardHandler;//!< The class that handles the low-level communication with the board. + DataLinkLayer dataLinkLayer;//!< The class that manages the TC Data Link Layer protocols. + uint8_t frameBuffer[MAX_FRAME_SIZE]; //!< The main buffer to store single frames in. + uint16_t frameLength; //!< Indicates the current length of the found frame. + object_id_t channelId; //!< Defines the id of the RMAP Channel to use. + MemoryHelper memoryHelper; //!< Helps handling memory messages. + + bool pendingWrite; //!< Attribute to manage remote Memory write. + + bool pendingRead; //!< Attribute to manage remote Memory read. + + ModeHelper modeHelper; //!< Helps handling mode messages. + HealthHelper healthHelper; //!< Helps setting the health information correctly. + ParameterHelper parameterHelper; + PowerSwitcher powerSwitcher; //!< Helps switching switches on and off. + + bool switchOffWasReported; //!< To avoid reporting SWITCH_WENT_OFF multiple times. + + CCSDSBoardFailureIsolation fdir; + class DataRateSet: public ControllerSet { + public: + DataRateSet(); + virtual ~DataRateSet(); + void setToDefault(); + PoolVector dataRate; + PoolVector dataRates; + }; + DataRateSet rateSet; + void setMode(Mode_t newMode, Submode_t newSubmode); //!< Method to safely setMode and inform everyone interested about it. + /** + * State machine of the Handler. + * Is responsible for handling mode transitions and for informing the modeHelper. + * In addition, it watches the SpW link and changes the Mode in case a link down or a link up is detected. + */ + void doStateMachine(); + void executeAllWriteCommands(); + void handleAllWriteReplies(); + void executeAllReadCommands(); + void handleAllReadReplies(); +}; + +#endif /* CCSDSHANDLER_H_ */ diff --git a/mission/utility/TmFunnel.cpp b/mission/utility/TmFunnel.cpp index c52fa6f9..e5de3ef7 100644 --- a/mission/utility/TmFunnel.cpp +++ b/mission/utility/TmFunnel.cpp @@ -1,3 +1,4 @@ +#include "OBSWConfig.h" #include #include #include @@ -97,7 +98,13 @@ ReturnValue_t TmFunnel::initialize() { "properly and implements AcceptsTelemetryIF" << std::endl; return ObjectManagerIF::CHILD_INIT_FAILED; } + +#if OBSW_USE_PTME_IP_CORE == 1 + // Live TM will be sent via the virtual channel 0 + tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue(0)); +#else tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue()); +#endif /* OBSW_USE_CCSDS_IP_CORES == 1 */ // Storage destination is optional. if(storageDestination == objects::NO_OBJECT) {