esp32_ethernet_milight_hub/lib/MiLightState/GroupStateStore.cpp
2021-01-15 22:49:01 +01:00

150 lines
4 KiB
C++

#include <GroupStateStore.h>
#include <MiLightRemoteConfig.h>
GroupStateStore::GroupStateStore(const size_t maxSize, const size_t flushRate)
: cache(GroupStateCache(maxSize)),
flushRate(flushRate),
lastFlush(0)
{ }
GroupState* GroupStateStore::get(const BulbId& id) {
GroupState* state = cache.get(id);
if (state == NULL) {
#if STATE_DEBUG
printf(
"Couldn't fetch state for 0x%04X / %d / %s in the cache, getting it from persistence\n",
id.deviceId,
id.groupId,
MiLightRemoteConfig::fromType(id.deviceType)->name.c_str()
);
#endif
trackEviction();
GroupState loadedState = GroupState::defaultState(id.deviceType);
const MiLightRemoteConfig* remoteConfig = MiLightRemoteConfig::fromType(id.deviceType);
if (remoteConfig == NULL) {
return NULL;
}
persistence.get(id, loadedState);
state = cache.set(id, loadedState);
}
return state;
}
GroupState* GroupStateStore::get(const uint16_t deviceId, const uint8_t groupId, const MiLightRemoteType deviceType) {
BulbId bulbId(deviceId, groupId, deviceType);
return get(bulbId);
}
// Save state for a bulb.
//
// Notes:
//
// * For device types with groups, group 0 is a "virtual" group. All devices paired with the same ID will
// respond to group 0. When state for an individual (i.e., != 0) group is changed, the state for
// group 0 becomes out of sync and should be cleared.
//
// * If id.groupId == 0, will iterate across all groups and individually save each group (recursively)
//
GroupState* GroupStateStore::set(const BulbId &id, const GroupState& state) {
BulbId otherId(id);
GroupState* storedState = get(id);
storedState->patch(state);
if (id.groupId == 0) {
const MiLightRemoteConfig* remote = MiLightRemoteConfig::fromType(id.deviceType);
#ifdef STATE_DEBUG
Serial.printf_P(PSTR("Fanning out group 0 state for device ID 0x%04X (%d groups in total)\n"), id.deviceId, remote->numGroups);
state.debugState("group 0 state = ");
#endif
for (size_t i = 1; i <= remote->numGroups; i++) {
otherId.groupId = i;
GroupState* individualState = get(otherId);
individualState->patch(state);
}
} else {
otherId.groupId = 0;
GroupState* group0State = get(otherId);
group0State->clearNonMatchingFields(state);
}
return storedState;
}
GroupState* GroupStateStore::set(const uint16_t deviceId, const uint8_t groupId, const MiLightRemoteType deviceType, const GroupState& state) {
BulbId bulbId(deviceId, groupId, deviceType);
return set(bulbId, state);
}
void GroupStateStore::clear(const BulbId& bulbId) {
GroupState* state = get(bulbId);
if (state != NULL) {
state->initFields();
state->patch(GroupState::defaultState(bulbId.deviceType));
}
}
void GroupStateStore::trackEviction() {
if (cache.isFull()) {
evictedIds.add(cache.getLru());
#ifdef STATE_DEBUG
BulbId bulbId = evictedIds.getLast();
printf(
"Evicting from cache: 0x%04X / %d / %s\n",
bulbId.deviceId,
bulbId.groupId,
MiLightRemoteConfig::fromType(bulbId.deviceType)->name.c_str()
);
#endif
}
}
bool GroupStateStore::flush() {
ListNode<GroupCacheNode*>* curr = cache.getHead();
bool anythingFlushed = false;
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 && !anythingFlushed) {
persistence.clear(evictedIds.shift());
anythingFlushed = true;
}
return anythingFlushed;
}
void GroupStateStore::limitedFlush() {
unsigned long now = millis();
if ((lastFlush + flushRate) < now) {
if (flush()) {
lastFlush = now;
}
}
}