| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  | #ifndef StatefulService_h
 | 
					
						
							|  |  |  | #define StatefulService_h
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <Arduino.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  | #include <ArduinoJson.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <list>
 | 
					
						
							|  |  |  | #include <functional>
 | 
					
						
							|  |  |  | #ifdef ESP32
 | 
					
						
							|  |  |  | #include <freertos/FreeRTOS.h>
 | 
					
						
							|  |  |  | #include <freertos/semphr.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 19:26:12 +01:00
										 |  |  | #ifndef DEFAULT_BUFFER_SIZE
 | 
					
						
							|  |  |  | #define DEFAULT_BUFFER_SIZE 1024
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  | enum class StateUpdateResult { | 
					
						
							|  |  |  |   CHANGED = 0,  // The update changed the state and propagation should take place if required
 | 
					
						
							|  |  |  |   UNCHANGED,    // The state was unchanged, propagation should not take place
 | 
					
						
							|  |  |  |   ERROR         // There was a problem updating the state, propagation should not take place
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-09 21:57:44 +01:00
										 |  |  | template <typename T> | 
					
						
							|  |  |  | using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>; | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-09 21:57:44 +01:00
										 |  |  | template <typename T> | 
					
						
							|  |  |  | using JsonStateReader = std::function<void(T& settings, JsonObject& root)>; | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  | typedef size_t update_handler_id_t; | 
					
						
							| 
									
										
										
										
											2020-05-21 08:42:21 +01:00
										 |  |  | typedef std::function<void(const String& originId)> StateUpdateCallback; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef struct StateUpdateHandlerInfo { | 
					
						
							| 
									
										
										
										
											2020-05-21 12:55:22 -07:00
										 |  |  |   static update_handler_id_t currentUpdatedHandlerId; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   update_handler_id_t _id; | 
					
						
							|  |  |  |   StateUpdateCallback _cb; | 
					
						
							|  |  |  |   bool _allowRemove; | 
					
						
							|  |  |  |   StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : | 
					
						
							|  |  |  |       _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; | 
					
						
							|  |  |  | } StateUpdateHandlerInfo_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <class T> | 
					
						
							|  |  |  | class StatefulService { | 
					
						
							|  |  |  |  public: | 
					
						
							|  |  |  |   template <typename... Args> | 
					
						
							|  |  |  | #ifdef ESP32
 | 
					
						
							|  |  |  |   StatefulService(Args&&... args) : | 
					
						
							|  |  |  |       _state(std::forward<Args>(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |   StatefulService(Args&&... args) : _state(std::forward<Args>(args)...) { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { | 
					
						
							|  |  |  |     if (!cb) { | 
					
						
							|  |  |  |       return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); | 
					
						
							|  |  |  |     _updateHandlers.push_back(updateHandler); | 
					
						
							|  |  |  |     return updateHandler._id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void removeUpdateHandler(update_handler_id_t id) { | 
					
						
							|  |  |  |     for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { | 
					
						
							|  |  |  |       if ((*i)._allowRemove && (*i)._id == id) { | 
					
						
							|  |  |  |         i = _updateHandlers.erase(i); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         ++i; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   StateUpdateResult update(std::function<StateUpdateResult(T&)> stateUpdater, const String& originId) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     StateUpdateResult result = stateUpdater(_state); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							|  |  |  |     if (result == StateUpdateResult::CHANGED) { | 
					
						
							|  |  |  |       callUpdateHandlers(originId); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T&)> stateUpdater) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     StateUpdateResult result = stateUpdater(_state); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater, const String& originId) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     StateUpdateResult result = stateUpdater(jsonObject, _state); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							|  |  |  |     if (result == StateUpdateResult::CHANGED) { | 
					
						
							|  |  |  |       callUpdateHandlers(originId); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     StateUpdateResult result = stateUpdater(jsonObject, _state); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							|  |  |  |     return result; | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   void read(std::function<void(T&)> stateReader) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     stateReader(_state); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   void read(JsonObject& jsonObject, JsonStateReader<T> stateReader) { | 
					
						
							|  |  |  |     beginTransaction(); | 
					
						
							|  |  |  |     stateReader(_state, jsonObject); | 
					
						
							|  |  |  |     endTransaction(); | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 08:42:21 +01:00
										 |  |  |   void callUpdateHandlers(const String& originId) { | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |     for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) { | 
					
						
							|  |  |  |       updateHandler._cb(originId); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  protected: | 
					
						
							|  |  |  |   T _state; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 20:18:43 +01:00
										 |  |  |   inline void beginTransaction() { | 
					
						
							|  |  |  | #ifdef ESP32
 | 
					
						
							|  |  |  |     xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   inline void endTransaction() { | 
					
						
							|  |  |  | #ifdef ESP32
 | 
					
						
							|  |  |  |     xSemaphoreGiveRecursive(_accessMutex); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 23:23:45 +01:00
										 |  |  |  private: | 
					
						
							|  |  |  | #ifdef ESP32
 | 
					
						
							|  |  |  |   SemaphoreHandle_t _accessMutex; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |   std::list<StateUpdateHandlerInfo_t> _updateHandlers; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif  // end StatefulService_h
 |