eive-obsw/mission/devices/SolarArrayDeploymentHandler.cpp

495 lines
16 KiB
C++
Raw Normal View History

#include "SolarArrayDeploymentHandler.h"
2022-10-25 11:31:06 +02:00
#include <fsfw/filesystem/HasFileSystemIF.h>
2022-10-14 13:30:35 +02:00
#include <fsfw/tasks/TaskFactory.h>
2022-10-12 17:18:57 +02:00
#include <filesystem>
#include <fstream>
#include <iostream>
2022-10-12 14:24:45 +02:00
#include "devices/gpioIds.h"
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw_hal/common/gpio/GpioCookie.h"
2022-10-13 17:48:32 +02:00
static constexpr bool DEBUG_MODE = true;
2022-10-12 13:56:25 +02:00
SolarArrayDeploymentHandler::SolarArrayDeploymentHandler(object_id_t setObjectId_,
GpioIF& gpioInterface,
PowerSwitchIF& mainLineSwitcher_,
pcdu::Switches mainLineSwitch_,
2022-10-12 14:09:07 +02:00
gpioId_t deplSA1, gpioId_t deplSA2,
SdCardMountedIF& sdcMountedIF)
: SystemObject(setObjectId_),
2022-10-12 13:56:25 +02:00
gpioInterface(gpioInterface),
deplSA1(deplSA1),
deplSA2(deplSA2),
2022-10-12 13:21:58 +02:00
mainLineSwitcher(mainLineSwitcher_),
mainLineSwitch(mainLineSwitch_),
2022-10-12 14:24:45 +02:00
sdcMan(sdcMountedIF),
actionHelper(this, nullptr) {
auto mqArgs = MqArgs(setObjectId_, static_cast<void*>(this));
commandQueue = QueueFactory::instance()->createMessageQueue(
cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
}
2022-10-12 14:24:45 +02:00
SolarArrayDeploymentHandler::~SolarArrayDeploymentHandler() = default;
ReturnValue_t SolarArrayDeploymentHandler::performOperation(uint8_t operationCode) {
2022-10-12 14:24:45 +02:00
using namespace std::filesystem;
2022-10-14 14:34:35 +02:00
if (opDivider.checkAndIncrement()) {
auto activeSdc = sdcMan.getActiveSdCard();
if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_0 and
sdcMan.isSdCardUsable(activeSdc.value())) {
if (exists(SD_0_DEPL_FILE)) {
// perform autonomous deployment handling
performAutonomousDepl(sd::SdCard::SLOT_0, dryRunStringInFile(SD_0_DEPL_FILE));
}
} else if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_1 and
sdcMan.isSdCardUsable(activeSdc.value())) {
if (exists(SD_1_DEPL_FILE)) {
// perform autonomous deployment handling
performAutonomousDepl(sd::SdCard::SLOT_1, dryRunStringInFile(SD_1_DEPL_FILE));
}
2022-10-14 15:33:34 +02:00
} else {
// TODO: This is FDIR domain. If both SD cards are not available for whatever reason,
// there is not much we can do except somehow use the scratch buffer which is
// not non-volatile. Implementation effort is considerable as well.
2022-10-12 14:24:45 +02:00
}
}
2022-10-12 17:18:57 +02:00
readCommandQueue();
handleStateMachine();
2022-10-12 14:24:45 +02:00
return returnvalue::OK;
}
2022-10-14 14:55:37 +02:00
void SolarArrayDeploymentHandler::handleStateMachine() {
if (stateMachine == MAIN_POWER_ON) {
mainLineSwitcher.sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_ON);
mainSwitchCountdown.setTimeout(mainLineSwitcher.getSwitchDelayMs());
stateMachine = WAIT_MAIN_POWER_ON;
sif::info << "S/A Deployment: Deployment power line on" << std::endl;
}
if (stateMachine == MAIN_POWER_OFF) {
// These should never fail
allOff();
stateMachine = WAIT_MAIN_POWER_OFF;
sif::info << "S/A Deployment: Deployment power line off" << std::endl;
}
if (stateMachine == WAIT_MAIN_POWER_ON) {
if (checkMainPowerOn()) {
if (DEBUG_MODE) {
sif::debug << "SA DEPL FSM: WAIT_MAIN_POWER_ON done -> SWITCH_DEPL_GPIOS" << std::endl;
}
stateMachine = SWITCH_DEPL_GPIOS;
}
}
if (stateMachine == WAIT_MAIN_POWER_OFF) {
if (checkMainPowerOff()) {
if (DEBUG_MODE) {
sif::debug << "SA DEPL FSM: WAIT_MAIN_POWER_OFF done -> FSM DONE" << std::endl;
}
sif::info << "S/A Deployment: FSM done" << std::endl;
finishFsm(returnvalue::OK);
}
}
if (stateMachine == SWITCH_DEPL_GPIOS) {
burnCountdown.setTimeout(fsmInfo.burnCountdownMs);
// This should never fail
channelAlternationCd.resetTimer();
if (not fsmInfo.dryRun) {
sa2Off();
sa1On();
fsmInfo.alternationDummy = true;
}
sif::info << "S/A Deployment: Burning" << std::endl;
triggerEvent(BURN_PHASE_START, fsmInfo.burnCountdownMs, fsmInfo.dryRun);
stateMachine = BURNING;
}
if (stateMachine == BURNING) {
saGpioAlternation();
if (burnCountdown.hasTimedOut()) {
if (DEBUG_MODE) {
sif::debug << "SA DEPL FSM: BURNING done -> WAIT_MAIN_POWER_OFF" << std::endl;
}
allOff();
triggerEvent(BURN_PHASE_DONE, fsmInfo.burnCountdownMs, fsmInfo.dryRun);
stateMachine = WAIT_MAIN_POWER_OFF;
}
}
}
ReturnValue_t SolarArrayDeploymentHandler::performAutonomousDepl(sd::SdCard sdCard, bool dryRun) {
2022-10-12 15:19:21 +02:00
using namespace std::filesystem;
using namespace std;
auto initFile = [](const char* filename) {
ofstream of(filename);
of << "phase: init\n";
of << "secs_since_start: 0\n";
};
if (sdCard == sd::SdCard::SLOT_0) {
if (not exists(SD_0_DEPLY_INFO)) {
initFile(SD_0_DEPLY_INFO);
}
2022-10-14 14:55:37 +02:00
if (not autonomousDeplForFile(sd::SdCard::SLOT_0, SD_0_DEPLY_INFO, dryRun)) {
2022-10-12 15:19:21 +02:00
initFile(SD_0_DEPLY_INFO);
}
2022-10-12 17:18:57 +02:00
} else if (sdCard == sd::SdCard::SLOT_1) {
2022-10-12 15:19:21 +02:00
if (not exists(SD_1_DEPLY_INFO)) {
initFile(SD_1_DEPLY_INFO);
}
2022-10-14 14:55:37 +02:00
if (not autonomousDeplForFile(sd::SdCard::SLOT_1, SD_1_DEPLY_INFO, dryRun)) {
2022-10-12 15:19:21 +02:00
initFile(SD_1_DEPLY_INFO);
}
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
}
2022-10-14 14:55:37 +02:00
bool SolarArrayDeploymentHandler::autonomousDeplForFile(sd::SdCard sdCard, const char* filename,
bool dryRun) {
2022-10-12 17:18:57 +02:00
using namespace std;
ifstream file(filename);
string line;
string word;
unsigned int lineNum = 0;
AutonomousDeplState deplState;
bool stateSwitch = false;
uint32_t secsSinceBoot = 0;
while (std::getline(file, line)) {
2022-10-14 14:17:20 +02:00
std::istringstream iss(line);
2022-10-12 17:18:57 +02:00
if (lineNum == 0) {
2022-10-14 14:17:20 +02:00
iss >> word;
if (word.find("phase:") == string::npos) {
return false;
}
iss >> word;
if (word.find(PHASE_INIT_STR) != string::npos) {
deplState = AutonomousDeplState::INIT;
} else if (word.find(PHASE_FIRST_BURN_STR) != string::npos) {
deplState = AutonomousDeplState::FIRST_BURN;
} else if (word.find(PHASE_WAIT_STR) != string::npos) {
deplState = AutonomousDeplState::WAIT;
} else if (word.find(PHASE_SECOND_BURN_STR) != string::npos) {
deplState = AutonomousDeplState::SECOND_BURN;
} else if (word.find(PHASE_DONE) != string::npos) {
deplState = AutonomousDeplState::DONE;
} else {
return false;
}
} else if (lineNum == 1) {
iss >> word;
2022-10-14 14:34:35 +02:00
if (iss.bad()) {
return false;
}
2022-10-14 14:17:20 +02:00
if (word.find("secs_since_start:") == string::npos) {
return false;
}
2022-10-12 17:18:57 +02:00
2022-10-14 14:17:20 +02:00
iss >> secsSinceBoot;
if (iss.bad()) {
return false;
}
2022-10-14 14:34:35 +02:00
if (not initUptime) {
initUptime = secsSinceBoot;
}
2022-10-14 14:17:20 +02:00
auto switchCheck = [&](AutonomousDeplState expected) {
if (deplState != expected) {
deplState = expected;
stateSwitch = true;
2022-10-12 17:18:57 +02:00
}
2022-10-14 14:17:20 +02:00
};
if ((secsSinceBoot > FIRST_BURN_START_TIME) and (secsSinceBoot < FIRST_BURN_END_TIME)) {
switchCheck(AutonomousDeplState::FIRST_BURN);
} else if ((secsSinceBoot > WAIT_START_TIME) and (secsSinceBoot < WAIT_END_TIME)) {
switchCheck(AutonomousDeplState::WAIT);
} else if ((secsSinceBoot > SECOND_BURN_START_TIME) and
(secsSinceBoot < SECOND_BURN_END_TIME)) {
switchCheck(AutonomousDeplState::SECOND_BURN);
} else if (secsSinceBoot > SECOND_BURN_END_TIME) {
switchCheck(AutonomousDeplState::DONE);
2022-10-12 17:18:57 +02:00
}
}
lineNum++;
}
2022-10-14 14:34:35 +02:00
if (initUptime) {
secsSinceBoot = initUptime.value();
2022-10-12 17:18:57 +02:00
}
2022-10-14 14:34:35 +02:00
// Uptime has increased by X seconds so we need to update the uptime count inside the file
secsSinceBoot += Clock::getUptime().tv_sec;
2022-10-14 15:05:44 +02:00
if (stateSwitch or firstAutonomousCycle) {
2022-10-12 17:18:57 +02:00
if (deplState == AutonomousDeplState::FIRST_BURN or
deplState == AutonomousDeplState::SECOND_BURN) {
startFsmOn(config::SA_DEPL_BURN_TIME_SECS, dryRun);
2022-10-14 14:34:35 +02:00
} else if (deplState == AutonomousDeplState::WAIT or deplState == AutonomousDeplState::DONE or
deplState == AutonomousDeplState::INIT) {
2022-10-13 15:59:56 +02:00
startFsmOff();
2022-10-12 17:18:57 +02:00
}
}
2022-10-14 14:55:37 +02:00
if (deplState == AutonomousDeplState::DONE) {
remove(filename);
if (sdCard == sd::SdCard::SLOT_0) {
remove(SD_0_DEPL_FILE);
} else {
remove(SD_1_DEPL_FILE);
}
2022-10-14 14:55:37 +02:00
triggerEvent(AUTONOMOUS_DEPLOYMENT_COMPLETED);
} else {
std::ofstream of(filename);
of << "phase: ";
if (deplState == AutonomousDeplState::INIT) {
of << PHASE_INIT_STR << "\n";
} else if (deplState == AutonomousDeplState::FIRST_BURN) {
of << PHASE_FIRST_BURN_STR << "\n";
} else if (deplState == AutonomousDeplState::WAIT) {
of << PHASE_WAIT_STR << "\n";
} else if (deplState == AutonomousDeplState::SECOND_BURN) {
of << PHASE_SECOND_BURN_STR << "\n";
2022-10-13 15:59:56 +02:00
}
2022-10-14 14:55:37 +02:00
of << "secs_since_start: " << std::to_string(secsSinceBoot) << "\n";
2022-10-12 17:18:57 +02:00
}
2022-10-14 15:05:44 +02:00
if (firstAutonomousCycle) {
firstAutonomousCycle = false;
}
2022-10-14 14:55:37 +02:00
return true;
}
2022-10-12 17:18:57 +02:00
bool SolarArrayDeploymentHandler::checkMainPowerOn() { return checkMainPower(true); }
bool SolarArrayDeploymentHandler::checkMainPowerOff() { return checkMainPower(false); }
bool SolarArrayDeploymentHandler::checkMainPower(bool onOff) {
if ((onOff and mainLineSwitcher.getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_ON) or
(not onOff and
mainLineSwitcher.getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_OFF)) {
return true;
}
if (mainSwitchCountdown.hasTimedOut()) {
if (onOff) {
triggerEvent(MAIN_SWITCH_ON_TIMEOUT);
} else {
triggerEvent(MAIN_SWITCH_OFF_TIMEOUT);
}
if (retryCounter < 3) {
if (onOff) {
stateMachine = MAIN_POWER_ON;
} else {
stateMachine = MAIN_POWER_OFF;
}
retryCounter++;
} else {
finishFsm(MAIN_SWITCH_TIMEOUT_FAILURE);
}
}
return false;
}
2022-10-13 18:42:47 +02:00
bool SolarArrayDeploymentHandler::startFsmOn(uint32_t burnCountdownSecs, bool dryRun) {
2022-10-13 15:59:56 +02:00
if (stateMachine != StateMachine::IDLE) {
2022-10-12 17:18:57 +02:00
return false;
}
2022-10-13 18:42:47 +02:00
if (burnCountdownSecs > config::SA_DEPL_MAX_BURN_TIME) {
burnCountdownSecs = config::SA_DEPL_MAX_BURN_TIME;
2022-10-13 15:59:56 +02:00
}
fsmInfo.dryRun = dryRun;
2022-10-13 18:42:47 +02:00
fsmInfo.burnCountdownMs = burnCountdownSecs * 1000;
2022-10-13 18:39:14 +02:00
stateMachine = StateMachine::MAIN_POWER_ON;
2022-10-12 17:18:57 +02:00
retryCounter = 0;
return true;
}
2022-10-13 15:59:56 +02:00
void SolarArrayDeploymentHandler::startFsmOff() {
2022-10-13 16:04:34 +02:00
if (stateMachine != StateMachine::IDLE) {
2022-10-13 15:59:56 +02:00
// off commands override the state machine. Cancel any active action commands.
finishFsm(returnvalue::FAILED);
}
retryCounter = 0;
2022-10-14 14:34:35 +02:00
stateMachine = StateMachine::MAIN_POWER_OFF;
2022-10-13 15:59:56 +02:00
}
2022-10-12 17:18:57 +02:00
void SolarArrayDeploymentHandler::finishFsm(ReturnValue_t resultForActionHelper) {
retryCounter = 0;
stateMachine = StateMachine::IDLE;
2022-10-13 17:48:32 +02:00
fsmInfo.dryRun = false;
fsmInfo.alternationDummy = false;
2022-10-12 17:18:57 +02:00
if (actionActive) {
2022-10-13 15:59:56 +02:00
bool success = false;
2022-10-13 16:04:34 +02:00
if (resultForActionHelper == returnvalue::OK or
resultForActionHelper == HasActionsIF::EXECUTION_FINISHED) {
2022-10-13 15:59:56 +02:00
success = true;
}
actionHelper.finish(success, rememberCommanderId, activeCmd, resultForActionHelper);
2022-10-12 17:18:57 +02:00
}
}
2022-10-13 15:59:56 +02:00
void SolarArrayDeploymentHandler::allOff() {
deploymentTransistorsOff();
2022-10-13 15:59:56 +02:00
mainLineSwitcher.sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
mainSwitchCountdown.setTimeout(mainLineSwitcher.getSwitchDelayMs());
}
bool SolarArrayDeploymentHandler::dryRunStringInFile(const char* filename) {
std::ifstream ifile(filename);
if (ifile.bad()) {
return false;
}
std::string line;
while (getline(ifile, line)) {
if (line.find("dryrun") != std::string::npos) {
return true;
2022-10-13 15:09:47 +02:00
}
2022-10-12 17:18:57 +02:00
}
return false;
2022-10-12 17:18:57 +02:00
}
ReturnValue_t SolarArrayDeploymentHandler::executeAction(ActionId_t actionId,
MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) {
2022-10-12 15:19:21 +02:00
ReturnValue_t result = returnvalue::OK;
2022-10-12 17:21:10 +02:00
if (actionId == DEPLOY_SOLAR_ARRAYS_MANUALLY) {
2022-10-13 15:09:47 +02:00
ManualDeploymentCommand cmd;
if (size < cmd.getSerializedSize()) {
return HasActionsIF::INVALID_PARAMETERS;
}
result = cmd.deSerialize(&data, &size, SerializeIF::Endianness::NETWORK);
if (result != returnvalue::OK) {
return result;
}
2022-10-13 15:59:56 +02:00
uint32_t burnCountdown = cmd.getBurnTime();
if (not startFsmOn(burnCountdown, cmd.isDryRun())) {
2022-10-13 15:59:56 +02:00
return HasActionsIF::IS_BUSY;
}
2022-10-13 18:42:47 +02:00
actionActive = true;
2022-10-13 18:44:50 +02:00
rememberCommanderId = commandedBy;
2022-10-13 15:59:56 +02:00
return result;
2022-10-13 16:04:34 +02:00
} else if (actionId == SWITCH_OFF_DEPLOYMENT) {
startFsmOff();
2022-10-13 18:42:47 +02:00
actionActive = true;
2022-10-13 18:44:50 +02:00
rememberCommanderId = commandedBy;
2022-10-13 16:04:34 +02:00
return result;
2022-10-13 15:09:47 +02:00
} else {
return HasActionsIF::INVALID_ACTION_ID;
2022-10-12 17:21:10 +02:00
}
return result;
}
ReturnValue_t SolarArrayDeploymentHandler::saGpioAlternation() {
ReturnValue_t status = returnvalue::OK;
ReturnValue_t result;
2022-10-13 17:48:32 +02:00
if (channelAlternationCd.hasTimedOut() and not fsmInfo.dryRun) {
if (fsmInfo.alternationDummy) {
result = sa1Off();
if (result != returnvalue::OK) {
status = result;
}
2022-10-14 13:30:35 +02:00
TaskFactory::delayTask(1);
result = sa2On();
if (result != returnvalue::OK) {
status = result;
}
} else {
2022-10-14 13:30:35 +02:00
result = sa2Off();
if (result != returnvalue::OK) {
status = result;
}
2022-10-14 13:30:35 +02:00
TaskFactory::delayTask(1);
result = sa1On();
if (result != returnvalue::OK) {
status = result;
}
}
fsmInfo.alternationDummy = not fsmInfo.alternationDummy;
channelAlternationCd.resetTimer();
}
return status;
}
ReturnValue_t SolarArrayDeploymentHandler::deploymentTransistorsOff() {
ReturnValue_t status = returnvalue::OK;
ReturnValue_t result = sa1Off();
if (result != returnvalue::OK) {
status = result;
}
result = sa2Off();
if (result != returnvalue::OK) {
status = result;
}
return status;
}
ReturnValue_t SolarArrayDeploymentHandler::sa1On() {
ReturnValue_t result = gpioInterface.pullHigh(deplSA1);
if (result != returnvalue::OK) {
sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar"
" array deployment switch 1 high"
<< std::endl;
// If gpio switch high failed, state machine is reset to wait for a command re-initiating
// the deployment sequence.
triggerEvent(DEPL_SA1_GPIO_SWTICH_ON_FAILED);
}
return result;
}
ReturnValue_t SolarArrayDeploymentHandler::sa1Off() {
ReturnValue_t result = gpioInterface.pullLow(deplSA1);
if (result != returnvalue::OK) {
sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar"
" array deployment switch 1 low"
<< std::endl;
// If gpio switch high failed, state machine is reset to wait for a command re-initiating
// the deployment sequence.
triggerEvent(DEPL_SA1_GPIO_SWTICH_OFF_FAILED);
}
return result;
}
ReturnValue_t SolarArrayDeploymentHandler::sa2On() {
ReturnValue_t result = gpioInterface.pullHigh(deplSA2);
if (result != returnvalue::OK) {
sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar"
" array deployment switch 2 high"
<< std::endl;
// If gpio switch high failed, state machine is reset to wait for a command re-initiating
// the deployment sequence.
triggerEvent(DEPL_SA2_GPIO_SWTICH_ON_FAILED);
}
return result;
}
ReturnValue_t SolarArrayDeploymentHandler::sa2Off() {
ReturnValue_t result = gpioInterface.pullLow(deplSA2);
if (result != returnvalue::OK) {
sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar"
" array deployment switch 2 low"
<< std::endl;
// If gpio switch high failed, state machine is reset to wait for a command re-initiating
// the deployment sequence.
triggerEvent(DEPL_SA2_GPIO_SWTICH_OFF_FAILED);
}
return result;
}
2022-10-13 17:48:32 +02:00
void SolarArrayDeploymentHandler::readCommandQueue() {
CommandMessage command;
ReturnValue_t result = commandQueue->receiveMessage(&command);
if (result != returnvalue::OK) {
return;
}
result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
return;
}
}
MessageQueueId_t SolarArrayDeploymentHandler::getCommandQueue() const {
return commandQueue->getId();
}
2022-10-13 17:48:32 +02:00
ReturnValue_t SolarArrayDeploymentHandler::initialize() {
ReturnValue_t result = actionHelper.initialize(commandQueue);
if (result != returnvalue::OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
return SystemObject::initialize();
}