// ---------------------------------------------------------------------- // // ComLogger.cpp // // ---------------------------------------------------------------------- #include #include #include #include #include #include namespace Svc { static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), "U16 must fit in the positive range of FwSizeType"); // ---------------------------------------------------------------------- // Construction, initialization, and destruction // ---------------------------------------------------------------------- ComLogger ::ComLogger(const char* compName, const char* incomingFilePrefix, U32 maxFileSize, bool storeBufferLength) : ComLoggerComponentBase(compName), m_maxFileSize(maxFileSize), m_fileMode(CLOSED), m_byteCount(0), m_writeErrorOccurred(false), m_openErrorOccurred(false), m_storeBufferLength(storeBufferLength), m_initialized(true) { this->init_log_file(incomingFilePrefix, maxFileSize, storeBufferLength); } ComLogger ::ComLogger(const char* compName) : ComLoggerComponentBase(compName), m_filePrefix(), m_maxFileSize(0), m_fileMode(CLOSED), m_fileName(), m_hashFileName(), m_byteCount(0), m_writeErrorOccurred(false), m_openErrorOccurred(false), m_storeBufferLength(), m_initialized(false) {} void ComLogger ::init_log_file(const char* incomingFilePrefix, U32 maxFileSize, bool storeBufferLength) { FW_ASSERT(incomingFilePrefix != nullptr); this->m_maxFileSize = maxFileSize; this->m_storeBufferLength = storeBufferLength; if (this->m_storeBufferLength) { FW_ASSERT(maxFileSize > sizeof(U16), static_cast(maxFileSize)); } // Assign the prefix checking if it is too big Fw::FormatStatus formatStatus = this->m_filePrefix.format("%s", incomingFilePrefix); FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS); this->m_initialized = true; } ComLogger ::~ComLogger() { // Close file: // this->closeFile(); // NOTE: the above did not work because we don't want to issue an event // in the destructor. This can cause "virtual method called" segmentation // faults. // So I am copying part of that function here. if (OPEN == this->m_fileMode) { // Close file: this->m_file.close(); // Write out the hash file to disk: this->writeHashFile(); // Update mode: this->m_fileMode = CLOSED; // Send event: // Fw::LogStringArg logStringArg((char*) fileName); // this->log_DIAGNOSTIC_FileClosed(logStringArg); } } // ---------------------------------------------------------------------- // Handler implementations // ---------------------------------------------------------------------- void ComLogger ::comIn_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) { FW_ASSERT(portNum == 0); // Get length of buffer: FwSizeType sizeNative = data.getSize(); // ComLogger only writes 16-bit sizes to save space // on disk: FW_ASSERT(sizeNative < 65536, static_cast(sizeNative)); U16 size = sizeNative & 0xFFFF; // Close the file if it will be too big: if (OPEN == this->m_fileMode) { U32 projectedByteCount = this->m_byteCount + size; if (this->m_storeBufferLength) { projectedByteCount += static_cast(sizeof(size)); } if (projectedByteCount > this->m_maxFileSize) { this->closeFile(); } } // Open the file if it there is not one open: if (CLOSED == this->m_fileMode) { this->openFile(); } // Write to the file if it is open: if (OPEN == this->m_fileMode) { this->writeComBufferToFile(data, size); } } void ComLogger ::CloseFile_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { this->closeFile(); this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } void ComLogger ::pingIn_handler(const FwIndexType portNum, U32 key) { // return key this->pingOut_out(0, key); } void ComLogger ::openFile() { FW_ASSERT(CLOSED == this->m_fileMode); if (!this->m_initialized) { this->log_WARNING_LO_FileNotInitialized(); return; } // Create filename: Fw::Time timestamp = getTime(); Fw::FormatStatus formatStatus = this->m_fileName.format( "%s_%" PRI_FwTimeBaseStoreType "_%" PRIu32 "_%06" PRIu32 ".com", this->m_filePrefix.toChar(), static_cast(timestamp.getTimeBase()), timestamp.getSeconds(), timestamp.getUSeconds()); FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS); this->m_hashFileName.format("%s%s", this->m_fileName.toChar(), Utils::Hash::getFileExtensionString()); FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS); Os::File::Status ret = m_file.open(this->m_fileName.toChar(), Os::File::OPEN_WRITE); if (Os::File::OP_OK != ret) { if (!this->m_openErrorOccurred) { // throttle this event, otherwise a positive // feedback event loop can occur! this->log_WARNING_HI_FileOpenError(ret, this->m_fileName); } this->m_openErrorOccurred = true; } else { // Reset event throttle: this->m_openErrorOccurred = false; // Reset byte count: this->m_byteCount = 0; // Set mode: this->m_fileMode = OPEN; } } void ComLogger ::closeFile() { if (OPEN == this->m_fileMode) { // Close file: this->m_file.close(); // Write out the hash file to disk: this->writeHashFile(); // Update mode: this->m_fileMode = CLOSED; // Send event: this->log_DIAGNOSTIC_FileClosed(this->m_fileName); } } void ComLogger ::writeComBufferToFile(Fw::ComBuffer& data, U16 size) { if (this->m_storeBufferLength) { U8 buffer[sizeof(size)]; Fw::SerialBuffer serialLength(&buffer[0], sizeof(size)); serialLength.serializeFrom(size); if (this->writeToFile(serialLength.getBuffAddr(), static_cast(serialLength.getSize()))) { this->m_byteCount += static_cast(serialLength.getSize()); } else { return; } } // Write buffer to file: if (this->writeToFile(data.getBuffAddr(), size)) { this->m_byteCount += size; } } bool ComLogger ::writeToFile(void* data, U16 length) { FwSizeType size = length; Os::File::Status ret = m_file.write(reinterpret_cast(data), size); if ((Os::File::OP_OK != ret) || (size != length)) { if (!this->m_writeErrorOccurred) { // throttle this event, otherwise a positive // feedback event loop can occur! this->log_WARNING_HI_FileWriteError(ret, static_cast(size), length, this->m_fileName); } this->m_writeErrorOccurred = true; return false; } this->m_writeErrorOccurred = false; return true; } void ComLogger ::writeHashFile() { Os::ValidateFile::Status validateStatus; validateStatus = Os::ValidateFile::createValidation(this->m_fileName.toChar(), this->m_hashFileName.toChar()); if (Os::ValidateFile::VALIDATION_OK != validateStatus) { this->log_WARNING_LO_FileValidationError(this->m_fileName, this->m_hashFileName, validateStatus); } } } // namespace Svc