Kaynağa Gözat

rate limit state flushes; add debug

Chris Mullins 8 yıl önce
ebeveyn
işleme
46aa12f217

+ 33 - 5
lib/MiLightState/GroupStateStore.cpp

@@ -1,8 +1,10 @@
 #include <GroupStateStore.h>
 #include <MiLightRemoteConfig.h>
 
-GroupStateStore::GroupStateStore(const size_t maxSize)
-  : cache(GroupStateCache(maxSize))
+GroupStateStore::GroupStateStore(const size_t maxSize, const size_t flushRate)
+  : cache(GroupStateCache(maxSize)),
+    flushRate(flushRate),
+    lastFlush(0)
 { }
 
 GroupState& GroupStateStore::get(const BulbId& id) {
@@ -42,16 +44,42 @@ void GroupStateStore::trackEviction() {
   }
 }
 
-void GroupStateStore::flush() {
+bool GroupStateStore::flush() {
   ListNode<GroupCacheNode*>* curr = cache.getHead();
+  bool anythingFlushed = false;
 
-  while (curr != NULL && curr->data->state.isDirty()) {
+  while (curr != NULL && curr->data->state.isDirty() && !anythingFlushed) {
     persistence.set(curr->data->id, curr->data->state);
     curr->data->state.clearDirty();
+
+#ifdef STATE_DEBUG
+    BulbId bulbId = curr->data->id;
+    printf(
+      "Flushing dirty state for 0x%04X / %d / %s\n",
+      bulbId.deviceId,
+      bulbId.groupId,
+      MiLightRemoteConfig::fromType(bulbId.deviceType)->name.c_str()
+    );
+#endif
+
     curr = curr->next;
+    anythingFlushed = true;
   }
 
-  while (evictedIds.size() > 0) {
+  while (evictedIds.size() > 0 && !anythingFlushed) {
     persistence.clear(evictedIds.shift());
+    anythingFlushed = true;
+  }
+
+  return anythingFlushed;
+}
+
+void GroupStateStore::limitedFlush() {
+  unsigned long now = millis();
+
+  if ((lastFlush + flushRate) < now) {
+    if (flush()) {
+      lastFlush = now;
+    }
   }
 }

+ 23 - 2
lib/MiLightState/GroupStateStore.h

@@ -7,17 +7,38 @@
 
 class GroupStateStore {
 public:
-  GroupStateStore(const size_t maxSize);
+  GroupStateStore(const size_t maxSize, const size_t flushRate);
 
+  /*
+   * Returns the state for the given BulbId.  If no state exists, a suitable
+   * default state will be returned.
+   */
   GroupState& get(const BulbId& id);
+
+  /*
+   * Sets the state for the given BulbId.  State will be marked as dirty and
+   * flushed to persistent storage.
+   */
   GroupState& set(const BulbId& id, const GroupState& state);
 
-  void flush();
+  /*
+   * Flushes all states to persistent storage.  Returns true iff anything was
+   * flushed.
+   */
+  bool flush();
+
+  /*
+   * Flushes at most one dirty state to persistent storage.  Rate limit
+   * specified by Settings.
+   */
+  void limitedFlush();
 
 private:
   GroupStateCache cache;
   GroupStatePersistence persistence;
   LinkedList<BulbId> evictedIds;
+  const size_t flushRate;
+  unsigned long lastFlush;
 
   void trackEviction();
 };

+ 1 - 1
lib/Settings/Settings.h

@@ -73,7 +73,7 @@ public:
     listenRepeats(3),
     _autoRestartPeriod(0),
     discoveryPort(48899),
-    stateFlushInterval(10),
+    stateFlushInterval(1000),
     mqttStateRateLimit(500)
   { }
 

+ 2 - 1
platformio.ini

@@ -23,10 +23,11 @@ lib_deps_external =
   CircularBuffer
 extra_scripts =
   pre:.build_web.py
-build_flags = !python .get_version.py -DMQTT_MAX_PACKET_SIZE=200 -Idist -Ilib/DataStructures 
+build_flags = !python .get_version.py -DMQTT_MAX_PACKET_SIZE=200 -Idist -Ilib/DataStructures
 # -D DEBUG_PRINTF
 # -D MQTT_DEBUG
 # -D MILIGHT_UDP_DEBUG
+# -D STATE_DEBUG
 
 [env:nodemcuv2]
 platform = espressif8266

+ 15 - 22
src/main.cpp

@@ -32,12 +32,11 @@ MiLightDiscoveryServer* discoveryServer = NULL;
 uint8_t currentRadioType = 0;
 
 // For tracking and managing group state
-GroupStateStore stateStore(MILIGHT_MAX_STATE_ITEMS);
-BulbStateUpdater* bulbStateUpdater;
-size_t lastFlush = 0;
+GroupStateStore* stateStore = NULL;
+BulbStateUpdater* bulbStateUpdater = NULL;
 
 int numUdpServers = 0;
-MiLightUdpServer** udpServers;
+MiLightUdpServer** udpServers = NULL;
 WiFiUDP udpSeder;
 
 /**
@@ -85,7 +84,7 @@ void initMilightUdpServers() {
 void onPacketSentHandler(uint8_t* packet, const MiLightRemoteConfig& config) {
   StaticJsonBuffer<200> buffer;
   JsonObject& result = buffer.createObject();
-  BulbId bulbId = config.packetFormatter->parsePacket(packet, result, &stateStore);
+  BulbId bulbId = config.packetFormatter->parsePacket(packet, result, stateStore);
 
   if (&bulbId == &DEFAULT_BULB_ID) {
     Serial.println(F("Skipping packet handler because packet was not decoded"));
@@ -96,9 +95,9 @@ void onPacketSentHandler(uint8_t* packet, const MiLightRemoteConfig& config) {
     *MiLightRemoteConfig::fromType(bulbId.deviceType);
 
   if (mqttClient) {
-    GroupState& groupState = stateStore.get(bulbId);
+    GroupState& groupState = stateStore->get(bulbId);
     groupState.patch(result);
-    stateStore.set(bulbId, groupState);
+    stateStore->set(bulbId, groupState);
 
     // Sends the state delta derived from the raw packet
     char output[200];
@@ -158,6 +157,9 @@ void applySettings() {
     delete mqttClient;
     delete bulbStateUpdater;
   }
+  if (stateStore) {
+    delete stateStore;
+  }
 
   radioFactory = MiLightRadioFactory::fromSettings(settings);
 
@@ -165,7 +167,9 @@ void applySettings() {
     Serial.println(F("ERROR: unable to construct radio factory"));
   }
 
-  milightClient = new MiLightClient(radioFactory, stateStore);
+  stateStore = new GroupStateStore(MILIGHT_MAX_STATE_ITEMS, settings.stateFlushInterval);
+
+  milightClient = new MiLightClient(radioFactory, *stateStore);
   milightClient->begin();
   milightClient->onPacketSent(onPacketSentHandler);
   milightClient->setResendCount(settings.packetRepeats);
@@ -173,7 +177,7 @@ void applySettings() {
   if (settings.mqttServer().length() > 0) {
     mqttClient = new MqttClient(settings, milightClient);
     mqttClient->begin();
-    bulbStateUpdater = new BulbStateUpdater(settings, *mqttClient, stateStore);
+    bulbStateUpdater = new BulbStateUpdater(settings, *mqttClient, *stateStore);
   }
 
   initMilightUdpServers();
@@ -199,14 +203,6 @@ bool shouldRestart() {
   return settings.getAutoRestartPeriod()*60*1000 < millis();
 }
 
-/**
- * Checks if group states should be flushed from memory onto flash
- */
-bool shouldFlush() {
-  return ((lastFlush + (settings.stateFlushInterval*1000) < millis())
-    || lastFlush > millis()); // in case millis() wraps
-}
-
 void setup() {
   Serial.begin(9600);
   String ssid = "ESP" + String(ESP.getChipId());
@@ -229,7 +225,7 @@ void setup() {
   SSDP.setDeviceType("upnp:rootdevice");
   SSDP.begin();
 
-  httpServer = new MiLightHttpServer(settings, milightClient, stateStore);
+  httpServer = new MiLightHttpServer(settings, milightClient, *stateStore);
   httpServer->onSettingsSaved(applySettings);
   httpServer->on("/description.xml", HTTP_GET, []() { SSDP.schema(httpServer->client()); });
   httpServer->begin();
@@ -257,10 +253,7 @@ void loop() {
 
   handleListen();
 
-  if (shouldFlush()) {
-    stateStore.flush();
-    lastFlush = millis();
-  }
+  stateStore->limitedFlush();
 
   if (shouldRestart()) {
     Serial.println(F("Auto-restart triggered. Restarting..."));