Browse Source

initial pass at group state persistence

Chris Mullins 8 years ago
parent
commit
f3cff69924

+ 313 - 0
lib/DataStructures/LinkedList.h

@@ -0,0 +1,313 @@
+/*
+  ********* Adapted from: *********
+	https://github.com/ivanseidel/LinkedList
+	Created by Ivan Seidel Gomes, March, 2013.
+	Released into the public domain.
+  *********************************
+
+  Changes:
+    - public access to ListNode (allows for splicing for LRU)
+    - doubly-linked
+    - remove caching stuff in favor of standard linked list iterating
+    - remove sorting
+*/
+
+
+#ifndef LinkedList_h
+#define LinkedList_h
+
+#include <stddef.h>
+
+template<class T>
+struct ListNode {
+	T data;
+	ListNode<T> *next;
+	ListNode<T> *prev;
+};
+
+template <typename T>
+class LinkedList {
+
+protected:
+	int _size;
+	ListNode<T> *root;
+	ListNode<T>	*last;
+
+public:
+	LinkedList();
+	~LinkedList();
+
+	/*
+		Returns current size of LinkedList
+	*/
+	virtual int size();
+	/*
+		Adds a T object in the specified index;
+		Unlink and link the LinkedList correcly;
+		Increment _size
+	*/
+	virtual bool add(int index, T);
+	/*
+		Adds a T object in the end of the LinkedList;
+		Increment _size;
+	*/
+	virtual bool add(T);
+	/*
+		Adds a T object in the start of the LinkedList;
+		Increment _size;
+	*/
+	virtual bool unshift(T);
+	/*
+		Set the object at index, with T;
+		Increment _size;
+	*/
+	virtual bool set(int index, T);
+	/*
+		Remove object at index;
+		If index is not reachable, returns false;
+		else, decrement _size
+	*/
+	virtual T remove(int index);
+	/*
+		Remove last object;
+	*/
+	virtual T pop();
+	/*
+		Remove first object;
+	*/
+	virtual T shift();
+	/*
+		Get the index'th element on the list;
+		Return Element if accessible,
+		else, return false;
+	*/
+	virtual T get(int index);
+
+	/*
+		Clear the entire array
+	*/
+	virtual void clear();
+
+	ListNode<T>* getNode(int index);
+  virtual void spliceToFront(ListNode<T>* node);
+  ListNode<T>* getHead() { return root; }
+
+};
+
+
+template<typename T>
+void LinkedList<T>::spliceToFront(ListNode<T>* node) {
+  // Node is already root
+  if (node->prev == NULL) {
+    return;
+  }
+
+  node->prev->next = node->next;
+  if (node->next != NULL) {
+    node->next->prev = node->prev;
+  }
+
+  root->prev = node;
+  node->next = root;
+  node->prev = NULL;
+  root = node;
+}
+
+// Initialize LinkedList with false values
+template<typename T>
+LinkedList<T>::LinkedList()
+{
+	root=NULL;
+	last=NULL;
+	_size=0;
+}
+
+// Clear Nodes and free Memory
+template<typename T>
+LinkedList<T>::~LinkedList()
+{
+	ListNode<T>* tmp;
+	while(root!=NULL)
+	{
+		tmp=root;
+		root=root->next;
+		delete tmp;
+	}
+	last = NULL;
+	_size=0;
+}
+
+/*
+	Actualy "logic" coding
+*/
+
+template<typename T>
+ListNode<T>* LinkedList<T>::getNode(int index){
+
+	int _pos = 0;
+	ListNode<T>* current = root;
+
+	while(_pos < index && current){
+		current = current->next;
+
+		_pos++;
+	}
+
+	return false;
+}
+
+template<typename T>
+int LinkedList<T>::size(){
+	return _size;
+}
+
+template<typename T>
+bool LinkedList<T>::add(int index, T _t){
+
+	if(index >= _size)
+		return add(_t);
+
+	if(index == 0)
+		return unshift(_t);
+
+	ListNode<T> *tmp = new ListNode<T>(),
+				 *_prev = getNode(index-1);
+	tmp->data = _t;
+	tmp->next = _prev->next;
+	_prev->next = tmp;
+
+	_size++;
+
+	return true;
+}
+
+template<typename T>
+bool LinkedList<T>::add(T _t){
+
+	ListNode<T> *tmp = new ListNode<T>();
+	tmp->data = _t;
+	tmp->next = NULL;
+
+	if(root){
+		// Already have elements inserted
+		last->next = tmp;
+		last = tmp;
+	}else{
+		// First element being inserted
+		root = tmp;
+		last = tmp;
+	}
+
+	_size++;
+
+	return true;
+}
+
+template<typename T>
+bool LinkedList<T>::unshift(T _t){
+
+	if(_size == 0)
+		return add(_t);
+
+	ListNode<T> *tmp = new ListNode<T>();
+	tmp->next = root;
+	tmp->data = _t;
+	root = tmp;
+
+	_size++;
+
+	return true;
+}
+
+template<typename T>
+bool LinkedList<T>::set(int index, T _t){
+	// Check if index position is in bounds
+	if(index < 0 || index >= _size)
+		return false;
+
+	getNode(index)->data = _t;
+	return true;
+}
+
+template<typename T>
+T LinkedList<T>::pop(){
+	if(_size <= 0)
+		return T();
+
+	if(_size >= 2){
+		ListNode<T> *tmp = getNode(_size - 2);
+		T ret = tmp->next->data;
+		delete(tmp->next);
+		tmp->next = NULL;
+		last = tmp;
+		_size--;
+		return ret;
+	}else{
+		// Only one element left on the list
+		T ret = root->data;
+		delete(root);
+		root = NULL;
+		last = NULL;
+		_size = 0;
+		return ret;
+	}
+}
+
+template<typename T>
+T LinkedList<T>::shift(){
+	if(_size <= 0)
+		return T();
+
+	if(_size > 1){
+		ListNode<T> *_next = root->next;
+		T ret = root->data;
+		delete(root);
+		root = _next;
+		_size --;
+
+		return ret;
+	}else{
+		// Only one left, then pop()
+		return pop();
+	}
+
+}
+
+template<typename T>
+T LinkedList<T>::remove(int index){
+	if (index < 0 || index >= _size)
+	{
+		return T();
+	}
+
+	if(index == 0)
+		return shift();
+
+	if (index == _size-1)
+	{
+		return pop();
+	}
+
+	ListNode<T> *tmp = getNode(index - 1);
+	ListNode<T> *toDelete = tmp->next;
+	T ret = toDelete->data;
+	tmp->next = tmp->next->next;
+	delete(toDelete);
+	_size--;
+	return ret;
+}
+
+
+template<typename T>
+T LinkedList<T>::get(int index){
+	ListNode<T> *tmp = getNode(index);
+
+	return (tmp ? tmp->data : T());
+}
+
+template<typename T>
+void LinkedList<T>::clear(){
+	while(size() > 0)
+		shift();
+}
+#endif

