diff --git a/src/CallContext.h b/src/CallContext.h index 4970dd9..89ab426 100644 --- a/src/CallContext.h +++ b/src/CallContext.h @@ -84,4 +84,206 @@ public: const uint8_t* getHashCode(); protected: void handlePage(const char* buffer, int length) override; +}; + +template class DynamicPointerArray { +private: + union element + { + T* singleElement; + T** multipleElements; + } u; + uint16_t arraySize = 0; +public: + ~DynamicPointerArray() + { + if (arraySize > 2) + free(u.multipleElements); + } + + void clear() + { + if (arraySize > 2) + free(u.multipleElements); + arraySize = 0; + u.singleElement = nullptr; + } + + uint16_t size() const { return arraySize; } + + void push_back(T* element){ + uint16_t currentSize = arraySize; + arraySize++; + if (currentSize == 0) + { + u.singleElement = element; + } + else if (currentSize == 1) + { + T** newBuffer = (T**) malloc(arraySize * sizeof(T*)); + if (newBuffer == NULL) + throw "Fatal error, no more memory"; + newBuffer[0] = u.singleElement; + newBuffer[1] = element; + u.multipleElements = newBuffer; + } + else + { + T** newBuffer = (T**) realloc(u.multipleElements, arraySize * sizeof(T*)); + if (newBuffer == NULL) + throw "Fatal error, no more memory"; + newBuffer[arraySize - 1] = element; + u.multipleElements = newBuffer; + } + } + T* front() const + { + return get(0); + } + T* back() const + { + return get(arraySize - 1); + } + bool empty() const + { + return arraySize == 0; + } + T* operator[](uint16_t index) const + { + return get(index); + } + + T* get(uint16_t index) const + { + if (index >= arraySize) + { + throw "Fatal error. Index does not exist."; + } + if (arraySize == 1) + return u.singleElement; + else + return u.multipleElements[index]; + } + + + void remove(uint16_t index) + { + if (index >= arraySize) + { + throw "Fatal error. Index does not exist."; + } + uint16_t currentSize = arraySize; + if (currentSize == 1) + { + u.singleElement = nullptr; + } + else if (currentSize == 2) + { + + if (index == 1) + { + T* firstElement = u.multipleElements[0]; + free(u.multipleElements); + u.singleElement = firstElement; + } + else + { + T* secondElement = u.multipleElements[1]; + free(u.multipleElements); + u.singleElement = secondElement; + } + } + else + { + for (int i = index; i < currentSize - 1; i++) + { + u.multipleElements[i] = u.multipleElements[i + 1]; + } + T** newBuffer = (T**) realloc(u.multipleElements, arraySize * sizeof(T*)); + if (newBuffer == NULL) + throw "Fatal error, no more memory"; + u.multipleElements = newBuffer; + } + arraySize--; + + } + + class DynamicPointerArrayIterator + { + const DynamicPointerArray* data; + uint32_t currentIndex; + friend class DynamicPointerArray; + public: + DynamicPointerArrayIterator(const DynamicPointerArrayIterator& r) : + data(r.data),currentIndex(r.currentIndex) { } + + DynamicPointerArrayIterator(const DynamicPointerArray* arr, uint32_t startIndex): data(arr),currentIndex(startIndex) { } + T* operator*() const + { + return data->get(currentIndex); + } + const DynamicPointerArrayIterator operator++(int) + { + DynamicPointerArrayIterator temp = *this; + ++*this; + return temp; + } + DynamicPointerArrayIterator& operator++() + { + ++currentIndex; + return *this; + } + bool operator==(const DynamicPointerArrayIterator& lhs) + { + return currentIndex==lhs.currentIndex; + } + bool operator!=(const DynamicPointerArrayIterator& lhs) + { + return currentIndex!=lhs.currentIndex; + } + }; + DynamicPointerArrayIterator begin() const{ return DynamicPointerArrayIterator(this,0); } + DynamicPointerArrayIterator end() const{ return DynamicPointerArrayIterator(this, arraySize); } + + class DynamicPointerArrayReverseIterator + { + const DynamicPointerArray* data; + uint32_t currentIndex; + friend class DynamicPointerArray; + public: + DynamicPointerArrayReverseIterator(const DynamicPointerArrayReverseIterator& r) : + data(r.data),currentIndex(r.currentIndex) { } + + DynamicPointerArrayReverseIterator(const DynamicPointerArray* arr, uint32_t startIndex): data(arr),currentIndex(startIndex) { } + T* operator*() const + { + return data->get(currentIndex); + } + const DynamicPointerArrayReverseIterator operator++(int) + { + DynamicPointerArrayReverseIterator temp = *this; + ++*this; + return temp; + } + DynamicPointerArrayReverseIterator& operator++() + { + currentIndex--; + return *this; + } + bool operator==(const DynamicPointerArrayReverseIterator& lhs) + { + return currentIndex==lhs.currentIndex; + } + bool operator!=(const DynamicPointerArrayReverseIterator& lhs) + { + return currentIndex!=lhs.currentIndex; + } + }; + DynamicPointerArrayReverseIterator rbegin() const{ return DynamicPointerArrayReverseIterator(this,arraySize - 1); } + DynamicPointerArrayReverseIterator rend() const{ return DynamicPointerArrayReverseIterator(this, 0 - 1); } + + void erase(DynamicPointerArrayIterator iterator) + { + remove(iterator.currentIndex); + } }; \ No newline at end of file diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index b7f4916..90ae58a 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -923,9 +923,21 @@ void Span::processSerialCommand(const char *c){ else if(invalidUUID((*chr)->type,(*chr)->isCustom)) LOG0(" *** ERROR #%d! Format of UUID is invalid ***\n",++nErrors); - else - if(std::find_if((*svc)->Characteristics.begin(),chr,[chr](SpanCharacteristic *c)->boolean{return(c->hapChar==(*chr)->hapChar);})!=chr) - LOG0(" *** ERROR #%d! Characteristic already defined for this Service ***\n",++nErrors); + else + // if(std::find_if((*svc)->Characteristics.begin(),chr,[chr](SpanCharacteristic *c)->boolean{return(c->hapChar==(*chr)->hapChar);})!=chr) + { + bool hasHabChar = false; + for(auto chara=(*svc)->Characteristics.begin(); chara!=(*svc)->Characteristics.end(); chara++) + { + if ((*chara)->hapChar == (*chr)->hapChar && chara != chr) + { + hasHabChar = true; + break; + } + } + if (hasHabChar) + LOG0(" *** ERROR #%d! Characteristic already defined for this Service ***\n",++nErrors); + } if((*chr)->setRangeError) LOG0(" *** WARNING #%d! Attempt to set Custom Range for this Characteristic ignored ***\n",++nWarnings); @@ -938,9 +950,20 @@ void Span::processSerialCommand(const char *c){ } // Characteristics - for(auto req=(*svc)->req.begin(); req!=(*svc)->req.end(); req++){ - if(std::find_if((*svc)->Characteristics.begin(),(*svc)->Characteristics.end(),[req](SpanCharacteristic *c)->boolean{return(c->hapChar==*req);})==(*svc)->Characteristics.end()) - LOG0(" *** WARNING #%d! Required '%s' Characteristic for this Service not found ***\n",++nWarnings,(*req)->hapName); + for(auto req=(*svc)->req.begin(); req!=(*svc)->req.end(); req++) + // if(std::find_if((*svc)->Characteristics.begin(),(*svc)->Characteristics.end(),[req](SpanCharacteristic *c)->boolean{return(c->hapChar==*req);})==(*svc)->Characteristics.end()) + { + bool hasHabChar = false; + for(auto chara=(*svc)->Characteristics.begin(); chara!=(*svc)->Characteristics.end(); chara++) + { + if ((*chara)->hapChar == *req) + { + hasHabChar = true; + break; + } + } + if (!hasHabChar) + LOG0(" *** WARNING #%d! Required '%s' Characteristic for this Service not found ***\n",++nWarnings,(*req)->hapName); } for(auto button=PushButtons.begin(); button!=PushButtons.end(); button++){ @@ -1645,7 +1668,6 @@ boolean Span::updateDatabase(boolean updateMDNS){ for(auto svc=(*acc)->Services.begin(); svc!=(*acc)->Services.end(); svc++){ if((void(*)())((*svc)->*(&SpanService::loop)) != (void(*)())(&SpanService::loop)) { // save pointers to services in Loops vector homeSpan.Loops.push_back((*svc)); - std::vector(homeSpan.Loops).swap(homeSpan.Loops); // shrink vector to fit } } } @@ -1685,7 +1707,7 @@ SpanAccessory::SpanAccessory(uint32_t aid){ SpanAccessory::~SpanAccessory(){ while(Services.rbegin()!=Services.rend()) // delete all Services in this Accessory - delete *Services.rbegin(); + delete *Services.rbegin(); auto acc=homeSpan.Accessories.begin(); // find Accessory in homeSpan vector and erase entry while((*acc)!=this) @@ -1729,7 +1751,6 @@ SpanService::SpanService(const char *type, const char *hapName, boolean isCustom this->isCustom=isCustom; homeSpan.Accessories.back()->Services.push_back(this); - std::vector(homeSpan.Accessories.back()->Services).swap(homeSpan.Accessories.back()->Services); // shrink vector to fit accessory=homeSpan.Accessories.back(); iid=++(homeSpan.Accessories.back()->iidCount); } @@ -1784,7 +1805,6 @@ SpanService *SpanService::setHidden(){ SpanService *SpanService::addLink(SpanService *svc){ linkedServices.push_back(svc); - std::vector(linkedServices).swap(linkedServices); // shrink vector to fit return(this); } @@ -1845,8 +1865,7 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){ } homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); - std::vector(homeSpan.Accessories.back()->Services.back()->Characteristics).swap(homeSpan.Accessories.back()->Services.back()->Characteristics); // shrink vector to fit - + iid=++(homeSpan.Accessories.back()->iidCount); service=homeSpan.Accessories.back()->Services.back(); aid=homeSpan.Accessories.back()->aid; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 4e2876f..f51a8d3 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -52,6 +52,7 @@ #include "HAPConstants.h" #include "HapQR.h" #include "Characteristics.h" +#include "CallContext.h" using std::vector; using std::unordered_map; @@ -247,13 +248,13 @@ class Span{ PushButton *controlButton = NULL; // controls HomeSpan configuration and resets Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point SpanWebLog webLog; // optional web status/log - TaskHandle_t pollTaskHandle = NULL; // optional task handle to use for poll() function + TaskHandle_t pollTaskHandle = pollTaskHandle; // optional task handle to use for poll() function boolean verboseWifiReconnect = true; // set to false to not print WiFi reconnect attempts messages SpanOTA spanOTA; // manages OTA process SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found - vector Accessories; // vector of pointers to all Accessories - vector Loops; // vector of pointer to all Services that have over-ridden loop() methods + DynamicPointerArray Accessories; // vector of pointers to all Accessories + DynamicPointerArray Loops; // vector of pointer to all Services that have over-ridden loop() methods vector Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event vector PushButtons; // vector of pointer to all PushButtons unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs) @@ -360,7 +361,7 @@ class Span{ Span& setRebootCallback(void (*f)(uint8_t),uint32_t t=DEFAULT_REBOOT_CALLBACK_TIME){rebootCallback=f;rebootCallbackTime=t;return(*this);} uint32_t getAutoPollMinFreeStack(){ - return uxTaskGetStackHighWaterMark(pollTaskHandle); + return pollTaskHandle != NULL ? uxTaskGetStackHighWaterMark(pollTaskHandle) : 0; } void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask() @@ -392,7 +393,7 @@ class SpanAccessory{ uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1) int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory - vector Services; // vector of pointers to all Services in this Accessory + DynamicPointerArray Services; // vector of pointers to all Services in this Accessory int sprintfAttributes(char *cBuf, int flags); // prints Accessory JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL @@ -419,8 +420,8 @@ class SpanService{ const char *hapName; // HAP Name boolean hidden=false; // optional property indicating service is hidden boolean primary=false; // optional property indicating service is primary - vector Characteristics; // vector of pointers to all Characteristics in this Service - vector linkedServices; // vector of pointers to any optional linked Services + DynamicPointerArray Characteristics; // vector of pointers to all Characteristics in this Service + DynamicPointerArray linkedServices; // vector of pointers to any optional linked Services boolean isCustom; // flag to indicate this is a Custom Service SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service @@ -438,7 +439,7 @@ class SpanService{ SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self - vector getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops + DynamicPointerArray& getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code