Change-Id: I72abe9b4c2deb08e58ce69786f853ccc4b385a5d
This commit is contained in:
Enrico Faulhaber 2017-09-11 15:22:33 +02:00
parent 9a402857f3
commit 357056d478
12 changed files with 71 additions and 34 deletions

View File

@ -242,11 +242,11 @@ merge datatype and validator:
-----------------------------
* ["enum", {<number_value>:<json_string>}]
* ["int"] or ["int", <lowest_allowed_value>, <highest_allowed_value>]
* ["blob"] or ["blob", <minimum_size_in_bytes or 0>, <maximum_size_in_bytes>]
* ["double"] or ["double", <lowest_allowed_value>, <highest_allowed_value>]
* ["string"] or ["string", <maximum_allowed_length>] or ["string", <min_size>, <max_size>]
* ["bool"]
* ["array", <basic_data_type>] or ["array", <dtype>, <min_elements>, <max_elements>]
* ["blob", <maximum_size_in_bytes>] or ["blob", <minimum_size_in_bytes>, <maximum_size_in_bytes>]
* ["string", <maximum_allowed_length>] or ["string", <min_size>, <max_size>]
* ["array", <basic_data_type>, <max_elements>] or ["array", <dtype>, <min_elements>, <max_elements>]
* ["tuple", [ <list_of_dtypes ]]
* ["struct", { <name_of_component_as_json_string>:<dtype>}]
@ -346,3 +346,17 @@ heartbeat
* if the client receives no pong within 3s it may close the connection
* later discussions showed, that the ping/pong should stay untouched and the keepalive time should be (de-)activated by a special message instead. Also the 'connection specific settings' from earlier drafts may be resurrected for this....
11.9.2017
=========
merge datatype and validator:
-----------------------------
* enum, int, double, bool, tuple, struct as before
* ["blob", <maximum_size_in_bytes>] or ["blob", <maximum_size_in_bytes>, <minimum_size_in_bytes>]
* ["string", <maximum_allowed_length>] or ["string", <max_size_in_bytes>, <minimum_size_in_bytes>]
* ["array", <basic_data_type>, <max_elements>] or ["array", <dtype>, <max_elements>, <min_elements>]
interface_class
---------------
* Drivable, Writable, Readable, Module (first character uppercase, no middle 'e')

View File

