clean up op done
This commit is contained in:
3
mission/payload/CMakeLists.txt
Normal file
3
mission/payload/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(
|
||||
${LIB_EIVE_MISSION} PRIVATE PayloadPcduHandler.cpp RadiationSensorHandler.cpp
|
||||
ScexDeviceHandler.cpp scexHelpers.cpp defs.cpp)
|
825
mission/payload/PayloadPcduHandler.cpp
Normal file
825
mission/payload/PayloadPcduHandler.cpp
Normal file
@ -0,0 +1,825 @@
|
||||
#include <fsfw/src/fsfw/datapool/PoolReadGuard.h>
|
||||
#include <mission/payload/PayloadPcduHandler.h>
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
|
||||
#ifdef XIPHOS_Q7S
|
||||
#include <fsfw_hal/linux/UnixFileGuard.h>
|
||||
#include <fsfw_hal/linux/spi/SpiComIF.h>
|
||||
#include <fsfw_hal/linux/spi/SpiCookie.h>
|
||||
#include <fsfw_hal/linux/utility.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include "devices/gpioIds.h"
|
||||
|
||||
PayloadPcduHandler::PayloadPcduHandler(object_id_t objectId, object_id_t comIF, CookieIF* cookie,
|
||||
GpioIF* gpioIF, SdCardMountedIF* sdcMan,
|
||||
Stack5VHandler& stackHandler, bool periodicPrintout)
|
||||
: DeviceHandlerBase(objectId, comIF, cookie),
|
||||
adcSet(this),
|
||||
stackHandler(stackHandler),
|
||||
periodicPrintout(periodicPrintout),
|
||||
gpioIF(gpioIF),
|
||||
sdcMan(sdcMan) {}
|
||||
|
||||
void PayloadPcduHandler::doStartUp() {
|
||||
if (state > States::STACK_5V_CORRECT) {
|
||||
// Config error
|
||||
sif::error << "PayloadPcduHandler::doStartUp: Invalid state" << std::endl;
|
||||
}
|
||||
clearSetOnOffFlag = true;
|
||||
if (state == States::PL_PCDU_OFF) {
|
||||
state = States::STACK_5V_SWITCHING;
|
||||
}
|
||||
if (state == States::STACK_5V_SWITCHING) {
|
||||
ReturnValue_t retval = stackHandler.deviceToOn(StackCommander::PL_PCDU, true);
|
||||
if (retval == BUSY) {
|
||||
return;
|
||||
}
|
||||
state = States::STACK_5V_PENDING;
|
||||
}
|
||||
if (state == States::STACK_5V_PENDING) {
|
||||
if (stackHandler.isSwitchOn()) {
|
||||
state = States::STACK_5V_CORRECT;
|
||||
}
|
||||
}
|
||||
if (state == States::STACK_5V_CORRECT) {
|
||||
quickTransitionAlreadyCalled = false;
|
||||
setMode(_MODE_TO_ON);
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::doShutDown() {
|
||||
if (not quickTransitionAlreadyCalled) {
|
||||
quickTransitionBackToOff(false, false);
|
||||
quickTransitionAlreadyCalled = true;
|
||||
}
|
||||
if (clearSetOnOffFlag) {
|
||||
std::memset(adcSet.processed.value, 0, adcSet.processed.getSerializedSize());
|
||||
clearSetOnOffFlag = false;
|
||||
}
|
||||
ReturnValue_t retval = stackHandler.deviceToOff(StackCommander::PL_PCDU, true);
|
||||
if (retval == BUSY) {
|
||||
return;
|
||||
}
|
||||
state = States::PL_PCDU_OFF;
|
||||
// No need to set mode _MODE_POWER_DOWN, power switching was already handled
|
||||
setMode(MODE_OFF);
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) {
|
||||
if (getMode() == _MODE_TO_NORMAL) {
|
||||
stateMachineToNormal(modeFrom, subModeFrom);
|
||||
return;
|
||||
} else if (getMode() == _MODE_TO_ON and modeFrom == MODE_NORMAL) {
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1);
|
||||
state = States::STACK_5V_CORRECT;
|
||||
}
|
||||
DeviceHandlerBase::doTransition(modeFrom, subModeFrom);
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_t subModeFrom) {
|
||||
using namespace plpcdu;
|
||||
bool doFinish = true;
|
||||
if (((getSubmode() >> SOLID_STATE_RELAYS_ADC_ON) & 0b1) == 1) {
|
||||
if (state == States::PL_PCDU_OFF) {
|
||||
sif::error << "PayloadPcduHandler::stateMachineToNormal: Unexpected state PL_PCDU_OFF"
|
||||
<< "detected" << std::endl;
|
||||
setMode(MODE_OFF);
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
if (state == States::STACK_5V_CORRECT) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::info << "Switching on SSR VBAT0 & VBAT1 GPIOs" << std::endl;
|
||||
#endif
|
||||
// Switch on relays here
|
||||
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT0);
|
||||
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT1);
|
||||
state = States::ON_TRANS_SSR;
|
||||
transitionOk = true;
|
||||
doFinish = false;
|
||||
}
|
||||
if (state == States::ON_TRANS_SSR) {
|
||||
// If necessary, check whether a certain amount of time has elapsed
|
||||
if (transitionOk) {
|
||||
transitionOk = false;
|
||||
state = States::ON_TRANS_ADC_CLOSE_ZERO;
|
||||
adcCountdown.setTimeout(50);
|
||||
adcCountdown.resetTimer();
|
||||
adcState = AdcStates::BOOT_DELAY;
|
||||
doFinish = false;
|
||||
// If the values are not close to zero, we should not allow transition
|
||||
monMode = MonitoringMode::CLOSE_TO_ZERO;
|
||||
}
|
||||
}
|
||||
if (state == States::ON_TRANS_ADC_CLOSE_ZERO) {
|
||||
if (adcState == AdcStates::BOOT_DELAY) {
|
||||
doFinish = false;
|
||||
if (adcCountdown.hasTimedOut()) {
|
||||
adcState = AdcStates::SEND_SETUP;
|
||||
adcCmdExecuted = false;
|
||||
}
|
||||
}
|
||||
if (adcState == AdcStates::SEND_SETUP) {
|
||||
if (adcCmdExecuted) {
|
||||
adcState = AdcStates::NORMAL;
|
||||
doFinish = true;
|
||||
adcCountdown.setTimeout(100);
|
||||
adcCountdown.resetTimer();
|
||||
adcCmdExecuted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto switchHandler = [&](NormalSubmodeBits bit, gpioId_t id, std::string info) {
|
||||
if (((diffMask >> bit) & 1) == 1) {
|
||||
if (((getSubmode() >> bit) & 1) == 1) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::info << "Enabling PL PCDU " << info << " module" << std::endl;
|
||||
#endif
|
||||
// Switch on DRO and start monitoring for negative voltages
|
||||
updateSwitchGpio(id, gpio::Levels::HIGH);
|
||||
} else {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::info << "Disabling PL PCDU " << info << " module" << std::endl;
|
||||
#endif
|
||||
updateSwitchGpio(id, gpio::Levels::LOW);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// sif::debug << "DIFF MASK: " << (int)diffMask << std::endl;
|
||||
|
||||
// No handling for the SSRs: If those are pulled low, the ADC is off
|
||||
// and normal mode does not really make sense anyway
|
||||
switchHandler(DRO_ON, gpioIds::PLPCDU_ENB_DRO, "DRO");
|
||||
switchHandler(X8_ON, gpioIds::PLPCDU_ENB_X8, "X8");
|
||||
switchHandler(TX_ON, gpioIds::PLPCDU_ENB_TX, "TX");
|
||||
switchHandler(MPA_ON, gpioIds::PLPCDU_ENB_MPA, "MPA");
|
||||
switchHandler(HPA_ON, gpioIds::PLPCDU_ENB_HPA, "HPA");
|
||||
if (doFinish) {
|
||||
setMode(MODE_NORMAL);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
|
||||
switch (adcState) {
|
||||
case (AdcStates::SEND_SETUP): {
|
||||
*id = plpcdu::SETUP_CMD;
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
case (AdcStates::NORMAL): {
|
||||
*id = plpcdu::READ_WITH_TEMP_EXT;
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NOTHING_TO_SEND;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
|
||||
if (adcState == AdcStates::SEND_SETUP) {
|
||||
*id = plpcdu::SETUP_CMD;
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
if (getMode() == _MODE_TO_NORMAL) {
|
||||
return buildNormalDeviceCommand(id);
|
||||
}
|
||||
return NOTHING_TO_SEND;
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::updateSwitchGpio(gpioId_t id, gpio::Levels level) {
|
||||
if (level == gpio::Levels::HIGH) {
|
||||
gpioIF->pullHigh(id);
|
||||
} else {
|
||||
gpioIF->pullLow(id);
|
||||
}
|
||||
adcCountdown.setTimeout(100);
|
||||
adcCountdown.resetTimer();
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::fillCommandAndReplyMap() {
|
||||
insertInCommandAndReplyMap(plpcdu::READ_CMD, 2, &adcSet);
|
||||
insertInCommandAndReplyMap(plpcdu::READ_TEMP_EXT, 1, &adcSet);
|
||||
insertInCommandAndReplyMap(plpcdu::READ_WITH_TEMP_EXT, 1, &adcSet);
|
||||
insertInCommandAndReplyMap(plpcdu::SETUP_CMD, 1);
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
|
||||
const uint8_t* commandData,
|
||||
size_t commandDataLen) {
|
||||
switch (deviceCommand) {
|
||||
case (plpcdu::SETUP_CMD): {
|
||||
cmdBuf[0] = plpcdu::SETUP_BYTE;
|
||||
rawPacket = cmdBuf.data();
|
||||
rawPacketLen = 1;
|
||||
break;
|
||||
}
|
||||
case (plpcdu::READ_CMD): {
|
||||
max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, rawPacketLen);
|
||||
rawPacket = cmdBuf.data();
|
||||
break;
|
||||
}
|
||||
case (plpcdu::READ_TEMP_EXT): {
|
||||
max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data(), rawPacketLen);
|
||||
rawPacket = cmdBuf.data();
|
||||
break;
|
||||
}
|
||||
case (plpcdu::READ_WITH_TEMP_EXT): {
|
||||
size_t sz = 0;
|
||||
max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, sz);
|
||||
max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data() + sz, sz);
|
||||
rawPacketLen = sz;
|
||||
rawPacket = cmdBuf.data();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::scanForReply(const uint8_t* start, size_t remainingSize,
|
||||
DeviceCommandId_t* foundId, size_t* foundLen) {
|
||||
// SPI is full duplex
|
||||
*foundId = getPendingCommand();
|
||||
*foundLen = remainingSize;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::interpretDeviceReply(DeviceCommandId_t id,
|
||||
const uint8_t* packet) {
|
||||
using namespace plpcdu;
|
||||
switch (id) {
|
||||
case (SETUP_CMD): {
|
||||
if (getMode() == _MODE_TO_NORMAL) {
|
||||
adcCmdExecuted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (READ_TEMP_EXT): {
|
||||
uint8_t tempStartIdx = TEMP_REPLY_SIZE - 2;
|
||||
adcSet.tempC.value =
|
||||
max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]);
|
||||
break;
|
||||
}
|
||||
case (READ_CMD): {
|
||||
PoolReadGuard pg(&adcSet);
|
||||
if (pg.getReadResult() != returnvalue::OK) {
|
||||
return pg.getReadResult();
|
||||
}
|
||||
handleExtConvRead(packet);
|
||||
checkAdcValues();
|
||||
adcSet.setValidity(true, true);
|
||||
handlePrintout();
|
||||
break;
|
||||
}
|
||||
case (READ_WITH_TEMP_EXT): {
|
||||
PoolReadGuard pg(&adcSet);
|
||||
if (pg.getReadResult() != returnvalue::OK) {
|
||||
return pg.getReadResult();
|
||||
}
|
||||
handleExtConvRead(packet);
|
||||
uint8_t tempStartIdx = ADC_REPLY_SIZE + TEMP_REPLY_SIZE - 2;
|
||||
adcSet.tempC.value =
|
||||
max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]);
|
||||
checkAdcValues();
|
||||
adcSet.setValidity(true, true);
|
||||
handlePrintout();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
uint32_t PayloadPcduHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) {
|
||||
// 20 minutes transition delay is allowed
|
||||
return 20 * 60 * 1000;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) {
|
||||
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::CHANNEL_VEC, &channelValues);
|
||||
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::PROCESSED_VEC, &processedValues);
|
||||
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::TEMP, &tempC);
|
||||
poolManager.subscribeForDiagPeriodicPacket(
|
||||
subdp::DiagnosticsHkPeriodicParams(adcSet.getSid(), false, 5.0));
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::setToGoToNormalModeImmediately(bool enable) {
|
||||
this->goToNormalMode = enable;
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::handleExtConvRead(const uint8_t* bufStart) {
|
||||
for (uint8_t idx = 0; idx < 12; idx++) {
|
||||
adcSet.channels[idx] = bufStart[idx * 2 + 1] << 8 | bufStart[idx * 2 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::handlePrintout() {
|
||||
using namespace plpcdu;
|
||||
if (periodicPrintout) {
|
||||
if (opDivider.checkAndIncrement()) {
|
||||
sif::info << "PL PCDU ADC hex [" << std::setfill('0') << std::hex;
|
||||
for (uint8_t idx = 0; idx < 12; idx++) {
|
||||
sif::info << std::setw(3) << adcSet.channels[idx];
|
||||
if (idx < 11) {
|
||||
sif::info << ",";
|
||||
}
|
||||
}
|
||||
sif::info << "] | T[C] " << std::dec << adcSet.tempC.value << std::endl;
|
||||
sif::info << "Neg V: " << adcSet.processed[U_NEG_V_FB] << std::endl;
|
||||
sif::info << "I HPA [mA]: " << adcSet.processed[I_HPA] << std::endl;
|
||||
sif::info << "U HPA [V]: " << adcSet.processed[U_HPA_DIV_6] << std::endl;
|
||||
sif::info << "I MPA [mA]: " << adcSet.processed[I_MPA] << std::endl;
|
||||
sif::info << "U MPA [V]: " << adcSet.processed[U_MPA_DIV_6] << std::endl;
|
||||
sif::info << "I TX [mA]: " << adcSet.processed[I_TX] << std::endl;
|
||||
sif::info << "U TX [V]: " << adcSet.processed[U_TX_DIV_6] << std::endl;
|
||||
sif::info << "I X8 [mA]: " << adcSet.processed[I_X8] << std::endl;
|
||||
sif::info << "U X8 [V]: " << adcSet.processed[U_X8_DIV_6] << std::endl;
|
||||
sif::info << "I DRO [mA]: " << adcSet.processed[I_DRO] << std::endl;
|
||||
sif::info << "U DRO [V]: " << adcSet.processed[U_DRO_DIV_6] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::enablePeriodicPrintout(bool enable, uint8_t divider) {
|
||||
this->periodicPrintout = enable;
|
||||
opDivider.setDivider(divider);
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::quickTransitionBackToOff(bool startTransitionToOff, bool notifyFdir) {
|
||||
States currentState = state;
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0);
|
||||
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1);
|
||||
state = States::STACK_5V_SWITCHING;
|
||||
adcState = AdcStates::OFF;
|
||||
if (startTransitionToOff) {
|
||||
startTransition(MODE_OFF, 0);
|
||||
}
|
||||
if (notifyFdir) {
|
||||
triggerEvent(TRANSITION_BACK_TO_OFF, static_cast<uint32_t>(currentState));
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::checkAdcValues() {
|
||||
using namespace plpcdu;
|
||||
checkJsonFileInit();
|
||||
adcSet.processed[U_BAT_DIV_6] =
|
||||
static_cast<float>(adcSet.channels[0]) * VOLTAGE_DIV / MAX122X_BIT * MAX122X_VREF;
|
||||
adcSet.processed[U_NEG_V_FB] =
|
||||
V_POS - VOLTAGE_DIV_U_NEG *
|
||||
(V_POS - static_cast<float>(adcSet.channels[1]) / MAX122X_BIT * MAX122X_VREF);
|
||||
adcSet.processed[I_HPA] = static_cast<float>(adcSet.channels[2]) * SCALE_CURRENT_HPA * 1000.0;
|
||||
adcSet.processed[U_HPA_DIV_6] = static_cast<float>(adcSet.channels[3]) * SCALE_VOLTAGE;
|
||||
adcSet.processed[I_MPA] = static_cast<float>(adcSet.channels[4]) * SCALE_CURRENT_MPA * 1000.0;
|
||||
adcSet.processed[U_MPA_DIV_6] = static_cast<float>(adcSet.channels[5]) * SCALE_VOLTAGE;
|
||||
adcSet.processed[I_TX] = static_cast<float>(adcSet.channels[6]) * SCALE_CURRENT_TX * 1000.0;
|
||||
adcSet.processed[U_TX_DIV_6] = static_cast<float>(adcSet.channels[7]) * SCALE_VOLTAGE;
|
||||
adcSet.processed[I_X8] = static_cast<float>(adcSet.channels[8]) * SCALE_CURRENT_X8 * 1000.0;
|
||||
adcSet.processed[U_X8_DIV_6] = static_cast<float>(adcSet.channels[9]) * SCALE_VOLTAGE;
|
||||
adcSet.processed[I_DRO] = static_cast<float>(adcSet.channels[10]) * SCALE_CURRENT_DRO * 1000.0;
|
||||
adcSet.processed[U_DRO_DIV_6] = static_cast<float>(adcSet.channels[11]) * SCALE_VOLTAGE;
|
||||
float lowerBound = 0.0;
|
||||
float upperBound = 0.0;
|
||||
bool adcTransition = false;
|
||||
adcTransition = state == States::ON_TRANS_DRO and adcCountdown.isBusy();
|
||||
// Now check against voltage and current limits, depending on state
|
||||
if (state >= States::ON_TRANS_DRO and not adcTransition) {
|
||||
if (ssrToDroInjectionRequested) {
|
||||
handleFailureInjection("SSR to DRO", NEG_V_OUT_OF_BOUNDS);
|
||||
ssrToDroInjectionRequested = false;
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[NEG_V_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[NEG_V_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_NEG_V_FB], lowerBound, upperBound,
|
||||
NEG_V_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[DRO_U_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[DRO_U_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_DRO_DIV_6], lowerBound, upperBound,
|
||||
U_DRO_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[DRO_I_UPPER_BOUND], upperBound);
|
||||
if (not checkCurrent(adcSet.processed[I_DRO], upperBound, I_DRO_OUT_OF_BOUNDS)) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::warning << "Detected out of bounds current for DRO: " << adcSet.processed[I_DRO]
|
||||
<< ", Raw: " << adcSet.channels[I_DRO] << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
adcTransition = state == States::ON_TRANS_X8 and adcCountdown.isBusy();
|
||||
if (state >= States::ON_TRANS_X8 and not adcTransition) {
|
||||
if (droToX8InjectionRequested) {
|
||||
handleFailureInjection("X8 to TX", U_X8_OUT_OF_BOUNDS);
|
||||
droToX8InjectionRequested = false;
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[X8_U_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[X8_U_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_X8_DIV_6], lowerBound, upperBound,
|
||||
U_X8_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[X8_I_UPPER_BOUND], upperBound);
|
||||
if (not checkCurrent(adcSet.processed[I_X8], upperBound, I_X8_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
adcTransition = state == States::ON_TRANS_TX and adcCountdown.isBusy();
|
||||
if (state >= States::ON_TRANS_TX and not adcTransition) {
|
||||
if (txToMpaInjectionRequested) {
|
||||
handleFailureInjection("TX to MPA", U_TX_OUT_OF_BOUNDS);
|
||||
txToMpaInjectionRequested = false;
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[TX_U_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[TX_U_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_TX_DIV_6], lowerBound, upperBound,
|
||||
U_TX_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[TX_I_UPPER_BOUND], upperBound);
|
||||
if (not checkCurrent(adcSet.processed[I_TX], upperBound, I_TX_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
adcTransition = state == States::ON_TRANS_MPA and adcCountdown.isBusy();
|
||||
if (state >= States::ON_TRANS_MPA and not adcTransition) {
|
||||
if (mpaToHpaInjectionRequested) {
|
||||
handleFailureInjection("MPA to HPA", U_HPA_OUT_OF_BOUNDS);
|
||||
mpaToHpaInjectionRequested = false;
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[MPA_U_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[MPA_U_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_MPA_DIV_6], lowerBound, upperBound,
|
||||
U_MPA_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[MPA_I_UPPER_BOUND], upperBound);
|
||||
if (not checkCurrent(adcSet.processed[I_MPA], upperBound, I_MPA_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
adcTransition = state == States::ON_TRANS_HPA and adcCountdown.isBusy();
|
||||
if (state >= States::ON_TRANS_HPA and not adcTransition) {
|
||||
if (allOnInjectRequested) {
|
||||
handleFailureInjection("All On", U_HPA_OUT_OF_BOUNDS);
|
||||
allOnInjectRequested = false;
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[HPA_U_LOWER_BOUND], lowerBound);
|
||||
params.getValue(PARAM_KEY_MAP[HPA_U_UPPER_BOUND], upperBound);
|
||||
if (not checkVoltage(adcSet.processed[U_HPA_DIV_6], lowerBound, upperBound,
|
||||
U_HPA_OUT_OF_BOUNDS)) {
|
||||
return;
|
||||
}
|
||||
params.getValue(PARAM_KEY_MAP[HPA_I_UPPER_BOUND], upperBound);
|
||||
if (not checkCurrent(adcSet.processed[I_HPA], upperBound, I_HPA_OUT_OF_BOUNDS)) {
|
||||
sif::warning << "PayloadPcduHandler::checkCurrent: I HPA exceeded limit: Measured "
|
||||
<< adcSet.processed[I_HPA] << " mA" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
transitionOk = true;
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::checkJsonFileInit() {
|
||||
if (not jsonFileInitComplete) {
|
||||
auto activeSd = sdcMan->getActiveSdCard();
|
||||
if (activeSd and sdcMan->isSdCardUsable(activeSd.value())) {
|
||||
if (sdcMan->getCurrentMountPrefix() == nullptr) {
|
||||
return;
|
||||
}
|
||||
params.initialize(sdcMan->getCurrentMountPrefix());
|
||||
jsonFileInitComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PayloadPcduHandler::checkVoltage(float val, float lowerBound, float upperBound, Event event) {
|
||||
bool tooLarge = false;
|
||||
if (val < lowerBound or val > upperBound) {
|
||||
if (val > upperBound) {
|
||||
tooLarge = true;
|
||||
} else {
|
||||
tooLarge = false;
|
||||
}
|
||||
uint32_t p2 = 0;
|
||||
serializeFloat(p2, val);
|
||||
triggerEvent(event, tooLarge, p2);
|
||||
transitionOk = false;
|
||||
quickTransitionBackToOff(true, true);
|
||||
quickTransitionAlreadyCalled = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PayloadPcduHandler::checkCurrent(float val, float upperBound, Event event) {
|
||||
if (val > upperBound) {
|
||||
uint32_t p2 = 0;
|
||||
serializeFloat(p2, val);
|
||||
triggerEvent(event, true, p2);
|
||||
transitionOk = false;
|
||||
quickTransitionBackToOff(true, true);
|
||||
quickTransitionAlreadyCalled = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::isModeCombinationValid(Mode_t mode, Submode_t submode) {
|
||||
using namespace plpcdu;
|
||||
if (mode == MODE_NORMAL) {
|
||||
uint8_t dhbSubmode = getSubmode();
|
||||
diffMask = submode ^ dhbSubmode;
|
||||
// Also deals with the case where the mode is MODE_ON, submode should be 0 here
|
||||
if ((((submode >> SOLID_STATE_RELAYS_ADC_ON) & 0b1) == SOLID_STATE_RELAYS_ADC_ON) and
|
||||
(getMode() == MODE_NORMAL and dhbSubmode != ALL_OFF_SUBMODE)) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
if (((((submode >> DRO_ON) & 1) == 1) and
|
||||
((dhbSubmode & 0b1) != (1 << SOLID_STATE_RELAYS_ADC_ON)))) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
if ((((submode >> X8_ON) & 1) == 1) and
|
||||
((dhbSubmode & 0b11) != ((1 << SOLID_STATE_RELAYS_ADC_ON) | (1 << DRO_ON)))) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
if (((((submode >> TX_ON) & 1) == 1) and
|
||||
((dhbSubmode & 0b111) !=
|
||||
((1 << X8_ON) | (1 << DRO_ON) | (1 << SOLID_STATE_RELAYS_ADC_ON))))) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
if ((((submode >> MPA_ON) & 1) == 1 and
|
||||
((dhbSubmode & 0b1111) !=
|
||||
((1 << TX_ON) | (1 << X8_ON) | (1 << DRO_ON) | (1 << SOLID_STATE_RELAYS_ADC_ON))))) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
if ((((submode >> HPA_ON) & 1) == 1 and
|
||||
((dhbSubmode & 0b11111) != ((1 << MPA_ON) | (1 << TX_ON) | (1 << X8_ON) | (1 << DRO_ON) |
|
||||
(1 << SOLID_STATE_RELAYS_ADC_ON))))) {
|
||||
return TRANS_NOT_ALLOWED;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
return DeviceHandlerBase::isModeCombinationValid(mode, submode);
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::serializeFloat(uint32_t& param, float val) {
|
||||
size_t dummy = 0;
|
||||
return SerializeAdapter::serialize(&val, reinterpret_cast<uint8_t*>(¶m), &dummy, 4,
|
||||
SerializeIF::Endianness::NETWORK);
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::getParameter(uint8_t domainId, uint8_t uniqueId,
|
||||
ParameterWrapper* parameterWrapper,
|
||||
const ParameterWrapper* newValues,
|
||||
uint16_t startAtIndex) {
|
||||
using namespace plpcdu;
|
||||
switch (uniqueId) {
|
||||
case (PlPcduParamIds::NEG_V_LOWER_BOUND):
|
||||
case (PlPcduParamIds::NEG_V_UPPER_BOUND):
|
||||
case (PlPcduParamIds::DRO_U_LOWER_BOUND):
|
||||
case (PlPcduParamIds::DRO_U_UPPER_BOUND):
|
||||
case (PlPcduParamIds::DRO_I_UPPER_BOUND):
|
||||
case (PlPcduParamIds::X8_U_LOWER_BOUND):
|
||||
case (PlPcduParamIds::X8_U_UPPER_BOUND):
|
||||
case (PlPcduParamIds::X8_I_UPPER_BOUND):
|
||||
case (PlPcduParamIds::TX_U_LOWER_BOUND):
|
||||
case (PlPcduParamIds::TX_U_UPPER_BOUND):
|
||||
case (PlPcduParamIds::TX_I_UPPER_BOUND):
|
||||
case (PlPcduParamIds::MPA_U_LOWER_BOUND):
|
||||
case (PlPcduParamIds::MPA_U_UPPER_BOUND):
|
||||
case (PlPcduParamIds::MPA_I_UPPER_BOUND):
|
||||
case (PlPcduParamIds::HPA_U_LOWER_BOUND):
|
||||
case (PlPcduParamIds::HPA_U_UPPER_BOUND):
|
||||
case (PlPcduParamIds::HPA_I_UPPER_BOUND):
|
||||
case (PlPcduParamIds::SSR_TO_DRO_WAIT_TIME):
|
||||
case (PlPcduParamIds::DRO_TO_X8_WAIT_TIME):
|
||||
case (PlPcduParamIds::X8_TO_TX_WAIT_TIME):
|
||||
case (PlPcduParamIds::TX_TO_MPA_WAIT_TIME):
|
||||
case (PlPcduParamIds::MPA_TO_HPA_WAIT_TIME): {
|
||||
handleDoubleParamUpdate(PARAM_KEY_MAP[static_cast<PlPcduParamIds>(uniqueId)],
|
||||
parameterWrapper, newValues);
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_SSR_TO_DRO_FAILURE): {
|
||||
ssrToDroInjectionRequested = true;
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_DRO_TO_X8_FAILURE): {
|
||||
droToX8InjectionRequested = true;
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_X8_TO_TX_FAILURE): {
|
||||
x8ToTxInjectionRequested = true;
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_TX_TO_MPA_FAILURE): {
|
||||
txToMpaInjectionRequested = true;
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_MPA_TO_HPA_FAILURE): {
|
||||
mpaToHpaInjectionRequested = true;
|
||||
break;
|
||||
}
|
||||
case (PlPcduParamIds::INJECT_ALL_ON_FAILURE): {
|
||||
allOnInjectRequested = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return DeviceHandlerBase::getParameter(domainId, uniqueId, parameterWrapper, newValues,
|
||||
startAtIndex);
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void PayloadPcduHandler::handleFailureInjection(std::string output, Event event) {
|
||||
sif::info << "PayloadPcduHandler::handleFailureInjection: " << output
|
||||
<< " failure injection. "
|
||||
"Transitioning back to off"
|
||||
<< std::endl;
|
||||
triggerEvent(event, 0, 0);
|
||||
transitionOk = false;
|
||||
quickTransitionBackToOff(true, true);
|
||||
quickTransitionAlreadyCalled = true;
|
||||
droToX8InjectionRequested = false;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::handleDoubleParamUpdate(std::string key,
|
||||
ParameterWrapper* parameterWrapper,
|
||||
const ParameterWrapper* newValues) {
|
||||
double newValue = 0.0;
|
||||
ReturnValue_t result = newValues->getElement<double>(&newValue, 0, 0);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
params.setValue(key, newValue);
|
||||
// Do this so the dumping and loading with the framework works as well
|
||||
doubleDummy = newValue;
|
||||
parameterWrapper->set(doubleDummy);
|
||||
return params.writeJsonFile();
|
||||
}
|
||||
|
||||
#ifdef XIPHOS_Q7S
|
||||
ReturnValue_t PayloadPcduHandler::extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie,
|
||||
const uint8_t* sendData, size_t sendLen,
|
||||
void* args) {
|
||||
auto handler = reinterpret_cast<PayloadPcduHandler*>(args);
|
||||
if (handler == nullptr) {
|
||||
sif::error << "GyroADIS16507Handler::spiSendCallback: Passed handler pointer is invalid!"
|
||||
<< std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
DeviceCommandId_t currentCommand = handler->getPendingCommand();
|
||||
switch (currentCommand) {
|
||||
case (plpcdu::READ_WITH_TEMP_EXT): {
|
||||
return transferAsTwo(comIf, cookie, sendData, sendLen, false);
|
||||
}
|
||||
case (plpcdu::READ_TEMP_EXT): {
|
||||
return transferAsTwo(comIf, cookie, sendData, sendLen, true);
|
||||
}
|
||||
default: {
|
||||
return comIf->performRegularSendOperation(cookie, sendData, sendLen);
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PayloadPcduHandler::transferAsTwo(SpiComIF* comIf, SpiCookie* cookie,
|
||||
const uint8_t* sendData, size_t sendLen,
|
||||
bool tempOnly) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
int retval = 0;
|
||||
// Prepare transfer
|
||||
int fileDescriptor = 0;
|
||||
UnixFileGuard fileHelper(comIf->getSpiDev(), fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
|
||||
if (fileHelper.getOpenResult() != returnvalue::OK) {
|
||||
return SpiComIF::OPENING_FILE_FAILED;
|
||||
}
|
||||
spi::SpiModes spiMode = spi::SpiModes::MODE_0;
|
||||
uint32_t spiSpeed = 0;
|
||||
cookie->getSpiParameters(spiMode, spiSpeed, nullptr);
|
||||
comIf->setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed);
|
||||
cookie->assignWriteBuffer(sendData);
|
||||
size_t transferLen = plpcdu::TEMP_REPLY_SIZE;
|
||||
if (not tempOnly) {
|
||||
transferLen += plpcdu::ADC_REPLY_SIZE;
|
||||
}
|
||||
cookie->setTransferSize(transferLen);
|
||||
|
||||
gpioId_t gpioId = cookie->getChipSelectPin();
|
||||
GpioIF& gpioIF = comIf->getGpioInterface();
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
uint32_t timeoutMs = 0;
|
||||
MutexIF* mutex = comIf->getCsMutex();
|
||||
if (mutex == nullptr) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::warning << "GyroADIS16507Handler::spiSendCallback: "
|
||||
"Mutex or GPIO interface invalid"
|
||||
<< std::endl;
|
||||
return returnvalue::FAILED;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
cookie->getMutexParams(timeoutType, timeoutMs);
|
||||
result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != returnvalue::OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
spi_ioc_transfer* transferStruct = cookie->getTransferStructHandle();
|
||||
uint64_t origTx = transferStruct->tx_buf;
|
||||
uint64_t origRx = transferStruct->rx_buf;
|
||||
if (tempOnly) {
|
||||
transferLen = 1;
|
||||
} else {
|
||||
transferLen = plpcdu::ADC_REPLY_SIZE + 1;
|
||||
}
|
||||
transferStruct->len = transferLen;
|
||||
// Pull SPI CS low. For now, no support for active high given
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
gpioIF.pullLow(gpioId);
|
||||
}
|
||||
|
||||
// Execute transfer
|
||||
// Initiate a full duplex SPI transfer.
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle());
|
||||
if (retval < 0) {
|
||||
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
|
||||
result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
#if FSFW_HAL_SPI_WIRETAPPING == 1
|
||||
comIf->performSpiWiretapping(cookie);
|
||||
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
|
||||
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
gpioIF.pullHigh(gpioId);
|
||||
}
|
||||
|
||||
transferStruct->tx_buf += transferLen;
|
||||
transferStruct->rx_buf += transferLen;
|
||||
transferStruct->len = plpcdu::TEMP_REPLY_SIZE - 1;
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
gpioIF.pullLow(gpioId);
|
||||
}
|
||||
|
||||
// Execute transfer
|
||||
// Initiate a full duplex SPI transfer.
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle());
|
||||
if (retval < 0) {
|
||||
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
|
||||
result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
#if FSFW_HAL_SPI_WIRETAPPING == 1
|
||||
comIf->performSpiWiretapping(cookie);
|
||||
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
|
||||
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
gpioIF.pullHigh(gpioId);
|
||||
}
|
||||
|
||||
transferStruct->tx_buf = origTx;
|
||||
transferStruct->rx_buf = origRx;
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
mutex->unlockMutex();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
#endif
|
183
mission/payload/PayloadPcduHandler.h
Normal file
183
mission/payload/PayloadPcduHandler.h
Normal file
@ -0,0 +1,183 @@
|
||||
#ifndef LINUX_DEVICES_PLPCDUHANDLER_H_
|
||||
#define LINUX_DEVICES_PLPCDUHANDLER_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
|
||||
#include <fsfw/timemanager/Countdown.h>
|
||||
#include <mission/payload/payloadPcduDefinitions.h>
|
||||
#include <mission/system/objects/Stack5VHandler.h>
|
||||
|
||||
#include "events/subsystemIdRanges.h"
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw_hal/common/gpio/GpioIF.h"
|
||||
#include "mission/memory/SdCardMountedIF.h"
|
||||
#include "mission/power/defs.h"
|
||||
#include "mission/system/DualLanePowerStateMachine.h"
|
||||
|
||||
#ifdef FSFW_OSAL_LINUX
|
||||
class SpiComIF;
|
||||
class SpiCookie;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Device handler for the EIVE Payload PCDU
|
||||
* @details
|
||||
* Documentation:
|
||||
* https://egit.irs.uni-stuttgart.de/eive/eive_dokumente/src/branch/master/400_Raumsegment/412_PayloaPCDUDocumentation/release/EIVE-D-421-001_PLPCDU_Documentation.pdf
|
||||
*
|
||||
* Important components:
|
||||
* - SSR - Solid State Relay: Decouples voltages from battery
|
||||
* - DRO - Dielectric Resonsant Oscillator: Generates modulation signal
|
||||
* - X8: Frequency X8 Multiplicator
|
||||
* - TX: Transmitter/Sender module. Modulates data onto carrier signal
|
||||
* - MPA - Medium Power Amplifier
|
||||
* - HPA - High Power Amplifier
|
||||
*/
|
||||
class PayloadPcduHandler : public DeviceHandlerBase {
|
||||
public:
|
||||
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PL_PCDU_HANDLER;
|
||||
//! [EXPORT] : [COMMENT] Could not transition properly and went back to ALL OFF
|
||||
static constexpr Event TRANSITION_BACK_TO_OFF =
|
||||
event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event NEG_V_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 1, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event U_DRO_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 2, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event I_DRO_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 3, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event U_X8_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 4, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event I_X8_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 5, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event U_TX_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 6, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event I_TX_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 7, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event U_MPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 8, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event I_MPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 9, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event U_HPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 10, severity::MEDIUM);
|
||||
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
|
||||
static constexpr Event I_HPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 11, severity::MEDIUM);
|
||||
|
||||
PayloadPcduHandler(object_id_t objectId, object_id_t comIF, CookieIF* cookie, GpioIF* gpioIF,
|
||||
SdCardMountedIF* sdcMan, Stack5VHandler& stackHandler, bool periodicPrintout);
|
||||
|
||||
void setToGoToNormalModeImmediately(bool enable);
|
||||
void enablePeriodicPrintout(bool enable, uint8_t divider);
|
||||
|
||||
#ifdef XIPHOS_Q7S
|
||||
static ReturnValue_t extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie,
|
||||
const uint8_t* sendData, size_t sendLen, void* args);
|
||||
static ReturnValue_t transferAsTwo(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData,
|
||||
size_t sendLen, bool tempOnly);
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum class States : uint8_t {
|
||||
PL_PCDU_OFF,
|
||||
STACK_5V_SWITCHING,
|
||||
STACK_5V_PENDING,
|
||||
STACK_5V_CORRECT,
|
||||
// Solid State Relay, enable battery voltages VBAT0 and VBAT1. This will also switch on
|
||||
// the ADC
|
||||
ON_TRANS_SSR,
|
||||
ON_TRANS_ADC_CLOSE_ZERO,
|
||||
// Enable Dielectric Resonant Oscillator and start monitoring voltages as
|
||||
// soon as DRO voltage reaches 6V
|
||||
ON_TRANS_DRO,
|
||||
// Switch on X8 compoennt and monitor voltages for 5 seconds
|
||||
ON_TRANS_X8,
|
||||
// Switch on TX component and monitor voltages for 5 seconds
|
||||
ON_TRANS_TX,
|
||||
// Switch on MPA component and monitor voltages for 5 seconds
|
||||
ON_TRANS_MPA,
|
||||
// Switch on HPA component and monitor voltages for 5 seconds
|
||||
ON_TRANS_HPA,
|
||||
// All components of the experiment are on
|
||||
PL_PCDU_ON,
|
||||
} state = States::PL_PCDU_OFF;
|
||||
|
||||
duallane::Submodes pwrSubmode = duallane::Submodes::A_SIDE;
|
||||
|
||||
enum class AdcMode { EXT_CONV, INT_CONV } adcMode = AdcMode::INT_CONV;
|
||||
|
||||
enum class MonitoringMode { NONE, CLOSE_TO_ZERO, NEGATIVE } monMode = MonitoringMode::NONE;
|
||||
|
||||
enum class AdcStates { OFF, BOOT_DELAY, SEND_SETUP, NORMAL } adcState = AdcStates::OFF;
|
||||
|
||||
bool goToNormalMode = false;
|
||||
plpcdu::PlPcduAdcSet adcSet;
|
||||
Stack5VHandler& stackHandler;
|
||||
std::array<uint8_t, plpcdu::MAX_ADC_REPLY_SIZE> cmdBuf = {};
|
||||
// This variable is tied to DRO +6 V voltage. Voltages, currents are monitored and the experiment
|
||||
// is shut down immediately if there is a negative voltage.
|
||||
bool transitionOk = false;
|
||||
bool commandExecuted = false;
|
||||
bool adcCmdExecuted = false;
|
||||
bool periodicPrintout = false;
|
||||
bool jsonFileInitComplete = false;
|
||||
double doubleDummy = 0.0;
|
||||
|
||||
bool ssrToDroInjectionRequested = false;
|
||||
bool droToX8InjectionRequested = false;
|
||||
bool x8ToTxInjectionRequested = false;
|
||||
bool txToMpaInjectionRequested = false;
|
||||
bool mpaToHpaInjectionRequested = false;
|
||||
bool allOnInjectRequested = false;
|
||||
bool clearSetOnOffFlag = true;
|
||||
|
||||
PeriodicOperationDivider opDivider = PeriodicOperationDivider(5);
|
||||
uint8_t tempReadDivisor = 1;
|
||||
Countdown countdown = Countdown(5000);
|
||||
Countdown adcCountdown = Countdown(50);
|
||||
GpioIF* gpioIF;
|
||||
SdCardMountedIF* sdcMan;
|
||||
plpcdu::PlPcduParameter params;
|
||||
bool quickTransitionAlreadyCalled = true;
|
||||
uint8_t diffMask = 0;
|
||||
|
||||
PoolEntry<uint16_t> channelValues = PoolEntry<uint16_t>({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
||||
PoolEntry<float> processedValues =
|
||||
PoolEntry<float>({0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0});
|
||||
PoolEntry<float> tempC = PoolEntry<float>({0.0});
|
||||
|
||||
void updateSwitchGpio(gpioId_t id, gpio::Levels level);
|
||||
|
||||
void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override;
|
||||
void doStartUp() override;
|
||||
void doShutDown() override;
|
||||
// Main FDIR function which goes from any PL PCDU state back to all off
|
||||
void quickTransitionBackToOff(bool startTransitionToOff, bool notifyFdir);
|
||||
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
|
||||
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
|
||||
void fillCommandAndReplyMap() override;
|
||||
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
|
||||
size_t commandDataLen) override;
|
||||
ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
|
||||
size_t* foundLen) override;
|
||||
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) override;
|
||||
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override;
|
||||
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
|
||||
ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, ParameterWrapper* parameterWrapper,
|
||||
const ParameterWrapper* newValues, uint16_t startAtIndex) override;
|
||||
|
||||
void handleExtConvRead(const uint8_t* bufStart);
|
||||
void handlePrintout();
|
||||
void checkAdcValues();
|
||||
void handleOutOfBoundsPrintout();
|
||||
void checkJsonFileInit();
|
||||
ReturnValue_t stateMachineToNormal(Mode_t modeFrom, Submode_t subModeFrom);
|
||||
bool checkVoltage(float val, float lowerBound, float upperBound, Event event);
|
||||
bool checkCurrent(float val, float upperBound, Event event);
|
||||
void handleFailureInjection(std::string output, Event event);
|
||||
ReturnValue_t serializeFloat(uint32_t& param, float val);
|
||||
ReturnValue_t handleDoubleParamUpdate(std::string key, ParameterWrapper* parameterWrapper,
|
||||
const ParameterWrapper* newValues);
|
||||
};
|
||||
|
||||
#endif /* LINUX_DEVICES_PLPCDUHANDLER_H_ */
|
239
mission/payload/RadiationSensorHandler.cpp
Normal file
239
mission/payload/RadiationSensorHandler.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
#include <OBSWConfig.h>
|
||||
#include <devices/gpioIds.h>
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
#include <mission/payload/RadiationSensorHandler.h>
|
||||
#include <mission/power/gsDefs.h>
|
||||
#include <mission/tcs/max1227.h>
|
||||
|
||||
RadiationSensorHandler::RadiationSensorHandler(object_id_t objectId, object_id_t comIF,
|
||||
CookieIF *comCookie, GpioIF *gpioIF,
|
||||
Stack5VHandler &stackHandler)
|
||||
: DeviceHandlerBase(objectId, comIF, comCookie),
|
||||
dataset(this),
|
||||
gpioIF(gpioIF),
|
||||
stackHandler(stackHandler) {
|
||||
if (comCookie == nullptr) {
|
||||
sif::error << "RadiationSensorHandler: Invalid com cookie" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
RadiationSensorHandler::~RadiationSensorHandler() {}
|
||||
|
||||
void RadiationSensorHandler::doStartUp() {
|
||||
if (internalState == InternalState::OFF) {
|
||||
ReturnValue_t retval = stackHandler.deviceToOn(StackCommander::RAD_SENSOR, true);
|
||||
if (retval == BUSY) {
|
||||
return;
|
||||
}
|
||||
internalState = InternalState::POWER_SWITCHING;
|
||||
}
|
||||
if (internalState == InternalState::POWER_SWITCHING) {
|
||||
if (stackHandler.isSwitchOn()) {
|
||||
internalState = InternalState::SETUP;
|
||||
}
|
||||
}
|
||||
if (internalState == InternalState::CONFIGURED) {
|
||||
if (goToNormalMode) {
|
||||
setMode(MODE_NORMAL);
|
||||
} else {
|
||||
setMode(_MODE_TO_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiationSensorHandler::doShutDown() {
|
||||
ReturnValue_t retval = stackHandler.deviceToOff(StackCommander::RAD_SENSOR, true);
|
||||
if (retval == BUSY) {
|
||||
return;
|
||||
}
|
||||
internalState = InternalState::OFF;
|
||||
setMode(_MODE_POWER_DOWN);
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::buildNormalDeviceCommand(DeviceCommandId_t *id) {
|
||||
switch (communicationStep) {
|
||||
case CommunicationStep::START_CONVERSION: {
|
||||
*id = RAD_SENSOR::START_CONVERSION;
|
||||
communicationStep = CommunicationStep::READ_CONVERSIONS;
|
||||
break;
|
||||
}
|
||||
case CommunicationStep::READ_CONVERSIONS: {
|
||||
*id = RAD_SENSOR::READ_CONVERSIONS;
|
||||
communicationStep = CommunicationStep::START_CONVERSION;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sif::debug << "RadiationSensorHandler::buildNormalDeviceCommand: Unknown communication "
|
||||
<< "step" << std::endl;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
}
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
|
||||
if (internalState == InternalState::SETUP) {
|
||||
*id = RAD_SENSOR::WRITE_SETUP;
|
||||
} else {
|
||||
return NOTHING_TO_SEND;
|
||||
}
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
|
||||
const uint8_t *commandData,
|
||||
size_t commandDataLen) {
|
||||
switch (deviceCommand) {
|
||||
case (RAD_SENSOR::WRITE_SETUP): {
|
||||
cmdBuffer[0] = RAD_SENSOR::SETUP_DEFINITION;
|
||||
rawPacket = cmdBuffer;
|
||||
rawPacketLen = 1;
|
||||
internalState = InternalState::CONFIGURED;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (RAD_SENSOR::START_CONVERSION): {
|
||||
ReturnValue_t result = gpioIF->pullHigh(gpioIds::ENABLE_RADFET);
|
||||
if (result != returnvalue::OK) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::warning
|
||||
<< "RadiationSensorHandler::buildCommandFromCommand: Pulling RADFET Enable pin "
|
||||
"high failed"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
/* First the fifo will be reset here */
|
||||
cmdBuffer[0] = RAD_SENSOR::RESET_DEFINITION;
|
||||
cmdBuffer[1] = RAD_SENSOR::CONVERSION_DEFINITION;
|
||||
rawPacket = cmdBuffer;
|
||||
rawPacketLen = 2;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (RAD_SENSOR::READ_CONVERSIONS): {
|
||||
cmdBuffer[0] = RAD_SENSOR::DUMMY_BYTE;
|
||||
std::memset(cmdBuffer, RAD_SENSOR::DUMMY_BYTE, RAD_SENSOR::READ_SIZE);
|
||||
rawPacket = cmdBuffer;
|
||||
rawPacketLen = RAD_SENSOR::READ_SIZE;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case RAD_SENSOR::ENABLE_DEBUG_OUTPUT: {
|
||||
printPeriodicData = true;
|
||||
rawPacketLen = 0;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case RAD_SENSOR::DISABLE_DEBUG_OUTPUT: {
|
||||
rawPacketLen = 0;
|
||||
printPeriodicData = false;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
default:
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
void RadiationSensorHandler::fillCommandAndReplyMap() {
|
||||
this->insertInCommandMap(RAD_SENSOR::WRITE_SETUP);
|
||||
this->insertInCommandMap(RAD_SENSOR::START_CONVERSION);
|
||||
this->insertInCommandMap(RAD_SENSOR::ENABLE_DEBUG_OUTPUT);
|
||||
this->insertInCommandMap(RAD_SENSOR::DISABLE_DEBUG_OUTPUT);
|
||||
this->insertInCommandAndReplyMap(RAD_SENSOR::READ_CONVERSIONS, 1, &dataset,
|
||||
RAD_SENSOR::READ_SIZE);
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t remainingSize,
|
||||
DeviceCommandId_t *foundId, size_t *foundLen) {
|
||||
*foundId = this->getPendingCommand();
|
||||
|
||||
switch (*foundId) {
|
||||
case RAD_SENSOR::START_CONVERSION:
|
||||
case RAD_SENSOR::WRITE_SETUP:
|
||||
*foundLen = remainingSize;
|
||||
return IGNORE_REPLY_DATA;
|
||||
case RAD_SENSOR::READ_CONVERSIONS: {
|
||||
ReturnValue_t result = gpioIF->pullLow(gpioIds::ENABLE_RADFET);
|
||||
if (result != returnvalue::OK) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::warning << "RadiationSensorHandler::scanForReply; Pulling RADFET Enale pin "
|
||||
"low failed"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RAD_SENSOR::ENABLE_DEBUG_OUTPUT:
|
||||
case RAD_SENSOR::DISABLE_DEBUG_OUTPUT:
|
||||
sif::info << "RadiationSensorHandler::scanForReply: " << remainingSize << std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*foundLen = remainingSize;
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::interpretDeviceReply(DeviceCommandId_t id,
|
||||
const uint8_t *packet) {
|
||||
switch (id) {
|
||||
case RAD_SENSOR::READ_CONVERSIONS: {
|
||||
uint8_t offset = 0;
|
||||
PoolReadGuard readSet(&dataset);
|
||||
uint16_t tempRaw = ((packet[offset] & 0x0f) << 8) | packet[offset + 1];
|
||||
dataset.temperatureCelcius = max1227::getTemperature(tempRaw);
|
||||
offset += 2;
|
||||
dataset.ain0 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
offset += 2;
|
||||
dataset.ain1 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
offset += 6;
|
||||
dataset.ain4 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
offset += 2;
|
||||
dataset.ain5 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
offset += 2;
|
||||
dataset.ain6 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
offset += 2;
|
||||
dataset.ain7 = (*(packet + offset) << 8) | *(packet + offset + 1);
|
||||
dataset.setValidity(true, true);
|
||||
if (printPeriodicData) {
|
||||
sif::info << "Radiation sensor temperature: " << dataset.temperatureCelcius << " °C"
|
||||
<< std::dec << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 0: " << dataset.ain0 << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 1: " << dataset.ain1 << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 4: " << dataset.ain4 << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 5: " << dataset.ain5 << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 6: " << dataset.ain6 << std::endl;
|
||||
sif::info << "Radiation sensor ADC value channel 7: " << dataset.ain7 << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sif::debug << "RadiationSensorHandler::interpretDeviceReply: Unknown reply id" << std::endl;
|
||||
return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
uint32_t RadiationSensorHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) {
|
||||
return 5000;
|
||||
}
|
||||
|
||||
ReturnValue_t RadiationSensorHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
localDataPoolMap.emplace(RAD_SENSOR::TEMPERATURE_C, new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN0, new PoolEntry<uint16_t>({0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN1, new PoolEntry<uint16_t>({0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN4, new PoolEntry<uint16_t>({0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN5, new PoolEntry<uint16_t>({0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN6, new PoolEntry<uint16_t>({0}));
|
||||
localDataPoolMap.emplace(RAD_SENSOR::AIN7, new PoolEntry<uint16_t>({0}));
|
||||
poolManager.subscribeForRegularPeriodicPacket(
|
||||
subdp::RegularHkPeriodicParams(dataset.getSid(), false, 20.0));
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void RadiationSensorHandler::setToGoToNormalModeImmediately() { this->goToNormalMode = true; }
|
||||
|
||||
void RadiationSensorHandler::enablePeriodicDataPrint(bool enable) {
|
||||
this->printPeriodicData = enable;
|
||||
}
|
57
mission/payload/RadiationSensorHandler.h
Normal file
57
mission/payload/RadiationSensorHandler.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_
|
||||
#define MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||
#include <fsfw_hal/common/gpio/GpioIF.h>
|
||||
#include <mission/payload/radSensorDefinitions.h>
|
||||
#include <mission/system/objects/Stack5VHandler.h>
|
||||
|
||||
/**
|
||||
* @brief This is the device handler class for radiation sensor on the OBC IF Board. The
|
||||
* sensor is based on the MAX1227 ADC converter.
|
||||
*
|
||||
* @details Datasheet of MAX1227: https://datasheets.maximintegrated.com/en/ds/MAX1227-MAX1231.pdf
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class RadiationSensorHandler : public DeviceHandlerBase {
|
||||
public:
|
||||
RadiationSensorHandler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie,
|
||||
GpioIF *gpioIF, Stack5VHandler &handler);
|
||||
virtual ~RadiationSensorHandler();
|
||||
void setToGoToNormalModeImmediately();
|
||||
void enablePeriodicDataPrint(bool enable);
|
||||
|
||||
protected:
|
||||
void doStartUp() override;
|
||||
void doShutDown() override;
|
||||
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
|
||||
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
|
||||
void fillCommandAndReplyMap() override;
|
||||
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData,
|
||||
size_t commandDataLen) override;
|
||||
ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId,
|
||||
size_t *foundLen) override;
|
||||
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
|
||||
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
|
||||
private:
|
||||
enum class CommunicationStep { START_CONVERSION, READ_CONVERSIONS };
|
||||
|
||||
enum class InternalState { OFF, POWER_SWITCHING, SETUP, CONFIGURED };
|
||||
|
||||
bool printPeriodicData = false;
|
||||
RAD_SENSOR::RadSensorDataset dataset;
|
||||
static const uint8_t MAX_CMD_LEN = RAD_SENSOR::READ_SIZE;
|
||||
GpioIF *gpioIF = nullptr;
|
||||
Stack5VHandler &stackHandler;
|
||||
|
||||
bool goToNormalMode = false;
|
||||
uint8_t cmdBuffer[MAX_CMD_LEN];
|
||||
InternalState internalState = InternalState::OFF;
|
||||
CommunicationStep communicationStep = CommunicationStep::START_CONVERSION;
|
||||
};
|
||||
|
||||
#endif /* MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_ */
|
395
mission/payload/ScexDeviceHandler.cpp
Normal file
395
mission/payload/ScexDeviceHandler.cpp
Normal file
@ -0,0 +1,395 @@
|
||||
#include <fsfw/filesystem/HasFileSystemIF.h>
|
||||
#include <linux/payload/ScexHelper.h>
|
||||
#include <mission/memory/SdCardMountedIF.h>
|
||||
#include <mission/payload/ScexDeviceHandler.h>
|
||||
#include <mission/payload/scexHelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include "fsfw/globalfunctions/CRC.h"
|
||||
|
||||
using std::ofstream;
|
||||
using namespace returnvalue;
|
||||
|
||||
ScexDeviceHandler::ScexDeviceHandler(object_id_t objectId, ScexUartReader& reader, CookieIF* cookie,
|
||||
SdCardMountedIF& sdcMan)
|
||||
: DeviceHandlerBase(objectId, reader.getObjectId(), cookie), sdcMan(sdcMan), reader(reader) {}
|
||||
|
||||
ScexDeviceHandler::~ScexDeviceHandler() {}
|
||||
|
||||
void ScexDeviceHandler::doStartUp() { setMode(MODE_ON); }
|
||||
|
||||
void ScexDeviceHandler::doShutDown() {
|
||||
reader.reset();
|
||||
commandActive = false;
|
||||
multiFileFinishOutstanding = false;
|
||||
setMode(_MODE_POWER_DOWN);
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { return OK; }
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { return OK; }
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
|
||||
const uint8_t* commandData,
|
||||
size_t commandDataLen) {
|
||||
using namespace scex;
|
||||
|
||||
auto cmdTyped = static_cast<scex::Cmds>(deviceCommand);
|
||||
if (std::find(VALID_CMDS.begin(), VALID_CMDS.end(), deviceCommand) == VALID_CMDS.end()) {
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
bool tempCheck = false;
|
||||
if (commandDataLen == 1) {
|
||||
tempCheck = commandData[0];
|
||||
}
|
||||
if (commandActive) {
|
||||
return DeviceHandlerIF::BUSY;
|
||||
}
|
||||
|
||||
switch (deviceCommand) {
|
||||
case (PING): {
|
||||
finishCountdown.setTimeout(SHORT_CD);
|
||||
// countdown starten
|
||||
finishCountdown.resetTimer();
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
|
||||
tempCheck);
|
||||
break;
|
||||
}
|
||||
case (EXP_STATUS_CMD): {
|
||||
finishCountdown.setTimeout(SHORT_CD);
|
||||
// countdown starten
|
||||
finishCountdown.resetTimer();
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
|
||||
tempCheck);
|
||||
break;
|
||||
}
|
||||
case (ION_CMD): {
|
||||
finishCountdown.setTimeout(SHORT_CD);
|
||||
// countdown starten
|
||||
finishCountdown.resetTimer();
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
|
||||
tempCheck);
|
||||
break;
|
||||
}
|
||||
case (TEMP_CMD): {
|
||||
finishCountdown.setTimeout(SHORT_CD);
|
||||
// countdown starten
|
||||
finishCountdown.resetTimer();
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
|
||||
tempCheck);
|
||||
break;
|
||||
}
|
||||
case (FRAM): {
|
||||
finishCountdown.setTimeout(LONG_CD);
|
||||
// countdown starten
|
||||
finishCountdown.resetTimer();
|
||||
if (debugMode) {
|
||||
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
|
||||
|
||||
sif::info << "ScexDeviceHandler::buildCommandFromCommand: RemainingMillis: "
|
||||
<< remainingMillis << std::endl;
|
||||
}
|
||||
|
||||
multiFileFinishOutstanding = true;
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
|
||||
{commandData + 1, commandDataLen - 1}, tempCheck);
|
||||
updatePeriodicReply(true, deviceCommand);
|
||||
break;
|
||||
}
|
||||
case (ONE_CELL): {
|
||||
finishCountdown.setTimeout(LONG_CD);
|
||||
// countdown starts
|
||||
finishCountdown.resetTimer();
|
||||
multiFileFinishOutstanding = true;
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
|
||||
{commandData + 1, commandDataLen - 1}, tempCheck);
|
||||
updatePeriodicReply(true, deviceCommand);
|
||||
break;
|
||||
}
|
||||
case (ALL_CELLS_CMD): {
|
||||
finishCountdown.setTimeout(LONG_CD);
|
||||
// countdown starts
|
||||
finishCountdown.resetTimer();
|
||||
multiFileFinishOutstanding = true;
|
||||
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
|
||||
{commandData + 1, commandDataLen - 1}, tempCheck);
|
||||
updatePeriodicReply(true, deviceCommand);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
commandActive = true;
|
||||
rawPacket = cmdBuf.data();
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ScexDeviceHandler::fillCommandAndReplyMap() {
|
||||
insertInCommandAndReplyMap(scex::Cmds::PING, 5, nullptr, 0, false, false, 0, &finishCountdown);
|
||||
insertInCommandAndReplyMap(scex::Cmds::ION_CMD, 3, nullptr, 0, false, false, 0, &finishCountdown);
|
||||
insertInCommandAndReplyMap(scex::Cmds::TEMP_CMD, 3, nullptr, 0, false, false, 0,
|
||||
&finishCountdown);
|
||||
insertInCommandAndReplyMap(scex::Cmds::EXP_STATUS_CMD, 3, nullptr, 0, false, false, 0,
|
||||
&finishCountdown);
|
||||
|
||||
insertInCommandAndReplyMap(scex::Cmds::ALL_CELLS_CMD, 0, nullptr, 0, true, false,
|
||||
scex::Cmds::ALL_CELLS_CMD, &finishCountdown);
|
||||
insertInCommandAndReplyMap(scex::Cmds::ONE_CELL, 0, nullptr, 0, true, false, scex::Cmds::ONE_CELL,
|
||||
&finishCountdown);
|
||||
insertInCommandAndReplyMap(scex::Cmds::FRAM, 0, nullptr, 0, true, false, scex::Cmds::FRAM,
|
||||
&finishCountdown);
|
||||
|
||||
insertInReplyMap(scex::Cmds::ERROR_REPLY, 3);
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::scanForReply(const uint8_t* start, size_t remainingSize,
|
||||
DeviceCommandId_t* foundId, size_t* foundLen) {
|
||||
size_t len = remainingSize;
|
||||
ReturnValue_t result = helper.deSerialize(&start, &len);
|
||||
|
||||
if (result == ScexHelper::INVALID_CRC) {
|
||||
sif::warning << "ScexDeviceHandler::scanForReply: CRC invalid" << std::endl;
|
||||
*foundLen = remainingSize;
|
||||
} else {
|
||||
result = handleValidReply(remainingSize, foundId, foundLen);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::handleValidReply(size_t remSize, DeviceCommandId_t* foundId,
|
||||
size_t* foundLen) {
|
||||
using namespace scex;
|
||||
ReturnValue_t result = OK;
|
||||
|
||||
switch (helper.getCmd()) {
|
||||
case (FRAM): {
|
||||
if (debugMode) {
|
||||
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
|
||||
|
||||
sif::info << "ScexDeviceHandler::handleValidReply: RemMillis: " << remainingMillis
|
||||
<< std::endl;
|
||||
}
|
||||
result = APERIODIC_REPLY;
|
||||
break;
|
||||
}
|
||||
case (ONE_CELL): {
|
||||
result = APERIODIC_REPLY;
|
||||
break;
|
||||
}
|
||||
case (ALL_CELLS_CMD): {
|
||||
result = APERIODIC_REPLY;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == APERIODIC_REPLY and multiFileFinishOutstanding) {
|
||||
finishAction(true, helper.getCmd(), OK);
|
||||
multiFileFinishOutstanding = false;
|
||||
}
|
||||
|
||||
*foundId = helper.getCmd();
|
||||
*foundLen = remSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) {
|
||||
using namespace scex;
|
||||
|
||||
ReturnValue_t status = OK;
|
||||
auto oneFileHandler = [&](std::string cmdName) {
|
||||
auto activeSd = sdcMan.getActiveSdCard();
|
||||
if (not activeSd) {
|
||||
return HasFileSystemIF::FILESYSTEM_INACTIVE;
|
||||
}
|
||||
fileId = date_time_string();
|
||||
std::ostringstream oss;
|
||||
auto prefix = sdcMan.getCurrentMountPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin";
|
||||
fileName = oss.str();
|
||||
ofstream out(fileName, ofstream::binary);
|
||||
if (out.bad()) {
|
||||
sif::error << "ScexDeviceHandler::interpretDeviceReply: Could not open file " << fileName
|
||||
<< std::endl;
|
||||
return FAILED;
|
||||
}
|
||||
out << helper;
|
||||
return OK;
|
||||
};
|
||||
auto multiFileHandler = [&](std::string cmdName) {
|
||||
if ((helper.getPacketCounter() == 1) or (not fileNameSet)) {
|
||||
auto activeSd = sdcMan.getActiveSdCard();
|
||||
if (not activeSd) {
|
||||
return HasFileSystemIF::FILESYSTEM_INACTIVE;
|
||||
}
|
||||
fileId = date_time_string();
|
||||
std::ostringstream oss;
|
||||
auto prefix = sdcMan.getCurrentMountPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin";
|
||||
fileName = oss.str();
|
||||
fileNameSet = true;
|
||||
ofstream out(fileName, ofstream::binary);
|
||||
if (out.bad()) {
|
||||
sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName
|
||||
<< std::endl;
|
||||
return FAILED;
|
||||
}
|
||||
out << helper;
|
||||
} else {
|
||||
ofstream out(fileName,
|
||||
ofstream::binary | ofstream::app); // append
|
||||
if (out.bad()) {
|
||||
sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName
|
||||
<< std::endl;
|
||||
return FAILED;
|
||||
}
|
||||
out << helper;
|
||||
}
|
||||
return OK;
|
||||
};
|
||||
id = helper.getCmd();
|
||||
switch (id) {
|
||||
case (PING): {
|
||||
status = oneFileHandler("ping_");
|
||||
break;
|
||||
}
|
||||
case (ION_CMD): {
|
||||
status = oneFileHandler("ion_");
|
||||
break;
|
||||
}
|
||||
case (TEMP_CMD): {
|
||||
status = oneFileHandler("temp_");
|
||||
break;
|
||||
}
|
||||
case (EXP_STATUS_CMD): {
|
||||
status = oneFileHandler("exp_status_");
|
||||
break;
|
||||
}
|
||||
case (FRAM): {
|
||||
status = multiFileHandler("fram_");
|
||||
break;
|
||||
}
|
||||
case (ONE_CELL): {
|
||||
status = multiFileHandler("one_cell_");
|
||||
break;
|
||||
}
|
||||
case (ALL_CELLS_CMD): {
|
||||
status = multiFileHandler("multi_cell_");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown DeviceCommand
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
if (helper.getPacketCounter() == helper.getTotalPacketCounter()) {
|
||||
reader.finish();
|
||||
commandActive = false;
|
||||
if (id != PING) {
|
||||
fileNameSet = false;
|
||||
}
|
||||
if (id == FRAM or id == ALL_CELLS_CMD or id == ONE_CELL) {
|
||||
triggerEvent(MULTI_PACKET_COMMAND_DONE, id);
|
||||
updatePeriodicReply(false, id);
|
||||
}
|
||||
}
|
||||
if (debugMode) {
|
||||
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
|
||||
sif::info << __FILE__ << __func__ << "(" << __LINE__ << ") RemMillis: " << remainingMillis
|
||||
<< std::endl;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void ScexDeviceHandler::performOperationHook() {
|
||||
auto mntPrefix = sdcMan.getCurrentMountPrefix();
|
||||
if (mntPrefix != nullptr) {
|
||||
std::filesystem::path fullFilePath = mntPrefix;
|
||||
std::error_code e;
|
||||
fullFilePath /= "scex";
|
||||
bool fileExists = std::filesystem::exists(fullFilePath, e);
|
||||
if (not fileExists) {
|
||||
bool created = std::filesystem::create_directory(fullFilePath, e);
|
||||
if (not created) {
|
||||
sif::error << "Could not create SCEX directory: " << e << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
|
||||
if (commandActive and finishCountdown.hasTimedOut()) {
|
||||
triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0);
|
||||
reader.finish();
|
||||
sif::warning << "ScexDeviceHandler::scanForReply: Reader timeout; RemMillis: "
|
||||
<< remainingMillis << std::endl;
|
||||
fileNameSet = false;
|
||||
commandActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ScexDeviceHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return OK; }
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::getSwitches(const uint8_t** switches, uint8_t* numberOfSwitches) {
|
||||
if (switchId) {
|
||||
*numberOfSwitches = 1;
|
||||
*switches = &switchId.value();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
std::string ScexDeviceHandler::date_time_string() {
|
||||
using namespace std;
|
||||
string date_time;
|
||||
Clock::TimeOfDay_t tod;
|
||||
Clock::getDateAndTime(&tod);
|
||||
ostringstream oss(std::ostringstream::ate);
|
||||
|
||||
if (tod.hour < 10) {
|
||||
oss << tod.year << tod.month << tod.day << "_0" << tod.hour;
|
||||
} else {
|
||||
oss << tod.year << tod.month << tod.day << "_" << tod.hour;
|
||||
}
|
||||
if (tod.minute < 10) {
|
||||
oss << 0 << tod.minute;
|
||||
|
||||
} else {
|
||||
oss << tod.minute;
|
||||
}
|
||||
if (tod.second < 10) {
|
||||
oss << 0 << tod.second;
|
||||
} else {
|
||||
oss << tod.second;
|
||||
}
|
||||
|
||||
date_time = oss.str();
|
||||
|
||||
return date_time;
|
||||
}
|
||||
|
||||
void ScexDeviceHandler::modeChanged() {}
|
||||
|
||||
void ScexDeviceHandler::setPowerSwitcher(PowerSwitchIF& powerSwitcher, power::Switch_t switchId) {
|
||||
DeviceHandlerBase::setPowerSwitcher(&powerSwitcher);
|
||||
this->switchId = switchId;
|
||||
}
|
||||
|
||||
ReturnValue_t ScexDeviceHandler::initializeAfterTaskCreation() {
|
||||
return DeviceHandlerBase::initializeAfterTaskCreation();
|
||||
}
|
65
mission/payload/ScexDeviceHandler.h
Normal file
65
mission/payload/ScexDeviceHandler.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_
|
||||
#define MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||
#include <linux/payload/ScexHelper.h>
|
||||
#include <linux/payload/ScexUartReader.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "eive/eventSubsystemIds.h"
|
||||
|
||||
class SdCardMountedIF;
|
||||
|
||||
class ScexDeviceHandler : public DeviceHandlerBase {
|
||||
public:
|
||||
ScexDeviceHandler(object_id_t objectId, ScexUartReader &reader, CookieIF *cookie,
|
||||
SdCardMountedIF &sdcMan);
|
||||
void setPowerSwitcher(PowerSwitchIF &powerSwitcher, power::Switch_t switchId);
|
||||
virtual ~ScexDeviceHandler();
|
||||
|
||||
private:
|
||||
static constexpr uint32_t LONG_CD = 180 * 1000;
|
||||
static constexpr uint32_t SHORT_CD = 12000;
|
||||
std::array<uint8_t, 64> cmdBuf = {};
|
||||
|
||||
std::optional<power::Switch_t> switchId;
|
||||
std::string fileId = "";
|
||||
std::string fileName = "";
|
||||
bool fileNameSet = false;
|
||||
bool commandActive = false;
|
||||
bool multiFileFinishOutstanding = false;
|
||||
bool debugMode = false;
|
||||
|
||||
scex::Cmds currCmd = scex::Cmds::PING;
|
||||
SdCardMountedIF &sdcMan;
|
||||
Countdown finishCountdown = Countdown(LONG_CD);
|
||||
|
||||
std::string date_time_string();
|
||||
|
||||
// DeviceHandlerBase private function implementation
|
||||
void doStartUp() override;
|
||||
void doShutDown() override;
|
||||
ScexHelper helper;
|
||||
ScexUartReader &reader;
|
||||
|
||||
void performOperationHook() override;
|
||||
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
|
||||
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
|
||||
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData,
|
||||
size_t commandDataLen) override;
|
||||
void fillCommandAndReplyMap() override;
|
||||
ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId,
|
||||
size_t *foundLen) override;
|
||||
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
|
||||
ReturnValue_t handleValidReply(size_t remSize, DeviceCommandId_t *foundId, size_t *foundLen);
|
||||
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
|
||||
ReturnValue_t getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) override;
|
||||
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
void modeChanged() override;
|
||||
};
|
||||
|
||||
#endif /* MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_ */
|
32
mission/payload/defs.cpp
Normal file
32
mission/payload/defs.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include <mission/payload/defs.h>
|
||||
|
||||
const char* payload::getModeStr(Mode mode) {
|
||||
static const char* modeStr = "UNKNOWN";
|
||||
switch (mode) {
|
||||
case (Mode::CAM_STREAM): {
|
||||
modeStr = "CAM STREAM";
|
||||
break;
|
||||
}
|
||||
case (Mode::EARTH_OBSV): {
|
||||
modeStr = "EARTH OBSV";
|
||||
break;
|
||||
}
|
||||
case (Mode::MPSOC_STREAM): {
|
||||
modeStr = "MPSOC STREAM";
|
||||
break;
|
||||
}
|
||||
case (Mode::OFF): {
|
||||
modeStr = "OFF";
|
||||
break;
|
||||
}
|
||||
case (Mode::SUPV_ONLY): {
|
||||
modeStr = "SUPV ONLY";
|
||||
break;
|
||||
}
|
||||
case (Mode::SCEX): {
|
||||
modeStr = "SCEX";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return modeStr;
|
||||
}
|
24
mission/payload/defs.h
Normal file
24
mission/payload/defs.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace payload {
|
||||
|
||||
enum Mode {
|
||||
OFF = 0,
|
||||
SUPV_ONLY = 10,
|
||||
MPSOC_STREAM = 11,
|
||||
CAM_STREAM = 12,
|
||||
EARTH_OBSV = 13,
|
||||
SCEX = 14
|
||||
};
|
||||
|
||||
extern const char* getModeStr(Mode mode);
|
||||
|
||||
namespace ploc {
|
||||
|
||||
enum Modes { OFF = 0, SUPV_ONLY = 1, MPSOC_ON = 2 };
|
||||
|
||||
}
|
||||
|
||||
} // namespace payload
|
242
mission/payload/payloadPcduDefinitions.h
Normal file
242
mission/payload/payloadPcduDefinitions.h
Normal file
@ -0,0 +1,242 @@
|
||||
#ifndef LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
|
||||
#define LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
|
||||
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include <mission/memory/NvmParameterBase.h>
|
||||
#include <mission/tcs/max1227.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
|
||||
namespace plpcdu {
|
||||
|
||||
using namespace max1227;
|
||||
|
||||
enum PlPcduAdcChannels : uint8_t {
|
||||
U_BAT_DIV_6 = 0,
|
||||
// According to schematic, will be 2.2158V for Vneg = +0V and 0.2446V for Vneg = -6V
|
||||
// Full Forumula: V_neg = V_post - (R1 + R2) / R1 * (V_pos - V_out) with R1 being 27.4k
|
||||
// and R2 being 49.9k. FB = Feedback
|
||||
U_NEG_V_FB = 1,
|
||||
I_HPA = 2,
|
||||
U_HPA_DIV_6 = 3,
|
||||
I_MPA = 4,
|
||||
U_MPA_DIV_6 = 5,
|
||||
I_TX = 6,
|
||||
U_TX_DIV_6 = 7,
|
||||
I_X8 = 8,
|
||||
U_X8_DIV_6 = 9,
|
||||
I_DRO = 10,
|
||||
U_DRO_DIV_6 = 11,
|
||||
NUM_CHANNELS = 12
|
||||
};
|
||||
|
||||
enum PlPcduParamIds : uint8_t {
|
||||
NEG_V_LOWER_BOUND = 0,
|
||||
NEG_V_UPPER_BOUND = 1,
|
||||
DRO_U_LOWER_BOUND = 2,
|
||||
DRO_U_UPPER_BOUND = 3,
|
||||
DRO_I_UPPER_BOUND = 4,
|
||||
X8_U_LOWER_BOUND = 5,
|
||||
X8_U_UPPER_BOUND = 6,
|
||||
X8_I_UPPER_BOUND = 7,
|
||||
TX_U_LOWER_BOUND = 8,
|
||||
TX_U_UPPER_BOUND = 9,
|
||||
TX_I_UPPER_BOUND = 10,
|
||||
MPA_U_LOWER_BOUND = 11,
|
||||
MPA_U_UPPER_BOUND = 12,
|
||||
MPA_I_UPPER_BOUND = 13,
|
||||
HPA_U_LOWER_BOUND = 14,
|
||||
HPA_U_UPPER_BOUND = 15,
|
||||
HPA_I_UPPER_BOUND = 16,
|
||||
|
||||
SSR_TO_DRO_WAIT_TIME = 17,
|
||||
DRO_TO_X8_WAIT_TIME = 18,
|
||||
X8_TO_TX_WAIT_TIME = 19,
|
||||
TX_TO_MPA_WAIT_TIME = 20,
|
||||
MPA_TO_HPA_WAIT_TIME = 21,
|
||||
|
||||
INJECT_SSR_TO_DRO_FAILURE = 30,
|
||||
INJECT_DRO_TO_X8_FAILURE = 31,
|
||||
INJECT_X8_TO_TX_FAILURE = 32,
|
||||
INJECT_TX_TO_MPA_FAILURE = 33,
|
||||
INJECT_MPA_TO_HPA_FAILURE = 34,
|
||||
INJECT_ALL_ON_FAILURE = 35
|
||||
};
|
||||
|
||||
static std::map<PlPcduParamIds, std::string> PARAM_KEY_MAP = {
|
||||
{NEG_V_LOWER_BOUND, "negVoltLowerBound"}, {NEG_V_UPPER_BOUND, "negVoltUpperBound"},
|
||||
{DRO_U_LOWER_BOUND, "droVoltLowerBound"}, {DRO_U_UPPER_BOUND, "droVoltUpperBound"},
|
||||
{DRO_I_UPPER_BOUND, "droCurrUpperBound"}, {X8_U_LOWER_BOUND, "x8VoltLowerBound"},
|
||||
{X8_U_UPPER_BOUND, "x8VoltUpperBound"}, {X8_I_UPPER_BOUND, "x8CurrUpperBound"},
|
||||
{TX_U_LOWER_BOUND, "txVoltLowerBound"}, {TX_U_UPPER_BOUND, "txVoltUpperBound"},
|
||||
{TX_I_UPPER_BOUND, "txCurrUpperBound"}, {MPA_U_LOWER_BOUND, "mpaVoltLowerBound"},
|
||||
{MPA_U_UPPER_BOUND, "mpaVoltUpperBound"}, {MPA_I_UPPER_BOUND, "mpaCurrUpperBound"},
|
||||
{HPA_U_LOWER_BOUND, "hpaVoltLowerBound"}, {HPA_U_UPPER_BOUND, "hpaVoltUpperBound"},
|
||||
{HPA_I_UPPER_BOUND, "hpaCurrUpperBound"}, {SSR_TO_DRO_WAIT_TIME, "ssrToDroWait"},
|
||||
{DRO_TO_X8_WAIT_TIME, "droToX8Wait"}, {X8_TO_TX_WAIT_TIME, "x8ToTxWait"},
|
||||
{TX_TO_MPA_WAIT_TIME, "txToMpaWait"}, {MPA_TO_HPA_WAIT_TIME, "mpaToHpaWait"},
|
||||
};
|
||||
|
||||
enum PlPcduPoolIds : uint32_t { CHANNEL_VEC = 0, PROCESSED_VEC = 1, TEMP = 2 };
|
||||
|
||||
static constexpr size_t MAX_ADC_REPLY_SIZE = 64;
|
||||
|
||||
static constexpr DeviceCommandId_t READ_CMD = 0;
|
||||
static constexpr DeviceCommandId_t SETUP_CMD = 1;
|
||||
static constexpr DeviceCommandId_t READ_TEMP_EXT = 2;
|
||||
static constexpr DeviceCommandId_t READ_WITH_TEMP_EXT = 3;
|
||||
|
||||
enum NormalSubmodeBits {
|
||||
SOLID_STATE_RELAYS_ADC_ON = 0,
|
||||
DRO_ON = 1,
|
||||
X8_ON = 2,
|
||||
TX_ON = 3,
|
||||
MPA_ON = 4,
|
||||
HPA_ON = 5
|
||||
};
|
||||
|
||||
static constexpr Submode_t ALL_OFF_SUBMODE = 0;
|
||||
static constexpr Submode_t ALL_ON_SUBMODE = (1 << HPA_ON) | (1 << MPA_ON) | (1 << TX_ON) |
|
||||
(1 << X8_ON) | (1 << DRO_ON) |
|
||||
(1 << SOLID_STATE_RELAYS_ADC_ON);
|
||||
|
||||
// 12 ADC values * 2 + trailing zero
|
||||
static constexpr size_t ADC_REPLY_SIZE = 25;
|
||||
// Conversion byte + 24 * zero
|
||||
static constexpr size_t TEMP_REPLY_SIZE = 25;
|
||||
|
||||
static constexpr uint8_t SETUP_BYTE =
|
||||
max1227::buildSetupByte(ClkSel::EXT_CONV_EXT_TIMED, RefSel::INT_REF_NO_WAKEUP, DiffSel::NONE_0);
|
||||
|
||||
static constexpr uint32_t ADC_SET_ID = READ_CMD;
|
||||
static constexpr uint8_t CHANNELS_NUM = 12;
|
||||
static constexpr uint8_t CHANNEL_N = CHANNELS_NUM - 1;
|
||||
// Store temperature as well
|
||||
static constexpr size_t DATASET_ENTRIES = CHANNELS_NUM + 1;
|
||||
|
||||
static constexpr uint8_t VOLTAGE_DIV = 6;
|
||||
// 12-bit ADC: 2 to the power of 12 minus 1
|
||||
static constexpr uint16_t MAX122X_BIT = 4095;
|
||||
static constexpr float MAX122X_VREF = 2.5;
|
||||
static constexpr float GAIN_INA169 = 100.0;
|
||||
static constexpr float R_SHUNT_HPA = 0.008;
|
||||
static constexpr float R_SHUNT_MPA = 0.015;
|
||||
static constexpr float R_SHUNT_TX = 0.05;
|
||||
static constexpr float R_SHUNT_X8 = 0.015;
|
||||
static constexpr float R_SHUNT_DRO = 0.22;
|
||||
static constexpr float V_POS = 3.3;
|
||||
static constexpr float VOLTAGE_DIV_U_NEG = (49.9 + 27.4) / 27.4;
|
||||
static constexpr float MAX122X_SCALE = MAX122X_VREF / MAX122X_BIT;
|
||||
static constexpr float SCALE_VOLTAGE = MAX122X_SCALE * VOLTAGE_DIV;
|
||||
static constexpr float SCALE_CURRENT_HPA = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_HPA);
|
||||
static constexpr float SCALE_CURRENT_MPA = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_MPA);
|
||||
static constexpr float SCALE_CURRENT_TX = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_TX);
|
||||
static constexpr float SCALE_CURRENT_X8 = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_X8);
|
||||
static constexpr float SCALE_CURRENT_DRO = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_DRO);
|
||||
|
||||
// TODO: Make these configurable parameters using a JSON file
|
||||
// Upper bound of currents in milliamperes [mA]
|
||||
static constexpr double DFT_NEG_V_LOWER_BOUND = -6.5;
|
||||
static constexpr double DFT_NEG_V_UPPER_BOUND = -2.7;
|
||||
|
||||
static constexpr double DFT_DRO_U_LOWER_BOUND = 5.0;
|
||||
static constexpr double DFT_DRO_U_UPPER_BOUND = 7.0;
|
||||
// Max Current DRO + Max Current Neg V | 40 + 15
|
||||
static constexpr double DFT_DRO_I_UPPER_BOUND = 55.0;
|
||||
|
||||
static constexpr double DFT_X8_U_LOWER_BOUND = 2.6;
|
||||
static constexpr double DFT_X8_U_UPPER_BOUND = 4.0;
|
||||
static constexpr double DFT_X8_I_UPPER_BOUND = 100.0;
|
||||
|
||||
static constexpr double DFT_TX_U_LOWER_BOUND = 2.6;
|
||||
static constexpr double DFT_TX_U_UPPER_BOUND = 4.0;
|
||||
static constexpr double DFT_TX_I_UPPER_BOUND = 250.0;
|
||||
|
||||
static constexpr double DFT_MPA_U_LOWER_BOUND = 2.6;
|
||||
static constexpr double DFT_MPA_U_UPPER_BOUND = 4.0;
|
||||
static constexpr double DFT_MPA_I_UPPER_BOUND = 650.0;
|
||||
|
||||
static constexpr double DFT_HPA_U_LOWER_BOUND = 9.4;
|
||||
static constexpr double DFT_HPA_U_UPPER_BOUND = 11.0;
|
||||
static constexpr double DFT_HPA_I_UPPER_BOUND = 3800.0;
|
||||
|
||||
// Wait time in floating point seconds
|
||||
static constexpr double DFT_SSR_TO_DRO_WAIT_TIME = 5.0;
|
||||
static constexpr double DFT_DRO_TO_X8_WAIT_TIME = 905.0;
|
||||
static constexpr double DFT_X8_TO_TX_WAIT_TIME = 5.0;
|
||||
static constexpr double DFT_TX_TO_MPA_WAIT_TIME = 5.0;
|
||||
static constexpr double DFT_MPA_TO_HPA_WAIT_TIME = 5.0;
|
||||
|
||||
/**
|
||||
* The current of the processed values is calculated and stored as a milliamperes [mA].
|
||||
* The voltages are stored as Volt values.
|
||||
*/
|
||||
class PlPcduAdcSet : public StaticLocalDataSet<DATASET_ENTRIES> {
|
||||
public:
|
||||
PlPcduAdcSet(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, ADC_SET_ID) {}
|
||||
|
||||
PlPcduAdcSet(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, ADC_SET_ID)) {}
|
||||
|
||||
lp_vec_t<uint16_t, 12> channels = lp_vec_t<uint16_t, 12>(sid.objectId, CHANNEL_VEC, this);
|
||||
lp_vec_t<float, 12> processed = lp_vec_t<float, 12>(sid.objectId, PROCESSED_VEC, this);
|
||||
lp_var_t<float> tempC = lp_var_t<float>(sid.objectId, TEMP, this);
|
||||
};
|
||||
|
||||
class PlPcduParameter : public NVMParameterBase {
|
||||
public:
|
||||
PlPcduParameter() : NVMParameterBase(""), mountPrefix("") {
|
||||
using namespace plpcdu;
|
||||
// Initialize with default values
|
||||
resetValues();
|
||||
}
|
||||
|
||||
ReturnValue_t initialize(std::string mountPrefix) {
|
||||
setFullName(mountPrefix + "/conf/plpcdu.json");
|
||||
ReturnValue_t result = readJsonFile();
|
||||
if (result != returnvalue::OK) {
|
||||
// File does not exist or reading JSON failed for various reason. Rewrite the JSON file
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
sif::info << "Creating PL PCDU JSON file at " << getFullName() << std::endl;
|
||||
#endif
|
||||
resetValues();
|
||||
writeJsonFile();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
void resetValues() {
|
||||
insertValue(PARAM_KEY_MAP[SSR_TO_DRO_WAIT_TIME], DFT_SSR_TO_DRO_WAIT_TIME);
|
||||
insertValue(PARAM_KEY_MAP[DRO_TO_X8_WAIT_TIME], DFT_DRO_TO_X8_WAIT_TIME);
|
||||
insertValue(PARAM_KEY_MAP[X8_TO_TX_WAIT_TIME], DFT_X8_TO_TX_WAIT_TIME);
|
||||
insertValue(PARAM_KEY_MAP[TX_TO_MPA_WAIT_TIME], DFT_TX_TO_MPA_WAIT_TIME);
|
||||
insertValue(PARAM_KEY_MAP[MPA_TO_HPA_WAIT_TIME], DFT_MPA_TO_HPA_WAIT_TIME);
|
||||
insertValue(PARAM_KEY_MAP[NEG_V_LOWER_BOUND], DFT_NEG_V_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[NEG_V_UPPER_BOUND], DFT_NEG_V_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[DRO_U_LOWER_BOUND], DFT_DRO_U_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[DRO_U_UPPER_BOUND], DFT_DRO_U_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[DRO_I_UPPER_BOUND], DFT_DRO_I_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[X8_U_LOWER_BOUND], DFT_X8_U_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[X8_U_UPPER_BOUND], DFT_X8_U_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[X8_I_UPPER_BOUND], DFT_X8_I_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[TX_U_LOWER_BOUND], DFT_TX_U_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[TX_U_UPPER_BOUND], DFT_TX_U_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[TX_I_UPPER_BOUND], DFT_TX_I_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[MPA_U_LOWER_BOUND], DFT_MPA_U_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[MPA_U_UPPER_BOUND], DFT_MPA_U_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[MPA_I_UPPER_BOUND], DFT_MPA_I_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[HPA_U_LOWER_BOUND], DFT_HPA_U_LOWER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[HPA_U_UPPER_BOUND], DFT_HPA_U_UPPER_BOUND);
|
||||
insertValue(PARAM_KEY_MAP[HPA_I_UPPER_BOUND], DFT_HPA_I_UPPER_BOUND);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mountPrefix;
|
||||
};
|
||||
|
||||
} // namespace plpcdu
|
||||
|
||||
#endif /* LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_ */
|
121
mission/payload/plocSpBase.h
Normal file
121
mission/payload/plocSpBase.h
Normal file
@ -0,0 +1,121 @@
|
||||
#ifndef MISSION_PAYLOAD_PLOCSPBASE_H_
|
||||
#define MISSION_PAYLOAD_PLOCSPBASE_H_
|
||||
|
||||
#include <fsfw/globalfunctions/CRC.h>
|
||||
#include <fsfw/tmtcpacket/ccsds/SpacePacketCreator.h>
|
||||
#include <fsfw/tmtcpacket/ccsds/SpacePacketReader.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ploc {
|
||||
|
||||
struct SpTcParams {
|
||||
SpTcParams(SpacePacketCreator& creator) : creator(creator) {}
|
||||
|
||||
SpTcParams(SpacePacketCreator& creator, uint8_t* buf, size_t maxSize)
|
||||
: creator(creator), buf(buf), maxSize(maxSize) {}
|
||||
|
||||
// Set full payload length. Must also consider the two CRC bytes
|
||||
void setFullPayloadLen(size_t fullPayloadLen_) { fullPayloadLen = fullPayloadLen_; }
|
||||
|
||||
SpacePacketCreator& creator;
|
||||
uint8_t* buf = nullptr;
|
||||
size_t maxSize = 0;
|
||||
size_t fullPayloadLen = 0;
|
||||
};
|
||||
|
||||
class SpTcBase {
|
||||
public:
|
||||
SpTcBase(SpTcParams params) : SpTcBase(params, 0x00, 1, 0) {}
|
||||
|
||||
SpTcBase(SpTcParams params, uint16_t apid, size_t payloadLen)
|
||||
: SpTcBase(params, apid, payloadLen, 0) {}
|
||||
|
||||
SpTcBase(SpTcParams params, uint16_t apid, size_t payloadLen, uint16_t seqCount)
|
||||
: spParams(params) {
|
||||
spParams.creator.setApid(apid);
|
||||
spParams.creator.setSeqCount(seqCount);
|
||||
payloadStart = spParams.buf + ccsds::HEADER_LEN;
|
||||
spParams.fullPayloadLen = payloadLen;
|
||||
updateSpFields();
|
||||
}
|
||||
|
||||
void updateSpFields() {
|
||||
updateLenFromParams();
|
||||
spParams.creator.setPacketType(ccsds::PacketType::TC);
|
||||
}
|
||||
|
||||
void updateLenFromParams() { spParams.creator.setDataLenField(spParams.fullPayloadLen - 1); }
|
||||
const uint8_t* getFullPacket() const { return spParams.buf; }
|
||||
|
||||
const uint8_t* getPacketData() const { return spParams.buf + ccsds::HEADER_LEN; }
|
||||
|
||||
size_t getFullPacketLen() const { return spParams.creator.getFullPacketLen(); }
|
||||
|
||||
uint16_t getApid() const { return spParams.creator.getApid(); }
|
||||
|
||||
uint16_t getSeqCount() const { return spParams.creator.getSequenceCount(); }
|
||||
|
||||
ReturnValue_t checkPayloadLen() {
|
||||
if (ccsds::HEADER_LEN + spParams.fullPayloadLen > spParams.maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t serializeHeader() {
|
||||
updateSpFields();
|
||||
size_t serLen = 0;
|
||||
return spParams.creator.serializeBe(spParams.buf, serLen, spParams.maxSize);
|
||||
}
|
||||
|
||||
ReturnValue_t checkSizeAndSerializeHeader() {
|
||||
ReturnValue_t result = checkPayloadLen();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
return serializeHeader();
|
||||
}
|
||||
|
||||
ReturnValue_t calcAndSetCrc() {
|
||||
/* Calculate crc */
|
||||
uint16_t crc = CRC::crc16ccitt(spParams.buf, getFullPacketLen() - 2);
|
||||
|
||||
/* Add crc to packet data field of space packet */
|
||||
size_t serializedSize = 0;
|
||||
return SerializeAdapter::serialize(&crc, spParams.buf + getFullPacketLen() - 2, &serializedSize,
|
||||
spParams.maxSize, SerializeIF::Endianness::BIG);
|
||||
}
|
||||
|
||||
protected:
|
||||
ploc::SpTcParams spParams;
|
||||
uint8_t* payloadStart;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for handling tm replies of the supervisor.
|
||||
*/
|
||||
class SpTmReader : public SpacePacketReader {
|
||||
public:
|
||||
SpTmReader() = default;
|
||||
/**
|
||||
* @brief Constructor creates idle packet and sets length field to maximum allowed size.
|
||||
*/
|
||||
SpTmReader(const uint8_t* buf, size_t maxSize) : SpacePacketReader(buf, maxSize) {}
|
||||
|
||||
ReturnValue_t setData(const uint8_t* buf, size_t maxSize) {
|
||||
return setReadOnlyData(buf, maxSize);
|
||||
}
|
||||
|
||||
ReturnValue_t checkCrc() const {
|
||||
if (CRC::crc16ccitt(getFullData(), getFullPacketLen()) != 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ploc
|
||||
|
||||
#endif /* MISSION_PAYLOAD_PLOCSPBASE_H_ */
|
83
mission/payload/radSensorDefinitions.h
Normal file
83
mission/payload/radSensorDefinitions.h
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_
|
||||
#define MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_
|
||||
|
||||
namespace RAD_SENSOR {
|
||||
|
||||
static const DeviceCommandId_t NONE = 0x0; // Set when no command is pending
|
||||
|
||||
/**
|
||||
* This command initiates the ADC conversion for all channels including the internal
|
||||
* temperature sensor.
|
||||
*/
|
||||
static const DeviceCommandId_t WRITE_SETUP = 1;
|
||||
static const DeviceCommandId_t START_CONVERSION = 2;
|
||||
static const DeviceCommandId_t READ_CONVERSIONS = 3;
|
||||
static const DeviceCommandId_t ENABLE_DEBUG_OUTPUT = 4;
|
||||
static const DeviceCommandId_t DISABLE_DEBUG_OUTPUT = 5;
|
||||
|
||||
/**
|
||||
* @brief This is the configuration byte which will be written to the setup register after
|
||||
* power on.
|
||||
*
|
||||
* @note Bit1 (DIFFSEL1) - Bit0 (DIFFSEL0): 0b00, no data follows the setup byte
|
||||
* Bit3 (REFSEL1) - Bit2 (REFSEL0): 0b10, internal reference, no wake-up delay
|
||||
* Bit5 (CLKSEL1) - Bit4 (CLKSEL0): 0b10, MAX1227 uses internal oscillator for timing
|
||||
* Bit7 - Bit6: 0b01, tells MAX1227 that this is the setup register
|
||||
*
|
||||
*/
|
||||
static const uint8_t SETUP_DEFINITION = 0b01101000;
|
||||
|
||||
/**
|
||||
* @brief This value will always be written to the ADC conversion register to specify the
|
||||
* conversions to perform.
|
||||
* @details Bit0: 1 - Enables temperature conversion
|
||||
* Bit2 (SCAN1) and Bit1 (SCAN0): 0b00 (channel conversion from 0 to N)
|
||||
* Bit6 - Bit3 defines N: 0b0111 (N = 7)
|
||||
* Bit7: Always 1. Tells the ADC that this is the conversion register.
|
||||
*/
|
||||
static const uint8_t CONVERSION_DEFINITION = 0b10111001;
|
||||
// static const uint8_t CONVERSION_DEFINITION = 0b10111111;
|
||||
|
||||
/**
|
||||
* @brief Writing this value resets the fifo of the MAX1227.
|
||||
*/
|
||||
static const uint8_t RESET_DEFINITION = 0b00011000;
|
||||
|
||||
static const uint8_t DUMMY_BYTE = 0xFF;
|
||||
|
||||
static const uint8_t RAD_SENSOR_DATA_SET_ID = READ_CONVERSIONS;
|
||||
|
||||
static const uint8_t DATASET_ENTRIES = 7;
|
||||
/**
|
||||
* One temperature value and conversions for AIN0 - AIN7
|
||||
*/
|
||||
static const uint8_t READ_SIZE = 18;
|
||||
|
||||
enum Max1227PoolIds : lp_id_t {
|
||||
TEMPERATURE_C,
|
||||
AIN0,
|
||||
AIN1,
|
||||
AIN4,
|
||||
AIN5,
|
||||
AIN6,
|
||||
AIN7,
|
||||
};
|
||||
|
||||
class RadSensorDataset : public StaticLocalDataSet<DATASET_ENTRIES> {
|
||||
public:
|
||||
RadSensorDataset(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, RAD_SENSOR_DATA_SET_ID) {}
|
||||
|
||||
RadSensorDataset(object_id_t objectId)
|
||||
: StaticLocalDataSet(sid_t(objectId, RAD_SENSOR_DATA_SET_ID)) {}
|
||||
|
||||
lp_var_t<float> temperatureCelcius = lp_var_t<float>(sid.objectId, TEMPERATURE_C, this);
|
||||
lp_var_t<uint16_t> ain0 = lp_var_t<uint16_t>(sid.objectId, AIN0, this);
|
||||
lp_var_t<uint16_t> ain1 = lp_var_t<uint16_t>(sid.objectId, AIN1, this);
|
||||
lp_var_t<uint16_t> ain4 = lp_var_t<uint16_t>(sid.objectId, AIN4, this);
|
||||
lp_var_t<uint16_t> ain5 = lp_var_t<uint16_t>(sid.objectId, AIN5, this);
|
||||
lp_var_t<uint16_t> ain6 = lp_var_t<uint16_t>(sid.objectId, AIN6, this);
|
||||
lp_var_t<uint16_t> ain7 = lp_var_t<uint16_t>(sid.objectId, AIN7, this);
|
||||
};
|
||||
} // namespace RAD_SENSOR
|
||||
|
||||
#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_ */
|
34
mission/payload/scexHelpers.cpp
Normal file
34
mission/payload/scexHelpers.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include <fsfw/globalfunctions/CRC.h>
|
||||
#include <mission/payload/scexHelpers.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
uint8_t scex::createCmdByte(Cmds cmd, bool tempCheck) {
|
||||
return (IDLE_BIT_0_DEF_STATE << 7) | (IDLE_BIT_1_DEF_STATE << 6) | (cmd << 1) | tempCheck;
|
||||
}
|
||||
|
||||
ReturnValue_t scex::prepareScexCmd(Cmds cmd, std::pair<uint8_t*, size_t> cmdBufPair, size_t& cmdLen,
|
||||
std::pair<const uint8_t*, size_t> usrDataPair, bool tempCheck) {
|
||||
using namespace scex;
|
||||
uint8_t* cmdBuf = cmdBufPair.first;
|
||||
const uint8_t* userData = usrDataPair.first;
|
||||
// Send command
|
||||
if (cmdBuf == nullptr or (cmdBufPair.second < usrDataPair.second + HEADER_LEN + CRC_LEN) or
|
||||
(usrDataPair.second > 0 and userData == nullptr)) {
|
||||
cmdLen = 0;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
cmdBuf[0] = createCmdByte(cmd, tempCheck);
|
||||
// These two fields are the packet counter and the total packet count. Those are 1 and 1 for each
|
||||
// telecommand so far
|
||||
cmdBuf[1] = 1;
|
||||
cmdBuf[2] = 1;
|
||||
cmdBuf[3] = (usrDataPair.second >> 8) & 0xff;
|
||||
cmdBuf[4] = usrDataPair.second & 0xff;
|
||||
std::memcpy(cmdBuf + HEADER_LEN, userData, usrDataPair.second);
|
||||
uint16_t crc = CRC::crc16ccitt(cmdBuf, usrDataPair.second + HEADER_LEN);
|
||||
cmdBuf[usrDataPair.second + HEADER_LEN] = (crc >> 8) & 0xff;
|
||||
cmdBuf[usrDataPair.second + HEADER_LEN + 1] = crc & 0xff;
|
||||
cmdLen = usrDataPair.second + HEADER_LEN + CRC_LEN;
|
||||
return returnvalue::OK;
|
||||
}
|
52
mission/payload/scexHelpers.h
Normal file
52
mission/payload/scexHelpers.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef MISSION_PAYLOAD_SCEXHELPERS_H_
|
||||
#define MISSION_PAYLOAD_SCEXHELPERS_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include <fsfw/events/Event.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "eive/eventSubsystemIds.h"
|
||||
#include "eive/objects.h"
|
||||
|
||||
// Definitions for the Solar Cell Experiment
|
||||
namespace scex {
|
||||
|
||||
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SCEX_HANDLER;
|
||||
|
||||
static constexpr Event MISSING_PACKET = event::makeEvent(SUBSYSTEM_ID, 0, severity::LOW);
|
||||
static constexpr Event EXPERIMENT_TIMEDOUT = event::makeEvent(SUBSYSTEM_ID, 1, severity::LOW);
|
||||
//! FRAM, One Cell or All cells command finished. P1: Command ID
|
||||
static constexpr Event MULTI_PACKET_COMMAND_DONE =
|
||||
event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO);
|
||||
|
||||
enum Cmds : DeviceCommandId_t {
|
||||
PING = 0b00111,
|
||||
ALL_CELLS_CMD = 0b00101,
|
||||
ONE_CELL = 0b00110,
|
||||
FRAM = 0b00001,
|
||||
EXP_STATUS_CMD = 0b00010,
|
||||
TEMP_CMD = 0b00011,
|
||||
ION_CMD = 0b00100,
|
||||
ERROR_REPLY = 0b01000,
|
||||
INVALID = 255
|
||||
};
|
||||
|
||||
static const std::vector<DeviceCommandId_t> VALID_CMDS = {
|
||||
PING, ALL_CELLS_CMD, ONE_CELL, FRAM, EXP_STATUS_CMD, TEMP_CMD, ION_CMD};
|
||||
|
||||
static constexpr uint8_t HEADER_LEN = 5;
|
||||
static constexpr uint8_t CRC_LEN = 2;
|
||||
|
||||
static constexpr uint8_t IDLE_BIT_0_DEF_STATE = 0;
|
||||
static constexpr uint8_t IDLE_BIT_1_DEF_STATE = 1;
|
||||
|
||||
uint8_t createCmdByte(Cmds cmd, bool tempCheck = false);
|
||||
|
||||
ReturnValue_t prepareScexCmd(Cmds cmd, std::pair<uint8_t*, size_t> cmdBufPair, size_t& cmdLen,
|
||||
std::pair<const uint8_t*, size_t> usrDataPair, bool tempCheck = false);
|
||||
|
||||
} // namespace scex
|
||||
|
||||
#endif /* MISSION_PAYLOAD_SCEXHELPERS_H_ */
|
Reference in New Issue
Block a user