8bbe52f6dca39504db7de1a8caa0e3980402d008
[platform/upstream/iotivity.git] / service / things-manager / sdk / src / GroupManager.cpp
1 //******************************************************************
2 //
3 // Copyright 2014 Samsung Electronics All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
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 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 /**
22  * @file
23  *
24  */
25
26 #include <algorithm>
27 #include <thread>
28 #include <unistd.h>
29 #include <string.h>
30
31 #include "GroupManager.h"
32
33 #define ACTION_DELIMITER "*"
34 #define DESC_DELIMITER "|"
35 #define ATTR_DELIMITER "="
36
37 using namespace OC;
38 using namespace OIC;
39
40 std::map< std::vector< std::string >, CandidateCallback > candidateRequest;
41 std::map< std::vector< std::string >, CandidateCallback > candidateRequestForTimer;
42 std::map< std::string, std::map< std::string, std::shared_ptr< OCResource > > > rtForResourceList;
43 std::vector< std::string > allFoundResourceTypes;
44 std::mutex callbackLock;
45
46
47 template< typename T >
48 bool IsSubset(std::vector< T > full, std::vector< T > sub)
49 {
50     std::sort(full.begin(), full.end());
51     std::sort(sub.begin(), sub.end());
52     return std::includes(full.begin(), full.end(), sub.begin(), sub.end());
53 }
54 std::vector< std::string > &str_split(const std::string &s, char delim,
55         std::vector< std::string > &elems)
56 {
57     std::stringstream ss(s);
58     std::string item;
59     while (std::getline(ss, item, delim))
60     {
61         elems.push_back(item);
62     }
63     return elems;
64 }
65
66 std::vector< std::string > str_split(const std::string &s, char delim)
67 {
68     std::vector< std::string > elems;
69     str_split(s, delim, elems);
70     return elems;
71 }
72
73 void GroupManager::onFoundResource(std::shared_ptr< OCResource > resource, int waitsec)
74 {
75
76     std::string resourceURI;
77     std::string hostAddress;
78     try
79     {
80         // Do some operations with resource object.
81         if (resource)
82         {
83
84             std::cout << "DISCOVERED Resource:" << std::endl;
85             // Get the resource URI
86             resourceURI = resource->uri();
87             std::cout << "\tURI of the resource: " << resourceURI << std::endl;
88
89             // Get the resource host address
90             hostAddress = resource->host();
91             std::cout << "\tHost address of the resource: " << hostAddress << std::endl;
92
93             // Get the resource types
94             std::cout << "\tList of resource types: " << std::endl;
95
96             hostAddress.append(resourceURI);
97
98             for (auto &resourceTypes : resource->getResourceTypes())
99             {
100                 std::cout << "\t\t" << resourceTypes << std::endl;
101
102                 if (std::find(allFoundResourceTypes.begin(), allFoundResourceTypes.end(),
103                         resourceTypes) == allFoundResourceTypes.end())
104                 {
105                     allFoundResourceTypes.push_back(resourceTypes);
106                 }
107
108                 rtForResourceList[resourceTypes][hostAddress] = resource;
109             }
110
111             // Get the resource interfaces
112             std::cout << "\tList of resource interfaces: " << std::endl;
113             for (auto &resourceInterfaces : resource->getResourceInterfaces())
114             {
115                 std::cout << "\t\t" << resourceInterfaces << std::endl;
116             }
117
118             if (waitsec == -1)
119             {
120                 findPreparedRequest(candidateRequest);
121             }
122         }
123         else
124         {
125             // Resource is invalid
126             std::cout << "Resource is invalid" << std::endl;
127         }
128
129     }
130     catch (std::exception& e)
131     {
132         //log(e.what());
133     }
134 }
135
136 GroupManager::GroupManager(void)
137 {
138     ;
139 }
140
141 /**
142  * Virtual destructor
143  */
144 GroupManager::~GroupManager(void)
145 {
146     candidateRequest.clear();
147     candidateRequestForTimer.clear();
148     rtForResourceList.clear();
149     allFoundResourceTypes.clear();
150 }
151
152 void GroupManager::findPreparedRequest(
153         std::map< std::vector< std::string >, CandidateCallback > &request)
154 {
155     std::lock_guard<std::mutex> lock(callbackLock);
156     std::vector< std::shared_ptr< OCResource > > resources;
157
158     for (auto it = request.begin(); it != request.end();)
159     {
160
161         if (IsSubset(allFoundResourceTypes, it->first))
162         {
163             for (unsigned int i = 0; i < it->first.size(); ++i)
164             {
165
166                 for (auto secondIt = rtForResourceList[it->first.at(i)].begin();
167                         secondIt != rtForResourceList[it->first.at(i)].end(); ++secondIt)
168                 {
169                     //insert resource related to request
170                     resources.push_back(secondIt->second);
171                 }
172             }
173
174             it->second(resources);
175
176             //TODO : decide policy - callback only once
177             request.erase(it++);
178         }
179         else
180         {
181             ++it;
182         }
183
184     }
185
186 }
187
188 void GroupManager::lazyCallback(int second)
189 {
190     sleep(second);
191     findPreparedRequest(candidateRequestForTimer);
192
193 }
194
195 OCStackResult GroupManager::findCandidateResources(
196         std::vector< std::string > resourceTypes,
197         CandidateCallback callback, int waitsec)
198 {
199     if (resourceTypes.size() < 1)
200     {
201         return OC_STACK_ERROR;
202     }
203     if(callback == NULL)
204     {
205         return OC_STACK_ERROR;
206     }
207
208     std::sort(resourceTypes.begin(), resourceTypes.end());
209     resourceTypes.erase(std::unique(resourceTypes.begin(), resourceTypes.end()),
210             resourceTypes.end());
211
212     if (waitsec >= 0)
213     {
214         candidateRequestForTimer.insert(std::make_pair(resourceTypes, callback));
215     }
216     else
217     {
218         candidateRequest.insert(std::make_pair(resourceTypes, callback));
219     }
220
221     for (unsigned int i = 0; i < resourceTypes.size(); ++i)
222     {
223         // std::cout << "resourceTypes : " << resourceTypes.at(i) << std::endl;
224
225         std::string query = OC_RSRVD_WELL_KNOWN_URI;
226         query.append("?rt=");
227         query.append(resourceTypes.at(i));
228
229         OCPlatform::findResource("",
230                 query,
231                 CT_DEFAULT,
232                 std::function < void(std::shared_ptr < OCResource > resource)
233                         > (std::bind(&GroupManager::onFoundResource, this, std::placeholders::_1,
234                                 waitsec)));
235     }
236
237     if (waitsec >= 0)
238     {
239         std::thread exec(
240                 std::function< void(int second) >(
241                         std::bind(&GroupManager::lazyCallback, this, std::placeholders::_1)),
242                 waitsec);
243         exec.detach();
244     }
245
246     return OC_STACK_OK;
247 }
248
249
250 OCStackResult GroupManager::bindResourceToGroup(OCResourceHandle& childHandle, std::shared_ptr< OCResource > resource, OCResourceHandle& collectionHandle)
251 {
252
253     OCStackResult result = OCPlatform::registerResource(childHandle, resource);
254
255     cout << "\tresource registed!" << endl;
256
257     if(result == OC_STACK_OK)
258     {
259         OCPlatform::bindResource(collectionHandle, childHandle);
260     }
261     else
262     {
263         cout << "\tresource Error!" << endl;
264     }
265
266     return result;
267  }
268
269
270
271 /*
272  Presence Check
273  */
274
275 std::map< std::string, CollectionPresenceCallback > presenceCallbacks;
276
277 // Callback to presence
278 void GroupManager::collectionPresenceHandler(OCStackResult result, const unsigned int nonce,
279         const std::string& /*hostAddress*/, std::string host, std::string uri)
280 {
281     std::cout << "uri : " << uri << std::endl;
282     std::cout << "host : " << host << std::endl;
283     std::cout << "result : " << result << std::endl;
284     switch (result)
285     {
286         case OC_STACK_OK:
287             std::cout << "Nonce# " << nonce << std::endl;
288             break;
289         case OC_STACK_PRESENCE_STOPPED:
290             std::cout << "Presence Stopped\n";
291             break;
292         case OC_STACK_PRESENCE_DO_NOT_HANDLE:
293             std::cout << "Presence do not handle\n";
294             break;
295         case OC_STACK_PRESENCE_TIMEOUT:
296             std::cout << "Presence TIMEOUT\n";
297             break;
298         default:
299             std::cout << "Error\n";
300             break;
301     }
302
303     if (presenceCallbacks.find(uri) != presenceCallbacks.end())
304     {
305         (presenceCallbacks.find(uri)->second)(uri, result);
306     }
307 }
308
309 void GroupManager::checkCollectionRepresentation(const OCRepresentation& rep,
310         CollectionPresenceCallback callback)
311 {
312     std::cout << "\tResource URI: " << rep.getUri() << std::endl;
313
314     std::vector< OCRepresentation > children = rep.getChildren();
315     if(children.size() == 0 )
316     {
317         callback("", OC_STACK_ERROR);
318         return;
319     }
320
321     for (auto oit = children.begin(); oit != children.end(); ++oit)
322     {
323         if(oit->getUri().find("coap://") == std::string::npos)
324         {
325             std::cout << "The resource with a URI " << oit->getUri() << " is not a remote resource."
326                 << " Thus, we ignore to send a request for presence check to the resource.\n";
327
328             continue;
329         }
330
331         std::vector< std::string > hostAddressVector = str_split(oit->getUri(), '/');
332         std::string hostAddress = "";
333         for (unsigned int i = 0; i < hostAddressVector.size(); ++i)
334         {
335             if (i < 3)
336             {
337                 hostAddress.append(hostAddressVector.at(i));
338                 if (i != 2)
339                 {
340                     hostAddress.append("/");
341                 }
342             }
343         }
344
345         std::vector< std::string > resourceTypes = oit->getResourceTypes();
346         // for (unsigned int i = 0; i < resourceTypes.size(); ++i)
347         // {
348         //     std::cout << "\t\t\tresourcetype :" << resourceTypes.at(i) << std::endl;
349         // }
350
351         // std::string resourceType = "core.";
352         // resourceType.append(str_split(oit->getUri(), '/').at(4));
353         // std::cout << "\t\tconvertRT : " << resourceType << std::endl;
354         // std::cout << "\t\tresource type front : " << resourceTypes.front() << endl;
355         // std::cout << "\t\thost : " << hostAddress << std::endl;
356         OCPlatform::OCPresenceHandle presenceHandle;
357         OCStackResult result = OC_STACK_ERROR;
358
359         try
360         {
361             result = OCPlatform::subscribePresence(presenceHandle, hostAddress,
362                     // resourceType,
363                     resourceTypes.front(),
364                     CT_DEFAULT,
365                     std::function<
366                             void(OCStackResult result, const unsigned int nonce,
367                                     const std::string& hostAddress) >(
368                             std::bind(&GroupManager::collectionPresenceHandler, this,
369                                     std::placeholders::_1, std::placeholders::_2,
370                                     std::placeholders::_3, hostAddress, oit->getUri())));
371         }catch(OCException& e)
372         {
373             std::cout<< "Exception in subscribePresence: "<< e.what() << std::endl;
374         }
375
376         if (result == OC_STACK_OK)
377         {
378             presenceCallbacks.insert(std::make_pair(oit->getUri(), callback));
379         }
380         else
381         {
382             callback("", OC_STACK_ERROR);
383         }
384     }
385 }
386
387 void GroupManager::onGetForPresence(const HeaderOptions& /*headerOptions*/,
388         const OCRepresentation& rep, const int eCode, CollectionPresenceCallback callback)
389 {
390     if (eCode == OC_STACK_OK)
391     {
392         std::cout << "GET request was successful" << std::endl;
393         std::cout << "Resource URI: " << rep.getUri() << std::endl;
394
395         checkCollectionRepresentation(rep, callback);
396
397     }
398     else
399     {
400         std::cout << "onGET Response error: " << eCode << std::endl;
401         callback("", OC_STACK_ERROR);
402     }
403 }
404
405 OCStackResult GroupManager::subscribeCollectionPresence(
406         std::shared_ptr< OCResource > collectionResource, CollectionPresenceCallback callback)
407 {
408     if(callback == NULL || collectionResource == NULL)
409     {
410         return OC_STACK_ERROR;
411     }
412
413     OCStackResult result = OC_STACK_OK;
414     //callback("core.room",OC_STACK_OK);
415
416     QueryParamsMap queryParam;
417
418     //parameter 1 = resourceType
419     collectionResource->get("", DEFAULT_INTERFACE, queryParam,
420             std::function<
421                     void(const HeaderOptions& headerOptions, const OCRepresentation& rep,
422                             const int eCode) >(
423                     std::bind(&GroupManager::onGetForPresence, this, std::placeholders::_1,
424                             std::placeholders::_2, std::placeholders::_3, callback)));
425
426     return result;
427 }
428
429 /*
430  Group Action
431  */
432
433 std::string GroupManager::getStringFromActionSet(const ActionSet *newActionSet)
434 {
435     std::string message = "";
436
437     if(newActionSet == NULL)
438         return message;
439
440     message = newActionSet->actionsetName;
441     message.append("*");
442     message.append(newActionSet->toString());
443     message.append("*");
444     for (auto iterAction = newActionSet->listOfAction.begin();
445             iterAction != newActionSet->listOfAction.end(); iterAction++)
446     {
447         message.append("uri=");
448         message.append((*iterAction)->target);
449         message.append("|");
450
451         for (auto iterCapa = (*iterAction)->listOfCapability.begin();
452                 iterCapa != (*iterAction)->listOfCapability.end(); iterCapa++)
453         {
454             message.append((*iterCapa)->capability);
455             message.append("=");
456             message.append((*iterCapa)->status);
457
458             if (iterCapa + 1 != (*iterAction)->listOfCapability.end())
459                 message.append("|");
460         }
461
462         if (iterAction + 1 != newActionSet->listOfAction.end())
463         {
464             message.append("*");
465         }
466     }
467
468     return message;
469 }
470
471 #define DELETE(p) { \
472     delete p; \
473     p = NULL; \
474 }
475
476 #define DELETEARRAY(p) { \
477     delete[] p; \
478     p = NULL; \
479 }
480
481 ActionSet* GroupManager::getActionSetfromString(std::string description)
482 {
483     char *token = NULL;
484     char *plainText = NULL;
485     char *plainPtr = NULL;
486     char *attr = NULL, *desc = NULL;
487
488     Action *action = NULL;
489     Capability *capa = NULL;
490     ActionSet *actionset = new ActionSet();
491
492     if(actionset == NULL)
493     {
494         goto exit;
495     }
496
497     if(description.empty())
498     {
499         goto exit;
500     }
501     else if(description.at(0) == '*')
502     {
503         goto exit;
504     }
505
506     plainText = new char[(description.length() + 1)];
507     strncpy(plainText, description.c_str(), description.length()+1);
508
509     // All tokens are seperated by "*" character.
510     // First token means an actionset name
511     token = strtok_r(plainText, ACTION_DELIMITER, &plainPtr);
512
513     if (token != NULL)
514     {
515         actionset->actionsetName = std::string(token);
516
517         if((actionset->actionsetName).empty())
518             goto exit;
519
520         // Find the second token
521         token = strtok_r(NULL, ACTION_DELIMITER, &plainPtr);
522     }
523     else
524     {
525         goto exit;
526     }
527
528     if (token != NULL)
529     {
530         // The second token consists of two digits: time delay and actionset Type.
531         // Time delay is used as a parameter to scheduled/recursive group action feature
532         // And actionset type indicates if the actionset is scheduled or recursive group action.
533         char *subText = NULL;
534         char *subToken = NULL;
535         char *subTextPtr = NULL;
536
537         subText = new char[strlen(token)+1];
538         strncpy(subText, token, strlen(token)+1);
539
540         subToken = strtok_r(subText, " ", &subTextPtr);
541         if(subToken == NULL)
542         {
543             DELETEARRAY(subText);
544             goto exit;
545         }
546         actionset->mDelay = atol(subToken);
547
548         subToken = strtok_r(NULL, " ", &subTextPtr);
549         if(subToken == NULL)
550         {
551             DELETEARRAY(subText);
552             goto exit;
553         }
554         actionset->type = (OIC::ACTIONSET_TYPE)atoi(subToken);
555
556         DELETEARRAY(subText);
557
558         // Find the third token
559         token = strtok_r(NULL, ACTION_DELIMITER, &plainPtr);
560     }
561     else
562     {
563         goto exit;
564     }
565
566     while (token)
567     {
568         // The third token consists of two sub-segement: host address plus URI and attribute key/value pair.
569         // These two segments can be divided by "|" character.
570         // The third token can be repeatively appeared.
571
572         char *descPtr = NULL;
573         desc = new char[(strlen(token) + 1)];
574
575         if (desc != NULL)
576         {
577             // copy a host address plus URI string from the third token
578             strncpy(desc, token, (strlen(token) + 1));
579
580             // Find "|" character to find key/value pair
581             token = strtok_r(desc, DESC_DELIMITER, &descPtr);
582
583             while (token != NULL)
584             {
585                 char *attrPtr = NULL;
586                 attr = new char[(strlen(token) + 1)];
587                 // copy the host address plus URI string
588                 strcpy(attr, token);
589
590                 // Find "=" charactor to divide attribute key and value string.
591                 token = strtok_r(attr, ATTR_DELIMITER, &attrPtr);
592                 while (token != NULL)
593                 {
594                     if ( (action == NULL) && strcmp(token, "uri") == 0)
595                     {
596                         token = strtok_r(NULL, ATTR_DELIMITER, &attrPtr);
597                         if(token == NULL)
598                         {
599                             goto exit;
600                         }
601
602                         action = new Action();
603                         if (action != NULL)
604                         {
605                             action->target = std::string(token);
606                         }
607                         else
608                         {
609                             goto exit;
610                         }
611                     }
612                     else
613                     {
614                         capa = new Capability();
615                         capa->capability = std::string(token);
616                         token = strtok_r(NULL, ATTR_DELIMITER, &attrPtr);
617
618                         if( token == NULL )
619                             goto exit;
620
621                         capa->status = std::string(token);
622
623                         if (action != NULL)
624                             action->listOfCapability.push_back(capa);
625                         else
626                             goto exit;
627                     }
628
629                     token = strtok_r(NULL, ATTR_DELIMITER, &attrPtr);
630                 }
631                 DELETEARRAY(attr);
632                 token = strtok_r(NULL, DESC_DELIMITER, &descPtr);
633             }
634
635             if( action != NULL )
636             {
637                 actionset->listOfAction.push_back(action);
638                 action = NULL;
639             }
640             else
641                 goto exit;
642             //delete action;
643         }
644         else
645         {
646             goto exit;
647         }
648
649         DELETEARRAY(desc);
650
651         token = strtok_r(NULL, ACTION_DELIMITER, &plainPtr);
652     }
653
654     DELETEARRAY(plainText);
655     return actionset;
656
657 exit:
658     DELETE(action);
659     DELETE(capa)
660     DELETE(actionset)
661     DELETEARRAY(attr);
662     DELETEARRAY(plainText);
663     DELETEARRAY(desc);
664     return NULL;
665 }
666
667 OCStackResult GroupManager::addActionSet(std::shared_ptr< OCResource > resource,
668         const ActionSet* newActionSet, PutCallback cb)
669 {
670     // BUILD message of ActionSet which it is included delimiter.
671     if ((resource != NULL) && (newActionSet != NULL))
672     {
673         if(newActionSet->mDelay < 0)
674         {
675             return OC_STACK_INVALID_PARAM;
676         }
677
678         std::string message = getStringFromActionSet(newActionSet);
679
680         OCRepresentation rep;
681         rep.setValue("ActionSet", message);
682         return resource->put(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
683                 QueryParamsMap(), cb);
684     }
685     else
686     {
687         return OC_STACK_ERROR;
688     }
689 }
690
691 OCStackResult GroupManager::executeActionSet(std::shared_ptr< OCResource > resource,
692         std::string actionsetName, PostCallback cb)
693 {
694     if (resource != NULL)
695     {
696         OCRepresentation rep;
697
698         rep.setValue("DoAction", actionsetName);
699         return resource->post(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
700                 QueryParamsMap(), cb);
701     }
702     else
703     {
704         return OC_STACK_ERROR;
705     }
706 }
707
708 OCStackResult GroupManager::executeActionSet(std::shared_ptr< OCResource > resource,
709         std::string actionsetName, long int delay, PostCallback cb)
710 {
711     if(delay <= 0 )
712     {
713         return OC_STACK_INVALID_PARAM;
714     }
715     if (resource != NULL)
716     {
717         std::string value = actionsetName;
718         value.append("*");
719         value.append(std::to_string(delay));
720
721         OCRepresentation rep;
722         rep.setValue("DoScheduledAction", value);
723         return resource->post(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
724                 QueryParamsMap(), cb);
725     }
726     else
727     {
728         return OC_STACK_ERROR;
729     }
730 }
731
732 OCStackResult GroupManager::cancelActionSet(std::shared_ptr< OCResource > resource,
733         std::string actionsetName, PostCallback cb)
734 {
735     if (resource != NULL)
736     {
737         OCRepresentation rep;
738
739         rep.setValue("CancelAction", actionsetName);
740         return resource->post(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
741                 QueryParamsMap(), cb);
742     }
743     else
744     {
745         return OC_STACK_ERROR;
746     }
747 }
748
749 OCStackResult GroupManager::getActionSet(std::shared_ptr< OCResource > resource,
750         std::string actionsetName, PostCallback cb)
751 {
752     if (resource != NULL)
753     {
754         OCRepresentation rep;
755
756         rep.setValue("GetActionSet", actionsetName);
757
758         return resource->post(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
759                 QueryParamsMap(), cb);
760     }
761     else
762     {
763         return OC_STACK_ERROR;
764     }
765 }
766
767 OCStackResult GroupManager::deleteActionSet(std::shared_ptr< OCResource > resource,
768         std::string actionsetName, PutCallback cb)
769 {
770     if (resource != NULL)
771     {
772         OCRepresentation rep;
773
774         rep.setValue("DelActionSet", actionsetName);
775
776         return resource->put(resource->getResourceTypes().front(), GROUP_INTERFACE, rep,
777                 QueryParamsMap(), cb);
778     }
779     else
780     {
781         return OC_STACK_ERROR;
782     }
783 }