kopia lustrzana https://github.com/meshtastic/firmware
Make phone queues use a static pointer queue (#7919)
* Make phone queues use a static pointer queue * Static init * Compile time constants now * Instead, lets just use the normal pointerqueue for linux native builds and static for IoT platforms * Add missing method * Missing methods * Update variant.hpull/7936/head
rodzic
c8afbe68b5
commit
d1d16fc25f
|
@ -61,8 +61,10 @@ Allocator<meshtastic_QueueStatus> &queueStatusPool = staticQueueStatusPool;
|
|||
#include "Router.h"
|
||||
|
||||
MeshService::MeshService()
|
||||
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE),
|
||||
toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2)
|
||||
#ifdef ARCH_PORTDUINO
|
||||
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_QUEUESTATUS_TOPHONE),
|
||||
toPhoneMqttProxyQueue(MAX_RX_MQTTPROXY_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_NOTIFICATION_TOPHONE)
|
||||
#endif
|
||||
{
|
||||
lastQueueStatus = {0, 0, 16, 0};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
#include "MeshRadio.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#ifdef ARCH_PORTDUINO
|
||||
#include "PointerQueue.h"
|
||||
#else
|
||||
#include "StaticPointerQueue.h"
|
||||
#endif
|
||||
#include "mesh-pb-constants.h"
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
#include "../platform/portduino/SimRadio.h"
|
||||
#endif
|
||||
|
@ -37,16 +42,32 @@ class MeshService
|
|||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||
/// we never hang because android hasn't been there in a while
|
||||
/// FIXME - save this to flash on deep sleep
|
||||
#ifdef ARCH_PORTDUINO
|
||||
PointerQueue<meshtastic_MeshPacket> toPhoneQueue;
|
||||
#else
|
||||
StaticPointerQueue<meshtastic_MeshPacket, MAX_RX_TOPHONE> toPhoneQueue;
|
||||
#endif
|
||||
|
||||
// keep list of QueueStatus packets to be send to the phone
|
||||
#ifdef ARCH_PORTDUINO
|
||||
PointerQueue<meshtastic_QueueStatus> toPhoneQueueStatusQueue;
|
||||
#else
|
||||
StaticPointerQueue<meshtastic_QueueStatus, MAX_RX_QUEUESTATUS_TOPHONE> toPhoneQueueStatusQueue;
|
||||
#endif
|
||||
|
||||
// keep list of MqttClientProxyMessages to be send to the client for delivery
|
||||
#ifdef ARCH_PORTDUINO
|
||||
PointerQueue<meshtastic_MqttClientProxyMessage> toPhoneMqttProxyQueue;
|
||||
#else
|
||||
StaticPointerQueue<meshtastic_MqttClientProxyMessage, MAX_RX_MQTTPROXY_TOPHONE> toPhoneMqttProxyQueue;
|
||||
#endif
|
||||
|
||||
// keep list of ClientNotifications to be send to the client (phone)
|
||||
#ifdef ARCH_PORTDUINO
|
||||
PointerQueue<meshtastic_ClientNotification> toPhoneClientNotificationQueue;
|
||||
#else
|
||||
StaticPointerQueue<meshtastic_ClientNotification, MAX_RX_NOTIFICATION_TOPHONE> toPhoneClientNotificationQueue;
|
||||
#endif
|
||||
|
||||
// This holds the last QueueStatus send
|
||||
meshtastic_QueueStatus lastQueueStatus;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "freertosinc.h"
|
||||
#include <cassert>
|
||||
|
||||
/**
|
||||
* A static circular buffer queue for pointers.
|
||||
* This provides the same interface as PointerQueue but uses a statically allocated
|
||||
* buffer instead of dynamic allocation.
|
||||
*/
|
||||
template <class T, int MaxElements> class StaticPointerQueue
|
||||
{
|
||||
static_assert(MaxElements > 0, "MaxElements must be greater than 0");
|
||||
|
||||
T *buffer[MaxElements];
|
||||
int head = 0;
|
||||
int tail = 0;
|
||||
int count = 0;
|
||||
concurrency::OSThread *reader = nullptr;
|
||||
|
||||
public:
|
||||
StaticPointerQueue()
|
||||
{
|
||||
// Initialize all buffer elements to nullptr to silence warnings and ensure clean state
|
||||
for (int i = 0; i < MaxElements; i++) {
|
||||
buffer[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int numFree() const { return MaxElements - count; }
|
||||
|
||||
bool isEmpty() const { return count == 0; }
|
||||
|
||||
int numUsed() const { return count; }
|
||||
|
||||
bool enqueue(T *x, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
if (count >= MaxElements) {
|
||||
return false; // Queue is full
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
reader->setInterval(0);
|
||||
concurrency::mainDelay.interrupt();
|
||||
}
|
||||
|
||||
buffer[tail] = x;
|
||||
tail = (tail + 1) % MaxElements;
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dequeue(T **p, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
if (count == 0) {
|
||||
return false; // Queue is empty
|
||||
}
|
||||
|
||||
*p = buffer[head];
|
||||
head = (head + 1) % MaxElements;
|
||||
count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns a ptr or null if the queue was empty
|
||||
T *dequeuePtr(TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
T *p;
|
||||
return dequeue(&p, maxWait) ? p : nullptr;
|
||||
}
|
||||
|
||||
void setReader(concurrency::OSThread *t) { reader = t; }
|
||||
|
||||
// For compatibility with PointerQueue interface
|
||||
int getMaxLen() const { return MaxElements; }
|
||||
};
|
|
@ -18,6 +18,21 @@
|
|||
#define MAX_RX_TOPHONE 32
|
||||
#endif
|
||||
|
||||
/// max number of QueueStatus packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_QUEUESTATUS_TOPHONE
|
||||
#define MAX_RX_QUEUESTATUS_TOPHONE 4
|
||||
#endif
|
||||
|
||||
/// max number of MqttClientProxyMessage packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_MQTTPROXY_TOPHONE
|
||||
#define MAX_RX_MQTTPROXY_TOPHONE 32
|
||||
#endif
|
||||
|
||||
/// max number of ClientNotification packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_NOTIFICATION_TOPHONE
|
||||
#define MAX_RX_NOTIFICATION_TOPHONE 4
|
||||
#endif
|
||||
|
||||
/// Verify baseline assumption of node size. If it increases, we need to reevaluate
|
||||
/// the impact of its memory footprint, notably on MAX_NUM_NODES.
|
||||
static_assert(sizeof(meshtastic_NodeInfoLite) <= 200, "NodeInfoLite size increased. Reconsider impact on MAX_NUM_NODES.");
|
||||
|
|
|
@ -1,42 +1,105 @@
|
|||
#include "../test_helpers.h"
|
||||
#include <memory>
|
||||
|
||||
// Helper function to test common packet fields and structure
|
||||
void verify_text_message_packet_structure(const std::string &json, const char *expected_text)
|
||||
{
|
||||
TEST_ASSERT_TRUE(json.length() > 0);
|
||||
|
||||
// Use smart pointer for automatic memory management
|
||||
std::unique_ptr<JSONValue> root(JSON::Parse(json.c_str()));
|
||||
TEST_ASSERT_NOT_NULL(root.get());
|
||||
TEST_ASSERT_TRUE(root->IsObject());
|
||||
|
||||
JSONObject jsonObj = root->AsObject();
|
||||
|
||||
// Check basic packet fields - use helper function to reduce duplication
|
||||
auto check_field = [&](const char *field, uint32_t expected_value) {
|
||||
auto it = jsonObj.find(field);
|
||||
TEST_ASSERT_TRUE(it != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL(expected_value, (uint32_t)it->second->AsNumber());
|
||||
};
|
||||
|
||||
check_field("from", 0x11223344);
|
||||
check_field("to", 0x55667788);
|
||||
check_field("id", 0x9999);
|
||||
|
||||
// Check message type
|
||||
auto type_it = jsonObj.find("type");
|
||||
TEST_ASSERT_TRUE(type_it != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL_STRING("text", type_it->second->AsString().c_str());
|
||||
|
||||
// Check payload
|
||||
auto payload_it = jsonObj.find("payload");
|
||||
TEST_ASSERT_TRUE(payload_it != jsonObj.end());
|
||||
TEST_ASSERT_TRUE(payload_it->second->IsObject());
|
||||
|
||||
JSONObject payload = payload_it->second->AsObject();
|
||||
auto text_it = payload.find("text");
|
||||
TEST_ASSERT_TRUE(text_it != payload.end());
|
||||
TEST_ASSERT_EQUAL_STRING(expected_text, text_it->second->AsString().c_str());
|
||||
|
||||
// No need for manual delete with smart pointer
|
||||
}
|
||||
|
||||
// Test TEXT_MESSAGE_APP port
|
||||
void test_text_message_serialization()
|
||||
{
|
||||
const char *test_text = "Hello Meshtastic!";
|
||||
meshtastic_MeshPacket packet =
|
||||
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, (const uint8_t *)test_text, strlen(test_text));
|
||||
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, reinterpret_cast<const uint8_t *>(test_text), strlen(test_text));
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
TEST_ASSERT_TRUE(json.length() > 0);
|
||||
|
||||
JSONValue *root = JSON::Parse(json.c_str());
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
TEST_ASSERT_TRUE(root->IsObject());
|
||||
|
||||
JSONObject jsonObj = root->AsObject();
|
||||
|
||||
// Check basic packet fields
|
||||
TEST_ASSERT_TRUE(jsonObj.find("from") != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL(0x11223344, (uint32_t)jsonObj["from"]->AsNumber());
|
||||
|
||||
TEST_ASSERT_TRUE(jsonObj.find("to") != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL(0x55667788, (uint32_t)jsonObj["to"]->AsNumber());
|
||||
|
||||
TEST_ASSERT_TRUE(jsonObj.find("id") != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL(0x9999, (uint32_t)jsonObj["id"]->AsNumber());
|
||||
|
||||
// Check message type
|
||||
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
|
||||
TEST_ASSERT_EQUAL_STRING("text", jsonObj["type"]->AsString().c_str());
|
||||
|
||||
// Check payload
|
||||
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
|
||||
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
|
||||
|
||||
JSONObject payload = jsonObj["payload"]->AsObject();
|
||||
TEST_ASSERT_TRUE(payload.find("text") != payload.end());
|
||||
TEST_ASSERT_EQUAL_STRING("Hello Meshtastic!", payload["text"]->AsString().c_str());
|
||||
|
||||
delete root;
|
||||
verify_text_message_packet_structure(json, test_text);
|
||||
}
|
||||
|
||||
// Test with nullptr to check robustness
|
||||
void test_text_message_serialization_null()
|
||||
{
|
||||
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, nullptr, 0);
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
verify_text_message_packet_structure(json, "");
|
||||
}
|
||||
|
||||
// Test TEXT_MESSAGE_APP port with very long message (boundary testing)
|
||||
void test_text_message_serialization_long_text()
|
||||
{
|
||||
// Test with actual message size limits
|
||||
constexpr size_t MAX_MESSAGE_SIZE = 200; // Typical LoRa payload limit
|
||||
std::string long_text(MAX_MESSAGE_SIZE, 'A');
|
||||
|
||||
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP,
|
||||
reinterpret_cast<const uint8_t *>(long_text.c_str()), long_text.length());
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
verify_text_message_packet_structure(json, long_text.c_str());
|
||||
}
|
||||
|
||||
// Test with message over size limit (should fail)
|
||||
void test_text_message_serialization_oversized()
|
||||
{
|
||||
constexpr size_t OVERSIZED_MESSAGE = 250; // Over the limit
|
||||
std::string oversized_text(OVERSIZED_MESSAGE, 'B');
|
||||
|
||||
meshtastic_MeshPacket packet = create_test_packet(
|
||||
meshtastic_PortNum_TEXT_MESSAGE_APP, reinterpret_cast<const uint8_t *>(oversized_text.c_str()), oversized_text.length());
|
||||
|
||||
// Should fail or return empty/error
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
// Should only verify first 234 characters for oversized messages
|
||||
std::string expected_text = oversized_text.substr(0, 234);
|
||||
verify_text_message_packet_structure(json, expected_text.c_str());
|
||||
}
|
||||
|
||||
// Add test for malformed UTF-8 sequences
|
||||
void test_text_message_serialization_invalid_utf8()
|
||||
{
|
||||
const uint8_t invalid_utf8[] = {0xFF, 0xFE, 0xFD, 0x00}; // Invalid UTF-8
|
||||
meshtastic_MeshPacket packet =
|
||||
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, invalid_utf8, sizeof(invalid_utf8) - 1);
|
||||
|
||||
// Should not crash, may produce replacement characters
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
TEST_ASSERT_TRUE(json.length() > 0);
|
||||
}
|
|
@ -2,4 +2,4 @@
|
|||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||
#define HAS_GPS 1
|
||||
#define MAX_RX_TOPHONE settingsMap[maxtophone]
|
||||
#define MAX_NUM_NODES settingsMap[maxnodes]
|
||||
#define MAX_NUM_NODES settingsMap[maxnodes]
|
||||
|
|
Ładowanie…
Reference in New Issue