diff options
Diffstat (limited to 'examples/OICMiddle')
-rw-r--r-- | examples/OICMiddle/Client.cpp | 83 | ||||
-rw-r--r-- | examples/OICMiddle/Client.h | 58 | ||||
-rw-r--r-- | examples/OICMiddle/LineInput.cpp | 458 | ||||
-rw-r--r-- | examples/OICMiddle/LineInput.h | 72 | ||||
-rw-r--r-- | examples/OICMiddle/OICMiddle.cpp | 152 | ||||
-rw-r--r-- | examples/OICMiddle/OICMiddle.h | 94 | ||||
-rw-r--r-- | examples/OICMiddle/README | 37 | ||||
-rw-r--r-- | examples/OICMiddle/RestInput.cpp | 166 | ||||
-rw-r--r-- | examples/OICMiddle/RestInput.h | 50 | ||||
-rw-r--r-- | examples/OICMiddle/SConstruct | 81 | ||||
-rw-r--r-- | examples/OICMiddle/Server.cpp | 155 | ||||
-rw-r--r-- | examples/OICMiddle/Server.h | 56 | ||||
-rw-r--r-- | examples/OICMiddle/WrapResource.cpp | 289 | ||||
-rw-r--r-- | examples/OICMiddle/WrapResource.h | 121 | ||||
-rw-r--r-- | examples/OICMiddle/makefile | 87 |
15 files changed, 1959 insertions, 0 deletions
diff --git a/examples/OICMiddle/Client.cpp b/examples/OICMiddle/Client.cpp new file mode 100644 index 000000000..8d6e7d16e --- /dev/null +++ b/examples/OICMiddle/Client.cpp @@ -0,0 +1,83 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <map> + +#include "WrapResource.h" +#include "Client.h" + +MiddleClient::MiddleClient() +{ + m_findCB = bind(&MiddleClient::foundOCResource, this, placeholders::_1); +} + +bool MiddleClient::init() +{ + findResources(); + return true; +} + +void MiddleClient::findResources() +{ + m_resourceMap.clear(); + + OC::OCPlatform::findResource("", OC_WELL_KNOWN_QUERY, m_findCB); +} + +void MiddleClient::foundOCResource(shared_ptr<OCResource> resource) +{ + WrapResource *wres; + string resourceID = formatResourceID(resource); + + m_mutexFoundCB.lock(); + + try { + wres = m_resourceMap.at(resourceID); + } catch (const std::out_of_range) { + wres = new WrapResource(resourceID, resource); + m_resourceMap[resourceID] = wres; + } + + m_mutexFoundCB.unlock(); + + wres->findTypes(); +} + +/* + * I need a unique ID, so I concatenate the host string and resource uri + * It's arbitrary and sufficient. + */ +string MiddleClient::formatResourceID(std::shared_ptr<OCResource> resource) +{ + string host = resource->host(); + if (host.compare(0, 7, "coap://") == 0) + host = host.erase(0, 7); + return host + resource->uri(); +} + +void MiddleClient::addResource(WrapResource *wres) +{ + string resourceID = wres->getResourceID(); + try { + m_resourceMap[resourceID]; + } catch (const std::out_of_range) { + m_resourceMap[resourceID] = wres; + } +} diff --git a/examples/OICMiddle/Client.h b/examples/OICMiddle/Client.h new file mode 100644 index 000000000..ef32e8635 --- /dev/null +++ b/examples/OICMiddle/Client.h @@ -0,0 +1,58 @@ +#ifndef CLIENT_H +#define CLIENT_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <mutex> + +#include "OICMiddle.h" + +typedef map<string, WrapResource *> resourcemap_t; +typedef pair<string, WrapResource *> resourcemappair_t; + +class MiddleClient +{ +public: + MiddleClient(); + + bool init(); + void findResources(); + + friend class LineInput; + friend class HueResource; + friend class HueResources; + friend class RestInput; + +protected: + mutex m_mutexFoundCB; + map<string, WrapResource *> m_resourceMap; + HueResources *m_hueResources; + std::function<void(std::shared_ptr<OCResource> resource)> m_findCB; + + void foundOCResource(shared_ptr<OCResource> resource); + string formatResourceID(std::shared_ptr<OCResource> resource); + void findHueResources(); + void addResource(WrapResource *wres); +}; + + +#endif // CLIENT_H + diff --git a/examples/OICMiddle/LineInput.cpp b/examples/OICMiddle/LineInput.cpp new file mode 100644 index 000000000..52a8528d8 --- /dev/null +++ b/examples/OICMiddle/LineInput.cpp @@ -0,0 +1,458 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <map> +#include <iostream> + +#include <stdio.h> + +#include "WrapResource.h" +#include "LineInput.h" + +#define NEED_CLIENT { if (!m_client) return LR_NoClient; } + +LineInput::LineInput(MiddleClient *client) + : m_client(client), m_server(nullptr), + m_obsCB(nullptr), m_observer(nullptr) +{ + m_obsCB = std::bind(&LineInput::obsCB, this, + placeholders::_1, + placeholders::_2, + placeholders::_3, + placeholders::_4, + placeholders::_5); +} + +void LineInput::setServer(MiddleServer *server) { + m_server = server; +} + +int LineInput::run() +{ + size_t len; + char *line = nullptr; + + while (true) { + fputs(">", stdout); + len = 0; + getline(&line, &len, stdin); + int n = strlen(line); + if (!n) + continue; + if (m_observer) { + m_observer->cancelObserve(); + m_observer = nullptr; + } + if (line[n - 1] == '\n') { + if (n == 1) + continue; + line[n - 1] = '\0'; + } + stringstream result; + LineResult lr = processLine(line, result, m_obsCB); + if (lr == LR_Quit) + break; + cout << result.str(); + } + free(line); + return true; +} + +LineResult LineInput::processLine(string command, stringstream& result, observecb_t cb) +{ + elements_t elems; + + if (parseLine(command, elems) != LR_OK) { + cerr << "syntax error" << endl; + return LR_Syntax; + } + if (!elems.size()) + return LR_NoCommand; + + if (elems[0] == "quit" || elems[0] == "exit") + return LR_Quit; + + if (elems.size() == 1) { + if (elems[0] == "help") { + return processHelp(elems, result); + } else if (elems[0] == "find") { + NEED_CLIENT return processFind(elems, result); + } else if (elems[0] == "show") { + NEED_CLIENT return processShow(elems, result); + } + } else if (elems.size() == 2) { + if (elems[0] == "details") { + NEED_CLIENT return processDetails(elems, result); + } else if (elems[0] == "get") { + NEED_CLIENT return processGet(elems, result); + } else if (elems[0] == "observe") { + NEED_CLIENT return processObserve(elems, result, cb); + } else if (elems[0] == "cancel") { + NEED_CLIENT return processCancel(elems, result); + } + } else { + if (elems[0] == "put") { + NEED_CLIENT return processPut(elems, result); + } + } + + return processUnrecognized(elems, result); +} + +LineResult LineInput::processHelp(elements_t& elems, stringstream& ss) +{ + ss << "\nUsage:\n" + "\tfind\t\tFind resources\n" + "\tshow\t\tShow resources\n" + "\tdetails n\tShow details of resource n\n" + "\tget n\t\tGet value(s) of resource n\n" + "\tput n v\t\tPut value(s) to resource n\n" + "\tobserve n\tObserve value(s) of resource n\n" + "\thelp\t\tThis usage message\n" + "\nResource can be identified by Resource ID or Show index\n" + "\nValue in 'put' can be key=value or key:value\n\n" + ; + return LR_OK; +} + +LineResult LineInput::processUnrecognized(elements_t& elems, stringstream& ss) +{ + ss << "Command not recognized\n"; + processHelp(elems, ss); + return LR_Unrecognized; +} + +LineResult LineInput::processFind(elements_t& elems, stringstream& ss) +{ + m_client->findResources(); + return LR_OK; +} + +void LineInput::registerResourceWithServer(std::string & url) { + string type; + std::size_t index = url.rfind("/"); + if (index != std::string::npos) { + type = url.substr(index+1); + } + const std::string resType = type; + const std::string iface = "MB_INTERFACE"; + m_server->registerResource(url, resType, iface); +} + +LineResult LineInput::processShow(elements_t& elems, stringstream& ss) +{ + int index = 0; + m_resourceList.clear(); + resourcemap_t& pmap = m_client->m_resourceMap; + + for (resourcemap_t::iterator it = pmap.begin(); it != pmap.end(); it++) { + string resID = it->first; + ss << index++ << '\t' << resID << '\n'; + m_resourceList.push_back(resID); + if (m_server) { + registerResourceWithServer(resID); + } + } + + return LR_OK; +} + +LineResult LineInput::processDetails(elements_t& elems, stringstream& ss) +{ + WrapResource *wres = resolveResource(elems[1], ss); + if (!wres) + return LR_NoResource; + + ss << wres->getResourceID() + " [ "; + for (auto &types : wres->getResourceTypes()) { + ss << types + ' '; + } + ss << "] "; + for (auto &ifs : wres->getResourceInterfaces()) { + ss << ifs << " "; + } + ss << '\n'; + return LR_OK; +} + +void printJSONAsTable(std::string &jsonString) { + std::string str = jsonString; + std::string key, value; + size_t found = str.find("rep"); + if (found == std::string::npos) { // not found + return; + } + str = str.substr(found+5); + while (true) { + found = str.find(":"); + if (found == std::string::npos) { + return; + } + key = str.substr(1, found-1); + str = str.substr(found); + found = str.find(","); + if (found != std::string::npos) { + value = str.substr(1, found-1); + str = str.substr(found); + } else { + found = str.find("}"); + if (found != std::string::npos) { + value = str.substr(1, found-1); + str = str.substr(found); + } + } + cout << key << "\t:" << value << endl; + } +} + +LineResult LineInput::processGet(elements_t& elems, stringstream& ss) +{ + WrapResource *wres = resolveResource(elems[1], ss); + if (!wres) + return LR_NoResource; + + token_t token = wres->getResource(); + + WrapRequest *wreq = wres->waitResource(token); + if (!wreq) { + ss << "Get timed out\n"; + return LR_Timeout; + } + + std::string jsonRep = wreq->m_rep.getJSONRepresentation(); + //ss << jsonRep << endl; + printJSONAsTable(jsonRep); + return LR_OK; +} + +LineResult LineInput::processPut(elements_t& elems, stringstream& ss) +{ + WrapResource *wres = resolveResource(elems[1], ss); + if (!wres) + return LR_NoResource; + + string format; + OCRepresentation rep; + + bool error = false; + for (size_t i = 2; i < elems.size(); i++) { + string elem = elems[i]; + char *s = (char *)elem.c_str(); // elem string is intentionally damaged + char *key = strtok(s, "=:"); + char *value = strtok(nullptr, ""); + if (!value) { + ss << "missing separator in element starting with " << key << '\n'; + error = true; + continue; + } + char delim = value[0]; + size_t len = strlen(value); + if (delim == '\'' || delim == '"') { + if (len > 1 && delim == value[len - 1]) { + value[len - 1] = '\0'; + value++; + } + } + string v(value, len); + stringmap_t formats = wres->getFormats(); + try { + format = formats.at(key); + } catch (...) { + cerr << "element in arg " << i << " has no format\n"; + continue; + } + if (format == "bool") { + bool b = v != "0" && v != "false"; + rep.setValue(key, b); + } else if (format == "number") { + char *end; + int n = (int)strtol(value, &end, 10); + if (size_t(end - value) != len) { + double d = atof(value); + rep.setValue(key, d); + } else { + rep.setValue(key, n); + } + } else { // assume string + rep.setValue(key, v); + } + } + if (error) + return LR_Param; + + token_t token = wres->putResource(rep); + + WrapRequest *wreq = wres->waitResource(token); + if (!wreq) { + ss << "Get timed out\n"; + return LR_Timeout; + } + + return LR_OK; +} + +LineResult LineInput::processObserve(elements_t& elems, stringstream& ss, observecb_t cb) +{ + WrapResource *wres = resolveResource(elems[1], ss); + if (!wres) + return LR_NoResource; + m_observer = wres; + wres->observeResource(cb); + return LR_OK; +} + +LineResult LineInput::processCancel(elements_t& elems, stringstream& ss) +{ + WrapResource *wres = resolveResource(elems[1], ss); + if (!wres) + return LR_NoResource; + + wres->cancelObserve(); + m_observer = nullptr; + return LR_OK; +} + +WrapResource *LineInput::resolveResource(string resID, stringstream& ss) +{ + size_t len; + string useID = resID; + int index = std::stoi(useID, &len); + + if (len == resID.size()) { // it's an index, not a uri + if (size_t(index) >= m_resourceList.size()) { + cout << "Resource index out of range (use 'show')\n"; + return nullptr; + } + useID = m_resourceList[index]; // now it's a uri + } + + resourcemap_t::iterator it = m_client->m_resourceMap.find(useID); + if (it == m_client->m_resourceMap.end()) { + cout << resID << " is currently not available\n"; + return nullptr; + } + + return it->second; +} + +void LineInput::obsCB(token_t token, const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode, const int sequenceNumber) +{ + if (!m_observer) + return; + cout << "cb " << eCode << " " << sequenceNumber << '\n'; + cout << rep.getJSONRepresentation() << "\n"; +} + +ParseState LineInput::finishElem(char*& e, elements_t& elems) +{ + *e = '\0'; + elems.push_back(m_elem); + e = m_elem; + return PS_Between; +} + +ParseState LineInput::putCharInElem(char c, char *& e, ParseState newState) +{ + *e++ = c; + if (size_t(e - m_elem) >= sizeof (m_elem)) + throw 20; // hightly unlikely exception + return newState; +} + +/* + * See processHelp() above for line format + */ +LineResult LineInput::parseLine(string lineIn, elements_t& elems) +{ + const char *d; + char c, *e, delim; + bool isSep1, isSep2; + size_t len = lineIn.size(); + ParseState state = PS_Between; + const char *line = lineIn.c_str(); + + d = line; + e = m_elem; + while (true) { + if (size_t(d - line) >= len) { + if (e != m_elem) { + if (state == PS_Infirst || state == PS_Endsecond || (state == PS_Insecond && !delim)) { + state = finishElem(e, elems); + return LR_OK; + } + } + return LR_Syntax; + } + c = *d++; + if (c == '\n') + continue; + isSep1 = c == ' ' || c == '\t'; + isSep2 = c == '=' || c == ':'; + + switch (state) { + case PS_Between: + if (isSep1) + continue; + if (isSep2) + return LR_Syntax; + state = putCharInElem(c, e, PS_Infirst); + break; + case PS_Infirst: + if (isSep1) { + state = finishElem(e, elems); + continue; + } + if (isSep2) { + delim = 0; + state = PS_Startsecond; + } + putCharInElem(c, e, state); + break; + case PS_Startsecond: + if (isSep1 || isSep2) + return LR_Syntax; + if (c == '\'' || c == '"' || c == '|') + delim = c; + state = putCharInElem(c, e, PS_Insecond); + break; + case PS_Insecond: + if (isSep1 && delim == 0) { + state = finishElem(e, elems); + continue; + } + if (c == delim) { + state = PS_Endsecond; + } + *e++ = c; + break; + case PS_Endsecond: + if (isSep1) { + state = finishElem(e, elems); + continue; + } + return LR_Syntax; + case PS_None: + return LR_Syntax; + } + } + return LR_OK; +} + + diff --git a/examples/OICMiddle/LineInput.h b/examples/OICMiddle/LineInput.h new file mode 100644 index 000000000..bc7d4afa2 --- /dev/null +++ b/examples/OICMiddle/LineInput.h @@ -0,0 +1,72 @@ +#ifndef LINEINPUT_H +#define LINEINPUT_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 "OICMiddle.h" +#include "Client.h" +#include "Server.h" + +typedef vector<string> elements_t; + +enum ParseState { + PS_None, + PS_Between, + PS_Infirst, + PS_Startsecond, + PS_Insecond, + PS_Endsecond, +}; + +class LineInput +{ +public: + LineInput(MiddleClient *client); + void setServer(MiddleServer *server); + int run(); + LineResult processLine(string command, stringstream& result, observecb_t cb); + +protected: + MiddleClient *m_client; + MiddleServer *m_server; + vector<string> m_resourceList; + observecb_t m_obsCB; + WrapResource *m_observer; + char m_elem[1000]; + + LineResult processHelp(elements_t& elems, stringstream& ss); + LineResult processUnrecognized(elements_t& elems, stringstream& ss); + LineResult processFind(elements_t& elems, stringstream& ss); + LineResult processShow(elements_t& elems, stringstream& ss); + LineResult processDetails(elements_t& elems, stringstream& ss); + LineResult processGet(elements_t& elems, stringstream& ss); + LineResult processPut(elements_t& elems, stringstream& ss); + LineResult processObserve(elements_t& elems, stringstream& ss, observecb_t cb); + LineResult processCancel(elements_t& elems, stringstream& ss); + WrapResource *resolveResource(string resID, stringstream& ss); + LineResult parseLine(string lineIn, elements_t& elems); + ParseState finishElem(char*& e, elements_t& elems); + ParseState putCharInElem(char c, char *& e, ParseState newState); + void obsCB(const token_t token, const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode, const int sequenceNumber); + void registerResourceWithServer(std::string &url); +}; + +#endif // LINEINPUT_H diff --git a/examples/OICMiddle/OICMiddle.cpp b/examples/OICMiddle/OICMiddle.cpp new file mode 100644 index 000000000..0da414599 --- /dev/null +++ b/examples/OICMiddle/OICMiddle.cpp @@ -0,0 +1,152 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// +// OICMiddle.cpp : OIC demo application for Minnowboard +// + +#include <unistd.h> + +#include "OICMiddle.h" +#include "WrapResource.h" +#include "Client.h" +#include "Server.h" +#include "LineInput.h" +#include "RestInput.h" + +class Middle middle; // one and only + +Middle::Middle() : + m_appType(AT_None), + m_useLineInput(false), + m_useRestInput(false), + m_client(nullptr), + m_server(nullptr), + m_lineInput(nullptr), + m_restInput(nullptr) +{ +} + +void Middle::init() +{ + +} + +void Middle::run(int argc, char* argv[]) +{ + parseCommandLineOptions(argc, argv); + + startPlatform(); + + if (m_appType & AT_Client) { + m_client = new MiddleClient(); + m_client->init(); + } + + m_lineInput = new LineInput(m_client); + + if (m_appType & AT_Server) { + m_server = new MiddleServer(); + m_server->init(); + } + if (m_useRestInput) { + if (!m_server) { + m_server = new MiddleServer(); + m_server->init(); + } + m_restInput = new RestInput(m_lineInput); + m_restInput->init(); + } + if (m_useLineInput) { + if (m_server) { + m_lineInput->setServer(m_server); + } + m_lineInput->run(); + } else { + while (true) + sleep(1); + } +} + +void Middle::startPlatform() +{ + uint16_t port = 0; + //std::string ipaddr = INADDR_ANY; + std::string ipaddr = "0.0.0.0"; + + PlatformConfig cfg { ServiceType::InProc, ModeType::Both, + ipaddr, port, QualityOfService::LowQos}; + + OC::OCPlatform::Configure(cfg); +} + +void Middle::provideHelp() +{ + static const char usage[] = "\nUsage: IOCMiddle args\n" + " where args may include any of these:\n" + "\t-client Run OIC client\n" + "\t-server Run OIC server\n" + "\t-both Run OIC client and server\n" + "\t-console Run console line interpreter\n" + "\t-rest Run ReST server\n" + "\t-hue addr Enable Hue resources on bridge at addr\n" + "\t-help Show Usage again\n" + "Any combination of the above is okay.\n\n"; + cout << usage; +} + +bool Middle::parseCommandLineOptions(int argc, char *argv[]) +{ + bool any = false; + + for (int i = 1; i < argc; i++) { + if (argv[i] == string("-server")) { + middle.m_appType = AT_Server; any = true; + } else if (argv[i] == string("-client")) { + middle.m_appType = AT_Client; any = true; + } else if (argv[i] == string("-both")) { + middle.m_appType = AT_Both; any = true; + } else if (argv[i] == string("-console")) { + middle.m_useLineInput = true; any = true; + } else if (argv[i] == string("-rest")) { + middle.m_useRestInput = true; any = true; + } else if (argv[i] == string("-hue")) { + if (i + 1 < argc && argv[i + 1][0] != '-') { + m_hueAddr = argv[++i]; + any = true; + } + } else if (argv[i] == string("-help")) { + any = false; + break; + } else { + std::cerr << "Not enough or invalid arguments, please try again.\n"; + exit(1); + } + } + if (!any) + provideHelp(); + return true; +} + +int main(int argc, char* argv[]) +{ + middle.run(argc, argv); + return 0; +} diff --git a/examples/OICMiddle/OICMiddle.h b/examples/OICMiddle/OICMiddle.h new file mode 100644 index 000000000..457b7a8fd --- /dev/null +++ b/examples/OICMiddle/OICMiddle.h @@ -0,0 +1,94 @@ +#ifndef OICMIDDLE_H +#define OICMIDDLE_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <string> +#include <cstdlib> +#include "OCPlatform.h" +#include "OCApi.h" + +class MiddleClient; +class MiddleServer; +class LineInput; +class RestInput; +class WrapResource; +class HueResources; + +using namespace OC; +using namespace std; + +enum AppType { + AT_None = 0, + AT_Server = 1, + AT_Client = 2, + AT_Both = 3 +}; + +enum LineResult { + LR_OK, + LR_NoCommand, + LR_NoClient, + LR_NoResource, + LR_Timeout, + LR_Param, + LR_Unrecognized, + LR_Quit, + LR_Syntax, + LR_Error +}; + +class HueResource; + +typedef int token_t; +typedef map<string, string> stringmap_t; + +class Middle +{ +public: + Middle(); + void init(); + void run(int argc, char* argv[]); + +protected: + friend class MiddleClient; + friend class MiddleServer; + friend class RestInput; + friend class HueResources; + + AppType m_appType; + bool m_useLineInput; + bool m_useRestInput; + string m_hueAddr; + MiddleClient *m_client; + MiddleServer *m_server; + LineInput *m_lineInput; + RestInput *m_restInput; + +protected: + void startPlatform(); + bool parseCommandLineOptions(int argc, char *argv[]); + void provideHelp(); +}; + +extern Middle middle; + +#endif // OICMIDDLE_H diff --git a/examples/OICMiddle/README b/examples/OICMiddle/README new file mode 100644 index 000000000..518fb2a3a --- /dev/null +++ b/examples/OICMiddle/README @@ -0,0 +1,37 @@ +OICMiddle was written to +* be part of a demonstration of OIC Yocto capability, +* act as an example of resource callbacks using class methods, +* provide a simple promiscuous resource editor for examining OIC systems, and +* act as a starting code base for further exploration of OIC capabilities. + +As a demonstration, it runs on an Minnowboard running a Yocto-built OS, acting +as a gateway between an Android device (acting as an OIC client) and an +Edison board (acting as an OIC server) with sensors and actuators. + +As an example of resource callbacks, it shows a method of using class methods +as callbacks, a critical capability not shown in any of the examples in +iotivity/resource/examples. + +As a promiscuous resource editor, it can find, get, put and observe any +resource using a simple command-line interface using the system console. + +As a code base, the command-line editor can be the basis for adding additional +editing capabilities, like the additions of various filters. + +Running OICMiddle with no arguments on a console shows the various capabilities +it offers. The most important are: + -client. Act as an OIC client. + -console. Accept command lines from console input to drive the OIC client. + -server. Advertise resources found by the OIC client as OIC resources. + +The -server capabilites might be the basis for a gateway. + +Typing 'help' (or invalid commands) to the console gives a console usage +message. The important ones are: + find Find all resources. Also performed automatically at startup. + show Show the found resources and an assigned index. + get Get the value(s) of the resource with the given index. + put Put one or more resource values for the given index. + + +12/24/2014 diff --git a/examples/OICMiddle/RestInput.cpp b/examples/OICMiddle/RestInput.cpp new file mode 100644 index 000000000..b6e240f35 --- /dev/null +++ b/examples/OICMiddle/RestInput.cpp @@ -0,0 +1,166 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 "WrapResource.h" +#include "RestInput.h" +#include "LineInput.h" +#include "OICMiddle.h" + +using namespace std; + +#define BUFLEN 10000 +#define MAX_CONNS 5 + +static bool enableDebug = false; // set to true to print debug messages + +void printDebugMessage(std::string message) +{ + if (enableDebug) { + cout << "RestInput: " << message << endl; + } +} + +RestInput::RestInput(LineInput *lineInput) : m_lineInput(lineInput) +{ + m_data = (char*)malloc(BUFLEN); + m_thread = new std::thread[MAX_CONNS]; + m_threadCount = 0; +} + +bool RestInput::init() +{ + m_sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (m_sockfd < 0) { + cerr << "Failed to open socket. Exiting" << endl; + return false; + } + m_port = 1441; //listening on port 1441 + + m_serverAddr.sin_family = AF_INET; + m_serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + m_serverAddr.sin_port = htons(m_port); + + if (::bind(m_sockfd, (struct sockaddr*)&m_serverAddr, sizeof m_serverAddr) < 0) { + cerr << "Failed to bind. Exiting " << endl; + return false; + } + + listen(m_sockfd, MAX_CONNS); + startAccept(m_sockfd); + return true; +} + +// accept incoming connection(s) +void RestInput::startAccept(int &sockfd) +{ + if (m_threadCount >= MAX_CONNS) { + cerr << " Max # of connections reached. Skipping " << endl; + return; + } else { + while (true) { + int connfd = accept(sockfd, (struct sockaddr *)NULL, NULL); + if (connfd < 0) { + cerr << " Failed to accept incoming connection " << endl; + return; + } + int n = read(connfd, m_data, BUFLEN); + if (n < 0) { + cerr << "Failed to read from socket" << endl; + return; + } + startThread(); + } + } +} + +// start client thread +void RestInput::startThread() +{ + std::thread t(&RestInput::processClient, this); + m_thread[m_threadCount] = std::move(t); + m_thread[m_threadCount++].join(); +} + +// process read commands for the client +void RestInput::processClient(void) +{ + std::string restCmd = m_data; + std::size_t found = restCmd.find('\n'); + if (found != std::string::npos) { + restCmd = restCmd.substr(0, found-1); + } + handleRead(restCmd); +} + +void RestInput::handleRead(std::string& restCmd) +{ + parseString(restCmd); + if (restCmd.find("exit") == 0) { + std::thread::id id = std::this_thread::get_id(); + for(int i = 0; i < m_threadCount; ++i) { + if (id == m_thread[i].get_id()) { + m_thread[i].detach(); + --m_threadCount; + cout << "Exiting thread " << id << endl; + } + } + return; + } + stringstream ss; + observecb_t cb; + std::string msg = "command sent to LineInput is: " + restCmd; + printDebugMessage(msg); + m_lineInput->processLine(restCmd, ss, cb); + if (restCmd.find("show") != string::npos) { + // if command is show, we want to list out the details of each resource + handleShow(ss, cb); + } +} + +void RestInput::handleShow(stringstream &ss, observecb_t &cb) { + std::string temp = ss.str(); + size_t n = std::count(temp.begin(), temp.end(), '\n'); // number of resources found + std::stringstream sstm; + std::string lineInputData; + + for (size_t i = 0; i < n; ++i) { + sstm.str(""); + sstm << "details " << i; + lineInputData = sstm.str(); + std::string msg = "Details: " + lineInputData; + printDebugMessage(msg); + m_lineInput->processLine(lineInputData, ss, cb); + sstm.str(""); + sstm << "get " << i; + lineInputData = sstm.str(); + msg = "Get: " + lineInputData; + printDebugMessage(msg); + m_lineInput->processLine(lineInputData, ss, cb); + } +} + +void RestInput::parseString(std::string &toParse) +{ + std::size_t pos = toParse.find("HTTP"); // split on HTTP + toParse = toParse.substr(0, pos); + pos = toParse.find("/"); // find 1st occurance of / + toParse = toParse.substr(pos + 1, toParse.size() - 1); + std::replace(toParse.begin(), toParse.end(), '/', ' '); // replace all '/' with ' ' +} diff --git a/examples/OICMiddle/RestInput.h b/examples/OICMiddle/RestInput.h new file mode 100644 index 000000000..2e1cad6fd --- /dev/null +++ b/examples/OICMiddle/RestInput.h @@ -0,0 +1,50 @@ +#ifndef RESTINPUT_H +#define RESTINPUT_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <netinet/in.h> + +class LineInput; +class Connection; + +class RestInput +{ +public: + RestInput(LineInput *lineInput); + bool init(); + void startAccept(int &sockfd); + void startThread(); + void processClient(void); + void handleRead(std::string & restCmd); + void handleShow(stringstream &ss, observecb_t &cb); + void parseString(std::string &toParse); + +protected: + LineInput *m_lineInput; + int m_sockfd, m_port, m_threadCount; + struct sockaddr_in m_serverAddr; + char *m_data; + std::thread *m_thread; +}; + +#endif // RESTINPUT_H + diff --git a/examples/OICMiddle/SConstruct b/examples/OICMiddle/SConstruct new file mode 100644 index 000000000..02a2d8e9b --- /dev/null +++ b/examples/OICMiddle/SConstruct @@ -0,0 +1,81 @@ +#For Yocto builds, set OS=yocto as a command-line argument to scons once +#the Yocto toolchain is installed and configured. + +#For Linux builds, set the following two variables: +#Set OIC_RESOURCE_PATH to the root of oic-resource on Ubuntu. + +OIC_RESOURCE_PATH = '../..' + +#Set OIC_LIBS_PATH to path on Ubuntu that contains liboc.so, liboctbstack.so, +#liboc_logger.so and libcoap.so. + +OIC_LIBS_PATH = '../../out/linux/x86_64/release' + +env = DefaultEnvironment() +target_os = ARGUMENTS.get("OS", "linux").lower() +output_dir = env.GetLaunchDir() + "/out/" + target_os +env.VariantDir(output_dir, env.GetLaunchDir(), duplicate=0) +env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall']) +env.AppendUnique(LINKFLAGS = ['-pthread']) +env.AppendUnique(LIBS = ['oc', 'octbstack', 'oc_logger', 'coap']) +env.Program(output_dir + '/OICMiddle', [output_dir + '/OICMiddle.cpp', + output_dir + '/Client.cpp', + output_dir + '/Server.cpp', + output_dir + '/WrapResource.cpp', + output_dir + '/LineInput.cpp', + output_dir + '/RestInput.cpp']) + +if target_os == "yocto": + ''' + This code injects Yocto cross-compilation flags into scons' default environment + in order to invoke the relevant tools while performing a build. + ''' + import os.path, re + sdk_root = '' + try: + CC = os.environ['CC'] + sdk_root = re.search(r'--sysroot=\S+', CC).group().split('=')[1] + target_prefix = CC.split()[0] + target_prefix = target_prefix[:len(target_prefix)-3] + tools = {"CC" : target_prefix+"gcc", + "CXX" : target_prefix+"g++", + "AS" : target_prefix+"as", + "LD" : target_prefix+"ld", + "GDB" : target_prefix+"gdb", + "STRIP" : target_prefix+"strip", + "RANLIB" : target_prefix+"ranlib", + "OBJCOPY" : target_prefix+"objcopy", + "OBJDUMP" : target_prefix+"objdump", + "AR" : target_prefix+"ar", + "NM" : target_prefix+"nm", + "M4" : "m4", + "STRINGS": target_prefix+"strings"} + PATH = os.environ['PATH'].split(os.pathsep) + for tool in tools: + if tool in os.environ: + for path in PATH: + if os.path.isfile(os.path.join(path, tools[tool])): + env[tool] = os.path.join(path, os.environ[tool]) + env.AppendUnique(CPPPATH = [ + sdk_root + '/usr/include/oic/', + sdk_root + '/usr/include/oic/stack/', + sdk_root + '/usr/include/oic/ocsocket/', + sdk_root + '/usr/include/oic/oc_logger/', + ]) + except: + print "ERROR configuring Yocto cross-toolchain environment." + Exit(1) +elif target_os == "linux": + if OIC_RESOURCE_PATH == '' or OIC_LIBS_PATH == '': + print "ERROR Please set both OIC_RESOURCE_PATH and OIC_LIBS_PATH in SConstruct" + Exit(1) + env.AppendUnique(CPPPATH = [ + OIC_RESOURCE_PATH + '/resource/include', + OIC_RESOURCE_PATH + '/resource/csdk/stack/include', + OIC_RESOURCE_PATH + '/resource/csdk/ocsocket/include', + OIC_RESOURCE_PATH + '/resource/oc_logger/include', + ]) + env.AppendUnique(LIBPATH = [OIC_LIBS_PATH]) +else: + print "ERROR ", target_os, " is an unsupported target" + Exit(1) diff --git a/examples/OICMiddle/Server.cpp b/examples/OICMiddle/Server.cpp new file mode 100644 index 000000000..b55e71556 --- /dev/null +++ b/examples/OICMiddle/Server.cpp @@ -0,0 +1,155 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 "Server.h" +#include "OCPlatform.h" +#include "OCApi.h" + +namespace PH = std::placeholders; + +MiddleServer *serverObject = nullptr; // to be filled in by object + +MiddleServer::MiddleServer() +{ + cb = std::bind(&MiddleServer::entityHandler, this, std::placeholders::_1); + serverObject = this; +} + +bool MiddleServer::init() { + return true; +} + +OCEntityHandlerResult MiddleServer::entityHandler(const std::shared_ptr<OCResourceRequest> request) { + if (!request) { + return OC_EH_OK; + } + + std::string requestType = request->getRequestType(); + int requestFlag = request->getRequestHandlerFlag(); + bool responseNeeded = false; + + if (requestFlag && RequestHandlerFlag::InitFlag) { + return OC_EH_OK; + } + + if (requestFlag && RequestHandlerFlag::RequestFlag) { + if (requestType == "PUT") { + responseNeeded = true; + } else if (requestType == "GET") { + responseNeeded = true; + } else if (requestType == "POST") { // handle post requests here + } else if (requestType == "DELETE") { // handle delete requests here + } + } + + if (requestFlag && RequestHandlerFlag::ObserverFlag) { + } + + if (responseNeeded) { + auto response = std::make_shared<OC::OCResourceResponse>(); + response->setRequestHandle(request->getRequestHandle()); + response->setResourceHandle(request->getResourceHandle()); + response->setErrorCode(200); + response->setResponseResult(OC_EH_OK); + if (OC_STACK_OK != OCPlatform::sendResponse(response)) { + return OC_EH_ERROR; + } + } + return OC_EH_OK; +} + +// for debug purposes - to see if the result of registerResource is valid or not +void MiddleServer::printRegisterResourceResult(OCStackResult &result) { + switch (result) { + case OC_STACK_OK: + cout << "OC_STACK_OK\n"; + break; + case OC_STACK_INVALID_URI: + cout << "OC_STACK_INVALID_URI\n"; + break; + case OC_STACK_INVALID_QUERY: + cout << "OC_STACK_INVALID_QUERY\n"; + break; + case OC_STACK_INVALID_IP: + cout << "OC_STACK_INVALID_IP\n"; + break; + case OC_STACK_INVALID_PORT: + cout << "OC_STACK_INVALID_PORT\n"; + break; + case OC_STACK_INVALID_CALLBACK: + cout << "OC_STACK_INVALID_CALLBACK\n"; + break; + case OC_STACK_INVALID_METHOD: + cout << "OC_STACK_INVALID_METHOD\n"; + break; + case OC_STACK_NO_MEMORY: + cout << "OC_STACK_NO_MEMORY\n"; + break; + case OC_STACK_COMM_ERROR: + cout << "OC_STACK_COMM_ERROR\n"; + break; + case OC_STACK_INVALID_PARAM: + cout << "OC_STACK_INVALID_PARAM\n"; + break; + case OC_STACK_NOTIMPL: + cout << "OC_STACK_NOTIMPL\n"; + break; + case OC_STACK_NO_RESOURCE: + cout << "OC_STACK_NO_RESOURCE\n"; + break; + case OC_STACK_RESOURCE_ERROR: + cout << "OC_STACK_RESOURCE_ERROR\n"; + break; + case OC_STACK_SLOW_RESOURCE: + cout << "OC_STACK_SLOW_RESOURCE\n"; + break; + case OC_STACK_NO_OBSERVERS: + cout << "OC_STACK_NO_OBSERVERS\n"; + break; + case OC_STACK_ERROR: + cout << "OC_STACK_ERROR\n"; + break; + default: + cout << "UNKNOWN\n"; + break; + } +} + +bool MiddleServer::registerResource(std::string & resourceUrl, const std::string& resourceTypeName, const std::string& resourceInterface) +{ + OCResourceHandle resourceHandle; + // OCResourceProperty is defined ocstack.h + uint8_t resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE; + + //uncomment to enable debugging + + // This will internally create and register the resource. + OCStackResult result = OC::OCPlatform::registerResource( + resourceHandle, resourceUrl, resourceTypeName, + resourceInterface, + cb, + resourceProperty); + // enable this to see the result of registerResource + //printRegisterResourceResult_(result); + if (result != OC_STACK_OK) { + return false; + } + return true; +} diff --git a/examples/OICMiddle/Server.h b/examples/OICMiddle/Server.h new file mode 100644 index 000000000..049c708f3 --- /dev/null +++ b/examples/OICMiddle/Server.h @@ -0,0 +1,56 @@ +#ifndef SERVER_H +#define SERVER_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 "OICMiddle.h" + +class MiddleServer +{ +private: + std::string m_name; + bool m_state; + int m_power; + std::string m_url; + OCResourceHandle m_resourceHandle; + OCRepresentation *m_rep; + std::function<OCEntityHandlerResult(const std::shared_ptr<OCResourceRequest>)> cb; + +public: + MiddleServer(); + + bool init(); + bool createAndRegisterResources(std::vector<std::string> &resourceUrlList, + std::vector<std::string> &resourceTypeList, + std::vector<std::string> &resourceInterfaceList, + std::vector<std::string> &nameList, + std::vector<std::string> &powerList, + std::vector<std::string> &stateList); + OCEntityHandlerResult entityHandler(const std::shared_ptr<OCResourceRequest>); + + bool registerResource(std::string & resourceUrl, + const std::string &resourceTypeName, + const std::string & resourceInterface); +private: + void printRegisterResourceResult(OCStackResult &result); +}; + +#endif // SERVER_H diff --git a/examples/OICMiddle/WrapResource.cpp b/examples/OICMiddle/WrapResource.cpp new file mode 100644 index 000000000..3cadb7ddf --- /dev/null +++ b/examples/OICMiddle/WrapResource.cpp @@ -0,0 +1,289 @@ +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <chrono> +#include <sys/time.h> + +#include "WrapResource.h" + +unsigned long GetTickCount() +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) != 0) + return 0; + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); +} + +WrapResource::WrapResource(string resourceID, ocresource_t resource) + : m_resourceID(resourceID), m_ocResource(resource), + m_listIndex(-1), m_x(0), m_repGetReady(false), m_gettingRep(false), + m_observeCB(nullptr), m_callbackRunning(false), + m_requestToken(0), m_observeRequest(nullptr), + m_typeRequest(nullptr) +{ +} + +string WrapResource::getResourceID() { + return m_resourceID; +} + +token_t WrapResource::getResource() +{ + WrapRequest *wreq; + QueryParamsMap m; + + wreq = newRequest(RT_Get); + m_ocResource->get(m, wreq->m_getCB, QualityOfService::HighQos); + return wreq->m_token; +} + +token_t WrapResource::putResource(OCRepresentation& rep) +{ + WrapRequest *wreq; + QueryParamsMap m; + + wreq = newRequest(RT_Put); + rep.setUri(m_ocResource->uri()); + m_ocResource->put(rep, m, wreq->m_putCB, QualityOfService::HighQos); + return wreq->m_token; +} + +token_t WrapResource::observeResource(observecb_t& cb) +{ + WrapRequest *wreq; + QueryParamsMap m; + ObserveType type; + + wreq = newRequest(RT_Observe); + m_observeRequest = wreq; + m_observeCB = cb; + m_callbackRunning = true; + type = ObserveType::Observe; + m_ocResource->observe(type, m, wreq->m_obsCB); + return wreq->m_token; +} + +bool WrapResource::cancelObserve() +{ + m_callbackRunning = false; + m_observeCB = nullptr; + + if (!m_observeRequest) + return false; + + OCStackResult result = m_ocResource->cancelObserve(); + if (result != OC_STACK_OK) + return false; + + m_observeRequest->m_touchTime = GetTickCount(); + return true; +} + +WrapRequest *WrapResource::waitResource(token_t token) +{ + WrapRequest *wreq; + cv_status st; + + try { + m_mutexMap.lock(); + wreq = m_requestMap.at(token); + m_mutexMap.unlock(); + } catch (const out_of_range& oor) { + m_mutexMap.unlock(); + return nullptr; + } + + std::unique_lock<std::mutex> lk(m_mutexGet); + st = wreq->m_cvGet.wait_for(lk, chrono::seconds(5)); + return (st == cv_status::no_timeout) ? wreq : nullptr; +} + +std::vector<std::string> WrapResource::getResourceTypes() +{ + return m_ocResource->getResourceTypes(); +} + +std::vector<std::string> WrapResource::getResourceInterfaces() +{ + return m_ocResource->getResourceInterfaces(); +} + +WrapRequest *WrapResource::newRequest(RequestType type) +{ + WrapRequest *wreq = new WrapRequest(this, type, ++m_requestToken); + m_requestMap[m_requestToken] = wreq; + return wreq; +} + +void WrapResource::resourceCallback(WrapRequest *wreq) +{ + parseJSON(wreq); + + if (wreq->m_forTypeOnly) { + wreq->m_typeReady = true; + return; + } + + if (wreq->m_type == RT_Observe) { + if (!m_observeCB) { + if (m_callbackRunning) + cout << "callback missing " << m_resourceID << '\n'; + return; + } + m_observeCB(wreq->m_token, wreq->m_headerOptions, wreq->m_rep, wreq->m_eCode, + wreq->m_sequenceNumber); + } else { + wreq->m_cvGet.notify_one(); + } + + wreq->m_touchTime = GetTickCount(); +} + +/* + * this parser infers types from json string since no other introspection + * is available. It also parses the key-value pairs. + */ +void WrapResource::parseJSON(WrapRequest *wreq) +{ + string sep = "\":"; + string anchor = "\"rep\":{"; + string json = wreq->m_rep.getJSONRepresentation(); + string name, type, value, next; + size_t r, e, e1, s, c; + + r = json.find(anchor); + if (r == string::npos) { + return; + } + c = r + anchor.length() - 1; + do { + c++; + if (json[c] != '"') { + if (json[c] == '}') + break; + return; + } + c++; + e = json.find(sep, c); + if (e == string::npos) { + return; + } + name = json.substr(c, e - c); + s = e + sep.length(); + char q = json[s]; + switch (q) { + case 't': + case 'f': + type = "bool"; + e1 = json.find_first_of(",}", s + 1); + if (e1 == string::npos) { + return; + } + value = json.substr(s, e1 - s); + break; + case '"': + type = "string"; + s++; + e1 = json.find_first_of("\"", s); + if (e1 == string::npos) { + return; + } + value = json.substr(s, e1 - s); + e1++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '.': + type = "number"; + e1 = json.find_first_of(",}", s + 1); + if (e1 == string::npos) { + return; + } + value = json.substr(s, e1 - s); + break; + default: + return; + } + wreq->m_valueMap[name] = value; // key-value map + m_typeMap[name] = type; // key-type map + c = e1; + } while (json[c] == ','); +} + +void WrapResource::findTypes() +{ + delete m_typeRequest; + m_typeRequest = new WrapRequest(this, RT_Get, ++m_requestToken); + m_typeRequest->m_forTypeOnly = true; + getResource(); +} + +const stringmap_t& WrapResource::getFormats() +{ + return m_typeMap; +} + +/********** WrapRequest ***********/ + +WrapRequest::WrapRequest(WrapResource *wres, RequestType type, token_t token) + : m_eCode(0), m_sequenceNumber(0), m_parent(wres), m_type(type), + m_token(token), m_forTypeOnly(false), m_typeReady(false) +{ + m_getCB = std::bind(&WrapRequest::getCB, this, + placeholders::_1, placeholders::_2, placeholders::_3); + m_putCB = std::bind(&WrapRequest::putCB, this, + placeholders::_1, placeholders::_2, placeholders::_3); + m_obsCB = std::bind(&WrapRequest::observeCB, this, + placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4); + m_touchTime = GetTickCount(); +} + +void WrapRequest::getCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, int eCode) +{ + m_headerOptions = headerOptions; + m_rep = rep; + m_eCode = eCode; + m_parent->resourceCallback(this); +} + +void WrapRequest::putCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, int eCode) +{ + m_headerOptions = headerOptions; + m_rep = rep; + m_eCode = eCode; + m_parent->resourceCallback(this); +} + +void WrapRequest::observeCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, int eCode, int sequenceNumber) +{ + m_headerOptions = headerOptions; + m_rep = rep; + m_eCode = eCode; + m_sequenceNumber = sequenceNumber; + m_parent->resourceCallback(this); +} diff --git a/examples/OICMiddle/WrapResource.h b/examples/OICMiddle/WrapResource.h new file mode 100644 index 000000000..472ce12de --- /dev/null +++ b/examples/OICMiddle/WrapResource.h @@ -0,0 +1,121 @@ +#ifndef WRAPRESOURCE_H +#define WRAPRESOURCE_H + +//****************************************************************** +// +// Copyright 2014 Intel Corporation. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// 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 <map> +#include <mutex> +#include <condition_variable> + +#include "OCPlatform.h" +#include "OCApi.h" + +#include "OICMiddle.h" + +using namespace OC; +using namespace std; + +class WrapRequest; +class MiddleClient; + +enum RequestType { + RT_Get, + RT_Put, + RT_Observe, +}; + +typedef std::shared_ptr<OCResource> ocresource_t; +typedef std::map<token_t, WrapRequest *> requestmap_t; +typedef std::function<void(const token_t token, const HeaderOptions&, const OCRepresentation&, const int, const int)> observecb_t; + +class WrapResource +{ +public: + WrapResource(string resourceID, ocresource_t resource); + + token_t getResource(); + token_t putResource(OCRepresentation& rep); + token_t observeResource(observecb_t& callback); + string getResourceID(); + bool cancelObserve(); + std::vector<std::string> getResourceTypes(); + std::vector<std::string> getResourceInterfaces(); + WrapRequest *waitResource(token_t token); + const stringmap_t& getFormats(); + + friend class WrapRequest; + friend class MiddleClient; + +protected: + WrapRequest *newRequest(RequestType type); + void resourceCallback(WrapRequest *wreq); + void parseJSON(WrapRequest *wreq); + void findTypes(); + + string m_resourceID; + ocresource_t m_ocResource; + int m_listIndex; + int m_x; + bool m_repGetReady; + bool m_gettingRep; + mutex m_mutexMap; + mutex m_mutexGet; + observecb_t m_observeCB; + bool m_callbackRunning; + int m_requestToken; + requestmap_t m_requestMap; + WrapRequest *m_observeRequest; // can only be one + stringmap_t m_typeMap; + vector<WrapRequest *> m_typeResults; + WrapRequest *m_typeRequest; +}; + +struct WrapRequest +{ + WrapRequest(WrapResource *wres, RequestType type, token_t token); + + friend class WrapResource; + + HeaderOptions m_headerOptions; + OCRepresentation m_rep; + int m_eCode; + int m_sequenceNumber; + stringmap_t m_valueMap; + unsigned long m_touchTime; + +protected: + WrapResource *m_parent; + RequestType m_type; + token_t m_token; + condition_variable m_cvGet; + bool m_forTypeOnly; + bool m_typeReady; + + void getCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode); + void putCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode); + void observeCB(const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode, const int sequenceNumber); + + GetCallback m_getCB; + PutCallback m_putCB; + ObserveCallback m_obsCB; +}; + +#endif // WRAPRESOURCE_H diff --git a/examples/OICMiddle/makefile b/examples/OICMiddle/makefile new file mode 100644 index 000000000..527921835 --- /dev/null +++ b/examples/OICMiddle/makefile @@ -0,0 +1,87 @@ +#//****************************************************************** +#// +#// Copyright 2014 Intel Corporation. +#// +#//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#// +#// 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. +#// +#//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +# override with `make BUILD=release` +# default to release build +BUILD := debug +PLATFORM := linux +CXX := g++ +#CXX := clang +OUT_DIR := $(BUILD) +OIC := ../.. +OIC_RES := $(OIC)/resource +OIC_LIB := $(OIC)/out/linux/x86_64/release +OBJS := OICMiddle.o \ + Client.o \ + Server.o \ + WrapResource.o \ + LineInput.o \ + RestInput.o + +CXX_FLAGS.debug := -O0 -g3 -std=c++0x -Wall -pthread + +CXX_FLAGS.release := -O3 -std=c++0x -Wall -pthread + +CXX_INC := -I$(OIC_RES)/include/ +CXX_INC += -I$(OIC_RES)/oc_logger/include +CXX_INC += -I$(OIC_RES)/csdk/stack/include +CXX_INC += -I$(OIC_RES)/csdk/ocsocket/include +CXX_INC += -I$(OIC_RES)/csdk/ocrandom/include +CXX_INC += -I$(OIC_RES)/csdk/logger/include +CXX_INC += -I$(OIC_RES)/csdk/libcoap +CXX_INC += -I$(OIC_RES)/../extlibs/cereal/include + +CXX_LIBS := $(OIC_LIB)/liboc.so +CXX_LIBS += $(OIC_LIB)/liboctbstack.so +CXX_LIBS += $(OIC_LIB)/liboc_logger.so +CXX_LIBS += $(OIC_LIB)/liboc_logger_core.so +CXX_LIBS += $(OIC_LIB)/libcoap.so + +all: prep_dirs OICMiddle + +prep_dirs: + -mkdir -p $(OUT_DIR) + +OICMiddle: $(OBJS) + $(CXX) $(CXX_FLAGS.$(BUILD)) -o $(OUT_DIR)/$@ $(OBJS) $(CXX_LIBS) + +OICMiddle.o: OICMiddle.cpp OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) OICMiddle.cpp $(CXX_INC) + +Client.o: Client.cpp Client.h OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) Client.cpp $(CXX_INC) + +Server.o: Server.cpp Server.h OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) Server.cpp $(CXX_INC) + +WrapResource.o: WrapResource.cpp WrapResource.h OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) WrapResource.cpp $(CXX_INC) + +LineInput.o: LineInput.cpp LineInput.h OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) LineInput.cpp $(CXX_INC) + +RestInput.o: RestInput.cpp RestInput.h OICMiddle.h + $(CXX) -c $(CXX_FLAGS.$(BUILD)) RestInput.cpp $(CXX_INC) + +clean: + rm $(OBJS) + rm -rf debug + rm -rf release + |