diff --git a/src/ESPUI.cpp b/src/ESPUI.cpp index f67c528..8fdb806 100644 --- a/src/ESPUI.cpp +++ b/src/ESPUI.cpp @@ -601,10 +601,13 @@ void ESPUIClass::onWsEvent( // Serial.println("ESPUIClass::OnWsEvent:Create new client."); MapOfClients[client->id()] = new ESPUIclient(client); } - MapOfClients[client->id()]->onWsEvent(type, arg, data, len); - } - ClearControlUpdateFlags(); + if(MapOfClients[client->id()]->onWsEvent(type, arg, data, len)) + { + // Serial.println("ESPUIClass::OnWsEvent:notify the clients that they need to be updated."); + NotifyClients(ESPUIclient::UpdateNeeded); + } + } return; } @@ -854,11 +857,21 @@ void ESPUIClass::updateControl(Control* control, int) { return; } - // tel the control it has been updated - control->HasBeenUpdated(); + // tell the control it has been updated + control->SetControlChangedId(ESPUI.GetNextControlChangeId()); NotifyClients(ClientUpdateType_t::UpdateNeeded); } +uint32_t ESPUIClass::GetNextControlChangeId() +{ + if(uint32_t(-1) == ControlChangeID) + { + // force a reload which resets the counters + jsonReload(); + } + return ++ControlChangeID; +} + void ESPUIClass::setPanelStyle(uint16_t id, const String& style, int clientId) { Control* control = getControl(id); @@ -1126,30 +1139,6 @@ void ESPUIClass::NotifyClients(ClientUpdateType_t newState) } } -void ESPUIClass::ClearControlUpdateFlags() -{ - bool CanClearUpdateFlags = true; - - for (auto& CurrentClient : MapOfClients) - { - if (!CurrentClient.second->IsSyncronized()) - { - CanClearUpdateFlags = false; - break; - } - } - - if (CanClearUpdateFlags) - { - Control* control = controls; - while (nullptr != control) - { - control->HasBeenSynchronized(); - control = control->next; - } - } -} - void ESPUIClass::jsonReload() { for (auto& CurrentClient : MapOfClients) diff --git a/src/ESPUI.h b/src/ESPUI.h index 77e8acd..74862ba 100644 --- a/src/ESPUI.h +++ b/src/ESPUI.h @@ -193,7 +193,7 @@ public: void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false); Verbosity verbosity = Verbosity::Quiet; - + uint32_t GetNextControlChangeId(); // emulate former extended callback API by using an intermediate lambda (no deprecation) uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function callback, void* userData) { @@ -255,12 +255,12 @@ protected: #define ClientUpdateType_t ESPUIclient::ClientUpdateType_t void NotifyClients(ClientUpdateType_t newState); void NotifyClient(uint32_t WsClientId, ClientUpdateType_t newState); - void ClearControlUpdateFlags(); bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId); std::map MapOfClients; + uint32_t ControlChangeID = 0; }; extern ESPUIClass ESPUI; diff --git a/src/ESPUIclient.cpp b/src/ESPUIclient.cpp index ef24ffe..a0b6cc3 100644 --- a/src/ESPUIclient.cpp +++ b/src/ESPUIclient.cpp @@ -100,7 +100,7 @@ bool ESPUIclient::SendClientNotification(ClientUpdateType_t value) { if(!CanSend()) { - // Serial.println(F("ESPUIclient::NotifyClient")); + // Serial.println(F("ESPUIclient::SendClientNotification:CannotSend")); break; } @@ -124,42 +124,12 @@ void ESPUIclient::NotifyClient(ClientUpdateType_t newState) { SetState(newState); pCurrentFsmState->NotifyClient(); - -#ifdef OldWay - do // once - { - // Serial.println(String("ESPUIclient::NotifyClient: State: ") + String(int(newState))); - SetState(newState); - - if (HasBeenNotified) - { - // do not need to do anything - break; - } - - if(TransferIsInprogress) - { - // record that a notification was needed while we were transfering data to the client - DelayedNotification = true; - break; - } - - DelayedNotification = false; - - if (SendJsonDocToWebSocket(document)) - { - HasBeenNotified = true; - } - - } while (false); - - return HasBeenNotified; -#endif // def OldWay } // Handle Websockets Communication -void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len) +bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len) { + bool Response = false; // Serial.println(String("ESPUIclient::OnWsEvent: type: ") + String(type)); switch (type) @@ -228,21 +198,23 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t if (cmd.equals(F("uiok"))) { - // Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck")); + + // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck:")) + pCurrentFsmState->GetStateName()); pCurrentFsmState->ProcessAck(id, emptyString); break; } if (cmd.equals(F("uifragmentok"))) { + // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck"); if(!emptyString.equals(value)) { - // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:value: '")) + value + "'"); + // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck:value:'" + value + "'"); pCurrentFsmState->ProcessAck(uint16_t(-1), value); } else { - // Serial.println(F("ERROR:ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:Fragment Header is missing")); + Serial.println(F("ERROR:ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:Fragment Header is missing")); } break; } @@ -253,6 +225,7 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t break; } + // Serial.println(F("WS_EVT_DATA:Process Control")); Control* control = ESPUI.getControl(id); if (nullptr == control) { @@ -265,6 +238,8 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t break; } control->onWsEvent(cmd, value); + // notify other clients of change + Response = true; break; } @@ -274,6 +249,8 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t break; } } // end switch + + return Response; } /* @@ -361,7 +338,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, if(InUpdateMode) { // In update mode we only count the controls that have been updated. - if(control->IsUpdated()) + if(control->NeedsSync(CurrentSyncID)) { ++currentIndex; } @@ -397,7 +374,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, if(InUpdateMode && !SingleControl) { - if(control->IsUpdated()) + if(control->NeedsSync(CurrentSyncID)) { // dont skip this control } @@ -505,6 +482,8 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t Tra { // Serial.println("ESPUIclient:SendControlsToClient: Tell client we are starting a transfer of controls."); document["type"] = (ClientUpdateType_t::RebuildNeeded == TransferMode) ? UI_INITIAL_GUI : UI_EXTEND_GUI; + CurrentSyncID = NextSyncID; + NextSyncID = ESPUI.GetNextControlChangeId(); } // Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"])); diff --git a/src/ESPUIclient.h b/src/ESPUIclient.h index 42d8abf..444725b 100644 --- a/src/ESPUIclient.h +++ b/src/ESPUIclient.h @@ -49,14 +49,19 @@ protected: bool SendClientNotification(ClientUpdateType_t value); +private: + uint32_t CurrentSyncID = 0; + uint32_t NextSyncID = 0; + public: ESPUIclient(AsyncWebSocketClient * _client); ESPUIclient(const ESPUIclient & source); virtual ~ESPUIclient(); void NotifyClient(ClientUpdateType_t value); - void onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len); + bool onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len); bool IsSyncronized(); uint32_t id() { return client->id(); } void SetState(ClientUpdateType_t value); bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document); + }; diff --git a/src/ESPUIclientFsm.cpp b/src/ESPUIclientFsm.cpp index 0efb75f..fe4ca36 100644 --- a/src/ESPUIclientFsm.cpp +++ b/src/ESPUIclientFsm.cpp @@ -69,7 +69,7 @@ void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t ControlIndex, String Fragme { // This is an unexpected request for control data from the browser // treat it as if it was a rebuild operation - // Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck")); + // Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck:Error: Rebuild")); Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded); } } @@ -97,6 +97,14 @@ void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex, Stri //---------------------------------------------- //---------------------------------------------- //---------------------------------------------- +void fsm_EspuiClient_state_Rebuilding::Init() +{ + // Serial.println(String("fsm_EspuiClient_state:Init: ") + GetStateName()); + Parent->CurrentSyncID = 0; + Parent->NextSyncID = 0; + Parent->pCurrentFsmState = this; +} + bool fsm_EspuiClient_state_Rebuilding::NotifyClient() { // Serial.println(F("fsm_EspuiClient_state_Rebuilding: NotifyClient")); @@ -117,6 +125,14 @@ void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex, String //---------------------------------------------- //---------------------------------------------- //---------------------------------------------- +void fsm_EspuiClient_state_Reloading::Init() +{ + // Serial.println(String("fsm_EspuiClient_state:Init: ") + GetStateName()); + Parent->CurrentSyncID = 0; + Parent->NextSyncID = 0; + Parent->pCurrentFsmState = this; +} + void fsm_EspuiClient_state_Reloading::ProcessAck(uint16_t ControlIndex, String FragmentRequestString) { if(!emptyString.equals(FragmentRequestString)) @@ -125,3 +141,9 @@ void fsm_EspuiClient_state_Reloading::ProcessAck(uint16_t ControlIndex, String F Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequestString); } } + +bool fsm_EspuiClient_state_Reloading::NotifyClient() +{ + // Serial.println(F("fsm_EspuiClient_state_Reloading: NotifyClient")); + return true; /* Ignore request */ +} diff --git a/src/ESPUIclientFsm.h b/src/ESPUIclientFsm.h index 0b794c1..8fdbaf1 100644 --- a/src/ESPUIclientFsm.h +++ b/src/ESPUIclientFsm.h @@ -59,6 +59,7 @@ public: fsm_EspuiClient_state_Rebuilding() {} virtual ~fsm_EspuiClient_state_Rebuilding() {} + void Init(); virtual bool NotifyClient(); virtual void ProcessAck(uint16_t id, String FragmentRequest); String GetStateName() { return String(F("Sending Rebuild")); } @@ -71,7 +72,8 @@ public: fsm_EspuiClient_state_Reloading() {} virtual ~fsm_EspuiClient_state_Reloading() {} - virtual bool NotifyClient() { return false; } + void Init(); + virtual bool NotifyClient(); virtual void ProcessAck(uint16_t id, String FragmentRequest); String GetStateName() { return String(F("Reloading")); } diff --git a/src/ESPUIcontrol.cpp b/src/ESPUIcontrol.cpp index 71d84b9..75606de 100644 --- a/src/ESPUIcontrol.cpp +++ b/src/ESPUIcontrol.cpp @@ -18,6 +18,7 @@ Control::Control(ControlType type, const char* label, std::function