Skip to content

Commit 283cf04

Browse files
Refactor contact management in MyMesh: replace lazy write delay with a debounced save mechanism. Introduce methods to track contact changes and ensure contacts are saved if modified before shutdown. Update UI tasks to flush contacts if dirty during reboot and power off.
1 parent 2546a5d commit 283cf04

File tree

4 files changed

+52
-23
lines changed

4 files changed

+52
-23
lines changed

examples/companion_radio/MyMesh.cpp

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@
8080
#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f
8181
#define DIRECT_SEND_PERHOP_FACTOR 6.0f
8282
#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250
83-
#define LAZY_CONTACTS_WRITE_DELAY 5000
83+
#define CONTACTS_SAVE_QUIET_WINDOW_MS 60000 // save after this period (1 minute) of no structural changes
84+
#define CONTACTS_SAVE_MAX_WINDOW_MS 300000 // but never delay beyond this total coalesce window (5 minutes)
8485

8586
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
8687

@@ -127,6 +128,15 @@ void MyMesh::writeDisabledFrame() {
127128
_serial->writeFrame(buf, 1);
128129
}
129130

131+
void MyMesh::markContactsChanged() {
132+
unsigned long now = _ms->getMillis();
133+
contacts_last_change = now;
134+
if (!contacts_dirty) {
135+
contacts_dirty = true;
136+
contacts_first_change = now;
137+
}
138+
}
139+
130140
void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) {
131141
int i = 0;
132142
out_frame[i++] = code;
@@ -268,8 +278,6 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
268278
p->path_len = path_len;
269279
memcpy(p->path, path, p->path_len);
270280
}
271-
272-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
273281
}
274282

275283
static int sort_by_recent(const void *a, const void *b) {
@@ -290,8 +298,6 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
290298
out_frame[0] = PUSH_CODE_PATH_UPDATED;
291299
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
292300
_serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected
293-
294-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
295301
}
296302

297303
bool MyMesh::processAck(const uint8_t *data) {
@@ -374,8 +380,6 @@ void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint3
374380
void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp,
375381
const uint8_t *sender_prefix, const char *text) {
376382
markConnectionActive(from);
377-
// from.sync_since change needs to be persisted
378-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
379383
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text);
380384
}
381385

@@ -641,7 +645,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
641645
clearPendingReqs();
642646
next_ack_idx = 0;
643647
sign_data = NULL;
644-
dirty_contacts_expiry = 0;
648+
contacts_dirty = false;
649+
contacts_last_change = contacts_first_change = 0;
645650
memset(advert_paths, 0, sizeof(advert_paths));
646651

