1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
18 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
34 #include "occlientbasicops.h"
36 static int UNICAST_DISCOVERY = 0;
37 static int TEST_CASE = 0;
39 static const char * TEST_APP_UNICAST_DISCOVERY_QUERY = "coap://0.0.0.0:5683/oc/core";
41 static std::string putPayload = "{\"state\":\"off\",\"power\":10}";
43 static std::string coapServerIP = "255.255.255.255";
44 static std::string coapServerPort = "5683";
45 static std::string coapServerResource = "/a/led";
50 typedef std::pair<bool, std::string> extract_result_t;
51 typedef std::string sid_t;
52 typedef std::set<std::string> SID_set_t;
55 void collateSIDs(const OCClientResponse * clientResponse, const std::string& server_ip);
57 /* SIGINT handler: set gQuitFlag to 1 for graceful termination */
58 void handleSigInt(int signum)
66 static void PrintUsage()
68 OC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1|2|3>");
69 OC_LOG(INFO, TAG, "-u <0|1> : Perform multicast/unicast discovery of resources");
70 OC_LOG(INFO, TAG, "-t 1 : Discover Resources");
71 OC_LOG(INFO, TAG, "-t 2 : Discover Resources and"
72 " Initiate Nonconfirmable Get/Put/Post Requests");
73 OC_LOG(INFO, TAG, "-t 3 : Discover Resources and Initiate Confirmable Get/Put/Post Requests");
76 OCStackResult InvokeOCDoResource(std::ostringstream &query,
77 OCMethod method, OCQualityOfService qos,
78 OCClientResponseHandler cb, OCHeaderOption * options, uint8_t numOptions)
81 OCCallbackData cbData;
85 cbData.context = (void*)DEFAULT_CONTEXT_VALUE;
89 // TODO-CA: The adapter type is set to WiFi but should be configurable - add as API param
90 ret = OCDoResource(&handle, method, query.str().c_str(), 0,
91 (method == OC_REST_PUT || method == OC_REST_POST) ? putPayload.c_str() : NULL,
92 OC_WIFI, qos, &cbData, options, numOptions);
94 ret = OCDoResource(&handle, method, query.str().c_str(), 0,
95 (method == OC_REST_PUT || method == OC_REST_POST) ? putPayload.c_str() : NULL,
96 qos, &cbData, options, numOptions);
99 if (ret != OC_STACK_OK)
101 OC_LOG_V(ERROR, TAG, "OCDoResource returns error %d with method %d", ret, method);
107 OCStackApplicationResult putReqCB(void* ctx, OCDoHandle handle, OCClientResponse * clientResponse)
109 if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
111 OC_LOG(INFO, TAG, "Callback Context for PUT recvd successfully");
116 OC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
117 OC_LOG_V(INFO, TAG, "JSON = %s =============> Put Response", clientResponse->resJSONPayload);
119 return OC_STACK_DELETE_TRANSACTION;
122 OCStackApplicationResult postReqCB(void *ctx, OCDoHandle handle, OCClientResponse *clientResponse)
124 if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
126 OC_LOG(INFO, TAG, "Callback Context for POST recvd successfully");
131 OC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
132 OC_LOG_V(INFO, TAG, "JSON = %s =============> Post Response",
133 clientResponse->resJSONPayload);
135 return OC_STACK_DELETE_TRANSACTION;
138 OCStackApplicationResult getReqCB(void* ctx, OCDoHandle handle, OCClientResponse * clientResponse)
140 if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
142 OC_LOG(INFO, TAG, "Callback Context for GET query recvd successfully");
147 OC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
148 OC_LOG_V(INFO, TAG, "SEQUENCE NUMBER: %d", clientResponse->sequenceNumber);
149 OC_LOG_V(INFO, TAG, "JSON = %s =============> Get Response",
150 clientResponse->resJSONPayload);
152 if(clientResponse->rcvdVendorSpecificHeaderOptions &&
153 clientResponse->numRcvdVendorSpecificHeaderOptions)
155 OC_LOG (INFO, TAG, "Received vendor specific options");
157 OCHeaderOption * rcvdOptions = clientResponse->rcvdVendorSpecificHeaderOptions;
158 for( i = 0; i < clientResponse->numRcvdVendorSpecificHeaderOptions; i++)
160 if(((OCHeaderOption)rcvdOptions[i]).protocolID == OC_COAP_ID)
162 OC_LOG_V(INFO, TAG, "Received option with OC_COAP_ID and ID %u with",
163 ((OCHeaderOption)rcvdOptions[i]).optionID );
164 OC_LOG_BUFFER(INFO, TAG, ((OCHeaderOption)rcvdOptions[i]).optionData,
165 ((OCHeaderOption)rcvdOptions[i]).optionLength);
169 return OC_STACK_DELETE_TRANSACTION;
172 // This is a function called back when a device is discovered
173 OCStackApplicationResult discoveryReqCB(void* ctx, OCDoHandle handle,
174 OCClientResponse * clientResponse)
176 uint8_t remoteIpAddr[4];
177 uint16_t remotePortNu;
179 if (ctx == (void*)DEFAULT_CONTEXT_VALUE)
181 OC_LOG(INFO, TAG, "Callback Context for DISCOVER query recvd successfully");
186 OC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
188 OCDevAddrToIPv4Addr((OCDevAddr *) clientResponse->addr, remoteIpAddr,
189 remoteIpAddr + 1, remoteIpAddr + 2, remoteIpAddr + 3);
190 OCDevAddrToPort((OCDevAddr *) clientResponse->addr, &remotePortNu);
193 "Device =============> Discovered %s @ %d.%d.%d.%d:%d",
194 clientResponse->resJSONPayload, remoteIpAddr[0], remoteIpAddr[1],
195 remoteIpAddr[2], remoteIpAddr[3], remotePortNu);
197 parseClientResponse(clientResponse);
199 collateSIDs(clientResponse, getIPAddrTBServer(clientResponse));
203 case TEST_NON_CON_OP:
204 InitGetRequest(OC_LOW_QOS);
206 InitPostRequest(OC_LOW_QOS);
209 InitGetRequest(OC_HIGH_QOS);
211 InitPostRequest(OC_HIGH_QOS);
219 return (UNICAST_DISCOVERY) ? OC_STACK_DELETE_TRANSACTION : OC_STACK_KEEP_TRANSACTION ;
224 OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
225 std::ostringstream query;
226 query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
227 return (InvokeOCDoResource(query, OC_REST_PUT, OC_LOW_QOS, putReqCB, NULL, 0));
230 int InitPostRequest(OCQualityOfService qos)
232 OCStackResult result;
233 OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
234 std::ostringstream query;
235 query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
237 // First POST operation (to create an LED instance)
238 result = InvokeOCDoResource(query, OC_REST_POST,
239 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
241 if (OC_STACK_OK != result)
243 // Error can happen if for example, network connectivity is down
244 OC_LOG(INFO, TAG, "First POST call did not succeed");
247 // Second POST operation (to create an LED instance)
248 result = InvokeOCDoResource(query, OC_REST_POST,
249 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
251 if (OC_STACK_OK != result)
253 OC_LOG(INFO, TAG, "Second POST call did not succeed");
256 // This POST operation will update the original resourced /a/led
257 return (InvokeOCDoResource(query, OC_REST_POST,
258 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
259 postReqCB, NULL, 0));
262 int InitGetRequest(OCQualityOfService qos)
264 OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
265 std::ostringstream query;
266 query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
268 return (InvokeOCDoResource(query, OC_REST_GET, (qos == OC_HIGH_QOS)?
269 OC_HIGH_QOS:OC_LOW_QOS, getReqCB, NULL, 0));
275 OCCallbackData cbData;
277 /* Start a discovery query*/
278 char szQueryUri[64] = { 0 };
279 if (UNICAST_DISCOVERY)
281 strcpy(szQueryUri, TEST_APP_UNICAST_DISCOVERY_QUERY);
285 strcpy(szQueryUri, OC_WELL_KNOWN_QUERY);
287 cbData.cb = discoveryReqCB;
288 cbData.context = (void*)DEFAULT_CONTEXT_VALUE;
291 // TODO-CA: The adapter type is set to all but should be configurable - add as API param
292 ret = OCDoResource(&handle, OC_REST_GET, szQueryUri, 0, 0, (OC_ETHERNET | OC_WIFI),
293 OC_LOW_QOS, &cbData, NULL, 0);
295 ret = OCDoResource(&handle, OC_REST_GET, szQueryUri, 0, 0, OC_LOW_QOS, &cbData, NULL, 0);
297 if (ret != OC_STACK_OK)
299 OC_LOG(ERROR, TAG, "OCStack resource error");
304 int main(int argc, char* argv[])
306 uint8_t addr[20] = {0};
307 uint8_t* paddr = NULL;
308 uint16_t port = USE_RANDOM_PORT;
309 uint8_t ifname[] = "eth0";
312 while ((opt = getopt(argc, argv, "u:t:")) != -1)
317 UNICAST_DISCOVERY = atoi(optarg);
320 TEST_CASE = atoi(optarg);
328 if ((UNICAST_DISCOVERY != 0 && UNICAST_DISCOVERY != 1) ||
329 (TEST_CASE < TEST_DISCOVER_REQ || TEST_CASE >= MAX_TESTS) )
336 /*Get Ip address on defined interface and initialize coap on it with random port number
337 * this port number will be used as a source port in all coap communications*/
338 if ( OCGetInterfaceAddress(ifname, sizeof(ifname), AF_INET, addr,
339 sizeof(addr)) == ERR_SUCCESS)
341 OC_LOG_V(INFO, TAG, "Starting occlient on address %s",addr);
345 /* Initialize OCStack*/
346 if (OCInit((char *) paddr, port, OC_CLIENT) != OC_STACK_OK)
348 OC_LOG(ERROR, TAG, "OCStack init error");
354 // Break from loop with Ctrl+C
355 signal(SIGINT, handleSigInt);
359 if (OCProcess() != OC_STACK_OK)
361 OC_LOG(ERROR, TAG, "OCStack process error");
367 OC_LOG(INFO, TAG, "Exiting occlient main loop...");
369 if (OCStop() != OC_STACK_OK)
371 OC_LOG(ERROR, TAG, "OCStack stop error");
377 std::string getIPAddrTBServer(OCClientResponse * clientResponse)
379 if(!clientResponse) return "";
380 if(!clientResponse->addr) return "";
381 uint8_t a, b, c, d = 0;
382 if(0 != OCDevAddrToIPv4Addr(clientResponse->addr, &a, &b, &c, &d) ) return "";
384 char ipaddr[16] = {'\0'};
385 // ostringstream not working correctly here, hence snprintf
386 snprintf(ipaddr, sizeof(ipaddr), "%d.%d.%d.%d", a,b,c,d);
387 return std::string (ipaddr);
391 std::string getPortTBServer(OCClientResponse * clientResponse)
393 if(!clientResponse) return "";
394 if(!clientResponse->addr) return "";
396 if(0 != OCDevAddrToPort(clientResponse->addr, &p) ) return "";
397 std::ostringstream ss;
402 std::string getQueryStrForGetPut(OCClientResponse * clientResponse)
408 /* You could do this with the JSON parser of your choice, a regular expression, PEG
409 grammar, etc.. This "sample" version does not handle cases like escaping strings,
410 but shows a simple way you might approach the task with just the C++98 library: */
411 extract_result_t extract_value(const std::string& search_key, const std::string& input)
413 const std::string key('\"' + search_key + "\":\"");
415 const size_t key_mark = input.find(key);
416 const size_t key_edge = key_mark + key.length();
417 const size_t val_mark = input.find_first_of("\"", key_edge);
419 if(std::string::npos == key_mark || std::string::npos == val_mark) {
420 std::ostringstream os;
422 os << "extract_value(): \"" << search_key << "\" not found in input";
423 OC_LOG(ERROR, TAG, os.str().c_str());
425 return std::make_pair(false, "");
428 return std::make_pair(true, input.substr(key_edge, val_mark - key_edge));
431 extract_result_t parseSID(const OCClientResponse * const clientResponse)
433 const char* const& pl_in = reinterpret_cast<const char *>(clientResponse->resJSONPayload);
435 const std::string pl(pl_in, strlen(pl_in));
437 const extract_result_t sid = extract_value("sid", pl);
438 const extract_result_t uri = extract_value("href", pl);
439 //TODO-CA: It should be just enough to send the SID alone and not the combination
441 return std::make_pair(sid.first and uri.first, sid.second + ':' + uri.second);
444 void parseClientResponse(OCClientResponse * clientResponse)
446 coapServerIP = getIPAddrTBServer(clientResponse);
447 coapServerPort = getPortTBServer(clientResponse);
448 coapServerResource = getQueryStrForGetPut(clientResponse);
451 void collateSIDs(const OCClientResponse * clientResponse, const std::string& server_ip)
453 static SID_set_t sids;
455 const extract_result_t sid_result = parseSID(clientResponse);
457 if(false == sid_result.first)
460 const sid_t& sid = sid_result.second;
462 /* ...there's no need for an application to take any special action, but we can tell
463 if we've seen a resource before, regardless of the transport it arrive on: */
464 std::ostringstream msg;
466 if(not sids.insert(sid).second)
467 msg << "SID " << sid << " has been seen before.\n";
469 msg << "SID " << sid << " is new.\n";
471 OC_LOG(INFO, TAG, msg.str().c_str());