v1.9.0 #175
@ -1,3 +1,4 @@
|
||||
#include <linux/obc/Ptme.h>
|
||||
#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 <linux/obc/Ptme.h>
|
||||
#include <linux/obc/PapbVcInterface.h>
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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,
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <linux/obc/CCSDSIPCoreBridge.h>
|
||||
#include <linux/obc/Ptme.h>
|
||||
|
||||
CCSDSIPCoreBridge::CCSDSIPCoreBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId, LinuxLibgpioIF* gpioComIF,
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
target_sources(${TARGET_NAME} PUBLIC
|
||||
CCSDSIPCoreBridge.cpp
|
||||
PapbVcInterface.cpp
|
||||
Ptme.cpp
|
||||
)
|
||||
|
||||
|
||||
|
104
linux/obc/PapbVcInterface.cpp
Normal file
104
linux/obc/PapbVcInterface.cpp
Normal 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
106
linux/obc/PapbVcInterface.h
Normal 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
68
linux/obc/Ptme.cpp
Normal 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
84
linux/obc/Ptme.h
Normal 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
26
linux/obc/PtmeConfig.h
Normal 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
28
linux/obc/PtmeIF.h
Normal 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
26
linux/obc/VcInterfaceIF.h
Normal 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_ */
|
605
mission/tmtc/CCSDSHandler.cpp
Normal file
605
mission/tmtc/CCSDSHandler.cpp
Normal 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
215
mission/tmtc/CCSDSHandler.h
Normal 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_ */
|
@ -1,3 +1,4 @@
|
||||
#include "OBSWConfig.h"
|
||||
#include <fsfw/ipc/QueueFactory.h>
|
||||
#include <fsfw/tmtcpacket/pus/tm.h>
|
||||
#include <fsfw/objectmanager/ObjectManager.h>
|
||||
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user