647652
// defaults
@@ -940,8 +945,6 @@ void MyMesh::handleCmdFrame(size_t len) {
940945
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
941946
if (recipient) {
942947
recipient->out_path_len = -1;
943-
// recipient->lastmod = ?? shouldn't be needed, app already has this version of contact
944-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
945948
writeOKFrame();
946949
} else {
947950
writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact
@@ -953,15 +956,15 @@ void MyMesh::handleCmdFrame(size_t len) {
953956
if (recipient) {
954957
updateContactFromFrame(*recipient, last_mod, cmd_frame, len);
955958
recipient->lastmod = last_mod;
956-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
959+
markContactsChanged();
957960
writeOKFrame();
958961
} else {
959962
ContactInfo contact;
960963
updateContactFromFrame(contact, last_mod, cmd_frame, len);
961964
contact.lastmod = last_mod;
962965
contact.sync_since = 0;
963966
if (addContact(contact)) {
964-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
967+
markContactsChanged();
965968
writeOKFrame();
966969
} else {
967970
writeErrFrame(ERR_CODE_TABLE_FULL);
@@ -971,7 +974,7 @@ void MyMesh::handleCmdFrame(size_t len) {
971974
uint8_t *pub_key = &cmd_frame[1];
972975
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
973976
if (recipient && removeContact(*recipient)) {
974-
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
977+
markContactsChanged();
975978
writeOKFrame();
976979
} else {
977980
writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove
@@ -1116,9 +1119,7 @@ void MyMesh::handleCmdFrame(size_t len) {
11161119
savePrefs();
11171120
writeOKFrame();
11181121
} else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) {
1119-
if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed?
1120-
saveContacts();
1121-
}
1122+
saveContactsIfDirty();
11221123
board.reboot();
11231124
} else if (cmd_frame[0] == CMD_GET_BATT_AND_STORAGE) {
11241125
uint8_t reply[11];
@@ -1587,6 +1588,7 @@ void MyMesh::checkCLIRescueCmd() {
15871588
}
15881589

15891590
} else if (strcmp(cli_command, "reboot") == 0) {
1591+
saveContactsIfDirty();
15901592
board.reboot(); // doesn't return
15911593
} else {
15921594
Serial.println(" Error: unknown command");
@@ -1632,10 +1634,16 @@ void MyMesh::loop() {
16321634
checkSerialInterface();
16331635
}
16341636

1635-
// is there are pending dirty contacts write needed?
1636-
if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) {
1637-
saveContacts();
1638-
dirty_contacts_expiry = 0;
1637+
// Debounced save
1638+
if (contacts_dirty) {
1639+
unsigned long now = _ms->getMillis();
1640+
bool quiet_met = (now - contacts_last_change) >= CONTACTS_SAVE_QUIET_WINDOW_MS;
1641+
bool max_reached = (now - contacts_first_change) >= CONTACTS_SAVE_MAX_WINDOW_MS;
1642+
if (quiet_met || max_reached) {
1643+
saveContacts();
1644+
contacts_dirty = false;
1645+
contacts_last_change = contacts_first_change = 0;
1646+
}
16391647
}
16401648

16411649
#ifdef DISPLAY_CLASS

examples/companion_radio/MyMesh.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {
101101

102102
int getRecentlyHeard(AdvertPath dest[], int max_num);
103103

104+
bool isContactsDirty() const { return contacts_dirty; }
105+
void flushContactsIfDirty() { saveContactsIfDirty(); }
106+
104107
protected:
105108
float getAirtimeBudgetFactor() const override;
106109
int getInterferenceThreshold() const override;
@@ -136,6 +139,15 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {
136139
uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override;
137140
void onSendTimeout() override;
138141

142+
bool hasDirtyContacts() const { return contacts_dirty; }
143+
void saveContactsIfDirty() {
144+
if (contacts_dirty) {
145+
saveContacts();
146+
contacts_dirty = false;
147+
contacts_last_change = contacts_first_change = 0;
148+
}
149+
}
150+
139151
// DataStoreHost methods
140152
bool onContactLoaded(const ContactInfo& contact) override { return addContact(contact); }
141153
bool getContactForSave(uint32_t idx, ContactInfo& contact) override { return getContactByIdx(idx, contact); }
@@ -147,6 +159,7 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {
147159
}
148160

149161
private:
162+
void markContactsChanged();
150163
void writeOKFrame();
151164
void writeErrFrame(uint8_t err_code);
152165
void writeDisabledFrame();
@@ -189,7 +202,10 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {
189202
uint8_t app_target_ver;
190203
uint8_t *sign_data;
191204
uint32_t sign_data_len;
192-
unsigned long dirty_contacts_expiry;
205+
206+
bool contacts_dirty;
207+
unsigned long contacts_last_change;
208+
unsigned long contacts_first_change;
193209

194210
uint8_t cmd_frame[MAX_FRAME_SIZE + 1];
195211
uint8_t out_frame[MAX_FRAME_SIZE + 1];

examples/companion_radio/ui-new/UITask.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,11 @@ void UITask::shutdown(bool restart){
472472
#endif // PIN_BUZZER
473473

474474
if (restart) {
475+
the_mesh.flushContactsIfDirty();
475476
_board->reboot();
476477
} else {
477478
_display->turnOff();
479+
the_mesh.flushContactsIfDirty();
478480
_board->powerOff();
479481
}
480482
}

examples/companion_radio/ui-orig/UITask.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,13 @@ void UITask::shutdown(bool restart){
292292

293293
#endif // PIN_BUZZER
294294

295-
if (restart)
295+
if (restart) {
296+
the_mesh.flushContactsIfDirty();
296297
_board->reboot();
297-
else
298+
} else {
299+
the_mesh.flushContactsIfDirty();
298300
_board->powerOff();
301+
}
299302
}
300303

301304
void UITask::loop() {

0 commit comments

Comments
 (0)