diff --git a/firmware/application/apps/ble_rx_app.cpp b/firmware/application/apps/ble_rx_app.cpp index df775088..ff7eff2a 100644 --- a/firmware/application/apps/ble_rx_app.cpp +++ b/firmware/application/apps/ble_rx_app.cpp @@ -506,6 +506,22 @@ BLERxView::BLERxView(NavigationView& nav) receiver_model.enable(); } +std::string BLERxView::build_line_str(BleRecentEntry entry) { + std::string macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false) + ","; + std::string timestameStr = entry.timestamp + ","; + std::string nameStr = entry.nameString + ","; + std::string pduStr = pdu_type_to_string(entry.pduType) + ","; + std::string dataStr = "0x" + entry.dataString + ","; + std::string hitsStr = to_string_dec_int(entry.numHits) + ","; + std::string dbStr = to_string_dec_int(entry.dbValue) + ","; + std::string channelStr = to_string_dec_int(entry.channelNumber) + ","; + + std::string lineStr = timestameStr + macAddressStr + nameStr + pduStr + dataStr + hitsStr + dbStr + channelStr; + lineStr += pad_string_with_spaces(maxLineLength - lineStr.length()); + + return lineStr; +} + void BLERxView::on_save_file(const std::string value) { std::filesystem::path packet_save_path{u"BLERX/Lists/List_????.csv"}; @@ -518,40 +534,119 @@ void BLERxView::on_save_file(const std::string value) { } bool BLERxView::saveFile(const std::filesystem::path& path) { + // Check to see if file was previously saved. + bool file_existed = file_exists(path); + + // Attempt to open, if it can't be opened. Create new. File f; - auto error = f.create(path); - if (error) + auto error = f.open(path, false, true); + + if (error) { return false; - - auto it = recent.begin(); - - std::string headerStr = "Timestamp, MAC Address, Name, Packet Type, Data, Hits, dB, Channel \n"; - f.write(headerStr.c_str(), headerStr.length()); - - while (it != recent.end()) { - BleRecentEntry entry = (BleRecentEntry)*it; - - std::string macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false) + ", "; - std::string timestameStr = entry.timestamp + ", "; - std::string nameStr = entry.nameString + ", "; - std::string pduStr = pdu_type_to_string(entry.pduType) + ", "; - std::string dataStr = "0x" + entry.dataString + ", "; - std::string hitsStr = to_string_dec_int(entry.numHits) + ", "; - std::string dbStr = to_string_dec_int(entry.dbValue) + ", "; - std::string channelStr = to_string_dec_int(entry.channelNumber) + "\n"; - - f.write(timestameStr.c_str(), timestameStr.length()); - f.write(macAddressStr.c_str(), macAddressStr.length()); - f.write(nameStr.c_str(), nameStr.length()); - f.write(pduStr.c_str(), pduStr.length()); - f.write(dataStr.c_str(), dataStr.length()); - f.write(hitsStr.c_str(), hitsStr.length()); - f.write(dbStr.c_str(), dbStr.length()); - f.write(channelStr.c_str(), channelStr.length()); - - it++; } + for (const auto& entry : recent) { + tempList.emplace_back(entry); + } + + if (!file_existed) { + f.write_line(headerStr.c_str()); + + auto it = tempList.begin(); + + while (it != tempList.end()) { + BleRecentEntry entry = (BleRecentEntry)*it; + f.write_line(build_line_str(entry).c_str()); + it++; + } + } else { + // Check file for macAddressStr before adding. + char currentLine[maxLineLength]; + uint64_t startPos = headerStr.length(); + uint64_t bytesRead = 0; + uint64_t bytePos = 0; + + File::Size currentSize = f.size(); + + File tempFile; + const std::filesystem::path tempFilePath = path + "~"; + auto error = tempFile.open(tempFilePath, false, true); + + if (error) { + return false; + } + + tempFile.write_line(headerStr.c_str()); + + f.seek(startPos); + + // Look for ones found and rewrite. + do { + memset(currentLine, 0, maxLineLength); + + bytesRead = readUntil(f, currentLine, currentSize, '\n'); + + if (!bytesRead) { + break; + } + + bytePos += bytesRead; + + std::string lineStr = ""; + std::string macAddressStr = ""; + BleRecentEntry foundEntry; + + char* token; + token = strtok(currentLine, ","); + + while (token != NULL) { + auto it = tempList.begin(); + + while (it != tempList.end()) { + BleRecentEntry& entry = reinterpret_cast(*it); + + macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false); + + if (strstr(token, macAddressStr.c_str()) != NULL) { + entry.entryFound = true; + foundEntry = entry; + break; + } + + it++; + } + + if (foundEntry.entryFound) { + break; + } + + token = strtok(NULL, ","); + } + + if (foundEntry.entryFound) { + tempFile.write_line(build_line_str(foundEntry).c_str()); + } + + } while (bytePos <= currentSize); + + // Write the ones not found. + auto it = tempList.begin(); + + while (it != tempList.end()) { + BleRecentEntry entry = (BleRecentEntry)*it; + + if (!entry.entryFound) { + tempFile.write_line(build_line_str(entry).c_str()); + } + + it++; + } + + // rename_file(tempFilePath, path); + } + + tempList.clear(); + return true; } diff --git a/firmware/application/apps/ble_rx_app.hpp b/firmware/application/apps/ble_rx_app.hpp index 6d58514a..40004382 100644 --- a/firmware/application/apps/ble_rx_app.hpp +++ b/firmware/application/apps/ble_rx_app.hpp @@ -85,6 +85,7 @@ struct BleRecentEntry { uint16_t numHits; ADV_PDU_TYPE pduType; uint8_t channelNumber; + bool entryFound; BleRecentEntry() : BleRecentEntry{0} { @@ -101,7 +102,8 @@ struct BleRecentEntry { include_name{}, numHits{}, pduType{}, - channelNumber{} { + channelNumber{}, + entryFound{} { } Key key() const { @@ -188,6 +190,7 @@ class BLERxView : public View { std::string title() const override { return "BLE RX"; }; private: + std::string build_line_str(BleRecentEntry entry); void on_save_file(const std::string value); bool saveFile(const std::filesystem::path& path); void on_data(BlePacketData* packetData); @@ -212,6 +215,8 @@ class BLERxView : public View { std::string filterBuffer{}; std::string filter{}; std::string listFileBuffer{}; + std::string headerStr = "Timestamp, MAC Address, Name, Packet Type, Data, Hits, dB, Channel"; + uint16_t maxLineLength = 140; static constexpr auto header_height = 3 * 16; static constexpr auto switch_button_height = 3 * 16; @@ -292,6 +297,7 @@ class BLERxView : public View { std::unique_ptr logger{}; BleRecentEntries recent{}; + BleRecentEntries tempList{}; const RecentEntriesColumns columns{{ {"Mac Address", 17}, diff --git a/firmware/application/apps/ble_tx_app.cpp b/firmware/application/apps/ble_tx_app.cpp index 2958698d..a0d65f65 100644 --- a/firmware/application/apps/ble_tx_app.cpp +++ b/firmware/application/apps/ble_tx_app.cpp @@ -94,33 +94,6 @@ uint32_t stringToUint32(const std::string& str) { return result; } -void readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter) { - std::size_t bytesRead = 0; - - while (true) { - char ch; - File::Result readResult = file.read(&ch, 1); - - if (readResult.is_ok() && readResult.value() > 0) { - if (ch == delimiter) { - // Found a space character, stop reading - break; - } else if (bytesRead < maxBufferSize) { - // Append the character to the result if there's space - result[bytesRead++] = ch; - } else { - // Buffer is full, break to prevent overflow - break; - } - } else { - break; // End of file or error - } - } - - // Null-terminate the result string - result[bytesRead] = '\0'; -} - static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) { uint64_t freq_hz; diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index ca2d73fb..9b6c6cf9 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -348,6 +348,35 @@ void generateRandomMacAddress(char* macAddress) { macAddress[12] = '\0'; // Null-terminate the string } +uint64_t readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter) { + std::size_t bytesRead = 0; + + while (true) { + char ch; + File::Result readResult = file.read(&ch, 1); + + if (readResult.is_ok() && readResult.value() > 0) { + if (ch == delimiter) { + // Found a space character, stop reading + break; + } else if (bytesRead < maxBufferSize) { + // Append the character to the result if there's space + result[bytesRead++] = ch; + } else { + // Buffer is full, break to prevent overflow + break; + } + } else { + break; // End of file or error + } + } + + // Null-terminate the result string + result[bytesRead] = '\0'; + + return bytesRead; +} + std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision) { const uint32_t powers_of_ten[5] = {1, 10, 100, 1000, 10000}; std::string string{""}; diff --git a/firmware/application/string_format.hpp b/firmware/application/string_format.hpp index 5f1b2bdc..4413afc2 100644 --- a/firmware/application/string_format.hpp +++ b/firmware/application/string_format.hpp @@ -81,6 +81,8 @@ std::string to_string_mac_address(const uint8_t* macAddress, uint8_t length, boo std::string to_string_formatted_mac_address(const char* macAddress); void generateRandomMacAddress(char* macAddress); +uint64_t readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter); + /* Scales 'n' to be a value less than 1000. 'base_unit' is the index of the unit from * 'unit_prefix' that 'n' is in initially. 3 is the index of the '1s' unit. */ std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision);