eive-obsw/mission/devices/HeaterHandler.cpp

372 lines
13 KiB
C++
Raw Normal View History

2021-02-14 18:30:12 +01:00
#include "HeaterHandler.h"
2021-05-17 16:37:29 +02:00
2022-05-02 17:37:00 +02:00
#include <fsfw/health/HealthTableIF.h>
#include <fsfw/ipc/QueueFactory.h>
2022-01-17 15:58:27 +01:00
#include <fsfw/objectmanager/ObjectManager.h>
2021-08-03 15:58:01 +02:00
#include <fsfw_hal/common/gpio/GpioCookie.h>
2021-01-28 14:55:21 +01:00
2022-05-02 17:37:00 +02:00
#include <stdexcept>
2022-01-17 15:58:27 +01:00
#include "devices/powerSwitcherList.h"
HeaterHandler::HeaterHandler(object_id_t setObjectId_, GpioIF* gpioInterface_, HeaterHelper helper,
PowerSwitchIF* mainLineSwitcher_, power::Switch_t mainLineSwitch_)
2022-01-17 15:58:27 +01:00
: SystemObject(setObjectId_),
helper(helper),
gpioInterface(gpioInterface_),
2022-05-02 17:37:00 +02:00
mainLineSwitcher(mainLineSwitcher_),
2022-01-17 15:58:27 +01:00
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");
}
2022-05-02 17:37:00 +02:00
if (mainLineSwitcher == nullptr) {
throw std::invalid_argument("HeaterHandler::HeaterHandler: Invalid PowerSwitchIF");
}
heaterMutex = MutexFactory::instance()->createMutex();
if (heaterMutex == nullptr) {
throw std::runtime_error("HeaterHandler::HeaterHandler: Creating Mutex failed");
}
2022-02-22 20:27:13 +01:00
auto mqArgs = MqArgs(setObjectId_, static_cast<void*>(this));
2022-01-17 15:58:27 +01:00
commandQueue = QueueFactory::instance()->createMessageQueue(
2022-02-22 20:27:13 +01:00
cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
2021-01-23 17:22:40 +01:00
}
2022-01-17 15:58:27 +01:00
HeaterHandler::~HeaterHandler() {}
2021-01-23 17:22:40 +01:00
2021-01-28 14:55:21 +01:00
ReturnValue_t HeaterHandler::performOperation(uint8_t operationCode) {
2022-05-02 22:58:06 +02:00
try {
readCommandQueue();
for (const auto& heater : helper.heaters) {
heater.first->performOperation(0);
}
2022-05-12 20:44:36 +02:00
handleSwitchHandling();
} catch (const std::out_of_range& e) {
2022-05-02 22:58:06 +02:00
sif::warning << "HeaterHandler::performOperation: "
"Out of range error | "
<< e.what() << std::endl;
2022-01-17 15:58:27 +01:00
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
2021-01-23 17:22:40 +01:00
}
2021-01-28 14:55:21 +01:00
ReturnValue_t HeaterHandler::initialize() {
2022-01-17 15:58:27 +01:00
ReturnValue_t result = SystemObject::initialize();
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = initializeHeaterMap();
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
return ObjectManagerIF::CHILD_INIT_FAILED;
}
ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
if (ipcStore == nullptr) {
2022-01-17 15:58:27 +01:00
sif::error << "HeaterHandler::initialize: IPC store not set up in factory." << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = actionHelper.initialize(commandQueue);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
return ObjectManagerIF::CHILD_INIT_FAILED;
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
2021-01-23 17:22:40 +01:00
}
2022-01-17 15:58:27 +01:00
ReturnValue_t HeaterHandler::initializeHeaterMap() {
2022-05-02 22:55:39 +02:00
for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) {
heaterVec.push_back(HeaterWrapper(helper.heaters[switchNr], SwitchState::OFF));
2022-01-17 15:58:27 +01:00
}
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
2021-01-28 14:55:21 +01:00
}
2021-01-23 17:22:40 +01:00
2021-01-28 14:55:21 +01:00
void HeaterHandler::readCommandQueue() {
2022-08-24 17:27:47 +02:00
ReturnValue_t result = returnvalue::OK;
2022-01-17 15:58:27 +01:00
CommandMessage command;
2022-05-02 17:51:00 +02:00
do {
result = commandQueue->receiveMessage(&command);
if (result == MessageQueueIF::EMPTY) {
break;
2022-08-24 17:27:47 +02:00
} else if (result != returnvalue::OK) {
2022-05-02 17:51:00 +02:00
sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl;
}
result = actionHelper.handleActionMessage(&command);
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
2022-05-02 17:51:00 +02:00
continue;
}
2022-08-24 17:27:47 +02:00
} while (result == returnvalue::OK);
2021-01-23 17:22:40 +01:00
}
2022-01-17 15:58:27 +01:00
ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) {
if (data == nullptr or size < 3) {
return HasActionsIF::INVALID_PARAMETERS;
}
2022-01-17 15:58:27 +01:00
if (actionId != SWITCH_HEATER) {
return COMMAND_NOT_SUPPORTED;
2022-01-17 15:58:27 +01:00
}
2022-05-04 10:34:11 +02:00
auto switchNr = data[0];
if (switchNr > 7) {
return HasActionsIF::INVALID_PARAMETERS;
}
2022-05-04 10:34:11 +02:00
auto& heater = heaterVec.at(switchNr);
2022-05-12 20:44:36 +02:00
auto actionRaw = data[1];
if (actionRaw != 0 and actionRaw != 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
2022-05-12 20:44:36 +02:00
auto action = static_cast<SwitchAction>(data[1]);
// Always accepts OFF commands
if (action == SwitchAction::SET_SWITCH_ON) {
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;
}
CmdSourceParam cmdSource = CmdSourceParam::EXTERNAL;
uint8_t cmdSourceRaw = data[2];
if (cmdSourceRaw > 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
cmdSource = static_cast<CmdSourceParam>(data[2]);
if (health == HasHealthIF::EXTERNAL_CONTROL and cmdSource == CmdSourceParam::INTERNAL) {
return HasHealthIF::IS_EXTERNALLY_CONTROLLED;
}
}
2022-05-12 20:44:36 +02:00
if (heater.cmdActive) {
return COMMAND_ALREADY_WAITING;
}
2022-05-12 20:44:36 +02:00
heater.action = action;
heater.cmdActive = true;
heater.replyQueue = commandedBy;
2022-08-24 17:27:47 +02:00
return returnvalue::OK;
2021-01-23 17:22:40 +01:00
}
2022-03-30 12:22:49 +02:00
ReturnValue_t HeaterHandler::sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) {
2022-01-17 15:58:27 +01:00
ReturnValue_t result;
store_address_t storeAddress;
uint8_t commandData[3] = {};
2022-01-17 15:58:27 +01:00
switch (onOff) {
case PowerSwitchIF::SWITCH_ON:
commandData[0] = switchNr;
commandData[1] = SET_SWITCH_ON;
commandData[2] = CmdSourceParam::INTERNAL;
2022-01-17 15:58:27 +01:00
break;
case PowerSwitchIF::SWITCH_OFF:
commandData[0] = switchNr;
commandData[1] = SET_SWITCH_OFF;
commandData[2] = CmdSourceParam::INTERNAL;
2022-01-17 15:58:27 +01:00
break;
default:
sif::error << "HeaterHandler::sendSwitchCommand: Invalid switch request" << std::endl;
break;
}
result = ipcStore->addData(&storeAddress, commandData, sizeof(commandData));
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
2022-01-17 15:58:27 +01:00
CommandMessage message;
ActionMessage::setCommand(&message, SWITCH_HEATER, storeAddress);
/* Send heater command to own command queue */
result = commandQueue->sendMessage(commandQueue->getId(), &message, 0);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
sif::debug << "HeaterHandler::sendSwitchCommand: Failed to send switch"
<< "message" << std::endl;
}
}
2022-03-30 12:22:49 +02:00
return result;
2021-01-23 17:22:40 +01:00
}
2022-05-12 20:44:36 +02:00
void HeaterHandler::handleSwitchHandling() {
2022-05-02 22:55:39 +02:00
for (uint8_t idx = 0; idx < heater::NUMBER_OF_SWITCHES; idx++) {
2022-05-12 20:44:36 +02:00
auto health = heaterVec[idx].healthDevice->getHealth();
if (heaterVec[idx].switchState == SwitchState::ON) {
// If a heater is still on but the device was marked faulty by the operator, the SW
// will shut down the heater
if (health == HasHealthIF::FAULTY or health == HasHealthIF::PERMANENT_FAULTY) {
heaterVec[idx].cmdActive = true;
heaterVec[idx].action = SET_SWITCH_OFF;
triggerEvent(FAULTY_HEATER_WAS_ON, idx, 0);
handleSwitchOffCommand(static_cast<heater::Switchers>(idx));
continue;
}
}
if (heaterVec[idx].cmdActive) {
switch (heaterVec[idx].action) {
2022-01-17 15:58:27 +01:00
case SET_SWITCH_ON:
2022-05-02 22:55:39 +02:00
handleSwitchOnCommand(static_cast<heater::Switchers>(idx));
2022-01-17 15:58:27 +01:00
break;
case SET_SWITCH_OFF:
2022-05-02 22:55:39 +02:00
handleSwitchOffCommand(static_cast<heater::Switchers>(idx));
2022-01-17 15:58:27 +01:00
break;
default:
sif::error << "HeaterHandler::handleActiveCommands: Invalid action commanded"
<< std::endl;
break;
}
}
}
2021-01-23 17:22:40 +01:00
}
2022-05-02 22:55:39 +02:00
void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) {
2022-08-24 17:27:47 +02:00
ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx);
2022-01-17 15:58:27 +01:00
/* Check if command waits for main switch being set on and whether the timeout has expired */
if (heater.waitMainSwitchOn && heater.mainSwitchCountdown.hasTimedOut()) {
2022-01-17 15:58:27 +01:00
// TODO - This requires the initiation of an FDIR procedure
triggerEvent(MAIN_SWITCH_TIMEOUT);
sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout"
<< std::endl;
2022-05-12 20:44:36 +02:00
heater.cmdActive = false;
heater.waitMainSwitchOn = false;
if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT);
2021-02-11 08:19:48 +01:00
}
2022-01-17 15:58:27 +01:00
return;
}
// Check state of main line switch
2022-01-17 15:58:27 +01:00
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_ON) {
if (checkSwitchState(heaterIdx) == SwitchState::OFF) {
gpioId_t gpioId = heater.gpioId;
2022-01-17 15:58:27 +01:00
result = gpioInterface->pullHigh(gpioId);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull gpio with id " << gpioId
<< " high" << std::endl;
triggerEvent(GPIO_PULL_HIGH_FAILED, result);
} else {
triggerEvent(HEATER_WENT_ON, heaterIdx, 0);
heater.switchState = ON;
2022-01-17 15:58:27 +01:00
}
} 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()) {
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
actionHelper.finish(true, heater.replyQueue, heater.action, result);
2022-01-17 15:58:27 +01:00
} else {
actionHelper.finish(false, heater.replyQueue, heater.action, result);
2022-01-17 15:58:27 +01:00
}
}
2022-05-12 20:44:36 +02:00
heater.cmdActive = false;
heater.waitMainSwitchOn = false;
} else if (mainSwitchState == PowerSwitchIF::SWITCH_OFF && heater.waitMainSwitchOn) {
// Just waiting for the main switch being set on
2022-01-17 15:58:27 +01:00
return;
} else if (mainSwitchState == PowerSwitchIF::SWITCH_OFF) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_ON);
heater.mainSwitchCountdown.setTimeout(mainLineSwitcher->getSwitchDelayMs());
heater.waitMainSwitchOn = true;
2022-01-17 15:58:27 +01:00
} else {
2022-05-12 20:44:36 +02:00
sif::debug << "HeaterHandler::handleSwitchHandling: Failed to get state of"
2022-01-17 15:58:27 +01:00
<< " main line switch" << std::endl;
if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, mainSwitchState);
}
2022-05-12 20:44:36 +02:00
heater.cmdActive = false;
2022-01-17 15:58:27 +01:00
}
}
2022-05-02 22:55:39 +02:00
void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) {
2022-08-24 17:27:47 +02:00
ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx);
// Check whether switch is already off
if (checkSwitchState(heaterIdx)) {
gpioId_t gpioId = heater.gpioId;
2022-01-17 15:58:27 +01:00
result = gpioInterface->pullLow(gpioId);
2022-08-24 17:27:47 +02:00
if (result != returnvalue::OK) {
2022-01-17 15:58:27 +01:00
sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId
<< " low" << std::endl;
triggerEvent(GPIO_PULL_LOW_FAILED, result);
} else {
auto result = heaterMutex->lockMutex();
heater.switchState = OFF;
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
heaterMutex->unlockMutex();
}
triggerEvent(HEATER_WENT_OFF, heaterIdx, 0);
// When all switches are off, also main line switch will be turned off
2022-01-17 15:58:27 +01:00
if (allSwitchesOff()) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
}
}
2022-01-17 15:58:27 +01:00
} else {
sif::info << "HeaterHandler::handleSwitchOffCommand: Switch already off" << std::endl;
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
2022-01-17 15:58:27 +01:00
}
if (heater.replyQueue != NO_COMMANDER) {
// Report back switch command reply if necessary
2022-08-24 17:27:47 +02:00
if (result == returnvalue::OK) {
actionHelper.finish(true, heater.replyQueue, heater.action, result);
2022-01-17 15:58:27 +01:00
} else {
actionHelper.finish(false, heater.replyQueue, heater.action, result);
}
2022-01-17 15:58:27 +01:00
}
2022-05-12 20:44:36 +02:00
heater.cmdActive = false;
}
HeaterHandler::SwitchState HeaterHandler::checkSwitchState(heater::Switchers switchNr) const {
MutexGuard mg(heaterMutex);
return heaterVec.at(switchNr).switchState;
}
2021-01-28 14:55:21 +01:00
2022-11-28 08:56:36 +01:00
ReturnValue_t HeaterHandler::switchHeater(heater::Switchers heater, SwitchState switchState) {
if (switchState == SwitchState::ON) {
return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON);
} else if (switchState == SwitchState::OFF) {
return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_OFF);
}
return returnvalue::FAILED;
2022-11-16 16:36:59 +01:00
}
2021-02-11 08:19:48 +01:00
bool HeaterHandler::allSwitchesOff() {
2022-01-17 15:58:27 +01:00
bool allSwitchesOrd = false;
MutexGuard mg(heaterMutex);
2022-01-17 15:58:27 +01:00
/* Or all switches. As soon one switch is on, allSwitchesOrd will be true */
2022-05-02 22:55:39 +02:00
for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) {
allSwitchesOrd = allSwitchesOrd || heaterVec.at(switchNr).switchState;
2022-01-17 15:58:27 +01:00
}
return !allSwitchesOrd;
2021-02-11 08:19:48 +01:00
}
2022-01-17 15:58:27 +01:00
MessageQueueId_t HeaterHandler::getCommandQueue() const { return commandQueue->getId(); }
2022-08-24 17:27:47 +02:00
ReturnValue_t HeaterHandler::sendFuseOnCommand(uint8_t fuseNr) { return returnvalue::OK; }
ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const {
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_OFF) {
return PowerSwitchIF::SWITCH_OFF;
}
if (switchNr > 7) {
2022-08-24 17:27:47 +02:00
return returnvalue::FAILED;
}
if (checkSwitchState(static_cast<heater::Switchers>(switchNr)) == SwitchState::ON) {
return PowerSwitchIF::SWITCH_ON;
}
return PowerSwitchIF::SWITCH_OFF;
}
2022-01-17 15:58:27 +01:00
ReturnValue_t HeaterHandler::getFuseState(uint8_t fuseNr) const { return 0; }
2022-05-02 17:37:00 +02:00
uint32_t HeaterHandler::getSwitchDelayMs(void) const { return 2000; }
2022-12-13 10:19:28 +01:00
HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switchers heater) {
auto* healthDev = heaterVec.at(heater).healthDevice;
if (healthDev != nullptr) {
MutexGuard mg(heaterMutex);
return healthDev->getHealth();
}
return HasHealthIF::HealthState::FAULTY;
}