various improvements for heater handler #717

Merged
muellerr merged 4 commits from only-one-heater-cmd-at-a-time into main 2023-06-28 10:01:23 +02:00
7 changed files with 89 additions and 49 deletions

View File

@ -26,6 +26,12 @@ will consitute of a breaking change warranting a new major release:
- Internal error reporter set is now enabled by default and generated every 120 seconds. - Internal error reporter set is now enabled by default and generated every 120 seconds.
- Persistent TM store dumps are now performed in chronological order. - Persistent TM store dumps are now performed in chronological order.
- Various robustness improvements for the heater handler. The heater handler will now only
process the command queue if it is not busy with switch commanding which reduces the amount
of possible bugs.
- The heater handler is only able to process messages stricly sequentially now but is scheduled
twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still
be handled relatively quickly.
# [v5.0.0] 2023-06-26 # [v5.0.0] 2023-06-26

View File

@ -324,6 +324,10 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
} }
result = tcsSystemTask->addComponent(objects::HEATER_HANDLER);
if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
}
#if OBSW_ADD_SYRLINKS == 1 #if OBSW_ADD_SYRLINKS == 1
PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask( PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask(

View File

@ -33,8 +33,8 @@ void ObjectFactory::produce(void* args) {
PersistentTmStores stores; PersistentTmStores stores;
ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel,
*SdCardManager::instance(), &ipcStore, &tmStore, stores, *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200,
200, true); true);
LinuxLibgpioIF* gpioComIF = nullptr; LinuxLibgpioIF* gpioComIF = nullptr;
SerialComIF* uartComIF = nullptr; SerialComIF* uartComIF = nullptr;

View File

@ -96,6 +96,25 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) {
} }
break; break;
} }
case (TestCase::COLD_PLOC_CONSECUTIVE): {
if (cycles == 15) {
sif::debug << "Setting cold PLOC temperature" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true);
}
if (cycles == 30) {
sif::debug << "Setting warmer PLOC temperature" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true);
}
if (cycles == 45) {
sif::debug << "Setting cold PLOC temperature again" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true);
}
if (cycles == 60) {
sif::debug << "Setting warmer PLOC temperature again" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true);
}
break;
}
case (TestCase::COLD_CAMERA): { case (TestCase::COLD_CAMERA): {
if (cycles == 15) { if (cycles == 15) {
sif::debug << "Setting cold CAM temperature" << std::endl; sif::debug << "Setting cold CAM temperature" << std::endl;

View File

@ -32,6 +32,7 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject
COLD_STR = 4, COLD_STR = 4,
COLD_STR_CONSECUTIVE = 5, COLD_STR_CONSECUTIVE = 5,
COLD_CAMERA = 6, COLD_CAMERA = 6,
COLD_PLOC_CONSECUTIVE = 7,
}; };
int iteration = 0; int iteration = 0;
uint32_t cycles = 0; uint32_t cycles = 0;

View File

@ -51,9 +51,13 @@ ReturnValue_t HeaterHandler::performOperation(uint8_t operationCode) {
if (mainLineSwitcher->getSwitchState(mainLineSwitch) == SWITCH_OFF) { if (mainLineSwitcher->getSwitchState(mainLineSwitch) == SWITCH_OFF) {
waitForSwitchOff = false; waitForSwitchOff = false;
mode = MODE_OFF; mode = MODE_OFF;
busyWithSwitchCommanding = false;
modeHelper.modeChanged(mode, submode); modeHelper.modeChanged(mode, submode);
} }
} }
if (busyWithSwitchCommanding and heaterCmdBusyCd.hasTimedOut()) {
busyWithSwitchCommanding = false;
}
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
sif::warning << "HeaterHandler::performOperation: " sif::warning << "HeaterHandler::performOperation: "
"Out of range error | " "Out of range error | "
@ -101,23 +105,23 @@ ReturnValue_t HeaterHandler::initializeHeaterMap() {
void HeaterHandler::readCommandQueue() { void HeaterHandler::readCommandQueue() {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
CommandMessage command; CommandMessage command;
do { if (not busyWithSwitchCommanding) {
result = commandQueue->receiveMessage(&command); result = commandQueue->receiveMessage(&command);
if (result == MessageQueueIF::EMPTY) { if (result == MessageQueueIF::EMPTY) {
break; return;
} else if (result != returnvalue::OK) { } else if (result != returnvalue::OK) {
sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl; sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl;
break; return;
}
result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
continue;
} }
result = modeHelper.handleModeCommand(&command); result = modeHelper.handleModeCommand(&command);
if (result == returnvalue::OK) { if (result == returnvalue::OK) {
continue; return;
} }
} while (result == returnvalue::OK); result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
return;
}
}
} }
ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
@ -167,6 +171,8 @@ ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t
heater.action = action; heater.action = action;
heater.cmdActive = true; heater.cmdActive = true;
heater.replyQueue = commandedBy; heater.replyQueue = commandedBy;
busyWithSwitchCommanding = true;
heaterCmdBusyCd.resetTimer();
return returnvalue::OK; return returnvalue::OK;
} }
@ -249,6 +255,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout" sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout"
<< std::endl; << std::endl;
heater.cmdActive = false; heater.cmdActive = false;
busyWithSwitchCommanding = false;
heater.waitMainSwitchOn = false; heater.waitMainSwitchOn = false;
if (heater.replyQueue != commandQueue->getId()) { if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT); actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT);
@ -259,27 +266,25 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
// Check state of main line switch // Check state of main line switch
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch); ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_ON) { if (mainSwitchState == PowerSwitchIF::SWITCH_ON) {
if (getSwitchState(heaterIdx) == SwitchState::OFF) { gpioId_t gpioId = heater.gpioId;
gpioId_t gpioId = heater.gpioId; result = gpioInterface->pullHigh(gpioId);
result = gpioInterface->pullHigh(gpioId); if (result != returnvalue::OK) {
if (result != returnvalue::OK) { sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull GPIO with ID " << gpioId
sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull gpio with id " << gpioId << " high" << std::endl;
<< " high" << std::endl; triggerEvent(GPIO_PULL_HIGH_FAILED, result);
triggerEvent(GPIO_PULL_HIGH_FAILED, result); }
} else { if (result == returnvalue::OK) {
triggerEvent(HEATER_WENT_ON, heaterIdx, 0); triggerEvent(HEATER_WENT_ON, heaterIdx, 0);
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, {
MODE_ON, 0); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
{ heater.switchState = ON;
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); }
heater.switchState = ON; EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
} MODE_ON, 0);
} busyWithSwitchCommanding = false;
} else { mode = HasModesIF::MODE_ON;
triggerEvent(SWITCH_ALREADY_ON, heaterIdx); modeHelper.modeChanged(mode, submode);
} }
mode = HasModesIF::MODE_ON;
modeHelper.modeChanged(mode, submode);
// There is no need to send action finish replies if the sender was the // There is no need to send action finish replies if the sender was the
// HeaterHandler itself // HeaterHandler itself
if (heater.replyQueue != commandQueue->getId()) { if (heater.replyQueue != commandQueue->getId()) {
@ -312,30 +317,33 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) { void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx); auto& heater = heaterVec.at(heaterIdx);
// Check whether switch is already off gpioId_t gpioId = heater.gpioId;
if (getSwitchState(heaterIdx)) { result = gpioInterface->pullLow(gpioId);
gpioId_t gpioId = heater.gpioId; if (result != returnvalue::OK) {
result = gpioInterface->pullLow(gpioId); sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId
if (result != returnvalue::OK) { << " low" << std::endl;
sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId triggerEvent(GPIO_PULL_LOW_FAILED, result);
<< " low" << std::endl; }
triggerEvent(GPIO_PULL_LOW_FAILED, result); if (result == returnvalue::OK) {
} else { // Check whether switch is already off
if (getSwitchState(heaterIdx) == SwitchState::ON) {
{ {
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
heater.switchState = OFF; heater.switchState = OFF;
} }
triggerEvent(HEATER_WENT_OFF, heaterIdx, 0); triggerEvent(HEATER_WENT_OFF, heaterIdx, 0);
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, } else {
MODE_OFF, 0); triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
// When all switches are off, also main line switch will be turned off }
if (allSwitchesOff()) { EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); MODE_OFF, 0);
waitForSwitchOff = true; // When all switches are off, also main line switch will be turned off
} if (allSwitchesOff()) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
waitForSwitchOff = true;
} else {
busyWithSwitchCommanding = false;
} }
} else {
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
} }
if (heater.replyQueue != NO_COMMANDER) { if (heater.replyQueue != NO_COMMANDER) {
// Report back switch command reply if necessary // Report back switch command reply if necessary

View File

@ -148,6 +148,7 @@ class HeaterHandler : public ExecutableObjectIF,
/** Size of command queue */ /** Size of command queue */
size_t cmdQueueSize = 20; size_t cmdQueueSize = 20;
bool waitForSwitchOff = true; bool waitForSwitchOff = true;
bool busyWithSwitchCommanding = false;
GpioIF* gpioInterface = nullptr; GpioIF* gpioInterface = nullptr;
@ -163,6 +164,7 @@ class HeaterHandler : public ExecutableObjectIF,
power::Switch_t mainLineSwitch; power::Switch_t mainLineSwitch;
ActionHelper actionHelper; ActionHelper actionHelper;
Countdown heaterCmdBusyCd = Countdown(2000);
StorageManagerIF* ipcStore = nullptr; StorageManagerIF* ipcStore = nullptr;