+ 64 - 0
lib/MiLightState/GroupState.cpp

@@ -0,0 +1,64 @@
+#include <GroupState.h>
+
+GroupId::GroupId()
+  : deviceId(0),
+    groupId(0),
+    deviceType(REMOTE_TYPE_UNKNOWN)
+{ }
+
+GroupId::GroupId(const GroupId &other)
+  : deviceId(other.deviceId),
+    groupId(other.groupId),
+    deviceType(other.deviceType)
+{ }
+
+GroupId::GroupId(
+  const uint16_t deviceId, const uint8_t groupId, const MiLightRemoteType deviceType
+)
+  : deviceId(deviceId),
+    groupId(groupId),
+    deviceType(deviceType)
+{ }
+
+void GroupId::operator=(const GroupId &other) {
+  deviceId = other.deviceId;
+  groupId = other.groupId;
+  deviceType = other.deviceType;
+}
+
+bool GroupId::operator==(const GroupId &other) {
+  return deviceId == other.deviceId
+    && groupId == other.groupId
+    && deviceType == other.deviceType;
+}
+
+GroupState::GroupState() {
+  _on         = 0;
+  _brightness = 0;
+  _hue        = 0;
+  _saturation = 0;
+  _mode       = 0;
+  _bulbMode   = 0;
+  _kelvin     = 0;
+}
+
+bool GroupState::isOn() { return _on; }
+void GroupState::setOn(bool on) { _on = on; }
+
+uint8_t GroupState::getBrightness() const { return _brightness; }
+void GroupState::setBrightness(uint8_t brightness) { _brightness = brightness; }
+
+uint8_t GroupState::getHue() { return _hue; }
+void GroupState::setHue(uint8_t hue) { _hue = hue; }
+
+uint8_t GroupState::getSaturation() { return _saturation; }
+void GroupState::setSaturation(uint8_t saturation) { _saturation = saturation; }
+
+uint8_t GroupState::getMode() { return _mode; }
+void GroupState::setMode(uint8_t mode) { _mode = mode; }
+
+uint8_t GroupState::getKelvin() { return _kelvin; }
+void GroupState::setKelvin(uint8_t kelvin) { _kelvin = kelvin; }
+
+BulbMode GroupState::getBulbMode() { return static_cast<BulbMode>(_bulbMode); }
+void GroupState::setBulbMode(BulbMode bulbMode) { _bulbMode = bulbMode; }

