mirror of
https://github.com/pelgraine/Meck.git
synced 2026-06-22 19:05:03 +02:00
audiobook player guide updated.
Summary of what changed in each file, all confined to the change points shown in the diffs: ChannelScreen.h — ChannelMessage gains a session-only scope_idx (initialised to 0xFF in the constructor and explicitly reset to 0xFF on SD load, since region is not persisted); addMessage gains a trailing defaulted scope_idx and stores it. The message-list line now renders (Xh)(Xb) Xm, with the byte figure taken from path_len's upper bits for floods and from the_mesh.getNodePrefs()->path_hash_mode + 1 for the 0xFF/0 sentinels. The path overlay shows Route: ... (N-byte) and a new Region: line (name, or (reg unknown), or nothing when unscoped). MyMesh.h / .cpp — a fixed 28-entry SCOPE_NAMES table, a _scope_keys array precomputed once at the end of begin() via initScopeKeys(), resolveScopeIndex() (matches pkt->transport_codes[0] against the candidates; 0xFF unscoped, 0xFE unmatched), and the public getScopeName() accessor. onChannelMessageRecv resolves the index and passes it to newMsg. AbstractUITask.h / UITask.h / UITask.cpp — newMsg gains a trailing uint8_t scope_idx = 0xFF; only the channel addMessage call forwards it. DMs and sent echoes keep the default, so they stay unscoped. MsgFileRecord and the SD save/load format are untouched, so there's no version bump.
This commit is contained in:
@@ -42,7 +42,8 @@ public:
|
||||
void disableSerial() { _serial->disable(); }
|
||||
virtual void msgRead(int msgcount) = 0;
|
||||
virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount,
|
||||
const uint8_t* path = nullptr, int8_t snr = 0) = 0;
|
||||
const uint8_t* path = nullptr, int8_t snr = 0,
|
||||
uint8_t scope_idx = 0xFF) = 0; // 0xFF = unscoped
|
||||
virtual void notify(UIEventType t = UIEventType::none) = 0;
|
||||
virtual void loop() = 0;
|
||||
virtual void showAlert(const char* text, int duration_millis) {}
|
||||
|
||||
@@ -670,6 +670,43 @@ const char* MyMesh::getChannelScopeName(const mesh::GroupChannel& channel) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// --- Region scope candidate list (display-only resolution of incoming channel msgs) ---
|
||||
// Fixed set of nameable regions. Keys are precomputed once at boot in initScopeKeys().
|
||||
const char* const MyMesh::SCOPE_NAMES[MyMesh::SCOPE_COUNT] = {
|
||||
"au",
|
||||
"au-nsw", "au-vic", "au-act", "au-sa", "au-wa", "au-tas", "au-nt", "au-qld",
|
||||
"au-nsw-syd", "au-nsw-bhs", "au-nsw-hun", "au-nsw-ntl", "au-nsw-wol",
|
||||
"au-nsw-cw", "au-nsw-wsi", "au-nsw-syd-iwc",
|
||||
"au-vic-mel", "au-vic-east", "au-vic-north", "au-vic-west",
|
||||
"au-act-cbr",
|
||||
"au-tas-hob",
|
||||
"au-qld-bne",
|
||||
"au-wa-per", "au-wa-fre", "au-wa-buy",
|
||||
"au-hume"
|
||||
};
|
||||
|
||||
void MyMesh::initScopeKeys() {
|
||||
for (uint8_t i = 0; i < SCOPE_COUNT; i++) {
|
||||
deriveScopeKey(SCOPE_NAMES[i], _scope_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MyMesh::resolveScopeIndex(const mesh::Packet* pkt) const {
|
||||
if (!pkt || !pkt->hasTransportCodes()) return 0xFF; // unscoped
|
||||
uint16_t code = pkt->transport_codes[0];
|
||||
for (uint8_t i = 0; i < SCOPE_COUNT; i++) {
|
||||
if (_scope_keys[i].calcTransportCode(pkt) == code) return i;
|
||||
}
|
||||
return 0xFE; // scoped, but not one of the known regions
|
||||
}
|
||||
|
||||
const char* MyMesh::getScopeName(uint8_t idx) const {
|
||||
if (idx == 0xFF) return nullptr; // unscoped -- no region line
|
||||
if (idx == 0xFE) return "(reg unknown)"; // scoped but unmatched
|
||||
if (idx < SCOPE_COUNT) return SCOPE_NAMES[idx];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp,
|
||||
const char *text) {
|
||||
markConnectionActive(from); // in case this is from a server, and we have a connection
|
||||
@@ -816,7 +853,8 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
||||
}
|
||||
if (_ui) {
|
||||
const uint8_t* msg_path = (pkt->isRouteFlood() && pkt->path_len > 0) ? pkt->path : nullptr;
|
||||
_ui->newMsg(path_len, channel_name, text, offline_queue_len, msg_path, pkt->_snr);
|
||||
uint8_t scope_idx = resolveScopeIndex(pkt);
|
||||
_ui->newMsg(path_len, channel_name, text, offline_queue_len, msg_path, pkt->_snr, scope_idx);
|
||||
if (!_prefs.buzzer_quiet) _ui->notify(UIEventType::channelMessage); //buzz if enabled
|
||||
}
|
||||
#endif
|
||||
@@ -1530,6 +1568,8 @@ void MyMesh::begin(bool has_display) {
|
||||
|
||||
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_set_tx_power(_prefs.tx_power_dbm);
|
||||
|
||||
initScopeKeys(); // precompute region-scope transport keys for incoming-message display
|
||||
}
|
||||
|
||||
const char *MyMesh::getNodeName() {
|
||||
|
||||
@@ -185,6 +185,10 @@ public:
|
||||
bool deriveScopeKey(const char* scopeName, TransportKey& keyOut);
|
||||
// Look up per-channel scope name by GroupChannel secret match. Returns nullptr if no scope set.
|
||||
const char* getChannelScopeName(const mesh::GroupChannel& channel);
|
||||
// Resolve a region scope index (as produced by resolveScopeIndex during onChannelMessageRecv)
|
||||
// to a display name. Returns nullptr for unscoped, "(reg unknown)" for scoped-but-unmatched,
|
||||
// otherwise the candidate region name. Used by the channel path-detail overlay.
|
||||
const char* getScopeName(uint8_t idx) const;
|
||||
|
||||
|
||||
protected:
|
||||
@@ -308,6 +312,18 @@ private:
|
||||
|
||||
TransportKey send_scope;
|
||||
|
||||
// --- Region scope resolution for incoming channel messages (display only) ---
|
||||
// A received scoped flood/direct packet carries a one-way transport code. We match
|
||||
// it against this fixed candidate list, whose keys are precomputed once at boot in
|
||||
// initScopeKeys(). resolveScopeIndex() returns an index into SCOPE_NAMES, 0xFF for
|
||||
// unscoped packets, or 0xFE for scoped-but-unmatched. Result is held in RAM only
|
||||
// (per-message), never persisted.
|
||||
static const uint8_t SCOPE_COUNT = 28;
|
||||
static const char* const SCOPE_NAMES[SCOPE_COUNT];
|
||||
TransportKey _scope_keys[SCOPE_COUNT];
|
||||
void initScopeKeys();
|
||||
uint8_t resolveScopeIndex(const mesh::Packet* pkt) const;
|
||||
|
||||
uint8_t cmd_frame[MAX_FRAME_SIZE + 1];
|
||||
uint8_t out_frame[MAX_FRAME_SIZE + 1];
|
||||
CayenneLPP telemetry;
|
||||
|
||||
@@ -68,6 +68,7 @@ public:
|
||||
uint8_t path[MSG_PATH_MAX]; // Repeater hop hashes
|
||||
char text[CHANNEL_MSG_TEXT_LEN];
|
||||
bool valid;
|
||||
uint8_t scope_idx; // Region scope index for display (session only, 0xFF = unscoped). Not persisted.
|
||||
};
|
||||
|
||||
// Simple hash for DM peer matching
|
||||
@@ -139,6 +140,7 @@ public:
|
||||
_messages[i].valid = false;
|
||||
_messages[i].dm_peer_hash = 0;
|
||||
memset(_messages[i].path, 0, MSG_PATH_MAX);
|
||||
_messages[i].scope_idx = 0xFF;
|
||||
}
|
||||
// Initialize unread counts
|
||||
memset(_unread, 0, sizeof(_unread));
|
||||
@@ -151,7 +153,7 @@ public:
|
||||
// suppressUnread: if true, do not increment the unread counter for this message
|
||||
void addMessage(uint8_t channel_idx, uint8_t path_len, const char* sender, const char* text,
|
||||
const uint8_t* path_bytes = nullptr, int8_t snr = 0, const char* peer_name = nullptr,
|
||||
bool suppressUnread = false) {
|
||||
bool suppressUnread = false, uint8_t scope_idx = 0xFF) {
|
||||
// Move to next slot in circular buffer
|
||||
_newestIdx = (_newestIdx + 1) % CHANNEL_MSG_HISTORY_SIZE;
|
||||
|
||||
@@ -161,6 +163,7 @@ public:
|
||||
msg->channel_idx = channel_idx;
|
||||
msg->snr = snr;
|
||||
msg->valid = true;
|
||||
msg->scope_idx = scope_idx;
|
||||
|
||||
// Set DM peer hash for conversation filtering
|
||||
if (channel_idx == 0xFF) {
|
||||
@@ -545,6 +548,7 @@ public:
|
||||
_messages[i].dm_peer_hash = rec.dm_peer_hash;
|
||||
memcpy(_messages[i].path, rec.path, MSG_PATH_MAX);
|
||||
memcpy(_messages[i].text, rec.text, CHANNEL_MSG_TEXT_LEN);
|
||||
_messages[i].scope_idx = 0xFF; // region scope is session-only, not stored on SD
|
||||
if (_messages[i].valid) loaded++;
|
||||
}
|
||||
|
||||
@@ -845,11 +849,21 @@ public:
|
||||
display.print("Route: Local/Sent");
|
||||
} else {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
sprintf(tmp, "Route: %d hop%s (%dB)", hopCount, hopCount == 1 ? "" : "s", bytesPerHop);
|
||||
sprintf(tmp, "Route: %d hop%s (%d-byte)", hopCount, hopCount == 1 ? "" : "s", bytesPerHop);
|
||||
display.print(tmp);
|
||||
}
|
||||
y += lineH;
|
||||
|
||||
// Region (scoped channel messages only; session, not persisted)
|
||||
const char* rgn = the_mesh.getScopeName(msg->scope_idx);
|
||||
if (rgn) {
|
||||
display.setCursor(0, y);
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
sprintf(tmp, "Region: %s", rgn);
|
||||
display.print(tmp);
|
||||
y += lineH;
|
||||
}
|
||||
|
||||
// SNR (if available — value is SNR×4)
|
||||
if (msg->snr != 0) {
|
||||
display.setCursor(0, y);
|
||||
@@ -1245,14 +1259,21 @@ public:
|
||||
sprintf(tmp, ">%dd ", age / 86400);
|
||||
}
|
||||
} else {
|
||||
int hopsDisp = (msg->path_len == 0xFF) ? 0 : (msg->path_len & 63);
|
||||
// Byte mode: flood packets encode it in the upper bits of path_len.
|
||||
// The sentinels (0xFF direct-received, 0 locally-sent) do not encode it,
|
||||
// so fall back to this device's configured path hash size.
|
||||
int bphDisp = (msg->path_len == 0xFF || msg->path_len == 0)
|
||||
? (the_mesh.getNodePrefs()->path_hash_mode + 1)
|
||||
: ((msg->path_len >> 6) + 1);
|
||||
if (age < 60) {
|
||||
sprintf(tmp, "(%d) %ds ", msg->path_len == 0xFF ? 0 : (msg->path_len & 63), age);
|
||||
sprintf(tmp, "(%dh)(%db) %ds ", hopsDisp, bphDisp, age);
|
||||
} else if (age < 3600) {
|
||||
sprintf(tmp, "(%d) %dm ", msg->path_len == 0xFF ? 0 : (msg->path_len & 63), age / 60);
|
||||
sprintf(tmp, "(%dh)(%db) %dm ", hopsDisp, bphDisp, age / 60);
|
||||
} else if (age < 86400) {
|
||||
sprintf(tmp, "(%d) %dh ", msg->path_len == 0xFF ? 0 : (msg->path_len & 63), age / 3600);
|
||||
sprintf(tmp, "(%dh)(%db) %dh ", hopsDisp, bphDisp, age / 3600);
|
||||
} else {
|
||||
sprintf(tmp, "(%d) %dd ", msg->path_len == 0xFF ? 0 : (msg->path_len & 63), age / 86400);
|
||||
sprintf(tmp, "(%dh)(%db) %dd ", hopsDisp, bphDisp, age / 86400);
|
||||
}
|
||||
}
|
||||
display.print(tmp);
|
||||
|
||||
@@ -1577,7 +1577,7 @@ void UITask::msgRead(int msgcount) {
|
||||
}
|
||||
|
||||
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount,
|
||||
const uint8_t* path, int8_t snr) {
|
||||
const uint8_t* path, int8_t snr, uint8_t scope_idx) {
|
||||
_msgcount = msgcount;
|
||||
|
||||
// --- Dedup: suppress retry spam (same sender + text within 60s) ---
|
||||
@@ -1725,7 +1725,7 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
|
||||
((ChannelScreen *) channel_screen)->addMessage(channel_idx, path_len, from_name, dmFormatted, path, snr, nullptr, suppressNotif);
|
||||
}
|
||||
} else {
|
||||
((ChannelScreen *) channel_screen)->addMessage(channel_idx, path_len, from_name, text, path, snr, nullptr, suppressNotif);
|
||||
((ChannelScreen *) channel_screen)->addMessage(channel_idx, path_len, from_name, text, path, snr, nullptr, suppressNotif, scope_idx);
|
||||
}
|
||||
|
||||
// If user is currently viewing this channel on the device, or companion
|
||||
|
||||
@@ -366,7 +366,8 @@ public:
|
||||
// from AbstractUITask
|
||||
void msgRead(int msgcount) override;
|
||||
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount,
|
||||
const uint8_t* path = nullptr, int8_t snr = 0) override;
|
||||
const uint8_t* path = nullptr, int8_t snr = 0,
|
||||
uint8_t scope_idx = 0xFF) override;
|
||||
void notify(UIEventType t = UIEventType::none) override;
|
||||
void loop() override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user