## Summary * **What is the goal of this PR?** Fix an infinite render loop bug in CalibreConnectActivity that caused the e-ink display to refresh continuously every ~421ms after a file transfer completed. * **What changes are included?** - Added lastProcessedCompleteAt member variable to track which server completion timestamp has already been processed - Modified the completion status update logic to only accept new values from the server, preventing re-processing of old timestamps - Added clarifying comments explaining the fix ## Problem Description After receiving a file via Calibre Wireless, the activity displays "Received: [filename]" for 6 seconds, then clears the message. However, the web server's wsLastCompleteAt timestamp persists indefinitely and is never cleared. This created a race condition: After 6 seconds, lastCompleteAt is set to 0 (timeout) In the next loop iteration, status.lastCompleteAt (still has the old timestamp) ≠ lastCompleteAt (0) The code restores lastCompleteAt from the server value Immediately, the 6-second timeout condition is met again This creates an infinite cycle causing unnecessary e-ink refreshes ## Solution The fix introduces lastProcessedCompleteAt to track which server timestamp value has already been processed: Only accept a new status.lastCompleteAt if it differs from lastProcessedCompleteAt Update lastProcessedCompleteAt when processing a new value Do NOT reset lastProcessedCompleteAt when the 6-second timeout clears lastCompleteAt This prevents re-processing the same old server value after the timeout. ## Testing Tested on device with multiple file transfer scenarios: ✅ File received message appears correctly after transfer ✅ Message clears after 6 seconds as expected ✅ No infinite render loop after timeout ✅ Multiple consecutive transfers work correctly ✅ Exiting and re-entering Calibre Wireless works as expected ## Performance Impact Before: Infinite refreshes every ~421ms after timeout (high battery drain, display wear) After: 2-3 refreshes after timeout, then stops (normal behavior) ## Additional Context This is a targeted fix that only affects the Calibre Wireless file transfer screen. The root cause is the architectural difference between the persistent web server state (wsLastCompleteAt) and the per-activity display state (lastCompleteAt). An alternative fix would be to clear wsLastCompleteAt in the web server after some timeout, but that would affect all consumers of the web server status. The chosen solution keeps the fix localized to CalibreConnectActivity. --- ### AI Usage Did you use AI tools to help write this code? _**< YES >**_
49 lines
1.7 KiB
C++
49 lines
1.7 KiB
C++
#pragma once
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "activities/ActivityWithSubactivity.h"
|
|
#include "network/CrossPointWebServer.h"
|
|
|
|
enum class CalibreConnectState { WIFI_SELECTION, SERVER_STARTING, SERVER_RUNNING, ERROR };
|
|
|
|
/**
|
|
* CalibreConnectActivity starts the file transfer server in STA mode,
|
|
* but renders Calibre-specific instructions instead of the web transfer UI.
|
|
*/
|
|
class CalibreConnectActivity final : public ActivityWithSubactivity {
|
|
CalibreConnectState state = CalibreConnectState::WIFI_SELECTION;
|
|
const std::function<void()> onComplete;
|
|
|
|
std::unique_ptr<CrossPointWebServer> webServer;
|
|
std::string connectedIP;
|
|
std::string connectedSSID;
|
|
unsigned long lastHandleClientTime = 0;
|
|
size_t lastProgressReceived = 0;
|
|
size_t lastProgressTotal = 0;
|
|
std::string currentUploadName;
|
|
std::string lastCompleteName;
|
|
unsigned long lastCompleteAt = 0;
|
|
unsigned long lastProcessedCompleteAt = 0; // Track which server value we've already processed
|
|
bool exitRequested = false;
|
|
|
|
void renderServerRunning() const;
|
|
|
|
void onWifiSelectionComplete(bool connected);
|
|
void startWebServer();
|
|
void stopWebServer();
|
|
|
|
public:
|
|
explicit CalibreConnectActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
|
const std::function<void()>& onComplete)
|
|
: ActivityWithSubactivity("CalibreConnect", renderer, mappedInput), onComplete(onComplete) {}
|
|
void onEnter() override;
|
|
void onExit() override;
|
|
void loop() override;
|
|
void render(Activity::RenderLock&&) override;
|
|
bool skipLoopDelay() override { return webServer && webServer->isRunning(); }
|
|
bool preventAutoSleep() override { return webServer && webServer->isRunning(); }
|
|
};
|