From efd622d123a6351e22f405dcf3812aab455b3cf2 Mon Sep 17 00:00:00 2001 From: Justas Azna Date: Fri, 14 Nov 2014 17:05:25 +0100 Subject: [PATCH] Add setup.py for easier installation and refactor imports --- examples/InstanceConstructor.py | 84 +++++++++--------- examples/MinimalServer.py | 22 ++--- examples/TestCreateFromJSON.py | 72 ++++++++-------- iottoolkit/__init__.py | 0 iottoolkit/interfaces/CoapObjectService.py | 132 ++++++++++++++--------------- iottoolkit/interfaces/HttpObjectService.py | 62 +++++++------- setup.py | 34 ++++++++ 7 files changed, 220 insertions(+), 186 deletions(-) create mode 100644 iottoolkit/__init__.py create mode 100644 setup.py diff --git a/examples/InstanceConstructor.py b/examples/InstanceConstructor.py index f32fa27..e87a143 100644 --- a/examples/InstanceConstructor.py +++ b/examples/InstanceConstructor.py @@ -1,26 +1,26 @@ ''' Created on Dec 12, 2013 -Create services from a service model instance, create objects from an object model instance -How are base objects mapped to services? +Create services from a service model instance, create objects from an object model instance +How are base objects mapped to services? This file creates a service object that has multiple services and a single object tree -There could be a service creation service where POSTing this descriptor would build objects -and create service instance, and this will be built as function sets, probably part of +There could be a service creation service where POSTing this descriptor would build objects +and create service instance, and this will be built as function sets, probably part of Resource Directory (RD) @author: mjkoster ''' -from core.RESTfulResource import RESTfulResource -from core.SmartObject import SmartObject +from iottoolkit.core.RESTfulResource import RESTfulResource +from iottoolkit.core.SmartObject import SmartObject from rdflib.term import Literal, URIRef -from interfaces.HttpObjectService import HttpObjectService -from interfaces.CoapObjectService import CoapObjectService +from iottoolkit.interfaces.HttpObjectService import HttpObjectService +from iottoolkit.interfaces.CoapObjectService import CoapObjectService from time import sleep from urlparse import urlparse import subprocess import rdflib -#workaround to register rdf JSON plugins +#workaround to register rdf JSON plugins from rdflib.plugin import Serializer, Parser rdflib.plugin.register('json-ld', Serializer, 'rdflib_jsonld.serializer', 'JsonLDSerializer') rdflib.plugin.register('json-ld', Parser, 'rdflib_jsonld.parser', 'JsonLDParser') @@ -44,14 +44,14 @@ 'IPV4': '', 'root': '/', 'discovery': '/' - }, + }, 'localCoAP': { 'scheme': 'coap', 'FQDN': 'localhost', 'port': 5683, 'IPV4': '', 'root': '/', - 'discovery': '/' + 'discovery': '/' } }, @@ -140,7 +140,7 @@ 'subscribesTo': ['mqtt://smartobjectservice.com:1883/sensors/rhvWeather-01/daily_rain'], } } - + } class SystemInstance(object): @@ -154,21 +154,21 @@ class SystemInstance(object): } ''' def __init__(self, systemConstructor): - + self._service_metadata = systemConstructor['service_metadata'] self._services = systemConstructor['services'] self._object_metadata = systemConstructor['object_metadata'] self._objects = systemConstructor['objects'] - + self._baseObject = None - + self._defaultResources = { 'SmartObject': ['Description', 'Agent'], 'ObservableProperty': ['Description', 'Observers'] } self._observerTypes = ['subscribesTo', 'publishesTo', 'bridgesTo', 'handledBy'] - + self._observerSchemes = ['http', 'coap', 'mqtt', 'handler'] self._mqttObserverTemplate = { @@ -180,25 +180,25 @@ def __init__(self, systemConstructor): 'keepAlive': 60, 'QoS': 0 } - + self._httpPublisherTemplate = { 'resourceName': 'httpPublisher', 'resourceClass': 'httpPublisher', 'targetURI': 'http://localhost:8000/' } - + self._httpSubscriberTemplate = { 'resourceName': 'httpSubscriber', 'resourceClass': 'httpSubscriber', 'ObserverURI': 'http://localhost:8000/', } - + self._coapPublisherTemplate = { 'resourceName': 'coapPublisher', 'resourceClass': 'coapPublisher', 'targetURI': 'coap://localhost:5683/' } - + self._coapSubscriberTemplate = { 'resourceName': 'coapSubscriber', 'resourceClass': 'coapSubscriber', @@ -213,15 +213,15 @@ def __init__(self, systemConstructor): ''' make objects from object models first - make list sorted by path element count + length for import from graph, + make list sorted by path element count + length for import from graph, could count a split list but this should be the same if we eat slashes somewhere - having the root object called '/' and '/' as the separator is extra work + having the root object called '/' and '/' as the separator is extra work ''' self._resourceList = sorted( self._objects.keys(), key=lambda s:s.count('/') ) self._resourceList = sorted( self._resourceList, key=lambda s:len(s)) for self._resourceLink in self._resourceList: self._resourceDescriptor = self._objects[self._resourceLink] - # see if base object needs to be created. + # see if base object needs to be created. if self._resourceLink is '/' and self._resourceDescriptor['resourceClass'] is 'SmartObject' and self._baseObject is None: self._newResource = SmartObject() self._baseObject = self._newResource @@ -236,7 +236,7 @@ def __init__(self, systemConstructor): 'resourceName': self._defaultResource, 'resourceClass': self._defaultResource }) - if self._defaultResource is 'Description': + if self._defaultResource is 'Description': self._newChildResource.create(self._graphFromModel(self._resourceLink, self._resourceDescriptor)) self._newResource.create({'resourceName':'.well-known', 'resourceClass':'Agent'})\ .create({'resourceName':'core', 'resourceClass':'LinkFormatProxy'}) @@ -251,14 +251,14 @@ def __init__(self, systemConstructor): ''' # make this a service Object (RESTfulResource) with dict as constructor self._serviceRegistry = self._objectFromPath('/services', self._baseObject) - self._serviceDescription = self._objectFromPath('/services/Description', self._baseObject) - + self._serviceDescription = self._objectFromPath('/services/Description', self._baseObject) + for self._serviceName in self._services: self._newService = ServiceObject(self._serviceName, self._services[self._serviceName], self._baseObject) self._serviceRegistry.resources.update({self._serviceName:self._newService}) self._serviceDescription.set(self._graphFromModel(self._serviceName, self._services[self._serviceName])) - - + + def _graphFromModel(self, link, meta): # make rdf-json from the model and return RDF graph for loading into Description g=rdflib.Graph() @@ -279,7 +279,7 @@ def _observerFromURI(self, currentResource, observerType, observerURI): if observerType is 'subscribesTo': resourceConstructor = self._httpSubscriberTemplate.copy() resourceConstructor['observerURI'] = observerURI - + elif URIObject.scheme == 'coap': if observerType is 'publishesTo': resourceConstructor = self._coapPublisherTemplate.copy() @@ -287,9 +287,9 @@ def _observerFromURI(self, currentResource, observerType, observerURI): if observerType is 'subscribesTo': resourceConstructor = self._coapSubscriberTemplate.copy() resourceConstructor['observerURI'] = observerURI - + elif URIObject.scheme == 'mqtt': - resourceConstructor = self._mqttObserverTemplate.copy() + resourceConstructor = self._mqttObserverTemplate.copy() resourceConstructor['connection'] = URIObject.netloc if observerType is 'publishesTo': resourceConstructor['pubTopic'] = URIObject.path @@ -300,15 +300,15 @@ def _observerFromURI(self, currentResource, observerType, observerURI): resourceConstructor['subTopic'] = URIObject.path elif URIObject.scheme == 'handler': - resourceConstructor = self._callbackNotifierTemplate.copy() + resourceConstructor = self._callbackNotifierTemplate.copy() resourceConstructor['handlerURI'] = observerURI - + else: print 'no scheme', URIObject.scheme return - - #create resource in currentResource.resources['Observers'] container - newObserver = currentResource.resources['Observers'].create(resourceConstructor) + + #create resource in currentResource.resources['Observers'] container + newObserver = currentResource.resources['Observers'].create(resourceConstructor) def _objectFromPath(self, path, baseObject): # fails if resource doesn't exist @@ -325,7 +325,7 @@ def __init__(self, serviceName, serviceConstructor, baseObject): 'resourceName': serviceName, 'resourceClass': serviceConstructor['scheme'] } - + RESTfulResource.__init__(self, baseObject, self._resourceConstructor ) self._serviceConstructor = serviceConstructor # TODO collect IP addresses and update the constructor @@ -334,16 +334,16 @@ def __init__(self, serviceName, serviceConstructor, baseObject): (self._objectFromPath(self._serviceConstructor['root'], baseObject), port=self._serviceConstructor['port']) URLObject= urlparse(self._httpService._baseObject.Properties.get('httpService')) self._serviceConstructor['FQDN'] = URLObject.netloc.split(':')[0] - + if self._serviceConstructor['scheme'] is 'coap': self._coapService = CoapObjectService\ (self._objectFromPath(self._serviceConstructor['root'], baseObject), port=self._serviceConstructor['port']) URLObject= urlparse(self._coapService._baseObject.Properties.get('coapService')) self._serviceConstructor['FQDN'] = URLObject.netloc.split(':')[0] - + if serviceConstructor['scheme'] is 'mqtt': subprocess.call('mosquitto -d -p ', self._serviceConstructor['port']) - + self._set(self._serviceConstructor) def _objectFromPath(self, path, baseObject): @@ -361,11 +361,11 @@ def _objectFromPath(self, path, baseObject): make an instance using the example constructor ''' system = SystemInstance(exampleConstructor) - + try: # register handlers etc. while 1: sleep(1) except KeyboardInterrupt: pass print 'got KeyboardInterrupt' - \ No newline at end of file + diff --git a/examples/MinimalServer.py b/examples/MinimalServer.py index 6bdb6eb..25d7050 100644 --- a/examples/MinimalServer.py +++ b/examples/MinimalServer.py @@ -1,16 +1,16 @@ ''' Created on Dec 7, 2013 -Minimal service creation, base object auto-created by the first server, -passed to constructor of second server +Minimal service creation, base object auto-created by the first server, +passed to constructor of second server @author: mjkoster ''' -from interfaces.HttpObjectService import HttpObjectService -from interfaces.CoapObjectService import CoapObjectService +from iottoolkit.interfaces.HttpObjectService import HttpObjectService +from iottoolkit.interfaces.CoapObjectService import CoapObjectService from time import sleep -#workaround to register rdf JSON plugins +#workaround to register rdf JSON plugins import rdflib from rdflib.plugin import Serializer, Parser rdflib.plugin.register('json-ld', Serializer, 'rdflib_jsonld.serializer', 'JsonLDSerializer') @@ -20,21 +20,21 @@ if __name__ == '__main__' : - - # make an empty instance of a SmartObject shared by 2 interfaces, + + # make an empty instance of a SmartObject shared by 2 interfaces, # CoAP and HTTP, default object root and default ports 5683 and 8000 # CoAP service makes the base object and it is passed to the http service constructor - + baseObject = HttpObjectService( CoapObjectService().baseObject ).baseObject - + # emulate the .well-known/core interface baseObject.create({'resourceName': '.well-known','resourceClass': 'SmartObject'},\ ).create({'resourceName': 'core','resourceClass': 'LinkFormatProxy'}) - + try: # register handlers etc. while 1: sleep(1) except KeyboardInterrupt: pass print 'got KeyboardInterrupt' - \ No newline at end of file + diff --git a/examples/TestCreateFromJSON.py b/examples/TestCreateFromJSON.py index c68df66..a225410 100644 --- a/examples/TestCreateFromJSON.py +++ b/examples/TestCreateFromJSON.py @@ -6,19 +6,19 @@ @author: mjkoster ''' -from core.SmartObject import SmartObject -from core.Description import Description -from core.ObservableProperty import ObservableProperty -from core.Observers import Observers -from core.PropertyOfInterest import PropertyOfInterest +from iottoolkit.core.SmartObject import SmartObject +from iottoolkit.core.Description import Description +from iottoolkit.core.ObservableProperty import ObservableProperty +from iottoolkit.core.Observers import Observers +from iottoolkit.core.PropertyOfInterest import PropertyOfInterest from rdflib.term import Literal, URIRef from rdflib.namespace import RDF, RDFS, XSD, OWL -from interfaces.HttpObjectService import HttpObjectService -from interfaces.CoapObjectService import CoapObjectService +from iottoolkit.interfaces.HttpObjectService import HttpObjectService +from iottoolkit.interfaces.CoapObjectService import CoapObjectService from time import sleep import sys -#workaround to register rdf JSON plugins +#workaround to register rdf JSON plugins import rdflib from rdflib.plugin import Serializer, Parser rdflib.plugin.register('json-ld', Serializer, 'rdflib_jsonld.serializer', 'JsonLDSerializer') @@ -28,22 +28,22 @@ if __name__ == '__main__' : - + baseObject = HttpObjectService().baseObject # make an instance of the service, default object root and default port 8000 - + coapService = CoapObjectService(baseObject) # create a second service port with an empty default base object to test create from JSON HttpObjectService(port=8001) - + # create the weather station resource template - + baseObject.Description = baseObject.create({'resourceName':'Description',\ - 'resourceClass': 'Description'}) + 'resourceClass': 'Description'}) # emulate the .well-known/core interface baseObject.create({'resourceName': '.well-known','resourceClass': 'SmartObject'},\ ).create({'resourceName': 'core','resourceClass': 'LinkFormatProxy'}) - + # example description in simple link-format like concepts baseObject.Description.set((URIRef('sensors/rhvWeather-01'), Literal('resourceClass'), Literal('SmartObject'))) baseObject.Description.set((URIRef('sensors/rhvWeather-01'), Literal('resourceType'), Literal('SensorSystem'))) @@ -53,57 +53,57 @@ baseObject.Description.set((URIRef('sensors/rhvWeather-01/outdoor_temperature'), Literal('resourceType'), Literal('temperature'))) baseObject.Description.set((URIRef('sensors/rhvWeather-01/outdoor_humidity'), Literal('interfaceType'), Literal('sensor'))) baseObject.Description.set((URIRef('sensors/rhvWeather-01/outdoor_humidity'), Literal('resourceType'), Literal('humidity'))) - - # sensors resource under the baseObject for all sensors - # top level object container for sensors, default class is SmartObject - sensors = baseObject.create({'resourceName': 'sensors', 'resourceClass': 'SmartObject'}) - + + # sensors resource under the baseObject for all sensors + # top level object container for sensors, default class is SmartObject + sensors = baseObject.create({'resourceName': 'sensors', 'resourceClass': 'SmartObject'}) + sensors.create({'resourceName':'Description',\ - 'resourceClass': 'Description'}) - + 'resourceClass': 'Description'}) + #weather resource under sensors for the weather sensor - # create a default class SmartObject for the weather sensor cluster - weather = sensors.create({'resourceName': 'rhvWeather-01', 'resourceClass': 'SmartObject'}) - + # create a default class SmartObject for the weather sensor cluster + weather = sensors.create({'resourceName': 'rhvWeather-01', 'resourceClass': 'SmartObject'}) + weather.create({'resourceName':'Description',\ - 'resourceClass': 'Description'}) + 'resourceClass': 'Description'}) + - # now create an Observable Property for each sensor output outdoor_temperature = weather.create({'resourceName': 'outdoor_temperature',\ 'resourceClass': 'ObservableProperty'}) - + outdoor_temperature.create({'resourceName':'Description',\ 'resourceClass': 'Description'}) - + outdoor_temperature.Observers = outdoor_temperature.create({'resourceName':'Observers',\ 'resourceClass': 'Observers'}) - + outdoor_temperature.Observers.create({'resourceName': 'mqttTestObserver',\ 'resourceClass': 'mqttObserver',\ 'connection': 'smartobjectservice.com',\ 'pubTopic': ''}) - + outdoor_humidity = weather.create({'resourceName': 'outdoor_humidity',\ 'resourceClass': 'ObservableProperty'}) - + outdoor_humidity.create({'resourceName':'Description',\ 'resourceClass': 'Description'}) - + outdoor_humidity.Observers = outdoor_humidity.create({'resourceName':'Observers',\ 'resourceClass': 'Observers'}) - + outdoor_humidity.Observers.create({'resourceName': 'mqttTestObserver',\ 'resourceClass': 'mqttObserver',\ 'connection': 'smartobjectservice.com',\ 'pubTopic': ''}) - - + + try: # register handlers etc. while 1: sleep(1) except KeyboardInterrupt: pass print 'got KeyboardInterrupt' - \ No newline at end of file + diff --git a/iottoolkit/__init__.py b/iottoolkit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/iottoolkit/interfaces/CoapObjectService.py b/iottoolkit/interfaces/CoapObjectService.py index 64a1c1f..334faeb 100644 --- a/iottoolkit/interfaces/CoapObjectService.py +++ b/iottoolkit/interfaces/CoapObjectService.py @@ -18,24 +18,24 @@ def __init__(self, baseObject=None, port=None): # FIXME if no baseObject given, self._port = 5683 # IETF default port for coap:// else: self._port = port - + if baseObject == None: - from core.SmartObject import SmartObject + from iottoolkit.core.SmartObject import SmartObject self._baseObject = SmartObject() else: self._baseObject = baseObject - + self.resources = self._baseObject.resources - + self._host = gethostname() self._baseObject.Properties.update({'coapService': 'coap://' + self._host + ':' + repr(self._port)}) self._coapHandler = CoapRequestHandler(self._baseObject) - self._coapServer = COAPServer(self._host, self._port, self._coapHandler) + self._coapServer = COAPServer(self._host, self._port, self._coapHandler) #print 'CoAP Service started at', self._baseObject.Properties.get('coapService') #starts thread as daemon, has run method loop - - @property + + @property def baseObject(self): return self._baseObject @@ -44,7 +44,7 @@ class CoapRequestHandler(object): def __init__(self,baseObject): self._linkCache = {} self._baseObject = baseObject - + def do_GET(self, path, options=None): self._query = None self._observing = False @@ -55,31 +55,31 @@ def do_GET(self, path, options=None): if number == COAPOption.OBSERVE: self._observing = True self._currentResource = self.linkToRef(path, self._baseObject) - + if hasattr(self._currentResource, 'serialize'): self._contentType = self._currentResource._serializeContentTypes[0] return 200, self._currentResource.serialize(self._currentResource.get(self._query), self._contentType), self._contentType else: self._contentType='application/json' return 200, json.dumps(self._currentResource.get()), self._contentType - + def do_PUT(self, path, payload, options=None): self._currentResource = self.linkToRef(path, self._baseObject) if hasattr(self._currentResource, 'serialize'): self._contentType=self._currentResource._serializeContentTypes[0] self._currentResource.set( self._currentResource.parse(str(payload), self._contentType) ) - return 200, '', self._contentType - else: + return 200, '', self._contentType + else: self._contentType='application/json' self._currentResource.set(json.loads(str(payload))) return 200, '', self._contentType - + def do_POST(self, path, payload, flag): pass - + def do_DELETE(self, path, payload, flag): pass - + def linkToRef(self, linkPath, baseObject): ''' takes a path string and walks the object tree from a base dictionary @@ -97,7 +97,7 @@ def linkToRef(self, linkPath, baseObject): self._resource = self._currentDict[self._pathElements[-1] ] #self._linkCache.update({ self._linkPath : self._resource }) return self._resource - + def getByLink(self, linkPath): return self.linkToRef(linkPath).get() @@ -121,11 +121,11 @@ def setByLink(self, linkPath, newValue): # See the License for the specific language governing permissions and # limitations under the License. ''' -added options passed to handler, added Observe (6) to options +added options passed to handler, added Observe (6) to options ''' #from webiopi.utils.version import PYTHON_MAJOR PYTHON_MAJOR=2 -#from webiopi.utils.logger import info, exception +#from webiopi.utils.logger import info, exception def info(msg): print(msg) @@ -154,7 +154,7 @@ def exception(e): def HTTPCode2CoAPCode(code): return int(code/100) * 32 + (code%100) - + class COAPContentFormat(): FORMATS = {0: "text/plain", 40: "application/link-format", @@ -172,7 +172,7 @@ def getCode(fmt): if COAPContentFormat.FORMATS[code] == fmt: return code return None - + @staticmethod def toString(code): if code == None: @@ -180,9 +180,9 @@ def toString(code): if code in COAPContentFormat.FORMATS: return COAPContentFormat.FORMATS[code] - + raise Exception("Unknown content format %d" % code) - + class COAPOption(): OPTIONS = {1: "If-Match", @@ -201,7 +201,7 @@ class COAPOption(): 35: "Proxy-Uri", 39: "Proxy-Scheme" } - + IF_MATCH = 1 URI_HOST = 3 ETAG = 4 @@ -217,8 +217,8 @@ class COAPOption(): LOCATION_QUERY = 20 PROXY_URI = 35 PROXY_SCHEME = 39 - - + + class COAPMessage(): TYPES = ["CON", "NON", "ACK", "RST"] CON = 0 @@ -238,19 +238,19 @@ def __init__(self, msg_type=0, code=0, uri=None): self.uri_path = "" self.content_format = None self.payload = None - + if uri != None: p = urlparse(uri) self.host = p.hostname if p.port: self.port = int(p.port) self.uri_path = p.path - + def __getOptionHeader__(self, byte): delta = (byte & 0xF0) >> 4 length = byte & 0x0F - return (delta, length) - + return (delta, length) + def __str__(self): result = [] result.append("Version: %s" % self.version) @@ -266,18 +266,18 @@ def __str__(self): result.append("Payload: %s" % self.payload) result.append("") return '\n'.join(result) - + def getOptionHeaderValue(self, value): if value > 268: return 14 if value > 12: return 13 return value - + def getOptionHeaderExtension(self, value): buff = bytearray() v = self.getOptionHeaderValue(value) - + if v == 14: value -= 269 buff.append((value & 0xFF00) >> 8) @@ -288,19 +288,19 @@ def getOptionHeaderExtension(self, value): buff.append(value) return buff - + def appendOption(self, buff, lastnumber, option, data): delta = option - lastnumber length = len(data) - + d = self.getOptionHeaderValue(delta) l = self.getOptionHeaderValue(length) - + b = 0 - b |= (d << 4) & 0xF0 + b |= (d << 4) & 0xF0 b |= l & 0x0F buff.append(b) - + ext = self.getOptionHeaderExtension(delta); for b in ext: buff.append(b) @@ -327,13 +327,13 @@ def getBytes(self): buff.append(self.code) buff.append((self.id & 0xFF00) >> 8) buff.append(self.id & 0x00FF) - + if self.token: for c in self.token: buff.append(c) lastnumber = 0 - + if len(self.uri_path) > 0: paths = self.uri_path.split("/") for p in paths: @@ -351,9 +351,9 @@ def getBytes(self): data.append((fmt_code & 0xFF00) >> 8) data.append(fmt_code & 0x00FF) lastnumber = self.appendOption(buff, lastnumber, COAPOption.CONTENT_FORMAT, data) - + buff.append(0xFF) - + if self.payload: if PYTHON_MAJOR >= 3: data = self.payload.encode() @@ -361,9 +361,9 @@ def getBytes(self): data = bytearray(self.payload) for c in data: buff.append(c) - + return buff - + def parseByteArray(self, buff): self.version = (buff[0] & 0xC0) >> 6 self.type = (buff[0] & 0x30) >> 4 @@ -375,7 +375,7 @@ def parseByteArray(self, buff): index += token_length self.code = buff[1] self.id = (buff[2] << 8) | buff[3] - + number = 0 # process options @@ -391,12 +391,12 @@ def parseByteArray(self, buff): elif delta == 14: delta += 255 + ((buff[index+offset] << 8) | buff[index+offset+1]) offset += 2 - + # length extended with 1 byte if length == 13: length += buff[index+offset] offset += 1 - + # length extended with 2 buff elif length == 14: length += 255 + ((buff[index+offset] << 8) | buff[index+offset+1]) @@ -423,17 +423,17 @@ def parseByteArray(self, buff): index += offset + length index += 1 # skip 0xFF / end-of-options - + if len(buff) > index: self.payload = buff[index:] else: self.payload = "" - + for option in self.options: (number, value) = option.values() if number == COAPOption.URI_PATH: self.uri_path += "/%s" % value - + class COAPRequest(COAPMessage): CODES = {0: None, @@ -467,7 +467,7 @@ class COAPDelete(COAPRequest): def __init__(self, uri): COAPRequest.__init__(self, COAPMessage.CON, COAPRequest.DELETE, uri) -class COAPResponse(COAPMessage): +class COAPResponse(COAPMessage): CODES = {0: None, 64: "2.00 OK", 65: "2.01 Created", @@ -490,7 +490,7 @@ class COAPResponse(COAPMessage): 162: "5.02 Bad Gateway", 163: "5.03 Service Unavailable", 164: "5.04 Gateway Timeout", - 165: "5.05 Proxying Not Supported" + 165: "5.05 Proxying Not Supported" } # 2.XX @@ -500,7 +500,7 @@ class COAPResponse(COAPMessage): VALID = 67 CHANGED = 68 CONTENT = 69 - + # 4.XX BAD_REQUEST = 128 UNAUTHORIZED = 129 @@ -512,7 +512,7 @@ class COAPResponse(COAPMessage): PRECONDITION_FAILED = 140 ENTITY_TOO_LARGE = 141 UNSUPPORTED_CONTENT = 143 - + # 5.XX INTERNAL_ERROR = 160 NOT_IMPLEMENTED = 161 @@ -520,7 +520,7 @@ class COAPResponse(COAPMessage): SERVICE_UNAVAILABLE = 163 GATEWAY_TIMEOUT = 164 PROXYING_NOT_SUPPORTED = 165 - + def __init__(self): COAPMessage.__init__(self) @@ -529,7 +529,7 @@ def __init__(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.settimeout(1.0) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) - + def sendRequest(self, message): data = message.getBytes(); sent = 0 @@ -559,7 +559,7 @@ def __init__(self, host, port, handler): self.running = True self.daemon = True self.start() - + def run(self): info("CoAP Server at coap://%s:%s/" % (self.host, self.port)) while self.running == True: @@ -575,26 +575,26 @@ def run(self): responseBytes = coapResponse.getBytes() self.socket.sendto(responseBytes, client) self.logger.debug('"%s %s CoAP/%.1f" %s -' % (coapRequest.CODES[coapRequest.code], coapRequest.uri_path, coapRequest.version, coapResponse.CODES[coapResponse.code])) - + except socket.timeout as e: continue except Exception as e: if self.running == True: exception(e) - + info("CoAP Server stopped") - + def enableMulticast(self): while not self.running: pass mreq = struct.pack("4sl", socket.inet_aton(self.multicast_ip), socket.INADDR_ANY) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) info("CoAP Server at coap://%s:%s/ (MULTICAST)" % (self.multicast_ip, self.port)) - + def stop(self): self.running = False self.socket.close() - + def processMessage(self, request, response): if request.type == COAPMessage.CON: response.type = COAPMessage.ACK @@ -606,7 +606,7 @@ def processMessage(self, request, response): response.id = request.id response.uri_path = request.uri_path - + if request.code == COAPRequest.GET: self.handler.do_GET(request, response) elif request.code == COAPRequest.PUT: @@ -615,11 +615,11 @@ def processMessage(self, request, response): response.code = COAPResponse.NOT_IMPLEMENTED else: exception(Exception("Received CoAP Response : %s" % response)) - + class COAPHandler(): def __init__(self, handler): self.handler = handler - + def do_GET(self, request, response): try: (code, body, contentType) = self.handler.do_GET(request.uri_path[1:], request.options) @@ -637,7 +637,7 @@ def do_GET(self, request, response): except Exception as e: response.code = COAPResponse.INTERNAL_ERROR raise e - + def do_PUT(self, request, response): try: (code, body, contentType) = self.handler.do_PUT(request.uri_path[1:], request.payload, request.options) @@ -655,4 +655,4 @@ def do_PUT(self, request, response): except Exception as e: response.code = COAPResponse.INTERNAL_ERROR raise e - + diff --git a/iottoolkit/interfaces/HttpObjectService.py b/iottoolkit/interfaces/HttpObjectService.py index 4c849d3..82ab355 100644 --- a/iottoolkit/interfaces/HttpObjectService.py +++ b/iottoolkit/interfaces/HttpObjectService.py @@ -1,9 +1,9 @@ ''' Created on Oct 18, 2012 -Create a RESTlite instance of a http server for SmartObjects. -based on wsgi/restlite with the restObject extensions to match object path segments -to resource names e.g. the URL path: /object1/barometricPressure/Description/Observers +Create a RESTlite instance of a http server for SmartObjects. +based on wsgi/restlite with the restObject extensions to match object path segments +to resource names e.g. the URL path: /object1/barometricPressure/Description/Observers maps to the python API identifier: object1.barometricPressure.Description.Observers @author: mjkoster @@ -22,12 +22,12 @@ class Request(restObject.Request): def __init__(self, env, start_response): self.env, self.start_response = env, start_response restObject.Request.__init__(self.env, self.start_response) - + class RestObject(restObject.RestObject): def __init__(self, rootObject, users): restObject.RestObject.__init__(self, rootObject, users) - + def contentTypeNegotiate(self, accepts, providedTypes): accepts = accepts.split(',') prefs = [] @@ -47,17 +47,17 @@ def contentTypeNegotiate(self, accepts, providedTypes): return providedTypes[0] # return our favorite type if we don't have a preferred type and */* is allowed else: raise restlite.Status('415 Unsupported Media Type') # no applicable type and client doesn't want whatever - + def _handleGET(self, currentResource): # if it's a complex structure class, invoke the serializer - + if hasattr(currentResource,'serialize') : # see if the resource has a serialize method responseType = self.contentTypeNegotiate(self.env['HTTP_ACCEPT'], currentResource.serializeContentTypes() ) - self.start_response('200 OK', [('Content-Type', responseType)]) + self.start_response('200 OK', [('Content-Type', responseType)]) return currentResource.serialize( currentResource.get(), responseType ) else: return restObject.RestObject._handleGET(self, currentResource) # default GET does JSON and XML - + def _handlePUT(self, currentResource): if hasattr(currentResource, 'parse') : if self.env['CONTENT_TYPE'] in currentResource.parseContentTypes() : @@ -66,7 +66,7 @@ def _handlePUT(self, currentResource): raise restlite.Status('415 Unsupported Media Type') else : restObject.RestObject._handlePUT(self, currentResource) # default PUT - + def _handlePOST(self, currentResource): if hasattr(currentResource, 'parse') : if self.env['CONTENT_TYPE'] in currentResource.parseContentTypes() : @@ -75,7 +75,7 @@ def _handlePOST(self, currentResource): raise restlite.Status('415 Unsupported Media Type') else : restObject.RestObject._handlePOST(self, currentResource) # default PUT - + def _handleDELETE(self, currentResource): if hasattr(currentResource, 'parse') : if self.env['CONTENT_TYPE'] in currentResource.parseContentTypes() : @@ -99,59 +99,59 @@ def handler(env, start_response): class HttpHandler(object): def __init__(self, baseObject): # get a handle to the Object Service root dictionary - self.objectHandler = bind(baseObject, users=None) - #bind to root resource dictionary passed to constructor + self.objectHandler = bind(baseObject, users=None) + #bind to root resource dictionary passed to constructor #bind returns the RestObject handler which uses the Request object # the handler calls the overriding _handleXX methods in this module self.routes = [(r'GET /favicon.ico$', self.favicon_handler),(r'GET,PUT,POST,DELETE ', self.objectHandler )] - return - + return + def favicon_handler(self, env, start_response) : - start_response('200 OK', [('Content-Type', 'image/gif')]) + start_response('200 OK', [('Content-Type', 'image/gif')]) try: with open('favicon.ico', 'rb') as f: result = f.read() except: raise restlite.Status, '400 Error Reading File' return(result) - -class HttpObjectService(object): - def __init__(self, baseObject=None, port=None): + +class HttpObjectService(object): + def __init__(self, baseObject=None, port=None): if port == None: self._port=8000 else: self._port = port # default port 8000 - + if baseObject == None: - from core.SmartObject import SmartObject + from iottoolkit.core.SmartObject import SmartObject self._baseObject = SmartObject() else: self._baseObject = baseObject - + self.resources = self._baseObject.resources - + self.start(self._port) - - @property + + @property def baseObject(self): return self._baseObject - - def start(self, port=None): + + def start(self, port=None): if port==None: self._port = 8000 # default port 8000 else: self._port=port # override port on start if supplied - + httpThread = threading.Thread(target = self._startHttpObjectService) httpThread.daemon = True httpThread.start() self._baseObject.Properties.update({'httpService': 'http://' + gethostname() + ':' + repr(self._port)}) print 'HTTP server started at', self._baseObject.Properties.get('httpService') - - + + def _startHttpObjectService(self): from wsgiref.simple_server import make_server - # HttpObjectService constructor method creates a Smart Object service and + # HttpObjectService constructor method creates a Smart Object service and # returns a constructor for a restlite router instance self.httpObjectHandler = HttpHandler(self._baseObject) self.httpd = make_server('', self._port, restlite.router(self.httpObjectHandler.routes)) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5d9c637 --- /dev/null +++ b/setup.py @@ -0,0 +1,34 @@ +from setuptools import setup, find_packages + +version = '0.1' + +setup( + name='iottoolkit', + version=version, + description="Reference implementation of the Smart Model API", + long_description=open('README.md').read(), + + # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[], + keywords='IoT', + author='Michael J Koster', + url='', + license='AGPL', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=False, + tests_require=[ + 'nose', + ], + test_suite='nose.collector', + install_requires=[ + 'isodate', + 'mosquitto', + 'rdflib-rdfjson', + 'rdflib-jsonld', + ], + dependency_links=[ + 'git+https://github.com/RDFLib/rdflib-rdfjson.git#egg=rdflib-rdfjson', + 'git+https://github.com/RDFLib/rdflib-jsonld.git#egg=rdflib-jsonld', + ] +)