Merge branch 'master' of ssh://git.smart-api.io//srv/git/smartapi
authorJani <jani@asema.com>
Mon, 26 Aug 2019 12:08:37 +0000 (15:08 +0300)
committerJani <jani@asema.com>
Mon, 26 Aug 2019 12:08:37 +0000 (15:08 +0300)
40 files changed:
Common/C++/SmartAPI/smartapi/agents/Agent.cpp
Common/C++/SmartAPI/smartapi/agents/Agent.h
Common/C++/SmartAPI/smartapi/agents/RegistrationAgent.cpp
Common/C++/SmartAPI/smartapi/agents/RegistrationAgent.h
Common/C++/SmartAPI/smartapi/common/Tools.h
Common/C++/SmartAPI/smartapi/model/Obj.h
Common/Java/SmartAPI/src/smartapi/common/ClassMapper.java
Common/Java/SmartAPI/src/smartapi/model/Obj.java
Common/Java/SmartAPI/src/smartapi/rdf/List.java
Common/Python/SmartAPI/agents/Agent.py
Common/Python/SmartAPI/agents/SearchAgent.py
Common/Python/SmartAPI/common/ClassMapper.py
Common/Python/SmartAPI/common/HttpMessage.py
Common/Python/SmartAPI/common/SERIALIZATION.py
Common/Python/SmartAPI/common/Tools.py
Common/Python/SmartAPI/model/Activity.py
Common/Python/SmartAPI/model/DependentPriceSpecification.py
Common/Python/SmartAPI/model/Obj.py
Common/Python/SmartAPI/model/ObjectOperationDependentPriceSpecification.py
Common/Python/SmartAPI/model/Offering.py
Common/Python/SmartAPI/model/Person.py
Common/Python/SmartAPI/model/PriceSpecification.py
Common/Python/SmartAPI/model/PropertyDependentPriceSpecification.py
Common/Python/SmartAPI/model/ValueObject.py
Common/Python/SmartAPI/rdf/List.py
Common/Python/SmartAPI/rdf/Model.py
Common/Python/SmartAPI/rdf/NudeList.py
Common/Python/SmartAPI/rdf/Resource.py
Common/Python/SmartAPI/rdf/Variant.py
Common/Python/SmartAPI/tests/TestSequences.py
Examples/C++/EventAgentSample/server.py [new file with mode: 0755]
Examples/C++/NotificationSample/NotificationSample.h [new file with mode: 0644]
Examples/C++/NotificationSample/NotificationSample.pro [new file with mode: 0644]
Examples/C++/NotificationSample/test_listener.py [new file with mode: 0755]
Examples/Python/AdaptDataService/AdaptDataService.py
Examples/Python/AdaptDataService/SampleSearchClient.py
Examples/Python/AdaptDataService/httpclient.py [new file with mode: 0644]
Examples/Python/CustomPropertiesAndClassesSample/smartapi_server_client_sample.py
Examples/Python/EventAgentSample/client.py [new file with mode: 0755]
Examples/Python/EventAgentSample/server.py [new file with mode: 0755]

index f2cda8e72e3c8e286fe0a24d820449a25f480fcc..0b978d46b4eae933453367eaa5cd1a0d50021557 100644 (file)
@@ -126,6 +126,11 @@ QByteArray  Agent::runSyncQuery(QString uri, QString boundary, QHttpMultiPart* p
        return result;
 }
 
