#include #include // Contains file controls like O_RDWR #include #include #include #include #include #include #include #include "OBSWConfig.h" #include "tas/hdlc.h" #ifdef XIPHOS_Q7S #include "bsp_q7s/fs/FilesystemHelper.h" #include "bsp_q7s/fs/SdCardManager.h" #endif #include "fsfw/tasks/TaskFactory.h" #include "fsfw/timemanager/Countdown.h" #include "fsfw_hal/linux/uart/helper.h" #include "mission/utility/Filenaming.h" #include "mission/utility/ProgressPrinter.h" #include "mission/utility/Timestamp.h" using namespace returnvalue; using namespace supv; PlocSupvHelper::PlocSupvHelper(object_id_t objectId) : SystemObject(objectId), recRingBuf(4096, true), decodedRingBuf(1200 * MAX_STORED_DECODED_PACKETS, true), ipcRingBuf(1200 * MAX_STORED_DECODED_PACKETS, true) { resetSpParams(); semaphore = SemaphoreFactory::instance()->createBinarySemaphore(); semaphore->acquire(); lock = MutexFactory::instance()->createMutex(); ipcLock = MutexFactory::instance()->createMutex(); } PlocSupvHelper::~PlocSupvHelper() = default; ReturnValue_t PlocSupvHelper::initializeInterface(CookieIF* cookie) { UartCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { return FAILED; } std::string devname = uartCookie->getDeviceFile(); /* Get file descriptor */ serialPort = open(devname.c_str(), O_RDWR); if (serialPort < 0) { sif::warning << "ScexUartReader::initializeInterface: open call failed with error [" << errno << ", " << strerror(errno) << std::endl; return FAILED; } // Setting up UART parameters tty.c_cflag &= ~PARENB; // Clear parity bit uart::setParity(tty, uartCookie->getParity()); uart::setStopbits(tty, uartCookie->getStopBits()); uart::setBitsPerWord(tty, BitsPerWord::BITS_8); tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control uart::enableRead(tty); uart::ignoreCtrlLines(tty); // Use non-canonical mode and clear echo flag tty.c_lflag &= ~(ICANON | ECHO); // Non-blocking mode, 0.5 seconds timeout tty.c_cc[VTIME] = 5; tty.c_cc[VMIN] = 0; uart::setBaudrate(tty, uartCookie->getBaudrate()); if (tcsetattr(serialPort, TCSANOW, &tty) != 0) { sif::warning << "ScexUartReader::initializeInterface: tcsetattr call failed with error [" << errno << ", " << strerror(errno) << std::endl; } // Flush received and unread data tcflush(serialPort, TCIOFLUSH); return OK; } ReturnValue_t PlocSupvHelper::initialize() { #ifdef XIPHOS_Q7S sdcMan = SdCardManager::instance(); if (sdcMan == nullptr) { sif::warning << "PlocSupvHelper::initialize: Invalid SD Card Manager" << std::endl; return returnvalue::FAILED; } #endif return returnvalue::OK; } ReturnValue_t PlocSupvHelper::performOperation(uint8_t operationCode) { bool putTaskToSleep = false; while (true) { semaphore->acquire(); while (true) { putTaskToSleep = handleUartReception(); if (putTaskToSleep) { performUartShutdown(); break; } lock->lockMutex(); InternalState currentState = state; lock->unlockMutex(); switch (currentState) { case InternalState::SLEEPING: case InternalState::GO_TO_SLEEP: { putTaskToSleep = true; break; } case InternalState::LONGER_REQUEST: { if (handleRunningLongerRequest() == REQUEST_DONE) { MutexGuard mg(lock); state = InternalState::DEFAULT; } break; } case InternalState::DEFAULT: { break; } } if (putTaskToSleep) { performUartShutdown(); break; } } } } bool PlocSupvHelper::handleUartReception() { ReturnValue_t result = OK; ssize_t bytesRead = read(serialPort, reinterpret_cast(recBuf.data()), static_cast(recBuf.size())); if (bytesRead == 0) { { MutexGuard mg(lock); if (state == InternalState::GO_TO_SLEEP) { return true; } } while (result != NO_PACKET_FOUND) { result = tryHdlcParsing(); } } else if (bytesRead < 0) { sif::warning << "PlocSupvHelper::performOperation: read call failed with error [" << errno << ", " << strerror(errno) << "]" << std::endl; return true; } else if (bytesRead >= static_cast(recBuf.size())) { sif::error << "PlocSupvHelper::performOperation: Receive buffer too small for " << bytesRead << " bytes" << std::endl; } else if (bytesRead > 0) { if (debugMode) { sif::info << "Received " << bytesRead << " bytes from the PLOC Supervisor:" << std::endl; } recRingBuf.writeData(recBuf.data(), bytesRead); tryHdlcParsing(); } return false; } ReturnValue_t PlocSupvHelper::startUpdate(std::string file, uint8_t memoryId, uint32_t startAddress) { supv::UpdateParams params; params.file = file; params.memId = memoryId; params.startAddr = startAddress; params.bytesWritten = 0; params.seqCount = 1; params.deleteMemory = true; return performUpdate(params); } ReturnValue_t PlocSupvHelper::performUpdate(const supv::UpdateParams& params) { lock->lockMutex(); InternalState current = state; lock->unlockMutex(); if (current != InternalState::DEFAULT) { return HasActionsIF::IS_BUSY; } ReturnValue_t result = returnvalue::OK; #ifdef XIPHOS_Q7S result = FilesystemHelper::checkPath(params.file); if (result != returnvalue::OK) { sif::warning << "PlocSupvHelper::startUpdate: File " << params.file << " does not exist" << std::endl; return result; } result = FilesystemHelper::fileExists(params.file); if (result != returnvalue::OK) { sif::warning << "PlocSupvHelper::startUpdate: The file " << params.file << " does not exist" << std::endl; return result; } #endif #ifdef TE0720_1CFA if (not std::filesystem::exists(file)) { sif::warning << "PlocSupvHelper::startUpdate: The file " << file << " does not exist" << std::endl; return returnvalue::FAILED; } #endif { MutexGuard mg(lock); update.file = params.file; update.fullFileSize = getFileSize(update.file); if (params.bytesWritten > update.fullFileSize) { sif::warning << "Invalid start bytes counter " << params.bytesWritten << ", smaller than full file length" << update.fullFileSize << std::endl; return returnvalue::FAILED; } update.length = update.fullFileSize - params.bytesWritten; update.memoryId = params.memId; update.startAddress = params.startAddr; update.progressPercent = 0; update.bytesWritten = params.bytesWritten; update.crcShouldBeChecked = true; update.packetNum = 1; update.deleteMemory = params.deleteMemory; update.sequenceCount = params.seqCount; state = InternalState::LONGER_REQUEST; request = Request::UPDATE; } return result; } ReturnValue_t PlocSupvHelper::performMemCheck(std::string file, uint8_t memoryId, uint32_t startAddress) { lock->lockMutex(); InternalState current = state; lock->unlockMutex(); if (current != InternalState::DEFAULT) { return HasActionsIF::IS_BUSY; } return performMemCheck(file, memoryId, startAddress, getFileSize(update.file), true); } ReturnValue_t PlocSupvHelper::performMemCheck(std::string file, uint8_t memoryId, uint32_t startAddress, size_t sizeToCheck, bool checkCrc) { { MutexGuard mg(lock); update.file = file; update.fullFileSize = getFileSize(file); state = InternalState::LONGER_REQUEST; request = Request::CHECK_MEMORY; update.memoryId = memoryId; update.startAddress = startAddress; update.length = sizeToCheck; update.crcShouldBeChecked = checkCrc; } return returnvalue::OK; } ReturnValue_t PlocSupvHelper::initiateUpdateContinuation() { lock->lockMutex(); InternalState current = state; lock->unlockMutex(); if (current != InternalState::DEFAULT) { return HasActionsIF::IS_BUSY; } MutexGuard mg(lock); state = InternalState::LONGER_REQUEST; request = Request::CONTINUE_UPDATE; return returnvalue::OK; } // ReturnValue_t PlocSupvHelper::startEventBufferRequest(std::string path) { //#ifdef XIPHOS_Q7S // ReturnValue_t result = FilesystemHelper::checkPath(path); // if (result != returnvalue::OK) { // return result; // } //#endif // if (not std::filesystem::exists(path)) { // return PATH_NOT_EXISTS; // } // eventBufferReq.path = path; // request = Request::REQUEST_EVENT_BUFFER; // //uartComIF->flushUartTxAndRxBuf(comCookie); // semaphore->release(); // return returnvalue::OK; // } void PlocSupvHelper::stop() { MutexGuard mg(lock); state = InternalState::GO_TO_SLEEP; } void PlocSupvHelper::executeFullCheckMemoryCommand() { ReturnValue_t result; if (update.crcShouldBeChecked) { sif::info << "PLOC SUPV Mem Check: Calculating Image CRC" << std::endl; result = calcImageCrc(); if (result != returnvalue::OK) { triggerEvent(SUPV_MEM_CHECK_FAIL, result); return; } } sif::info << "PLOC SUPV Mem Check: Selecting Memory" << std::endl; result = selectMemory(); if (result != returnvalue::OK) { triggerEvent(SUPV_MEM_CHECK_FAIL, result); return; } sif::info << "PLOC SUPV Mem Check: Preparing Update" << std::endl; result = prepareUpdate(); if (result != returnvalue::OK) { triggerEvent(SUPV_MEM_CHECK_FAIL, result); return; } sif::info << "PLOC SUPV Mem Check: Memory Check" << std::endl; result = handleCheckMemoryCommand(); if (result == returnvalue::OK) { triggerEvent(SUPV_MEM_CHECK_OK, result); } else { triggerEvent(SUPV_MEM_CHECK_FAIL, result); } } ReturnValue_t PlocSupvHelper::executeUpdate() { ReturnValue_t result = returnvalue::OK; sif::info << "PLOC SUPV Update MPSoC: Calculating Image CRC" << std::endl; result = calcImageCrc(); if (result != returnvalue::OK) { return result; } sif::info << "PLOC SUPV Update MPSoC: Selecting Memory" << std::endl; result = selectMemory(); if (result != returnvalue::OK) { return result; } sif::info << "PLOC SUPV Update MPSoC: Preparing Update" << std::endl; result = prepareUpdate(); if (result != returnvalue::OK) { return result; } if (update.deleteMemory) { sif::info << "PLOC SUPV Update MPSoC: Erasing Memory" << std::endl; result = eraseMemory(); if (result != returnvalue::OK) { return result; } } return updateOperation(); } ReturnValue_t PlocSupvHelper::continueUpdate() { ReturnValue_t result = prepareUpdate(); if (result != returnvalue::OK) { return result; } return updateOperation(); } ReturnValue_t PlocSupvHelper::updateOperation() { sif::info << "PlocSupvHelper::performUpdate: Writing Update Packets" << std::endl; auto result = writeUpdatePackets(); if (result != returnvalue::OK) { return result; } sif::info << "PlocSupvHelper::performUpdate: Memory Check" << std::endl; return handleCheckMemoryCommand(); } ReturnValue_t PlocSupvHelper::writeUpdatePackets() { ReturnValue_t result = returnvalue::OK; #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 ProgressPrinter progressPrinter("Supervisor update", update.fullFileSize, ProgressPrinter::HALF_PERCENT); #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ uint8_t tempData[supv::WriteMemory::CHUNK_MAX + 1]{}; std::ifstream file(update.file, std::ifstream::binary); uint16_t dataLength = 0; ccsds::SequenceFlags seqFlags; while (update.bytesWritten < update.fullFileSize) { size_t remainingSize = update.fullFileSize - update.bytesWritten; bool lastSegment = false; if (remainingSize > supv::WriteMemory::CHUNK_MAX) { dataLength = supv::WriteMemory::CHUNK_MAX; } else { lastSegment = true; dataLength = static_cast(remainingSize); } if (file.is_open()) { file.seekg(update.bytesWritten, std::ios::beg); file.read(reinterpret_cast(tempData), dataLength); if (!file) { sif::warning << "PlocSupvHelper::performUpdate: Read only " << file.gcount() << " of " << dataLength << " bytes" << std::endl; sif::info << "PlocSupvHelper::performUpdate: Failed when trying to read byte " << update.bytesWritten << std::endl; } } else { return FILE_CLOSED_ACCIDENTALLY; } if (update.bytesWritten == 0) { seqFlags = ccsds::SequenceFlags::FIRST_SEGMENT; } else if (lastSegment) { seqFlags = ccsds::SequenceFlags::LAST_SEGMENT; } else { seqFlags = ccsds::SequenceFlags::CONTINUATION; } resetSpParams(); float progress = static_cast(update.bytesWritten) / update.fullFileSize; uint8_t progPercent = std::floor(progress * 100); if (progPercent > update.progressPercent) { update.progressPercent = progPercent; if (progPercent % 5 == 0) { // Useful to allow restarting the update triggerEvent(SUPV_UPDATE_PROGRESS, buildProgParams1(progPercent, update.sequenceCount), update.bytesWritten); } } supv::WriteMemory packet(spParams); result = packet.buildPacket(seqFlags, update.sequenceCount, update.memoryId, update.startAddress + update.bytesWritten, dataLength, tempData); if (result != returnvalue::OK) { triggerEvent(WRITE_MEMORY_FAILED, buildProgParams1(progPercent, update.sequenceCount), update.bytesWritten); return result; } result = encodeAndSendPacket(packet.getFullPacket(), packet.getFullPacketLen()); if (result != returnvalue::OK) { triggerEvent(WRITE_MEMORY_FAILED, buildProgParams1(progPercent, update.sequenceCount), update.bytesWritten); return result; } update.sequenceCount++; update.packetNum += 1; update.bytesWritten += dataLength; #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 progressPrinter.print(update.bytesWritten); #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ } return result; } uint32_t PlocSupvHelper::buildProgParams1(uint8_t percent, uint16_t seqCount) { return (static_cast(percent) << 24) | static_cast(seqCount); } // ReturnValue_t PlocSupvHelper::performEventBufferRequest() { // using namespace supv; // ReturnValue_t result = returnvalue::OK; // resetSpParams(); // RequestLoggingData packet(spParams); // result = packet.buildPacket(RequestLoggingData::Sa::REQUEST_EVENT_BUFFERS); // if (result != returnvalue::OK) { // return result; // } // result = sendCommand(packet); // if (result != returnvalue::OK) { // return result; // } // result = handleAck(); // if (result != returnvalue::OK) { // return result; // } // result = // handleTmReception(ccsds::HEADER_LEN, tmBuf.data(), // supv::recv_timeout::UPDATE_STATUS_REPORT); // if (result != returnvalue::OK) { // return result; // } // ploc::SpTmReader spReader(tmBuf.data(), tmBuf.size()); // bool exeAlreadyReceived = false; // if (spReader.getApid() == supv::APID_EXE_FAILURE) { // exeAlreadyReceived = true; // result = handleRemainingExeReport(spReader); // } else if (spReader.getApid() == supv::APID_MRAM_DUMP_TM) { // result = handleEventBufferReception(spReader); // } // // if (not exeAlreadyReceived) { // result = handleExe(); // if (result != returnvalue::OK) { // return result; // } // } // return result; // } ReturnValue_t PlocSupvHelper::handleRemainingExeReport(ploc::SpTmReader& reader) { size_t remBytes = reader.getPacketDataLen() + 1; ReturnValue_t result = handleTmReception(remBytes, tmBuf.data() + ccsds::HEADER_LEN); if (result != returnvalue::OK) { sif::warning << "Reading exe failure report failed" << std::endl; } result = exeReportHandling(); if (result != returnvalue::OK) { sif::warning << "Handling exe report failed" << std::endl; } return result; } ReturnValue_t PlocSupvHelper::selectMemory() { ReturnValue_t result = returnvalue::OK; resetSpParams(); supv::MPSoCBootSelect packet(spParams); result = packet.buildPacket(update.memoryId); if (result != returnvalue::OK) { return result; } result = handlePacketTransmissionNoReply(packet); if (result != returnvalue::OK) { return result; } return returnvalue::OK; } ReturnValue_t PlocSupvHelper::prepareUpdate() { ReturnValue_t result = returnvalue::OK; resetSpParams(); supv::NoPayloadPacket packet(spParams, Apid::BOOT_MAN, static_cast(tc::BootManId::PREPARE_UPDATE)); result = packet.buildPacket(); if (result != returnvalue::OK) { return result; } result = handlePacketTransmissionNoReply(packet, PREPARE_UPDATE_EXECUTION_REPORT); if (result != returnvalue::OK) { return result; } return returnvalue::OK; } ReturnValue_t PlocSupvHelper::eraseMemory() { ReturnValue_t result = returnvalue::OK; resetSpParams(); supv::EraseMemory eraseMemory(spParams); result = eraseMemory.buildPacket(update.memoryId, update.startAddress + update.bytesWritten, update.length); if (result != returnvalue::OK) { return result; } result = handlePacketTransmissionNoReply(eraseMemory, supv::timeout::ERASE_MEMORY); if (result != returnvalue::OK) { return result; } return returnvalue::OK; } ReturnValue_t PlocSupvHelper::handlePacketTransmissionNoReply(supv::TcBase& packet, uint32_t timeoutExecutionReport) { ReturnValue_t result = returnvalue::OK; result = encodeAndSendPacket(packet.getFullPacket(), packet.getFullPacketLen()); if (result != returnvalue::OK) { return result; } Countdown countdown(timeoutExecutionReport); bool ackReceived = false; while (true) { if (not decodedQueue.empty()) { size_t packetLen = 0; decodedQueue.retrieve(&packetLen); decodedRingBuf.readData(decodedBuf.data(), packetLen); tmReader.setData(decodedBuf.data(), packetLen); result = checkReceivedTm(); if (result != returnvalue::OK) { continue; } if (tmReader.getApid() == Apid::TMTC_MAN) { uint8_t serviceId = tmReader.getServiceId(); int retval = 0; if (not ackReceived) { retval = handleAckReception(packet, serviceId, packetLen); if (retval == 1) { ackReceived = true; } else if (retval == -1) { return returnvalue::FAILED; } } else { retval = handleExeAckReception(packet, serviceId, packetLen); if (retval == 1) { break; } else if (retval == -1) { return returnvalue::FAILED; } } } else { pushIpcData(decodedBuf.data(), packetLen); decodedRingBuf.deleteData(packetLen); } } else { TaskFactory::delayTask(50); } if (countdown.hasTimedOut()) { return result::NO_REPLY_TIMEOUT; } } return returnvalue::OK; } int PlocSupvHelper::handleAckReception(supv::TcBase& tc, uint8_t serviceId, size_t packetLen) { if (serviceId == static_cast(supv::tm::TmtcId::ACK) or serviceId == static_cast(supv::tm::TmtcId::NAK)) { AcknowledgmentReport ackReport(tmReader); ReturnValue_t result = ackReport.parse(); if (result != returnvalue::OK) { triggerEvent(ACK_RECEPTION_FAILURE); return returnvalue::FAILED; } if (ackReport.getRefApid() == tc.getApid() and ackReport.getRefServiceId() == tc.getServiceId()) { if (serviceId == static_cast(supv::tm::TmtcId::ACK)) { return 1; } else if (serviceId == static_cast(supv::tm::TmtcId::NAK)) { ackReport.printStatusInformation(); triggerEvent(SUPV_ACK_FAILURE_REPORT, buildApidServiceParam1(ackReport.getRefApid(), ackReport.getRefServiceId()), ackReport.getStatusCode()); return -1; } // Should never happen return -1; } else { pushIpcData(decodedBuf.data(), packetLen); decodedRingBuf.deleteData(packetLen); } } return 0; } int PlocSupvHelper::handleExeAckReception(supv::TcBase& tc, uint8_t serviceId, size_t packetLen) { if (serviceId == static_cast(supv::tm::TmtcId::EXEC_ACK) or serviceId == static_cast(supv::tm::TmtcId::EXEC_NAK)) { ExecutionReport exeReport(tmReader); ReturnValue_t result = exeReport.parse(); if (result != returnvalue::OK) { triggerEvent(EXE_RECEPTION_FAILURE); return returnvalue::FAILED; } if (exeReport.getRefApid() == tc.getApid() and exeReport.getRefServiceId() == tc.getServiceId()) { if (serviceId == static_cast(supv::tm::TmtcId::EXEC_ACK)) { return 1; } else if (serviceId == static_cast(supv::tm::TmtcId::EXEC_NAK)) { exeReport.printStatusInformation(); triggerEvent(SUPV_EXE_FAILURE_REPORT, buildApidServiceParam1(exeReport.getRefApid(), exeReport.getRefServiceId()), exeReport.getStatusCode()); return -1; } // Should never happen return -1; } else { pushIpcData(decodedBuf.data(), packetLen); decodedRingBuf.deleteData(packetLen); } } return 0; } ReturnValue_t PlocSupvHelper::checkReceivedTm() { ReturnValue_t result = tmReader.checkSize(); if (result != returnvalue::OK) { triggerEvent(SUPV_REPLY_SIZE_MISSMATCH, rememberApid); return result; } result = tmReader.checkCrc(); if (result != returnvalue::OK) { triggerEvent(SUPV_REPLY_CRC_MISSMATCH, rememberApid); return result; } return result; } ReturnValue_t PlocSupvHelper::calcImageCrc() { ReturnValue_t result = returnvalue::OK; if (update.fullFileSize == 0) { return returnvalue::FAILED; } #ifdef XIPHOS_Q7S result = FilesystemHelper::checkPath(update.file); if (result != returnvalue::OK) { sif::warning << "PlocSupvHelper::calcImageCrc: File " << update.file << " does not exist" << std::endl; return result; } #endif auto crc16Calcer = etl::crc16_ccitt(); std::ifstream file(update.file, std::ifstream::binary); std::array crcBuf{}; #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 ProgressPrinter progress("Supervisor update crc calculation", update.fullFileSize, ProgressPrinter::ONE_PERCENT); #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ uint32_t byteCount = 0; size_t bytesToRead = 1024; while (byteCount < update.fullFileSize) { size_t remLen = update.fullFileSize - byteCount; if (remLen < 1024) { bytesToRead = remLen; } else { bytesToRead = 1024; } file.seekg(byteCount, file.beg); file.read(reinterpret_cast(crcBuf.data()), bytesToRead); crc16Calcer.add(crcBuf.begin(), crcBuf.begin() + bytesToRead); #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 progress.print(byteCount); #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ byteCount += bytesToRead; } #if OBSW_DEBUG_PLOC_SUPERVISOR == 1 progress.print(byteCount); #endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */ update.crc = crc16Calcer.value(); return result; } ReturnValue_t PlocSupvHelper::handleCheckMemoryCommand() { ReturnValue_t result = returnvalue::OK; resetSpParams(); supv::CheckMemory packet(spParams); result = packet.buildPacket(update.memoryId, update.startAddress, update.fullFileSize); if (result != returnvalue::OK) { return result; } result = encodeAndSendPacket(packet.getFullPacket(), packet.getFullPacketLen()); if (result != returnvalue::OK) { return result; } Countdown countdown(timeout::CRC_EXECUTION_TIMEOUT); bool ackReceived = false; bool checkReplyReceived = false; while (true) { if (not decodedQueue.empty()) { size_t packetLen = 0; decodedQueue.retrieve(&packetLen); decodedRingBuf.readData(decodedBuf.data(), packetLen); tmReader.setData(decodedBuf.data(), packetLen); result = checkReceivedTm(); if (result != returnvalue::OK) { continue; } if (tmReader.getApid() == Apid::TMTC_MAN) { uint8_t serviceId = tmReader.getServiceId(); int retval = 0; if (not ackReceived) { retval = handleAckReception(packet, serviceId, packetLen); if (retval == 1) { ackReceived = true; } else if (retval == -1) { return returnvalue::FAILED; } } else if (checkReplyReceived) { retval = handleExeAckReception(packet, serviceId, packetLen); if (retval == 1) { break; } else if (retval == -1) { return returnvalue::FAILED; } } } else if (tmReader.getApid() == Apid::MEM_MAN) { if (ackReceived) { supv::UpdateStatusReport report(tmReader); result = report.parse(); if (result != returnvalue::OK) { return result; } if (update.crcShouldBeChecked) { result = report.verifyCrc(update.crc); if (result != returnvalue::OK) { sif::warning << "PlocSupvHelper::handleCheckMemoryCommand: CRC failure. Expected CRC 0x" << std::setfill('0') << std::hex << std::setw(4) << static_cast(update.crc) << " but received CRC 0x" << std::setw(4) << report.getCrc() << std::dec << std::endl; return result; } } checkReplyReceived = true; } } else { pushIpcData(decodedBuf.data(), packetLen); decodedRingBuf.deleteData(packetLen); } } else { TaskFactory::delayTask(50); } if (countdown.hasTimedOut()) { return result::NO_REPLY_TIMEOUT; } } return returnvalue::OK; // result = handleAck(); // if (result != returnvalue::OK) { // return result; // } // // bool exeAlreadyHandled = false; // uint32_t timeout = std::max(CRC_EXECUTION_TIMEOUT, supv::timeout::UPDATE_STATUS_REPORT); // result = handleTmReception(ccsds::HEADER_LEN, tmBuf.data(), timeout); // ploc::SpTmReader spReader(tmBuf.data(), tmBuf.size()); // if (spReader.getApid() == supv::APID_EXE_FAILURE) { // exeAlreadyHandled = true; // result = handleRemainingExeReport(spReader); // } else if (spReader.getApid() == supv::APID_UPDATE_STATUS_REPORT) { // size_t remBytes = spReader.getPacketDataLen() + 1; // result = handleTmReception(remBytes, tmBuf.data() + ccsds::HEADER_LEN, // supv::timeout::UPDATE_STATUS_REPORT); // if (result != returnvalue::OK) { // sif::warning // << "PlocSupvHelper::handleCheckMemoryCommand: Failed to receive update status report" // << std::endl; // return result; // } // result = updateStatusReport.checkCrc(); // if (result != returnvalue::OK) { // sif::warning << "PlocSupvHelper::handleCheckMemoryCommand: CRC check failed" << std::endl; // return result; // } // // Copy into other buffer because data will be overwritten when reading execution report // std::memcpy(statusReportBuf.data(), tmBuf.data(), updateStatusReport.getNominalSize()); // } // // if (not exeAlreadyHandled) { // result = handleExe(CRC_EXECUTION_TIMEOUT); // if (result != returnvalue::OK) { // return result; // } // } // // // Now process the status report // updateStatusReport.setData(statusReportBuf.data(), statusReportBuf.size()); // result = updateStatusReport.parseDataField(); // if (result != returnvalue::OK) { // return result; // } // if (update.crcShouldBeChecked) { // result = updateStatusReport.verifycrc(update.crc); // if (result != returnvalue::OK) { // sif::warning << "PlocSupvHelper::handleCheckMemoryCommand: CRC failure. Expected CRC 0x" // << std::setfill('0') << std::hex << std::setw(4) // << static_cast(update.crc) << " but received CRC 0x" << // std::setw(4) // << updateStatusReport.getCrc() << std::dec << std::endl; // return result; // } // } return result; } uint32_t PlocSupvHelper::getFileSize(std::string filename) { std::ifstream file(filename, std::ifstream::binary); file.seekg(0, file.end); uint32_t size = file.tellg(); file.close(); return size; } ReturnValue_t PlocSupvHelper::handleEventBufferReception(ploc::SpTmReader& reader) { ReturnValue_t result = returnvalue::OK; // TODO: Fix //#ifdef XIPHOS_Q7S // if (not sdcMan->getActiveSdCard()) { // return HasFileSystemIF::FILESYSTEM_INACTIVE; // } //#endif // std::string filename = Filenaming::generateAbsoluteFilename( // eventBufferReq.path, eventBufferReq.filename, timestamping); // std::ofstream file(filename, std::ios_base::app | std::ios_base::out); // uint32_t packetsRead = 0; // size_t requestLen = 0; // bool firstPacket = true; // for (packetsRead = 0; packetsRead < NUM_EVENT_BUFFER_PACKETS; packetsRead++) { // if (terminate) { // triggerEvent(SUPV_EVENT_BUFFER_REQUEST_TERMINATED, packetsRead - 1); // file.close(); // return PROCESS_TERMINATED; // } // if (packetsRead == NUM_EVENT_BUFFER_PACKETS - 1) { // requestLen = SIZE_EVENT_BUFFER_LAST_PACKET; // } else { // requestLen = SIZE_EVENT_BUFFER_FULL_PACKET; // } // if (firstPacket) { // firstPacket = false; // requestLen -= 6; // } // result = handleTmReception(requestLen); // if (result != returnvalue::OK) { // sif::debug << "PlocSupvHelper::handleEventBufferReception: Failed while trying to read // packet" // << " " << packetsRead + 1 << std::endl; // file.close(); // return result; // } // ReturnValue_t result = reader.checkCrc(); // if (result != returnvalue::OK) { // triggerEvent(SUPV_REPLY_CRC_MISSMATCH, rememberApid); // return result; // } // uint16_t apid = reader.getApid(); // if (apid != supv::APID_MRAM_DUMP_TM) { // sif::warning << "PlocSupvHelper::handleEventBufferReception: Did not expect space packet " // << "with APID 0x" << std::hex << apid << std::endl; // file.close(); // return EVENT_BUFFER_REPLY_INVALID_APID; // } // // TODO: Fix // // file.write(reinterpret_cast(reader.getPacketData()), // // reader.getPayloadDataLength()); // } return result; } void PlocSupvHelper::resetSpParams() { spParams.buf = cmdBuf.data(); } ReturnValue_t PlocSupvHelper::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { if (sendData == nullptr or sendLen == 0) { return FAILED; } lock->lockMutex(); if (state == InternalState::SLEEPING or state == InternalState::LONGER_REQUEST) { lock->unlockMutex(); return FAILED; } lock->unlockMutex(); return encodeAndSendPacket(sendData, sendLen); } ReturnValue_t PlocSupvHelper::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; } ReturnValue_t PlocSupvHelper::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { return returnvalue::OK; } ReturnValue_t PlocSupvHelper::handleRunningLongerRequest() { ReturnValue_t result = OK; switch (request) { case Request::UPDATE: { result = executeUpdate(); if (result == returnvalue::OK) { triggerEvent(SUPV_UPDATE_SUCCESSFUL, result); } else if (result == PROCESS_TERMINATED) { // Event already triggered } else { triggerEvent(SUPV_UPDATE_FAILED, result); } break; } case Request::CHECK_MEMORY: { executeFullCheckMemoryCommand(); break; } case Request::CONTINUE_UPDATE: { result = continueUpdate(); if (result == returnvalue::OK) { triggerEvent(SUPV_CONTINUE_UPDATE_SUCCESSFUL, result); } else if (result == PROCESS_TERMINATED) { // Event already triggered } else { triggerEvent(SUPV_CONTINUE_UPDATE_FAILED, result); } break; } case Request::REQUEST_EVENT_BUFFER: { // result = performEventBufferRequest(); // if (result == returnvalue::OK) { // triggerEvent(SUPV_EVENT_BUFFER_REQUEST_SUCCESSFUL, result); // } else if (result == PROCESS_TERMINATED) { // // Event already triggered // break; // } else { // triggerEvent(SUPV_EVENT_BUFFER_REQUEST_FAILED, result); // } break; } case Request::DEFAULT: { break; } } return false; } ReturnValue_t PlocSupvHelper::encodeAndSendPacket(const uint8_t* sendData, size_t sendLen) { size_t encodedLen = 0; hdlc_add_framing(sendData, sendLen, encodedSendBuf.data(), &encodedLen); size_t bytesWritten = write(serialPort, encodedSendBuf.data(), encodedLen); if (bytesWritten != encodedLen) { sif::warning << "ScexUartReader::sendMessage: Sending ping command to solar experiment failed" << std::endl; return FAILED; } return returnvalue::OK; } ReturnValue_t PlocSupvHelper::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { MutexGuard mg(ipcLock); if (ipcQueue.empty()) { *size = 0; return OK; } ipcQueue.retrieve(size); *buffer = ipcBuffer.data(); ReturnValue_t result = ipcRingBuf.readData(ipcBuffer.data(), *size, true); if (result != OK) { sif::warning << "PlocSupvHelper::readReceivedMessage: Reading RingBuffer failed" << std::endl; } return OK; } ReturnValue_t PlocSupvHelper::tryHdlcParsing() { size_t bytesRead = 0; ReturnValue_t result = parseRecRingBufForHdlc(bytesRead); if (result == returnvalue::OK) { // Packet found, advance read pointer. if (state == InternalState::LONGER_REQUEST) { decodedRingBuf.writeData(decodedBuf.data(), bytesRead); decodedQueue.insert(bytesRead); } else { MutexGuard mg(ipcLock); ipcRingBuf.writeData(decodedBuf.data(), bytesRead); ipcQueue.insert(bytesRead); } recRingBuf.deleteData(bytesRead); } else if (result != NO_PACKET_FOUND) { sif::warning << "ScexUartReader::performOperation: Possible packet loss" << std::endl; // Markers found at wrong place // which might be a hint for a possibly lost packet. // Maybe trigger event? recRingBuf.deleteData(bytesRead); } return result; } ReturnValue_t PlocSupvHelper::parseRecRingBufForHdlc(size_t& readSize) { size_t availableData = recRingBuf.getAvailableReadData(); if (availableData == 0) { return NO_PACKET_FOUND; } if (availableData > encodedBuf.size()) { return DECODE_BUF_TOO_SMALL; } ReturnValue_t result = recRingBuf.readData(encodedBuf.data(), availableData); if (result != returnvalue::OK) { return result; } bool startMarkerFound = false; size_t startIdx = 0; return returnvalue::OK; for (size_t idx = 0; idx < availableData; idx++) { // handle start marker if (encodedBuf[idx] == HDLC_START_MARKER) { if (not startMarkerFound) { startMarkerFound = true; startIdx = idx; } else { readSize = idx; return POSSIBLE_PACKET_LOSS_CONSECUTIVE_START; } } if (encodedBuf[idx] == HDLC_END_MARKER) { if (startMarkerFound) { // Probably a packet, so decode it size_t decodedLen = 0; hdlc_remove_framing(encodedBuf.data() + startIdx, idx + 1, decodedBuf.data(), &decodedLen); readSize = decodedLen; return returnvalue::OK; } else { readSize = ++idx; return POSSIBLE_PACKET_LOSS_CONSECUTIVE_END; } } } return NO_PACKET_FOUND; } void PlocSupvHelper::pushIpcData(const uint8_t* data, size_t len) { MutexGuard mg(ipcLock); ipcRingBuf.writeData(data, len); ipcQueue.insert(len); } uint32_t PlocSupvHelper::buildApidServiceParam1(uint8_t apid, uint8_t serviceId) { return (apid << 8) | serviceId; } void PlocSupvHelper::performUartShutdown() { tcflush(serialPort, TCIOFLUSH); // Clear ring buffers recRingBuf.clear(); decodedRingBuf.clear(); while (not decodedQueue.empty()) { decodedQueue.pop(); } MutexGuard mg(ipcLock); ipcRingBuf.clear(); while (not ipcQueue.empty()) { ipcQueue.pop(); } state = InternalState::GO_TO_SLEEP; }