Merge branch 'develop' into baseui_nodeactionsoverhaul

This commit is contained in:
Jason P
2026-01-12 19:44:08 -06:00
committed by GitHub
7 changed files with 106 additions and 40 deletions

View File

@@ -13,6 +13,11 @@
#define MESSAGE_TEXT_POOL_SIZE (MAX_MESSAGES_SAVED * MAX_MESSAGE_SIZE)
#endif
// Default autosave interval 2 hours, override per device later with -DMESSAGE_AUTOSAVE_INTERVAL_SEC=300 (etc)
#ifndef MESSAGE_AUTOSAVE_INTERVAL_SEC
#define MESSAGE_AUTOSAVE_INTERVAL_SEC (2 * 60 * 60)
#endif
// Global message text pool and state
static char *g_messagePool = nullptr;
static size_t g_poolWritePos = 0;
@@ -102,6 +107,60 @@ void MessageStore::addLiveMessage(const StoredMessage &msg)
pushWithLimit(liveMessages, msg);
}
#if ENABLE_MESSAGE_PERSISTENCE
static bool g_messageStoreHasUnsavedChanges = false;
static uint32_t g_lastAutoSaveMs = 0; // last time we actually saved
static inline uint32_t autosaveIntervalMs()
{
uint32_t sec = (uint32_t)MESSAGE_AUTOSAVE_INTERVAL_SEC;
if (sec < 60)
sec = 60;
return sec * 1000UL;
}
static inline bool reachedMs(uint32_t now, uint32_t target)
{
return (int32_t)(now - target) >= 0;
}
// Mark new messages in RAM that need to be saved later
static inline void markMessageStoreUnsaved()
{
g_messageStoreHasUnsavedChanges = true;
if (g_lastAutoSaveMs == 0) {
g_lastAutoSaveMs = millis();
}
}
// Called periodically from the main loop in main.cpp
static inline void autosaveTick(MessageStore *store)
{
if (!store)
return;
uint32_t now = millis();
if (g_lastAutoSaveMs == 0) {
g_lastAutoSaveMs = now;
return;
}
if (!reachedMs(now, g_lastAutoSaveMs + autosaveIntervalMs()))
return;
// Autosave interval reached, only save if there are unsaved messages.
if (g_messageStoreHasUnsavedChanges) {
LOG_INFO("Autosaving MessageStore to flash");
store->saveToFlash();
} else {
LOG_INFO("Autosave skipped, no changes to save");
g_lastAutoSaveMs = now;
}
}
#endif
// Add from incoming/outgoing packet
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
{
@@ -131,6 +190,11 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa
}
addLiveMessage(sm);
#if ENABLE_MESSAGE_PERSISTENCE
markMessageStoreUnsaved();
#endif
return liveMessages.back();
}
@@ -155,6 +219,10 @@ void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const st
sm.ackStatus = AckStatus::NONE;
addLiveMessage(sm);
#if ENABLE_MESSAGE_PERSISTENCE
markMessageStoreUnsaved();
#endif
}
#if ENABLE_MESSAGE_PERSISTENCE
@@ -239,6 +307,10 @@ void MessageStore::saveToFlash()
f.close();
#endif
// Reset autosave state after any save
g_messageStoreHasUnsavedChanges = false;
g_lastAutoSaveMs = millis();
}
void MessageStore::loadFromFlash()
@@ -270,6 +342,9 @@ void MessageStore::loadFromFlash()
f.close();
#endif
// Loading messages does not trigger an autosave
g_messageStoreHasUnsavedChanges = false;
g_lastAutoSaveMs = millis();
}
#else
@@ -290,6 +365,11 @@ void MessageStore::clearAllMessages()
f.write(&count, 1); // write "0 messages"
f.close();
#endif
#if ENABLE_MESSAGE_PERSISTENCE
g_messageStoreHasUnsavedChanges = false;
g_lastAutoSaveMs = millis();
#endif
}
// Internal helper: erase first or last message matching a predicate
@@ -421,6 +501,14 @@ uint16_t MessageStore::storeText(const char *src, size_t len)
return storeTextInPool(src, len);
}
#if ENABLE_MESSAGE_PERSISTENCE
void messageStoreAutosaveTick()
{
// Called from the main loop to check autosave timing
autosaveTick(&messageStore);
}
#endif
// Global definition
MessageStore messageStore("default");
#endif
#endif

View File

@@ -125,6 +125,11 @@ class MessageStore
std::string filename; // Flash filename for persistence
};
#if ENABLE_MESSAGE_PERSISTENCE
// Called periodically from main loop to trigger time based autosave
void messageStoreAutosaveTick();
#endif
// Global instance (defined in MessageStore.cpp)
extern MessageStore messageStore;

