#include "FreshSupvHandler.h" #include <fsfw/datapool/PoolReadGuard.h> #include <fsfw/filesystem.h> #include <atomic> #include <filesystem> #include "eive/definitions.h" #include "eive/objects.h" #include "fsfw/action.h" #include "fsfw/globalfunctions/arrayprinter.h" #include "fsfw/ipc/CommandMessage.h" #include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/ipc/QueueFactory.h" #include "fsfw/objectmanager/ObjectManagerIF.h" #include "fsfw/returnvalues/returnvalue.h" #include "fsfw/tasks/TaskFactory.h" #include "linux/payload/plocSupvDefs.h" using namespace supv; using namespace returnvalue; std::atomic_bool supv::SUPV_ON = false; FreshSupvHandler::FreshSupvHandler(DhbConfig cfg, CookieIF* comCookie, Gpio uartIsolatorSwitch, PowerSwitchIF& switchIF, power::Switch_t powerSwitch) : FreshDeviceHandlerBase(cfg), comCookie(comCookie), switchIF(switchIF), switchId(powerSwitch), uartIsolatorSwitch(uartIsolatorSwitch), hkSet(this), bootStatusReport(this), latchupStatusReport(this), countersReport(this), adcReport(this) { spParams.buf = commandBuffer.data(); spParams.maxSize = commandBuffer.size(); eventQueue = QueueFactory::instance()->createMessageQueue(10); } void FreshSupvHandler::performDeviceOperation(uint8_t opCode) { if (not transitionActive and mode == MODE_OFF) { // Nothing to do for now. return; } if (opCode == OpCode::DEFAULT_OPERATION) { EventMessage event; for (ReturnValue_t result = eventQueue->receiveMessage(&event); result == returnvalue::OK; result = eventQueue->receiveMessage(&event)) { switch (event.getMessageId()) { case EventMessage::EVENT_MESSAGE: handleEvent(&event); break; default: sif::debug << "PlocSupervisorHandler::performOperationHook: Did not subscribe to this event" << " message" << std::endl; break; } } if (transitionActive) { if (targetMode == MODE_ON or targetMode == MODE_NORMAL) { handleTransitionToOn(); } else if (targetMode == MODE_OFF) { handleTransitionToOff(); } else { // This should never happen. sif::error << "FreshSupvHandler: Invalid transition mode: " << targetMode << std::endl; targetMode = MODE_OFF; targetSubmode = 0; handleTransitionToOff(); } } else { // I think the SUPV is not able to process multiple commands consecutively, so only send // normal command if no other command is pending. We handle the action queue first, which // should ensure that these commands take precendence. if (mode == MODE_NORMAL and not isCommandPending()) { auto cmdIter = activeActionCmds.find( buildActiveCmdKey(Apid::HK, static_cast<uint8_t>(tc::HkId::GET_REPORT))); if (cmdIter == activeActionCmds.end() or not cmdIter->second.isPending) { spParams.buf = commandBuffer.data(); commandedByCached = MessageQueueIF::NO_QUEUE; sendEmptyCmd(supv::GET_HK_REPORT, Apid::HK, static_cast<uint8_t>(tc::HkId::GET_REPORT), true); } } } } else if (opCode == OpCode::PARSE_TM) { for (auto& activeCmd : activeActionCmds) { if (activeCmd.second.isPending and activeCmd.second.cmdCountdown.hasTimedOut()) { if (activeCmd.second.commandedBy != MessageQueueIF::NO_QUEUE) { actionHelper.finish(false, activeCmd.second.commandedBy, activeCmd.first, DeviceHandlerIF::TIMEOUT); } activeCmd.second.isPending = false; } } parseTmPackets(); } } ReturnValue_t FreshSupvHandler::performDeviceOperationPreQueueHandling(uint8_t opCode) { if (opCode != OpCode::DEFAULT_OPERATION) { return returnvalue::OK; } // We parse for TM packets shortly before handling the queue, this might complete some packets, // which then allows the handling of new action commands. return parseTmPackets(); } ReturnValue_t FreshSupvHandler::handleCommandMessage(CommandMessage* message) { // No custom messages. return returnvalue::FAILED; } LocalPoolDataSetBase* FreshSupvHandler::getDataSetHandle(sid_t sid) { if (sid == hkSet.getSid()) { return &hkSet; } else if (sid == bootStatusReport.getSid()) { return &bootStatusReport; } else if (sid == latchupStatusReport.getSid()) { return &latchupStatusReport; } else if (sid == countersReport.getSid()) { return &countersReport; } else if (sid == adcReport.getSid()) { return &adcReport; } return nullptr; } ReturnValue_t FreshSupvHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, LocalDataPoolManager& poolManager) { // TODO: Copy code from god handler here. localDataPoolMap.emplace(supv::NUM_TMS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TEMP_PS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TEMP_PL, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::HK_SOC_STATE, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::NVM0_1_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::NVM3_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::MISSION_IO_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::FMC_STATE, &fmcStateEntry); localDataPoolMap.emplace(supv::NUM_TCS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TEMP_SUP, &tempSupEntry); localDataPoolMap.emplace(supv::UPTIME, new PoolEntry<uint64_t>({0})); localDataPoolMap.emplace(supv::CPULOAD, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::AVAILABLEHEAP, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::BR_SOC_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::POWER_CYCLES, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::BOOT_AFTER_MS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::BOOT_TIMEOUT_POOL_VAR_MS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::ACTIVE_NVM, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::BP0_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::BP1_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::BP2_STATE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::BOOT_STATE, &bootStateEntry); localDataPoolMap.emplace(supv::BOOT_CYCLES, &bootCyclesEntry); localDataPoolMap.emplace(supv::LATCHUP_ID, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::CNT0, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT1, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT2, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT3, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT4, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT5, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::CNT6, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MSEC, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_SEC, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MIN, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_HOUR, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_DAY, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_MON, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_TIME_YEAR, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::LATCHUP_RPT_IS_SET, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(supv::SIGNATURE, new PoolEntry<uint32_t>()); localDataPoolMap.emplace(supv::LATCHUP_HAPPENED_CNTS, &latchupCounters); localDataPoolMap.emplace(supv::ADC_DEVIATION_TRIGGERS_CNT, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TC_RECEIVED_CNT, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TM_AVAILABLE_CNT, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::SUPERVISOR_BOOTS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MPSOC_BOOTS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MPSOC_BOOT_FAILED_ATTEMPTS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MPSOC_POWER_UP, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MPSOC_UPDATES, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MPSOC_HEARTBEAT_RESETS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::CPU_WDT_RESETS, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::PS_HEARTBEATS_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::PL_HEARTBEATS_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::EB_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::BM_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::LM_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::AM_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::TCTMM_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::MM_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::HK_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::DL_TASK_LOST, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(supv::RWS_TASKS_LOST, new PoolEntry<uint32_t>(3)); localDataPoolMap.emplace(supv::ADC_RAW, &adcRawEntry); localDataPoolMap.emplace(supv::ADC_ENG, &adcEngEntry); poolManager.subscribeForRegularPeriodicPacket( subdp::RegularHkPeriodicParams(hkSet.getSid(), false, 10.0)); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { using namespace supv; // The SUPV does not have any action commands where there is no communication with the device // involved. if (mode != MODE_ON and mode != MODE_NORMAL) { return HasModesIF::INVALID_MODE; } ReturnValue_t result; if (uartManager->longerRequestActive()) { return result::SUPV_HELPER_EXECUTING; } switch (actionId) { case PERFORM_UPDATE: { if (size > config::MAX_PATH_SIZE + config::MAX_FILENAME_SIZE) { return result::FILENAME_TOO_LONG; } UpdateParams params; result = extractUpdateCommand(data, size, params); if (result != returnvalue::OK) { return result; } result = uartManager->performUpdate(params); if (result != returnvalue::OK) { return result; } return EXECUTION_FINISHED; } case CONTINUE_UPDATE: { uartManager->initiateUpdateContinuation(); return EXECUTION_FINISHED; } case MEMORY_CHECK_WITH_FILE: { UpdateParams params; result = extractBaseParams(&data, size, params); if (result != returnvalue::OK) { return result; } if (not std::filesystem::exists(params.file)) { return HasFileSystemIF::FILE_DOES_NOT_EXIST; } uartManager->performMemCheck(params.file, params.memId, params.startAddr); return EXECUTION_FINISHED; } default: break; } // The PLOC SUPV is not able to process multiple commands consecutively. if (isCommandPending()) { return HasActionsIF::IS_BUSY; } if (isCommandAlreadyActive(actionId)) { return HasActionsIF::IS_BUSY; } spParams.buf = commandBuffer.data(); this->commandedByCached = commandedBy; switch (actionId) { case GET_HK_REPORT: { sendEmptyCmd(supv::GET_HK_REPORT, Apid::HK, static_cast<uint8_t>(tc::HkId::GET_REPORT), true); result = returnvalue::OK; break; } case START_MPSOC: { sif::info << "PLOC SUPV: Starting MPSoC" << std::endl; sendEmptyCmd(supv::START_MPSOC, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::START_MPSOC), false); result = returnvalue::OK; break; } case SHUTDOWN_MPSOC: { sif::info << "PLOC SUPV: Shutting down MPSoC" << std::endl; sendEmptyCmd(supv::SHUTDOWN_MPSOC, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::SHUTDOWN_MPSOC), false); result = returnvalue::OK; break; } case SEL_MPSOC_BOOT_IMAGE: { prepareSelBootImageCmd(data); result = returnvalue::OK; break; } case RESET_MPSOC: { sif::info << "PLOC SUPV: Resetting MPSoC" << std::endl; sendEmptyCmd(supv::RESET_MPSOC, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::RESET_MPSOC), false); result = returnvalue::OK; break; } case SET_TIME_REF: { result = prepareSetTimeRefCmd(); break; } case SET_BOOT_TIMEOUT: { prepareSetBootTimeoutCmd(data, size); result = returnvalue::OK; break; } case SET_MAX_RESTART_TRIES: { prepareRestartTriesCmd(data, size); result = returnvalue::OK; break; } case DISABLE_PERIOIC_HK_TRANSMISSION: { prepareDisableHk(); result = returnvalue::OK; break; } case GET_BOOT_STATUS_REPORT: { sendEmptyCmd(supv::GET_BOOT_STATUS_REPORT, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::GET_BOOT_STATUS_REPORT), true); result = returnvalue::OK; break; } case ENABLE_LATCHUP_ALERT: { result = prepareLatchupConfigCmd(data, actionId, size); break; } case DISABLE_LATCHUP_ALERT: { result = prepareLatchupConfigCmd(data, actionId, size); break; } case SET_ALERT_LIMIT: { result = prepareSetAlertLimitCmd(data, size); break; } case GET_LATCHUP_STATUS_REPORT: { sendEmptyCmd(supv::GET_LATCHUP_STATUS_REPORT, Apid::LATCHUP_MON, static_cast<uint8_t>(tc::LatchupMonId::GET_STATUS_REPORT), true); result = returnvalue::OK; break; } case SET_GPIO: { result = prepareSetGpioCmd(data, size); break; } case FACTORY_RESET: { result = prepareFactoryResetCmd(data, size); break; } case READ_GPIO: { result = prepareReadGpioCmd(data, size); break; } case SET_SHUTDOWN_TIMEOUT: { prepareSetShutdownTimeoutCmd(data, size); result = returnvalue::OK; break; } case FACTORY_FLASH: { sendEmptyCmd(supv::FACTORY_FLASH, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::FACTORY_FLASH), false); result = returnvalue::OK; break; } case RESET_PL: { sendEmptyCmd(supv::RESET_PL, Apid::BOOT_MAN, static_cast<uint8_t>(tc::BootManId::RESET_PL), false); result = returnvalue::OK; break; } case SET_ADC_ENABLED_CHANNELS: { prepareSetAdcEnabledChannelsCmd(data); result = returnvalue::OK; break; } case SET_ADC_WINDOW_AND_STRIDE: { prepareSetAdcWindowAndStrideCmd(data); result = returnvalue::OK; break; } case SET_ADC_THRESHOLD: { prepareSetAdcThresholdCmd(data); result = returnvalue::OK; break; } case WIPE_MRAM: { result = prepareWipeMramCmd(data, size); break; } case REQUEST_ADC_REPORT: { sendEmptyCmd(supv::REQUEST_ADC_REPORT, Apid::ADC_MON, static_cast<uint8_t>(tc::AdcMonId::REQUEST_ADC_SAMPLE), true); result = returnvalue::OK; break; } case REQUEST_LOGGING_COUNTERS: { sendEmptyCmd(supv::REQUEST_LOGGING_COUNTERS, Apid::DATA_LOGGER, static_cast<uint8_t>(tc::DataLoggerServiceId::REQUEST_COUNTERS), true); result = returnvalue::OK; break; } default: sif::debug << "PlocSupervisorHandler::buildCommandFromCommand: Command not implemented" << std::endl; result = DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; break; } return result; } ReturnValue_t FreshSupvHandler::prepareSetTimeRefCmd() { Clock::TimeOfDay_t time; ReturnValue_t result = Clock::getDateAndTime(&time); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::prepareSetTimeRefCmd: Failed to get current time" << std::endl; return result::GET_TIME_FAILURE; } supv::SetTimeRef packet(spParams); result = packet.buildPacket(&time); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_TIME_REF, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::checkModeCommand(Mode_t commandedMode, Submode_t commandedSubmode, uint32_t* msToReachTheMode) { if (commandedMode != MODE_OFF) { PoolReadGuard pg(&enablePl); if (pg.getReadResult() == returnvalue::OK) { if (enablePl.plUseAllowed.isValid() and not enablePl.plUseAllowed.value) { return NON_OP_STATE_OF_CHARGE; } } } if (commandedMode != HasModesIF::MODE_OFF and commandedMode != HasModesIF::MODE_ON and commandedMode != MODE_NORMAL) { return returnvalue::FAILED; } if (commandedMode != mode and msToReachTheMode != nullptr) { if (commandedMode == MODE_OFF) { *msToReachTheMode = supv::MAX_TRANSITION_TIME_TO_OFF_MS; } else { *msToReachTheMode = supv::MAX_TRANSITION_TIME_TO_ON_MS; } } return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSelBootImageCmd(const uint8_t* commandData) { supv::MPSoCBootSelect packet(spParams); ReturnValue_t result = packet.buildPacket(commandData[0], commandData[1], commandData[2], commandData[3]); if (result != returnvalue::OK) { return result; } sendCommand(supv::SEL_MPSOC_BOOT_IMAGE, packet, false); return returnvalue::OK; } void FreshSupvHandler::startTransition(Mode_t newMode, Submode_t newSubmode) { if (newMode == mode or (mode == MODE_ON and newMode == MODE_NORMAL) or (newMode == MODE_ON and mode == MODE_NORMAL)) { // Can finish immediately. setMode(newMode, newSubmode); return; } // Transition for both OFF to ON/NORMAL and ON/NORMAL to OFF require small state machine. transitionActive = true; targetMode = newMode; targetSubmode = newSubmode; if (targetMode == MODE_ON or targetMode == MODE_NORMAL) { startupState = StartupState::IDLE; } else if (targetMode == MODE_OFF) { shutdownState = ShutdownState::IDLE; } } void FreshSupvHandler::handleTransitionToOn() { if (startupState == StartupState::IDLE) { startupState = StartupState::POWER_SWITCHING; switchIF.sendSwitchCommand(switchId, PowerSwitchIF::SWITCH_ON); switchTimeout.resetTimer(); } else if (modeHelper.isTimedOut()) { targetMode = MODE_OFF; shutdownState = ShutdownState::IDLE; handleTransitionToOff(); return; } if (startupState == StartupState::POWER_SWITCHING) { if (switchIF.getSwitchState(switchId) == PowerSwitchIF::SWITCH_ON) { startupState = StartupState::BOOTING; bootTimeout.resetTimer(); } else if (switchTimeout.hasTimedOut()) { targetMode = MODE_OFF; shutdownState = ShutdownState::IDLE; handleTransitionToOff(); return; } } if (startupState == StartupState::BOOTING and bootTimeout.hasTimedOut()) { uartIsolatorSwitch.pullHigh(); uartManager->start(); if (SET_TIME_DURING_BOOT) { startupState = StartupState::SET_TIME; } else { startupState = StartupState::ON; } } if (startupState == StartupState::SET_TIME) { spParams.buf = commandBuffer.data(); ReturnValue_t result = prepareSetTimeRefCmd(); if (result != returnvalue::OK) { sif::error << "FreshSupvHandler: Setting time command prepration failed" << std::endl; startupState = StartupState::ON; } else { startupState = StartupState::WAIT_FOR_TIME_REPLY; } } if (startupState == StartupState::TIME_WAS_SET) { startupState = StartupState::ON; } if (startupState == StartupState::ON) { hkSet.setReportingEnabled(true); supv::SUPV_ON = true; transitionActive = false; setMode(targetMode); } } void FreshSupvHandler::handleTransitionToOff() { if (shutdownState == ShutdownState::IDLE) { hkSet.setReportingEnabled(false); hkSet.setValidity(false, true); uartManager->stop(); activeActionCmds.clear(); uartIsolatorSwitch.pullLow(); switchIF.sendSwitchCommand(switchId, PowerSwitchIF::SWITCH_OFF); shutdownState = ShutdownState::POWER_SWITCHING; } if (shutdownState == ShutdownState::POWER_SWITCHING) { if (switchIF.getSwitchState(switchId) == PowerSwitchIF::SWITCH_OFF or modeHelper.isTimedOut()) { supv::SUPV_ON = false; transitionActive = false; setMode(MODE_OFF); } } } ReturnValue_t FreshSupvHandler::sendCommand(DeviceCommandId_t commandId, TcBase& tc, bool replyExpected, uint32_t cmdCountdownMs) { if (supv::DEBUG_PLOC_SUPV) { if (not(supv::REDUCE_NORMAL_MODE_PRINTOUT and mode == MODE_NORMAL and commandId == supv::GET_HK_REPORT)) { sif::debug << "PLOC SUPV: SEND PACKET Size " << tc.getFullPacketLen() << " Module APID " << (int)tc.getModuleApid() << " Service ID " << (int)tc.getServiceId() << std::endl; } } ActiveCmdInfo info(commandId, cmdCountdownMs); auto activeCmdIter = activeActionCmds.find(buildActiveCmdKey(tc.getModuleApid(), tc.getServiceId())); if (activeCmdIter == activeActionCmds.end()) { info.isPending = true; info.replyPacketExpected = replyExpected; info.commandedBy = commandedByCached; info.cmdCountdown.resetTimer(); activeActionCmds.emplace(buildActiveCmdKey(tc.getModuleApid(), tc.getServiceId()), info); } else { if (activeCmdIter->second.isPending) { return HasActionsIF::IS_BUSY; } activeCmdIter->second.isPending = true; activeCmdIter->second.commandId = commandId; activeCmdIter->second.commandedBy = commandedByCached; activeCmdIter->second.ackRecv = false; activeCmdIter->second.ackExeRecv = false; activeCmdIter->second.replyPacketExpected = replyExpected; activeCmdIter->second.replyPacketReceived = false; activeCmdIter->second.cmdCountdown.setTimeout(cmdCountdownMs); activeCmdIter->second.cmdCountdown.resetTimer(); } ReturnValue_t result = uartManager->sendMessage(comCookie, tc.getFullPacket(), tc.getFullPacketLen()); return result; } ReturnValue_t FreshSupvHandler::initialize() { uartManager = ObjectManager::instance()->get<PlocSupvUartManager>(objects::PLOC_SUPERVISOR_HELPER); if (uartManager == nullptr) { return returnvalue::FAILED; } uartManager->initializeInterface(comCookie); ReturnValue_t result = eventSubscription(); if (result != returnvalue::OK) { return result; } return FreshDeviceHandlerBase::initialize(); } ReturnValue_t FreshSupvHandler::sendEmptyCmd(DeviceCommandId_t commandId, uint16_t apid, uint8_t serviceId, bool replyExpected) { supv::NoPayloadPacket packet(spParams, apid, serviceId); ReturnValue_t result = packet.buildPacket(); if (result != returnvalue::OK) { return result; } sendCommand(commandId, packet, replyExpected); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetBootTimeoutCmd(const uint8_t* commandData, size_t cmdDataLen) { if (cmdDataLen < 4) { return HasActionsIF::INVALID_PARAMETERS; } supv::SetBootTimeout packet(spParams); uint32_t timeout = *(commandData) << 24 | *(commandData + 1) << 16 | *(commandData + 2) << 8 | *(commandData + 3); ReturnValue_t result = packet.buildPacket(timeout); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_BOOT_TIMEOUT, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareRestartTriesCmd(const uint8_t* commandData, size_t cmdDataLen) { if (cmdDataLen < 1) { return HasActionsIF::INVALID_PARAMETERS; } uint8_t restartTries = *(commandData); supv::SetRestartTries packet(spParams); ReturnValue_t result = packet.buildPacket(restartTries); if (result != returnvalue::OK) { return result; } sendCommand(supv::ENABLE_AUTO_TM, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareDisableHk() { supv::DisablePeriodicHkTransmission packet(spParams); ReturnValue_t result = packet.buildPacket(); if (result != returnvalue::OK) { return result; } sendCommand(supv::DISABLE_AUTO_TM, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareLatchupConfigCmd(const uint8_t* commandData, DeviceCommandId_t deviceCommand, size_t cmdDataLen) { ReturnValue_t result = returnvalue::OK; if (cmdDataLen < 1) { return HasActionsIF::INVALID_PARAMETERS; } uint8_t latchupId = *commandData; if (latchupId > 6) { return result::INVALID_LATCHUP_ID; } switch (deviceCommand) { case (supv::ENABLE_LATCHUP_ALERT): { supv::LatchupAlert packet(spParams); result = packet.buildPacket(true, latchupId); if (result != returnvalue::OK) { return result; } sendCommand(deviceCommand, packet, false); break; } case (supv::DISABLE_LATCHUP_ALERT): { supv::LatchupAlert packet(spParams); result = packet.buildPacket(false, latchupId); if (result != returnvalue::OK) { return result; } sendCommand(deviceCommand, packet, false); break; } default: { sif::debug << "PlocSupervisorHandler::prepareLatchupConfigCmd: Invalid command id" << std::endl; result = returnvalue::FAILED; break; } } return result; } ReturnValue_t FreshSupvHandler::prepareSetAlertLimitCmd(const uint8_t* commandData, size_t cmdDataLen) { if (cmdDataLen < 5) { return HasActionsIF::INVALID_PARAMETERS; } uint8_t offset = 0; uint8_t latchupId = *commandData; offset += 1; uint32_t dutycycle = *(commandData + offset) << 24 | *(commandData + offset + 1) << 16 | *(commandData + offset + 2) << 8 | *(commandData + offset + 3); if (latchupId > 6) { return result::INVALID_LATCHUP_ID; } supv::SetAlertlimit packet(spParams); ReturnValue_t result = packet.buildPacket(latchupId, dutycycle); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_ALERT_LIMIT, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetShutdownTimeoutCmd(const uint8_t* commandData, size_t cmdDataLen) { if (cmdDataLen < 4) { return HasActionsIF::INVALID_PARAMETERS; } uint32_t timeout = 0; ReturnValue_t result = returnvalue::OK; size_t size = sizeof(timeout); result = SerializeAdapter::deSerialize(&timeout, &commandData, &size, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::prepareSetShutdownTimeoutCmd: Failed to deserialize timeout" << std::endl; return result; } supv::SetShutdownTimeout packet(spParams); result = packet.buildPacket(timeout); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_SHUTDOWN_TIMEOUT, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetGpioCmd(const uint8_t* commandData, size_t commandDataLen) { if (commandDataLen < 3) { return HasActionsIF::INVALID_PARAMETERS; } uint8_t port = *commandData; uint8_t pin = *(commandData + 1); uint8_t val = *(commandData + 2); supv::SetGpio packet(spParams); ReturnValue_t result = packet.buildPacket(port, pin, val); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_GPIO, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareReadGpioCmd(const uint8_t* commandData, size_t commandDataLen) { if (commandDataLen < 2) { return HasActionsIF::INVALID_PARAMETERS; } uint8_t port = *commandData; uint8_t pin = *(commandData + 1); supv::ReadGpio packet(spParams); ReturnValue_t result = packet.buildPacket(port, pin); if (result != returnvalue::OK) { return result; } sendCommand(supv::READ_GPIO, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareFactoryResetCmd(const uint8_t* commandData, size_t len) { FactoryReset resetCmd(spParams); if (len < 1) { return HasActionsIF::INVALID_PARAMETERS; } ReturnValue_t result = resetCmd.buildPacket(commandData[0]); if (result != returnvalue::OK) { return result; } sendCommand(supv::FACTORY_RESET, resetCmd, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetAdcEnabledChannelsCmd(const uint8_t* commandData) { uint16_t ch = *(commandData) << 8 | *(commandData + 1); supv::SetAdcEnabledChannels packet(spParams); ReturnValue_t result = packet.buildPacket(ch); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_ADC_ENABLED_CHANNELS, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetAdcWindowAndStrideCmd(const uint8_t* commandData) { uint8_t offset = 0; uint16_t windowSize = *(commandData + offset) << 8 | *(commandData + offset + 1); offset += 2; uint16_t stridingStepSize = *(commandData + offset) << 8 | *(commandData + offset + 1); supv::SetAdcWindowAndStride packet(spParams); ReturnValue_t result = packet.buildPacket(windowSize, stridingStepSize); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_ADC_WINDOW_AND_STRIDE, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareSetAdcThresholdCmd(const uint8_t* commandData) { uint32_t threshold = *(commandData) << 24 | *(commandData + 1) << 16 | *(commandData + 2) << 8 | *(commandData + 3); supv::SetAdcThreshold packet(spParams); ReturnValue_t result = packet.buildPacket(threshold); if (result != returnvalue::OK) { return result; } sendCommand(supv::SET_ADC_THRESHOLD, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::prepareWipeMramCmd(const uint8_t* commandData, size_t cmdDataLen) { if (cmdDataLen < 8) { return HasActionsIF::INVALID_PARAMETERS; } uint32_t start = 0; uint32_t stop = 0; size_t size = sizeof(start) + sizeof(stop); SerializeAdapter::deSerialize(&start, &commandData, &size, SerializeIF::Endianness::BIG); SerializeAdapter::deSerialize(&stop, &commandData, &size, SerializeIF::Endianness::BIG); if ((stop - start) <= 0) { return result::INVALID_MRAM_ADDRESSES; } supv::MramCmd packet(spParams); ReturnValue_t result = packet.buildPacket(start, stop, supv::MramCmd::MramAction::WIPE); if (result != returnvalue::OK) { return result; } sendCommand(supv::WIPE_MRAM, packet, false); return returnvalue::OK; } ReturnValue_t FreshSupvHandler::parseTmPackets() { uint8_t* receivedData = nullptr; size_t receivedSize = 0; while (true) { ReturnValue_t result = uartManager->readReceivedMessage(comCookie, &receivedData, &receivedSize); if (result != returnvalue::OK or receivedSize == 0) { break; } tmReader.setReadOnlyData(receivedData, receivedSize); if (tmReader.checkCrc() != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::parseTmPackets: CRC failure for packet with size " << receivedSize << std::endl; arrayprinter::print(receivedData, receivedSize); continue; } if (supv::DEBUG_PLOC_SUPV) { handlePacketPrint(); } uint16_t apid = tmReader.getModuleApid(); switch (apid) { case (Apid::TMTC_MAN): { switch (tmReader.getServiceId()) { case (static_cast<uint8_t>(supv::tm::TmtcId::ACK)): case (static_cast<uint8_t>(supv::tm::TmtcId::NAK)): { handleAckReport(receivedData); continue; } case (static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK)): case (static_cast<uint8_t>(supv::tm::TmtcId::EXEC_NAK)): { handleExecutionReport(receivedData); continue; } } break; } case (Apid::HK): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::HkId::REPORT)) { handleHkReport(receivedData); continue; } else if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::HkId::HARDFAULTS)) { handleBadApidServiceCombination(SUPV_UNINIMPLEMENTED_TM, apid, tmReader.getServiceId()); return INVALID_DATA; } break; } case (Apid::BOOT_MAN): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::BootManId::BOOT_STATUS_REPORT)) { handleBootStatusReport(receivedData); continue; } break; } case (Apid::LATCHUP_MON): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::LatchupMonId::LATCHUP_STATUS_REPORT)) { handleLatchupStatusReport(receivedData); continue; } break; } case (Apid::ADC_MON): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::AdcMonId::ADC_REPORT)) { genericHandleTm("ADC", receivedData, adcReport, supv::Apid::ADC_MON, static_cast<uint8_t>(supv::tc::AdcMonId::REQUEST_ADC_SAMPLE)); continue; } break; } case (Apid::MEM_MAN): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::MemManId::UPDATE_STATUS_REPORT)) { sif::warning << "FreshSupvHandler: Update status report parsing not implemented" << std::endl; // confirmReplyPacketReceived(supv::Apid::MEM_MAN, // supv::tc::MemManId::UPDATE_STATUS_REPORT); continue; } break; } case (Apid::DATA_LOGGER): { if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::DataLoggerId::COUNTERS_REPORT)) { genericHandleTm("COUNTERS", receivedData, countersReport, supv::Apid::DATA_LOGGER, static_cast<uint8_t>(supv::tc::DataLoggerServiceId::REQUEST_COUNTERS)); continue; } } } handleBadApidServiceCombination(SUPV_UNKNOWN_TM, apid, tmReader.getServiceId()); } return returnvalue::OK; } void FreshSupvHandler::handleBadApidServiceCombination(Event event, unsigned int apid, unsigned int serviceId) { const char* printString = ""; if (event == SUPV_UNKNOWN_TM) { printString = "PlocSupervisorHandler: Unknown"; } else if (event == SUPV_UNINIMPLEMENTED_TM) { printString = "PlocSupervisorHandler: Unimplemented"; } triggerEvent(event, apid, tmReader.getServiceId()); sif::warning << printString << " APID service combination 0x" << std::setw(2) << std::setfill('0') << std::hex << apid << ", 0x" << std::setw(2) << serviceId << std::endl; } void FreshSupvHandler::handlePacketPrint() { if (tmReader.getModuleApid() == Apid::TMTC_MAN) { if ((tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::ACK)) or (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::NAK))) { AcknowledgmentReport ack(tmReader); ReturnValue_t result = ack.parse(false); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler: Parsing ACK failed. "; if (result == result::INVALID_SERVICE_ID) { sif::warning << "Invalid service ID" << std::endl; } else if (result == result::CRC_FAILURE) { sif::warning << "CRC check failed" << std::endl; } else { sif::warning << "Returncode 0x" << std::hex << std::setw(4) << result << std::dec << std::endl; } } if (mode == MODE_NORMAL and supv::REDUCE_NORMAL_MODE_PRINTOUT and ack.getRefModuleApid() == (uint8_t)supv::Apid::HK and ack.getRefServiceId() == (uint8_t)supv::tc::HkId::GET_REPORT) { return; } const char* printStr = "???"; if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::ACK)) { printStr = "ACK"; } else if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::NAK)) { printStr = "NAK"; } sif::debug << "PlocSupervisorHandler: RECV " << printStr << " for APID Module ID " << (int)ack.getRefModuleApid() << " Service ID " << (int)ack.getRefServiceId() << " Seq Count " << ack.getRefSequenceCount() << std::endl; return; } else if ((tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK)) or (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_NAK))) { ExecutionReport exe(tmReader); ReturnValue_t result = exe.parse(false); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler: Parsing EXE failed" << std::endl; } const char* printStr = "???"; if (mode == MODE_NORMAL and supv::REDUCE_NORMAL_MODE_PRINTOUT and exe.getRefModuleApid() == (uint8_t)supv::Apid::HK and exe.getRefServiceId() == (uint8_t)supv::tc::HkId::GET_REPORT) { return; } if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK)) { printStr = "ACK EXE"; } else if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_NAK)) { printStr = "NAK EXE"; } sif::debug << "PlocSupervisorHandler: RECV " << printStr << " for APID Module ID " << (int)exe.getRefModuleApid() << " Service ID " << (int)exe.getRefServiceId() << " Seq Count " << exe.getRefSequenceCount() << std::endl; return; } } if (mode == MODE_NORMAL and supv::REDUCE_NORMAL_MODE_PRINTOUT and tmReader.getModuleApid() == supv::Apid::HK and tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::HkId::REPORT)) { return; } sif::debug << "PlocSupervisorHandler: RECV PACKET Size " << tmReader.getFullPacketLen() << " Module APID " << (int)tmReader.getModuleApid() << " Service ID " << (int)tmReader.getServiceId() << std::endl; } bool FreshSupvHandler::isCommandAlreadyActive(ActionId_t actionId) const { // Not the most efficient implementation but who cares. It's not like this map is going // to be huge in the nominal case.. for (const auto& info : activeActionCmds) { if (info.second.commandId == actionId and info.second.isPending) { return true; } } return false; } ReturnValue_t FreshSupvHandler::extractUpdateCommand(const uint8_t* commandData, size_t size, supv::UpdateParams& params) { size_t remSize = size; if (size > (config::MAX_FILENAME_SIZE + config::MAX_PATH_SIZE + sizeof(params.memId)) + sizeof(params.startAddr) + sizeof(params.bytesWritten) + sizeof(params.seqCount) + sizeof(uint8_t)) { sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Data size too big" << std::endl; return result::INVALID_LENGTH; } ReturnValue_t result = returnvalue::OK; result = extractBaseParams(&commandData, size, params); result = SerializeAdapter::deSerialize(¶ms.bytesWritten, &commandData, &remSize, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize bytes " "already written" << std::endl; return result; } result = SerializeAdapter::deSerialize(¶ms.seqCount, &commandData, &remSize, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize start sequence count" << std::endl; return result; } uint8_t delMemRaw = 0; result = SerializeAdapter::deSerialize(&delMemRaw, &commandData, &remSize, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Failed to deserialize whether to delete " "memory" << std::endl; return result; } params.deleteMemory = delMemRaw; return returnvalue::OK; } ReturnValue_t FreshSupvHandler::extractBaseParams(const uint8_t** commandData, size_t& remSize, supv::UpdateParams& params) { bool nullTermFound = false; for (size_t idx = 0; idx < remSize; idx++) { if ((*commandData)[idx] == '\0') { nullTermFound = true; break; } } if (not nullTermFound) { return returnvalue::FAILED; } params.file = std::string(reinterpret_cast<const char*>(*commandData)); if (params.file.size() > (config::MAX_FILENAME_SIZE + config::MAX_PATH_SIZE)) { sif::warning << "PlocSupervisorHandler::extractUpdateCommand: Filename too long" << std::endl; return result::FILENAME_TOO_LONG; } *commandData += params.file.size() + SIZE_NULL_TERMINATOR; remSize -= (params.file.size() + SIZE_NULL_TERMINATOR); params.memId = **commandData; *commandData += 1; remSize -= 1; ReturnValue_t result = SerializeAdapter::deSerialize(¶ms.startAddr, commandData, &remSize, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler::extractBaseParams: Failed to deserialize start address" << std::endl; return result; } return result; } void FreshSupvHandler::handleEvent(EventMessage* eventMessage) { ReturnValue_t result = returnvalue::OK; object_id_t objectId = eventMessage->getReporter(); Event event = eventMessage->getEvent(); switch (objectId) { case objects::PLOC_SUPERVISOR_HELPER: { // After execution of update procedure, PLOC is in a state where it draws approx. 700 mA of // current. To leave this state the shutdown MPSoC command must be sent here. if (event == PlocSupvUartManager::SUPV_UPDATE_FAILED || event == PlocSupvUartManager::SUPV_UPDATE_SUCCESSFUL || event == PlocSupvUartManager::SUPV_CONTINUE_UPDATE_FAILED || event == PlocSupvUartManager::SUPV_CONTINUE_UPDATE_SUCCESSFUL || event == PlocSupvUartManager::SUPV_MEM_CHECK_FAIL || event == PlocSupvUartManager::SUPV_MEM_CHECK_OK) { if (not isCommandAlreadyActive(supv::SHUTDOWN_MPSOC)) { CommandMessage actionMsg; ActionMessage::setCommand(&actionMsg, supv::SHUTDOWN_MPSOC, store_address_t::invalid()); result = messageQueue->sendMessageFrom(getCommandQueue(), &actionMsg, MessageQueueIF::NO_QUEUE); if (result != returnvalue::OK) { triggerEvent(supv::SUPV_MPSOC_SHUTDOWN_BUILD_FAILED); sif::warning << "PlocSupervisorHandler::handleEvent: Failed to build MPSoC shutdown " "command" << std::endl; return; } } } break; } default: sif::debug << "PlocMPSoCHandler::handleEvent: Did not subscribe to this event" << std::endl; break; } } ReturnValue_t FreshSupvHandler::eventSubscription() { ReturnValue_t result = returnvalue::OK; EventManagerIF* manager = ObjectManager::instance()->get<EventManagerIF>(objects::EVENT_MANAGER); if (manager == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "PlocSupervisorHandler::eventSubscritpion: Invalid event manager" << std::endl; #endif return ObjectManagerIF::CHILD_INIT_FAILED; ; } result = manager->registerListener(eventQueue->getId()); if (result != returnvalue::OK) { return result; } result = manager->subscribeToEventRange( eventQueue->getId(), event::getEventId(PlocSupvUartManager::SUPV_UPDATE_FAILED), event::getEventId(PlocSupvUartManager::SUPV_MEM_CHECK_FAIL)); if (result != returnvalue::OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "PlocSupervisorHandler::eventSubscritpion: Failed to subscribe to events from " " ploc supervisor helper" << std::endl; #endif return ObjectManagerIF::CHILD_INIT_FAILED; } return result; } ReturnValue_t FreshSupvHandler::handleAckReport(const uint8_t* data) { using namespace supv; ReturnValue_t result = returnvalue::OK; AcknowledgmentReport ack(tmReader); result = ack.parse(false); if (result != returnvalue::OK) { return result; } auto infoIter = activeActionCmds.find(buildActiveCmdKey(ack.getRefModuleApid(), ack.getRefServiceId())); if (infoIter == activeActionCmds.end()) { triggerEvent(SUPV_ACK_UNKNOWN_COMMAND, ack.getRefModuleApid(), ack.getRefServiceId()); return result; } ActiveCmdInfo& info = infoIter->second; if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::NAK)) { triggerEvent(SUPV_ACK_FAILURE, info.commandId, static_cast<uint32_t>(ack.getStatusCode())); ack.printStatusInformationAck(); printAckFailureInfo(ack.getStatusCode(), info.commandId); if (info.commandedBy != MessageQueueIF::NO_QUEUE) { actionHelper.finish(false, info.commandedBy, info.commandId, result::RECEIVED_ACK_FAILURE); } info.isPending = false; return returnvalue::OK; } info.ackRecv = true; performCommandCompletionHandling(static_cast<supv::Apid>((infoIter->first >> 16) & 0xffff), infoIter->first & 0xff, info); return result; } void FreshSupvHandler::performCommandCompletionHandling(supv::Apid apid, uint8_t serviceId, ActiveCmdInfo& info) { if (info.ackRecv and info.ackExeRecv and (not info.replyPacketExpected or info.replyPacketReceived)) { if (info.commandedBy != MessageQueueIF::NO_QUEUE) { actionHelper.finish(true, info.commandedBy, info.commandId, returnvalue::OK); } info.isPending = false; } } void FreshSupvHandler::printAckFailureInfo(uint16_t statusCode, DeviceCommandId_t commandId) { switch (commandId) { case (supv::SET_TIME_REF): { sif::warning << "PlocSupervisoHandler: Setting time failed. Make sure the OBC has a valid time" << std::endl; break; } default: break; } } uint32_t FreshSupvHandler::buildActiveCmdKey(uint16_t moduleApid, uint8_t serviceId) { return (moduleApid << 16) | serviceId; } ReturnValue_t FreshSupvHandler::handleExecutionReport(const uint8_t* data) { using namespace supv; ReturnValue_t result = returnvalue::OK; ExecutionReport exe(tmReader); result = exe.parse(false); if (result != OK) { sif::warning << "FreshSupvHandler::handleExecutionReport: Parsing ACK EXE failed" << std::endl; return result; } auto infoIter = activeActionCmds.find(buildActiveCmdKey(exe.getRefModuleApid(), exe.getRefServiceId())); if (infoIter == activeActionCmds.end()) { triggerEvent(SUPV_EXE_ACK_UNKNOWN_COMMAND, exe.getRefModuleApid(), exe.getRefServiceId()); return result; } ActiveCmdInfo& info = infoIter->second; if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK)) { result = handleExecutionSuccessReport(info, exe); } else if (tmReader.getServiceId() == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_NAK)) { handleExecutionFailureReport(info, exe); return returnvalue::OK; } info.ackExeRecv = true; performCommandCompletionHandling(static_cast<supv::Apid>((infoIter->first >> 16) & 0xffff), infoIter->first & 0xff, info); return result; } ReturnValue_t FreshSupvHandler::handleExecutionSuccessReport(ActiveCmdInfo& info, ExecutionReport& report) { switch (info.commandId) { case supv::READ_GPIO: { // TODO: Fix uint16_t gpioState = report.getStatusCode(); #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 sif::info << "PlocSupervisorHandler: Read GPIO TM, State: " << gpioState << std::endl; #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ uint8_t data[sizeof(gpioState)]; size_t size = 0; ReturnValue_t result = SerializeAdapter::serialize(&gpioState, data, &size, sizeof(gpioState), SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::debug << "PlocSupervisorHandler: Failed to deserialize GPIO state" << std::endl; } result = actionHelper.reportData(info.commandedBy, info.commandId, data, sizeof(data)); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler: Read GPIO, failed to report data" << std::endl; } break; } case supv::SET_TIME_REF: { if (startupState == StartupState::WAIT_FOR_TIME_REPLY) { startupState = StartupState::TIME_WAS_SET; } break; } default: break; } return returnvalue::OK; } void FreshSupvHandler::handleExecutionFailureReport(ActiveCmdInfo& info, ExecutionReport& report) { using namespace supv; report.printStatusInformationExe(); if (info.commandId != DeviceHandlerIF::NO_COMMAND_ID) { triggerEvent(SUPV_EXE_FAILURE, info.commandId, static_cast<uint32_t>(report.getStatusCode())); } if (info.commandedBy) { actionHelper.finish(false, info.commandedBy, info.commandId, report.getStatusCode()); } info.isPending = false; } void FreshSupvHandler::confirmReplyPacketReceived(supv::Apid apid, uint8_t serviceId) { auto infoIter = activeActionCmds.find(buildActiveCmdKey(apid, serviceId)); if (infoIter != activeActionCmds.end()) { ActiveCmdInfo& info = infoIter->second; info.replyPacketReceived = true; performCommandCompletionHandling(static_cast<supv::Apid>((infoIter->first >> 16) & 0xffff), infoIter->first & 0xff, info); } } ReturnValue_t FreshSupvHandler::handleHkReport(const uint8_t* data) { ReturnValue_t result = returnvalue::OK; result = verifyPacket(data, tmReader.getFullPacketLen()); if (result == result::CRC_FAILURE) { sif::error << "PlocSupervisorHandler::handleHkReport: Hk report has invalid crc" << std::endl; return result; } uint16_t offset = supv::PAYLOAD_OFFSET; hkSet.tempPs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.tempPl = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.tempSup = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; size_t size = sizeof(hkSet.uptime.value); result = SerializeAdapter::deSerialize(&hkSet.uptime, data + offset, &size, SerializeIF::Endianness::BIG); offset += 8; hkSet.cpuLoad = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.availableHeap = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.numTcs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.numTms = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.socState = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8 | *(data + offset + 3); offset += 4; hkSet.nvm0_1_state = *(data + offset); offset += 1; hkSet.nvm3_state = *(data + offset); offset += 1; hkSet.missionIoState = *(data + offset); offset += 1; hkSet.fmcState = *(data + offset); offset += 1; hkSet.setValidity(true, true); confirmReplyPacketReceived(supv::Apid::HK, static_cast<uint8_t>(supv::tc::HkId::GET_REPORT)); #if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1 sif::info << "PlocSupervisorHandler::handleHkReport: temp_ps: " << hkSet.tempPs << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: temp_pl: " << hkSet.tempPl << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: temp_sup: " << hkSet.tempSup << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: uptime: " << hkSet.uptime << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: cpu_load: " << hkSet.cpuLoad << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: available_heap: " << hkSet.availableHeap << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: num_tcs: " << hkSet.numTcs << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: num_tms: " << hkSet.numTms << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: soc_state: " << hkSet.socState << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: nvm0_1_state: " << static_cast<unsigned int>(hkSet.nvm0_1_state.value) << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: nvm3_state: " << static_cast<unsigned int>(hkSet.nvm3_state.value) << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: mission_io_state: " << static_cast<unsigned int>(hkSet.missionIoState.value) << std::endl; sif::info << "PlocSupervisorHandler::handleHkReport: fmc_state: " << static_cast<unsigned int>(hkSet.fmcState.value) << std::endl; #endif return result; } ReturnValue_t FreshSupvHandler::verifyPacket(const uint8_t* start, size_t foundLen) { if (CRC::crc16ccitt(start, foundLen) != 0) { return result::CRC_FAILURE; } return returnvalue::OK; } ReturnValue_t FreshSupvHandler::handleBootStatusReport(const uint8_t* data) { ReturnValue_t result = returnvalue::OK; result = verifyPacket(data, tmReader.getFullPacketLen()); if (result == result::CRC_FAILURE) { sif::error << "PlocSupervisorHandler::handleBootStatusReport: Boot status report has invalid" " crc" << std::endl; return result; } PoolReadGuard pg(&bootStatusReport); if (pg.getReadResult() != returnvalue::OK) { return pg.getReadResult(); } const uint8_t* payloadStart = tmReader.getPayloadStart(); uint16_t offset = 0; bootStatusReport.socState = payloadStart[0]; offset += 1; bootStatusReport.powerCycles = payloadStart[1]; offset += 1; bootStatusReport.bootAfterMs = *(payloadStart + offset) << 24 | *(payloadStart + offset + 1) << 16 | *(payloadStart + offset + 2) << 8 | *(payloadStart + offset + 3); offset += 4; bootStatusReport.bootTimeoutMs = *(payloadStart + offset) << 24 | *(payloadStart + offset + 1) << 16 | *(payloadStart + offset + 2) << 8 | *(payloadStart + offset + 3); offset += 4; bootStatusReport.activeNvm = *(payloadStart + offset); offset += 1; bootStatusReport.bp0State = *(payloadStart + offset); offset += 1; bootStatusReport.bp1State = *(payloadStart + offset); offset += 1; bootStatusReport.bp2State = *(payloadStart + offset); offset += 1; bootStatusReport.bootState = *(payloadStart + offset); offset += 1; bootStatusReport.bootCycles = *(payloadStart + offset); bootStatusReport.setValidity(true, true); confirmReplyPacketReceived(supv::Apid::BOOT_MAN, static_cast<uint8_t>(supv::tc::BootManId::GET_BOOT_STATUS_REPORT)); #if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1 sif::info << "PlocSupervisorHandler::handleBootStatusReport: SoC State (0 - off, 1 - booting, 2 " "- Update, 3 " "- operating, 4 - Shutdown, 5 - Reset): " << static_cast<unsigned int>(bootStatusReport.socState.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: Power Cycles: " << static_cast<unsigned int>(bootStatusReport.powerCycles.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: BootAfterMs: " << bootStatusReport.bootAfterMs << " ms" << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: BootTimeoutMs: " << std::dec << bootStatusReport.bootTimeoutMs << " ms" << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: Active NVM: " << static_cast<unsigned int>(bootStatusReport.activeNvm.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP0: " << static_cast<unsigned int>(bootStatusReport.bp0State.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP1: " << static_cast<unsigned int>(bootStatusReport.bp1State.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: BP2: " << static_cast<unsigned int>(bootStatusReport.bp2State.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: Boot state: " << static_cast<unsigned int>(bootStatusReport.bootState.value) << std::endl; sif::info << "PlocSupervisorHandler::handleBootStatusReport: Boot cycles: " << static_cast<unsigned int>(bootStatusReport.bootCycles.value) << std::endl; #endif return result; } ReturnValue_t FreshSupvHandler::handleLatchupStatusReport(const uint8_t* data) { ReturnValue_t result = returnvalue::OK; result = verifyPacket(data, tmReader.getFullPacketLen()); if (result == result::CRC_FAILURE) { sif::error << "PlocSupervisorHandler::handleLatchupStatusReport: Latchup status report has " << "invalid crc" << std::endl; return result; } PoolReadGuard pg(&latchupStatusReport); if (pg.getReadResult() != returnvalue::OK) { return pg.getReadResult(); } const uint8_t* payloadData = tmReader.getPayloadStart(); uint16_t offset = 0; latchupStatusReport.id = *(payloadData + offset); offset += 1; latchupStatusReport.cnt0 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt1 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt2 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt3 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt4 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt5 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; latchupStatusReport.cnt6 = *(payloadData + offset) << 8 | *(payloadData + offset + 1); offset += 2; uint16_t msec = *(payloadData + offset) << 8 | *(payloadData + offset + 1); latchupStatusReport.isSynced = msec >> supv::LatchupStatusReport::IS_SET_BIT_POS; latchupStatusReport.timeMsec = msec & (~(1 << latchupStatusReport.IS_SET_BIT_POS)); offset += 2; latchupStatusReport.timeSec = *(payloadData + offset); offset += 1; latchupStatusReport.timeMin = *(payloadData + offset); offset += 1; latchupStatusReport.timeHour = *(payloadData + offset); offset += 1; latchupStatusReport.timeDay = *(payloadData + offset); offset += 1; latchupStatusReport.timeMon = *(payloadData + offset); offset += 1; latchupStatusReport.timeYear = *(payloadData + offset); latchupStatusReport.setValidity(true, true); confirmReplyPacketReceived(supv::Apid::LATCHUP_MON, static_cast<uint8_t>(supv::tc::LatchupMonId::GET_STATUS_REPORT)); #if OBSW_VERBOSE_LEVEL >= 1 && OBSW_DEBUG_PLOC_SUPERVISOR == 1 sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Latchup ID: " << static_cast<unsigned int>(latchupStatusReport.id.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT0: " << latchupStatusReport.cnt0 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT1: " << latchupStatusReport.cnt1 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT2: " << latchupStatusReport.cnt2 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT3: " << latchupStatusReport.cnt3 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT4: " << latchupStatusReport.cnt4 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT5: " << latchupStatusReport.cnt5 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: CNT6: " << latchupStatusReport.cnt6 << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Sec: " << static_cast<unsigned int>(latchupStatusReport.timeSec.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Min: " << static_cast<unsigned int>(latchupStatusReport.timeMin.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Hour: " << static_cast<unsigned int>(latchupStatusReport.timeHour.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Day: " << static_cast<unsigned int>(latchupStatusReport.timeDay.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Mon: " << static_cast<unsigned int>(latchupStatusReport.timeMon.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Year: " << static_cast<unsigned int>(latchupStatusReport.timeYear.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: Msec: " << static_cast<unsigned int>(latchupStatusReport.timeMsec.value) << std::endl; sif::info << "PlocSupervisorHandler::handleLatchupStatusReport: isSet: " << static_cast<unsigned int>(latchupStatusReport.isSet.value) << std::endl; #endif return result; } ReturnValue_t FreshSupvHandler::genericHandleTm(const char* contextString, const uint8_t* data, LocalPoolDataSetBase& set, supv::Apid apid, uint8_t serviceId) { ReturnValue_t result = returnvalue::OK; result = verifyPacket(data, tmReader.getFullPacketLen()); if (result == result::CRC_FAILURE) { sif::warning << "PlocSupervisorHandler: " << contextString << " report has " << "invalid CRC" << std::endl; return result; } const uint8_t* dataField = data + supv::PAYLOAD_OFFSET; PoolReadGuard pg(&set); if (pg.getReadResult() != returnvalue::OK) { return result; } set.setValidityBufferGeneration(false); size_t size = set.getSerializedSize(); result = set.deSerialize(&dataField, &size, SerializeIF::Endianness::BIG); if (result != returnvalue::OK) { sif::warning << "PlocSupervisorHandler: Deserialization failed" << std::endl; } set.setValidityBufferGeneration(true); set.setValidity(true, true); confirmReplyPacketReceived(apid, serviceId); return result; } bool FreshSupvHandler::isCommandPending() const { for (const auto& info : activeActionCmds) { if (info.second.isPending) { return true; } } return false; }