diff --git a/.vscode/settings.json b/.vscode/settings.json index c8690e93f..c4226c10f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,7 +20,8 @@ "xmemory0": "cpp", "initializer_list": "cpp", "queue": "cpp", - "xlocnum": "cpp" + "xlocnum": "cpp", + "istream": "cpp" }, "C_Cpp.errorSquiggles": "Disabled", "cmake.configureOnOpen": true diff --git a/src/eez/dlog_file.cpp b/src/eez/dlog_file.cpp index 3d1a6b74e..31030fcd5 100644 --- a/src/eez/dlog_file.cpp +++ b/src/eez/dlog_file.cpp @@ -79,6 +79,9 @@ void Writer::writeFileHeaderAndMetaFields(const Parameters ¶meters) { writeUint32Field(FIELD_ID_DATA_SIZE, 0); m_dataSizeFieldOffset = m_bufferIndex - 4; + writeUint32Field(FIELD_ID_BOOKMARKS_SIZE, 0); + m_bookmarksSizeFieldOffset = m_bufferIndex - 4; + writeUint8Field(FIELD_ID_X_UNIT, parameters.xAxis.unit); writeFloatField(FIELD_ID_X_STEP, parameters.xAxis.step); writeUint8Field(FIELD_ID_X_SCALE_TYPE, parameters.xAxis.scaleType); @@ -421,6 +424,9 @@ bool Reader::readRemainingFileHeaderAndMetaFields(Parameters ¶meters) { else if (fieldId == FIELD_ID_DATA_SIZE) { parameters.dataSize = readUint32(); } + else if (fieldId == FIELD_ID_BOOKMARKS_SIZE) { + parameters.bookmarksSize = readUint32(); + } else if (fieldId == FIELD_ID_X_UNIT) { parameters.xAxis.unit = (Unit)readUint8(); } @@ -528,57 +534,6 @@ bool Reader::readRemainingFileHeaderAndMetaFields(Parameters ¶meters) { return !invalidHeader; } -Bookmarks *Reader::readBookmarks(size_t srcBufferSize, uint8_t *destBuffer, size_t destBufferSize) { - auto bookmarks = (Bookmarks *)destBuffer; - auto bookmark = bookmarks->list; - uint8_t *texts = destBuffer + destBufferSize; - - bookmarks->count = 0; - - m_offset = 0; - while (m_offset < srcBufferSize) { - uint16_t fieldLength = readUint16(); - if (fieldLength == 0) { - break; - } - - if (m_offset - sizeof(uint16_t) + fieldLength > srcBufferSize) { - break; - } - - uint8_t fieldId = readUint8(); - - uint16_t fieldDataLength = fieldLength - sizeof(uint16_t) - sizeof(uint8_t); - - if (fieldId == FIELD_ID_BOOKMARK) { - texts -= fieldDataLength + 1; - if ((uint8_t *)(bookmark + 1) > texts) { - // TODO report that there is not enough room to store all the bookmarks - break; - } - - bookmark->position = readUint32(); - bookmark->text = (const char *)texts; - - fieldDataLength -= 4; - - auto p = texts; - for (int i = 0; i < fieldDataLength; i++) { - *p++ = readUint8(); - } - *p = 0; - - bookmarks->count++; - bookmark++; - } else { - // unknown field, skip - m_offset += fieldDataLength; - } - } - - return bookmarks; -} - uint8_t Reader::readUint8() { uint32_t i = m_offset; m_offset += 1; diff --git a/src/eez/dlog_file.h b/src/eez/dlog_file.h index c2ed20f69..513236237 100644 --- a/src/eez/dlog_file.h +++ b/src/eez/dlog_file.h @@ -75,7 +75,9 @@ enum Fields { // Default is 0. FIELD_ID_DATA_CONTAINS_SAMPLE_VALIDITY_BIT = 4, - FIELD_ID_DATA_SIZE = 5, // number of data rows + FIELD_ID_DATA_SIZE = 5, // no. of data rows + + FIELD_ID_BOOKMARKS_SIZE = 6, // no. of bookmarks FIELD_ID_X_UNIT = 10, FIELD_ID_X_STEP = 11, @@ -97,8 +99,6 @@ enum Fields { FIELD_ID_CHANNEL_MODULE_TYPE = 50, FIELD_ID_CHANNEL_MODULE_REVISION = 51, - - FIELD_ID_BOOKMARK = 62, }; enum DataType { @@ -127,6 +127,9 @@ enum DataType { DATA_TYPE_DOUBLE_BE, }; +static const size_t MAX_BOOKMARK_TEXT_LEN = 25; +static const size_t NUM_BYTES_PER_BOOKMARK_IN_INDEX = 8; + struct Range { float min; float max; @@ -184,20 +187,11 @@ struct Parameters { float duration; uint32_t dataSize; + uint32_t bookmarksSize; void initYAxis(int yAxisIndex); }; -struct Bookmark { - uint32_t position; - const char *text; -}; - -struct Bookmarks { - uint32_t count; - Bookmark list[]; -}; - struct Writer { public: Writer(uint8_t *buffer, uint32_t bufferSize); @@ -224,6 +218,7 @@ struct Writer { uint32_t getDataOffset() { return m_dataOffset; } uint32_t getFinishTimeFieldOffset() { return m_finalDurationFieldOffset; } uint32_t getDataSizeFieldOffset() { return m_dataSizeFieldOffset; }; + uint32_t getBookmarksSizeFieldOffset() { return m_bookmarksSizeFieldOffset; }; uint32_t getFileLength() { return m_fileLength; } uint32_t getBitMask() { return m_bitMask; } @@ -237,6 +232,7 @@ struct Writer { uint32_t m_dataOffset = 0; uint32_t m_finalDurationFieldOffset = 0; uint32_t m_dataSizeFieldOffset = 0; + uint32_t m_bookmarksSizeFieldOffset = 0; void writeUint8Field(uint8_t id, uint8_t value); void writeUint8FieldWithIndex(uint8_t id, uint8_t value, uint8_t index); @@ -257,7 +253,6 @@ struct Reader { bool readFileHeaderAndMetaFields(Parameters ¶meters, uint32_t &headerRemaining); bool readRemainingFileHeaderAndMetaFields(Parameters ¶meters); // call this only in case of VERSION2 - Bookmarks *readBookmarks(size_t srcBufferSize, uint8_t *destBuffer, size_t destBufferSize); uint16_t getVersion() { return m_version; } uint32_t getDataOffset() { return m_dataOffset; } diff --git a/src/eez/gui/data.cpp b/src/eez/gui/data.cpp index 96843be14..13337d51f 100644 --- a/src/eez/gui/data.cpp +++ b/src/eez/gui/data.cpp @@ -851,23 +851,13 @@ float ytDataGetPeriod(Cursor cursor, int16_t id) { return value.getFloat(); } -void ytDataGetVisibleBookmarks(Cursor cursor, int16_t id, BookmarksSlice &bookmarksSlice) { - bookmarksSlice.start = 0; - bookmarksSlice.end = 0; - Value value(&bookmarksSlice, VALUE_TYPE_POINTER); - DATA_OPERATION_FUNCTION(id, DATA_OPERATION_YT_DATA_GET_VISIBLE_BOOKMARKS, cursor, value); -} - -uint32_t ytDataGetBookmarkPosition(Cursor cursor, int16_t id, uint32_t bookmarkIndex) { - Value value(bookmarkIndex, VALUE_TYPE_UINT32); - DATA_OPERATION_FUNCTION(id, DATA_OPERATION_YT_DATA_GET_BOOKMARK_POSITION, cursor, value); - return value.getUInt32(); -} - -uint32_t ytDataGetSelectedBookmarkIndex(Cursor cursor, int16_t id) { +uint8_t *ytDataGetBookmarks(Cursor cursor, int16_t id) { Value value; - DATA_OPERATION_FUNCTION(id, DATA_OPERATION_YT_DATA_GET_SELECTED_BOOKMARK_INDEX, cursor, value); - return value.getUInt32(); + DATA_OPERATION_FUNCTION(id, DATA_OPERATION_YT_DATA_GET_BOOKMARKS, cursor, value); + if (value.getType() == VALUE_TYPE_POINTER) { + return (uint8_t *)value.getVoidPointer(); + } + return nullptr; } bool ytDataIsCursorVisible(Cursor cursor, int16_t id) { diff --git a/src/eez/gui/data.h b/src/eez/gui/data.h index b7f1a2531..70d7fc537 100644 --- a/src/eez/gui/data.h +++ b/src/eez/gui/data.h @@ -391,9 +391,7 @@ enum DataOperationEnum { DATA_OPERATION_YT_DATA_GET_GET_VALUE_FUNC, DATA_OPERATION_YT_DATA_GET_GRAPH_UPDATE_METHOD, DATA_OPERATION_YT_DATA_GET_PERIOD, - DATA_OPERATION_YT_DATA_GET_VISIBLE_BOOKMARKS, - DATA_OPERATION_YT_DATA_GET_BOOKMARK_POSITION, - DATA_OPERATION_YT_DATA_GET_SELECTED_BOOKMARK_INDEX, + DATA_OPERATION_YT_DATA_GET_BOOKMARKS, DATA_OPERATION_YT_DATA_IS_CURSOR_VISIBLE, DATA_OPERATION_YT_DATA_GET_CURSOR_OFFSET, DATA_OPERATION_YT_DATA_GET_CURSOR_X_VALUE, @@ -470,13 +468,7 @@ void ytDataGetLabel(Cursor cursor, int16_t id, uint8_t valueIndex, char *text, i Value::YtDataGetValueFunctionPointer ytDataGetGetValueFunc(Cursor cursor, int16_t id); uint8_t ytDataGetGraphUpdateMethod(Cursor cursor, int16_t id); float ytDataGetPeriod(Cursor cursor, int16_t id); -struct BookmarksSlice { - uint32_t start; - uint32_t end; -}; -void ytDataGetVisibleBookmarks(Cursor cursor, int16_t id, BookmarksSlice &bookmarks); -uint32_t ytDataGetBookmarkPosition(Cursor cursor, int16_t id, uint32_t bookmarkIndex); -uint32_t ytDataGetSelectedBookmarkIndex(Cursor cursor, int16_t id); +uint8_t *ytDataGetBookmarks(Cursor cursor, int16_t id); bool ytDataIsCursorVisible(Cursor cursor, int16_t id); uint32_t ytDataGetCursorOffset(Cursor cursor, int16_t id); Value ytDataGetCursorXValue(Cursor cursor, int16_t id); diff --git a/src/eez/gui/widgets/yt_graph.cpp b/src/eez/gui/widgets/yt_graph.cpp index 1a89838bd..aa5e23af4 100644 --- a/src/eez/gui/widgets/yt_graph.cpp +++ b/src/eez/gui/widgets/yt_graph.cpp @@ -41,12 +41,12 @@ struct YTGraphWidgetState { uint32_t historyValuePosition; uint8_t ytGraphUpdateMethod; uint32_t cursorPosition; + uint8_t *bookmarks; bool showLabels; int8_t selectedValueIndex; bool valueIsVisible[MAX_NUM_OF_Y_VALUES]; float valueDiv[MAX_NUM_OF_Y_VALUES]; float valueOffset[MAX_NUM_OF_Y_VALUES]; - uint32_t selectedBookmarkIndex; }; FixPointersFunctionType YT_GRAPH_fixPointers = nullptr; @@ -246,6 +246,7 @@ struct YTGraphStaticDrawHelper { int yMax; uint32_t cursorPosition; + uint8_t *bookmarks; Value::YtDataGetValueFunctionPointer ytDataGetValue; @@ -440,17 +441,17 @@ struct YTGraphStaticDrawHelper { } // draw bookmarks - BookmarksSlice bookmarksSlice; - ytDataGetVisibleBookmarks(widgetCursor.cursor, widgetCursor.widget->data, bookmarksSlice); - uint32_t selectedBookmarkIndex = ytDataGetSelectedBookmarkIndex(widgetCursor.cursor, widgetCursor.widget->data); - for (uint32_t bookmarkIndex = bookmarksSlice.start; bookmarkIndex < bookmarksSlice.end; bookmarkIndex++) { - if (bookmarkIndex == selectedBookmarkIndex) { - display::setColor(COLOR_ID_SELECTED_BOOKMARK); - } else { - display::setColor(COLOR_ID_BOOKMARK); + if (bookmarks) { + for (int x = 0; x < widget->w; x++) { + if (bookmarks[x]) { + if (bookmarks[x] == 2) { + display::setColor(COLOR_ID_SELECTED_BOOKMARK); + } else { + display::setColor(COLOR_ID_BOOKMARK); + } + display::drawVLine(startX + x, widgetCursor.y, widget->h - 1); + } } - uint32_t bookmarkPosition = ytDataGetBookmarkPosition(widgetCursor.cursor, widgetCursor.widget->data, bookmarkIndex); - display::drawVLine(startX + bookmarkPosition - currentHistoryValuePosition, widgetCursor.y, widget->h - 1); } // draw cursor @@ -513,7 +514,7 @@ DrawFunctionType YT_GRAPH_draw = [](const WidgetCursor &widgetCursor) { currentState->historyValuePosition = ytDataGetPosition(widgetCursor.cursor, widget->data); currentState->ytGraphUpdateMethod = ytDataGetGraphUpdateMethod(widgetCursor.cursor, widget->data); currentState->cursorPosition = currentState->historyValuePosition + ytDataGetCursorOffset(widgetCursor.cursor, widget->data); - currentState->selectedBookmarkIndex = ytDataGetSelectedBookmarkIndex(widgetCursor.cursor, widget->data); + currentState->bookmarks = ytDataGetBookmarks(widgetCursor.cursor, widget->data); bool visibleValuesChanged = false; @@ -552,12 +553,18 @@ DrawFunctionType YT_GRAPH_draw = [](const WidgetCursor &widgetCursor) { currentState->selectedValueIndex != previousState->selectedValueIndex || visibleValuesChanged || previousHistoryValuePosition != currentState->historyValuePosition || - (!previousState || previousState->numHistoryValues != currentState->numHistoryValues || previousState->cursorPosition != currentState->cursorPosition || previousState->selectedBookmarkIndex != currentState->selectedBookmarkIndex) + ( + !previousState || + previousState->numHistoryValues != currentState->numHistoryValues || + previousState->cursorPosition != currentState->cursorPosition || + previousState->bookmarks != currentState->bookmarks + ) ) { if (currentState->ytGraphUpdateMethod == YT_GRAPH_UPDATE_METHOD_STATIC) { YTGraphStaticDrawHelper drawHelper(widgetCursor); drawHelper.cursorPosition = currentState->cursorPosition; + drawHelper.bookmarks = currentState->bookmarks; drawHelper.drawStatic(previousHistoryValuePosition, currentState->historyValuePosition, currentState->numHistoryValues, graphWidth, currentState->showLabels, currentState->selectedValueIndex); } else { const Style* style = getStyle(widget->style); diff --git a/src/eez/modules/psu/dlog_record.cpp b/src/eez/modules/psu/dlog_record.cpp index 19e46db49..2b0132adf 100644 --- a/src/eez/modules/psu/dlog_record.cpp +++ b/src/eez/modules/psu/dlog_record.cpp @@ -117,7 +117,8 @@ static float getValue(uint32_t rowIndex, uint8_t columnIndex, float *max) { static File g_file; static bool g_bookmarksFileOpened; -static File g_bookmarksFile; +static File g_bookmarksIndexFile; +static File g_bookmarksTextFile; static int fileOpen() { if (!g_file.open(g_recordingParameters.filePath, FILE_CREATE_ALWAYS | FILE_WRITE)) { @@ -219,6 +220,7 @@ void fileWrite(bool flush) { // } if (!err) { + g_file.sync(); g_lastSavedBufferIndex += bufferSize; g_lastSavedBufferTickCount = millis(); } @@ -506,7 +508,17 @@ static int doInitiate(bool traceInitiated) { } static void resetAllParameters() { - memset(&g_recordingParameters, 0, sizeof(g_recordingParameters)); + #if defined(EEZ_PLATFORM_STM32) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wclass-memaccess" + #endif + + memset(&g_recordingParameters, 0, sizeof(g_recordingParameters)); + + #if defined(EEZ_PLATFORM_STM32) + #pragma GCC diagnostic pop + #endif + g_recordingParameters.yAxis.unit = UNIT_UNKNOWN; g_recordingParameters.yAxis.dataType = dlog_file::DATA_TYPE_FLOAT; g_recordingParameters.yAxis.transformOffset = 0; @@ -920,63 +932,131 @@ void logBookmark(const char *text, size_t textLen) { char filePath[MAX_PATH_LENGTH]; stringCopy(filePath, MAX_PATH_LENGTH, g_recordingParameters.filePath); - stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bookmarks"); - if (!g_bookmarksFile.open(filePath, FILE_CREATE_ALWAYS | FILE_WRITE)) { + stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bi"); + if (!g_bookmarksIndexFile.open(filePath, FILE_CREATE_ALWAYS | FILE_WRITE)) { // TODO report error return; } + stringCopy(filePath, MAX_PATH_LENGTH, g_recordingParameters.filePath); + stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bt"); + if (!g_bookmarksTextFile.open(filePath, FILE_CREATE_ALWAYS | FILE_WRITE)) { + // TODO report error + return; + } + g_bookmarksFileOpened = true; } - uint8_t buffer[7]; + uint8_t buffer[8]; - uint16_t length = (uint16_t)(sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint32_t) + textLen); - auto p = (uint8_t *)&length; - buffer[0] = p[0]; - buffer[1] = p[1]; - - buffer[2] = dlog_file::FIELD_ID_BOOKMARK; + ((uint32_t *)buffer)[0] = g_activeRecording.size; + ((uint32_t *)buffer)[1] = g_bookmarksTextFile.size(); - p = (uint8_t *)&g_activeRecording.size; - buffer[3] = p[0]; - buffer[4] = p[1]; - buffer[5] = p[2]; - buffer[6] = p[3]; + if (g_bookmarksIndexFile.write(buffer, sizeof(buffer)) != sizeof(buffer)) { + // TODO report error + } - g_bookmarksFile.write(buffer, sizeof(buffer)); + g_bookmarksIndexFile.sync(); - g_bookmarksFile.write(text, textLen); + if (textLen > dlog_file::MAX_BOOKMARK_TEXT_LEN) { + textLen = dlog_file::MAX_BOOKMARK_TEXT_LEN; + } + + if (g_bookmarksTextFile.write(text, textLen) != textLen) { + // TODO report error + } - g_bookmarksFile.sync(); + g_bookmarksTextFile.sync(); } } static void finalizeBookmarks() { - // append bookmarks file to the end of dlog file - g_bookmarksFile.close(); - char filePath[MAX_PATH_LENGTH]; + uint32_t fileSize; + + // + // append bookmarks index file to the end of dlog file + // + g_bookmarksIndexFile.close(); stringCopy(filePath, MAX_PATH_LENGTH, g_recordingParameters.filePath); - stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bookmarks"); + stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bi"); - if (!g_bookmarksFile.open(filePath, FILE_OPEN_EXISTING | FILE_READ)) { + if (!g_bookmarksIndexFile.open(filePath, FILE_OPEN_EXISTING | FILE_READ)) { // TODO report error return; } - uint32_t fileSize = g_bookmarksFile.size(); + fileSize = g_bookmarksIndexFile.size(); - g_file.seek(g_writer.getDataOffset() + g_activeRecording.size * g_activeRecording.numBytesPerRow); + if (!g_file.seek(g_writer.getBookmarksSizeFieldOffset())) { + // TODO report error + return; + } + uint32_t bookmarksSize = fileSize / dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX; + if (g_file.write(&bookmarksSize, sizeof(bookmarksSize)) != sizeof(bookmarksSize)) { + // TODO report error + return; + } + + if (!g_file.seek(g_writer.getDataOffset() + g_activeRecording.size * g_activeRecording.numBytesPerRow)) { + // TODO report error + return; + } for (uint32_t i = 0; i < fileSize; i += CHUNK_SIZE) { uint32_t n = MIN(CHUNK_SIZE, fileSize - i); - g_bookmarksFile.read(DLOG_RECORD_BUFFER, n); - g_file.write(DLOG_RECORD_BUFFER, n); + if (g_bookmarksIndexFile.read(DLOG_RECORD_BUFFER, n) != n) { + // TODO report error + return; + } + if (g_file.write(DLOG_RECORD_BUFFER, n) != n) { + // TODO report error + return; + } + } + + uint8_t buffer[8]; + + ((uint32_t *)buffer)[0] = 0; + ((uint32_t *)buffer)[1] = g_bookmarksTextFile.size(); + + if (g_file.write(buffer, sizeof(buffer)) != sizeof(buffer)) { + // TODO report error + } + + g_bookmarksIndexFile.close(); + sd_card::deleteFile(filePath, nullptr); + + // + // append bookmarks text file to the end of dlog file + // + g_bookmarksTextFile.close(); + + stringCopy(filePath, MAX_PATH_LENGTH, g_recordingParameters.filePath); + stringAppendString(filePath, MAX_PATH_LENGTH, ".dlog-bt"); + + if (!g_bookmarksTextFile.open(filePath, FILE_OPEN_EXISTING | FILE_READ)) { + // TODO report error + return; + } + + fileSize = g_bookmarksTextFile.size(); + + for (uint32_t i = 0; i < fileSize; i += CHUNK_SIZE) { + uint32_t n = MIN(CHUNK_SIZE, fileSize - i); + if (g_bookmarksTextFile.read(DLOG_RECORD_BUFFER, n) != n) { + // TODO report error + return; + } + if (g_file.write(DLOG_RECORD_BUFFER, n) != n) { + // TODO report error + return; + } } - g_bookmarksFile.close(); + g_bookmarksTextFile.close(); sd_card::deleteFile(filePath, nullptr); } diff --git a/src/eez/modules/psu/dlog_view.cpp b/src/eez/modules/psu/dlog_view.cpp index 52f81618b..68facc6e1 100644 --- a/src/eez/modules/psu/dlog_view.cpp +++ b/src/eez/modules/psu/dlog_view.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -54,6 +55,12 @@ namespace dlog_view { //////////////////////////////////////////////////////////////////////////////// +static int VIEW_WIDTH = 480; +static int VIEW_HEIGHT = 240; + +static int NUM_HORZ_DIVISIONS = 12; +static int NUM_VERT_DIVISIONS = 6; + static State g_state; static uint32_t g_loadingStartTickCount; bool g_showLatest = true; @@ -76,12 +83,22 @@ static uint8_t *g_rowDataStart = FILE_VIEW_BUFFER; static const uint32_t ROW_VALUES_BUFFER_SIZE = 65536; static uint8_t *g_bookmarksDataStart = g_rowDataStart + ROW_VALUES_BUFFER_SIZE; -static const uint32_t BOOKMARKS_DATA_SIZE = 65536; -static dlog_file::Bookmarks *g_bookmarks; +struct Bookmark { + uint32_t position; + const char *text; +}; +static Bookmark *g_bookmarks = (Bookmark *)g_bookmarksDataStart; +static const uint32_t BOOKMARKS_PER_PAGE = 8; +static const uint32_t BOOKMARKS_DATA_SIZE = 2 * (BOOKMARKS_PER_PAGE * (sizeof(Bookmark) + dlog_file::MAX_BOOKMARK_TEXT_LEN + 1)); static uint32_t g_selectedBookmarkIndex = -1; static uint32_t g_bookmarksScrollPosition = 0; +static uint32_t g_loadedBookmarksScrollPosition = 0; +uint8_t *g_visibleBookmarksBuffer = g_bookmarksDataStart + BOOKMARKS_DATA_SIZE; +uint8_t *g_visibleBookmarks; +float g_visibleBookmarksPositionStart; +float g_visibleBookmarksPositionEnd; -static CacheBlock *g_cacheBlocks = (CacheBlock *)(g_bookmarksDataStart + BOOKMARKS_DATA_SIZE); +static CacheBlock *g_cacheBlocks = (CacheBlock *)(g_visibleBookmarksBuffer + 2 * 480); static const uint32_t CACHE_BLOCKS_BUFFER_SIZE = FILE_VIEW_BUFFER_SIZE - ROW_VALUES_BUFFER_SIZE - BOOKMARKS_DATA_SIZE; static uint32_t NUM_ELEMENTS_PER_BLOCKS; @@ -101,12 +118,6 @@ static enum { TAB_BOOKMARKS } g_selectedTab = TAB_OPTIONS; -static int VIEW_WIDTH = 480; -static int VIEW_HEIGHT = 240; - -static int NUM_HORZ_DIVISIONS = 12; -static int NUM_VERT_DIVISIONS = 6; - //////////////////////////////////////////////////////////////////////////////// bool Parameters::isDlogItemAvailable(int slotIndex, int subchannelIndex, int resourceIndex) { @@ -469,6 +480,23 @@ void loadBlock() { g_refreshed = true; } +static uint32_t getPosition(Recording& recording); +static void adjustXAxisOffset(Recording &recording); +static void loadVisibleBookmarks(float positionStart, float positionEnd); + +void tick() { + if (g_bookmarksScrollPosition != g_loadedBookmarksScrollPosition) { + loadBookmarks(); + } + + auto &recording = dlog_view::getRecording(); + auto positionStart = recording.xAxisOffset / recording.parameters.xAxis.step; + auto positionEnd = positionStart + VIEW_WIDTH * recording.parameters.period / recording.parameters.xAxis.step; + if (positionStart != g_visibleBookmarksPositionStart || positionEnd != g_visibleBookmarksPositionEnd) { + loadVisibleBookmarks(positionStart, positionEnd); + } +} + void stateManagment() { auto isExecuting = dlog_record::isExecuting(); if (!isExecuting && g_wasExecuting) { @@ -546,7 +574,7 @@ static float getDuration(Recording &recording) { return recording.size > 0 ? (recording.size - 1) * recording.parameters.xAxis.step : 0; } -void adjustXAxisOffset(Recording &recording) { +static void adjustXAxisOffset(Recording &recording) { auto duration = getDuration(recording); if (recording.xAxisOffset + VIEW_WIDTH * recording.parameters.period > duration) { recording.xAxisOffset = duration - VIEW_WIDTH * recording.parameters.period; @@ -845,6 +873,182 @@ static void scaleToFit(Recording &recording) { } } +void setBookmarksScrollPosition(uint32_t scrollPosition) { + auto &recording = getRecording(); + + if (scrollPosition + BOOKMARKS_PER_PAGE > recording.parameters.bookmarksSize) { + scrollPosition = recording.parameters.bookmarksSize - BOOKMARKS_PER_PAGE; + } + + g_bookmarksScrollPosition = scrollPosition; +} + +void loadBookmarks() { + auto &recording = getRecording(); + + auto scrollPosition = g_bookmarksScrollPosition; + auto bookmarks = g_bookmarks == (Bookmark *)g_bookmarksDataStart ? (Bookmark *)(g_bookmarksDataStart + BOOKMARKS_DATA_SIZE / 2) : (Bookmark *)g_bookmarksDataStart; + + int n = MIN(recording.parameters.bookmarksSize - scrollPosition, BOOKMARKS_PER_PAGE); + + if (n <= 0) { + return; + } + + File file; + if (!file.open(g_filePath, FILE_OPEN_EXISTING | FILE_READ)) { + // TODO report error + return; + } + + uint32_t bookmarksIndexOffset = recording.dataOffset + recording.parameters.dataSize * recording.numBytesPerRow; + + if (!file.seek(bookmarksIndexOffset + scrollPosition * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX)) { + // TODO report error + file.close(); + return; + } + + uint8_t buffer[(BOOKMARKS_PER_PAGE + 1) * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX]; + if (file.read(buffer, (n + 1) * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX) != (n + 1) * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX) { + // TODO report error + file.close(); + return; + } + + auto text = (char *)(bookmarks + BOOKMARKS_PER_PAGE); + + uint32_t bookmarksTextOffset = bookmarksIndexOffset + (recording.parameters.bookmarksSize + 1) * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX; + + uint8_t textLen[BOOKMARKS_PER_PAGE]; + uint32_t previous_textOffset = 0; + for (int i = 0; i <= n; i++) { + uint32_t textOffset = ((uint32_t *)(buffer + i * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX))[1]; + if (i == 0) { + bookmarksTextOffset += textOffset; + } else { + textLen[i - 1] = textOffset - previous_textOffset; + text += textLen[i - 1] + 1; + } + + if (i < n) { + bookmarks[i].position = ((uint32_t *)(buffer + i * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX))[0]; + bookmarks[i].text = text; + } + + previous_textOffset = textOffset; + } + + if (!file.seek(bookmarksTextOffset)) { + // TODO report error + file.close(); + return; + } + + text = (char *)(bookmarks + BOOKMARKS_PER_PAGE); + + for (int i = 0; i < n; i++) { + if (file.read(text, textLen[i]) != textLen[i]) { + // TODO report error + file.close(); + return; + } + text[textLen[i]] = 0; + text += textLen[i] + 1; + } + + g_loadedBookmarksScrollPosition = scrollPosition; + g_bookmarks = bookmarks; + + file.close(); +} + +void loadVisibleBookmarks(float positionStart, float positionEnd) { + auto &recording = getRecording(); + + File file; + if (!file.open(g_filePath, FILE_OPEN_EXISTING | FILE_READ)) { + // TODO report error + return; + } + + uint32_t bookmarksIndexOffset = recording.dataOffset + recording.parameters.dataSize * recording.numBytesPerRow; + + if (!file.seek(bookmarksIndexOffset)) { + // TODO report error + file.close(); + return; + } + + uint32_t a = 0; + uint32_t b = recording.parameters.bookmarksSize - 1; + while (a < b) { + uint32_t c = (a + b) / 2; + + if (!file.seek(bookmarksIndexOffset + c * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX)) { + // TODO report error + file.close(); + return; + } + uint32_t position; + if (file.read(&position, sizeof(uint32_t)) != sizeof(uint32_t)) { + // TODO report error + file.close(); + return; + } + + if (position < positionStart) { + if (a == c) { + break; + } + a = c; + } else { + if (b == c) { + break; + } + b = c; + } + } + + uint8_t *visibleBookmarks = g_visibleBookmarks == g_visibleBookmarksBuffer ? g_visibleBookmarksBuffer + 480 : g_visibleBookmarksBuffer; + + for (int i = 0; i < VIEW_WIDTH; i++) { + visibleBookmarks[i] = 0; + } + + for (; a < recording.parameters.bookmarksSize; a++) { + if (!file.seek(bookmarksIndexOffset + a * dlog_file::NUM_BYTES_PER_BOOKMARK_IN_INDEX)) { + // TODO report error + file.close(); + return; + } + uint32_t position; + if (file.read(&position, sizeof(uint32_t)) != sizeof(uint32_t)) { + // TODO report error + file.close(); + return; + } + + if (position >= positionEnd) { + break; + } + + auto i = (int)roundf((position - positionStart) * recording.parameters.xAxis.step / recording.parameters.period); + + if (i >= 0 && i < 480) { + if (visibleBookmarks[i] != 2) { + visibleBookmarks[i] = a == g_selectedBookmarkIndex ? 2 : 1; + } + } + } + + g_visibleBookmarks = visibleBookmarks; + g_visibleBookmarksPositionStart = positionStart; + g_visibleBookmarksPositionEnd = positionEnd; + + file.close(); +} + bool openFile(const char *filePath, int *err) { g_state = STATE_LOADING; g_loadingStartTickCount = millis(); @@ -853,11 +1057,22 @@ bool openFile(const char *filePath, int *err) { stringCopy(g_filePath, sizeof(g_filePath), filePath); } +#if defined(EEZ_PLATFORM_STM32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + memset(&g_dlogFile, 0, sizeof(Recording)); - g_bookmarks = 0; +#if defined(EEZ_PLATFORM_STM32) +#pragma GCC diagnostic pop +#endif + g_selectedBookmarkIndex = -1; g_bookmarksScrollPosition = 0; + g_visibleBookmarks = 0; + g_visibleBookmarksPositionStart = 0; + g_visibleBookmarksPositionEnd = 0; if (!isLowPriorityThread()) { sendMessageToLowPriorityThread(THREAD_MESSAGE_DLOG_SHOW_FILE); @@ -865,7 +1080,7 @@ bool openFile(const char *filePath, int *err) { } File file; - if (file.open(filePath != nullptr ? filePath : g_filePath, FILE_OPEN_EXISTING | FILE_READ)) { + if (file.open(g_filePath, FILE_OPEN_EXISTING | FILE_READ)) { uint8_t *buffer = FILE_VIEW_BUFFER; uint32_t read = file.read(buffer, dlog_file::DLOG_VERSION1_HEADER_SIZE); if (read == dlog_file::DLOG_VERSION1_HEADER_SIZE) { @@ -947,21 +1162,6 @@ bool openFile(const char *filePath, int *err) { initDlogValues(g_dlogFile); calcColumnIndexes(g_dlogFile); - if (g_dlogFile.parameters.dataSize > 0) { - auto offset = g_dlogFile.dataOffset + g_dlogFile.parameters.dataSize * g_dlogFile.numBytesPerRow; - auto dataLength = file.size(); - uint32_t bookmarksLength = dataLength - offset; - if (bookmarksLength > ROW_VALUES_BUFFER_SIZE) { - // TODO report that there is not enough room to store all the bookmarks - bookmarksLength = ROW_VALUES_BUFFER_SIZE; - } - file.seek(offset); - uint32_t read = file.read(buffer, bookmarksLength); - if (read == bookmarksLength) { - g_bookmarks = reader.readBookmarks(bookmarksLength, g_bookmarksDataStart, BOOKMARKS_DATA_SIZE); - } - } - g_dlogFile.numSamples = g_dlogFile.parameters.dataSize ? g_dlogFile.parameters.dataSize : (file.size() - g_dlogFile.dataOffset) / g_dlogFile.numBytesPerRow; @@ -1007,7 +1207,9 @@ bool openFile(const char *filePath, int *err) { file.close(); - if (g_state != STATE_READY) { + if (g_state == STATE_READY) { + loadBookmarks(); + } else { g_state = STATE_ERROR; } @@ -1018,10 +1220,6 @@ Recording &getRecording() { return g_showLatest && g_wasExecuting ? dlog_record::g_activeRecording : g_dlogFile; } -dlog_file::Bookmarks *getBookmarks() { - return g_showLatest && g_wasExecuting ? nullptr : g_bookmarks; -} - float roundValue(float value) { float prec; if (value < 1E-5f) { @@ -1747,37 +1945,8 @@ void data_recording(DataOperationEnum operation, Cursor cursor, Value &value) { value = YT_GRAPH_UPDATE_METHOD_STATIC; } else if (operation == DATA_OPERATION_YT_DATA_GET_PERIOD) { value = Value(recording.parameters.period, UNIT_SECOND); - } else if (operation == DATA_OPERATION_YT_DATA_GET_VISIBLE_BOOKMARKS) { - dlog_file::Bookmarks *bookmarks = getBookmarks(); - if (bookmarks && bookmarks->count > 0) { - float xStart = dlog_view::getPosition(recording) * recording.parameters.period; - float xEnd = (dlog_view::getPosition(recording) + VIEW_WIDTH) * recording.parameters.period; - auto bookmarksSlice = (BookmarksSlice *)value.getVoidPointer(); - for (uint32_t bookmarkIndex = 0; bookmarkIndex < bookmarks->count; bookmarkIndex++) { - auto bookmark = bookmarks->list + bookmarkIndex; - float xValue = bookmark->position * recording.parameters.xAxis.step; - if (xValue >= xStart) { - if (xValue < xEnd) { - if (bookmarksSlice->start == bookmarksSlice->end) { - bookmarksSlice->start = bookmarkIndex; - } - bookmarksSlice->end = bookmarkIndex + 1; - } else { - break; - } - } - } - } - } else if (operation == DATA_OPERATION_YT_DATA_GET_BOOKMARK_POSITION) { - uint32_t bookmarkIndex = value.getUInt32(); - dlog_file::Bookmarks *bookmarks = getBookmarks(); - if (bookmarks && bookmarkIndex < bookmarks->count) { - auto bookmark = bookmarks->list + bookmarkIndex; - float xValue = bookmark->position * recording.parameters.xAxis.step; - value = Value((uint32_t)round(xValue / recording.parameters.period), VALUE_TYPE_UINT32); - } - } else if (operation == DATA_OPERATION_YT_DATA_GET_SELECTED_BOOKMARK_INDEX) { - value = Value(g_selectedBookmarkIndex, VALUE_TYPE_UINT32); + } else if (operation == DATA_OPERATION_YT_DATA_GET_BOOKMARKS) { + value = Value(g_visibleBookmarks, VALUE_TYPE_POINTER); } else if (operation == DATA_OPERATION_YT_DATA_IS_CURSOR_VISIBLE) { value = &recording != &dlog_record::g_activeRecording; } else if (operation == DATA_OPERATION_YT_DATA_GET_CURSOR_OFFSET) { @@ -2309,13 +2478,10 @@ void data_dlog_view_selected_tab(DataOperationEnum operation, Cursor cursor, Val } } -static const uint32_t BOOKMARKS_PER_PAGE = 8; - void action_dlog_view_select_bookmarks_tab() { g_selectedTab = TAB_BOOKMARKS; - auto bookmarks = getBookmarks(); - if (bookmarks && bookmarks->count > BOOKMARKS_PER_PAGE) { + if (getRecording().parameters.bookmarksSize > BOOKMARKS_PER_PAGE) { setFocusCursor(Cursor(-1), DATA_ID_DLOG_BOOKMARKS); } } @@ -2326,25 +2492,20 @@ void action_dlog_view_select_options_tab() { void data_dlog_bookmarks_is_scrollbar_visible(DataOperationEnum operation, Cursor cursor, Value &value) { if (operation == DATA_OPERATION_GET) { - auto bookmarks = getBookmarks(); - value = bookmarks && bookmarks->count > BOOKMARKS_PER_PAGE; + value = getRecording().parameters.bookmarksSize > BOOKMARKS_PER_PAGE; } } void data_dlog_bookmarks(DataOperationEnum operation, Cursor cursor, Value &value) { - auto bookmarks = getBookmarks(); + auto bookmarksSize = getRecording().parameters.bookmarksSize; if (operation == DATA_OPERATION_COUNT) { - value = (int)(bookmarks ? bookmarks->count : 0); + value = (int)(bookmarksSize); } else if (operation == DATA_OPERATION_YT_DATA_GET_SIZE) { - value = Value(bookmarks ? bookmarks->count : 0, VALUE_TYPE_UINT32); + value = Value(bookmarksSize, VALUE_TYPE_UINT32); } else if (operation == DATA_OPERATION_YT_DATA_GET_POSITION) { value = Value(g_bookmarksScrollPosition, VALUE_TYPE_UINT32); } else if (operation == DATA_OPERATION_YT_DATA_SET_POSITION) { - if (value.getUInt32() + BOOKMARKS_PER_PAGE > bookmarks->count) { - g_bookmarksScrollPosition = bookmarks->count - BOOKMARKS_PER_PAGE; - } else { - g_bookmarksScrollPosition = value.getUInt32(); - } + setBookmarksScrollPosition(value.getUInt32()); } else if (operation == DATA_OPERATION_YT_DATA_GET_PAGE_SIZE) { value = Value(BOOKMARKS_PER_PAGE, VALUE_TYPE_UINT32); } else if (operation == DATA_OPERATION_GET) { @@ -2352,80 +2513,73 @@ void data_dlog_bookmarks(DataOperationEnum operation, Cursor cursor, Value &valu } else if (operation == DATA_OPERATION_GET_MIN) { value = MakeValue(0, UNIT_NONE); } else if (operation == DATA_OPERATION_GET_MAX) { - value = MakeValue(1.0f * (bookmarks->count - BOOKMARKS_PER_PAGE), UNIT_NONE); + value = MakeValue(1.0f * (bookmarksSize - BOOKMARKS_PER_PAGE), UNIT_NONE); } else if (operation == DATA_OPERATION_GET_ENCODER_STEP_VALUES) { auto stepValues = value.getStepValues(); - static float values[] = { 1.0f, 5.0f, 10.0f, 20.0f }; + static float values[] = { 1.0f, 1.0f * BOOKMARKS_PER_PAGE, 2.0f * BOOKMARKS_PER_PAGE, 5.0f * BOOKMARKS_PER_PAGE, 10.0f * BOOKMARKS_PER_PAGE }; stepValues->values = values; stepValues->count = sizeof(values) / sizeof(float); stepValues->unit = UNIT_NONE; stepValues->encoderSettings.accelerationEnabled = true; - stepValues->encoderSettings.range = 10.0f * stepValues->values[0]; - stepValues->encoderSettings.step = stepValues->values[0]; + stepValues->encoderSettings.range = 10.0f * BOOKMARKS_PER_PAGE; + stepValues->encoderSettings.step = 1.0f; stepValues->encoderSettings.mode = edit_mode_step::g_scrollBarEncoderMode; value = 1; } else if (operation == DATA_OPERATION_SET_ENCODER_MODE) { psu::gui::edit_mode_step::g_scrollBarEncoderMode = (EncoderMode)value.getInt(); } else if (operation == DATA_OPERATION_SET) { - uint32_t position = (uint32_t)value.getFloat(); - if (position + BOOKMARKS_PER_PAGE > bookmarks->count) { - g_bookmarksScrollPosition = bookmarks->count - BOOKMARKS_PER_PAGE; - } else { - g_bookmarksScrollPosition = position; - } + setBookmarksScrollPosition((uint32_t)value.getFloat()); } } void data_dlog_bookmark_x_value(DataOperationEnum operation, Cursor cursor, Value &value) { if (operation == DATA_OPERATION_GET) { - auto bookmarks = getBookmarks(); - if (bookmarks && (uint32_t)cursor < bookmarks->count) { - auto &recording = dlog_view::getRecording(); - value = Value(bookmarks->list[cursor].position * recording.parameters.xAxis.step, dlog_view::getXAxisUnit(recording)); + auto i = cursor - g_bookmarksScrollPosition; + if (i >= 0 && i < BOOKMARKS_PER_PAGE) { + auto &recording = getRecording(); + value = Value(g_bookmarks[i].position * recording.parameters.xAxis.step, dlog_view::getXAxisUnit(recording)); } } } void data_dlog_bookmark_text(DataOperationEnum operation, Cursor cursor, Value &value) { if (operation == DATA_OPERATION_GET) { - auto bookmarks = getBookmarks(); - if (bookmarks && (uint32_t)cursor < bookmarks->count) { - value = bookmarks->list[cursor].text; + auto i = cursor - g_bookmarksScrollPosition; + if (i >= 0 && i < BOOKMARKS_PER_PAGE) { + value = g_bookmarks[i].text; } } } void data_dlog_bookmark_is_selected(DataOperationEnum operation, Cursor cursor, Value &value) { if (operation == DATA_OPERATION_GET) { - auto bookmarks = getBookmarks(); - if (bookmarks) { - value = (uint32_t)cursor == g_selectedBookmarkIndex; - } + value = (uint32_t)cursor == g_selectedBookmarkIndex; } } void action_dlog_view_select_bookmark() { g_selectedBookmarkIndex = getFoundWidgetAtDown().cursor; - auto bookmarks = getBookmarks(); - - auto cursorOffset = bookmarks->list[g_selectedBookmarkIndex].position * g_dlogFile.parameters.xAxis.step / g_dlogFile.parameters.period - dlog_view::getPosition(g_dlogFile); - if (cursorOffset < 0 || cursorOffset >= (uint32_t)VIEW_WIDTH) { - cursorOffset = VIEW_WIDTH / 2.0f; - } + if (g_bookmarksScrollPosition == g_loadedBookmarksScrollPosition && g_selectedBookmarkIndex >= g_bookmarksScrollPosition && g_selectedBookmarkIndex < g_bookmarksScrollPosition + BOOKMARKS_PER_PAGE) { + // position cursor at the bookmark position + auto bookmarkIndex = g_selectedBookmarkIndex - g_bookmarksScrollPosition; - g_dlogFile.xAxisOffset = bookmarks->list[g_selectedBookmarkIndex].position * g_dlogFile.parameters.xAxis.step - cursorOffset * g_dlogFile.parameters.period; - adjustXAxisOffset(g_dlogFile); + auto cursorOffset = g_bookmarks[bookmarkIndex].position * g_dlogFile.parameters.xAxis.step / g_dlogFile.parameters.period - getPosition(g_dlogFile); + if (cursorOffset < 0 || cursorOffset >= (uint32_t)VIEW_WIDTH) { + cursorOffset = VIEW_WIDTH / 2.0f; + } - g_dlogFile.cursorOffset = (uint32_t)roundf(cursorOffset); + g_dlogFile.xAxisOffset = g_bookmarks[bookmarkIndex].position * g_dlogFile.parameters.xAxis.step - cursorOffset * g_dlogFile.parameters.period; + adjustXAxisOffset(g_dlogFile); - if (bookmarks && bookmarks->count > BOOKMARKS_PER_PAGE) { - setFocusCursor(Cursor(-1), DATA_ID_DLOG_BOOKMARKS); + g_dlogFile.cursorOffset = (uint32_t)roundf(cursorOffset); } + + setFocusCursor(Cursor(-1), DATA_ID_DLOG_BOOKMARKS); } void data_dlog_view_is_zoom_in_enabled(DataOperationEnum operation, Cursor cursor, Value &value) { diff --git a/src/eez/modules/psu/dlog_view.h b/src/eez/modules/psu/dlog_view.h index b80284cc6..35e3fdcbf 100644 --- a/src/eez/modules/psu/dlog_view.h +++ b/src/eez/modules/psu/dlog_view.h @@ -163,6 +163,12 @@ float getSample(Recording &recording, uint8_t *rowData, unsigned columnIndex); // this is called from the thread that owns SD card void loadBlock(); +// this is called from the thread that owns SD card +void loadBookmarks(); + +// this is called from the thread that owns SD card +void tick(); + // this should be called during GUI state managment phase void stateManagment(); diff --git a/src/eez/modules/psu/scpi/dlog.cpp b/src/eez/modules/psu/scpi/dlog.cpp index 496738d3c..cd698401f 100644 --- a/src/eez/modules/psu/scpi/dlog.cpp +++ b/src/eez/modules/psu/scpi/dlog.cpp @@ -884,6 +884,11 @@ scpi_result_t scpi_cmd_senseDlogTraceBookmark(scpi_t *context) { return SCPI_RES_ERR; } + if (textLen > dlog_file::MAX_BOOKMARK_TEXT_LEN) { + SCPI_ErrorPush(context, SCPI_ERROR_CHARACTER_DATA_TOO_LONG); + return SCPI_RES_ERR; + } + dlog_record::logBookmark(text, textLen); return SCPI_RES_OK; diff --git a/src/eez/tasks.cpp b/src/eez/tasks.cpp index cd6a11760..4aa26c5d9 100644 --- a/src/eez/tasks.cpp +++ b/src/eez/tasks.cpp @@ -309,6 +309,8 @@ void lowPriorityThreadOneIter() { dlog_view::openFile(nullptr); } else if (type == THREAD_MESSAGE_DLOG_LOAD_BLOCK) { dlog_view::loadBlock(); + } else if (type == THREAD_MESSAGE_DLOG_LOAD_BOOKMARKS) { + dlog_view::loadBookmarks(); } else if (type == THREAD_MESSAGE_ABORT_DOWNLOADING) { psu::scpi::abortDownloading(); } else if (type == THREAD_MESSAGE_SCREENSHOT) { @@ -503,6 +505,8 @@ void lowPriorityThreadOneIter() { uart::tick(); + dlog_view::tick(); + return; } diff --git a/src/eez/tasks.h b/src/eez/tasks.h index 7460c8eeb..f7352d5c7 100644 --- a/src/eez/tasks.h +++ b/src/eez/tasks.h @@ -88,6 +88,7 @@ enum LowPriorityThreadMessage { THREAD_MESSAGE_DLOG_STATE_TRANSITION, THREAD_MESSAGE_DLOG_SHOW_FILE, THREAD_MESSAGE_DLOG_LOAD_BLOCK, + THREAD_MESSAGE_DLOG_LOAD_BOOKMARKS, THREAD_MESSAGE_ABORT_DOWNLOADING, THREAD_MESSAGE_SCREENSHOT, THREAD_MESSAGE_FILE_MANAGER_LOAD_DIRECTORY,