Files
MeshCore-Solo/examples/companion_radio/AbstractUITask.h
MarekZegare4 bcf8774962 feat(ui): message delivery status (DM ACK + channel relay)
End-to-end delivery indicators on the device UI, drawn next to outgoing
messages and auto-scaled to the font (legible on landscape e-ink).

DM (and room servers, which share the DM path):
- Pending / delivered / failed marker driven by the real end-to-end ACK.
  Pending shows a row of dots — one per send — so auto-resend progress is
  visible before it resolves to ✓ / ✗.
- Auto-resend: new pref dm_resend_count (0-5, default 2), Settings ›
  Messages › Resend. A pending DM whose ACK times out is re-sent (reusing
  the original timestamp) until resends run out, then ✗. Driven from
  UITask::loop so it completes in the background, independent of screen.
- Incoming dedup: a retry reuses the sender timestamp + text but carries a
  fresh packet hash, so addDMMsg drops copies matching prefix+ts+text.

Channels (flood, no recipient ACK):
- ✓ only once a repeater echo confirms the send was relayed into the mesh;
  no echo is normal, not a failure (no pending/fail shown). A small ring
  tracks a burst of sends so each matches its echo. Receive-path hashing is
  gated so the hot flood path is untouched when idle.

Shared:
- Markers shown in both the history list and the fullscreen message view.
- Reusable scalable mini-icon facility in icons.h (bitmap + auto-scale);
  adding a new status icon is a bitmap plus one draw call.

No changes to the upstream mesh library (src/).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 23:33:16 +02:00

68 lines
2.5 KiB
C++

#pragma once
#include <MeshCore.h>
#include <helpers/ui/DisplayDriver.h>
#include <helpers/ui/UIScreen.h>
#include <helpers/SensorManager.h>
#include <helpers/BaseSerialInterface.h>
#include <Arduino.h>
#ifdef PIN_BUZZER
#include <helpers/ui/buzzer.h>
#endif
#include "NodePrefs.h"
enum class UIEventType {
none,
contactMessage,
channelMessage,
roomMessage,
advertReceivedFlood,
advertReceivedZeroHop,
ack
};
class AbstractUITask {
protected:
mesh::MainBoard* _board;
BaseSerialInterface* _serial;
bool _connected;
AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) {
_connected = false;
}
public:
void setHasConnection(bool connected) {
bool prev = _connected;
_connected = connected;
if (prev && !connected) onBLEDisconnected();
}
bool hasConnection() const { return _connected; }
virtual void onBLEDisconnected() {}
// An end-to-end ACK (CRC) arrived for one of our sent messages — drives the
// DM delivery-status marker. Default no-op for UIs that don't track it.
virtual void onMsgAck(uint32_t ack_crc) { (void)ack_crc; }
// A repeater rebroadcast of one of our channel sends was heard (seq from
// lastChannelRelaySeq()) — drives the channel "relayed into mesh" marker.
virtual void onChannelRelayed(uint32_t seq) { (void)seq; }
// True only when a BLE central is actually bonded/connected. On a dual
// (BLE+USB) interface hasConnection() is always true (USB counts), so use
// this for BLE-specific UI like the pairing-PIN prompt.
bool isBLEConnected() const { return _serial->isBLEConnected(); }
// True when a companion app is connected over any transport (BLE bonded or an
// open USB-CDC port). For app-connected behaviour like Auto buzzer mute.
bool isClientConnected() const { return _serial->isClientConnected(); }
uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); }
bool isSerialEnabled() const { return _serial->isEnabled(); }
void enableSerial() { _serial->enable(); }
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, uint8_t contact_type = 0, const uint8_t* pub_key = nullptr) = 0;
virtual void notify(UIEventType t = UIEventType::none) = 0;
virtual void addChannelMsg(uint8_t channel_idx, const char* text) {}
virtual void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0) {}
virtual void loop() = 0;
};