mbed Connector Endpoint interface. This interface permits a mbed endpoint to easily setup MDS resources and emit those resources to an MDS server.

Dependents:   IoT_LED_demo ServoTest uWater_Project hackathon ... more

api/DynamicResource.cpp

Committer:
ansond
Date:
2015-03-20
Revision:
24:a6915e19814e
Parent:
23:caa0260acc21
Child:
25:1fc958ac14d1

File content as of revision 24:a6915e19814e:

/**
 * @file    DynamicResource.cpp
 * @brief   mbed CoAP Endpoint Dynamic Resource class
 * @author  Doug Anson/Chris Paola
 * @version 1.0
 * @see
 *
 * Copyright (c) 2014
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "DynamicResource.h"

// InstancePointerTable Helper
#include "InstancePointerTableHelper.h"

// default constructor
DynamicResource::DynamicResource(const Logger *logger,const char *name,const char *res_type,uint8_t res_mask,const bool observable) : Resource<string>(logger,string(name),string(""))
{
    this->m_res_type = string(res_type);
    this->m_observable = observable;
    this->m_res_mask = res_mask;
    this->m_obs_number = 0;
    this->m_obs_token_ptr = NULL;
    this->m_obs_token_len = 0;
    this->m_data_wrapper = NULL;
}

// constructor (input initial value)
DynamicResource::DynamicResource(const Logger *logger,const char *name,const char *res_type,const string value,uint8_t res_mask,const bool observable) : Resource<string>(logger,string(name),value)
{
    this->m_res_type = string(res_type);
    this->m_observable = observable;
    this->m_res_mask = res_mask;
    this->m_obs_number = 0;
    this->m_obs_token_ptr = NULL;
    this->m_obs_token_len = 0;
    this->m_data_wrapper = NULL;
}

// constructor (strings)
DynamicResource::DynamicResource(const Logger *logger,const string name,const string res_type,const string value,uint8_t res_mask,const bool observable) : Resource<string>(logger,name,value)
{
    this->m_res_type = res_type;
    this->m_observable = observable;
    this->m_res_mask = res_mask;
    this->m_obs_number = 0;
    this->m_obs_token_ptr = NULL;
    this->m_obs_token_len = 0;
    this->m_data_wrapper = NULL;
}

// copy constructor
DynamicResource::DynamicResource(const DynamicResource &resource) : Resource<string>((const Resource<string> &)resource)
{
    this->m_res_type = resource.m_res_type;
    this->m_observable = resource.m_observable;
    this->m_res_mask = resource.m_res_mask;
    this->m_obs_number = resource.m_obs_number;
    this->m_obs_token_ptr = resource.m_obs_token_ptr;
    this->m_obs_token_len = resource.m_obs_token_len;
    this->m_data_wrapper = resource.m_data_wrapper;
}

// destructor
DynamicResource::~DynamicResource()
{
    if(this->m_obs_token_ptr) free(this->m_obs_token_ptr);
}

// bind resource to NSDL
void DynamicResource::bind(void *p)
{
    if (p != NULL) {
        sn_nsdl_resource_info_s *resource_ptr = (sn_nsdl_resource_info_s *)p;
        const uint8_t *name = (const uint8_t *)(this->getName().c_str());
        const uint8_t *res_type = (const uint8_t *)this->m_res_type.c_str();
        int name_length = this->getName().size();
        int res_type_length = this->m_res_type.size();
        int is_observable = 0;
        if (this->m_observable == true) is_observable = 1;
        const string *key = new string(this->getName());
        ipt_helper_add_instance_pointer(key,this);
        nsdl_create_dynamic_resource(resource_ptr,name_length,(uint8_t *)name,res_type_length,(uint8_t *)res_type,is_observable,&ipt_helper_nsdl_callback_stub,this->m_res_mask);
        this->logger()->log("DynamicResource: [%s] type: [%s] bound (observable: %d)",name,res_type,is_observable);
    } else {
        this->logger()->log("DynamicResource: NULL parameter in bind()");
    }
}

// process NSDL message
uint8_t DynamicResource::process(sn_coap_hdr_s *received_coap_ptr, sn_nsdl_addr_s *address, sn_proto_info_s *proto)
{
    sn_coap_hdr_s *coap_res_ptr = 0;

    // create our key for debugging output...
    string key = this->coapDataToString(received_coap_ptr->uri_path_ptr,received_coap_ptr->uri_path_len);

    if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_GET) {
        coap_res_ptr = sn_coap_build_response(received_coap_ptr, COAP_MSG_CODE_RESPONSE_CONTENT);

        // process the GET if we have registered a callback for it...
        if ((this->m_res_mask&SN_GRS_GET_ALLOWED) != 0) {
            // call the resource get() to get the resource value
            this->logger()->log("Calling resource(GET) for [%s]...",key.c_str());
            string value = this->get();

            // convert the string from the GET to something suitable for CoAP payloads          
            if (this->getDataWrapper() != NULL) {
                // wrap the data...
                this->getDataWrapper()->wrap((uint8_t *)value.c_str(),(int)value.size());
                
                // announce (after wrap)
                this->logger()->log("Building payload for [%s]=[%s]...",key.c_str(),this->getDataWrapper()->get());
                
                // fill in the CoAP response payload
                coap_res_ptr->payload_len = this->getDataWrapper()->length();
                coap_res_ptr->payload_ptr = this->getDataWrapper()->get();
            }
            else {
                // announce (no wrap)
                this->logger()->log("Building payload for [%s]=[%s]...",key.c_str(),value.c_str());
                
                // do not wrap the data...
                coap_res_ptr->payload_len = value.size();
                coap_res_ptr->payload_ptr = (uint8_t *)value.c_str();
            }
            
            // Observation handling... 
            if(received_coap_ptr->token_ptr) {
                if(this->m_obs_token_ptr) {
                    free(this->m_obs_token_ptr);
                    this->m_obs_token_ptr = NULL;
                    this->m_obs_token_len = 0;
                }
                
                this->m_obs_token_ptr = (uint8_t*)malloc(received_coap_ptr->token_len);
                if(this->m_obs_token_ptr) {
                    memcpy(this->m_obs_token_ptr, received_coap_ptr->token_ptr,received_coap_ptr->token_len);
                    this->m_obs_token_len = received_coap_ptr->token_len;
                }
            }
            
            // Observation handling...
            if(received_coap_ptr->options_list_ptr && received_coap_ptr->options_list_ptr->observe) {
                coap_res_ptr->options_list_ptr = (sn_coap_options_list_s*)malloc(sizeof(sn_coap_options_list_s));
                memset(coap_res_ptr->options_list_ptr, 0, sizeof(sn_coap_options_list_s));
                coap_res_ptr->options_list_ptr->observe_ptr = &this->m_obs_number;          // see nullify note below...
                coap_res_ptr->options_list_ptr->observe_len = 1;
                this->m_obs_number++;
            }

            // build out the response and send...
            sn_nsdl_send_coap_message(address,coap_res_ptr);
            
            //
            // nullify note: 
            //
            // because our obs_number (assigned to observe_ptr) is part of this object instance, we dont 
            // want to have the underlying free() try to free it... to just nullify here
            //
            if (coap_res_ptr->options_list_ptr) coap_res_ptr->options_list_ptr->observe_ptr = 0;
        } 
        else {
            this->logger()->log("ERROR: resource(GET) mask is munged (mask: 0x%x)",this->m_res_mask);
        }
    } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_PUT) {
        if(received_coap_ptr->payload_len > 0) {
            // process the PUT if we have registered a callback for it...
            if ((this->m_res_mask&SN_GRS_PUT_ALLOWED) != 0) {
                // binder interacts only with strings
                string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);

                // call the resource put() to set the resource value
                this->logger()->log("Calling resource(PUT) with [%s]=[%s]...",key.c_str(),value.c_str());
                this->put(value);

                // build out the response and send...
                this->logger()->log("resource(PUT) completed for [%s]...",key.c_str());
                coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
                sn_nsdl_send_coap_message(address,coap_res_ptr);
            } else {
                this->logger()->log("ERROR: resource(PUT) mask is munged (mask: 0x%x)",this->m_res_mask);
            }
        } else {
            this->logger()->log("ERROR: Binder(PUT) payload is NULL...");
        }
    } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_POST) {
        if(received_coap_ptr->payload_len > 0) {
            // process the POST if we have registered a callback for it...
            if ((this->m_res_mask&SN_GRS_POST_ALLOWED) != 0) {
                // binder interacts only with strings
                string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);

                // call the resource post() to set the resource value
                this->logger()->log("Calling resource(POST) with [%s]=[%s]...",key.c_str(),value.c_str());
                this->post(value);

                // build out the response and send...
                this->logger()->log("resource(POST) completed for [%s]...",key.c_str());
                coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
                sn_nsdl_send_coap_message(address,coap_res_ptr);
            } else {
                this->logger()->log("ERROR: resource(POST) mask is munged (mask: 0x%x)",this->m_res_mask);
            }
        } else {
            this->logger()->log("ERROR: Binder(POST) payload is NULL...");
        }
    } else if(received_coap_ptr->msg_code == COAP_MSG_CODE_REQUEST_DELETE) {
        if(received_coap_ptr->payload_len > 0) {
            // process the DELETE if we have registered a callback for it...
            if ((this->m_res_mask&SN_GRS_DELETE_ALLOWED) != 0) {
                // binder interacts only with strings
                string value = this->coapDataToString(received_coap_ptr->payload_ptr,received_coap_ptr->payload_len);

                // call the resource del() to set the resource value
                this->logger()->log("Calling resource(DELETE) with [%s]=[%s]...",key.c_str(),value.c_str());
                this->del(value);

                // build out the response and send...
                this->logger()->log("resource(DELETE) completed for [%s]...",key.c_str());
                coap_res_ptr = sn_coap_build_response(received_coap_ptr,COAP_MSG_CODE_RESPONSE_CHANGED);
                sn_nsdl_send_coap_message(address,coap_res_ptr);
            } else {
                this->logger()->log("ERROR: resource(DELETE) mask is munged (mask: 0x%x)",this->m_res_mask);
            }
        } else {
            this->logger()->log("ERROR: Binder(DELETE) payload is NULL...");
        }
    }

    sn_coap_parser_release_allocated_coap_msg_mem(coap_res_ptr);
    
    return 0;
}

// send the notification
int DynamicResource::notify(const string data) {
    return this->notify((uint8_t *)data.c_str(),(int)data.length());
}

// send the notification
int DynamicResource::notify(uint8_t *data,int data_length) {
    int status = sn_nsdl_send_observation_notification(this->m_obs_token_ptr,this->m_obs_token_len,data,data_length,&this->m_obs_number,1,COAP_MSG_TYPE_NON_CONFIRMABLE,0);
    if (status == 0) {
        this->logger()->log("ERROR: resource(NOTIFY) send failed...");
    }
    return status;
}

// default PUT (does nothing)
void DynamicResource::put(const string value)
{
    // not used by default
    ;
}

// default POST (does nothing)
void DynamicResource::post(const string value)
{
    // not used by default
    ;
}

// default DELETE (does nothing)
void DynamicResource::del(const string value)
{
    // not used by default
    ;
}

// convenience method to get the URI from its buffer field...
string DynamicResource::coapDataToString(uint8_t *coap_data_ptr,int coap_data_ptr_length)
{
    if (coap_data_ptr != NULL && coap_data_ptr_length > 0) {
        if (this->getDataWrapper() != NULL) {
            // unwrap the data...
            this->getDataWrapper()->unwrap(coap_data_ptr,coap_data_ptr_length);
            char *buf = (char *)this->getDataWrapper()->get();                  // assumes data is null terminated in DataWrapper...
            return string(buf);
        }
        else {
            // no unwrap of the data...
            char buf[MAX_VALUE_BUFFER_LENGTH+1];
            memset(buf,0,MAX_VALUE_BUFFER_LENGTH+1);
            memcpy(buf,(char *)coap_data_ptr,coap_data_ptr_length);
            return string(buf);
        }
    }
    return string("");
}