move handlers to messages

Change-Id: I1dd49e4d273d9fc3503757fd902767a4b1c42990
This commit is contained in:
Enrico Faulhaber 2016-06-13 17:26:10 +02:00
parent b52c2d7a60
commit e914d12096
5 changed files with 333 additions and 204 deletions

View File

@ -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

View File

@ -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

View File

@ -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(),

View File

@ -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)

View File

@ -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()