This a complete version of OICMiddle.
authorSudarshan Prasad <sudarshan.prasad@intel.com>
Tue, 30 Dec 2014 01:20:39 +0000 (17:20 -0800)
committerSudarshan Prasad <sudarshan.prasad@intel.com>
Tue, 30 Dec 2014 03:38:23 +0000 (19:38 -0800)
The ReST capability has limited functionality, which we hope to expand
shortly after release.  We believe the other parts of OICMiddle are
sufficiently robust that the application will be useful as both demo
and example in the OIC 0.9.0 release.

In a partial review prior to moving from 01.org to IoTivity, one
reviewer provided 50 comments.  All of the comments were addressed,
though not all were agreed with.  In some cases, comments were added
to the code to address the concern.  Others may require further
discussion.

Change-Id: I3e52de09a0737dd8dd0323fa2a20e4c94224f9c7
Signed-off-by: John.Light <john.j.light@intel.com>
Signed-off-by: Sudarshan Prasad <sudarshan.prasad@intel.com>
15 files changed:
examples/OICMiddle/Client.cpp [new file with mode: 0644]
examples/OICMiddle/Client.h [new file with mode: 0644]
examples/OICMiddle/LineInput.cpp [new file with mode: 0644]
examples/OICMiddle/LineInput.h [new file with mode: 0644]
examples/OICMiddle/OICMiddle.cpp [new file with mode: 0644]
examples/OICMiddle/OICMiddle.h [new file with mode: 0644]
examples/OICMiddle/README [new file with mode: 0644]
examples/OICMiddle/RestInput.cpp [new file with mode: 0644]
examples/OICMiddle/RestInput.h [new file with mode: 0644]
examples/OICMiddle/SConstruct [new file with mode: 0644]
examples/OICMiddle/Server.cpp [new file with mode: 0644]
examples/OICMiddle/Server.h [new file with mode: 0644]
examples/OICMiddle/WrapResource.cpp [new file with mode: 0644]
examples/OICMiddle/WrapResource.h [new file with mode: 0644]
examples/OICMiddle/makefile [new file with mode: 0644]

diff --git a/examples/OICMiddle/Client.cpp b/examples/OICMiddle/Client.cpp
new file mode 100644 (file)
index 0000000..8d6e7d1
--- /dev/null
@@ -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 (file)
index 0000000..ef32e86
--- /dev/null
@@ -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 (file)
index 0000000..52a8528
--- /dev/null
@@ -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 (file)
index 0000000..bc7d4af
--- /dev/null
@@ -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 (file)
index 0000000..0da4145
--- /dev/null
@@ -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 (file)
index 0000000..457b7a8
--- /dev/null
@@ -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 (file)
index 0000000..518fb2a
--- /dev/null
@@ -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 (file)
index 0000000..b6e240f
--- /dev/null
@@ -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 (file)
index 0000000..2e1cad6
--- /dev/null
@@ -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 (file)
index 0000000..02a2d8e
--- /dev/null
@@ -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 (file)
index 0000000..b55e715
--- /dev/null
@@ -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 (file)
index 0000000..049c708
--- /dev/null
@@ -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 (file)
index 0000000..3cadb7d
--- /dev/null
@@ -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 (file)
index 0000000..472ce12
--- /dev/null
@@ -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 (file)
index 0000000..5279218
--- /dev/null
@@ -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
+