mqtt specific components for the impact mbed endpoint library
Dependents: mbed_mqtt_endpoint_ublox_ethernet mbed_mqtt_endpoint_ublox_cellular mbed_mqtt_endpoint_nxp
Diff: MBEDEndpoint.cpp
- 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