Python: Fix URL in Variant parsing and serialization; Notification
authorjani <jani@asema.com>
Wed, 7 Aug 2019 21:47:01 +0000 (00:47 +0300)
committerjani <jani@asema.com>
Wed, 7 Aug 2019 21:47:01 +0000 (00:47 +0300)
sending in Adapt data sample

Common/Python/SmartAPI/common/ClassMapper.py
Common/Python/SmartAPI/common/Tools.py
Common/Python/SmartAPI/model/Activity.py
Common/Python/SmartAPI/model/ValueObject.py
Common/Python/SmartAPI/rdf/Resource.py
Common/Python/SmartAPI/rdf/Variant.py
Common/Python/SmartAPI/tests/TestSequences.py
Examples/Python/AdaptDataService/AdaptDataService.py
Examples/Python/AdaptDataService/httpclient.py [new file with mode: 0644]
Examples/Python/CustomPropertiesAndClassesSample/smartapi_server_client_sample.py

index daa75bf76c3adf31d56af4ff19dfe72ae62a66e4..da0b3e1a13bc955264f35a5939a2a9bd850ba049 100644 (file)
@@ -152,8 +152,8 @@ class ClassMapper(object):
                        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 76b3fe90a50653406e570a1c2cb0074ded17bb19..17e51c26e507e96b7a2bf71133d065622a889753 100644 (file)
@@ -450,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 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 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..34ff8d66a16616d39396f3c5be2d9f26fe125589 100644 (file)
@@ -43,6 +43,8 @@ class Variant(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 +59,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:
index 7baa1587c7e87f16a6a1d45272a41476cedc45e5..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,15 +2162,16 @@ def conditionalPriceSpecificationTest():
     
 def main():
     #objectCopyTest()
-    #serializeRequestTest()
+    serializeRequestTest()
+    serializeNotificationTest()
     #longSerializeParseTest()
     #propertySerializeParseTest()
     #inputOutputTest()
     #parameterSerializeParseTest()
     #physicalEntityTest()
     #waypointsTest()
-    basicListSerializeParseTest()
-    baseObjectListSerializeParseTest()
+    #basicListSerializeParseTest()
+    #baseObjectListSerializeParseTest()
     #timeseriesListSerializeParseTest()
     #listSpeedTestSerialize()
     #listSpeedTestSerializeAndParse()
@@ -2184,7 +2200,7 @@ def main():
     #conceptValidationTest()
     #velocityTest()
     #unitPriceSpecificationTest()
-    conditionalPriceSpecificationTest()
+    #conditionalPriceSpecificationTest()
     
 if __name__=='__main__':
     main()
index ceece97b55086935844bd33d460dd22c3d90de2a..2f012bc8102cf22d358a5c7c1a4e29f2c0b923d4 100755 (executable)
@@ -2,7 +2,11 @@
 
 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
 from io import BytesIO
+from threading import Thread
+from httpclient import HttpClient
+import time
 import traceback
+import random
 
 from SmartAPI.agents.RegistrationAgent import RegistrationAgent
 from SmartAPI.agents.SearchAgent import SearchAgent
@@ -11,6 +15,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,13 +30,8 @@ from SmartAPI.model.TimeSeries import TimeSeries
 from SmartAPI.model.ValueObject import ValueObject
 
 
-from SmartAPI.common.PROPERTY import PROPERTY
-from SmartAPI.rdf.OrderedList import OrderedList
-from SmartAPI.rdf.ItemizedList import ItemizedList
-from SmartAPI.rdf.NudeList import NudeList
-
-
 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"
@@ -39,12 +39,51 @@ registrationServerKeyUri = "http://find.smart-api.io/smart/v1.0e1.0/key"
 #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)
+               print payload
+               self.http_client.send_data(payload)
+
 
 class SampleRegistration(object):
        
-
        def __init__(self):
                pass
 
@@ -70,9 +109,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()
@@ -81,7 +120,7 @@ class SampleRegistration(object):
                        read.setAuthorization(auth)
 
                        dataSource = Entity()
-                       dataSource.setIdentifierUri(myIdentity + "devices/Cdemodevice")
+                       dataSource.setIdentifierUri(myDeviceIdentity)
                        dataSource.setName("Demo Adapt datasource")
                        dataSource.addCapability(read)
                        dataSource.setManagedBy(myIdentity)
@@ -128,21 +167,6 @@ class SampleRegistration(object):
                        dataSource.addOffering(offering)
                        dataSource.addValueObject(power)
                        
-                       """
-                       il = ItemizedList()
-                       l = [x for x in range(10)]
-                       il.add_items(l)
-                       
-                       ol = OrderedList()
-                       ol.add_items(l)
-                       
-                       nl = NudeList()
-                       nl.add_items(l)
-
-                       dataSource.add(PROPERTY.ANGULARVELOCITYX, il)
-                       dataSource.add(PROPERTY.ANGULARVELOCITYY, ol)
-                       dataSource.add(PROPERTY.ANGULARVELOCITYZ, nl)
-                       """
                        agent.addEntity(sampleService)
                        agent.addEntity(dataSource);
 
@@ -204,15 +228,20 @@ class SampleDataService(BaseHTTPRequestHandler):
                        
                elif req_path.endswith("/access"):
                        request = Tools.parseRequest(body)
-                       print("request: " + request.toString())
-                               
-                       activity = request.getActivities()[0]  # type: Activity
-                               
-                       # Handle timeseries request
-                       if activity.hasTemporalContext():
-                               response_bytes.write(self.handleTemporalContext(activity, request))
-                       else:
-                               print("unhandled request received!")
+                       
+                       for a in request.getActivities():
+                               if a.method.asString() == RESOURCE.SUBSCRIBE:
+                                       print "Subscribing to notifications"
+                                       self.handleSubscription(a)
+                                       
+                               elif a.method.asString() == RESOURCE.READ:
+                                       print "Reading data"
+                                       
+                                       # Handle timeseries request
+                                       if a.hasTemporalContext():
+                                               response_bytes.write(self.handleTemporalContext(a, request))
+                                       else:
+                                               print("unhandled request received!")
                
                else:
                        print("unhandled request received!")
@@ -220,6 +249,15 @@ class SampleDataService(BaseHTTPRequestHandler):
                # 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: ")
diff --git a/Examples/Python/AdaptDataService/httpclient.py b/Examples/Python/AdaptDataService/httpclient.py
new file mode 100644 (file)
index 0000000..cb5f5d2
--- /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 + "/json")
+               self.ssl = (self.u.scheme == "https")
+               self.lock = Lock()
+               
+       def send_data(self, data):
+               self.lock.acquire()
+               try:
+                       payload = dumps(data)
+                       headers = { "content-type" : "application/json" }
+                       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, payload, headers)
+                       response = conn.getresponse()
+                       result = response.read()
+                       conn.close()
+               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'])