@ -64,7 +64,7 @@ vorerst folgende Festlegung:
],
"group": "very important/stuff",
"implementation": "secop.devices.cryo.Cryostat",
"interfaces": ["Driveable", "Readable", "Device"],
"interfaces": ["Drivable", "Readable", "Device"],
"parameters": ["status", {"readonly": true,
"datatype": ["tuple", ["enum", {"unknown":-1,"idle":100, "warn":200, "unstable":250, "busy":300,"error":400}], "string"],
"description": "current status of the device"

View File

@ -38,7 +38,7 @@
## Testsuite ##
* embedded tests inside the actual files grow difficult to maintain
=> need a testsuite (nose+pylint?)
=> need a testsuite (pytest)
## docu ##
@ -48,3 +48,8 @@
## transfer of blobs via json ##
* use base64

View File

@ -312,7 +312,10 @@ class Client(object):
if modname in self._cache:
if pname in self._cache:
previous = self._cache[modname][pname]
self._cache.setdefault(modname, {})[pname] = Value(*data)
if data:
self._cache.setdefault(modname, {})[pname] = Value(*data)
else:
self.log.warning('got malformed answer! (spec data)' % (spec, data))
# self.log.info('cache: %s:%s=%r (was: %s)', modname, pname, data, previous)
if spec in self.callbacks:
for func in self.callbacks[spec]:
@ -537,7 +540,7 @@ class Client(object):
return self.describing_data['modules'][module]['properties']
def getModuleBaseClass(self, module):
return self.getModuleProperties(module)['interface']
return self.getModuleProperties(module)['interface_class']
def getCommands(self, module):
return self.describing_data['modules'][module]['commands']

View File

@ -131,8 +131,8 @@ class NodeCtrl(QWidget):
row = 0
for modname in sorted(self._node.modules):
modprops = self._node.getModuleProperties(modname)
baseclass = modprops['interface']
description = modprops['interface']
interfaces = modprops['interface_class']
description = modprops['description']
unit = self._node.getProperties(modname, 'value').get('unit', '')
if unit:
@ -142,12 +142,12 @@ class NodeCtrl(QWidget):
label = QLabel(labelstr)
label.setFont(labelfont)
if baseclass == 'Driveable':
widget = DriveableWidget(self._node, modname, self)
elif baseclass == 'Readable':
if 'Drivable' in interfaces:
widget = DrivableWidget(self._node, modname, self)
elif 'Readable' in interfaces:
widget = ReadableWidget(self._node, modname, self)
else:
widget = QLabel('Unsupported Interfaceclass %r' % baseclass)
widget = QLabel('Unsupported Interfaceclasses %r' % interfaces)
if description:
widget.setToolTip(description)
@ -159,6 +159,7 @@ class NodeCtrl(QWidget):
self._moduleWidgets.extend((label, widget))
layout.setRowStretch(row, 1)
class ReadableWidget(QWidget):
def __init__(self, node, module, parent=None):
@ -166,6 +167,8 @@ class ReadableWidget(QWidget):
self._node = node
self._module = module
self._status_type = self._node.getProperties(self._module, 'status').get('datatype')
params = self._node.getProperties(self._module, 'value')
datatype = params.get('datatype', StringType())
self._is_enum = isinstance(datatype, EnumType)
@ -206,7 +209,10 @@ class ReadableWidget(QWidget):
# XXX: also connect update_status signal to LineEdit ??
def update_status(self, status, qualifiers={}):
self.statusLineEdit.setText(str(status))
display_string = self._status_type.subtypes[0].entries.get(status[0])
if status[1]:
display_string += ':' + status[1]
self.statusLineEdit.setText(display_string)
# may change meaning of cmdPushButton
def _init_current_widgets(self):
@ -241,7 +247,7 @@ class ReadableWidget(QWidget):
self.update_target(*value)
class DriveableWidget(ReadableWidget):
class DrivableWidget(ReadableWidget):
def _init_target_widgets(self):
params = self._node.getProperties(self._module, 'target')

View File

@ -40,6 +40,9 @@
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: lightgrey;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -67,6 +70,12 @@
</item>
<item row="0" column="0" colspan="3">
<widget class="QLineEdit" name="statusLineEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color: lightgrey;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>

View File

@ -25,13 +25,13 @@ import time
import random
import threading
from secop.modules import Driveable, CMD, PARAM
from secop.modules import Drivable, CMD, PARAM
from secop.protocol import status
from secop.datatypes import FloatRange, EnumType, TupleOf
from secop.lib import clamp, mkthread
class CryoBase(Driveable):
class CryoBase(Drivable):
pass

View File

@ -24,12 +24,12 @@ import time
import random
import threading
from secop.modules import Readable, Driveable, PARAM
from secop.modules import Readable, Drivable, PARAM
from secop.datatypes import EnumType, FloatRange, IntRange, ArrayOf, StringType, TupleOf, StructOf, BoolType
from secop.protocol import status
class Switch(Driveable):
class Switch(Drivable):
"""switch it on or off....
"""
PARAMS = {
@ -94,7 +94,7 @@ class Switch(Driveable):
return info
class MagneticField(Driveable):
class MagneticField(Drivable):
"""a liquid magnet
"""
PARAMS = {
@ -194,7 +194,7 @@ class CoilTemp(Readable):
return round(2.3 + random.random(), 3)
class SampleTemp(Driveable):
class SampleTemp(Drivable):
"""a sample temperature
"""
PARAMS = {

View File

@ -22,7 +22,7 @@
import random
from secop.modules import Readable, Driveable, PARAM
from secop.modules import Readable, Drivable, PARAM
from secop.datatypes import FloatRange, StringType
@ -37,7 +37,7 @@ class LN2(Readable):
return round(100 * random.random(), 1)
class Heater(Driveable):
class Heater(Drivable):
"""Just a driveable.
class name indicates it to be some heating element,
@ -56,7 +56,7 @@ class Heater(Driveable):
pass
class Temp(Driveable):
class Temp(Drivable):
"""Just a driveable.
class name indicates it to be some temperature controller,

View File

@ -23,7 +23,7 @@
import random
from secop.datatypes import EnumType, TupleOf, FloatRange, get_datatype, StringType
from secop.modules import Readable, Device, Driveable, PARAM
from secop.modules import Readable, Device, Drivable, PARAM
from secop.protocol import status
try:
@ -58,7 +58,7 @@ except ImportError:
class EpicsReadable(Readable):
"""EpicsDriveable handles a Driveable interfacing to EPICS v4"""
"""EpicsDrivable handles a Drivable interfacing to EPICS v4"""
# Commmon PARAMS for all EPICS devices
PARAMS = {
'value': PARAM('EPICS generic value',
@ -117,8 +117,8 @@ class EpicsReadable(Readable):
return (status.OK, 'no pv set')
class EpicsDriveable(Driveable):
"""EpicsDriveable handles a Driveable interfacing to EPICS v4"""
class EpicsDrivable(Drivable):
"""EpicsDrivable handles a Drivable interfacing to EPICS v4"""
# Commmon PARAMS for all EPICS devices
PARAMS = {
'target': PARAM('EPICS generic target', datatype=FloatRange(),
@ -193,7 +193,7 @@ class EpicsDriveable(Driveable):
# features are agreed upon
class EpicsTempCtrl(EpicsDriveable):
class EpicsTempCtrl(EpicsDrivable):
PARAMS = {
# TODO: restrict possible values with oneof datatype

View File

@ -33,10 +33,10 @@ from secop.lib.sequence import SequencerMixin, Step
from secop.protocol import status
from secop.datatypes import *
from secop.errors import SECoPServerError, ConfigError, ProgrammingError, CommunicationError, HardwareError, DisabledError
from secop.modules import PARAM, CMD, OVERRIDE, Device, Readable, Driveable
from secop.modules import PARAM, CMD, OVERRIDE, Device, Readable, Drivable
class GarfieldMagnet(SequencerMixin, Driveable):
class GarfieldMagnet(SequencerMixin, Drivable):
"""Garfield Magnet
uses a polarity switch ('+' or '-') to flip polarity and an onoff switch

View File

@ -41,7 +41,7 @@ from secop.lib import lazy_property, mkthread
from secop.protocol import status
from secop.datatypes import *
from secop.errors import SECoPServerError, ConfigError, ProgrammingError, CommunicationError, HardwareError
from secop.modules import PARAM, CMD, OVERRIDE, Device, Readable, Driveable
from secop.modules import PARAM, CMD, OVERRIDE, Device, Readable, Drivable
# Only export these classes for 'from secop_mlz import *'
@ -394,7 +394,7 @@ class Sensor(AnalogInput):
self._dev.Adjust(value)
class AnalogOutput(PyTangoDevice, Driveable):
class AnalogOutput(PyTangoDevice, Drivable):
"""
The AnalogOutput handles all devices which set an analogue value.
@ -800,7 +800,7 @@ class PartialDigitalInput(NamedDigitalInput):
return value # mapping is done by datatype upon export()
class DigitalOutput(PyTangoDevice, Driveable):
class DigitalOutput(PyTangoDevice, Drivable):
"""
A devices that can set and read a digital value corresponding to a
bitfield.