+ 35 - 5
lib/MiLightState/GroupState.h

@@ -1,5 +1,6 @@
 #include <inttypes.h>
 #include <inttypes.h>
 #include <Arduino.h>
 #include <Arduino.h>
+#include <MiLightButtons.h>
 
 
 #ifndef _GROUP_STATE_H
 #ifndef _GROUP_STATE_H
 #define _GROUP_STATE_H
 #define _GROUP_STATE_H
@@ -7,19 +8,31 @@
 struct GroupId {
 struct GroupId {
   uint16_t deviceId;
   uint16_t deviceId;
   uint8_t groupId;
   uint8_t groupId;
+  MiLightRemoteType deviceType;
+
+  GroupId();
+  GroupId(const GroupId& other);
+  GroupId(const uint16_t deviceId, const uint8_t groupId, const MiLightRemoteType deviceType);
+  bool operator==(const GroupId& other);
+  void operator=(const GroupId& other);
+};
+
+enum BulbMode {
+  BULB_MODE_WHITE,
+  BULB_MODE_COLOR,
+  BULB_MODE_SCENE
 };
 };
 
 
-struct GroupState {
-  // xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx
-  // ^..
-  uint32_t data;
+class GroupState {
+public:
+  GroupState();
 
 
   // 1 bit
   // 1 bit
   bool isOn();
   bool isOn();
   void setOn(bool on);
   void setOn(bool on);
 
 
   // 7 bits
   // 7 bits
-  uint8_t getBrightness();
+  uint8_t getBrightness() const;
   void setBrightness(uint8_t brightness);
   void setBrightness(uint8_t brightness);
 
 
   // 8 bits
   // 8 bits
@@ -37,6 +50,23 @@ struct GroupState {
   // 7 bits
   // 7 bits
   uint8_t getKelvin();
   uint8_t getKelvin();
   void setKelvin(uint8_t kelvin);
   void setKelvin(uint8_t kelvin);
+
+  // 3 bits
+  BulbMode getBulbMode();
+  void setBulbMode(BulbMode mode);
+
+private:
+  uint32_t
+    _on         : 1,
+    _brightness : 7,
+    _hue        : 8,
+    _saturation : 7,
+    _mode       : 4,
+    _bulbMode   : 3,
+                : 2;
+  uint8_t
+    _kelvin     : 7,
+                : 1;
 };
 };
 
 
 struct GroupStateNode {
 struct GroupStateNode {

+ 44 - 0
lib/MiLightState/GroupStateCache.cpp

@@ -0,0 +1,44 @@
+#include <GroupStateCache.h>
+
+GroupStateCache::GroupStateCache(const size_t maxSize)
+  : maxSize(maxSize)
+{ }
+
+const GroupState* GroupStateCache::get(const GroupId& id) {
+  return getInternal(id);
+}
+
+void GroupStateCache::set(const GroupId& id, const GroupState& state) {
+  GroupCacheNode* pushedNode = NULL;
+  if (cache.size() >= maxSize) {
+    pushedNode = cache.pop();
+  }
+
+  GroupState* cachedState = getInternal(id);
+  if (cachedState == NULL) {
+    if (pushedNode == NULL) {
+      cache.unshift(new GroupCacheNode(id, state));
+    } else {
+      pushedNode->id = id;
+      pushedNode->state = state;
+      cache.unshift(pushedNode);
+    }
+  } else {
+    *cachedState = state;
+  }
+}
+
+GroupState* GroupStateCache::getInternal(const GroupId& id) {
+  ListNode<GroupCacheNode*>* cur = cache.getHead();
+
+  while (cur != NULL) {
+    if (cur->data->id == id) {
+      GroupState* result = &cur->data->state;
+      cache.spliceToFront(cur);
+      return result;
+    }
+    cur = cur->next;
+  }
+
+  return NULL;
+}

+ 30 - 0
lib/MiLightState/GroupStateCache.h

@@ -0,0 +1,30 @@
+#include <LinkedList.h>
+#include <GroupStateStore.h>
+
+#ifndef _GROUP_STATE_CACHE_H
+#define _GROUP_STATE_CACHE_H
+
+struct GroupCacheNode {
+  GroupCacheNode() {}
+  GroupCacheNode(const GroupId& id, const GroupState& state)
+    : id(id), state(state) { }
+
+  GroupId id;
+  GroupState state;
+};
+
+class GroupStateCache : public GroupStateStore {
+public:
+  GroupStateCache(const size_t maxSize);
+
+  const GroupState* get(const GroupId& id);
+  void set(const GroupId& id, const GroupState& state);
+
+private:
+  LinkedList<GroupCacheNode*> cache;
+  const size_t maxSize;
+
+  GroupState* getInternal(const GroupId& id);
+};
+
+#endif

+ 4 - 7
lib/MiLightState/GroupStateStore.h

@@ -1,15 +1,12 @@
 #include <GroupState.h>
 #include <GroupState.h>
 
 
-#ifndef _STATE_CACHE_H
-#define _STATE_CACHE_H
+#ifndef _GROUP_STATE_STORE_H
+#define _GROUP_STATE_STORE_H
 
 
 class GroupStateStore {
 class GroupStateStore {
 public:
 public:
-  bool get(const GroupId& id, GroupState& state);
-  void set(const GroupId& id, const GroupState& state);
-
-private:
-  void evictOldest(GroupState& state);
+  virtual const GroupState* get(const GroupId& id) = 0;
+  virtual void set(const GroupId& id, const GroupState& state) = 0;
 };
 };
 
 
 #endif
 #endif

+ 2 - 0
src/main.cpp

@@ -16,6 +16,7 @@
 #include <RGBConverter.h>
 #include <RGBConverter.h>
 #include <MiLightDiscoveryServer.h>
 #include <MiLightDiscoveryServer.h>
 #include <MiLightClient.h>
 #include <MiLightClient.h>
+#include <GroupStateCache.h>
 
 
 WiFiManager wifiManager;
 WiFiManager wifiManager;
 
 
@@ -27,6 +28,7 @@ MiLightHttpServer *httpServer = NULL;
 MqttClient* mqttClient = NULL;
 MqttClient* mqttClient = NULL;
 MiLightDiscoveryServer* discoveryServer = NULL;
 MiLightDiscoveryServer* discoveryServer = NULL;
 uint8_t currentRadioType = 0;
 uint8_t currentRadioType = 0;
+GroupStateCache* stateCache = new GroupStateCache(100);
 
 
 int numUdpServers = 0;
 int numUdpServers = 0;
 MiLightUdpServer** udpServers;
 MiLightUdpServer** udpServers;