Qualify RAK repeater RAM and retire stale channel-busy waits
This commit is contained in:
1 parent
e34e83e6b4
commit
adb11e7b41
18 files changed
+535
-110
No files matched your search
+32
-4
@@ -96,6 +96,8 @@ void Dispatcher::begin() {
|
||||
n_recv_flood = n_recv_direct = 0;
|
||||
last_meshcore_recv_millis = 0;
|
||||
_err_flags = 0;
|
||||
cad_busy_start = 0;
|
||||
profile_cad_busy[0] = profile_cad_busy[1] = 0;
|
||||
outbound_radio_retry_at = 0;
|
||||
outbound_radio_prepare_deadline = 0;
|
||||
outbound_radio_retry_pending = false;
|
||||
@@ -329,6 +331,7 @@ bool Dispatcher::queueDirectOtaRadioFrame(const uint8_t* message, uint16_t lengt
|
||||
const uint16_t encoded = ota::ota_direct_wire_encode(
|
||||
foreign_radio_bytes, sizeof(foreign_radio_bytes), message, length);
|
||||
if (!encoded) return false;
|
||||
retireUnusedCADWait(profile);
|
||||
foreign_radio_length = encoded;
|
||||
foreign_radio_profile = profile;
|
||||
foreign_radio_protocol = RadioProfileProtocol::MeshCore;
|
||||
@@ -353,6 +356,7 @@ bool Dispatcher::queueForeignRadioFrame(const uint8_t* bytes, size_t length,
|
||||
|| !profiles || profile != 1 || !profiles->enabled() || !profiles->canTransmit(profile)
|
||||
|| profiles->params(profile).protocol != protocol
|
||||
|| generation != profiles->generation[profile]) return false;
|
||||
retireUnusedCADWait(profile);
|
||||
memcpy(foreign_radio_bytes, bytes, length);
|
||||
foreign_radio_length = (uint16_t)length;
|
||||
foreign_radio_profile = profile;
|
||||
@@ -1382,6 +1386,30 @@ void Dispatcher::releasePacket(Packet* packet) {
|
||||
_mgr->free(packet);
|
||||
}
|
||||
|
||||
void Dispatcher::retireUnusedCADWait(uint8_t profile) {
|
||||
if (profile_cad_busy[profile] && !(outbound && outbound->radio_profile == profile)
|
||||
#if MESH_ENABLE_FOREIGN_RADIO
|
||||
&& !(hasForeignRadioFrame() && foreign_radio_profile == profile)
|
||||
#endif
|
||||
) {
|
||||
// Cancellation/echo suppression can retire the last packet for a profile
|
||||
// without observing a free channel. Its old age must not force a later,
|
||||
// unrelated packet to transmit immediately with a false timeout. Check
|
||||
// only on enqueue with an aged wait, never during the idle/sleep loop.
|
||||
bool waiting = false;
|
||||
for (int i = 0, count = _mgr->getOutboundTotal(); i < count; ++i) {
|
||||
const auto* queued = _mgr->getOutboundByIdx(i);
|
||||
if (queued && queued->radio_profile == profile) { waiting = true; break; }
|
||||
}
|
||||
if (!waiting) profile_cad_busy[profile] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dispatcher::queueTransmitCandidate(Packet* packet, uint8_t priority, uint32_t scheduled_for) {
|
||||
retireUnusedCADWait(packet->radio_profile);
|
||||
return _mgr->queueOutbound(packet, priority, scheduled_for);
|
||||
}
|
||||
|
||||
bool Dispatcher::queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t delay_millis) {
|
||||
if (packet->violatesRoutePolicy()) {
|
||||
return false;
|
||||
@@ -1399,13 +1427,13 @@ bool Dispatcher::queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t
|
||||
if (profiles == nullptr) {
|
||||
if (packet->tx_radio != RADIO_TX_AUTO
|
||||
&& !(explicitRadioTxMask(packet->tx_radio, false) & 1)) return false;
|
||||
return _mgr->queueOutbound(packet, priority, futureMillis(delay_millis));
|
||||
return queueTransmitCandidate(packet, priority, futureMillis(delay_millis));
|
||||
}
|
||||
if (packet->radio_bound) {
|
||||
if (!isPacketRadioCurrent(packet)) return false;
|
||||
if (profiles && packet->radio_profile != packet->radio_origin
|
||||
&& !allowRadioProfileCross(packet)) return false;
|
||||
return _mgr->queueOutbound(packet, priority, futureMillis(delay_millis));
|
||||
return queueTransmitCandidate(packet, priority, futureMillis(delay_millis));
|
||||
}
|
||||
if (!packet->radio_local && packet->radio_generation
|
||||
&& packet->radio_generation != profiles->generation[packet->radio_profile]) return false;
|
||||
@@ -1436,7 +1464,7 @@ bool Dispatcher::queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t
|
||||
}
|
||||
packet->radio_generation = profiles->generation[packet->radio_profile];
|
||||
packet->radio_bound = true;
|
||||
if (!_mgr->queueOutbound(packet, priority, futureMillis(delay_millis))) return false;
|
||||
if (!queueTransmitCandidate(packet, priority, futureMillis(delay_millis))) return false;
|
||||
if (packet->radio_profile != origin) onRadioProfileCopyQueued(packet, nullptr, priority);
|
||||
const uint8_t other = packet->radio_profile ^ 1;
|
||||
if (mask & (1U << other)) {
|
||||
@@ -1445,7 +1473,7 @@ bool Dispatcher::queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t
|
||||
*copy = *packet;
|
||||
copy->radio_profile = other;
|
||||
copy->radio_generation = profiles->generation[other];
|
||||
if (_mgr->queueOutbound(copy, priority, futureMillis(delay_millis))) {
|
||||
if (queueTransmitCandidate(copy, priority, futureMillis(delay_millis))) {
|
||||
onRadioProfileCopyQueued(copy, packet, priority);
|
||||
onTracePacketQueuedForSend(copy);
|
||||
} else {
|
||||
|
||||
@@ -723,6 +723,8 @@ public:
|
||||
unsigned long futureMillis(int millis_from_now) const;
|
||||
|
||||
private:
|
||||
void retireUnusedCADWait(uint8_t profile);
|
||||
bool queueTransmitCandidate(Packet* packet, uint8_t priority, uint32_t scheduled_for);
|
||||
// Keep the frequently accessed core fields at compact ARM load offsets.
|
||||
AGCResetScheduler agc_reset;
|
||||
uint32_t receive_context_generation = 0;
|
||||
|
||||
@@ -68,7 +68,7 @@ bool sameRuleConfiguration(const Entry& left, const Entry& right) {
|
||||
&& left.min_hops == right.min_hops
|
||||
&& left.max_hops == right.max_hops
|
||||
&& left.suspend_on_temp_radio == right.suspend_on_temp_radio
|
||||
&& memcmp(left.scope_name, right.scope_name, sizeof(left.scope_name)) == 0
|
||||
&& memcmp(FloodRuleTargets::scopeName(left), FloodRuleTargets::scopeName(right), sizeof(FloodRuleTargets::scopeName(left))) == 0
|
||||
&& left.match_blacklisted_path == right.match_blacklisted_path
|
||||
&& left.scope_uses_slow_timing == right.scope_uses_slow_timing
|
||||
&& left.incoming_scope_kind == right.incoming_scope_kind
|
||||
@@ -82,8 +82,8 @@ bool sameRuleConfiguration(const Entry& left, const Entry& right) {
|
||||
&& left.path_hash_size == right.path_hash_size
|
||||
&& left.path_hops == right.path_hops
|
||||
&& memcmp(left.path, right.path, sizeof(left.path)) == 0
|
||||
&& memcmp(left.target_region_name, right.target_region_name,
|
||||
sizeof(left.target_region_name)) == 0
|
||||
&& memcmp(FloodRuleTargets::regionName(left), FloodRuleTargets::regionName(right),
|
||||
sizeof(FloodRuleTargets::regionName(left))) == 0
|
||||
&& left.drop_on_match == right.drop_on_match
|
||||
&& left.rate_limit_enabled == right.rate_limit_enabled
|
||||
&& left.rate_per_minute == right.rate_per_minute
|
||||
|
||||
@@ -117,8 +117,8 @@ bool formatCompact(char* out, size_t capacity, unsigned slot, const Entry& entry
|
||||
default: break;
|
||||
}
|
||||
if (entry.drop_on_match) text.append(" d");
|
||||
else if (entry.scope_name[0]) text.append(" s=%s", entry.scope_name);
|
||||
else if (entry.target_region_name[0]) text.append(" r=%s", entry.target_region_name);
|
||||
else if (FloodRuleTargets::scopeName(entry)[0]) text.append(" s=%s", FloodRuleTargets::scopeName(entry));
|
||||
else if (FloodRuleTargets::regionName(entry)[0]) text.append(" r=%s", FloodRuleTargets::regionName(entry));
|
||||
if (entry.rate_limit_enabled) text.append(" q=%u", (unsigned)entry.rate_per_minute);
|
||||
if (entry.priority) text.append(" pri=%u", (unsigned)entry.priority);
|
||||
if (entry.stop_on_match) text.append(" s");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <Packet.h>
|
||||
#include "FloodRuleTargets.h"
|
||||
#include "TxtDataHelpers.h"
|
||||
#include "FloodTextPattern.h"
|
||||
#include "FloodGroupPlainText.h"
|
||||
@@ -80,7 +81,7 @@ inline bool validRuleFields(const Entry& entry) {
|
||||
const bool content_valid = !contentRequested(entry.extra)
|
||||
|| ((entry.channel_key_len == 16 || entry.channel_key_len == 32)
|
||||
&& (entry.payload_type == 0xFF || entry.payload_type == PAYLOAD_TYPE_GRP_TXT));
|
||||
const bool flood_actions = entry.scope_name[0] || entry.target_region_name[0]
|
||||
const bool flood_actions = FloodRuleTargets::scopeName(entry)[0] || FloodRuleTargets::regionName(entry)[0]
|
||||
|| entry.retry_on_match || entry.scope_uses_slow_timing;
|
||||
return path_valid && content_valid && (!flood_actions
|
||||
|| entry.extra.route == LegacyRoute || entry.extra.route == FloodRoute);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
// Rule targets are mutually exclusive. The repeater stores one tagged name;
|
||||
// the room engine and portable policy users retain their existing fields.
|
||||
// Preserve array references so explicit FPF7 serialization keeps both fixed
|
||||
// width fields, including the zero-filled inactive name.
|
||||
namespace FloodRuleTargets {
|
||||
template<typename Entry>
|
||||
auto scopeNameImpl(const Entry& entry, int) -> decltype(entry.scopeName()) {
|
||||
return entry.scopeName();
|
||||
}
|
||||
template<typename Entry>
|
||||
auto scopeNameImpl(const Entry& entry, long) -> decltype((entry.scope_name)) {
|
||||
return entry.scope_name;
|
||||
}
|
||||
template<typename Entry>
|
||||
auto regionNameImpl(const Entry& entry, int) -> decltype(entry.regionName()) {
|
||||
return entry.regionName();
|
||||
}
|
||||
template<typename Entry>
|
||||
auto regionNameImpl(const Entry& entry, long) -> decltype((entry.target_region_name)) {
|
||||
return entry.target_region_name;
|
||||
}
|
||||
template<typename Entry>
|
||||
auto scopeName(const Entry& entry) -> decltype(scopeNameImpl(entry, 0)) {
|
||||
return scopeNameImpl(entry, 0);
|
||||
}
|
||||
template<typename Entry>
|
||||
auto regionName(const Entry& entry) -> decltype(regionNameImpl(entry, 0)) {
|
||||
return regionNameImpl(entry, 0);
|
||||
}
|
||||
} // namespace FloodRuleTargets
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "OLEDControllerSettings.h"
|
||||
#endif
|
||||
|
||||
#if defined(ESP32_PLATFORM) || (defined(NRF52_PLATFORM) && defined(RAK4631_COMBINED_ETHERNET))
|
||||
#if defined(ESP32_PLATFORM) || defined(NRF52_PLATFORM)
|
||||
// Adafruit_SSD1306::begin allocates WIDTH * ((HEIGHT + 7) / 8) once and
|
||||
// reuses that framebuffer on later begin calls. The driver itself is embedded
|
||||
// in the linked global display, not allocated on the heap. Even including
|
||||
|
||||
Reference in new issue
Block a user