View File

@@ -450,7 +450,7 @@ void menuHandler::clockMenu()
}
void menuHandler::messageResponseMenu()
{
enum optionsNumbers { Back = 0, ViewMode, DeleteAll, DeleteOldest, ReplyMenu, MuteChannel, Aloud, enumEnd };
enum optionsNumbers { Back = 0, ViewMode, DeleteMenu, ReplyMenu, MuteChannel, Aloud, enumEnd };
static const char *optionsArray[enumEnd];
static int optionsEnumArray[enumEnd];
@@ -480,7 +480,7 @@ void menuHandler::messageResponseMenu()
// Delete submenu
optionsArray[options] = "Delete";
optionsEnumArray[options++] = 900;
optionsEnumArray[options++] = DeleteMenu;
#ifdef HAS_I2S
optionsArray[options] = "Read Aloud";
@@ -521,34 +521,10 @@ void menuHandler::messageResponseMenu()
nodeDB->saveToDisk();
}
// Delete submenu
} else if (selected == 900) {
} else if (selected == DeleteMenu) {
menuHandler::menuQueue = menuHandler::delete_messages_menu;
screen->runNow();
// Delete oldest FIRST (only change)
} else if (selected == DeleteOldest) {
auto mode = graphics::MessageRenderer::getThreadMode();
int ch = graphics::MessageRenderer::getThreadChannel();
uint32_t peer = graphics::MessageRenderer::getThreadPeer();
if (mode == graphics::MessageRenderer::ThreadMode::ALL) {
// Global oldest
messageStore.deleteOldestMessage();
} else if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
// Oldest in current channel
messageStore.deleteOldestMessageInChannel(ch);
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
// Oldest in current DM
messageStore.deleteOldestMessageWithPeer(peer);
}
// Delete all messages
} else if (selected == DeleteAll) {
messageStore.clearAllMessages();
graphics::MessageRenderer::clearThreadRegistries();
graphics::MessageRenderer::clearMessageCache();
#ifdef HAS_I2S
} else if (selected == Aloud) {
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
@@ -717,7 +693,6 @@ void menuHandler::deleteMessagesMenu()
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
messageStore.deleteOldestMessageWithPeer(peer);
}
return;
}
@@ -730,7 +705,6 @@ void menuHandler::deleteMessagesMenu()
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
messageStore.deleteAllMessagesWithPeer(peer);
}
return;
}
};

View File

@@ -38,6 +38,9 @@
#include "target_specific.h"
#include <memory>
#include <utility>
#if HAS_SCREEN
#include "MessageStore.h"
#endif
#ifdef ELECROW_ThinkNode_M5
PCA9557 io(0x18, &Wire);
@@ -1652,6 +1655,9 @@ void loop()
if (dispdev)
static_cast<TFTDisplay *>(dispdev)->sdlLoop();
}
#endif
#if HAS_SCREEN && ENABLE_MESSAGE_PERSISTENCE
messageStoreAutosaveTick();
#endif
long delayMsec = mainController.runOrDelay();

View File

@@ -281,8 +281,6 @@ typedef struct _meshtastic_AdminMessage {
meshtastic_SharedContact add_contact;
/* Initiate or respond to a key verification request */
meshtastic_KeyVerificationAdmin key_verification;
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
meshtastic_OTAMode reboot_ota_mode;
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
int32_t factory_reset_device;
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
@@ -341,7 +339,6 @@ extern "C" {
#define meshtastic_AdminMessage_payload_variant_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_restore_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
@@ -436,7 +433,6 @@ extern "C" {
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_add_contact_tag 66
#define meshtastic_AdminMessage_key_verification_tag 67
#define meshtastic_AdminMessage_reboot_ota_mode_tag 68
#define meshtastic_AdminMessage_factory_reset_device_tag 94
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
#define meshtastic_AdminMessage_exit_simulator_tag 96
@@ -497,7 +493,6 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_ed
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,add_contact,add_contact), 66) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,key_verification,key_verification), 67) \
X(a, STATIC, ONEOF, UENUM, (payload_variant,reboot_ota_mode,reboot_ota_mode), 68) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \

View File

@@ -296,6 +296,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_THINKNODE_M6 = 120,
/* Elecrow Meshstick 1262 */
meshtastic_HardwareModel_MESHSTICK_1262 = 121,
/* LilyGo T-Beam 1W */
meshtastic_HardwareModel_TBEAM_1_WATT = 122,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -1366,10 +1368,6 @@ extern "C" {
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
#define _meshtastic_MeshPacket_Priority_ARRAYSIZE ((meshtastic_MeshPacket_Priority)(meshtastic_MeshPacket_Priority_MAX+1))