+void Agent::runAsyncQuery(int callId, QObject* caller, const char* onSuccess, const char* onFail, QString uri, QVariant userData)
+{
+       runAsyncQuery(callId, caller, onSuccess, onFail, uri, QString(), QString(), QString(), userData);
+}
+
 void Agent::runAsyncQuery(int callId, QObject* caller, const char* onSuccess, const char* onFail, QString uri, QString contentType, QString acceptedType, QString payload, QVariant userData)
 {
        runAsyncQuery(AsyncData(callId, caller, onSuccess, onFail, userData), uri, contentType, acceptedType, payload);
@@ -135,14 +140,15 @@ void Agent::runAsyncQuery(AsyncData adata, QString uri, QString contentType, QSt
 {
        QNetworkRequest r;
        r.setUrl(QUrl(uri));
-       r.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
-       r.setRawHeader("Accept", acceptedType.toLatin1());
-       if (mAuthorizationHeaderValue.length() > 0) {
-               r.setRawHeader("Authorization", mAuthorizationHeaderValue.toLatin1());
-       }
+       if (contentType.length() > 0)                                   r.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
+       if (acceptedType.length() > 0)                                  r.setRawHeader("Accept", acceptedType.toLatin1());
+       if (mAuthorizationHeaderValue.length() > 0)     r.setRawHeader("Authorization", mAuthorizationHeaderValue.toLatin1());
 
        if (mDebug) {
-               fprintf(stderr, "\nPOST request to %s, call id %d:\n%s\n", uri.toLocal8Bit().constData(), adata.callId, payload.toLocal8Bit().constData());
+               if (payload.isEmpty())
+                       fprintf(stderr, "\nGET request to %s, call id %d:\n", uri.toLocal8Bit().constData(), adata.callId);
+               else
+                       fprintf(stderr, "\nPOST request to %s, call id %d:\n%s\n", uri.toLocal8Bit().constData(), adata.callId, payload.toLocal8Bit().constData());
        }
 
        QNetworkReply* reply = NULL;
index 0d3ffa1cc39005b534b18021a7c3bea246406859..5bd9f34a7efb4b988f088fba33aa372f17fbea2f 100644 (file)
@@ -45,6 +45,7 @@ protected:
        QByteArray runSyncQuery(QString uri, QString contentType, QString acceptedType, QString payload = QString(), QMap<QString, QString> headers = QMap<QString, QString>(), QByteArray *contentTypeOut = NULL);
        QByteArray runSyncQuery(QString uri, QString contentType, QHttpMultiPart* parts, QByteArray* contentTypeOut = NULL);
        void runAsyncQuery(int callId, QObject* caller, const char* onSuccess, const char* onFail, QString uri, QString contentType, QString acceptedType, QString payload, QVariant userData);
+       void runAsyncQuery(int callId, QObject* caller, const char* onSuccess, const char* onFail, QString uri, QVariant userData);
        void runAsyncQuery(AsyncData adata, QString uri, QString contentType, QString acceptedType, QString payload);
        void copyErrorStrings(Message* message, QVariantList* errorsOut);
 
index aff0ad886c0251ade8807cdbd472dff04237ed2e..439d8de0840a8de94f34117a9c2a57d7900ddd54 100644 (file)
@@ -23,6 +23,7 @@ RegistrationAgent::RegistrationAgent(QString myIdentity, QNetworkAccessManager*
        mRegistrationUri = SMARTAPI_REGISTER_URI;
        mRegistryKeyUri = SMARTAPI_REGISTER_KEY_URI;
        cachedPKey = NULL;
+       mEncrypted = true;
 }
 
 RegistrationAgent::~RegistrationAgent()
@@ -37,16 +38,11 @@ void RegistrationAgent::setIdentity(QString identity)
 
 bool RegistrationAgent::registrate(bool lock)
 {
-       QByteArray aesKey;
-       Request* req = generateRequest(RESOURCE__WRITE, &aesKey, lock, true);
-       HttpMessage* message = Tools::serializeRequest(req, mSerialization);
-       QByteArray messageBody = message->getAsByteArray();
-       QByteArray contentType = message->getContentType();
-       req->destroyCascade();
-       mEntities.clear();
-       delete message;
-
-       runAsyncQuery(1, this, "onRegisterDone", "onRegisterFailed", mRegistrationUri, contentType, CONTENT_TYPE__TURTLE, messageBody, aesKey);
+       if (mEncrypted) {
+               getServerPublicKey(0, this, "onServerPublicKeyForRegister", "onServerPublicKeyFailure", lock);
+       } else {
+               onServerPublicKeyForRegister(0, QByteArray(), lock);
+       }
        return true;
 }
 
@@ -56,34 +52,29 @@ bool RegistrationAgent::registrate(Entity* e, bool lock)
        return registrate(lock);
 }
 
-QByteArray RegistrationAgent::getRegistrationPayload()
+bool RegistrationAgent::deregistrate()
 {
-       QByteArray aesKey;
-       Request* req = generateRequest(RESOURCE__WRITE, &aesKey, false, false);
-       HttpMessage* message = Tools::serializeRequest(req, mSerialization);
-       QByteArray messageBody = message->getAsByteArray();
-       req->destroyCascade();
-       mEntities.clear();
-       delete message;
-       return messageBody;
+       if (mEncrypted) {
+               getServerPublicKey(0, this, "onServerPublicKeyForDeregister", "onServerPublicKeyFailure", QVariant());
+       } else {
+               onServerPublicKeyForDeregister(0, QByteArray(), QVariant());
+       }
+       return true;
 }
 
-bool RegistrationAgent::deregistrate()
+QByteArray RegistrationAgent::getRegistrationPayload()
 {
        QByteArray aesKey;
-       Request* req = generateRequest(RESOURCE__DELETE, &aesKey);
+       Request* req = generateRequest(RESOURCE__WRITE, &aesKey, NULL, false, false);
        HttpMessage* message = Tools::serializeRequest(req, mSerialization);
        QByteArray messageBody = message->getAsByteArray();
-       QByteArray contentType = message->getContentType();
        req->destroyCascade();
        mEntities.clear();
        delete message;
-
-       runAsyncQuery(1, this, "onDeregisterDone", "onDeregisterFailed", mRegistrationUri, contentType, CONTENT_TYPE__TURTLE, messageBody, aesKey);
-       return true;
+       return messageBody;
 }
 
-Request* RegistrationAgent::generateRequest(QString method, QByteArray* aesKey, bool locked, bool encypted)
+Request* RegistrationAgent::generateRequest(QString method, QByteArray* aesKey, EVP_PKEY* pubKey, bool locked, bool encypted)
 {
        Request* req = RequestFactory::createRegistrationRequest(mMyIdentity);
        Activity* activity = new Activity(SMARTAPI_REGISTER_ACTIVITY_URI);
@@ -101,11 +92,8 @@ Request* RegistrationAgent::generateRequest(QString method, QByteArray* aesKey,
                fprintf(stderr, "\nRequest before encrypting:\n%s\n", Tools::toString(req, mSerialization).toLocal8Bit().constData());
        }
 
-       if (encypted) {
-               cachedPKey = getServerPublicKey();
-               if (aesKey && cachedPKey) {
-                       *aesKey = activity->encrypt(cachedPKey);
-               }
+       if (encypted && aesKey && pubKey) {
+               *aesKey = activity->encrypt(pubKey);
        }
 
        return req;
@@ -116,6 +104,70 @@ void RegistrationAgent::addEntity(Entity* e)
        mEntities.append(e);
 }
 
+EVP_PKEY* RegistrationAgent::getServerPublicKey()
+{
+       QString keyString = "";
+       EVP_PKEY* key = NULL;
+       keyString = runSyncQuery(mRegistryKeyUri, "", "");
+       key = SmartAPICrypto::extractPemFormatPublicKey(keyString);
+       return key;
+}
+
+void RegistrationAgent::getServerPublicKey(int callId, QObject* caller, const char* onSuccess, const char* onFail, QVariant userData)
+{
+       AsyncData ac(callId, caller, onSuccess, onFail, userData);
+       runAsyncQuery(callId, this, "onServerPublicKeyReceived", "onServerPublicKeyFailure", mRegistryKeyUri, qVariantFromValue(ac));
+}
+
+void RegistrationAgent::onServerPublicKeyReceived(int callId, QVariant replyContentType, QByteArray replyData, QVariant userData)
+{
+       Q_UNUSED(callId)
+       Q_UNUSED(replyContentType)
+       AsyncData h = qvariant_cast<AsyncData>(userData);
+       if (h.caller != NULL && h.onSuccess != NULL) {
+               QMetaObject::invokeMethod(h.caller, h.onSuccess, Qt::DirectConnection, Q_ARG(int, h.callId), Q_ARG(QByteArray, replyData), Q_ARG(QVariant, h.callbackData));
+       }
+}
+
+void RegistrationAgent::onServerPublicKeyForRegister(int callId, QByteArray replyData, QVariant userData)
+{
+       AsyncData h = qvariant_cast<AsyncData>(userData);
+       EVP_PKEY* key = SmartAPICrypto::extractPemFormatPublicKey(QString(replyData));
+
+       QByteArray aesKey;
+       Request* req = generateRequest(RESOURCE__WRITE, &aesKey, key, userData.toBool(), true);
+       HttpMessage* message = Tools::serializeRequest(req, mSerialization);
+       QByteArray messageBody = message->getAsByteArray();
+       QByteArray contentType = message->getContentType();
+       req->destroyCascade();
+       mEntities.clear();
+       delete message;
+
+       runAsyncQuery(callId, this, "onRegisterDone", "onRegisterFailed", mRegistrationUri, contentType, CONTENT_TYPE__TURTLE, messageBody, aesKey);
+}
+
+void RegistrationAgent::onServerPublicKeyForDeregister(int callId, QByteArray replyData, QVariant userData)
+{
+       AsyncData h = qvariant_cast<AsyncData>(userData);
+       EVP_PKEY* key = SmartAPICrypto::extractPemFormatPublicKey(QString(replyData));
+
+       QByteArray aesKey;
+       Request* req = generateRequest(RESOURCE__DELETE, &aesKey, key, false, true);
+       HttpMessage* message = Tools::serializeRequest(req, mSerialization);
+       QByteArray messageBody = message->getAsByteArray();
+       QByteArray contentType = message->getContentType();
+       req->destroyCascade();
+       mEntities.clear();
+       delete message;
+
+       runAsyncQuery(callId, this, "onDeregisterDone", "onDeregisterFailed", mRegistrationUri, contentType, CONTENT_TYPE__TURTLE, messageBody, aesKey);
+}
+
+void RegistrationAgent::onServerPublicKeyFailure(int callId, int errorCode, QVariant userData)
+{
+       onRegisterFailed(callId, errorCode, userData);
+}
+
 void RegistrationAgent::onRegisterDone(int callId, QVariant replyContentType, QByteArray replyData, QVariant keyData)
 {
        if (cachedPKey != NULL) { SmartAPICrypto::destroyKey(cachedPKey); cachedPKey = NULL; }
@@ -156,6 +208,7 @@ void RegistrationAgent::onRegisterDone(int callId, QVariant replyContentType, QB
 
 void RegistrationAgent::onRegisterFailed(int callId, int errorCode, QVariant callbackData)
 {
+       Q_UNUSED(callbackData)
        if (cachedPKey != NULL) { SmartAPICrypto::destroyKey(cachedPKey); cachedPKey = NULL; }
        emit registrationNetworkError(callId, errorCode);
 }
@@ -188,11 +241,3 @@ void RegistrationAgent::onDeregisterFailed(int callId, int errorCode)
        emit deregistrationNetworkError(callId, errorCode);
 }
 
-EVP_PKEY* RegistrationAgent::getServerPublicKey()
-{
-       QString keyString = "";
-       EVP_PKEY* key = NULL;
-       keyString = runSyncQuery(mRegistryKeyUri, "", "");
-       key = SmartAPICrypto::extractPemFormatPublicKey(keyString);
-       return key;
-}
index fe4d2533c1e57905bc8128a55be880dc293af9a1..32dd56c2680cbb5895ebe203745460918d93450b 100644 (file)
@@ -30,6 +30,10 @@ public:
        QString getRegistryKeyUri() { return mRegistryKeyUri; }
 
 public slots:
+       void onServerPublicKeyReceived(int callId, QVariant replyContentType, QByteArray replyData, QVariant userData);
+       void onServerPublicKeyForRegister(int callId, QByteArray replyData, QVariant userData);
+       void onServerPublicKeyForDeregister(int callId, QByteArray replyData, QVariant userData);
+       void onServerPublicKeyFailure(int callId, int errorCode, QVariant userData);
        void onRegisterDone(int callId, QVariant replyContentType, QByteArray replyData, QVariant keyData);
        void onRegisterFailed(int callId, int errorCode, QVariant callbackData);
        void onDeregisterDone(int callId, QVariant replyContentType, QByteArray replyData, QVariant keyData);
@@ -44,14 +48,16 @@ signals:
        void deregistrationSucceeded(int);
 
 private:
-       Request* generateRequest(QString method, QByteArray* aesKey, bool locked = false, bool encrypted = true);
+       Request* generateRequest(QString method, QByteArray* aesKey, EVP_PKEY* pubKey, bool locked = false, bool encrypted = true);
        EVP_PKEY* getServerPublicKey();
+       void getServerPublicKey(int callId, QObject* caller, const char* onSuccess, const char* onFail, QVariant userData);
 
        QString mMyIdentity;
        QString mRegistrationUri;
        QString mRegistryKeyUri;
        QList<Entity*> mEntities;
        EVP_PKEY* cachedPKey;
+       bool mEncrypted;
 };
 
 #endif // __REGISTRATIONAGENT_H__
index 7f96c9f66bce3f1b5f50f11a08a9c7b11855bfc2..fe3432e789c591aa6b792d4e848ef8accd840514 100644 (file)
@@ -39,6 +39,7 @@ public:
        static Request* parseRequest(QByteArray content, QByteArray contentType);
        static Response* parseResponse(QByteArray content, QByteArray contentType);
        static Notification* parseNotification(QByteArray content);
+       static Obj* parseMessage(QByteArray content, QByteArray contentType);
 
        static QString toString(Obj* obj, QString serialization, QList<PrefixUriPair> uris);
        static QString toString(Obj* obj, QString serialization = SERIALIZATION__DEFAULT, Model* model = NULL);
@@ -115,7 +116,6 @@ public:
 
 private:
        static HttpMessage* serializeMessage(Obj* obj, QString serialization, QByteArray* contentTypeOut);
-       static Obj* parseMessage(QByteArray content, QByteArray contentType);
 
        static void addRecursivelyToModel(Resource* resource, Model* model);
        static void addRecursivelyToStatements(Resource* resource, Model* model, QList<Statement*>* statements);
index d3979de865fe537cdc9a51c8881558d122542b11..c0c3764aabb5f79a17281a466866e09004613744 100644 (file)
@@ -100,10 +100,6 @@ public:
        QString getObjectIdentifier()                                                           { return getIdentifierUriPart(2); }
        QString getObjectIdentifier(int index)                                          { return getIdentifierUriPart(1 + index); }
 
-       bool hasTransaction()                                                                           { return (mTransaction != NULL); }
-       Transaction* getTransaction()                                                           { return mTransaction; }
-       void setTransaction(Transaction* transaction)                           { mTransaction = transaction; }
-
        bool hasSameAs()                                                                                        { return (mSameAs != NULL); }
        void setSameAs(QString uri);
        void setSameAs(Obj* sameAs)                                                                     { delete mSameAs; mSameAs = sameAs; }
@@ -148,6 +144,10 @@ public:
                        QString unitOfmeasurement, QString name,
                        QString description);
 
+       bool hasTransaction()                                                                           { return (mTransaction != NULL); }
+       Transaction* getTransaction()                                                           { return mTransaction; }
+       void setTransaction(Transaction* transaction)                           { mTransaction = transaction; }
+
        void addError(Error* e)                                                                         { mErrors.append(e); }
        PropertyList<Error*> getErrors()                                                        { return mErrors; }
        bool hasErrors()                                                                                        { return (mErrors.length() > 0); }
index 3b1d8f773e4da75d4cf25d75a4538ba367b61d4c..0656c047b1640be53531a07283076fb31420861d 100644 (file)
@@ -114,12 +114,10 @@ public class ClassMapper {
                                Obj ret = ClassMapper.getClass( obj.getIdentifierUri() );
                                if ( ret != null ) {
                                        options.add(ret);
-                                       //return ret;
                                }
                        }
                }
                return ClassMapper.getMotherClass(options);
-               //return new Obj();
        }
        
        public static Obj getClass(String type)
@@ -135,61 +133,6 @@ public class ClassMapper {
                } else {
                        return null;
                }
