move handlers to messages
Change-Id: I1dd49e4d273d9fc3503757fd902767a4b1c42990
This commit is contained in:
parent
b52c2d7a60
commit
e914d12096
@ -1,11 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""Define SECoP Device classes
|
||||||
|
|
||||||
|
also define helpers to derive properties of the device"""
|
||||||
|
|
||||||
from lib import attrdict
|
from lib import attrdict
|
||||||
|
|
||||||
class Status(object):
|
class Status(object):
|
||||||
OK = 200
|
"""Map Menaing of a devices status to some constants
|
||||||
MOVING = 210
|
|
||||||
WARN = 220
|
which may be used for transport"""
|
||||||
UNSTABLE = 230
|
OK = 100
|
||||||
ERROR = 240
|
MOVING = 200
|
||||||
|
WARN = 300
|
||||||
|
UNSTABLE = 350
|
||||||
|
ERROR = 400
|
||||||
UNKNOWN = 999
|
UNKNOWN = 999
|
||||||
|
|
||||||
status = Status()
|
status = Status()
|
||||||
@ -13,34 +42,43 @@ status = Status()
|
|||||||
|
|
||||||
# XXX: deriving PARS/CMDS should be done in a suitable metaclass....
|
# XXX: deriving PARS/CMDS should be done in a suitable metaclass....
|
||||||
class Device(object):
|
class Device(object):
|
||||||
|
"""Minimalist Device
|
||||||
|
|
||||||
|
all others derive from this"""
|
||||||
name = None
|
name = None
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
raise NotImplemented
|
raise NotImplementedError('All Devices need a Status!')
|
||||||
def read_name(self):
|
def read_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Readable(Device):
|
class Readable(Device):
|
||||||
|
"""A Readable Device"""
|
||||||
unit = ''
|
unit = ''
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
raise NotImplemented
|
raise NotImplementedError('A Readable MUST provide a value')
|
||||||
def read_unit(self):
|
def read_unit(self):
|
||||||
return self.unit
|
return self.unit
|
||||||
|
|
||||||
class Writeable(Readable):
|
class Writeable(Readable):
|
||||||
|
"""Writeable can be told to change it's vallue"""
|
||||||
|
target = None
|
||||||
def read_target(self):
|
def read_target(self):
|
||||||
return self.target
|
return self.target
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.target = target
|
self.target = target
|
||||||
|
|
||||||
class Driveable(Writeable):
|
class Driveable(Writeable):
|
||||||
def do_wait(self):
|
"""A Moveable which may take a while to reach its target,
|
||||||
raise NotImplemented
|
|
||||||
|
hence stopping it may be desired"""
|
||||||
def do_stop(self):
|
def do_stop(self):
|
||||||
raise NotImplemented
|
raise NotImplementedError('A Driveable MUST implement the STOP() '
|
||||||
|
'command')
|
||||||
|
|
||||||
|
|
||||||
def get_device_pars(dev):
|
def get_device_pars(dev):
|
||||||
# returns a mapping of the devices parameter names to some 'description'
|
"""return a mapping of the devices parameter names to some
|
||||||
|
'description'"""
|
||||||
res = {}
|
res = {}
|
||||||
for n in dir(dev):
|
for n in dir(dev):
|
||||||
if n.startswith('read_'):
|
if n.startswith('read_'):
|
||||||
@ -52,13 +90,15 @@ def get_device_pars(dev):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def get_device_cmds(dev):
|
def get_device_cmds(dev):
|
||||||
# returns a mapping of the devices commands names to some 'description'
|
"""return a mapping of the devices command names to some
|
||||||
|
'description'"""
|
||||||
res = {}
|
res = {}
|
||||||
for n in dir(dev):
|
for n in dir(dev):
|
||||||
if n.startswith('do_'):
|
if n.startswith('do_'):
|
||||||
cname = n[5:]
|
cname = n[5:]
|
||||||
func = getattr(dev, n)
|
func = getattr(dev, n)
|
||||||
entry = attrdict(description=func.__doc__, args='unknown') # XXX: use inspect!
|
# XXX: use inspect!
|
||||||
|
entry = attrdict(description=func.__doc__, args='unknown')
|
||||||
res[cname] = entry
|
res[cname] = entry
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
74
src/lib.py
74
src/lib.py
@ -1,50 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""Define helpers"""
|
||||||
|
|
||||||
class attrdict(dict):
|
class attrdict(dict):
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
return self[key]
|
return self[key]
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
self[key] = value
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print "minimal testing: transport"
|
print "minimal testing: lib"
|
||||||
testcases = dict(
|
d = attrdict(a=1, b=2)
|
||||||
error=[ErrorReply(),
|
_ = d.a + d['b']
|
||||||
NoSuchDeviceErrorReply('device3'),
|
d.c = 9
|
||||||
NoSuchParamErrorReply('device2', 'param3'),
|
d['d'] = 'c'
|
||||||
ParamReadonlyErrorReply('device1', 'param1'),
|
assert d[d.d] == 9
|
||||||
UnsupportedFeatureErrorReply('feature5'),
|
|
||||||
NoSuchCommandErrorReply('device1','fance_command'),
|
|
||||||
CommandFailedErrorReply('device1','stop'),
|
|
||||||
InvalidParamValueErrorReply('device1','param2','STRING_Value'),
|
|
||||||
],
|
|
||||||
reply=[Reply(),
|
|
||||||
ListDevicesReply('device1', 'device2'),
|
|
||||||
ListDeviceParamsReply('device', ['param1', 'param2']),
|
|
||||||
ReadValueReply('device2', 3.1415),
|
|
||||||
ReadParamReply('device1', 'param2', 2.718),
|
|
||||||
WriteParamReply('device1', 'param2', 2.718),
|
|
||||||
RequestAsyncDataReply('device1', 'XXX: what to put here?'),
|
|
||||||
AsyncDataUnit('device1', 'param2', 2.718),
|
|
||||||
ListOfFeaturesReply('feature1', 'feature2'),
|
|
||||||
ActivateFeatureReply(),
|
|
||||||
],
|
|
||||||
request=[Request(),
|
|
||||||
ListDevicesRequest(),
|
|
||||||
ListDeviceParamsRequest('device1'),
|
|
||||||
ReadValueRequest('device2'),
|
|
||||||
ReadParamRequest('device1', 'param2'),
|
|
||||||
WriteParamRequest('device1', 'param2', 2.718),
|
|
||||||
RequestAsyncDataRequest('device1', ['param1', 'param2']),
|
|
||||||
ListOfFeaturesRequest(),
|
|
||||||
ActivateFeatureRequest('feature1'),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
for msgtype, msgs in testcases.items():
|
|
||||||
print "___ testing %ss ___" % msgtype
|
|
||||||
for msg in msgs:
|
|
||||||
print msg.__class__.__name__, 'is', msgtype,
|
|
||||||
decoded = parse(msg)
|
|
||||||
if decoded[0] != msgtype:
|
|
||||||
print "\tFAIL, got %r but expected %r" %(decoded[0], msgtype)
|
|
||||||
else:
|
|
||||||
print "\tOk"
|
|
||||||
print
|
|
||||||
|
|
||||||
|
129
src/messages.py
129
src/messages.py
@ -1,7 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""Define SECoP Messages"""
|
||||||
|
|
||||||
from lib import attrdict
|
from lib import attrdict
|
||||||
|
import time
|
||||||
|
from device import get_device_pars, get_device_cmds
|
||||||
|
|
||||||
class Request(object):
|
class Request(object):
|
||||||
|
"""Base class for all Requests"""
|
||||||
pars = []
|
pars = []
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
pars = ', '.join('%s=%r' % (k, self.__dict__[k]) for k in self.pars)
|
pars = ', '.join('%s=%r' % (k, self.__dict__[k]) for k in self.pars)
|
||||||
@ -9,6 +35,7 @@ class Request(object):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
class Reply(object):
|
class Reply(object):
|
||||||
|
"""Base class for all Replies"""
|
||||||
pars = []
|
pars = []
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
pars = ', '.join('%s=%r' % (k, self.__dict__[k]) for k in self.pars)
|
pars = ', '.join('%s=%r' % (k, self.__dict__[k]) for k in self.pars)
|
||||||
@ -37,9 +64,10 @@ class ListDeviceParamsReply(Reply):
|
|||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
class ReadValueRequest(Request):
|
class ReadValueRequest(Request):
|
||||||
pars = ['device']
|
pars = ['device', 'maxage']
|
||||||
def __init__(self, device, maxage=0):
|
def __init__(self, device, maxage=0):
|
||||||
self.device = device
|
self.device = device
|
||||||
|
self.maxage = maxage
|
||||||
|
|
||||||
class ReadValueReply(Reply):
|
class ReadValueReply(Reply):
|
||||||
pars = ['device', 'value', 'timestamp', 'error', 'unit']
|
pars = ['device', 'value', 'timestamp', 'error', 'unit']
|
||||||
@ -52,10 +80,11 @@ class ReadValueReply(Reply):
|
|||||||
|
|
||||||
|
|
||||||
class ReadParamRequest(Request):
|
class ReadParamRequest(Request):
|
||||||
pars = ['device', 'param']
|
pars = ['device', 'param', 'maxage']
|
||||||
def __init__(self, device, param, maxage=0):
|
def __init__(self, device, param, maxage=0):
|
||||||
self.device = device
|
self.device = device
|
||||||
self.param = param
|
self.param = param
|
||||||
|
self.maxage = maxage
|
||||||
|
|
||||||
class ReadParamReply(Reply):
|
class ReadParamReply(Reply):
|
||||||
pars = ['device', 'param', 'value', 'timestamp', 'error', 'unit']
|
pars = ['device', 'param', 'value', 'timestamp', 'error', 'unit']
|
||||||
@ -77,7 +106,8 @@ class WriteParamRequest(Request):
|
|||||||
|
|
||||||
class WriteParamReply(Reply):
|
class WriteParamReply(Reply):
|
||||||
pars = ['device', 'param', 'readback_value', 'timestamp', 'error', 'unit']
|
pars = ['device', 'param', 'readback_value', 'timestamp', 'error', 'unit']
|
||||||
def __init__(self, device, param, readback_value, timestamp=0, error=0, unit=None):
|
def __init__(self, device, param, readback_value, timestamp=0, error=0,
|
||||||
|
unit=None):
|
||||||
self.device = device
|
self.device = device
|
||||||
self.param = param
|
self.param = param
|
||||||
self.readback_value = readback_value
|
self.readback_value = readback_value
|
||||||
@ -121,7 +151,7 @@ class ActivateFeatureReply(Reply):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
Features = [
|
FEATURES = [
|
||||||
'Feature1',
|
'Feature1',
|
||||||
'Feature2',
|
'Feature2',
|
||||||
'Feature3',
|
'Feature3',
|
||||||
@ -176,9 +206,76 @@ class InvalidParamValueErrorReply(ErrorReply):
|
|||||||
self.param = param
|
self.param = param
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
class attrdict(dict):
|
class MessageHandler(object):
|
||||||
def __getattr__(self, key):
|
"""puts meaning to the request objects"""
|
||||||
return self[key]
|
def handle_ListDevices(self, msgargs):
|
||||||
|
return ListDevicesReply(self.listDevices())
|
||||||
|
|
||||||
|
def handle_ListDeviceParams(self, msgargs):
|
||||||
|
devobj = self.getDevice(msgargs.device)
|
||||||
|
if devobj:
|
||||||
|
return ListDeviceParamsReply(msgargs.device,
|
||||||
|
get_device_pars(devobj))
|
||||||
|
else:
|
||||||
|
return NoSuchDeviceErrorReply(msgargs.device)
|
||||||
|
|
||||||
|
def handle_ReadValue(self, msgargs):
|
||||||
|
devobj = self.getDevice(msgargs.device)
|
||||||
|
if devobj:
|
||||||
|
return ReadValueReply(msgargs.device, devobj.read_value(),
|
||||||
|
timestamp=time.time())
|
||||||
|
else:
|
||||||
|
return NoSuchDeviceErrorReply(msgargs.device)
|
||||||
|
|
||||||
|
def handle_ReadParam(self, msgargs):
|
||||||
|
devobj = self.getDevice(msgargs.device)
|
||||||
|
if devobj:
|
||||||
|
readfunc = getattr(devobj, 'read_%s' % msgargs.param, None)
|
||||||
|
if readfunc:
|
||||||
|
return ReadParamReply(msgargs.device, msgargs.param,
|
||||||
|
readfunc(), timestamp=time.time())
|
||||||
|
else:
|
||||||
|
return NoSuchParamErrorReply(msgargs.device, msgargs.param)
|
||||||
|
else:
|
||||||
|
return NoSuchDeviceErrorReply(msgargs.device)
|
||||||
|
|
||||||
|
def handle_WriteParam(self, msgargs):
|
||||||
|
devobj = self.getDevice(msgargs.device)
|
||||||
|
if devobj:
|
||||||
|
writefunc = getattr(devobj, 'write_%s' % msgargs.param, None)
|
||||||
|
if writefunc:
|
||||||
|
readbackvalue = writefunc(msgargs.value) or msgargs.value
|
||||||
|
return WriteParamReply(msgargs.device, msgargs.param,
|
||||||
|
readbackvalue,
|
||||||
|
timestamp=time.time())
|
||||||
|
else:
|
||||||
|
if getattr(devobj, 'read_%s' % msgargs.param, None):
|
||||||
|
return ParamReadonlyErrorReply(msgargs.device,
|
||||||
|
msgargs.param)
|
||||||
|
else:
|
||||||
|
return NoSuchParamErrorReply(msgargs.device,
|
||||||
|
msgargs.param)
|
||||||
|
else:
|
||||||
|
return NoSuchDeviceErrorReply(msgargs.device)
|
||||||
|
|
||||||
|
def handle_RequestAsyncData(self, msgargs):
|
||||||
|
return ErrorReply('AsyncData is not (yet) supported')
|
||||||
|
|
||||||
|
def handle_ListOfFeatures(self, msgargs):
|
||||||
|
# no features supported (yet)
|
||||||
|
return ListOfFeaturesReply([])
|
||||||
|
|
||||||
|
def handle_ActivateFeature(self, msgargs):
|
||||||
|
return ErrorReply('Features are not (yet) supported')
|
||||||
|
|
||||||
|
def unhandled(self, msgname, msgargs):
|
||||||
|
"""handler for unhandled Messages
|
||||||
|
|
||||||
|
(no handle_<messagename> method was defined)
|
||||||
|
"""
|
||||||
|
self.log.error('IGN: got unhandled request %s' % msgname)
|
||||||
|
return ErrorReply('Got Unhandled Request')
|
||||||
|
|
||||||
|
|
||||||
def parse(message):
|
def parse(message):
|
||||||
# parses a message and returns
|
# parses a message and returns
|
||||||
@ -197,7 +294,21 @@ def parse(message):
|
|||||||
return msgtype, msgname, \
|
return msgtype, msgname, \
|
||||||
attrdict([(k, getattr(message, k)) for k in message.pars])
|
attrdict([(k, getattr(message, k)) for k in message.pars])
|
||||||
|
|
||||||
|
__ALL__ = ['ErrorReply',
|
||||||
|
'NoSuchDeviceErrorReply', 'NoSuchParamErrorReply'
|
||||||
|
'ParamReadonlyErrorReply', 'UnsupportedFeatureErrorReply',
|
||||||
|
'NoSuchCommandErrorReply', 'CommandFailedErrorReply',
|
||||||
|
'InvalidParamValueErrorReply',
|
||||||
|
'Reply',
|
||||||
|
'ListDevicesReply', 'ListDeviceParamsReply', 'ReadValueReply',
|
||||||
|
'ReadParamReply', 'WriteParamReply', 'RequestAsyncDataReply',
|
||||||
|
'AsyncDataUnit', 'ListOfFeaturesReply', 'ActivateFeatureReply',
|
||||||
|
'Request',
|
||||||
|
'ListDevicesRequest', 'ListDeviceParamsRequest', 'ReadValueRequest',
|
||||||
|
'ReadParamRequest', 'WriteParamRequest', 'RequestAsyncDataRequest',
|
||||||
|
'ListOfFeaturesRequest', 'ActivateFeatureRequest',
|
||||||
|
'parse', 'MessageHandler',
|
||||||
|
]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print "minimal testing: transport"
|
print "minimal testing: transport"
|
||||||
@ -217,7 +328,7 @@ if __name__ == '__main__':
|
|||||||
ReadValueReply('device2', 3.1415),
|
ReadValueReply('device2', 3.1415),
|
||||||
ReadParamReply('device1', 'param2', 2.718),
|
ReadParamReply('device1', 'param2', 2.718),
|
||||||
WriteParamReply('device1', 'param2', 2.718),
|
WriteParamReply('device1', 'param2', 2.718),
|
||||||
RequestAsyncDataReply('device1', 'XXX: what to put here?'),
|
RequestAsyncDataReply('device1', '?what to put here?'),
|
||||||
AsyncDataUnit('device1', 'param2', 2.718),
|
AsyncDataUnit('device1', 'param2', 2.718),
|
||||||
ListOfFeaturesReply('feature1', 'feature2'),
|
ListOfFeaturesReply('feature1', 'feature2'),
|
||||||
ActivateFeatureReply(),
|
ActivateFeatureReply(),
|
||||||
|
119
src/server.py
119
src/server.py
@ -1,12 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
|
||||||
|
"""Define basic SECoP DeviceServer"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from messages import *
|
from messages import parse, ListDevicesRequest, ListDeviceParamsRequest, \
|
||||||
from device import *
|
ReadParamRequest, ErrorReply, MessageHandler
|
||||||
|
|
||||||
|
|
||||||
class DeviceServer(object):
|
|
||||||
|
class DeviceServer(MessageHandler):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
self.log = logging
|
self.log = logging
|
||||||
@ -25,20 +49,33 @@ class DeviceServer(object):
|
|||||||
|
|
||||||
def unRegisterDevice(self, device_obj_or_name):
|
def unRegisterDevice(self, device_obj_or_name):
|
||||||
if not device_obj_or_name in self._devices:
|
if not device_obj_or_name in self._devices:
|
||||||
self.log.error('IGN: Device %r not registered!' % device_obj_or_name)
|
self.log.error('IGN: Device %r not registered!' %
|
||||||
|
device_obj_or_name)
|
||||||
else:
|
else:
|
||||||
del self._devices[device_obj_or_name]
|
del self._devices[device_obj_or_name]
|
||||||
# may need to do more
|
# may need to do more
|
||||||
|
|
||||||
|
def getDevice(self, devname):
|
||||||
|
"""returns the requested deviceObj or None"""
|
||||||
|
devobj = self._devices.get(devname, None)
|
||||||
|
return devobj
|
||||||
|
|
||||||
|
def listDevices(self):
|
||||||
|
return list(self._devices.keys())
|
||||||
|
|
||||||
def handle(self, msg):
|
def handle(self, msg):
|
||||||
# server got a message, handle it
|
# server got a message, handle it
|
||||||
msgtype, msgname, msgargs = parse(msg)
|
msgtype, msgname, msgargs = parse(msg)
|
||||||
if msgtype != 'request':
|
if msgtype != 'request':
|
||||||
self.log.error('IGN: Server only handles request, but got %s/%s!' % (msgtype, msgname))
|
self.log.error('IGN: Server only handles request, but got %s/%s!' %
|
||||||
|
(msgtype, msgname))
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.log.info('handling message %s with %r' % (msgname, msgargs))
|
self.log.info('handling message %s with %r' % (msgname, msgargs))
|
||||||
res = self._handle(msgname, msgargs)
|
handler = getattr(self, 'handle_%s' * msgname, None)
|
||||||
|
if handler is None:
|
||||||
|
handler = self.unhandled
|
||||||
|
res = handler(msgargs)
|
||||||
self.log.info('replying with %r' % res)
|
self.log.info('replying with %r' % res)
|
||||||
return res
|
return res
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@ -46,58 +83,9 @@ class DeviceServer(object):
|
|||||||
self.log.info('replying with %r' % res)
|
self.log.info('replying with %r' % res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _handle(self, msgname, msgargs):
|
|
||||||
# check all supported Requests, act and return reply
|
|
||||||
self.log.debug('handling request %r' % msgname)
|
|
||||||
if msgname == 'ListDevices':
|
|
||||||
return ListDevicesReply(list(self._devices.keys()))
|
|
||||||
elif msgname == 'ListDeviceParams':
|
|
||||||
devobj = self._devices.get(msgargs.device, None)
|
|
||||||
if devobj:
|
|
||||||
return ListDeviceParamsReply(msgargs.device, get_device_pars(devobj))
|
|
||||||
else:
|
|
||||||
return NoSuchDeviceErrorReply(msgargs.device)
|
|
||||||
elif msgname == 'ReadValue':
|
|
||||||
devobj = self._devices.get(msgargs.device, None)
|
|
||||||
if devobj:
|
|
||||||
return ReadValueReply(msgargs.device, devobj.read_value(), timestamp=time.time())
|
|
||||||
else:
|
|
||||||
return NoSuchDeviceErrorReply(msgargs.device)
|
|
||||||
elif msgname == 'ReadParam':
|
|
||||||
devobj = self._devices.get(msgargs.device, None)
|
|
||||||
if devobj:
|
|
||||||
readfunc = getattr(devobj, 'read_%s' % msgargs.param, None)
|
|
||||||
if readfunc:
|
|
||||||
return ReadParamReply(msgargs.device, msgargs.param, readfunc(), timestamp=time.time())
|
|
||||||
else:
|
|
||||||
return NoSuchParamErrorReply(msgargs.device, msgargs.param)
|
|
||||||
else:
|
|
||||||
return NoSuchDeviceErrorReply(msgargs.device)
|
|
||||||
elif msgname == 'WriteParam':
|
|
||||||
devobj = self._devices.get(msgargs.device, None)
|
|
||||||
if devobj:
|
|
||||||
writefunc = getattr(devobj, 'write_%s' % msgargs.param, None)
|
|
||||||
if writefunc:
|
|
||||||
return WriteParamReply(msgargs.device, msgargs.param, writefunc(msgargs.value) or msgargs.value, timestamp=time.time())
|
|
||||||
else:
|
|
||||||
if getattr(devobj, 'read_%s' % msgargs.param, None):
|
|
||||||
return ParamReadonlyErrorReply(msgargs.device, msgargs.param)
|
|
||||||
else:
|
|
||||||
return NoSuchParamErrorReply(msgargs.device, msgargs.param)
|
|
||||||
else:
|
|
||||||
|
|
||||||
return NoSuchDeviceErrorReply(msgargs.device)
|
|
||||||
elif msgname == 'RequestAsyncData':
|
|
||||||
return ErrorReply('AsyncData is not (yet) supported')
|
|
||||||
elif msgname == 'ListOfFeatures':
|
|
||||||
return ListOfFeaturesReply([])
|
|
||||||
elif msgname == 'ActivateFeature':
|
|
||||||
return ErrorReply('Features are not (yet) supported')
|
|
||||||
else:
|
|
||||||
self.log.error('IGN: got unhandled request %s' % msgname)
|
|
||||||
return ErrorReply('Got Unhandled Request')
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from device import Driveable, status
|
||||||
class TestDevice(Driveable):
|
class TestDevice(Driveable):
|
||||||
name = 'Unset'
|
name = 'Unset'
|
||||||
unit = 'Oinks'
|
unit = 'Oinks'
|
||||||
@ -112,8 +100,8 @@ class TestDevice(Driveable):
|
|||||||
raise KeyError()
|
raise KeyError()
|
||||||
def read_none(self):
|
def read_none(self):
|
||||||
pass
|
pass
|
||||||
# def read_NotImplemented(self):
|
def read_NotImplemented(self):
|
||||||
# raise NotImplemented()
|
raise NotImplementedError('funny errors should be transported')
|
||||||
def do_wait(self):
|
def do_wait(self):
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
def do_stop(self):
|
def do_stop(self):
|
||||||
@ -127,10 +115,8 @@ class TestDevice(Driveable):
|
|||||||
def do_add_args(self, arg1, arg2):
|
def do_add_args(self, arg1, arg2):
|
||||||
return arg1 + arg2
|
return arg1 + arg2
|
||||||
def do_return_stuff(self):
|
def do_return_stuff(self):
|
||||||
return [{a:1},(2,3)]
|
return [{'a':1}, (2, 3)]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print "minimal testing: server"
|
print "minimal testing: server"
|
||||||
srv = DeviceServer()
|
srv = DeviceServer()
|
||||||
srv.registerDevice(TestDevice(), 'dev1')
|
srv.registerDevice(TestDevice(), 'dev1')
|
||||||
@ -146,12 +132,15 @@ if __name__ == '__main__':
|
|||||||
if pinfo.readonly:
|
if pinfo.readonly:
|
||||||
print ' - param %r is readonly' % p
|
print ' - param %r is readonly' % p
|
||||||
if pinfo.description:
|
if pinfo.description:
|
||||||
print ' - param %r\'s description is: %r' % (p, pinfo.description)
|
print ' - param %r\'s description is: %r' % (p,
|
||||||
|
pinfo.description)
|
||||||
else:
|
else:
|
||||||
print ' - param %r has no description' % p
|
print ' - param %r has no description' % p
|
||||||
replytype, replyname, rv = parse(srv.handle(ReadParamRequest(dev, p)))
|
replytype, replyname, rv = parse(srv.handle(ReadParamRequest(dev,
|
||||||
|
p)))
|
||||||
if replytype == 'error':
|
if replytype == 'error':
|
||||||
print ' - reading param %r resulted in error/%s' %(p, replyname)
|
print ' - reading param %r resulted in error/%s' % (p,
|
||||||
|
replyname)
|
||||||
else:
|
else:
|
||||||
print ' - param %r current value is %r' % (p, rv.value)
|
print ' - param %r current value is %r' % (p, rv.value)
|
||||||
print ' - param %r current unit is %r' % (p, rv.unit)
|
print ' - param %r current unit is %r' % (p, rv.unit)
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
import threading
|
|
||||||
import SocketServer
|
import SocketServer
|
||||||
try:
|
try:
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
@ -78,8 +77,9 @@ class SECoPClient(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
if not self._socket:
|
if not self._socket:
|
||||||
raise Exception('%r is not connected!' % self)
|
raise Exception('%r is not connected!' % self)
|
||||||
self._socket.close(socket.SH_RDONLY)
|
self._socket.shutdown(socket.SHUT_WR)
|
||||||
self._socket.close(socket.SH_RDWR)
|
self._socket.shutdown(socket.SHUT_RDWR)
|
||||||
|
self._socket.close()
|
||||||
self._socket = None
|
self._socket = None
|
||||||
|
|
||||||
def _sendRequest(self, request):
|
def _sendRequest(self, request):
|
||||||
@ -93,7 +93,7 @@ class SECoPClient(object):
|
|||||||
rawdata = ''
|
rawdata = ''
|
||||||
while True:
|
while True:
|
||||||
data = self._socket.recv(MAX_MESSAGE_SIZE)
|
data = self._socket.recv(MAX_MESSAGE_SIZE)
|
||||||
if not(data):
|
if not data:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
# XXX: needs timeout mechanism!
|
# XXX: needs timeout mechanism!
|
||||||
continue
|
continue
|
||||||
@ -112,11 +112,11 @@ class SECoPRequestHandler(SocketServer.BaseRequestHandler):
|
|||||||
def handle(self):
|
def handle(self):
|
||||||
"""handle a new tcp-connection"""
|
"""handle a new tcp-connection"""
|
||||||
# self.client_address
|
# self.client_address
|
||||||
socket = self.request
|
mysocket = self.request
|
||||||
frame = ''
|
frame = ''
|
||||||
# start serving
|
# start serving
|
||||||
while True:
|
while True:
|
||||||
_frame = socket.recv(MAX_MESSAGE_SIZE)
|
_frame = mysocket.recv(MAX_MESSAGE_SIZE)
|
||||||
if not _frame:
|
if not _frame:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
continue
|
||||||
@ -125,7 +125,7 @@ class SECoPRequestHandler(SocketServer.BaseRequestHandler):
|
|||||||
if msg:
|
if msg:
|
||||||
requestObj = decodeMessage(msg)
|
requestObj = decodeMessage(msg)
|
||||||
replyObj = self.handle_request(requestObj)
|
replyObj = self.handle_request(requestObj)
|
||||||
self.send(encodeMessageFrame(encodeMessage(replyObj)))
|
mysocket.send(encodeMessageFrame(encodeMessage(replyObj)))
|
||||||
frame = ''
|
frame = ''
|
||||||
|
|
||||||
def handle_request(self, requestObj):
|
def handle_request(self, requestObj):
|
||||||
@ -138,6 +138,7 @@ class SECoPServer(SocketServer.ThreadingTCPServer, DeviceServer):
|
|||||||
daemon_threads = False
|
daemon_threads = False
|
||||||
|
|
||||||
def startup_server():
|
def startup_server():
|
||||||
srv = SECoPServer(('localhost', DEF_PORT), SECoPRequestHandler, bind_and_activate=True)
|
srv = SECoPServer(('localhost', DEF_PORT), SECoPRequestHandler,
|
||||||
|
bind_and_activate=True)
|
||||||
srv.serve_forever()
|
srv.serve_forever()
|
||||||
srv.server_close()
|
srv.server_close()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user