Skip to content

Commit

Permalink
Merge pull request #2739 from meshtastic/delivery-report
Browse files Browse the repository at this point in the history
UI/UX: Display delivered message on incoming ACK.
  • Loading branch information
caveman99 authored Dec 11, 2023
2 parents d552ee3 + d952da8 commit 3593839
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 63 deletions.
55 changes: 0 additions & 55 deletions src/mesh/MemoryPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
16 changes: 16 additions & 0 deletions src/mesh/MeshService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/mesh/MeshService.h
Original file line number Diff line number Diff line change
Expand Up @@ -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); }

Expand Down
4 changes: 4 additions & 0 deletions src/mesh/TypedQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
43 changes: 39 additions & 4 deletions src/modules/CannedMessageModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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;
Expand Down Expand Up @@ -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...");
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -650,4 +685,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result;
}

#endif
#endif
31 changes: 27 additions & 4 deletions src/modules/CannedMessageModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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();

Expand All @@ -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];
Expand All @@ -83,4 +106,4 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
};

extern CannedMessageModule *cannedMessageModule;
#endif
#endif

0 comments on commit 3593839

Please sign in to comment.