mqtt specific components for the impact mbed endpoint library

Dependents:   mbed_mqtt_endpoint_ublox_ethernet mbed_mqtt_endpoint_ublox_cellular mbed_mqtt_endpoint_nxp

Revision:
0:a3fc1c6ef150
Child:
5:1ba6e68bf50e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MBEDEndpoint.cpp	Wed Mar 26 19:48:35 2014 +0000
@@ -0,0 +1,494 @@
+/* Copyright C2013 Doug Anson, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files the "Software", to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+ 
+ #include "MQTTTransport.h"
+ #include "MBEDEndpoint.h"
+ 
+ // MBED Light support
+ #include "MBEDLight.h"
+ 
+ // Emulated Resource Factory
+ #include "EmulatedResourceFactory.h"
+ 
+ // Emulated Actions we can act on
+ #include "EmulatedLightDimmerAction.h"
+ #include "EmulatedLightSwitchAction.h"
+ 
+ // string support
+ #include <stdlib.h>
+ #include <string.h>
+   
+ // shutdown endpoint reference
+ extern void closedown(int code);
+  
+ // default constructor
+ MBEDEndpoint::MBEDEndpoint(ErrorHandler *error_handler,EthernetInterface *ethernet) {
+     bool success = true;
+     this->m_instance_id = 0;
+     this->m_preferences = NULL;
+     memset(this->m_lcd_status,0,TEMP_BUFFER_LEN+1);
+     memset(this->m_gw_address,0,PREFERENCE_VALUE_LEN+1);
+     for(int i=0;i<NUM_TRANSPORTS;++i) this->m_transports[i] = NULL;
+     this->m_error_handler = error_handler;
+     this->m_error_handler->setEndpoint((void *)this);
+     if (success) this->initPreferences();
+     if (success) this->initEndpointName();
+     if (success) this->logger()->turnLEDBlue();
+#ifdef MAC_ADDRESS
+     extern char fmt_mac[RESOURCE_VALUE_LEN+1];
+     if (success)this->logger()->log("%s (MAC: %s)",ENDPOINT_VERSION_ANNOUNCE,fmt_mac);
+#else
+     if (success)this->logger()->log(ENDPOINT_VERSION_ANNOUNCE);
+#endif
+     if (success) this->initGWAddress();
+     if (success) this->logger()->log("IOC GW IP: %s",GW_IPADDRESS);
+     if (PL_ENABLE && success) this->logger()->log("Philips Light ID: %d Philips Gateway IP: %s",PL_LIGHT_ID,PL_GW_ADDRESS);
+     if (success) success = this->initializeEthernet(ethernet);
+     if (success) this->logger()->turnLEDYellow();
+     if (success)this->m_map = new MBEDToIOCResourceMap(error_handler); 
+     if (success) success = this->initializeTransports();
+     if (success) success = this->initializeLights();   
+     if (success) this->logger()->turnLEDOrange();
+     this->logger()->lcdStatusOnly(true);
+     if (!success) closedown(2);
+ }
+ 
+ // default destructor
+ MBEDEndpoint::~MBEDEndpoint() {
+     bool success = true;
+     if (success) this->logger()->turnLEDYellow();
+     if (success) success = this->closeLights();
+     if (success) success = this->closeTransports();
+     if (success) success = this->closeEthernet();
+     if (success) this->logger()->turnLEDBlue();
+     if (this->m_map != NULL) delete this->m_map;
+ }
+  
+ // get the IOC <--> MBED resource map
+ MBEDToIOCResourceMap *MBEDEndpoint::getMap() { return this->m_map; }
+ 
+ // initialize our preferences
+ void MBEDEndpoint::initPreferences() { if (this->m_preferences == NULL) this->m_preferences = new Preferences(this->logger()); if (this->m_preferences != NULL) this->m_preferences->fixCoordsForIOC(); }
+
+ // get our preferences
+ Preferences *MBEDEndpoint::preferences() { return this->m_preferences; }
+ 
+ // initialize the GW address from the configuration
+ void MBEDEndpoint::initGWAddress() {
+     memset(this->m_gw_address,0,PREFERENCE_VALUE_LEN+1);
+     this->preferences()->getPreference("gw_address",this->m_gw_address,PREFERENCE_VALUE_LEN,GW_IPADDRESS);
+     this->logger()->log("GW IP: %s",this->getGWAddress());
+ }
+ 
+ // get our GW address
+ char *MBEDEndpoint::getGWAddress() { return this->m_gw_address; }
+ 
+ // get our LCD status
+ char *MBEDEndpoint::getLCDStatus() {
+     memset(this->m_lcd_status,0,TEMP_BUFFER_LEN+1);
+     
+     // look at Light#0 to determine the IOC linkage ID...
+     char *ioc = this->m_lights[0]->getResourceFactory()->getResourceValue(IOC_LINKAGE_RESOURCE);
+     
+     // color our LED depending on whether we have IOC linkage or not...
+     if (ioc == NULL || strcmp(ioc,IOC_LINKAGE_UNSET) == 0) this->logger()->turnLEDOrange();
+     else this->logger()->turnLEDGreen();
+           
+     sprintf(this->m_lcd_status,"Node: %s\nGW IP: %s\nIOC Link: %s",this->getEndpointName(),this->getGWAddress(),ioc);
+     return this->m_lcd_status;
+ }
+ 
+ // initialize the Lights
+ bool MBEDEndpoint::initializeLights() {
+     int index = this->preferences()->getIntPreference("endpoint_id",LIGHT_NAME_INDEX); 
+     this->logger()->log("Initializing Lights...");
+     for(int i=0;i<NUM_LIGHTS;++i) {
+         this->m_lights[i] = new MBEDLight(this->logger(),this->m_transports,i+index,this);
+         this->m_lights[i]->setDimmerAction(new EmulatedLightDimmerAction(this->logger(),this->m_lights[i]));
+         this->m_lights[i]->setSwitchAction(new EmulatedLightSwitchAction(this->logger(),this->m_lights[i]));
+     }
+     return true;
+ }
+ 
+ // does the input name match any of our light resources?
+ int MBEDEndpoint::indexOfLight(char *name) {
+     bool found = false;
+     int index = -1;
+     
+     for(int i=0;i<NUM_LIGHTS && !found;++i) {
+         if (strcmp(this->m_lights[i]->getName(),name) == 0) {
+             found = true;
+             index = i;
+         }
+     }
+     
+     return index;
+ }
+ 
+ // get a specific resources
+ ResourceFactory *MBEDEndpoint::getResources(int index) {
+     if (index >= 0 && index < NUM_LIGHTS) return this->m_lights[index]->resources();
+     return NULL;
+ }
+ 
+ // initialize our ResourceFactory
+ ResourceFactory *MBEDEndpoint::initResourceFactory() { return new EmulatedResourceFactory(this->logger(),(void *)this); }
+ 
+ // Initialize the Endpoint Name - will be the first Light resource name (and there must be one...)
+ void MBEDEndpoint::initEndpointName() {
+     this->m_instance_id = this->preferences()->getIntPreference("endpoint_id",LIGHT_NAME_INDEX);
+     memset(this->m_endpoint_name,0,LIGHT_NAME_LEN+1);
+     sprintf(this->m_endpoint_name,LIGHT_NAME,this->m_instance_id);
+ }
+ 
+ // get our endpoint name
+ char *MBEDEndpoint::getEndpointName() { return this->m_endpoint_name; }
+ 
+ // get our instance id
+ int MBEDEndpoint::getInstanceID() { return this->m_instance_id; }
+ 
+ // initialize a specific transport
+ bool MBEDEndpoint::initializeTransport(int index,char *key,Transport *transport) {
+     bool success = false;
+     if (this->m_transports[index] == NULL) {
+          this->logger()->log("Initializing %s Transport...", key);
+          this->m_transports[index] = transport;
+          if (this->m_transports[index] != NULL) success = this->m_transports[index]->connect();
+          if (success) this->logger()->log("Transport %s initialized and connected",key);
+      }
+      else {
+          this->logger()->log("%s already connected (OK)...", key);
+          success = true;
+      }
+      return success;
+ }
+ 
+ // initialize our transports
+ bool MBEDEndpoint::initializeTransports() {
+      bool success = true;
+      
+      if (success == true) {
+        // MQTT Initialization
+        success = this->initializeTransport(MQTT_TRANSPORT,"MQTT",new MQTTTransport(this->m_error_handler,this,this->getMap()));
+      }
+      
+      if (success == true) {
+          // HTTP Initialization
+          success = this->initializeTransport(HTTP_TRANSPORT,"HTTP",new IOCHTTPTransport(this->m_error_handler,this));
+      }
+      return success;
+ }
+ 
+ // initialize our Ethernet 
+ bool MBEDEndpoint::initializeEthernet(EthernetInterface *ethernet) {
+     bool success = false;
+     this->m_ethernet = ethernet;
+     if (this->m_ethernet != NULL) {
+         this->logger()->log("Initializing Ethernet...");
+         
+         // connect up ethernet
+         this->m_ethernet->init();
+         this->m_ethernet->connect();
+         
+         // display our IP address
+         char *ipaddr = this->m_ethernet->getIPAddress();
+         if (ipaddr != NULL && strlen(ipaddr) > 0) {
+            this->logger()->log("IPAddress: %s",this->m_ethernet->getIPAddress());
+            success = true;
+         }
+         else {
+            this->logger()->log("Ethernet Not Connected...");
+            success = false;
+         }
+     }
+     else {
+         this->logger()->log("No Ethernet instance found");
+         success = false;
+     }
+     return success;
+ }
+ 
+ // load up all endpoints into the IOC
+ bool MBEDEndpoint::loadEndpoints() {
+     bool success = true;
+     this->logger()->log("Loading All Endpoints to IOC...");
+     for(int i=0;i<NUM_LIGHTS && success;++i) success = this->loadEndpoint(this->m_lights[i]);
+     return success;
+ }
+ 
+ // load up our endpoint to the IOC
+ bool MBEDEndpoint::loadEndpoint(Light *light) {      
+     bool success = false;
+     char result[IOC_RESULT_LEN+1];
+     char payload[IOC_PAYLOAD_LEN+1];
+     
+     // initialize
+     memset(result,0,IOC_RESULT_LEN+1);
+     memset(payload,0,IOC_PAYLOAD_LEN+1);
+     
+     // DEBUG
+     this->logger()->log("Building Payload for Light: %s...",light->getName());
+          
+     // build the payload
+     char *data = this->buildIOCPayload(payload,IOC_PAYLOAD_LEN,light);
+      
+     // issue the request
+     if (data != NULL && strlen(data) > 0) {
+        // DEBUG
+        this->logger()->log("Sending Light: %s to the IOC...",light->getName());
+     
+        // load
+        success = this->m_transports[LOAD_TRANSPORT]->loadEndpoint((char *)payload,strlen(payload),(char *)result,IOC_RESULT_LEN);
+     }
+     
+     // DEBUG
+     //if (success) this->logger()->log("Saving IOC ID for Light: %s...",light->getName());
+     
+     // update the IOC ID if found
+     if (success) this->saveIOCID(light,result);
+     
+     // DEBUG
+     if (success) this->logger()->log("Light: %s IOC ID=%d sent successfully",light->getName(),light->getIOCID());
+     
+     // DEBUG
+     if (!success) {
+         if (light != NULL) this->logger()->log("Light: %s send FAILED",light->getName());
+         else this->logger()->log("Light: send FAILED (NULL LIGHT)");
+     }
+     
+     // return our status
+     return success;
+ }
+ 
+ // update all endpoints to the IOC
+ bool MBEDEndpoint::updateEndpoints() {
+     bool success = true;
+     for(int i=0;i<NUM_LIGHTS && success;++i) success = this->updateEndpoints(i);
+     return success;
+ }
+ 
+ // update all endpoints to the IOC
+ bool MBEDEndpoint::updateEndpoints(int index) {
+     if (index >= 0 && index < NUM_LIGHTS) return this->updateEndpoint(this->m_lights[index]);
+     return false; 
+ }
+ 
+ // update our endpoint with the IOC
+ bool MBEDEndpoint::updateEndpoint(Light *light) { 
+     bool success = false;
+     char result[IOC_RESULT_LEN+1];
+     char payload[IOC_PAYLOAD_LEN+1];
+     
+     // initialize
+     memset(result,0,IOC_RESULT_LEN+1);
+     memset(payload,0,IOC_PAYLOAD_LEN+1);
+          
+     // build the payload
+     char *data = this->buildIOCPayload(payload,IOC_PAYLOAD_LEN,light);
+     
+     // issue the request  
+     if (data != NULL && strlen(data) > 0) {
+        // DEBUG
+        this->logger()->log("Updating Light: %s at the IOC...",light->getName()); 
+        
+        // update
+        success = this->m_transports[LOAD_TRANSPORT]->updateEndpoint(light->getIOCID(),(char *)payload,strlen(payload),(char *)result,IOC_RESULT_LEN);
+     }
+     
+     // DEBUG
+     if (success) this->logger()->log("Update of Endpoint to IOC successful");
+     else this->logger()->log("Update of Endpoint to IOC FAILED");
+          
+     // return our status
+     return success; 
+ } 
+ 
+ // build out the Payload
+ char *MBEDEndpoint::buildIOCPayload(char *data,int data_length,Light *light) {
+     char tmp[TEMP_BUFFER_LEN+1];
+      
+     // construct the payload for Load/Updates
+     ResourceFactory *factory = light->getResourceFactory();
+     
+     // start the buffer
+     strcat(data,"{");
+     
+     // loop through the resources and build a JSON representation for the payload
+     for(int i=0;i<factory->numResources();++i) {
+         // get the ith resource
+         Resource *resource = factory->getResource(i);
+         if (resource != NULL) {
+             // add to the JSON payload
+             char *name = this->getMap()->endpointNameToIOCName(resource->getName());
+             char *value = resource->getValue();
+             
+             // make sure that we have a positive IOC resource match for the NSP resource 
+             if (name != NULL && strlen(name) > 0) {
+                 // Handle LOCATION a special way
+                 if (strcmp(name,"LOCATION") != 0) {
+                     // standard name,value for IOC
+                     sprintf(tmp, "\"%s\":\"%s\",",name,value); 
+                  }
+                 else {
+                     // IOC expects "Point(X,Y)" for LOCATION
+                     sprintf(tmp, "\"%s\":\"Point(%s)\",",name,value); 
+                 }   
+                 strcat(data,tmp);
+            
+                 // Handle /dev/addldata 
+                 char *dev_addldata = this->getMap()->endpointNameToIOCName("/dev/addldata");
+                 if (dev_addldata != NULL && strcmp(name,dev_addldata) == 0 && light != NULL && light->getIOCID() > 0) {
+                     char buf[IOC_IOC_ID_LEN+1]; memset(buf,0,IOC_IOC_ID_LEN+1); sprintf(buf,"%d",light->getIOCID());
+                     sprintf(tmp,"\"%s\":\"id:%s\",",name,buf);
+                     strcat(data,tmp);
+                 }
+             }
+         }
+     }
+     
+     // Special Case: STARTDATETIME
+     strcat(data,ENDPOINT_STARTTIME);
+     
+     // Special Case: ENDDATETIME
+     strcat(data,ENDPOINT_STOPTIME);
+                
+     // Special Case: NAME
+     sprintf(tmp,"\"NAME\":\"%s\",",light->getName());
+     strcat(data,tmp);
+          
+     // Special Case: TIMEZONEOFFSET
+     strcat(data,ENDPOINT_TIMEZONE);
+          
+     // close
+     strcat(data,"}");
+     
+     // DEBUG
+     //this->logger()->log("Loading Payload: %s",data);
+     
+     // return the payload
+     return data;
+ }
+  
+ // save the IOC ID
+ void MBEDEndpoint::saveIOCID(Light *light,char *json) {
+     if (json != NULL) {          
+         //this->logger()->log("RESULT: %s",json);
+         
+         // look for "id":
+         char *check = "\"id\":";
+         char *pos1 = strstr(json,check);
+         if (pos1 != NULL) {
+             char *pos2 = strstr(pos1,","); 
+             if (pos1 != NULL && pos2 != NULL && pos2 > pos1) {
+                 pos1 += strlen(check);
+                 int length = pos2 - pos1;  
+                 char str_ioc_id[IOC_IOC_ID_LEN+1];
+                 memset(str_ioc_id,0,IOC_IOC_ID_LEN+1);
+                 strncpy(str_ioc_id,pos1,length);
+                 
+                 // DEBUG
+                 //this->logger()->log("IOC ID found: %s",str_ioc_id);
+                 
+                 // parse into int  
+                 int ioc_id = 0;             
+                 sscanf(str_ioc_id,"%d",&ioc_id);
+                          
+                 // save the IOC ID
+                 if (ioc_id > 0) light->setIOCID(ioc_id);
+             }
+             else {
+                 // cannot find the ID tag in the result JSON
+                 this->logger()->log("Cannot find the IOC ID in the JSON result");
+                 this->logger()->log("JSON: %s",json);
+             }
+         }
+         else {
+             // cannot find the ID tag in the result JSON
+             this->logger()->log("Cannot find the IOC ID in the JSON result");
+             this->logger()->log("JSON: %s",json);
+        }
+     }
+ }
+ 
+ // close down the Lights
+ bool MBEDEndpoint::closeLights() {
+    bool success = true;
+    this->logger()->log("Closing down Lights...");
+    for(int i=0;i<NUM_LIGHTS;++i)
+        if (this->m_lights[i] != NULL) delete this->m_lights[i];
+    return success;
+ }
+ 
+ // close a given transport
+ bool MBEDEndpoint::closeTransport(int index,char *key) {
+    this->logger()->log("Closing down %s Transport...", key);
+    if (this->m_transports[index] != NULL) delete this->m_transports[index];
+    return true;
+ }
+ 
+ // close down our transports
+ bool MBEDEndpoint::closeTransports() {
+     bool success = true;
+     
+     if (success) {
+         // close MQTT
+         success = this->closeTransport(MQTT_TRANSPORT,"MQTT");
+     }
+     
+     if (success) {
+         // close HTTP
+         success = this->closeTransport(HTTP_TRANSPORT,"HTTP");
+     }
+     
+     return success;
+ }
+ 
+ // close down our Ethernet 
+ bool MBEDEndpoint::closeEthernet() {
+     this->logger()->log("Closing down Ethernet...");
+     if (this->m_ethernet != NULL) this->m_ethernet->disconnect();
+     return true;
+ }
+ 
+ // min function
+ int MBEDEndpoint::min(int value1,int value2) {
+    if (value1 < value2) return value1;
+    return value2;
+ }
+ 
+ // get our error handler
+ ErrorHandler *MBEDEndpoint::logger() { return this->m_error_handler; }
+ 
+ // main running loop
+ void MBEDEndpoint::run() {
+     this->logger()->log("Endpoint Main Loop");
+     while(true) {
+         // sleep a bit
+         //this->logger()->log("Sleeping for a bit...");
+         wait_ms(MAIN_LOOP_SLEEP);
+         
+         // check for events 
+         //this->logger()->log("Processing Events...");
+         for(int i=0;i<NUM_TRANSPORTS;++i) this->m_transports[i]->checkAndProcess();
+         
+         // check for exit
+         //this->logger()->log("Checking for exit...");
+         this->logger()->checkForExit();
+     }
+}
+ 
\ No newline at end of file