ptme handling wip

This commit is contained in:
Jakob Meier 2021-09-19 12:27:48 +02:00
parent 4b5f22f013
commit ddd8ff2180
18 changed files with 1312 additions and 6 deletions

View File

@ -1,3 +1,4 @@
#include <linux/obc/Ptme.h>
#include "ObjectFactory.h" #include "ObjectFactory.h"
#include "OBSWConfig.h" #include "OBSWConfig.h"
#include "devConf.h" #include "devConf.h"
@ -23,8 +24,6 @@
#include "linux/devices/SusHandler.h" #include "linux/devices/SusHandler.h"
#include "linux/csp/CspCookie.h" #include "linux/csp/CspCookie.h"
#include "linux/csp/CspComIF.h" #include "linux/csp/CspComIF.h"
#include "linux/obc/CCSDSIPCoreBridge.h"
#include "mission/core/GenericFactory.h" #include "mission/core/GenericFactory.h"
#include "mission/devices/PDU1Handler.h" #include "mission/devices/PDU1Handler.h"
#include "mission/devices/PDU2Handler.h" #include "mission/devices/PDU2Handler.h"
@ -83,6 +82,9 @@
#include "linux/boardtest/LibgpiodTest.h" #include "linux/boardtest/LibgpiodTest.h"
#endif #endif
#include <linux/obc/Ptme.h>
#include <linux/obc/PapbVcInterface.h>
ResetArgs resetArgsGnss0; ResetArgs resetArgsGnss0;
ResetArgs resetArgsGnss1; ResetArgs resetArgsGnss1;
@ -171,6 +173,26 @@ void ObjectFactory::produce(void* args){
#endif /* TE7020 != 0 */ #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 #if OBSW_USE_TMTC_TCP_BRIDGE == 0
auto udpBridge = new UdpTmTcBridge(objects::TMTC_BRIDGE, objects::CCSDS_PACKET_DISTRIBUTOR); auto udpBridge = new UdpTmTcBridge(objects::TMTC_BRIDGE, objects::CCSDS_PACKET_DISTRIBUTOR);
new UdpTcPollingTask(objects::TMTC_POLLING_TASK, objects::TMTC_BRIDGE); 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 #if BOARD_TE0720 == 1 && OBSW_TEST_CCSDS_BRIDGE == 1
GpioCookie* gpioCookieCcsdsIp = new GpioCookie; 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); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_BUSY_N, papbBusyN);
GpiodRegular* papbEmpty = new GpiodRegular(std::string("gpiochip0"), 1, GpiodRegular* papbEmpty = new GpiodRegular(std::string("gpiochip0"), 1,
std::string("Chip Select Sus Sensor")); std::string("PAPBEmpty_VC0"));
gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_EMPTY, papbEmpty); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_EMPTY, papbEmpty);
gpioComIF->addGpios(gpioCookieCcsdsIp); gpioComIF->addGpios(gpioCookieCcsdsIp);

View File

