mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-23 13:33:03 +00:00
Pause messaging while route images download
This commit is contained in:
1 parent
5c1a99fd49
commit
ca6dac655f
4 files changed
+219
-2
No files matched your search
@@ -102,6 +102,7 @@ Trail Mate is a low-frequency terminal, not a desktop router node.
|
||||
| Screen on | Connected messaging is allowed with normal terminal budget. | Direct gateway traffic is allowed with terminal budget. | User foreground work is allowed; background work is deferred. |
|
||||
| Wake protected | Existing sockets may use minimal budget. New reconnects and HTTP starts are deferred. | Existing sockets may use minimal budget. New reconnects are deferred. | Deferred. |
|
||||
| Screen off | MQTT may connect and normally send/receive. | Gateway may connect and receive with expanded terminal budget. | Queued maintenance/background work may run, one transaction at a time. |
|
||||
| Foreground HTTP pending | Suspended. No connect, read, write, publish, or downlink bridge budget. | Suspended. Existing gateway socket may be closed by its budget handler. | The pending foreground download owns the next HTTP opportunity while it waits for memory/SD/UI resources. |
|
||||
| HTTP active | MQTT and Reticulum are reduced to minimal messaging budget. | Reduced to minimal gateway budget. | Exactly one HTTP transaction owns the HTTP resource. |
|
||||
| OTA active | Protocol pump budget is suspended. | Protocol pump budget is suspended. | OTA is exclusive. |
|
||||
| Reticulum call active | Suspended. MQTT sockets must not connect, pump, publish, or consume RX budget. | Exclusive `call.audio` link traffic only. Discovery, public announce ingest, route/path background noise, and non-call link work are deferred. | Denied. No HTTP, OTA, catalog, pack, language, image, or route download may start. |
|
||||
|
||||
@@ -95,16 +95,22 @@ struct RuntimeStatus
|
||||
{
|
||||
bool http_active = false;
|
||||
bool ota_active = false;
|
||||
bool foreground_download_pending = false;
|
||||
Client owner = Client::Unknown;
|
||||
AccessKind active_kind = AccessKind::WifiConnect;
|
||||
Client pending_owner = Client::Unknown;
|
||||
AccessKind pending_kind = AccessKind::WifiConnect;
|
||||
ScreenPhase screen_phase = ScreenPhase::Unknown;
|
||||
std::uint32_t active_since_ms = 0;
|
||||
std::uint32_t pending_since_ms = 0;
|
||||
std::uint32_t wake_protected_until_ms = 0;
|
||||
};
|
||||
|
||||
bool ensure_connected(const Request& request, Decision* out_decision = nullptr);
|
||||
Lease acquire(const Request& request);
|
||||
void release(const Lease& lease);
|
||||
bool begin_foreground_download(const Request& request, Decision* out_decision = nullptr);
|
||||
void end_foreground_download(Client client, AccessKind kind);
|
||||
TrafficBudget traffic_budget(Client client, Priority priority);
|
||||
RuntimeStatus status();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
|
||||
#include "platform/ui/device_runtime.h"
|
||||
#include "platform/ui/http_client_runtime.h"
|
||||
#include "platform/ui/wifi_access_runtime.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
@@ -41,7 +42,7 @@ constexpr uint16_t kRouteViewHeight = 180;
|
||||
constexpr std::size_t kRouteCacheInternalReserveBytes = 96 * 1024;
|
||||
constexpr std::size_t kRouteCacheInternalSlackBytes = 8 * 1024;
|
||||
constexpr std::size_t kRouteJpegDecoderWorkBytes = 3100;
|
||||
constexpr std::size_t kRouteHttpInternalFreeTargetBytes = 24 * 1024;
|
||||
constexpr std::size_t kRouteHttpInternalFreeTargetBytes = 18 * 1024;
|
||||
constexpr std::size_t kRouteHttpInternalLargestTargetBytes = 8 * 1024;
|
||||
constexpr std::size_t kRouteImageDownloadAttempts = 2;
|
||||
constexpr int kRouteImageMemoryWaitAttempts = 16;
|
||||
@@ -892,6 +893,58 @@ struct RouteImageProgressContext
|
||||
TickType_t last_tick = 0;
|
||||
};
|
||||
|
||||
class RouteImageForegroundDownloadScope
|
||||
{
|
||||
public:
|
||||
RouteImageForegroundDownloadScope() = default;
|
||||
~RouteImageForegroundDownloadScope()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
RouteImageForegroundDownloadScope(const RouteImageForegroundDownloadScope&) = delete;
|
||||
RouteImageForegroundDownloadScope& operator=(const RouteImageForegroundDownloadScope&) = delete;
|
||||
|
||||
void begin()
|
||||
{
|
||||
if (active_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
platform::ui::wifi_access::Request request{};
|
||||
request.client = platform::ui::wifi_access::Client::RouteStorage;
|
||||
request.kind = platform::ui::wifi_access::AccessKind::HttpDownload;
|
||||
request.priority = platform::ui::wifi_access::Priority::UserForeground;
|
||||
request.reason = "route_image_pending";
|
||||
platform::ui::wifi_access::Decision decision =
|
||||
platform::ui::wifi_access::Decision::Granted;
|
||||
active_ = platform::ui::wifi_access::begin_foreground_download(
|
||||
request,
|
||||
&decision);
|
||||
if (!active_)
|
||||
{
|
||||
std::printf("[RouteImage][wifi] pending denied decision=%s\n",
|
||||
platform::ui::wifi_access::decision_name(decision));
|
||||
}
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
if (!active_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
platform::ui::wifi_access::end_foreground_download(
|
||||
platform::ui::wifi_access::Client::RouteStorage,
|
||||
platform::ui::wifi_access::AccessKind::HttpDownload);
|
||||
active_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool active_ = false;
|
||||
};
|
||||
|
||||
void route_image_download_progress(std::uint32_t current_bytes,
|
||||
std::uint32_t total_bytes,
|
||||
void* context)
|
||||
@@ -1093,6 +1146,8 @@ void route_image_worker_task(void* param)
|
||||
}
|
||||
else
|
||||
{
|
||||
RouteImageForegroundDownloadScope foreground_download;
|
||||
foreground_download.begin();
|
||||
for (std::size_t index = 0; index < total;)
|
||||
{
|
||||
const RouteImageDownloadItem& item = download_items[index];
|
||||
|
||||
@@ -25,9 +25,13 @@ struct RuntimeState
|
||||
{
|
||||
bool http_active = false;
|
||||
bool ota_active = false;
|
||||
bool foreground_download_pending = false;
|
||||
Client owner = Client::Unknown;
|
||||
AccessKind active_kind = AccessKind::WifiConnect;
|
||||
Client pending_owner = Client::Unknown;
|
||||
AccessKind pending_kind = AccessKind::WifiConnect;
|
||||
std::uint32_t active_since_ms = 0;
|
||||
std::uint32_t pending_since_ms = 0;
|
||||
std::uint32_t foreground_download_settle_until_ms = 0;
|
||||
std::uint32_t wake_protected_until_ms = 0;
|
||||
bool saw_screen_sample = false;
|
||||
@@ -135,6 +139,18 @@ bool foreground_download_active_for_other(const Request& request)
|
||||
return active;
|
||||
}
|
||||
|
||||
bool foreground_download_pending_for_other(const Request& request)
|
||||
{
|
||||
bool pending = false;
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
pending = s_state.foreground_download_pending &&
|
||||
foreground_download_owner(s_state.pending_owner) &&
|
||||
!(s_state.pending_owner == request.client &&
|
||||
s_state.pending_kind == request.kind);
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
return pending;
|
||||
}
|
||||
|
||||
bool foreground_download_settle_active_for(Client client, std::uint32_t now_ms)
|
||||
{
|
||||
if (!messaging_client(client))
|
||||
@@ -175,6 +191,15 @@ bool acquire_http(const Request& request, ScreenPhase phase, Lease& out)
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
return false;
|
||||
}
|
||||
if (s_state.foreground_download_pending &&
|
||||
foreground_download_owner(s_state.pending_owner) &&
|
||||
!(s_state.pending_owner == request.client &&
|
||||
s_state.pending_kind == request.kind))
|
||||
{
|
||||
out.decision = Decision::Busy;
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
return false;
|
||||
}
|
||||
s_state.http_active = true;
|
||||
s_state.ota_active = request.kind == AccessKind::OtaDownload;
|
||||
s_state.owner = request.client;
|
||||
@@ -220,6 +245,12 @@ bool long_lived_allowed(const Request& request, ScreenPhase phase, Lease& out)
|
||||
out.decision = Decision::Busy;
|
||||
return false;
|
||||
}
|
||||
if (foreground_download_pending_for_other(request) &&
|
||||
messaging_client(request.client))
|
||||
{
|
||||
out.decision = Decision::Busy;
|
||||
return false;
|
||||
}
|
||||
if (foreground_download_settle_active_for(request.client, sys::millis_now()))
|
||||
{
|
||||
out.decision = Decision::Busy;
|
||||
@@ -310,6 +341,11 @@ bool ensure_connected(const Request& request, Decision* out_decision)
|
||||
{
|
||||
decision = Decision::Busy;
|
||||
}
|
||||
else if (foreground_download_pending_for_other(request) &&
|
||||
messaging_client(request.client))
|
||||
{
|
||||
decision = Decision::Busy;
|
||||
}
|
||||
else if (foreground_download_settle_active_for(request.client, now_ms))
|
||||
{
|
||||
decision = Decision::Busy;
|
||||
@@ -445,6 +481,117 @@ void release(const Lease& lease)
|
||||
static_cast<unsigned long>(held_ms));
|
||||
}
|
||||
|
||||
bool begin_foreground_download(const Request& request, Decision* out_decision)
|
||||
{
|
||||
const std::uint32_t now_ms = sys::millis_now();
|
||||
const ScreenPhase phase = sample_screen_phase(now_ms);
|
||||
Decision decision = Decision::Granted;
|
||||
|
||||
if (request.client == Client::Unknown ||
|
||||
request.kind != AccessKind::HttpDownload ||
|
||||
request.priority != Priority::UserForeground ||
|
||||
!foreground_download_owner(request.client))
|
||||
{
|
||||
decision = Decision::InvalidRequest;
|
||||
}
|
||||
else if (::platform::ui::reticulum_call::realtime_mode_active())
|
||||
{
|
||||
decision = Decision::CallExclusive;
|
||||
}
|
||||
else if (phase == ScreenPhase::WakeProtected)
|
||||
{
|
||||
decision = Decision::DeferredForWake;
|
||||
}
|
||||
else
|
||||
{
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
if (s_state.ota_active)
|
||||
{
|
||||
decision = Decision::OtaExclusive;
|
||||
}
|
||||
else if (s_state.http_active &&
|
||||
!(s_state.owner == request.client &&
|
||||
s_state.active_kind == request.kind))
|
||||
{
|
||||
decision = Decision::Busy;
|
||||
}
|
||||
else if (s_state.foreground_download_pending &&
|
||||
!(s_state.pending_owner == request.client &&
|
||||
s_state.pending_kind == request.kind))
|
||||
{
|
||||
decision = Decision::Busy;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_state.foreground_download_pending = true;
|
||||
s_state.pending_owner = request.client;
|
||||
s_state.pending_kind = request.kind;
|
||||
if (s_state.pending_since_ms == 0)
|
||||
{
|
||||
s_state.pending_since_ms = now_ms;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
}
|
||||
|
||||
if (out_decision)
|
||||
{
|
||||
*out_decision = decision;
|
||||
}
|
||||
if (decision == Decision::Granted)
|
||||
{
|
||||
std::printf("[WiFiAccess] foreground pending client=%s kind=%s phase=%s reason=%s\n",
|
||||
client_name(request.client),
|
||||
access_kind_name(request.kind),
|
||||
screen_phase_name(phase),
|
||||
request.reason ? request.reason : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf("[WiFiAccess] foreground pending denied client=%s kind=%s phase=%s decision=%s reason=%s\n",
|
||||
client_name(request.client),
|
||||
access_kind_name(request.kind),
|
||||
screen_phase_name(phase),
|
||||
decision_name(decision),
|
||||
request.reason ? request.reason : "");
|
||||
}
|
||||
return decision == Decision::Granted;
|
||||
}
|
||||
|
||||
void end_foreground_download(Client client, AccessKind kind)
|
||||
{
|
||||
const std::uint32_t now_ms = sys::millis_now();
|
||||
std::uint32_t held_ms = 0;
|
||||
bool cleared = false;
|
||||
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
if (s_state.foreground_download_pending &&
|
||||
s_state.pending_owner == client &&
|
||||
s_state.pending_kind == kind)
|
||||
{
|
||||
held_ms = s_state.pending_since_ms == 0 ? 0 : now_ms - s_state.pending_since_ms;
|
||||
s_state.foreground_download_pending = false;
|
||||
s_state.pending_owner = Client::Unknown;
|
||||
s_state.pending_kind = AccessKind::WifiConnect;
|
||||
s_state.pending_since_ms = 0;
|
||||
if (foreground_download_owner(client) && kind == AccessKind::HttpDownload)
|
||||
{
|
||||
s_state.foreground_download_settle_until_ms =
|
||||
now_ms + kForegroundDownloadSettleMs;
|
||||
}
|
||||
cleared = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
|
||||
if (cleared)
|
||||
{
|
||||
std::printf("[WiFiAccess] foreground released client=%s kind=%s held_ms=%lu\n",
|
||||
client_name(client),
|
||||
access_kind_name(kind),
|
||||
static_cast<unsigned long>(held_ms));
|
||||
}
|
||||
}
|
||||
|
||||
TrafficBudget traffic_budget(Client client, Priority priority)
|
||||
{
|
||||
(void)priority;
|
||||
@@ -457,6 +604,8 @@ TrafficBudget traffic_budget(Client client, Priority priority)
|
||||
const bool ota = s_state.ota_active;
|
||||
const Client owner = s_state.owner;
|
||||
const AccessKind active_kind = s_state.active_kind;
|
||||
const bool foreground_pending = s_state.foreground_download_pending &&
|
||||
foreground_download_owner(s_state.pending_owner);
|
||||
const std::uint32_t foreground_settle_until_ms =
|
||||
s_state.foreground_download_settle_until_ms;
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
@@ -471,7 +620,9 @@ TrafficBudget traffic_budget(Client client, Priority priority)
|
||||
static_cast<std::int32_t>(foreground_settle_until_ms - now_ms) > 0;
|
||||
|
||||
if (ota ||
|
||||
((foreground_download_active || foreground_download_settle_active) &&
|
||||
((foreground_download_active ||
|
||||
foreground_pending ||
|
||||
foreground_download_settle_active) &&
|
||||
messaging_client(client)))
|
||||
{
|
||||
budget.allow_connect = false;
|
||||
@@ -506,9 +657,13 @@ RuntimeStatus status()
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
out.http_active = s_state.http_active;
|
||||
out.ota_active = s_state.ota_active;
|
||||
out.foreground_download_pending = s_state.foreground_download_pending;
|
||||
out.owner = s_state.owner;
|
||||
out.active_kind = s_state.active_kind;
|
||||
out.pending_owner = s_state.pending_owner;
|
||||
out.pending_kind = s_state.pending_kind;
|
||||
out.active_since_ms = s_state.active_since_ms;
|
||||
out.pending_since_ms = s_state.pending_since_ms;
|
||||
out.wake_protected_until_ms = s_state.wake_protected_until_ms;
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
out.screen_phase = sample_screen_phase(now_ms);
|
||||
|
||||
Reference in new issue
Block a user