diff --git a/etc/sim_mlz_amagnet.cfg b/etc/sim_mlz_amagnet.cfg new file mode 100644 index 0000000..2c3f579 --- /dev/null +++ b/etc/sim_mlz_amagnet.cfg @@ -0,0 +1,135 @@ +[equipment SIM_MLZ_amagnet(Garfield)] +description=MLZ-Amagnet + . + Water cooled magnet from ANTARES@MLZ. + . + Use module to control the magnetic field. + Don't forget to select symmetry first (can be moved only at zero field!). + . + Monitor T1..T4 (Coil temps), if they get to hot, field will ramp down! + . + In case of Problems, contact the ANTARES people at MLZ. + +visibility=expert +foo=bar + +[interface tcp] +interface=tcp +bindto=0.0.0.0 +bindport=10767 +# protocol to use for this interface +framing=eol +encoding=secop + +[module enable] +class=secop.simulation.SimWritable +value.datatype=["enum", {'On':1,'Off':0}] +target.datatype=["enum", {'On':1,'Off':0}] +.description='Enables to Output of the Powersupply' +.visibility='advanced' + +[module polarity] +class=secop.simulation.SimWritable +value.datatype=["enum", {'+1':1,'0':0,'-1':-1}] +target.datatype=["enum", {'+1':1,'0':0,'-1':-1}] +.description=polarity (+/-) switch + . + there is an interlock in the plc: + if there is current, switching polarity is forbidden + if polarity is short, powersupply is disabled +.visibility=advanced +#comtries = 50 + + +[module symmetry] +class=secop.simulation.SimWritable +value.datatype=["enum",{'symmetric':1,'short':0, 'asymmetric':-1}] +target.datatype=["enum",{'symmetric':1,'short':0, 'asymmetric':-1}] +.description=par/ser switch selecting (a)symmetric mode + . + note: on the front panel symmetric is ser, asymmetric is par +.visibility=advanced +value.default = 'symmetric' +value.value = 'symmetric' + +[module T1] +class=secop.simulation.SimReadable +.description=Temperature1 of the coils system +#warnlimits=(0, 50) +value.unit='degC' +value.default = 23.45 + +[module T2] +class=secop.simulation.SimReadable +.description=Temperature2 of the coils system +#warnlimits=(0, 50) +value.unit='degC' +value.default = 23.45 + +[module T3] +class=secop.simulation.SimReadable +.description=Temperature3 of the coils system +#warnlimits=(0, 50) +value.unit='degC' +value.default = 23.45 + +[module T4] +class=secop.simulation.SimReadable +.description=Temperature4 of the coils system +#warnlimits=(0, 50) +value.unit='degC' +value.default = 23.45 + +[module currentsource] +class=secop.simulation.SimDrivable +.description=Device for the magnet power supply (current mode) +abslimits=(0,200) +speed=1 +ramp=60 +precision=0.02 +current=0 +voltage=10 +#unit=A +.visibility=advanced +.extra_params = abslimits, speed, ramp, precision, current, voltage, window +abslimits.datatype = ["tuple", [["double"], ["double"]]] +abslimits.value = (0, 200) +abslimits.default = (0, 200) +abslimits.unit = 'A' +speed.datatype = ["double", 0, 10] +speed.default = 10 +speed.unit = 'A/s' +ramp.datatype = ["double", 0, 600] +ramp.default = 600 +ramp.unit = 'A/min' +precision.datatype = ["double"] +precision.default = 0.1 +precision.unit = 'A' +current.datatype = ["double", 0, 200] +current.default = 0 +current.unit = 'A' +voltage.datatype = ["double", 0, 10] +voltage.default = 0 +voltage.unit = 'V' +window.datatype = ["double", 0, 120] +window.default = 10 +window.unit = 's' + +[module mf] +class=secop_mlz.amagnet.GarfieldMagnet +.description=magnetic field module, handling polarity switching and stuff +subdev_currentsource=currentsource +subdev_enable=enable +subdev_polswitch=polarity +subdev_symmetry=symmetry +target.unit='T' +value.unit='T' +userlimits=(-0.35, 0.35) +calibrationtable={'symmetric':[0.00186517, 0.0431937, -0.185956, 0.0599757, 0.194042], + 'short': [0.0, 0.0, 0.0, 0.0, 0.0], + 'asymmetric':[0.00136154, 0.027454, -0.120951, 0.0495289, 0.110689]} +.meaning=The magnetic field +.priority=100 +.visibility=user + +abslimits.default=-0.4,0.4 diff --git a/secop/datatypes.py b/secop/datatypes.py index 3c4f371..84ab99d 100644 --- a/secop/datatypes.py +++ b/secop/datatypes.py @@ -605,7 +605,7 @@ DATATYPES = dict( enum=lambda kwds: EnumType('', **kwds), struct=lambda named_subtypes: StructOf( **dict((n, get_datatype(t)) for n, t in list(named_subtypes.items()))), - command=lambda args, res: CommandType(map(get_datatype, args), res), + command=lambda args, res: CommandType(map(get_datatype, args), get_datatype(res)), ) diff --git a/secop/features.py b/secop/features.py index 7c41ab9..c996f1f 100644 --- a/secop/features.py +++ b/secop/features.py @@ -71,7 +71,7 @@ class Has_PIDTable(HAS_PID): class HAS_Persistent(Feature): #extra_Status { - # 'decoupled' : Status.OK+1, # to be discussed. + # 'decoupled' : Status.IDLE+1, # to be discussed. # 'coupling' : Status.BUSY+1, # to be discussed. # 'coupled' : Status.BUSY+2, # to be discussed. # 'decoupling' : Status.BUSY+3, # to be discussed. diff --git a/secop/gui/modulectrl.py b/secop/gui/modulectrl.py index 774f47e..405e143 100644 --- a/secop/gui/modulectrl.py +++ b/secop/gui/modulectrl.py @@ -78,7 +78,7 @@ def showCommandResultDialog(command, args, result, extras=''): m = QMessageBox() if not args: args = '' - m.setText('calling: %s(%s)\nyielded: %s\nqualifiers: %s' % + m.setText('calling: %s(%s)\nyielded: %r\nqualifiers: %s' % (command, args, result, extras)) m.exec_() diff --git a/secop/lib/sequence.py b/secop/lib/sequence.py index debcab9..3c28a40 100644 --- a/secop/lib/sequence.py +++ b/secop/lib/sequence.py @@ -26,7 +26,6 @@ from time import sleep from secop.lib import mkthread -from secop.protocol import status from secop.errors import IsBusyError @@ -128,18 +127,18 @@ class SequencerMixin(object): def read_status(self, maxage=0): if self.seq_is_alive(): - return status.BUSY, u'moving: ' + self._seq_phase + return self.Status.BUSY, u'moving: ' + self._seq_phase elif self._seq_error: if self._seq_fault_on_error: - return status.ERROR, self._seq_error - return status.WARN, self._seq_error + return self.Status.ERROR, self._seq_error + return self.Status.WARN, self._seq_error elif self._seq_stopped: if self._seq_fault_on_stop: - return status.ERROR, self._seq_stopped - return status.WARN, self._seq_stopped + return self.Status.ERROR, self._seq_stopped + return self.Status.WARN, self._seq_stopped if hasattr(self, u'read_hw_status'): return self.read_hw_status(maxage) - return status.OK, u'' + return self.Status.IDLE, u'' def do_stop(self): if self.seq_is_alive(): diff --git a/secop/protocol/dispatcher.py b/secop/protocol/dispatcher.py index d3d557d..9005968 100644 --- a/secop/protocol/dispatcher.py +++ b/secop/protocol/dispatcher.py @@ -192,7 +192,7 @@ class Dispatcher(object): if moduleobj is None: raise NoSuchModuleError(module=modulename) - cmdspec = moduleobj.commands.get(command, None) + cmdspec = moduleobj.accessibles.get(command, None) if cmdspec is None: raise NoSuchCommandError(module=modulename, command=command) if len(cmdspec.datatype.argtypes) != len(arguments): diff --git a/secop/protocol/interface/tcp.py b/secop/protocol/interface/tcp.py index f44c00b..c3df995 100644 --- a/secop/protocol/interface/tcp.py +++ b/secop/protocol/interface/tcp.py @@ -31,6 +31,7 @@ except ImportError: from secop.lib import formatExtendedStack, formatException from secop.protocol.messages import HELPREPLY, Message, HelpMessage +from secop.errors import SECoPError DEF_PORT = 10767 @@ -161,6 +162,9 @@ class TCPRequestHandler(socketserver.BaseRequestHandler): msgObj = Message(*msg) msgObj.origin = origin.decode('latin-1') msgObj = serverobj.dispatcher.handle_request(self, msgObj) + except SECoPError as err: + msgObj.set_error(err.name, str(err), {'exception': formatException(), + 'traceback': formatExtendedStack()}) except Exception as err: # create Error Obj instead msgObj.set_error(u'Internal', str(err), {'exception': formatException(), @@ -179,7 +183,7 @@ class TCPRequestHandler(socketserver.BaseRequestHandler): if data: self._queue.append(data) else: - self.log.error('should asynq_queue %s' % data) + self.log.error('should async_queue empty data!') def queue_reply(self, data): """called by dispatcher to queue (sync) replies""" @@ -187,7 +191,7 @@ class TCPRequestHandler(socketserver.BaseRequestHandler): if data: self._queue.appendleft(data) else: - self.log.error('should queue %s' % data) + self.log.error('should queue empty data!') def finish(self): """called when handle() terminates, i.e. the socket closed""" @@ -215,16 +219,8 @@ class TCPServer(socketserver.ThreadingTCPServer): if ':' in bindto: bindto, _port = bindto.rsplit(':') portnum = int(_port) - # tcp is a byte stream, so we need Framers (to get frames) - # and encoders (to en/decode messages from frames) - interfaceopts.pop('framing') # HACK - interfaceopts.pop('encoding') # HACK -# self.framingCLS = FRAMERS[interfaceopts.pop('framing', 'none')] -# self.encodingCLS = ENCODERS[interfaceopts.pop('encoding', 'pickle')] self.log.info("TCPServer binding to %s:%d" % (bindto, portnum)) -# self.log.debug("TCPServer using framing=%s" % self.framingCLS.__name__) -# self.log.debug("TCPServer using encoding=%s" % self.encodingCLS.__name__) socketserver.ThreadingTCPServer.__init__( self, (bindto, portnum), TCPRequestHandler, bind_and_activate=True) self.log.info("TCPServer initiated") diff --git a/secop/protocol/messages.py b/secop/protocol/messages.py index f27fd65..2a98d77 100644 --- a/secop/protocol/messages.py +++ b/secop/protocol/messages.py @@ -29,7 +29,7 @@ from secop.protocol.errors import EXCEPTIONS IDENTREQUEST = u'*IDN?' # literal # literal! first part is fixed! -IDENTREPLY = u'SINE2020&ISSE,SECoP,V2018-02-13,rc2' +IDENTREPLY = u'SINE2020&ISSE,SECoP,V2018-06-16,rc1' DESCRIPTIONREQUEST = u'describe' # literal DESCRIPTIONREPLY = u'describing' # + +json diff --git a/secop/simulation.py b/secop/simulation.py index 6ae0a8e..85225a8 100644 --- a/secop/simulation.py +++ b/secop/simulation.py @@ -98,6 +98,8 @@ class SimWritable(SimBase, Writable): def write_target(self, value): self.value = value + def _hw_wait(self): + pass class SimDrivable(SimBase, Drivable): def __init__(self, logger, cfgdict, devname, dispatcher): @@ -126,4 +128,8 @@ class SimDrivable(SimBase, Drivable): else: self._value = self.target sleep(0.3) - self.status = self.Status.OK, '' + self.status = self.Status.IDLE, '' + + def _hw_wait(self): + while self.status[0] == self.Status.BUSY: + sleep(0.3) diff --git a/secop_mlz/amagnet.py b/secop_mlz/amagnet.py index 720b325..793ee65 100644 --- a/secop_mlz/amagnet.py +++ b/secop_mlz/amagnet.py @@ -144,7 +144,10 @@ class GarfieldMagnet(SequencerMixin, Drivable): def read_calibration(self, maxage=0): try: - return self.calibrationtable[self._symmetry.value] + try: + return self.calibrationtable[self._symmetry.value] + except KeyError: + return self.calibrationtable[self._symmetry.value.name] except KeyError: minslope = min(entry[0] for entry in self.calibrationtable.values()) @@ -217,10 +220,10 @@ class GarfieldMagnet(SequencerMixin, Drivable): # called from SequencerMixin.read_status if no sequence is running if self._enable.value == 'Off': return self.Status.WARN, 'Disabled' - if self._enable.read_status(maxage)[0] != self.Status.OK: + if self._enable.read_status(maxage)[0] != self.Status.IDLE: return self._enable.status if self._polswitch.value in ['0', 0]: - return self.Status.OK, 'Shorted, ' + self._currentsource.status[1] + return self.Status.IDLE, 'Shorted, ' + self._currentsource.status[1] if self._symmetry.value in ['short', 0]: return self._currentsource.status[ 0], 'Shorted, ' + self._currentsource.status[1]