@ -19,6 +19,7 @@ enum commonClassIds: uint8_t {
PLOC_SUPERVISOR_HANDLER, //PLSV PLOC_SUPERVISOR_HANDLER, //PLSV
SUS_HANDLER, //SUSS SUS_HANDLER, //SUSS
CCSDS_IP_CORE_BRIDGE, //IPCI CCSDS_IP_CORE_BRIDGE, //IPCI
PTME, //PTME
PLOC_UPDATER, //PLUD PLOC_UPDATER, //PLUD
GOM_SPACE_HANDLER, //GOMS GOM_SPACE_HANDLER, //GOMS
PLOC_MEMORY_DUMPER, //PLMEMDUMP PLOC_MEMORY_DUMPER, //PLMEMDUMP

View File

@ -6,5 +6,12 @@
// Use TCP instead of UDP for the TMTC bridge. This allows using the TMTC client locally // 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 // because UDP packets are not allowed in the VPN
#define OBSW_USE_TMTC_TCP_BRIDGE 1 #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_ */ #endif /* COMMON_CONFIG_COMMONCONFIG_H_ */

View File

@ -11,6 +11,11 @@ enum commonObjects: uint32_t {
TMTC_BRIDGE = 0x50000300, TMTC_BRIDGE = 0x50000300,
TMTC_POLLING_TASK = 0x50000400, TMTC_POLLING_TASK = 0x50000400,
FILE_SYSTEM_HANDLER = 0x50000500, FILE_SYSTEM_HANDLER = 0x50000500,
PTME = 0x50000600,
PAPB_VC0 = 0x50000700,
PAPB_VC0 = 0x500007001,
PAPB_VC0 = 0x500007002,
PAPB_VC0 = 0x500007003,
/* 0x43 ('C') for Controllers */ /* 0x43 ('C') for Controllers */
THERMAL_CONTROLLER = 0x43400001, THERMAL_CONTROLLER = 0x43400001,

View File

@ -1,7 +1,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
#include <linux/obc/CCSDSIPCoreBridge.h> #include <linux/obc/Ptme.h>
CCSDSIPCoreBridge::CCSDSIPCoreBridge(object_id_t objectId, object_id_t tcDestination, CCSDSIPCoreBridge::CCSDSIPCoreBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId, LinuxLibgpioIF* gpioComIF, object_id_t tmStoreId, object_id_t tcStoreId, LinuxLibgpioIF* gpioComIF,

View File

@ -96,6 +96,7 @@ namespace config {
/* Add mission configuration flags here */ /* Add mission configuration flags here */
static constexpr uint32_t OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE = 50; static constexpr uint32_t OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE = 50;
static constexpr uint32_t PLOC_UPDATER_QUEUE_SIZE = 50; static constexpr uint32_t PLOC_UPDATER_QUEUE_SIZE = 50;
static constexpr uint8_t NUMBER_OF_VIRTUAL_CHANNELS = 4;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,5 +1,6 @@
target_sources(${TARGET_NAME} PUBLIC target_sources(${TARGET_NAME} PUBLIC
CCSDSIPCoreBridge.cpp PapbVcInterface.cpp
Ptme.cpp
) )

View File

@ -0,0 +1,104 @@
#include <linux/obc/PapbVcInterface.h>
#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<uint32_t>(*(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<uint8_t>(idx & 0xFF);
}
ReturnValue_t result = sendTm(testPacket, 1105);
if(result != RETURN_OK) {
return result;
}
return RETURN_OK;
}

106
linux/obc/PapbVcInterface.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef LINUX_OBC_PAPBVCINTERFACE_H_
#define LINUX_OBC_PAPBVCINTERFACE_H_
#include "OBSWConfig.h"
#include "linux/obc/VcInterfaceIF.h"
#include <fsfw_hal/common/gpio/gpioDefinitions.h>
#include <fsfw_hal/linux/gpio/LinuxLibgpioIF.h>
#include <linux/obc/PapbVcInterface.h>
#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_ */

68
linux/obc/Ptme.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/obc/Ptme.h>
#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<uint32_t*>(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<unsigned int>(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;
}
}

84
linux/obc/Ptme.h Normal file
View File

@ -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 <fsfw_hal/common/gpio/gpioDefinitions.h>
#include <fsfw_hal/linux/gpio/LinuxLibgpioIF.h>
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include <cstring>
#include <unordered_map>
/**
* @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<VcId_t, VcInterfaceIF*>;
using VcInterfaceMapIter = VcInterfaceMap::iterator;
VcInterfaceMap vcInterfaceMap;
};
#endif /* LINUX_OBC_PTME_H_ */

26
linux/obc/PtmeConfig.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef LINUX_OBC_PTMECONFIG_H_
#define LINUX_OBC_PTMECONFIG_H_
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include <cstring>
/**
* @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_ */

28
linux/obc/PtmeIF.h Normal file
View File

@ -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_ */

26
linux/obc/VcInterfaceIF.h Normal file
View File

@ -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_ */

View File

@ -0,0 +1,605 @@
/*******************************
* FLP Flight Software Framework (FSFW)
* (c) 2016 IRS, Uni Stuttgart
*******************************/
/*
* CCSDSHandler.cpp
*
* Created on: Feb 9, 2012
* Author: baetz
*/
#include <framework/devicehandlers/DeviceHandlerIF.h>
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/rmap/RMAPChannelIF.h>
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include <framework/tmtcservices/AcceptsTelecommandsIF.h>
#include <mission/obc/ccsdsboard/CCSDSHandler.h>
#include <math.h>
#include <config/datapool/dataPoolInit.h>
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<float, BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD> 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<PtmeIF>(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<StorageManagerIF>(
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<VCGeneration>(
// 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);
}

215
mission/tmtc/CCSDSHandler.h Normal file
View File

@ -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<float, BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD> dataRate;
PoolVector<float, BoardHandler::USED_VIRTUAL_CHANNELS_PER_BOARD> 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_ */

View File

@ -1,3 +1,4 @@
#include "OBSWConfig.h"
#include <fsfw/ipc/QueueFactory.h> #include <fsfw/ipc/QueueFactory.h>
#include <fsfw/tmtcpacket/pus/tm.h> #include <fsfw/tmtcpacket/pus/tm.h>
#include <fsfw/objectmanager/ObjectManager.h> #include <fsfw/objectmanager/ObjectManager.h>
@ -97,7 +98,13 @@ ReturnValue_t TmFunnel::initialize() {
"properly and implements AcceptsTelemetryIF" << std::endl; "properly and implements AcceptsTelemetryIF" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED; 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()); tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue());
#endif /* OBSW_USE_CCSDS_IP_CORES == 1 */
// Storage destination is optional. // Storage destination is optional.
if(storageDestination == objects::NO_OBJECT) { if(storageDestination == objects::NO_OBJECT) {