eive-obsw/mission/devices/HeaterHandler.cpp
Robin Mueller 0172351e89
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
additional try catch block
2022-05-02 22:58:06 +02:00

301 lines
10 KiB
C++

#include "HeaterHandler.h"
#include <fsfw/health/HealthTableIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw_hal/common/gpio/GpioCookie.h>
#include <stdexcept>
#include "devices/gpioIds.h"
#include "devices/powerSwitcherList.h"
HeaterHandler::HeaterHandler(object_id_t setObjectId_, GpioIF* gpioInterface_, HeaterHelper helper,
PowerSwitchIF* mainLineSwitcher_, power::Switch_t mainLineSwitch_)
: SystemObject(setObjectId_),
helper(helper),
gpioInterface(gpioInterface_),
mainLineSwitcher(mainLineSwitcher_),
mainLineSwitch(mainLineSwitch_),
actionHelper(this, nullptr) {
for (const auto& heater : helper.heaters) {
if (heater.first == nullptr) {
throw std::invalid_argument("HeaterHandler::HeaterHandler: Invalid Heater Object");
}
}
if (gpioInterface_ == nullptr) {
throw std::invalid_argument("HeaterHandler::HeaterHandler: Invalid Gpio IF");
}
if (mainLineSwitcher == nullptr) {
throw std::invalid_argument("HeaterHandler::HeaterHandler: Invalid PowerSwitchIF");
}
auto mqArgs = MqArgs(setObjectId_, static_cast<void*>(this));
commandQueue = QueueFactory::instance()->createMessageQueue(
cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
}
HeaterHandler::~HeaterHandler() {}
ReturnValue_t HeaterHandler::performOperation(uint8_t operationCode) {
try {
readCommandQueue();
handleActiveCommands();
for (const auto& heater : helper.heaters) {
heater.first->performOperation(0);
}
} catch(const std::out_of_range& e) {
sif::warning << "HeaterHandler::performOperation: "
"Out of range error | " << e.what() << std::endl;
}
return RETURN_OK;
}
ReturnValue_t HeaterHandler::initialize() {
ReturnValue_t result = SystemObject::initialize();
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = initializeHeaterMap();
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
if (ipcStore == nullptr) {
sif::error << "HeaterHandler::initialize: IPC store not set up in factory." << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = actionHelper.initialize(commandQueue);
if (result != RETURN_OK) {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
return RETURN_OK;
}
ReturnValue_t HeaterHandler::initializeHeaterMap() {
for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) {
heaterVec.push_back(HeaterWrapper(helper.heaters[switchNr]));
}
return RETURN_OK;
}
void HeaterHandler::readCommandQueue() {
ReturnValue_t result = RETURN_OK;
CommandMessage command;
do {
result = commandQueue->receiveMessage(&command);
if (result == MessageQueueIF::EMPTY) {
break;
} else if (result != RETURN_OK) {
sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl;
}
result = actionHelper.handleActionMessage(&command);
if (result == RETURN_OK) {
continue;
}
} while (result == RETURN_OK);
}
ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) {
if (data == nullptr or size < 2) {
return HasActionsIF::INVALID_PARAMETERS;
}
if (actionId != SWITCH_HEATER) {
return COMMAND_NOT_SUPPORTED;
}
auto switchNr = *data;
if (switchNr > 7) {
return HasActionsIF::INVALID_PARAMETERS;
}
auto heater = heaterVec.at(switchNr);
HasHealthIF::HealthState health = heater.healthDevice->getHealth();
if (health == HasHealthIF::FAULTY or health == HasHealthIF::PERMANENT_FAULTY or
health == HasHealthIF::NEEDS_RECOVERY) {
return HasHealthIF::OBJECT_NOT_HEALTHY;
}
if (heater.active) {
return COMMAND_ALREADY_WAITING;
}
auto action = data[1];
if (action != 0 and action != 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
heater.action = static_cast<SwitchAction>(data[1]);
heater.active = true;
heater.replyQueue = commandedBy;
return RETURN_OK;
}
ReturnValue_t HeaterHandler::sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) {
ReturnValue_t result;
store_address_t storeAddress;
uint8_t commandData[2] = {};
switch (onOff) {
case PowerSwitchIF::SWITCH_ON:
commandData[0] = switchNr;
commandData[1] = SET_SWITCH_ON;
break;
case PowerSwitchIF::SWITCH_OFF:
commandData[0] = switchNr;
commandData[1] = SET_SWITCH_OFF;
break;
default:
sif::error << "HeaterHandler::sendSwitchCommand: Invalid switch request" << std::endl;
break;
}
result = ipcStore->addData(&storeAddress, commandData, sizeof(commandData));
if (result == RETURN_OK) {
CommandMessage message;
ActionMessage::setCommand(&message, SWITCH_HEATER, storeAddress);
/* Send heater command to own command queue */
result = commandQueue->sendMessage(commandQueue->getId(), &message, 0);
if (result != RETURN_OK) {
sif::debug << "HeaterHandler::sendSwitchCommand: Failed to send switch"
<< "message" << std::endl;
}
}
return result;
}
void HeaterHandler::handleActiveCommands() {
for (uint8_t idx = 0; idx < heater::NUMBER_OF_SWITCHES; idx++) {
if (heaterVec[idx].active) {
switch (heaterVec[idx].action) {
case SET_SWITCH_ON:
handleSwitchOnCommand(static_cast<heater::Switchers>(idx));
break;
case SET_SWITCH_OFF:
handleSwitchOffCommand(static_cast<heater::Switchers>(idx));
break;
default:
sif::error << "HeaterHandler::handleActiveCommands: Invalid action commanded"
<< std::endl;
break;
}
}
}
}
void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) {
ReturnValue_t result = RETURN_OK;
auto& heater = heaterVec.at(heaterIdx);
/* Check if command waits for main switch being set on and whether the timeout has expired */
if (heater.waitMainSwitchOn && heater.mainSwitchCountdown.hasTimedOut()) {
// TODO - This requires the initiation of an FDIR procedure
triggerEvent(MAIN_SWITCH_TIMEOUT);
sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout"
<< std::endl;
heater.active = false;
heater.waitMainSwitchOn = false;
if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT);
}
return;
}
// Check state of main line switch
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_ON) {
if (!checkSwitchState(heaterIdx)) {
gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullHigh(gpioId);
if (result != RETURN_OK) {
sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull gpio with id " << gpioId
<< " high" << std::endl;
triggerEvent(GPIO_PULL_HIGH_FAILED, result);
} else {
heater.switchState = ON;
}
} else {
triggerEvent(SWITCH_ALREADY_ON, heaterIdx);
}
// There is no need to send action finish replies if the sender was the
// HeaterHandler itself
if (heater.replyQueue != commandQueue->getId()) {
if (result == RETURN_OK) {
actionHelper.finish(true, heater.replyQueue, heater.action, result);
} else {
actionHelper.finish(false, heater.replyQueue, heater.action, result);
}
}
heater.active = false;
heater.waitMainSwitchOn = false;
} else if (mainSwitchState == PowerSwitchIF::SWITCH_OFF && heater.waitMainSwitchOn) {
// Just waiting for the main switch being set on
return;
} else if (mainSwitchState == PowerSwitchIF::SWITCH_OFF) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_ON);
heater.mainSwitchCountdown.setTimeout(mainLineSwitcher->getSwitchDelayMs());
heater.waitMainSwitchOn = true;
} else {
sif::debug << "HeaterHandler::handleActiveCommands: Failed to get state of"
<< " main line switch" << std::endl;
if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, mainSwitchState);
}
heater.active = false;
}
}
void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) {
ReturnValue_t result = RETURN_OK;
auto& heater = heaterVec.at(heaterIdx);
// Check whether switch is already off
if (checkSwitchState(heaterIdx)) {
gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullLow(gpioId);
if (result != RETURN_OK) {
sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId
<< " low" << std::endl;
triggerEvent(GPIO_PULL_LOW_FAILED, result);
} else {
heater.switchState = OFF;
// When all switches are off, also main line switch will be turned off
if (allSwitchesOff()) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
}
}
} else {
sif::info << "HeaterHandler::handleSwitchOffCommand: Switch already off" << std::endl;
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
}
if (heater.replyQueue != NO_COMMANDER) {
// Report back switch command reply if necessary
if (result == HasReturnvaluesIF::RETURN_OK) {
actionHelper.finish(true, heater.replyQueue, heater.action, result);
} else {
actionHelper.finish(false, heater.replyQueue, heater.action, result);
}
}
heater.active = false;
}
HeaterHandler::SwitchState HeaterHandler::checkSwitchState(heater::Switchers switchNr) {
return heaterVec.at(switchNr).switchState;
}
bool HeaterHandler::allSwitchesOff() {
bool allSwitchesOrd = false;
/* Or all switches. As soon one switch is on, allSwitchesOrd will be true */
for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) {
allSwitchesOrd = allSwitchesOrd || heaterVec.at(switchNr).switchState;
}
return !allSwitchesOrd;
}
MessageQueueId_t HeaterHandler::getCommandQueue() const { return commandQueue->getId(); }
ReturnValue_t HeaterHandler::sendFuseOnCommand(uint8_t fuseNr) { return RETURN_OK; }
ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const { return 0; }
ReturnValue_t HeaterHandler::getFuseState(uint8_t fuseNr) const { return 0; }
uint32_t HeaterHandler::getSwitchDelayMs(void) const { return 2000; }