Merge pull request #2739 from meshtastic/delivery-report

UI/UX: Display delivered message on incoming ACK.
This commit is contained in:
Thomas Göttgens 2023-12-11 22:59:19 +01:00 committed by GitHub
commit 35938392f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 63 deletions

View File

@ -73,58 +73,3 @@ template <class T> class MemoryDynamic : public Allocator<T>
return p;
}
};
/**
* A pool based allocator
*
*/
template <class T> class MemoryPool : public Allocator<T>
{
PointerQueue<T> dead;
T *buf; // our large raw block of memory
size_t maxElements;
public:
explicit MemoryPool(size_t _maxElements) : dead(_maxElements), maxElements(_maxElements)
{
buf = new T[maxElements];
// prefill dead
for (size_t i = 0; i < maxElements; i++)
release(&buf[i]);
}
~MemoryPool() { delete[] buf; }
/// Return a buffer for use by others
void release(T *p)
{
assert(p >= buf &&
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
assert(dead.enqueue(p, 0));
}
#ifdef HAS_FREE_RTOS
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
{
assert(p >= buf &&
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
assert(dead.enqueueFromISR(p, higherPriWoken));
}
#endif
protected:
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
/// probably don't want this version).
virtual T *alloc(TickType_t maxWait)
{
T *p = dead.dequeuePtr(maxWait);
assert(p);
return p;
}
};

View File

@ -140,6 +140,22 @@ void MeshService::reloadOwner(bool shouldSave)
}
}
// search the queue for a request id and return the matching nodenum
NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id)
{
NodeNum nodenum = 0;
for (int i = 0; i < toPhoneQueue.numUsed(); i++) {
meshtastic_MeshPacket *p = toPhoneQueue.dequeuePtr(0);
if (p->id == request_id) {
nodenum = p->to;
// make sure to continue this to make one full loop
}
// put it right back on the queue
toPhoneQueue.enqueue(p, 0);
}
return nodenum;
}
/**
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a

View File

@ -82,6 +82,9 @@ class MeshService
/// Return the next MqttClientProxyMessage packet destined to the phone.
meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); }
// search the queue for a request id and return the matching nodenum
NodeNum getNodenumFromRequestId(uint32_t request_id);
// Release QueueStatus packet to pool
void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); }

View File

@ -27,6 +27,8 @@ template <class T> class TypedQueue
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
int numUsed() { return uxQueueMessagesWaiting(h); }
/** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking
* they want */
bool enqueue(T x, TickType_t maxWait)
@ -80,6 +82,8 @@ template <class T> class TypedQueue
bool isEmpty() { return q.empty(); }
int numUsed() { return q.size(); }
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
{
if (reader) {

View File

@ -233,7 +233,9 @@ void CannedMessageModule::sendText(NodeNum dest, const char *message, bool wantR
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh(p);
service.sendToMesh(
p, RX_SRC_LOCAL,
true); // send to mesh, cc to phone. Even if there's no phone connected, this stores the message to match ACKs
}
int32_t CannedMessageModule::runOnce()
@ -244,7 +246,8 @@ int32_t CannedMessageModule::runOnce()
}
// LOG_DEBUG("Check status\n");
UIFrameEvent e = {false, true};
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED)) {
// TODO: might have some feedback of sendig state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
e.frameChanged = true;
@ -483,7 +486,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
{
char buffer[50];
if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
String displayString;
if (this->ack) {
displayString = "Delivered to\n%s";
} else {
displayString = "Delivery failed\nto %s";
}
display->drawStringf(display->getWidth() / 2 + x, 0 + y + 12, buffer, displayString,
cannedMessageModule->getNodeName(this->incoming));
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
@ -546,6 +560,27 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
}
}
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
// look for a request_id
if (mp.decoded.request_id != 0) {
UIFrameEvent e = {false, true};
e.frameChanged = true;
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
meshtastic_Routing decoded = meshtastic_Routing_init_default;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
this->notifyObservers(&e);
// run the next time 2 seconds later
setIntervalFromNow(2000);
}
}
return ProcessMessage::CONTINUE;
}
void CannedMessageModule::loadProtoForModule()
{
if (!nodeDB.loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
@ -650,4 +685,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result;
}
#endif
#endif

View File

@ -9,6 +9,7 @@ enum cannedMessageModuleRunState {
CANNED_MESSAGE_RUN_STATE_ACTIVE,
CANNED_MESSAGE_RUN_STATE_FREETEXT,
CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE,
CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED,
CANNED_MESSAGE_RUN_STATE_ACTION_SELECT,
CANNED_MESSAGE_RUN_STATE_ACTION_UP,
CANNED_MESSAGE_RUN_STATE_ACTION_DOWN,
@ -37,15 +38,29 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
const char *getMessageByIndex(int index);
const char *getNodeName(NodeNum node);
bool shouldDraw();
void eventUp();
void eventDown();
void eventSelect();
// void eventUp();
// void eventDown();
// void eventSelect();
void handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response);
void handleSetCannedMessageModuleMessages(const char *from_msg);
String drawWithCursor(String text, int cursor);
/*
-Override the wantPacket method. We need the Routing Messages to look for ACKs.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP:
return true;
default:
return false;
}
}
protected:
virtual int32_t runOnce() override;
@ -63,6 +78,12 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
/** Called to handle a particular incoming message
* @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered
* for it
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
void loadProtoForModule();
bool saveProtoForModule();
@ -75,6 +96,8 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
String freetext = ""; // Text Buffer for Freetext Editor
bool destSelect = false; // Freetext Editor Mode
NodeNum dest = NODENUM_BROADCAST;
NodeNum incoming = NODENUM_BROADCAST;
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE + 1];
char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT];
@ -83,4 +106,4 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
};
extern CannedMessageModule *cannedMessageModule;
#endif
#endif