-//             if (type != null) {
-//                     if (type.equals( RESOURCE.ABILITY )) return new Ability();
-//                     if (type.equals( RESOURCE.ABSTRACTENTITY )) return new AbstractEntity();
-//                     if (type.equals( RESOURCE.ACTIVITY )) return new Activity();
-//                     if (type.equals( RESOURCE.ADDRESS )) return new Address();
-//                     if (type.equals( RESOURCE.ALIVEREQUEST )) return new AliveRequest();
-//                     if (type.equals( RESOURCE.ALIVERESPONSE )) return new AliveResponse();
-//                     if (type.equals( RESOURCE.AVAILABILITY )) return new Availability();
-//                     if (type.equals( RESOURCE.CONDITION )) return new Condition();
-//                     if (type.equals( RESOURCE.CONTROLLABILITY )) return new Controllability();
-//                     if (type.equals( RESOURCE.GEO_POINT )) return new Coordinates();
-//                     if (type.equals( RESOURCE.GRADING )) return new Grading();
-//                     if (type.equals( RESOURCE.DEVICE )) return new Device();
-//                     if (type.equals( RESOURCE.DIRECTION )) return new Direction();
-//                     if (type.equals( RESOURCE.ENTITY )) return new Entity();
-//                     if (type.equals( RESOURCE.ENUMERATION )) return new Enumeration();
-//                     if (type.equals( RESOURCE.ERROR )) return new smartapi.model.Error();
-//                     if (type.equals( RESOURCE.EVALUATION )) return new Evaluation();
-//                     if (type.equals( RESOURCE.INPUT )) return new Input();
-//                     if (type.equals( RESOURCE.INTERFACEADDRESS )) return new InterfaceAddress();
-//                     if (type.equals( RESOURCE.ADDRESS )) return new Address();
-//                     if (type.equals( RESOURCE.MAP )) return new Map();
-//                     if (type.equals( RESOURCE.MESSAGE )) return new Message(); 
-//                     if (type.equals( RESOURCE.NOTIFICATION )) return new Notification();
-//                     if (type.equals( RESOURCE.OBJECT )) return new Obj();
-//                     if (type.equals( RESOURCE.OFFERING )) return new Offering();
-//                     if (type.equals( RESOURCE.ORIENTATION )) return new Orientation();
-//                     if (type.equals( RESOURCE.OUTPUT )) return new Output();
-//                     if (type.equals( RESOURCE.ORGANIZATION )) return new Organization();
-//                     if (type.equals( RESOURCE.PARAMETER )) return new Parameter();
-//                     if (type.equals( RESOURCE.PERSON )) return new Person();
-//                     if (type.equals( RESOURCE.PHYSICALENTITY )) return new PhysicalEntity();
-//                     if (type.equals( RESOURCE.PROVENANCE )) return new Provenance();
-//                     if (type.equals( RESOURCE.REQUEST )) return new Request();
-//                     if (type.equals( RESOURCE.RESPONSE )) return new Response();
-//                     if (type.equals( RESOURCE.RING )) return new Ring();
-//                     if (type.equals( RESOURCE.ROUTE )) return new Route();
-//                     if (type.equals( RESOURCE.SERVICE )) return new Service();
-//                     if (type.equals( RESOURCE.SERVICEPROVIDER )) return new ServiceProvider();
-//                     if (type.equals( RESOURCE.SIZE )) return new Size();
-//                     if (type.equals( RESOURCE.VELOCITY )) return new Velocity();
-//                     if (type.equals( RESOURCE.SOMEITEMS )) return new SomeItems();
-//                     if (type.equals( RESOURCE.STATUS )) return new Status();
-//                     if (type.equals( RESOURCE.SYSTEMOFINTEREST )) return new SystemOfInterest();
-//                     if (type.equals( RESOURCE.TEMPORALCONTEXT )) return new TemporalContext();
-//                     if (type.equals( RESOURCE.TIMESERIES )) return new TimeSeries();
-//                     if (type.equals( RESOURCE.TRANSACTION )) return new Transaction();
-//                     if (type.equals( RESOURCE.UNITPRICESPECIFICATION )) return new UnitPriceSpecification();
-//                     if (type.equals( RESOURCE.VALUEOBJECT )) return new ValueObject();
-//                     if (type.equals( RESOURCE.WAYPOINT )) return new Waypoint();
-//                     if (type.equals( RESOURCE.WAYPOINTS )) return new Waypoints();
-//                     if (type.equals( RESOURCE.ZONE )) return new Zone();
-//             }
-//             return null;
-
        }
        
        public static Obj getMotherClass(ArrayList<Obj> options)
index 6971f83d65e71c126d70ebe10e2d9840ed7e0aef..f54efb8a19594da47c471abb7a128451778277b5 100644 (file)
@@ -1309,7 +1309,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public String getFirstAsString(int propertyIndex)
@@ -1319,7 +1319,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public Integer getFirstAsInt(int propertyIndex)
@@ -1329,7 +1329,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public Double getFirstAsDouble(int propertyIndex)
@@ -1339,7 +1339,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public Boolean getFirstAsBoolean(int propertyIndex)
@@ -1349,7 +1349,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public Date getFirstAsDate(int propertyIndex)
@@ -1359,7 +1359,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
        public Duration getFirstAsDuration(int propertyIndex)
@@ -1369,7 +1369,7 @@ public class Obj implements ObjectInterface {
                } catch ( Exception e ) {
                        e.printStackTrace();
                        return null;
-               }               
+               }
        }
 
 
