v1.9.0 #175
@ -28,8 +28,13 @@
|
|||||||
#include "linux/boardtest/UartTestClass.h"
|
#include "linux/boardtest/UartTestClass.h"
|
||||||
#include "linux/csp/CspComIF.h"
|
#include "linux/csp/CspComIF.h"
|
||||||
#include "linux/csp/CspCookie.h"
|
#include "linux/csp/CspCookie.h"
|
||||||
|
#include "linux/devices/GPSHyperionLinuxController.h"
|
||||||
|
#include "linux/devices/devicedefinitions/StarTrackerDefinitions.h"
|
||||||
|
#include "linux/devices/startracker/StarTrackerHandler.h"
|
||||||
|
#include "linux/devices/startracker/StrHelper.h"
|
||||||
#include "tmtc/apid.h"
|
#include "tmtc/apid.h"
|
||||||
#include "tmtc/pusIds.h"
|
#include "tmtc/pusIds.h"
|
||||||
|
|
||||||
#if OBSW_TEST_LIBGPIOD == 1
|
#if OBSW_TEST_LIBGPIOD == 1
|
||||||
#include "linux/boardtest/LibgpiodTest.h"
|
#include "linux/boardtest/LibgpiodTest.h"
|
||||||
#endif
|
#endif
|
||||||
@ -51,13 +56,6 @@
|
|||||||
#include "fsfw_hal/linux/spi/SpiCookie.h"
|
#include "fsfw_hal/linux/spi/SpiCookie.h"
|
||||||
#include "fsfw_hal/linux/uart/UartComIF.h"
|
#include "fsfw_hal/linux/uart/UartComIF.h"
|
||||||
#include "fsfw_hal/linux/uart/UartCookie.h"
|
#include "fsfw_hal/linux/uart/UartCookie.h"
|
||||||
#include "linux/boardtest/SpiTestClass.h"
|
|
||||||
#include "linux/csp/CspComIF.h"
|
|
||||||
#include "linux/csp/CspCookie.h"
|
|
||||||
#include "linux/devices/GPSHyperionLinuxController.h"
|
|
||||||
#include "linux/devices/devicedefinitions/StarTrackerDefinitions.h"
|
|
||||||
#include "linux/devices/startracker/StarTrackerHandler.h"
|
|
||||||
#include "linux/devices/startracker/StrHelper.h"
|
|
||||||
#include "mission/core/GenericFactory.h"
|
#include "mission/core/GenericFactory.h"
|
||||||
#include "mission/devices/ACUHandler.h"
|
#include "mission/devices/ACUHandler.h"
|
||||||
#include "mission/devices/BpxBatteryHandler.h"
|
#include "mission/devices/BpxBatteryHandler.h"
|
||||||
@ -69,6 +67,7 @@
|
|||||||
#include "mission/devices/PCDUHandler.h"
|
#include "mission/devices/PCDUHandler.h"
|
||||||
#include "mission/devices/PDU1Handler.h"
|
#include "mission/devices/PDU1Handler.h"
|
||||||
#include "mission/devices/PDU2Handler.h"
|
#include "mission/devices/PDU2Handler.h"
|
||||||
|
#include "mission/devices/PayloadPcduHandler.h"
|
||||||
#include "mission/devices/PlocMPSoCHandler.h"
|
#include "mission/devices/PlocMPSoCHandler.h"
|
||||||
#include "mission/devices/RadiationSensorHandler.h"
|
#include "mission/devices/RadiationSensorHandler.h"
|
||||||
#include "mission/devices/RwHandler.h"
|
#include "mission/devices/RwHandler.h"
|
||||||
@ -83,6 +82,7 @@
|
|||||||
#include "mission/devices/devicedefinitions/RwDefinitions.h"
|
#include "mission/devices/devicedefinitions/RwDefinitions.h"
|
||||||
#include "mission/devices/devicedefinitions/SusDefinitions.h"
|
#include "mission/devices/devicedefinitions/SusDefinitions.h"
|
||||||
#include "mission/devices/devicedefinitions/SyrlinksDefinitions.h"
|
#include "mission/devices/devicedefinitions/SyrlinksDefinitions.h"
|
||||||
|
#include "mission/devices/devicedefinitions/payloadPcduDefinitions.h"
|
||||||
#include "mission/tmtc/CCSDSHandler.h"
|
#include "mission/tmtc/CCSDSHandler.h"
|
||||||
#include "mission/tmtc/VirtualChannel.h"
|
#include "mission/tmtc/VirtualChannel.h"
|
||||||
#include "mission/utility/TmFunnel.h"
|
#include "mission/utility/TmFunnel.h"
|
||||||
@ -390,8 +390,7 @@ void ObjectFactory::createSunSensorComponents(LinuxLibgpioIF* gpioComIF, SpiComI
|
|||||||
spiCookie =
|
spiCookie =
|
||||||
new SpiCookie(addresses::SUS_6, gpioIds::CS_SUS_6, q7s::SPI_DEFAULT_DEV, SUS::MAX_CMD_SIZE,
|
new SpiCookie(addresses::SUS_6, gpioIds::CS_SUS_6, q7s::SPI_DEFAULT_DEV, SUS::MAX_CMD_SIZE,
|
||||||
spi::DEFAULT_MAX_1227_MODE, spi::SUS_MAX1227_SPI_FREQ);
|
spi::DEFAULT_MAX_1227_MODE, spi::SUS_MAX1227_SPI_FREQ);
|
||||||
SusHandler* susHandler6 = new SusHandler(objects::SUS_6, 6, objects::SPI_COM_IF, spiCookie,
|
SusHandler* susHandler6 = new SusHandler(objects::SUS_6, 6, objects::SPI_COM_IF, spiCookie);
|
||||||
gpioComIF, gpioIds::CS_SUS_6);
|
|
||||||
|
|
||||||
spiCookie =
|
spiCookie =
|
||||||
new SpiCookie(addresses::SUS_7, gpioIds::CS_SUS_7, q7s::SPI_DEFAULT_DEV, SUS::MAX_CMD_SIZE,
|
new SpiCookie(addresses::SUS_7, gpioIds::CS_SUS_7, q7s::SPI_DEFAULT_DEV, SUS::MAX_CMD_SIZE,
|
||||||
@ -737,7 +736,7 @@ void ObjectFactory::createHeaterComponents() {
|
|||||||
heaterGpiosCookie->addGpio(gpioIds::HEATER_7, gpio);
|
heaterGpiosCookie->addGpio(gpioIds::HEATER_7, gpio);
|
||||||
|
|
||||||
new HeaterHandler(objects::HEATER_HANDLER, objects::GPIO_IF, heaterGpiosCookie,
|
new HeaterHandler(objects::HEATER_HANDLER, objects::GPIO_IF, heaterGpiosCookie,
|
||||||
objects::PCDU_HANDLER, pcduSwitches::TCS_BOARD_8V_HEATER_IN);
|
objects::PCDU_HANDLER, pcduSwitches::Switches::PDU2_CH3_TCS_BOARD_HEATER_IN_8V);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectFactory::createSolarArrayDeploymentComponents() {
|
void ObjectFactory::createSolarArrayDeploymentComponents() {
|
||||||
@ -757,8 +756,8 @@ void ObjectFactory::createSolarArrayDeploymentComponents() {
|
|||||||
// TODO: Find out burn time. For now set to 1000 ms.
|
// TODO: Find out burn time. For now set to 1000 ms.
|
||||||
new SolarArrayDeploymentHandler(objects::SOLAR_ARRAY_DEPL_HANDLER, objects::GPIO_IF,
|
new SolarArrayDeploymentHandler(objects::SOLAR_ARRAY_DEPL_HANDLER, objects::GPIO_IF,
|
||||||
solarArrayDeplCookie, objects::PCDU_HANDLER,
|
solarArrayDeplCookie, objects::PCDU_HANDLER,
|
||||||
pcduSwitches::DEPLOYMENT_MECHANISM, gpioIds::DEPLSA1,
|
pcduSwitches::Switches::PDU2_CH5_DEPLOYMENT_MECHANISM_8V,
|
||||||
gpioIds::DEPLSA2, 1000);
|
gpioIds::DEPLSA1, gpioIds::DEPLSA2, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectFactory::createSyrlinksComponents() {
|
void ObjectFactory::createSyrlinksComponents() {
|
||||||
@ -1181,15 +1180,6 @@ void ObjectFactory::createPlPcduComponents(LinuxLibgpioIF* gpioComIF, SpiComIF*
|
|||||||
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_DRO, consumer, Direction::OUT,
|
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_DRO, consumer, Direction::OUT,
|
||||||
gpio::Levels::LOW);
|
gpio::Levels::LOW);
|
||||||
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_DRO, gpio);
|
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_DRO, gpio);
|
||||||
consumer = "PLPCDU_ENB_HPA";
|
|
||||||
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_HPA, consumer, Direction::OUT,
|
|
||||||
gpio::Levels::LOW);
|
|
||||||
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_HPA, gpio);
|
|
||||||
consumer = "PLPCDU_ENB_MPA";
|
|
||||||
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_MPA, consumer, Direction::OUT,
|
|
||||||
gpio::Levels::LOW);
|
|
||||||
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_MPA, gpio);
|
|
||||||
|
|
||||||
consumer = "PLPCDU_ENB_X8";
|
consumer = "PLPCDU_ENB_X8";
|
||||||
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_X8, consumer, Direction::OUT,
|
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_X8, consumer, Direction::OUT,
|
||||||
gpio::Levels::LOW);
|
gpio::Levels::LOW);
|
||||||
@ -1198,6 +1188,14 @@ void ObjectFactory::createPlPcduComponents(LinuxLibgpioIF* gpioComIF, SpiComIF*
|
|||||||
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_TX, consumer, Direction::OUT,
|
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_TX, consumer, Direction::OUT,
|
||||||
gpio::Levels::LOW);
|
gpio::Levels::LOW);
|
||||||
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_TX, gpio);
|
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_TX, gpio);
|
||||||
|
consumer = "PLPCDU_ENB_MPA";
|
||||||
|
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_MPA, consumer, Direction::OUT,
|
||||||
|
gpio::Levels::LOW);
|
||||||
|
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_MPA, gpio);
|
||||||
|
consumer = "PLPCDU_ENB_HPA";
|
||||||
|
gpio = new GpiodRegularByLineName(q7s::gpioNames::PL_PCDU_ENABLE_HPA, consumer, Direction::OUT,
|
||||||
|
gpio::Levels::LOW);
|
||||||
|
plPcduGpios->addGpio(gpioIds::PLPCDU_ENB_HPA, gpio);
|
||||||
|
|
||||||
// Chip select pin is active low
|
// Chip select pin is active low
|
||||||
consumer = "PLPCDU_ADC_CS";
|
consumer = "PLPCDU_ADC_CS";
|
||||||
@ -1205,6 +1203,22 @@ void ObjectFactory::createPlPcduComponents(LinuxLibgpioIF* gpioComIF, SpiComIF*
|
|||||||
gpio::Levels::HIGH);
|
gpio::Levels::HIGH);
|
||||||
plPcduGpios->addGpio(gpioIds::PLPCDU_ADC_CS, gpio);
|
plPcduGpios->addGpio(gpioIds::PLPCDU_ADC_CS, gpio);
|
||||||
gpioComIF->addGpios(plPcduGpios);
|
gpioComIF->addGpios(plPcduGpios);
|
||||||
|
SpiCookie* spiCookie = new SpiCookie(addresses::PLPCDU_ADC, gpioIds::PLPCDU_ADC_CS,
|
||||||
|
q7s::SPI_DEFAULT_DEV, plpcdu::MAX_ADC_REPLY_SIZE,
|
||||||
|
spi::DEFAULT_MAX_1227_MODE, spi::PL_PCDU_MAX_1227_SPEED);
|
||||||
|
// Create device handler components
|
||||||
|
auto plPcduHandler =
|
||||||
|
new PayloadPcduHandler(objects::PLPCDU_HANDLER, objects::SPI_COM_IF, spiCookie, gpioComIF,
|
||||||
|
SdCardManager::instance(), false);
|
||||||
|
spiCookie->setCallbackMode(PayloadPcduHandler::extConvAsTwoCallback, plPcduHandler);
|
||||||
|
static_cast<void>(plPcduHandler);
|
||||||
|
#if OBSW_TEST_PL_PCDU == 1
|
||||||
|
plPcduHandler->setStartUpImmediately();
|
||||||
|
#if OBSW_DEBUG_PL_PCDU == 1
|
||||||
|
plPcduHandler->setToGoToNormalModeImmediately(true);
|
||||||
|
plPcduHandler->enablePeriodicPrintout(true, 5);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) {
|
void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) {
|
||||||
|
@ -32,6 +32,8 @@ static const uint32_t SUS_MAX1227_SPI_FREQ = 976'000;
|
|||||||
static constexpr uint32_t DEFAULT_MAX_1227_SPEED = 976'000;
|
static constexpr uint32_t DEFAULT_MAX_1227_SPEED = 976'000;
|
||||||
static constexpr spi::SpiModes DEFAULT_MAX_1227_MODE = spi::SpiModes::MODE_3;
|
static constexpr spi::SpiModes DEFAULT_MAX_1227_MODE = spi::SpiModes::MODE_3;
|
||||||
|
|
||||||
|
static constexpr uint32_t PL_PCDU_MAX_1227_SPEED = 976'000;
|
||||||
|
|
||||||
static constexpr uint32_t DEFAULT_ADIS16507_SPEED = 976'000;
|
static constexpr uint32_t DEFAULT_ADIS16507_SPEED = 976'000;
|
||||||
static constexpr spi::SpiModes DEFAULT_ADIS16507_MODE = spi::SpiModes::MODE_3;
|
static constexpr spi::SpiModes DEFAULT_ADIS16507_MODE = spi::SpiModes::MODE_3;
|
||||||
|
|
||||||
|
@ -17,5 +17,6 @@ target_sources(${LIB_EIVE_MISSION} PRIVATE
|
|||||||
RwHandler.cpp
|
RwHandler.cpp
|
||||||
max1227.cpp
|
max1227.cpp
|
||||||
SusHandler.cpp
|
SusHandler.cpp
|
||||||
|
PayloadPcduHandler.cpp
|
||||||
SolarArrayDeploymentHandler.cpp
|
SolarArrayDeploymentHandler.cpp
|
||||||
)
|
)
|
||||||
|
808
mission/devices/PayloadPcduHandler.cpp
Normal file
808
mission/devices/PayloadPcduHandler.cpp
Normal file
@ -0,0 +1,808 @@
|
|||||||
|
#include "PayloadPcduHandler.h"
|
||||||
|
|
||||||
|
#include <fsfw/src/fsfw/datapool/PoolReadGuard.h>
|
||||||
|
|
||||||
|
#ifdef FSFW_OSAL_LINUX
|
||||||
|
#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,
|
||||||
|
bool periodicPrintout)
|
||||||
|
: DeviceHandlerBase(objectId, comIF, cookie),
|
||||||
|
adcSet(this),
|
||||||
|
periodicPrintout(periodicPrintout),
|
||||||
|
gpioIF(gpioIF),
|
||||||
|
sdcMan(sdcMan) {}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::doStartUp() {
|
||||||
|
if ((state != States::PCDU_OFF) and (state != States::ON_TRANS_SSR)) {
|
||||||
|
// Config error
|
||||||
|
sif::error << "PayloadPcduHandler::doStartUp: Invalid state" << std::endl;
|
||||||
|
}
|
||||||
|
if (state == States::PCDU_OFF) {
|
||||||
|
// Switch on relays here
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT0);
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT1);
|
||||||
|
state = States::ON_TRANS_SSR;
|
||||||
|
transitionOk = true;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
// We are now in ON mode
|
||||||
|
startTransition(MODE_NORMAL, 0);
|
||||||
|
adcCountdown.setTimeout(50);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
adcState = AdcStates::BOOT_DELAY;
|
||||||
|
// The ADC can now be read. If the values are not close to zero, we should not allow
|
||||||
|
// transition
|
||||||
|
monMode = MonitoringMode::CLOSE_TO_ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::stateMachineToNormal() {
|
||||||
|
using namespace plpcdu;
|
||||||
|
if (adcState == AdcStates::BOOT_DELAY) {
|
||||||
|
if (adcCountdown.hasTimedOut()) {
|
||||||
|
adcState = AdcStates::SEND_SETUP;
|
||||||
|
adcCmdExecuted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (adcState == AdcStates::SEND_SETUP) {
|
||||||
|
if (adcCmdExecuted) {
|
||||||
|
adcState = AdcStates::NORMAL;
|
||||||
|
setMode(MODE_NORMAL, NORMAL_ADC_ONLY);
|
||||||
|
adcCountdown.setTimeout(100);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
adcCmdExecuted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (submode == plpcdu::NORMAL_ALL_ON) {
|
||||||
|
if (state == States::ON_TRANS_ADC_CLOSE_ZERO) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
float waitTime = DFT_SSR_TO_DRO_WAIT_TIME;
|
||||||
|
params.getValue(PARAM_KEY_MAP[SSR_TO_DRO_WAIT_TIME], waitTime);
|
||||||
|
countdown.setTimeout(std::round(waitTime * 1000));
|
||||||
|
countdown.resetTimer();
|
||||||
|
commandExecuted = true;
|
||||||
|
// TODO: For now, skip ADC check
|
||||||
|
transitionOk = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::ON_TRANS_DRO;
|
||||||
|
// Now start monitoring for negative voltages instead
|
||||||
|
monMode = MonitoringMode::NEGATIVE;
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == States::ON_TRANS_DRO) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
float waitTime = DFT_DRO_TO_X8_WAIT_TIME;
|
||||||
|
params.getValue(PARAM_KEY_MAP[DRO_TO_X8_WAIT_TIME], waitTime);
|
||||||
|
countdown.setTimeout(std::round(waitTime * 1000));
|
||||||
|
countdown.resetTimer();
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::info << "Enabling PL PCDU DRO module" << std::endl;
|
||||||
|
#endif
|
||||||
|
// Switch on DRO and start monitoring for negative voltages
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_DRO);
|
||||||
|
adcCountdown.setTimeout(100);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
commandExecuted = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::ON_TRANS_X8;
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == States::ON_TRANS_X8) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
float waitTime = DFT_X8_TO_TX_WAIT_TIME;
|
||||||
|
params.getValue(PARAM_KEY_MAP[X8_TO_TX_WAIT_TIME], waitTime);
|
||||||
|
countdown.setTimeout(std::round(waitTime * 1000));
|
||||||
|
countdown.resetTimer();
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::info << "Enabling PL PCDU X8 module" << std::endl;
|
||||||
|
#endif
|
||||||
|
// Switch on X8
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_X8);
|
||||||
|
adcCountdown.setTimeout(100);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
commandExecuted = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::ON_TRANS_TX;
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == States::ON_TRANS_TX) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
float waitTime = DFT_TX_TO_MPA_WAIT_TIME;
|
||||||
|
params.getValue(PARAM_KEY_MAP[TX_TO_MPA_WAIT_TIME], waitTime);
|
||||||
|
countdown.setTimeout(std::round(waitTime * 1000));
|
||||||
|
countdown.resetTimer();
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::info << "Enabling PL PCDU TX module" << std::endl;
|
||||||
|
#endif
|
||||||
|
// Switch on TX
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_TX);
|
||||||
|
// Wait for 100 ms before checking ADC values
|
||||||
|
adcCountdown.setTimeout(100);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
commandExecuted = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::ON_TRANS_MPA;
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == States::ON_TRANS_MPA) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
float waitTime = DFT_MPA_TO_HPA_WAIT_TIME;
|
||||||
|
params.getValue(PARAM_KEY_MAP[MPA_TO_HPA_WAIT_TIME], waitTime);
|
||||||
|
countdown.setTimeout(std::round(waitTime * 1000));
|
||||||
|
countdown.resetTimer();
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::info << "Enabling PL PCDU MPA module" << std::endl;
|
||||||
|
#endif
|
||||||
|
// Switch on MPA
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_MPA);
|
||||||
|
// Wait for 100 ms before checking ADC values
|
||||||
|
adcCountdown.setTimeout(100);
|
||||||
|
adcCountdown.resetTimer();
|
||||||
|
commandExecuted = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::ON_TRANS_HPA;
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == States::ON_TRANS_HPA) {
|
||||||
|
if (not commandExecuted) {
|
||||||
|
// Switch on HPA
|
||||||
|
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_HPA);
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::info << "Enabling PL PCDU HPA module" << std::endl;
|
||||||
|
#endif
|
||||||
|
commandExecuted = true;
|
||||||
|
}
|
||||||
|
// ADC values are ok, 5 seconds have elapsed
|
||||||
|
if (transitionOk and countdown.hasTimedOut()) {
|
||||||
|
state = States::PCDU_ON;
|
||||||
|
setMode(MODE_NORMAL, plpcdu::NORMAL_ALL_ON);
|
||||||
|
countdown.resetTimer();
|
||||||
|
commandExecuted = false;
|
||||||
|
transitionOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) {
|
||||||
|
if (mode == _MODE_TO_NORMAL) {
|
||||||
|
stateMachineToNormal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::doShutDown() { transitionBackToOff(false); }
|
||||||
|
|
||||||
|
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 (mode == _MODE_TO_NORMAL) {
|
||||||
|
return buildNormalDeviceCommand(id);
|
||||||
|
}
|
||||||
|
return NOTHING_TO_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 RETURN_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 HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t PayloadPcduHandler::interpretDeviceReply(DeviceCommandId_t id,
|
||||||
|
const uint8_t* packet) {
|
||||||
|
using namespace plpcdu;
|
||||||
|
switch (id) {
|
||||||
|
case (SETUP_CMD): {
|
||||||
|
if (mode == _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() != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return pg.getReadResult();
|
||||||
|
}
|
||||||
|
handleExtConvRead(packet);
|
||||||
|
checkAdcValues();
|
||||||
|
adcSet.setValidity(true, true);
|
||||||
|
handlePrintout();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (READ_WITH_TEMP_EXT): {
|
||||||
|
PoolReadGuard pg(&adcSet);
|
||||||
|
if (pg.getReadResult() != HasReturnvaluesIF::RETURN_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 HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PayloadPcduHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) {
|
||||||
|
// 20 minutes transition delay is allowed
|
||||||
|
return 20 * 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.subscribeForPeriodicPacket(adcSet.getSid(), false, 0.1, true);
|
||||||
|
return HasReturnvaluesIF::RETURN_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::transitionBackToOff(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::PCDU_OFF;
|
||||||
|
adcState = AdcStates::OFF;
|
||||||
|
setMode(MODE_OFF);
|
||||||
|
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)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transitionOk = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::checkJsonFileInit() {
|
||||||
|
if (not jsonFileInitComplete) {
|
||||||
|
sd::SdCard prefSd;
|
||||||
|
sdcMan->getPreferredSdCard(prefSd);
|
||||||
|
if (sdcMan->isSdCardMounted(prefSd)) {
|
||||||
|
params.initialize(sdcMan->getCurrentMountPrefix(prefSd));
|
||||||
|
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;
|
||||||
|
transitionBackToOff(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;
|
||||||
|
transitionBackToOff(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t PayloadPcduHandler::isModeCombinationValid(Mode_t mode, Submode_t submode) {
|
||||||
|
if (mode == MODE_NORMAL and submode <= 1) {
|
||||||
|
return HasReturnvaluesIF::RETURN_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 HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PayloadPcduHandler::handleFailureInjection(std::string output, Event event) {
|
||||||
|
sif::info << "PayloadPcduHandler::checkAdcValues: " << output
|
||||||
|
<< " failure injection. "
|
||||||
|
"Transitioning back to off"
|
||||||
|
<< std::endl;
|
||||||
|
triggerEvent(event, 0, 0);
|
||||||
|
transitionOk = false;
|
||||||
|
transitionBackToOff();
|
||||||
|
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 != HasReturnvaluesIF::RETURN_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 FSFW_OSAL_LINUX
|
||||||
|
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 HasReturnvaluesIF::RETURN_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 HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t PayloadPcduHandler::transferAsTwo(SpiComIF* comIf, SpiCookie* cookie,
|
||||||
|
const uint8_t* sendData, size_t sendLen,
|
||||||
|
bool tempOnly) {
|
||||||
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
|
int retval = 0;
|
||||||
|
// Prepare transfer
|
||||||
|
int fileDescriptor = 0;
|
||||||
|
std::string device = cookie->getSpiDevice();
|
||||||
|
UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
|
||||||
|
if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_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->getMutex(&timeoutType, &timeoutMs);
|
||||||
|
if (mutex == nullptr or gpioIF == nullptr) {
|
||||||
|
#if OBSW_VERBOSE_LEVEL >= 1
|
||||||
|
sif::warning << "GyroADIS16507Handler::spiSendCallback: "
|
||||||
|
"Mutex or GPIO interface invalid"
|
||||||
|
<< std::endl;
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpioId != gpio::NO_GPIO) {
|
||||||
|
result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
|
if (result != RETURN_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 HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
170
mission/devices/PayloadPcduHandler.h
Normal file
170
mission/devices/PayloadPcduHandler.h
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#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 "events/subsystemIdRanges.h"
|
||||||
|
#include "fsfw/FSFW.h"
|
||||||
|
#include "fsfw_hal/common/gpio/GpioIF.h"
|
||||||
|
#include "mission/devices/devicedefinitions/payloadPcduDefinitions.h"
|
||||||
|
#include "mission/memory/SdCardMountedIF.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, bool periodicPrintout);
|
||||||
|
|
||||||
|
void setToGoToNormalModeImmediately(bool enable);
|
||||||
|
void enablePeriodicPrintout(bool enable, uint8_t divider);
|
||||||
|
|
||||||
|
static ReturnValue_t extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie,
|
||||||
|
const uint8_t* sendData, size_t sendLen, void* args);
|
||||||
|
|
||||||
|
#ifdef FSFW_OSAL_LINUX
|
||||||
|
static ReturnValue_t transferAsTwo(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData,
|
||||||
|
size_t sendLen, bool tempOnly);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class States : uint8_t {
|
||||||
|
PCDU_OFF,
|
||||||
|
// 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
|
||||||
|
PCDU_ON,
|
||||||
|
} state = States::PCDU_OFF;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
PeriodicOperationDivider opDivider = PeriodicOperationDivider(5);
|
||||||
|
uint8_t tempReadDivisor = 1;
|
||||||
|
Countdown countdown = Countdown(5000);
|
||||||
|
Countdown adcCountdown = Countdown(50);
|
||||||
|
GpioIF* gpioIF;
|
||||||
|
SdCardMountedIF* sdcMan;
|
||||||
|
plpcdu::PlPcduParameter params;
|
||||||
|
|
||||||
|
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 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 transitionBackToOff(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();
|
||||||
|
void stateMachineToNormal();
|
||||||
|
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_ */
|
231
mission/devices/devicedefinitions/payloadPcduDefinitions.h
Normal file
231
mission/devices/devicedefinitions/payloadPcduDefinitions.h
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
#ifndef LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
|
||||||
|
#define LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
|
||||||
|
|
||||||
|
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||||
|
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include "OBSWConfig.h"
|
||||||
|
#include "mission/devices/max1227.h"
|
||||||
|
#include "mission/memory/NVMParameterBase.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;
|
||||||
|
|
||||||
|
static constexpr Submode_t NORMAL_ADC_ONLY = 0;
|
||||||
|
static constexpr Submode_t NORMAL_ALL_ON = 1;
|
||||||
|
|
||||||
|
// 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 DRO_U_LOWER_BOUND = 5.0;
|
||||||
|
static constexpr double DRO_U_UPPER_BOUND = 7.0;
|
||||||
|
// Max Current DRO + Max Current Neg V | 40 + 15
|
||||||
|
static constexpr double 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 = 3000.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 != HasReturnvaluesIF::RETURN_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 HasReturnvaluesIF::RETURN_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_ */
|
Loading…
Reference in New Issue
Block a user