User Tools

Site Tools


request_and_response

Querying and Setting Resource States

This article follows the sections on Initialize & Setting and Resource Find & Registration. Now, we'll cover how to communicate with resources.

Querying Resource State Using [GET]

This operation fetches the value of a simple resource. In this example, we fetch the state from the light resource.

Sequence Diagram

Querying Resource State Steps:

  1. The client application calls resource.get(…) to retrieve a representation from the resources.
  2. The call is marshalled to the stack which is either running in-process or out-of-process (daemon).
  3. The C API is called to dispatch the request. The call may look like the following
    OCDoResource(OC_REST_GET, "//192.168.1.11/light/1, 0, 0, OC_CONFIRMABLE, callback);
  4. Where CoAP is used as a transport, the lower stack will send a GET request to the target server.
  5. On the server side, the OCProcess() function (message pump) receives and parses the request from the socket, then dispatches it to the correct entity handler based on the URI of the request.
  6. Where the C++ API is used, the C++ entity handler parses the payload and marshals it to the client application depending on if the server stack is running in-process or out-of-process (daemon).
  7. The C++ SDK passes it up the C++ handler associated with the OCResource.
  8. The handler returns the result code and representation to the SDK.
  9. The SDK marshals the result code and representation to the C++ entity handler.
  10. The entity handler returns the result code and representation to the CoAP protocol.
  11. The CoAP protocol transports the results to the client device.
  12. The results are returned the OCDoResource callback.
  13. The results are returned to the C++ client application's asyncResultCallback.

C SDK

This section will cover the process of querying resource states using the C API. If you're working in C++ skip to the following section.

Here are the relevant functions.

From iotivity/resource/csdk/stack/include/ocstack.h:

  • OCDoResource (client) - Discovers or performs requests on a resource that's specified by a URI.
  • OCDoResponse (server) - Sends a response to a request.

Please refer to the OC Stack section of the CSDK API documentation for more information.

OCDoResource (client)

OCDoResource is used to make a request from a resource in the following way:

        OCMethod method = OC_REST_POST;
        OCCallbackData cbData = { NULL, NULL, NULL };
        cbData.cb = handleGet;

        OIC_LOG_V(INFO,TAG,"loop: about to %d",method);
        result = OCDoResource(&handle, method, requestUri, &destination, payload,
                           ConnectivityType, qos, &cbData, NULL,0);

Where handleGet is a user's callback function.

OCDoResponse (server)

First, start with an OCEntityHandlerRequest object that is used server-side from the callback function we previously passed to OCCreateResource in the Resource registering and discovery guide.

As a reminder :

  OCStackResult res = OCCreateResource(&Light.handle,
                                       "core.light",
                                       OC_RSRVD_INTERFACE_DEFAULT,
                                       "/light/1",
                                       handleOCEntity,
                                       NULL,
                                       OC_DISCOVERABLE | OC_OBSERVABLE);

Then, the user's callback function can return the state of the resource. In this case, whether the light is on or off.

OCEntityHandlerResult handleOCEntity(OCEntityHandlerFlag flag,
                                     OCEntityHandlerRequest *entityHandlerRequest,
                                     void *callbackParam)
{

    if (entityHandlerRequest && (flag & OC_REQUEST_FLAG))
    {
        OIC_LOG(INFO, TAG, "Flag includes OC_REQUEST_FLAG");

        if (OC_REST_GET == entityHandlerRequest->method)
        {
        // ...
        }
        else if (OC_REST_POST == entityHandlerRequest->method)
        {
        // ...
        }
    }
}

The next section on responding to requests provides full processing details.

C++ SDK

With the C++ SDK, a resource state can be requested, using GET or PUT operations.

Here are the methods that are called:

The details of everything that's being called here will be covered in a later section.

Querying resource State [GET] in C++ [Client]

// Local function to get representation of light resource
void getLightRepresentation(std::shared_ptr<OCResource> resource)
{
  if(resource)
  {
    std::cout << "Getting Light Representation..."<<std::endl;
    // Invoke resource's get API with the callback parameter 
    QueryParamsMap test;
    resource->get(test, &onGet);
  }
}
// callback handler on GET request
void onGet(const OCRepresentation& rep, const int eCode)
{
  if(eCode == SUCCESS_RESPONSE)
  {
    std::cout << "GET request was successful" << std::endl;
    AttributeMap attributeMap = rep.getAttributeMap();
    std::cout << "Resource URI: " << rep.getUri() << std::endl;
    for(auto it = attributeMap.begin(); it != attributeMap.end(); ++it)
    {
      std::cout << "\tAttribute name: "<< it->first << " value: ";
      for(auto valueItr = it->second.begin(); valueItr != it->second.end(); ++valueItr)
      {
        std::cout <<"\t"<< *valueItr << " ";
      }
      std::cout << std::endl;
    }
    std::vector<OCRepresentation> children = rep.getChildren();
    for(auto oit = children.begin(); oit != children.end(); ++oit)
    {
      std::cout << "Child Resource URI: " << oit->getUri() << std::endl;
      attributeMap = oit->getAttributeMap();
      for(auto it = attributeMap.begin(); it != attributeMap.end(); ++it)
      {
        std::cout << "\tAttribute name: "<< it->first << " value: ";
        for(auto valueItr = it->second.begin(); valueItr != it->second.end(); ++valueItr)
        { 
          std::cout <<"\t"<< *valueItr << " ";
        }
        std::cout << std::endl;
      }
    }
    putLightRepresentation(curResource);
  }
  else
  {
    std::cout << "onGET Response error: " << eCode << std::endl;
    std::exit(-1);
  }
}

Querying resource State [GET] in C++ [Server]

// Handling GET request in Entity handler
if(requestType == "GET")
  {
    cout << "\t\t\trequestType : GET\n";
    // Check for query params (if any)
    QueryParamsMap queryParamsMap = request->getQueryParameters();
    cout << "\t\t\tquery params: \n";
    for(QueryParamsMap::iterator it = queryParamsMap.begin(); it != queryParamsMap.end(); it++)
    {
      cout << "\t\t\t\t" << it->first << ":" << it->second << endl;
    }
    // Process query params and do required operations ..
    // Get the representation of this resource at this point and send it as response
    // AttributeMap attributeMap;
    OCRepresentation rep;
    rep = myLightResource.getRepresentation();
    if(response)
    {
      // TODO Error Code
      response->setErrorCode(200);
      auto findRes = queryParamsMap.find("if");
      if(findRes != queryParamsMap.end())
      {
        response->setResourceRepresentation(rep, findRes->second);
      }
      else
      {
        response->setResourceRepresentation(rep, DEFAULT_INTERFACE);
      }
    }
}

Over the air request

In this example, we are querying state from one of the lights. At this point, the resource was discovered by its type, and we understand its interface and the attributes that the resource exposes.

Field Value Note(s)
Address 192.168.1.11:5683 Unicast packet
Header CON, GET, MID=0x7d42
URI-Path light “/light/1”
URI-Path 1
Accept application/json

Over the air response

Assuming that the request is valid, we expect the following reply from the resource.

From 192.168.1.11:

Field Value Explanation
Address 192.168.1.1:5683 Client Address
Header ACK, CONTENT, MID=0x7d42 Success w/Content
Content Type application/json
Payload { “power” : 0, “level” : 10 }

Setting a resource state using [PUT]

This operation sets the value of a simple resource. In this example, we turn on a light resource and set the brightness to 50%.

Sequence Diagram

Setting a resource state [PUT]

Steps:

  1. The client application calls resource.put(…) to set representation of resource. Example call: resource.put(attributeMap, queryParamsMap, &onPut);
  2. Client SDK internally calls the setResourceAttributes function of the client wrapper. Example call:
    OCDoResource(OC_REST_PUT, "//192.168.1.11/light/1, 0, 0, OC_CONFIRMABLE, callback);
  3. Send PUT request to remote device
  4. The OCProcess() service function (server-side message pump) reads the packet from the socket and dispatches the request to the entity handler for the provided URI.
  5. The entity handler, which was provided by an upper layer when the resource was registered, parses the representation and in the case of the C++ API passes the results to the upper layer handler. In a C only environment, the results would also be processed in the entity handler.
  6. The upper layer entity handler written by the app developer/vendor is invoked, and the response is formed accordingly.
  7. The upper layer entity handler returns success or failure with a response.
  8. Returns success or failure to lower layer for transmission to client.
  9. Returns success or failure to lower layer for transmission to client.
  10. Returns success or failure to lower layer for transmission to client.
  11. Result is formatted and sent over network to client
  12. The OCProcess() service function (client-side message pump) reads results and passes the results back to the client application via the callback passed to OCDoResource

Set Resource's State [PUT] in C++ [Client]

void putLightRepresentation(std::shared_ptr<OCResource> resource)
{
  if(resource)
  {
    OCRepresentation rep;
 
    std::cout << "Putting light representation..."<<std::endl;
    // Create AttributeMap
    AttributeMap attributeMap;
    // Add the attribute name and values in the attribute map
    AttributeValues stateVal;
    stateVal.push_back("true");
 
    AttributeValues powerVal;
    powerVal.push_back("10");
 
    attributeMap["state"] = stateVal;
    attributeMap["power"] = powerVal;
 
    // Create QueryParameters Map and add query params (if any)
    QueryParamsMap queryParamsMap;
 
    rep.setAttributeMap(attributeMap);
 
    // Invoke resource's put API with attribute map, query map and the callback parameter
    resource->put(rep, queryParamsMap, &onPut);
  }
}
 
// callback handler on PUT request
void onPut(const OCRepresentation& rep, const int eCode)
{
  if(eCode == SUCCESS_RESPONSE)
  {
    std::cout << "PUT request was successful" << std::endl;
 
    AttributeMap attributeMap = rep.getAttributeMap();
 
    for(auto it = attributeMap.begin(); it != attributeMap.end(); ++it)
    {
      std::cout << "\tAttribute name: "<< it->first << " value: ";
      for(auto valueItr = it->second.begin(); valueItr != it->second.end(); ++valueItr)
      {
        std::cout <<"\t"<< *valueItr << " ";
      }
      std::cout << std::endl;
    }
 
    std::vector<OCRepresentation> children = rep.getChildren();
 
    for(auto oit = children.begin(); oit != children.end(); ++oit)
    {
      attributeMap = oit->getAttributeMap();
 
      for(auto it = attributeMap.begin(); it != attributeMap.end(); ++it)
      {
        std::cout << "\tAttribute name: "<< it->first << " value: ";
        for(auto valueItr = it->second.begin(); valueItr != it->second.end(); ++valueItr)
        {
          std::cout <<"\t"<< *valueItr << " ";
        }
        std::cout << std::endl;
      }
    }
  }
  else
  {
    std::cout << "onPut Response error: " << eCode << std::endl;
std::exit(-1);
  }

Set Resource's State [PUT] in C++ [Server]

//Entity handle sample for PUT
  if(requestType == "PUT")
  {
    cout << "\t\t\trequestType : PUT\n";
 
    // Check for query params (if any)
    QueryParamsMap queryParamsMap = request->getQueryParameters();
 
    cout << "\t\t\tquery params: \n";
    for(auto it = queryParamsMap.begin(); it != queryParamsMap.end(); it++)
    {
      cout << "\t\t\t\t" << it->first << ":" << it->second << endl;
    }
 
    // Get the representation from the request
    OCRepresentation rep = request->getResourceRepresentation();
 
    myLightResource.setRepresentation(rep); // See code snippet below
 
    // Do related operations related to PUT request // See code snippet below
    rep = myLightResource.getRepresentation();
 
    if(response)
    {
      response->setErrorCode(200);
 
      auto findRes = queryParamsMap.find("if");
 
      if(findRes != queryParamsMap.end())
      {
 
        response->setResourceRepresentation(rep, findRes->second);
      }
      else
      {
        response->setResourceRepresentation(rep, DEFAULT_INTERFACE);
      }
    }
  }
  void setRepresentation(OCRepresentation& light)
  {
    AttributeMap attributeMap = light.getAttributeMap();
 
    if(attributeMap.find("state") != attributeMap.end() && attributeMap.find("power") != attributeMap.end())
    {
      cout << "\t\t\t" << "Received representation: " << endl;
      cout << "\t\t\t\t" << "power: " << attributeMap["power"][0] << endl;
      cout << "\t\t\t\t" << "state: " << attributeMap["state"][0] << endl;
 
      m_state = attributeMap["state"][0].compare("true") == 0;
      m_power= std::stoi(attributeMap["power"][0]);
    }
  }
 
  OCRepresentation getRepresentation()
  {
    OCRepresentation light;
 
    light.setUri(m_lightUri);
 
    std::vector<std::string> interfaces;
    //interfaces.push_back(m_lightInterface);
 
    light.setResourceInterfaces(interfaces);
 
    std::vector<std::string> types;
    //types.push_back(m_lightType);
 
    light.setResourceTypes(types);
 
    AttributeMap attributeMap;
    AttributeValues stateVal;
    if(m_state)
    {
      stateVal.push_back("true");
    }
    else
    {
      stateVal.push_back("false");
    }
 
    AttributeValues powerVal;
    powerVal.push_back(to_string(m_power));
 
    attributeMap["state"] = stateVal;
    attributeMap["power"] = powerVal;
 
    light.setAttributeMap(attributeMap);
 
    return light;
  }

Over the air request

In this example, we are pushing state to one of the lights. At this point, the resource was discovered by its type, and we understand its interface and the attributes exposed by the resource.

Field Value Note(s)
Address 192.168.1.13:5683 Unicast packet
Header CON, PUT, MID=0x7d41 Confirmation is requested
URI-Path light “/light/1”
URI-Path 1
Content-Type application/json
Payload { “power” : 1, “level” : 5 }

Over the air response

Assuming that the request is valid and the resource is able to complete the transition, the following represents a successful change in state.

From 192.168.1.13:

Field Value Explanation
Address 192.168.1.1:5683 Client Address
Header ACK, CHANGED, MID=0x7d41 Success (changed)

Demo example

The rest of this article will cover elements from the following two examples:

To run these examples, you will need to build them for your system. The directory they are run from will vary depending on where you output the built code.

Once you have the examples built, start the simple server, which will emulate an OCF/OIC device; in this case, a light:

$ cd /opt/iotivity/examples/resource/cpp/ 
$ ./simpleserver
 
Usage : simpleserver 
    Default - Non-secure resource and notify all observers
    1 - Non-secure resource and notify list of observers
 
    2 - Secure resource and notify all observers
    3 - Secure resource and notify list of observers
 
    4 - Non-secure resource, GET slow response, notify all observers
Created resource.
Added Interface and Type
Waiting

Now start the simple client in the another shell session:

$ cd /opt/iotivity/examples/resource/cpp/ 
$ ./simpleclient
 
---------------------------------------------------------------------
Usage : simpleclient 
   ObserveType : 1 - Observe
   ObserveType : 2 - ObserveAll
---------------------------------------------------------------------
 
 
 
Finding Resource...
Finding Resource for second time...
In foundResource
In foundResource
Found resource 4126ec5c-16ce-4b9b-84e6-15ad6e268774In foundResource
/oic/d for the first time on server with ID: 4126ec5c-16ce-4b9b-84e6-15ad6e268774
DISCOVERED Resource:
Found resource 4126ec5c-16ce-4b9b-84e6-15ad6e268774/oic/p for the first time on server with ID:         URI of the resource: /oic/d4126ec5c-16ce-4b9b-84e6-15ad6e268774
 
DISCOVERED Resource:
        URI of the resource: /oic/p
Found resource 4126ec5c-16ce-4b9b-84e6-15ad6e268774/a/light for the first time on server with ID:       Host address of the resource: coap://[fe80::b82b:e6ff:fe30:8efd]:44475
        List of resource types:
        Host address of the resource:           oic.wk.d
        List of resource interfaces:
4126ec5c-16ce-4b9b-84e6-15ad6e268774            oic.if.baseline
                oic.if.r
coap://[fe80::b82b:e6ff:fe30:8efd]:44475
        List of resource types:
 
                oic.wk.p
        List of resource interfaces:
DISCOVERED Resource:            oic.if.baseline
        URI of the resource: /a/light
 
                oic.if.r
        Host address of the resource: coap://[fe80::b82b:e6ff:fe30:8efd]:44475
        List of resource types:
                core.light
                core.brightlight
        List of resource interfaces:
                oic.if.baseline
                oic.if.ll
Getting Light Representation...

The OIC specifications describe a RESTful model of communications, where everything is modeled as resources and web-style commands are used to operate on representations of these resources, such as GET, POST, PUT, etc. The communication model is called CRUDN (Create, Retrieve, Update, Delete, Notify). In this example, the IoTivity server sets up resources to describe the (emulated) OIC device. Once the client starts, it tries to discover if there are interesting resources on the network using a multicast GET; the server responds to this with the resources it is hosting. In our example, there is a light resource and a brightlight resource (meaning the light that can have brightness controlled). At this point, the two can speak to each other with the client driving the conversation.

We can see the client getting a representation of the state of the emulated light:

GET request was successful
Resource URI: /a/light
        state: false
        power: 0
        name: John's light
	
GET request was successful
Resource URI: /a/light
        state: false
        power: 0
        name: John's light

It then creates a state on the server that has a power value of 15.

Putting light representation...
PUT request was successful
        state: true
        power: 15
        name: John's light
	
Putting light representation...
PUT request was successful
        state: true
        power: 15
        name: John's light

Next the client tries to update the light state to a value of 55:

Posting light representation...
POST request was successful
        Uri of the created resource: /a/light1
Posting light representation...
POST request was successful
        state: true
        power: 55
        name: John's light
	
Posting light representation...
POST request was successful
        Uri of the created resource: /a/light1
Posting light representation...
POST request was successful
        state: true
        power: 55
        name: John's light

Next Steps

Once you've reviewed the materials of this document, the next step is to read the wiki page on responding to requests

Additional Resources

request_and_response.txt · Last modified: 2017/04/03 21:27 by Ben Lloyd Pearson