@@ -1400,7 +1400,7 @@ public class Obj implements ObjectInterface {
                property = NS.toAbsoluteUri(property);
                Integer propertyIndex = PROPERTY.getKey(property);
                if ( propertyIndex != null ) {
-                       return getFirst(propertyIndex);                 
+                       return getFirst(propertyIndex); 
                } else {
                        return null;
                }
index aab47f58d344b3af462517f09541f74a3bc4a07a..7b9d954b07b4662c4fae209e91b26364655dc24a 100644 (file)
@@ -120,10 +120,6 @@ abstract public class List extends Obj {
        
        public void addItems(Object obj)
        {
-//             if ( obj instanceof List ) {
-//                     list.addAll(((List)obj).getItems());
-//                     return;
-//             }
                if ( obj instanceof Statement ) {
                        RDFNode node = (RDFNode)((Statement)obj).getObject();
                        if ( node.isResource() ) {
index 6bccbb7319dafe0bf3a5f6715924d8b741813532..15b5c8ed1e104170f6875d205a37aa695c5830a4 100755 (executable)
@@ -45,7 +45,6 @@ class Agent(object):
         return self.serverAddress
     
     def setServerAddress(self, serverAddress):
-        print "setting address", self, serverAddress
         self.serverAddress = serverAddress
     
     def debugMode(self):
index fdd3ba422a84e8072e43983eda8a0935ce0ffe1a..1e9dded1a2221288dc0f17a23baed5f6e242595f 100755 (executable)
@@ -65,11 +65,12 @@ class SearchAgent(Agent):
         try:
             # send message
             responseStr = self.httpClient.sendPost(self.serverAddress, messageBody, self.serialization, self.serialization, seasMethod=self.httpClient.SMARTAPI_METHOD_REQUEST)[0]                        
+            
             response = Response.fromString(responseStr, self.serialization)
             if response is not None:
                 if SearchAgent.debug:
                     print '\n Response from SMARTAPI Search Service:'
-                    print Tools.toString(response, self.serialization)
+                    print responseStr
                     Tools.printErrors(response)
                 if (response.hasActivity()):
                     return response.firstActivity().getEntities()
index fd8e5e8e911ab657059103e40d30190b7d755ac0..da0b3e1a13bc955264f35a5939a2a9bd850ba049 100644 (file)
@@ -6,36 +6,51 @@ class ClassMapper(object):
                from SmartAPI.model.Ability import Ability
                from SmartAPI.model.AbstractEntity import AbstractEntity
                from SmartAPI.model.Activity import Activity
-               from SmartAPI.model.Address import Address
                from SmartAPI.model.Account import Account
                from SmartAPI.model.AccountAction import AccountAction
                from SmartAPI.model.AccountTransaction import AccountTransaction
+               from SmartAPI.model.Address import Address
                from SmartAPI.model.AliveRequest import AliveRequest
                from SmartAPI.model.AliveResponse import AliveResponse
+               from SmartAPI.model.Authorization import Authorization
                from SmartAPI.model.Availability import Availability
                from SmartAPI.model.Capacity import Capacity
                from SmartAPI.model.Condition import Condition
                from SmartAPI.model.Contract import Contract
                from SmartAPI.model.Controllability import Controllability
                from SmartAPI.model.Coordinates import Coordinates
+               from SmartAPI.model.DependentPriceSpecification import DependentPriceSpecification
                from SmartAPI.model.Device import Device
                from SmartAPI.model.Direction import Direction
+               from SmartAPI.model.DistanceDependentPriceSpecification import DistanceDependentPriceSpecification
+               from SmartAPI.model.DurationAtDistanceDependentPriceSpecification import DurationAtDistanceDependentPriceSpecification
+               from SmartAPI.model.DurationAtZoneDependentPriceSpecification import DurationAtZoneDependentPriceSpecification
+               from SmartAPI.model.DurationDependentPriceSpecification import DurationDependentPriceSpecification
                from SmartAPI.model.Entity import Entity
+               from SmartAPI.model.Enumeration import Enumeration
                from SmartAPI.model.Error import Error
                from SmartAPI.model.Evaluation import Evaluation
+               from SmartAPI.model.Grading import Grading
                from SmartAPI.model.Input import Input
                from SmartAPI.model.InterfaceAddress import InterfaceAddress
+               from SmartAPI.model.License import License
+               from SmartAPI.model.LinearCalculation import LinearCalculation
                from SmartAPI.model.Map import Map
                from SmartAPI.model.Message import Message
                from SmartAPI.model.Notification import Notification
                from SmartAPI.model.Obj import Obj
+               from SmartAPI.model.ObjectOperationDependentPriceSpecification import ObjectOperationDependentPriceSpecification
+               from SmartAPI.model.Offering import Offering
                from SmartAPI.model.Organization import Organization
                from SmartAPI.model.Orientation import Orientation
                from SmartAPI.model.Output import Output
                from SmartAPI.model.Parameter import Parameter
                from SmartAPI.model.Person import Person
                from SmartAPI.model.PhysicalEntity import PhysicalEntity
+               from SmartAPI.model.PriceSpecification import PriceSpecification
+               from SmartAPI.model.PropertyDependentPriceSpecification import PropertyDependentPriceSpecification
                from SmartAPI.model.Provenance import Provenance
+               from SmartAPI.model.Reference import Reference
                from SmartAPI.model.Request import Request
                from SmartAPI.model.Response import Response
                from SmartAPI.model.Ring import Ring
@@ -43,20 +58,26 @@ class ClassMapper(object):
                from SmartAPI.model.Service import Service
                from SmartAPI.model.ServiceProvider import ServiceProvider
                from SmartAPI.model.Size import Size
+               from SmartAPI.model.SomeItems import SomeItems
                from SmartAPI.model.Status import Status
                from SmartAPI.model.SystemOfInterest import SystemOfInterest
                from SmartAPI.model.TemporalContext import TemporalContext
+               from SmartAPI.model.TimeDependentPriceListSpecification import TimeDependentPriceListSpecification
+               from SmartAPI.model.TimeDependentPriceSpecification import TimeDependentPriceSpecification
                from SmartAPI.model.TimeSeries import TimeSeries
+               from SmartAPI.model.Transaction import Transaction
+               from SmartAPI.model.TravelDistanceDependentPriceSpecification import TravelDistanceDependentPriceSpecification
+               from SmartAPI.model.TravelDistanceDurationDependentPriceSpecification import TravelDistanceDurationDependentPriceSpecification
+               from SmartAPI.model.TypeAndQuantityNode import TypeAndQuantityNode
+               from SmartAPI.model.UnitPriceSpecification import UnitPriceSpecification
                from SmartAPI.model.ValueObject import ValueObject
                from SmartAPI.model.Velocity import Velocity
                from SmartAPI.model.Waypoint import Waypoint
                from SmartAPI.model.Waypoints import Waypoints
                from SmartAPI.model.Zone import Zone
-               from SmartAPI.model.UnitPriceSpecification import UnitPriceSpecification
-               from SmartAPI.model.Enumeration import Enumeration
-               from SmartAPI.model.Transaction import Transaction
-               from SmartAPI.model.SomeItems import SomeItems
-               from SmartAPI.model.Offering import Offering
+               from SmartAPI.model.ZoneDependentPriceSpecification import ZoneDependentPriceSpecification
+               from SmartAPI.model.ZoneTravelDependentPriceSpecification import ZoneTravelDependentPriceSpecification
+               from SmartAPI.model.ZoneTravelDurationDependentPriceSpecification import ZoneTravelDurationDependentPriceSpecification
 
                
                self.class_map = {
@@ -70,11 +91,16 @@ class ClassMapper(object):
                        RESOURCE.ADDRESS: Address,
                        RESOURCE.ALIVEREQUEST: AliveRequest,
                        RESOURCE.ALIVERESPONSE: AliveResponse,
+                       RESOURCE.AUTHORIZATION: Authorization,
                        RESOURCE.AVAILABILITY: Availability,
                        RESOURCE.CAPACITY: Capacity,
                        RESOURCE.CONDITION: Condition,
                        RESOURCE.CONTRACT: Contract,
                        RESOURCE.CONTROLLABILITY: Controllability,
+                       RESOURCE.DISTANCEDEPENDENTPRICESPECIFICATION: DistanceDependentPriceSpecification,
+                       RESOURCE.DURATIONATDISTANCEDEPENDENTPRICESPECIFICATION: DurationAtDistanceDependentPriceSpecification,
+                       RESOURCE.DURATIONATZONEDEPENDENTPRICESPECIFICATION: DurationAtZoneDependentPriceSpecification,
+                       RESOURCE.DURATIONDEPENDENTPRICESPECIFICATION: DurationDependentPriceSpecification,
                        RESOURCE.DEVICE: Device,
                        RESOURCE.DIRECTION: Direction,
                        RESOURCE.ENTITY: Entity,
@@ -114,9 +140,20 @@ class ClassMapper(object):
        #               RESOURCE.VARIANT: Variant,
                        RESOURCE.WAYPOINT: Waypoint,
                        RESOURCE.WAYPOINTS: Waypoints,
-                       RESOURCE.ZONE: Zone
-               }       
+                       RESOURCE.ZONE: Zone,
                        
+                       RESOURCE.OBJECTOPERATIONDEPENDENTPRICESPECIFICATION: ObjectOperationDependentPriceSpecification,
+                       RESOURCE.PRICESPECIFICATION: PriceSpecification,
+                       RESOURCE.PROPERTYDEPENDENTPRICESPECIFICATION: PropertyDependentPriceSpecification,
+                       RESOURCE.TRAVELDISTANCEDEPENDENTPRICESPECIFICATION: TravelDistanceDependentPriceSpecification,
+                       RESOURCE.TRAVELDISTANCEDURATIONDEPENDENTPRICESPECIFICATION: TravelDistanceDurationDependentPriceSpecification,
+                       RESOURCE.TYPEANDQUANTITYNODE: TypeAndQuantityNode,
+                       RESOURCE.ZONEDEPENDENTPRICESPECIFICATION: ZoneDependentPriceSpecification,
+                       RESOURCE.ZONETRAVELDEPENDENTPRICESPECIFICATION: ZoneTravelDependentPriceSpecification,
+                       RESOURCE.ZONETRAVELDURATIONDEPENDENTPRICESPECIFICATION: ZoneTravelDurationDependentPriceSpecification
+               
+               }
+
        def getClass(self, typelist, default = None, custom_classes = {}):
                for t in typelist:
                        if self.class_map.has_key(t):
index 736695a4fa6ce89de03ff3463bedd502599b4e77..bb3875820695beef639a1042804c99431c2ee646 100644 (file)
@@ -31,8 +31,8 @@ class HttpMessage(object):
         self.add(self.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE, mainPartString, mainPartContentType)
     
     def add(self, partId, partString, partContentType='text/turtle'):
-        [mainType, subType]=partContentType.split('/')
-        if mainType.lower()=='text':
+        [mainType, subType] = partContentType.split('/')
+        if mainType.lower() == 'text':
             part = MIMEText(partString, subType)
         elif mainType.lower() == 'application':
             part = MIMEApplication(partString, subType, email.encoders.encode_7or8bit)
@@ -103,11 +103,11 @@ class HttpMessage(object):
         '''  
         return self.getBody()
     
-    def asStringCRLF(self):  
+    def asStringCRLF(self):
         '''
         similar to asString(), but replace '\n' with correct CRLF, to make it in align with RFC, ready for
         sending out through HTTP.
-        '''             
+        '''
         pars = self.multipart.as_string().split('\n\n', 1)
         wholeHeader = pars[0]
         result = wholeHeader.replace('\n', '\r\n') + '\r\n\r\n'
@@ -115,22 +115,22 @@ class HttpMessage(object):
         bodysections = wholeBody.split('\n--'+self.multipart.get_boundary())
 #         print '        bodysections:'
 #         print bodysections
-        for bsec in bodysections:   
+        for bsec in bodysections:
             if bsec.startswith('--'+self.multipart.get_boundary()):
                 # the first part
                 prefix_len = 2 + len(self.multipart.get_boundary())
                 first_bsec = bsec[prefix_len:]
-                first_embed_pars = first_bsec.split('\n\n', 1)                                            
+                first_embed_pars = first_bsec.split('\n\n', 1)
                 first_embedheader = first_embed_pars[0]
                 first_embedbody = first_embed_pars[1]
                 result = result + '--' + self.multipart.get_boundary() + first_embedheader.replace('\n', '\r\n') + '\r\n\r\n' + first_embedbody
             elif bsec != '--\n':
-                embed_pars = bsec.split('\n\n', 1)                               
+                embed_pars = bsec.split('\n\n', 1)
                 embedheader = embed_pars[0]
                 embedbody = embed_pars[1]
                 result = result + '\r\n--'+self.multipart.get_boundary() + embedheader.replace('\n', '\r\n') + '\r\n\r\n' + embedbody          
-        result = result +  '\r\n--'+self.multipart.get_boundary() + '--\r\n'      
-        return result      
+        result = result +  '\r\n--'+self.multipart.get_boundary() + '--\r\n'
+        return result
         
 
 class HttpMessageSingle(object):
@@ -182,7 +182,7 @@ class HttpMessageSingle(object):
         wholeHeader = pars[0]
         wholeBody = pars[1]
         result = wholeHeader.replace('\n', '\r\n') + '\r\n\r\n' + wholeBody
-        return result        
+        return result
     
     def getContentType(self):
         '''
@@ -238,7 +238,7 @@ def main():
     msg.addMainpart('Cassini grand finale.', 'text/turtle')
     msg.add('part1', 'Saturn and Titan', 'text/turtle')
     
-    print msg.asString()    
+    print msg.asString()
     print '---- end ---'  
     print msg.asString().split('XXXXX') 
     
index 0a4f5107556c7b176ca6a76947a9dcf7c84f4db3..949d23134241b02139fb65470e27881b02a4a9f2 100644 (file)
@@ -23,5 +23,6 @@ class SERIALIZATION(object):
                elif serialization.lower() == cls.N_TRIPLE:
                        return 'application/n-triples'
                elif serialization.lower() == cls.JSON_LD:
-                       return 'application/ld+json'    
+                       return 'application/ld+json'
+               return serialization
        
\ No newline at end of file
index 593b0c56255c2aea699e1c45e6a6f9230b5c00d9..17e51c26e507e96b7a2bf71133d065622a889753 100644 (file)
@@ -348,6 +348,14 @@ class Tools(object):
                types = cls.getResourceTypes(resource)
                return ClassMapper().getClass(types, default = default, custom_classes = custom_classes)
 
+       @classmethod
+       def getResourceClassFromBNode(cls, model, node, default = None, custom_classes = None):
+               types = []
+               for statement in model.listStatements(subject = node, predicate = URIRef(PROPERTY.RDF_TYPE), object = None):
+                       types.append(statement.getResource().toString())
+               
+               return ClassMapper().getClass(types, default = default, custom_classes = custom_classes)
+       
        @classmethod
        def createIdentifierUri(cls, myDomain, systemIdentifier, *objectIdentifiers):
                '''
@@ -442,7 +450,6 @@ class Tools(object):
                '''
                '''
                return cls.serializeMessage(message, serialization, noHeader, printDebug)
-               
        
        @classmethod
        def serializeRequest(cls, message, serialization = SERIALIZATION.TURTLE, noHeader=True, printDebug=False):
index a6fd8298f9ad78ab3ffa275ff07fbb8960467fa0..3eae555a5993590a838b5f2bf6cd58153f598025 100644 (file)
@@ -169,7 +169,7 @@ class Activity(Evaluation):
                        return entity
 
        def firstEntity(self):
-               return getFirstEntity()
+               return self.getFirstEntity()
        
        def getFirstEntity(self):
                try:
@@ -178,7 +178,7 @@ class Activity(Evaluation):
                        return None
 
        def firstInput(self):
-               return getFirstInput()
+               return self.getFirstInput()
        
        def getFirstInput(self):
                try:
@@ -187,7 +187,7 @@ class Activity(Evaluation):
                        return None
    
        def firstOutput(self):
-               return getFirstOutput()
+               return self.getFirstOutput()
 
        def getFirstOutput(self):
                try:
index 7bb0710fecf78aa64cd31364ad4f6302c14dd03e..348f5360a0f334caaaee42ea08941a54a2f65125 100644 (file)
@@ -10,7 +10,7 @@ class DependentPriceSpecification(PriceSpecification):
        def __init__(self, uri = None, value = None, unit = None, validFrom = None, validThrough = None):
                super(DependentPriceSpecification, self).__init__(uri = uri, value = value, unit = unit, validFrom = validFrom, validThrough = validThrough)
                self.init_propertylist(PROPERTY.LIST, 'conditions', LinkedList, self.hasCondition, self.getConditionsContainer)
-       
+               
        def hasCondition(self):
                return self.conditions is not None and not self.conditions.isEmpty()
                
index ad58e37d595379b336aa0f7abb44277934d9e0a0..7db4f98f054fcfad12e6491e45f99986ff500c99 100644 (file)
@@ -30,9 +30,9 @@ from SmartAPI.rdf.ItemizedList import ItemizedList
 
 
 class Obj(object):
-               
+       
        referenceUriPrefix = "http://smart-api.io/reference/1.0/smart#"
-                       
+       
        def __init__(self, seasIdentifierUri = None):
                self.seasIdentifierUri = seasIdentifierUri
                self.sameAs = None
@@ -202,7 +202,11 @@ class Obj(object):
                                        rdfList = None
                                if rdfList:  # this is an rdf list with certain format
                                        v = entry[1]()
-                                       rdfList.add_items(v) # getter
+                                       # v may now contain either an instance of a list or a list of entries. Act accordingly
+                                       if isinstance(v, List):
+                                               rdfList.add_items(v.get_items()) # getter
+                                       else:
+                                               rdfList.add_items(v)
                                        try:
                                                # wrapping this part in a try allows for the scenario where v is just a Python list for some reason
                                                rdfList.setBaseObject(v.getBaseObject())
@@ -401,13 +405,13 @@ class Obj(object):
                '''
                if isinstance(element, Resource) and Tools.parsedObjs.has_key(element.toString()):
                        return Tools.parsedObjs.get(element.toString())
-                       
+               
                elif isinstance(element, Resource) and not Tools.parsedObjs.has_key(element.toString()):
                        if not element.isAnon():
                                obj = cls(element.toString())
                        else:
                                obj = cls()
-                               
+                       
                        Tools.parsedObjs[element.toString()] = obj
                        
                        for i in element.findProperties():
index bee3780398d8c08ce260b52ca73efe11fe6bac33..fdeaae32e06f15194929e206218debf342cde00e 100644 (file)
@@ -2,7 +2,7 @@ from SmartAPI.model.PropertyDependentPriceSpecification import PropertyDependent
 from SmartAPI.common.RESOURCE import RESOURCE
 
 
-class ObjectOperationDependentPriceSpecification(DependentPriceSpecification):
+class ObjectOperationDependentPriceSpecification(PropertyDependentPriceSpecification):
 
        def __init__(self, uri = None, value = None, unit = None, validFrom = None, validThrough = None):
                super(ObjectOperationDependentPriceSpecification, self).__init__(uri = uri, value = value, unit = unit, validFrom = validFrom, validThrough = validThrough)
index c1497d083249b5b9b4ff43993ecddfcaaee080e5..efade8295c7fbdd8dd84aee653f7bbf1b67ff5ec 100644 (file)
@@ -89,10 +89,15 @@ class Offering(GrObj):
     
     def _parseStatement(self, statement, custom_classes = None):
         from TypeAndQuantityNode import TypeAndQuantityNode
-        
+
         self.parse_property(statement, PROPERTY.HASBUSINESSFUNCTION, self.setBusinessFunction, Obj, custom_classes = custom_classes)
-        self.parse_property(statement, PROPERTY.HASPRICESPECIFICATION, self.addPriceSpecification, PriceSpecification, custom_classes = custom_classes)
         self.parse_property(statement, PROPERTY.INCLUDES, self.addIncludes, SomeItems, custom_classes = custom_classes)
         self.parse_property(statement, PROPERTY.INCLUDESOBJECT, self.addTypeAndQuantityNode, TypeAndQuantityNode, custom_classes = custom_classes)
+        
+        if str(statement.getPredicate()) == PROPERTY.HASPRICESPECIFICATION:
+            # Price spec may be subclassed, find the correct subclass
+            klass = Tools.getResourceClassFromBNode(statement.getModel(), statement.getObject())
+            self.parse_property(statement, PROPERTY.HASPRICESPECIFICATION, self.addPriceSpecification, klass, custom_classes = custom_classes)
+        
         super(Offering, self)._parseStatement(statement, custom_classes = custom_classes)
     
\ No newline at end of file
index 532170b1909b2ac2b9f5b23e29b7562833578a4d..b9a374dadf38b123d3c7e3eadeb632749aca8e48 100644 (file)
@@ -12,7 +12,11 @@ class Person(PhysicalEntity):
                PhysicalEntity.__init__(self, uri)
                self.setType(RESOURCE.PERSON)
                self.init_property(PROPERTY.USERNAME, 'username', self.hasUsername, self.getUsername)
-
+               self.init_property(PROPERTY.PASSWORD, 'password', self.hasPassword, self.getPassword)
+               self.init_property(PROPERTY.GIVEN_NAME, 'firstname', self.hasFirstname, self.getFirstname)
+               self.init_property(PROPERTY.FAMILY_NAME, 'lastname', self.hasLastname, self.getLastname)
+               self.init_property(PROPERTY.PASSWORDHASHALGORITHM, 'passwordHashAlgorithm', self.hasPasswordHashAlgorithm, self.getPasswordHashAlgorithm)
+               
        def hasUsername(self):
                return self.username is not None
        
@@ -23,8 +27,58 @@ class Person(PhysicalEntity):
                if not isinstance(u, Variant):
                        u = Variant(u)
                self.username = u
+       
+       def hasPassword(self):
+               return self.password is not None
+       
+       def getPassword(self):
+               return self.password
+       
+       def setPassword(self, u):
+               if not isinstance(u, Variant):
+                       u = Variant(u)
+               self.password = u
+               
+       def hasFirstname(self):
+               return self.firstname is not None
+       
+       def getFirstname(self):
+               return self.firstname
+       
+       def setFirstname(self, u):
+               if not isinstance(u, Variant):
+                       u = Variant(u)
+               self.firstname = u
+               
+       def hasLastname(self):
+               return self.lastname is not None
+       
+       def getLastname(self):
+               return self.lastname
+       
+       def setLastname(self, u):
+               if not isinstance(u, Variant):
+                       u = Variant(u)
+               self.lastname = u
+               
+       def hasPasswordHashAlgorithm(self):
+               return self.passwordHashAlgorithm is not None
+       
+       def getPasswordHashAlgorithm(self):
+               return self.passwordHashAlgorithm
+       
+       def setPasswordHashAlgorithm(self, u):
+               if not isinstance(u, Variant):
+                       u = Variant(u)
+               self.passwordHashAlgorithm = u
                
        def _parseStatement(self, statement, custom_classes = None):
                self.parse_property(statement, PROPERTY.USERNAME, self.setUsername, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.PASSWORD, self.setPassword, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.GIVEN_NAME, self.setFirstname, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.FAMILY_NAME, self.setLastname, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.PASSWORDHASHALGORITHM, self.setPasswordHashAlgorithm, Variant, custom_classes = custom_classes)
+               
                super(Person, self)._parseStatement(statement, custom_classes = custom_classes)
-               
\ No newline at end of file
+
+       ()
\ No newline at end of file
index 2b9c7847424cd5377ddce685afdb77b28abaaa64..ef3a712700e48d235d4b1296498cd6084f560603 100644 (file)
@@ -153,7 +153,7 @@ class PriceSpecification(GrObj):
             vat = Variant(vat)
         self.valueAddedTaxIncluded = vat
     
-    def hasVatPercentage(self):  
+    def hasVatPercentage(self):
         return self.vatPercentage is not None
     
     def getVatPercentage(self): 
index 11a72a77fdf8b607ae553e730654c704cc66edba..e2421c05cdecf02c21df1978a9fc70028e8d176b 100644 (file)
@@ -1,4 +1,5 @@
 from SmartAPI.model.DependentPriceSpecification import DependentPriceSpecification
+from SmartAPI.model.Obj import Obj
 from SmartAPI.common.RESOURCE import RESOURCE
 from SmartAPI.common.PROPERTY import PROPERTY
 from SmartAPI.rdf.Variant import Variant
@@ -23,7 +24,9 @@ class PropertyDependentPriceSpecification(DependentPriceSpecification):
 
        def setReferenceObject(self, r):
                if r is not None:
-                       if isinstance(r, str): 
+                       if isinstance(r, Obj): 
+                               r = Variant(URIRef(r.getIdentifierUri()))
+                       elif isinstance(r, str): 
                                r = Variant(URIRef(r))
                        elif isinstance(r, URIRef):
                                r = Variant(r)
@@ -37,14 +40,16 @@ class PropertyDependentPriceSpecification(DependentPriceSpecification):
 
        def setProperty(self, p):
                if p is not None:
-                       if isinstance(p, str): 
+                       if isinstance(p, Obj): 
+                               p = Variant(URIRef(p.getIdentifierUri()))
+                       elif isinstance(p, str): 
                                p = Variant(URIRef(p))
                        elif isinstance(r, URIRef):
                                p = Variant(p)
-                       self.referenceObject = p
+                       self.property = p
                
        def _parseStatement(self, statement, custom_classes = None):
-               self.parse_property(statement, PROPERTY.REFERENCEOBJECT, self.setCoordinates, Variant, custom_classes = custom_classes)
-               self.parse_property(statement, PROPERTY.SMARTAPI_PROPERTY, self.setCoordinates, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.REFERENCEOBJECT, self.setReferenceObject, Variant, custom_classes = custom_classes)
+               self.parse_property(statement, PROPERTY.SMARTAPI_PROPERTY, self.setProperty, Variant, custom_classes = custom_classes)
                super(PropertyDependentPriceSpecification, self)._parseStatement(statement, custom_classes = custom_classes)
                
index dfa3594b104bb2642c1d34d3e425a7c8adc94298..91ee2594a711ce2d7f15460fc6d93c446eb88645 100644 (file)
@@ -34,6 +34,7 @@ class ValueObject(Obj):
                self.setUnit(unit)
                self.setSecondaryQuantity(secondaryQuantity)
                self.setSecondaryUnit(secondaryUnit)
+               
                if value is not None:
                        if not isinstance(value, Variant):
                                value = Variant(value)
@@ -60,13 +61,7 @@ class ValueObject(Obj):
                @type quantity: string. It can look like "asema:SomeCustomType" or an URI
                '''
                if u is not None:
-                       if isinstance(u, Obj):
-                               self.quantity = URIRef(u.getIdentifierUri())
-                               return
-                       if not isinstance(u, Variant):
-                               self.quantity = Variant(URIRef(NS.toAbsoluteUri(u)))
-                       else:
-                               self.quantity = URIRef(u)
+                       self.quantity = self.convert(u)
 
        def hasUnit(self):
                return self.unit is not None
@@ -79,13 +74,7 @@ class ValueObject(Obj):
                @type unit: string. It can look like "asema:SomeCustomType" or an URI
                '''
                if u is not None:
-                       if isinstance(u, Obj):
-                               self.unit = URIRef(u.getIdentifierUri())
-                               return
-                       if not isinstance(u, Variant):
-                               self.unit = Variant(URIRef(NS.toAbsoluteUri(u)))
-                       else:
-                               self.unit = URIRef(u)
+                       self.unit = self.convert(u)
                        
        def hasSecondaryQuantity(self):
                return self.secondaryQuantity is not None
@@ -98,10 +87,7 @@ class ValueObject(Obj):
                @type secondaryQuantity: string. It can look like "asema:SomeCustomType" or an URI
                '''
                if u is not None:
-                       if not isinstance(u, Variant):
-                               self.secondaryQuantity = Variant(URIRef(NS.toAbsoluteUri(u)))
-                       else:
-                               self.secondaryQuantity = URIRef(u)
+                       self.secondaryQuantity = self.convert(u)
                        
        def hasSecondaryUnit(self):
                return self.secondaryUnit is not None
@@ -114,10 +100,7 @@ class ValueObject(Obj):
                @type unit: string. It can look like "asema:SomeCustomType" or an URI
                '''
                if u is not None:
-                       if not isinstance(u, Variant):
-                               self.secondaryUnit = Variant(URIRef(NS.toAbsoluteUri(u)))
-                       else:
-                               self.secondaryUnit = URIRef(u)
+                       self.secondaryUnit = self.convert(u)
 
        def hasDataType(self):
                return self.dataType is not None
@@ -130,7 +113,7 @@ class ValueObject(Obj):
                @type dataType: string. It can look like "asema:SomeCustomType" or an URI
                '''
                if u is not None:
-                       self.dataType = Variant(NS.toAbsoluteUri(u))
+                       self.dataType = self.convert(u)
 
        def hasValue(self):
                return self.value is not None
@@ -174,7 +157,7 @@ class ValueObject(Obj):
        def hasTemporalContext(self):
                return self.temporalContext is not None
        
-       def setTemporalContext(self, temporalContext = None, start = None, end = None):         
+       def setTemporalContext(self, temporalContext = None, start = None, end = None):
                from SmartAPI.model.TemporalContext import TemporalContext
                if temporalContext is not None:
                        self.temporalContext = temporalContext
@@ -192,7 +175,18 @@ class ValueObject(Obj):
                if not isinstance(instant, Variant):
                        instant = Variant(instant)
                self.instant = instant
-       
+
+       def convert(self, v):
+               if isinstance(v, Obj):
+                       return Variant(URIRef(u.getIdentifierUri()))
+               elif isinstance(v, Variant):
+                       return v
+               else:
+                       try:
+                               return Variant(URIRef(v))
+                       except:
+                               return None
+
        def _parseStatement(self, statement, custom_classes = None):
                from SmartAPI.rdf.Resource import Resource
                from SmartAPI.model.TemporalContext import TemporalContext
index 47fda190a58a683c19a8726211bc43b11c7eb6e6..696ea12ae1aba6cb853695fa9e301253e0440411 100644 (file)
@@ -35,7 +35,7 @@ class List(object):
     
     def extend(self, items):
         self.elements.extend(items)
-         
+    
     def size(self):
         return len(self.elements)
     
index fd861e8e3b35b26e9496ceb0276f3232499e823d..30219e78bbc5636ac4b9c65ca4e684e66e101d80 100644 (file)
@@ -83,11 +83,10 @@ class Model(object):
     # the the form of s-p-o of a statement that embeds them
     def _add_element(self, object, predicate, subject = None):
         from SmartAPI.rdf.Variant import Variant
-        from SmartAPI.common.Tools import Tools
         
         if isinstance(object, Resource) and (object not in self.serializedResources):
-            self.serializedResources.append(object)  
-                    
+            self.serializedResources.append(object)
+            
             n = object.getNode()
             
             for p in object.listProperties():
index 77cd8871281a6fe410f855ad998d763e4c32a302..5a6cf1a083a9b0f80a29e55d70bfd408e2f748aa 100644 (file)
@@ -8,7 +8,7 @@ class NudeList(List):
     def add_items(self, items):
         
         if isinstance(items, list):
-            for i in items:               
+            for i in items:
                 try:  # objects support toNude, simple variables don't
                     self.elements.append(i.toNude())
                 except:
index 235cd74849392d15322a493768c3d213cee04330..8309b5dd917dad2505ba97e8a7edb0b1c90e7525 100644 (file)
@@ -48,6 +48,9 @@ class Resource(object):
     def isLiteral(self):
         return isinstance(self.node, Literal)
     
+    def isUri(self):
+        return isinstance(self.node, URIRef)
+    
     def addProperty(self, propertyType, property):
         if self.properties.has_key(propertyType.getUri()):
             self.properties[propertyType.getUri()].append(property)
index 8dab4efdce0e79f8b42eadbba6cdede18c93feb6..0f5b7e52704c07904ec6995243db2b00c2cf6429 100644 (file)
@@ -29,20 +29,14 @@ class Variant(object):
                else:
                        return self.asTerm()
 
-       def asTerm(self):
-               if self.isUri():
-                       return self.variant
-               elif self.isNull():
-                       return None
-               else:
-                       return Literal(self.variant)
-
        @classmethod
        def parse(cls, element, custom_classes = None):
                # get predicate and object
                if isinstance(element, Resource):
                        if element.isLiteral():
                                return cls.parse(element.getNode())
+                       elif element.isUri():
+                               return cls.parse(element.getNode())
                        else:
                                klass = Tools().getResourceClass(element, default = Obj, custom_classes = custom_classes)
                                if klass is not None:
@@ -57,6 +51,8 @@ class Variant(object):
                                        if (propsNum == 0):
                                                v = Variant(URIRef(element.getNode().toPython()))
                                return v
+               elif isinstance(element, URIRef):
+                       return Variant(element)
                elif isinstance(element, Literal):
                        return Variant(element.toPython())
                else:
@@ -182,7 +178,15 @@ class Variant(object):
        def getAsString(self):
                return str(self.variant)
 
-       # Most method below have no significance in Python as the language
+       def asTerm(self):
+               if self.isUri():
+                       return self.variant
+               elif self.isNull():
+                       return None
+               else:
+                       return Literal(self.variant)
+               
+       # Most methods below have no significance in Python as the language
        # is not strongly typed. They are however offered as convenience
        # to make implementions between different languages as similar
        # as possible
@@ -222,6 +226,9 @@ class Variant(object):
 
        def asBoolean(self):
                return self.variant
+       
+       def asDateTime(self):
+               return self.variant
                
        def asDate(self):
                try:
index 1fd733d5af4869ea3b1ddc554281229362b03c21..c6f0be0da37c14dd7cea599a7a835bc8767d9219 100755 (executable)
@@ -55,6 +55,7 @@ from SmartAPI.model.Obj import Obj
 from SmartAPI.factory.Factory import Factory
 from SmartAPI.factory.RequestFactory import RequestFactory
 from SmartAPI.factory.ResponseFactory import ResponseFactory
+from SmartAPI.factory.NotificationFactory import NotificationFactory
 from SmartAPI.factory.CommandFactory import CommandFactory
 
 from SmartAPI.common.Tools import Tools
@@ -157,21 +158,14 @@ def serializeRequestTest():
 
     tsRequest = RequestFactory().create(myIdentity);
 
-    system = SystemOfInterest()
-    system.setSameAs(sourceIdentity)
-    system.setSessionKey("Measurement")
-    tsRequest.setSystemOfInterest(system)
-
     tc = TemporalContext()
     tc.setStart(seriesStart)
     tc.setEnd(seriesEnd)
-    tc.setDuration(2,2,2,2,2,2)
+    tc.setDuration(2, 2, 2, 2, 2, 2)
    
     a = Activity()
     i = Input()
     i.setTemporalContext(tc)
-    a.setInput(i)
-    tsRequest.setActivity(a)
     
     if len(request_vars) == 1:
         tsRequest.setQuantity(NS.SMARTAPI + request_vars[0][0])
@@ -183,8 +177,12 @@ def serializeRequestTest():
             v.setQuantity(NS.SMARTAPI + request_vars[idx][0])
             v.setUnit(NS.SMARTAPI + request_vars[idx][1])
             v.setValue(Variant(sampleValue))
-            tsRequest.addValueObject(v)
+            i.add(PROPERTY.VALUEOBJECT, v)
 
+    a.setInput(i)
+    a.setMethod(RESOURCE.READ)
+    tsRequest.setActivity(a)
+    
     payload = Tools().toString(tsRequest, SERIALIZATION.TURTLE)
     print "Serialze to String ..."
     print payload
@@ -194,9 +192,26 @@ def serializeRequestTest():
     print "***End ", newPayload.firstActivity().firstInput().getTemporalContext().getEnd()
     print Tools().toString(newPayload, SERIALIZATION.TURTLE)
     
-    
     return tsRequest
 
+def serializeNotificationTest():
+    myIdentity = "http://tests.smart-api.io/python/notificationsender/Cabcd";
+    n = NotificationFactory.create(myIdentity)
+    a = Activity()
+    a.setMethod(RESOURCE.NOTIFY)
+    e = Entity(myIdentity + "/Cdevice")
+    
+    power = ValueObject(myIdentity + "service/Ppower")
+    power.setQuantity(RESOURCE.POWER)
+    power.setUnit(RESOURCE.AMPERE)
+    power.setValue(501.0)
+    
+    e.addValueObject(power);
+    a.addEntity(e);
+    n.setActivity(a);
+    n.turtlePrint()
+    
+    return True
 
 def requestResponseTest3():
     '''
@@ -2147,7 +2162,8 @@ def conditionalPriceSpecificationTest():
     
 def main():
     #objectCopyTest()
-    #serializeRequestTest()
+    serializeRequestTest()
+    serializeNotificationTest()
     #longSerializeParseTest()
     #propertySerializeParseTest()
     #inputOutputTest()
@@ -2184,7 +2200,7 @@ def main():
     #conceptValidationTest()
     #velocityTest()
     #unitPriceSpecificationTest()
-    conditionalPriceSpecificationTest()
+    #conditionalPriceSpecificationTest()
     
 if __name__=='__main__':
     main()
diff --git a/Examples/C++/EventAgentSample/server.py b/Examples/C++/EventAgentSample/server.py
new file mode 100755 (executable)
index 0000000..a80b13c
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+
+
+import paho.mqtt.publish as publish
+
+from SmartAPI.factory.NotificationFactory import NotificationFactory
+from SmartAPI.factory.ResponseFactory import ResponseFactory
+from SmartAPI.common.Tools import Tools
+from SmartAPI.common.SERIALIZATION import SERIALIZATION
+from SmartAPI.common.NS import NS
+from SmartAPI.model.Status import Status
+from SmartAPI.model.Activity import Activity
+from SmartAPI.model.Entity import Entity
+
+mqtt_server = "broker.asema.com"
+
+calculation_id = 1
+
+delay_min_time_seconds = 10
+delay_max_time_seconds = 20
+
+import web
+
+class SampleServer():
+    def __init__(self):
+        pass
+
+    # Creates a dummy result in RDF format and sends it over MQTT
+    # to listeners
+    def notify_ready(self, id):
+        notification = NotificationFactory().create("http://www.calculator.com/example")
+        notification.setMessageId(id)
+        status = Status()
+        status.setPercentage(100)
+        notification.setStatus(status)
+        activity = Activity()
+        entity = Entity("http://www.calculator.com/calculatedentity")
+        activity.addEntity(entity)
+        notification.addActivity(activity)
+    
+        payload, content_type = Tools().serializeNotification(notification)
+        publish.single("sample/result/youcom/calculations", payload, hostname=mqtt_server)
+    
+    def POST(self):
+        # parse request message into Smart API Request Object     
+        req_str = rawRequest(web.ctx.env) 
+        content_type = web.ctx.env.get('Content-Type')
+        request = Tools().parseRequest(req_str, content_type)
+
+        response = ResponseFactory().create("http://talk.asema.com/demo/smart/demoresponder")
+        activity = Activity()
+        response.addActivity(activity)
+        status = Status()
+        status.setPercentage(100)
+        response.setStatus(status)
+        
+        payload, content_type = Tools().serializeResponse(response)
+        
+        self.notify_ready(1)
+        return payload
+
+
+def rawRequest(env):
+    req = env['wsgi.input'].read(int(env['CONTENT_LENGTH']))
+    return req    
+
+if __name__ == "__main__":
+    # the url for this tiny Smart API server will be: 
+    # http://0.0.0.0:8080/demo/smart/v1.0e1.0/access
+    app = web.application(('/smart/v1.0e1.0/access', 'SampleServer'), globals())
+    web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", 3333))
+    
\ No newline at end of file
diff --git a/Examples/C++/NotificationSample/NotificationSample.h b/Examples/C++/NotificationSample/NotificationSample.h
new file mode 100644 (file)
index 0000000..380fa5e
--- /dev/null
@@ -0,0 +1,58 @@
+#include "factory/NotificationFactory.h"
+#include "agents/EventAgent.h"
+#include "model/Entity.h"
+
+#include <QCoreApplication>
+
+class NotificationSample : public QObject
+{
+       Q_OBJECT
+
+public:
+       NotificationSample() {
+               mAgent = new EventAgent();
+               mAgent->setAutosubscribe(false);
+               connect(mAgent, SIGNAL(connected()), this, SLOT(onAgentConnected()));
+               connect(mAgent, SIGNAL(published()), this, SLOT(onAgentPublished()));
+       }
+
+       ~NotificationSample() { mAgent->disconnect(); delete mAgent; }
+       void runSample() { mAgent->connect(); }
+
+private slots:
+       void onAgentConnected()
+       {
+               QString sensorIdentifier = "http://acme.com/sensor/Cabc123";
+               QString myIdentifier = "http://acme.com/service/Cnotifier";
+
+               Notification* n = NotificationFactory::create(myIdentifier);
+               Activity* a = new Activity();
+               Entity* e = new Entity(sensorIdentifier);
+               ValueObject* vo = new ValueObject(RESOURCE__POWER, RESOURCE__WATT, 1012);
+
+               e->addValueObject(vo);
+               a->addEntity(e);
+               n->setActivity(a);
+               mAgent->publish("a/b/youcom/calculations", n);
+       }
+
+       void onAgentPublished()
+       {
+               qDebug() << "notification published";
+       }
+
+private:
+       EventAgent* mAgent;
+};
+
+
+int main(int argc, char* argv[])
+{
+       QCoreApplication app(argc, argv);
+       NotificationSample* sample = new NotificationSample();
+       sample->runSample();
+       app.exec();
+
+       delete sample;
+       return 0;
+}
diff --git a/Examples/C++/NotificationSample/NotificationSample.pro b/Examples/C++/NotificationSample/NotificationSample.pro
new file mode 100644 (file)
index 0000000..2a310c7
--- /dev/null
@@ -0,0 +1,21 @@
+QT += network
+
+TARGET = NotificationSample
+
+INCLUDEPATH += "/usr/include/raptor2/"
+INCLUDEPATH += "/usr/include/rasqal/"
+INCLUDEPATH += "/usr/include/redland/"
+INCLUDEPATH += "/usr/include/smartapi/"
+
+LIBS += -lraptor2
+LIBS += -lrasqal
+LIBS += -lrdf
+LIBS += -lsmartapi
+
+include("../../../Common/C++/SmartAPI/smartapipahomqtt.pro")
+
+HEADERS += NotificationSample.h
+
+DESTDIR = bin
+OBJECTS_DIR = objects
+MOC_DIR = MOCs
\ No newline at end of file
diff --git a/Examples/C++/NotificationSample/test_listener.py b/Examples/C++/NotificationSample/test_listener.py
new file mode 100755 (executable)
index 0000000..0674ea4
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+""" 
+"""
+import paho.mqtt.client as mqtt
+
+default_mqtt_broker_address = "broker.asema.com"
+default_mqtt_broker_port    = 1883
+mqtt_topic = "+/+/+/+"
+
+# Callback for when the MQTT client connects.
+def on_connect(client, userdata, flags, rc):
+    client.subscribe(mqtt_topic)
+
+# Callback on broker socket disconnect
+def on_disconnect(client, userdata, flags, rc):
+    if rc != 0:
+        print("Unexpected disconnection.")
+    
+# Callback for when a notification is received.
+def on_publish(client, userdata, msg):
+    print "Incoming:", msg.payload
+
+client = mqtt.Client()
+client.on_connect = on_connect
+client.on_disconnect = on_disconnect
+client.on_message = on_publish
+client.connect(default_mqtt_broker_address, default_mqtt_broker_port, 60)
+
+# Wait for notifications
+client.loop_forever()
\ No newline at end of file
index 1306536e37543c3cee4c8e940b9e6cffc9330c54..9b1f7b1afac32406f74298ca768ff59b801a0d63 100755 (executable)
@@ -2,7 +2,12 @@
 
 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
 from io import BytesIO
+from threading import Thread
+from httpclient import HttpClient
+import time
+import datetime
 import traceback
+import random
 
 from SmartAPI.agents.RegistrationAgent import RegistrationAgent
 from SmartAPI.agents.SearchAgent import SearchAgent
@@ -11,6 +16,7 @@ from SmartAPI.common.Tools import Tools
 from SmartAPI.common.RESOURCE import RESOURCE
 
 from SmartAPI.factory.ResponseFactory import ResponseFactory
+from SmartAPI.factory.NotificationFactory import NotificationFactory
 
 from SmartAPI.model.Activity import Activity
 from SmartAPI.model.Authorization import Authorization
@@ -25,20 +31,61 @@ from SmartAPI.model.TimeSeries import TimeSeries
 from SmartAPI.model.ValueObject import ValueObject
 
 
+myOrgIdentity = "http://www.asema.com/company/"
 myIdentity = "http://adapt.asema.com/demos/python/datasource/"
+myDeviceIdentity = myIdentity + "devices/Cdemodevice"
 adaptServiceIdentity = "http://adapt.asema.com"
-#registrationServerUri = "http://find.smart-api.io/smart/v1.0e1.0/access"
-#registrationServerKeyUri = "http://find.smart-api.io/smart/v1.0e1.0/key"
-registrationServerUri = "http://192.168.2.96:8080/smartapifind-core/smart/v1.0e1.0/access"
-registrationServerKeyUri = "http://192.168.2.96:8080/smartapifind-core/smart/v1.0e1.0/key"
+registrationServerUri = "http://find.smart-api.io/smart/v1.0e1.0/access"
+registrationServerKeyUri = "http://find.smart-api.io/smart/v1.0e1.0/key"
+#registrationServerUri = "http://192.168.2.96:8080/smartapifind-core/smart/v1.0e1.0/access"
+#registrationServerKeyUri = "http://192.168.2.96:8080/smartapifind-core/smart/v1.0e1.0/key"
 
 PORT = 8111
+delay_between_sends_in_seconds = 5
 
 
+class Notifier(Thread):
+       
+       def __init__(self, iface):
+               Thread.__init__(self)
+               self.daemon = True
+               self.iface = iface
+               self.running = True
+               
+               targetPath = iface.getScheme().asString() + "://" + iface.getHost().asString() + ":" + iface.getPort().asString() + iface.getPath().asString()
+               print "Start sending notifications to", targetPath
+               self.http_client = HttpClient(targetPath)
+               
+       def run(self):
+               while self.running:
+                       self.sendNotification()
+                       time.sleep(delay_between_sends_in_seconds)
+       
+       def stop(self):
+               self.running = False
+               
+       def sendNotification(self):
+               n = NotificationFactory.create(myIdentity)
+               a = Activity()
+               a.setMethod(RESOURCE.NOTIFY)
+               e = Entity(myDeviceIdentity)
+               
+               power = ValueObject(myIdentity + "service/Ppower")
+               power.setQuantity(RESOURCE.POWER)
+               power.setUnit(RESOURCE.AMPERE)
+               power.setValue(float(random.randint(100, 700)))
+               
+               e.addValueObject(power)
+               a.addEntity(e)
+               n.setActivity(a)
+               
+               payload, contentType = Tools.serializeNotification(n)
+               response = self.http_client.send_data(payload, contentType)
+               print response
+
 
 class SampleRegistration(object):
        
-
        def __init__(self):
                pass
 
@@ -50,7 +97,7 @@ class SampleRegistration(object):
 
                # registrate
                try:
-                       org = Organization()
+                       org = Organization(myOrgIdentity)
                        org.setName("Asema Electronics Ltd")
                        
                        sampleService = Service(myIdentity)
@@ -64,9 +111,9 @@ class SampleRegistration(object):
                        auth.addMethod(RESOURCE.HTTPSTANDARD);
        
                        iface = InterfaceAddress()
-                       iface.setHost("adapt.asema.com")
+                       iface.setHost("127.0.0.1")
                        iface.setPath("/test/")
-                       iface.setPort(80)
+                       iface.setPort(PORT)
                        iface.setScheme("http")
 
                        read = Activity()
@@ -75,13 +122,17 @@ class SampleRegistration(object):
                        read.setAuthorization(auth)
 
                        dataSource = Entity()
-                       dataSource.setIdentifierUri(myIdentity + "devices/Cdemodevice")
+                       dataSource.setIdentifierUri(myDeviceIdentity)
                        dataSource.setName("Demo Adapt datasource")
+                       dataSource.setDescription("This datasource profived random power readings as a demo for attaching a source into Asema Adapt")
                        dataSource.addCapability(read)
                        dataSource.setManagedBy(myIdentity)
                        dataSource.setServedBy(adaptServiceIdentity)
                        dataSource.setCoordinates(latitude=60.180824, longitude=24.832116)
-
+                       dataSource.addOwner(org);
+                       dataSource.addCreator(org);
+                       dataSource.setManagedBy(myOrgIdentity);
+                       
                        dataSource.addCapability(read)
                        
                        power = ValueObject(myIdentity + "service/Ppower")
@@ -97,6 +148,8 @@ class SampleRegistration(object):
                        priceSpec = PropertyDependentPriceSpecification()
                        priceSpec.setName("Sample pricing")
                        priceSpec.addType(RESOURCE.PRICETYPEDISCOUNT)
+                       priceSpec.setReferenceObject(myDeviceIdentity)
+                       priceSpec.setProperty(power)
                        
                        c1 = Condition()
                        c1.addGreater(0)
@@ -121,7 +174,7 @@ class SampleRegistration(object):
                        offering.addPriceSpecification(priceSpec)
                        dataSource.addOffering(offering)
                        dataSource.addValueObject(power)
-
+                       
                        agent.addEntity(sampleService)
                        agent.addEntity(dataSource);
 
@@ -183,30 +236,58 @@ class SampleDataService(BaseHTTPRequestHandler):
                        
                elif req_path.endswith("/access"):
                        request = Tools.parseRequest(body)
-                       print("request: " + request.toString())
-                               
-                       activity = request.getActivities()[0]  # type: Activity
+                       
+                       for a in request.getActivities():
+                               if a.method.asString() == RESOURCE.SUBSCRIBE:
+                                       print "Subscribing to notifications"
+                                       self.handleSubscription(a)
                                
-                       # Handle timeseries request
-                       if activity.hasTemporalContext():
-                               response_bytes.write(self.handleTemporalContext(activity, request))
-                       else:
-                               print("unhandled request received!")
+                               # Read values, either a timeseries or single value
+                               elif a.method.asString() == RESOURCE.READ:
+                                       # Handle timeseries request
+                                       if a.hasTemporalContext():
+                                               response_bytes.write(self.handleTimeseriesRequest(a, request))
+                                       elif a.hasEntity():
+                                               e = a.getFirstEntity()
+                                               response_bytes.write(self.handleSingleValueRequest(e, a, request))
+                                       else:
+                                               print("Unknown request received")
                
                else:
-                       print("unhandled request received!")
+                       print("Unknown path received")
                
                # Write output to client
                self.wfile.write(response_bytes.getvalue())
        
+       def handleSubscription(self, activity):
+               if activity.hasInterface():
+                       iface = activity.getInterfaces()[0]
+                       print "Will send data to", iface.getHost().asString()
+                       n = Notifier(iface)
+                       n.run()
+               
+               else:
+                       print "Error: cannot subscribe without inteface data"
        
-       def handleTemporalContext(self, activity, request):
-               print("Timeseries request: ")
-               print("start")
-               print(activity.temporalContext.start)
-               print("end")
-               print(activity.temporalContext.end)
+       def handleSingleValueRequest(self, entity, activity, request):
+               response = ResponseFactory.create(myIdentity, request)
+               response_activity = Activity(activity.getIdentifierUri())
                
+               e = Entity(entity.getIdentifierUri())
+               value_object = ValueObject()
+               value_object.setValue(random.randint(100, 400))
+               value_object.setQuantity(RESOURCE.POWER)
+               value_object.setUnit(RESOURCE.AMPERE)
+               e.addValueObject(value_object)
+               response_activity.addEntity(e)
+
+               return Tools.serializeResponse(response)[0]
+               
+       def handleTimeseriesRequest(self, activity, request):
+               start = activity.temporalContext.start.asDateTime()
+               end =  activity.temporalContext.end.asDateTime()
+               print "Timeseries request from ", start, "to", end
+
                # For the client to recognize from which activity this result is, the response
                # activity will carry the same identifier that was in the request
                response = ResponseFactory.create(myIdentity, request)
@@ -216,29 +297,20 @@ class SampleDataService(BaseHTTPRequestHandler):
                The way results are obtained is up to the server. This could
                for instance be a result from a database query,
                """
-               results = [
-                       {'time': '2019-06-10T15:18:54.842', 'power': 25},
-                       {'time': '2019-06-01T02:11:54.842', 'power': 21},
-                       {'time': '2019-05-25T11:11:54.842', 'power': 32}]
-               
                time_series = TimeSeries()
-               
-               # Add data to response timeseries
-               for r in results:
+               while start < end:
                        value_object = ValueObject()
-                       value_object.setInstant(r["time"])
-                       value_object.setValue(r["power"])
+                       value_object.setInstant(start)
+                       value_object.setValue(random.randint(100, 400))
                        
                        time_series.addListItem(value_object)
+                       start = start + datetime.timedelta(hours=1)
                        
-                       # Add timeseries to respose activity
-                       response_activity.addTimeSerie(time_series)
-                       
-                       response.addActivity(response_activity)
-                       
-                       print("Reply:")
-                       print(Tools.serializeResponse(response)[0])
-                       return Tools.serializeResponse(response)[0]
+               # Add timeseries to respose activity
+               response_activity.addTimeSerie(time_series)
+               response.addActivity(response_activity)
+               
+               return Tools.serializeResponse(response)[0]
 
 
 def main():
index a977849e7bfe6a988087cdc06b00e1ea4828ebde..3a1e97a91b48994b9944d6fe765dd09988250588 100755 (executable)
@@ -24,7 +24,7 @@ class SampleSearch(object):
        def do(self):
                agent = SearchAgent(myIdentity)
                agent.setServerAddress(registrationServerUri)
-               agent.setDebugMode(True)
+               agent.setDebugMode(False)
 
                e = Entity()
                e.setServedBy(adaptServiceIdentity)
@@ -39,6 +39,7 @@ class SampleSearch(object):
                else:
                        print "No results found."
 
+
 def main():
        s = SampleSearch()
        s.do()
diff --git a/Examples/Python/AdaptDataService/httpclient.py b/Examples/Python/AdaptDataService/httpclient.py
new file mode 100644 (file)
index 0000000..bbf432b
--- /dev/null
@@ -0,0 +1,32 @@
+import sys
+import datetime
+import urllib2
+import httplib
+from urlparse import urlparse
+from simplejson import dumps
+from threading import Lock
+
+class HttpClient(object):
+       def __init__(self, server):
+               self.server = server
+               self.u = urlparse(self.server)
+               self.ssl = (self.u.scheme == "https")
+               self.lock = Lock()
+               
+       def send_data(self, data, content_type):
+               self.lock.acquire()
+               try:
+                       headers = { "content-type" : content_type }
+                       if self.ssl:
+                               conn = httplib.HTTPSConnection(self.u.netloc, timeout = 10)
+                       else:
+                               conn = httplib.HTTPConnection(self.u.netloc, timeout = 10)
+                       conn.request("POST", self.u.path, data, headers)
+                       response = conn.getresponse()
+                       result = response.read()
+                       conn.close()
+                       return result
+               except:
+                       print "Error sending data", sys.exc_info()[1]
+               finally:
+                       self.lock.release()
index 1854784fd7d6ee21f3f9d84734e11443144583aa..b9e51037e591946e70a030417c84f98496345ea0 100755 (executable)
@@ -398,7 +398,7 @@ class SampleServerActivityProcessor4(object):
                                print "** Searching for persons with age between " + str(minimum_age) + " and " + str(maximum_age) + " **\n"
                                results = [
                                        {'name': 'Huey', 'age': 25, 'lines_of_code': 1100},
-                                       {'name': 'Dewey', 'age': 21, 'lines_of_code': 955}]                                     
+                                       {'name': 'Dewey', 'age': 21, 'lines_of_code': 955}]
                                for r in results:
                                        resp_entity = Programmer(personIdentityPrefix + r['name'])
                                        resp_entity.setName(r['name'])
diff --git a/Examples/Python/EventAgentSample/client.py b/Examples/Python/EventAgentSample/client.py
new file mode 100755 (executable)
index 0000000..c37ff8e
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+from SmartAPI.agents.EventAgent import EventAgent
+from SmartAPI.common.HttpClient import HttpClient
+from SmartAPI.common.Tools import Tools
+from SmartAPI.common.SERIALIZATION import SERIALIZATION
+from SmartAPI.factory.Factory import Factory
+from SmartAPI.model.Entity import Entity
+
+def on_results(notification):
+    e = notification.getActivities()[0].getEntities()[0]
+    print "GOT CALLBACK ENTITY", e.getIdentifierUri()
+    # do something with the entity data here
+    
+
+serverUri = "http://127.0.0.1:3333/smart/v1.0e1.0/access"
+
+agent = EventAgent(on_results, mqtt_topic = "+/+/youcom/calculations")
+agent.connect()
+httpClient = HttpClient()
+entity = Entity("http://you.com/devices/C1234")
+request = Factory.createReadRequest("http://me.com", entity)
+payload, content_type = Tools.serializeRequest(request)
+response_body, response_headers = httpClient.sendPost(serverUri, payload, content_type = content_type)
+print "DIRECT RESPONSE:\n", response_body
diff --git a/Examples/Python/EventAgentSample/server.py b/Examples/Python/EventAgentSample/server.py
new file mode 100755 (executable)
index 0000000..f8f8af8
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+
+import paho.mqtt.publish as publish
+
+from SmartAPI.factory.NotificationFactory import NotificationFactory
+from SmartAPI.factory.ResponseFactory import ResponseFactory
+from SmartAPI.common.Tools import Tools
+from SmartAPI.common.SERIALIZATION import SERIALIZATION
+from SmartAPI.common.NS import NS
+from SmartAPI.model.Status import Status
+from SmartAPI.model.Activity import Activity
+from SmartAPI.model.Entity import Entity
+
+use_jsonld = True
+
+mqtt_server = "broker.asema.com"
+
+calculation_id = 1
+
+delay_min_time_seconds = 10
+delay_max_time_seconds = 20
+
+import web
+
+class SampleServer():
+    def __init__(self):
+        pass
+
+    # Creates a dummy result in RDF format and sends it over MQTT
+    # to listeners
+    def notify_ready(self, id):
+        notification = NotificationFactory().create("http://www.calculator.com/example")
+        notification.setMessageId(id)
+        status = Status()
+        status.setPercentage(100)
+        notification.setStatus(status)
+        activity = Activity()
+        entity = Entity("http://www.calculator.com/calculatedentity")
+        activity.addEntity(entity)
+        notification.addActivity(activity)
+    
+        payload, content_type = Tools().serializeNotification(notification)
+        publish.single("sample/result/youcom/calculations", payload, hostname=mqtt_server)
+    
+    def POST(self):
+        # parse request message into Smart API Request Object     
+        req_str = rawRequest(web.ctx.env) 
+        content_type = web.ctx.env.get('Content-Type')
+        request = Tools().parseRequest(req_str, content_type)
+
+        response = ResponseFactory().create("http://talk.asema.com/demo/smart/demoresponder")
+        activity = Activity()
+        response.addActivity(activity)
+        status = Status()
+        status.setPercentage(100)
+        response.setStatus(status)
+        
+        payload, content_type = Tools().serializeResponse(response)
+        
+        self.notify_ready(1)
+        return payload
+
+
+def rawRequest(env):
+    req = env['wsgi.input'].read(int(env['CONTENT_LENGTH']))
+    return req    
+
+if __name__ == "__main__":
+    # the url for this tiny Smart API server will be: 
+    # http://0.0.0.0:8080/demo/smart/v1.0e1.0/access
+    app = web.application(('/smart/v1.0e1.0/access', 'SampleServer'), globals())
+    web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", 3333))
+    
\ No newline at end of file