fix transport and display of node-properties
Change-Id: I35a3021768e386a5ce922c8e24128d0bc3a039be
This commit is contained in:
@ -37,10 +37,10 @@ from secop.version import get_version
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.viewcode']
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@ -179,7 +179,6 @@ texinfo_documents = [
|
||||
]
|
||||
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
@ -201,6 +200,5 @@ epub_copyright = copyright
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
@ -1,6 +1,5 @@
|
||||
[equipment]
|
||||
id=MLZ_amagnet(Garfield)
|
||||
.visibility=expert
|
||||
[equipment MLZ_amagnet(Garfield)]
|
||||
visibility=expert
|
||||
foo=bar
|
||||
|
||||
[interface tcp]
|
||||
|
@ -1,5 +1,5 @@
|
||||
[equipment]
|
||||
id=ccr12
|
||||
[node ccr12]
|
||||
description = CCR12 box of MLZ Sample environment group
|
||||
|
||||
[interface tcp]
|
||||
interface=tcp
|
||||
|
@ -1,14 +1,9 @@
|
||||
[equipment]
|
||||
[equipment cryo_7]
|
||||
# set SEC-node properties
|
||||
id=cryo_7
|
||||
description = short description
|
||||
|
||||
This is a very long description providing all the glory details in all the glory details about the stuff we are describing
|
||||
|
||||
.description = short description
|
||||
|
||||
This is a very long description providing all the glory details in all the glory details about the stuff we are describing
|
||||
|
||||
|
||||
[interface tcp]
|
||||
interface=tcp
|
||||
|
@ -1,5 +1,5 @@
|
||||
[equipment]
|
||||
id=Equipment_ID_for_demonstration
|
||||
[equipment Equipment_ID_for_demonstration]
|
||||
description = virtual modules to play around with
|
||||
|
||||
[interface tcp]
|
||||
interface=tcp
|
||||
|
@ -1,5 +1,4 @@
|
||||
[equipment]
|
||||
id=see_demo_equipment
|
||||
[equipment see_demo_equipment]
|
||||
|
||||
[client]
|
||||
connectto=0.0.0.0
|
||||
|
11
etc/test.cfg
11
etc/test.cfg
@ -1,5 +1,12 @@
|
||||
[equipment]
|
||||
id=test config
|
||||
[node test config]
|
||||
description=description of the testing sec-node
|
||||
.
|
||||
Here should be the long description.
|
||||
It can be very long.
|
||||
.
|
||||
a single . on a line gets stripped so you can make paragraphs.
|
||||
.
|
||||
These texts are supposed to be possibly very long.
|
||||
|
||||
[interface tcp]
|
||||
interface=tcp
|
||||
|
@ -317,7 +317,8 @@ class Client(object):
|
||||
if data:
|
||||
self._cache.setdefault(modname, {})[pname] = Value(*data)
|
||||
else:
|
||||
self.log.warning('got malformed answer! (spec data)' % (spec, data))
|
||||
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]:
|
||||
|
@ -340,25 +340,30 @@ class BoolType(DataType):
|
||||
|
||||
class ArrayOf(DataType):
|
||||
|
||||
def __init__(self, subtype, minsize_or_size=None, maxsize=None):
|
||||
if maxsize is None:
|
||||
maxsize = minsize_or_size
|
||||
self.minsize = minsize_or_size
|
||||
self.maxsize = maxsize
|
||||
if self.minsize is not None and self.maxsize is not None and \
|
||||
self.minsize > self.maxsize:
|
||||
raise ValueError('minsize must be less than or equal to maxsize!')
|
||||
def __init__(self, subtype, minsize=0, maxsize=None):
|
||||
if not isinstance(subtype, DataType):
|
||||
raise ValueError(
|
||||
'ArrayOf only works with DataType objs as first argument!')
|
||||
# if only one arg is given, it is maxsize!
|
||||
if minsize and not maxsize:
|
||||
maxsize = minsize
|
||||
minsize = 0
|
||||
self.as_json = ['array', subtype.as_json, maxsize]
|
||||
elif maxsize:
|
||||
self.as_json = ['array', subtype.as_json, maxsize, minsize]
|
||||
else:
|
||||
self.as_json = ['array', subtype.as_json]
|
||||
self.minsize = minsize or 0
|
||||
self.maxsize = maxsize
|
||||
self.subtype = subtype
|
||||
self.as_json = ['array', self.subtype.as_json,
|
||||
self.maxsize, self.minsize]
|
||||
if self.minsize is not None and self.minsize < 0:
|
||||
if self.maxsize is not None and self.minsize > maxsize:
|
||||
raise ValueError('minsize must be less than or equal to maxsize!')
|
||||
|
||||
if self.minsize < 0:
|
||||
raise ValueError('Minimum size must be >= 0!')
|
||||
if self.maxsize is not None and self.maxsize < 1:
|
||||
raise ValueError('Maximum size must be >= 1!')
|
||||
if self.minsize is not None and self.maxsize is not None and self.minsize > self.maxsize:
|
||||
if self.maxsize is not None and self.minsize > self.maxsize:
|
||||
raise ValueError('Maximum size must be >= Minimum size')
|
||||
|
||||
def __repr__(self):
|
||||
@ -534,10 +539,12 @@ DATATYPES = dict(
|
||||
bool=lambda: BoolType(),
|
||||
int=lambda _min=None, _max=None: IntRange(_min, _max),
|
||||
double=lambda _min=None, _max=None: FloatRange(_min, _max),
|
||||
blob=lambda _min=None, _max=None: BLOBType(_min, _max),
|
||||
string=lambda _min=None, _max=None: StringType(_min, _max),
|
||||
array=lambda subtype, _min=None, _max=None: ArrayOf(
|
||||
get_datatype(subtype), _min, _max),
|
||||
blob=lambda _max=None, _min=None: BLOBType(
|
||||
_min, _max) if _min else BLOBType(_max),
|
||||
string=lambda _max=None, _min=None: StringType(
|
||||
_min, _max) if _min else StringType(_max),
|
||||
array=lambda subtype, _max=None, _min=None: ArrayOf(
|
||||
get_datatype(subtype), _min, _max) if _min else ArrayOf(getdatatype(subtype), _min),
|
||||
tuple=lambda subtypes: TupleOf(*map(get_datatype, subtypes)),
|
||||
enum=lambda kwds: EnumType(**kwds),
|
||||
struct=lambda named_subtypes: StructOf(
|
||||
|
@ -43,7 +43,8 @@ class NodeCtrl(QWidget):
|
||||
self.contactPointLabel.setText(self._node.contactPoint)
|
||||
self.equipmentIdLabel.setText(self._node.equipmentId)
|
||||
self.protocolVersionLabel.setText(self._node.protocolVersion)
|
||||
self.nodeDescriptionLabel.setText(self._node.describingData.get('description','no description available'))
|
||||
self.nodeDescriptionLabel.setText(self._node.describingData['properties'].get(
|
||||
'description', 'no description available'))
|
||||
self._clearLog()
|
||||
|
||||
# now populate modules tab
|
||||
@ -168,7 +169,8 @@ class ReadableWidget(QWidget):
|
||||
self._node = node
|
||||
self._module = module
|
||||
|
||||
self._status_type = self._node.getProperties(self._module, 'status').get('datatype')
|
||||
self._status_type = self._node.getProperties(
|
||||
self._module, 'status').get('datatype')
|
||||
|
||||
params = self._node.getProperties(self._module, 'value')
|
||||
datatype = params.get('datatype', StringType())
|
||||
@ -198,9 +200,11 @@ class ReadableWidget(QWidget):
|
||||
if pname in params:
|
||||
return params[pname].value
|
||||
try:
|
||||
# if queried, we get the qualifiers as well, but don't want them here
|
||||
# if queried, we get the qualifiers as well, but don't want them
|
||||
# here
|
||||
import mlzlog
|
||||
mlzlog.getLogger('cached values').warn('no cached value for %s:%s' % (self._module, pname))
|
||||
mlzlog.getLogger('cached values').warn(
|
||||
'no cached value for %s:%s' % (self._module, pname))
|
||||
val = self._node.getParameter(self._module, pname)[0]
|
||||
return val
|
||||
except Exception:
|
||||
|
@ -82,109 +82,119 @@ p, li { white-space: pre-wrap; }
|
||||
<string>NodeInfo</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contact point:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="contactPointLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Equipment ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="equipmentIdLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Protocol version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="protocolVersionLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="nodeDescriptionLabel">
|
||||
<property name="text">
|
||||
<string>Description
|
||||
long line</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<widget class="QScrollArea" name="scrollArea_2">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>610</width>
|
||||
<height>413</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contact point:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="contactPointLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Equipment ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="equipmentIdLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Protocol version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="protocolVersionLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="nodeDescriptionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>aaaaaaaaaaa
|
||||
aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaa</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>label_2</zorder>
|
||||
<zorder>gridLayoutWidget</zorder>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -49,7 +49,6 @@ CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
|
||||
class lazy_property(object):
|
||||
"""A property that calculates its value only once."""
|
||||
|
||||
|
@ -49,7 +49,12 @@ from secop.lib import formatExtendedStack, formatException
|
||||
class Dispatcher(object):
|
||||
|
||||
def __init__(self, logger, options):
|
||||
self.equipment_id = options.pop('equipment_id')
|
||||
# to avoid errors, we want to eat all options here
|
||||
self.equipment_id = options['equipment_id']
|
||||
self.nodeopts = {}
|
||||
for k in list(options):
|
||||
self.nodeopts[k] = options.pop(k)
|
||||
|
||||
self.log = logger
|
||||
# map ALL modulename -> moduleobj
|
||||
self._modules = {}
|
||||
@ -227,6 +232,7 @@ class Dispatcher(object):
|
||||
result['equipment_id'] = self.equipment_id
|
||||
result['firmware'] = 'The SECoP playground'
|
||||
result['version'] = "2017.07"
|
||||
result.update(self.nodeopts)
|
||||
# XXX: what else?
|
||||
return result
|
||||
|
||||
|
@ -107,7 +107,8 @@ class Server(object):
|
||||
|
||||
deviceopts = []
|
||||
interfaceopts = []
|
||||
equipment_id = 'unknown'
|
||||
equipment_id = None
|
||||
nodeopts = []
|
||||
for section in parser.sections():
|
||||
if section.lower().startswith('device '):
|
||||
# device section
|
||||
@ -135,11 +136,30 @@ class Server(object):
|
||||
(self._cfgfile, ifname))
|
||||
# all went well so far
|
||||
interfaceopts.append([ifname, ifopts])
|
||||
if parser.has_option('equipment', 'id'):
|
||||
equipment_id = parser.get('equipment', 'id').replace(' ', '_')
|
||||
if section.lower().startswith('equipment ') or section.lower().startswith('node '):
|
||||
if equipment_id is not None:
|
||||
raise ConfigError('cfgfile %r: only one [node <id>] section allowed, found another [%s]!' % (
|
||||
self._cfgfile, section))
|
||||
# equipment/node settings
|
||||
equipment_id = section.split(' ', 1)[1].replace(' ', '_')
|
||||
nodeopts = dict(item for item in parser.items(section))
|
||||
nodeopts['equipment_id'] = equipment_id
|
||||
nodeopts['id'] = equipment_id
|
||||
# MAGIC: transform \n.\n into \n\n which are normally stripped
|
||||
# by the ini parser
|
||||
for k in nodeopts:
|
||||
v = nodeopts[k]
|
||||
while '\n.\n' in v:
|
||||
v = v.replace('\n.\n', '\n\n')
|
||||
nodeopts[k] = v
|
||||
|
||||
if equipment_id is None:
|
||||
self.log.error('Need a [node <id>] section, none found!')
|
||||
raise ConfigError(
|
||||
'cfgfile %r: need an [node <id>] option!' % (self._cfgfile))
|
||||
|
||||
self._dispatcher = self._buildObject(
|
||||
'Dispatcher', Dispatcher, dict(equipment_id=equipment_id))
|
||||
'Dispatcher', Dispatcher, nodeopts)
|
||||
self._processInterfaceOptions(interfaceopts)
|
||||
self._processModuleOptions(deviceopts)
|
||||
|
||||
|
@ -75,7 +75,8 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
default=(1.0, 0.0, 0.0, 0.0, 0.0)),
|
||||
'calibrationtable': PARAM('Map of Coefficients for calibration per symmetry setting',
|
||||
datatype=StructOf(symmetric=ArrayOf(FloatRange(), 5, 5),
|
||||
short=ArrayOf(FloatRange(), 5, 5),
|
||||
short=ArrayOf(
|
||||
FloatRange(), 5, 5),
|
||||
asymmetric=ArrayOf(FloatRange(), 5, 5)), export=False),
|
||||
}
|
||||
|
||||
|
22
setup.py
22
setup.py
@ -35,17 +35,17 @@ uidir = path.join(path.dirname(__file__), 'secop', 'gui', 'ui')
|
||||
uis = [path.join('gui', 'ui', entry) for entry in listdir(uidir)]
|
||||
|
||||
setup(
|
||||
name = 'secop-core',
|
||||
version = secop.version.get_version(),
|
||||
license = 'GPL',
|
||||
author = 'Enrico Faulhaber',
|
||||
author_email = 'enrico.faulhaber@frm2.tum.de',
|
||||
description = 'SECoP Playground core system',
|
||||
packages = find_packages(),
|
||||
package_data = {'secop': ['RELEASE-VERSION'] + uis},
|
||||
data_files = [('/etc/init.d', ['etc/secop-server'])],
|
||||
scripts = scripts,
|
||||
classifiers = [
|
||||
name='secop-core',
|
||||
version=secop.version.get_version(),
|
||||
license='GPL',
|
||||
author='Enrico Faulhaber',
|
||||
author_email='enrico.faulhaber@frm2.tum.de',
|
||||
description='SECoP Playground core system',
|
||||
packages=find_packages(),
|
||||
package_data={'secop': ['RELEASE-VERSION'] + uis},
|
||||
data_files=[('/etc/init.d', ['etc/secop-server'])],
|
||||
scripts=scripts,
|
||||
classifiers=[
|
||||
'Development Status :: 6 - Mature',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Science/Research',
|
||||
|
@ -24,14 +24,17 @@
|
||||
import pytest
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, sys.path[0]+'/..')
|
||||
sys.path.insert(0, sys.path[0] + '/..')
|
||||
|
||||
from collections import OrderedDict
|
||||
from secop.client.baseclient import Client
|
||||
|
||||
# define Test-only connection object
|
||||
|
||||
|
||||
class TestConnect(object):
|
||||
callbacks = []
|
||||
|
||||
def writeline(self, line):
|
||||
pass
|
||||
|
||||
@ -49,35 +52,37 @@ def clientobj(request):
|
||||
print (" TEARDOWN ClientObj")
|
||||
|
||||
|
||||
def test_describing_data_decode(clientobj):
|
||||
assert OrderedDict([('a',1)]) == clientobj._decode_list_to_ordereddict(['a',1])
|
||||
assert {'modules':{}, 'properties':{}} == clientobj._decode_substruct(['modules'],{})
|
||||
describing_data = {'equipment_id': 'eid',
|
||||
'modules': ['LN2', {'commands': [],
|
||||
'interfaces': ['Readable', 'Module'],
|
||||
'parameters': ['value', {'datatype': ['double'],
|
||||
'description': 'current value',
|
||||
def test_describing_data_decode(clientobj):
|
||||
assert OrderedDict(
|
||||
[('a', 1)]) == clientobj._decode_list_to_ordereddict(['a', 1])
|
||||
assert {'modules': {}, 'properties': {}
|
||||
} == clientobj._decode_substruct(['modules'], {})
|
||||
describing_data = {'equipment_id': 'eid',
|
||||
'modules': ['LN2', {'commands': [],
|
||||
'interfaces': ['Readable', 'Module'],
|
||||
'parameters': ['value', {'datatype': ['double'],
|
||||
'description': 'current value',
|
||||
'readonly': True,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
decoded_data = {'modules': {'LN2': {'commands': {},
|
||||
'parameters': {'value': {'datatype': ['double'],
|
||||
'description': 'current value',
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
decoded_data = {'modules': {'LN2': {'commands': {},
|
||||
'parameters': {'value': {'datatype': ['double'],
|
||||
'description': 'current value',
|
||||
'readonly': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'properties': {'interfaces': ['Readable', 'Module']}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'properties': {'equipment_id': 'eid',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a = clientobj._decode_substruct(['modules'], describing_data)
|
||||
for modname, module in a['modules'].items():
|
||||
a['modules'][modname] = clientobj._decode_substruct(['parameters', 'commands'], module)
|
||||
a['modules'][modname] = clientobj._decode_substruct(
|
||||
['parameters', 'commands'], module)
|
||||
assert a == decoded_data
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
import pytest
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, sys.path[0]+'/..')
|
||||
sys.path.insert(0, sys.path[0] + '/..')
|
||||
|
||||
from secop.datatypes import DataType, FloatRange, IntRange, \
|
||||
EnumType, BLOBType, StringType, BoolType, ArrayOf, TupleOf, StructOf, \
|
||||
@ -41,6 +41,7 @@ def test_DataType():
|
||||
dt.validate('')
|
||||
dt.export()
|
||||
|
||||
|
||||
def test_FloatRange():
|
||||
dt = FloatRange(-3.14, 3.14)
|
||||
assert dt.as_json == ['double', -3.14, 3.14]
|
||||
@ -52,16 +53,17 @@ def test_FloatRange():
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate([19,'X'])
|
||||
dt.validate([19, 'X'])
|
||||
dt.validate(1)
|
||||
dt.validate(0)
|
||||
assert dt.export(-2.718) == -2.718
|
||||
with pytest.raises(ValueError):
|
||||
FloatRange('x','Y')
|
||||
FloatRange('x', 'Y')
|
||||
|
||||
dt = FloatRange()
|
||||
assert dt.as_json == ['double']
|
||||
|
||||
|
||||
def test_IntRange():
|
||||
dt = IntRange(-3, 3)
|
||||
assert dt.as_json == ['int', -3, 3]
|
||||
@ -73,21 +75,22 @@ def test_IntRange():
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate([19,'X'])
|
||||
dt.validate([19, 'X'])
|
||||
dt.validate(1)
|
||||
dt.validate(0)
|
||||
with pytest.raises(ValueError):
|
||||
IntRange('xc','Yx')
|
||||
IntRange('xc', 'Yx')
|
||||
|
||||
dt = IntRange()
|
||||
assert dt.as_json == ['int']
|
||||
|
||||
|
||||
def test_EnumType():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
EnumType(1)
|
||||
with pytest.raises(ValueError):
|
||||
EnumType('a',b=0)
|
||||
EnumType('a', b=0)
|
||||
|
||||
dt = EnumType(a=3, c=7, stuff=1)
|
||||
assert dt.as_json == ['enum', dict(a=3, c=7, stuff=1)]
|
||||
@ -99,7 +102,7 @@ def test_EnumType():
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate('XX')
|
||||
with pytest.raises(TypeError):
|
||||
dt.validate([19,'X'])
|
||||
dt.validate([19, 'X'])
|
||||
|
||||
assert dt.validate('a') == 'a'
|
||||
assert dt.validate('stuff') == 'stuff'
|
||||
@ -113,8 +116,14 @@ def test_EnumType():
|
||||
with pytest.raises(ValueError):
|
||||
dt.export(2)
|
||||
|
||||
|
||||
def test_BLOBType():
|
||||
# test constructor catching illegal arguments
|
||||
dt = BLOBType()
|
||||
assert dt.as_json == ['blob']
|
||||
dt = BLOBType(10)
|
||||
assert dt.as_json == ['blob', 10]
|
||||
|
||||
dt = BLOBType(3, 10)
|
||||
assert dt.as_json == ['blob', 10, 3]
|
||||
|
||||
@ -135,6 +144,11 @@ def test_BLOBType():
|
||||
|
||||
def test_StringType():
|
||||
# test constructor catching illegal arguments
|
||||
dt = StringType()
|
||||
assert dt.as_json == ['string']
|
||||
dt = StringType(12)
|
||||
assert dt.as_json == ['string', 12]
|
||||
|
||||
dt = StringType(4, 11)
|
||||
assert dt.as_json == ['string', 11, 4]
|
||||
|
||||
@ -178,16 +192,21 @@ def test_ArrayOf():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
ArrayOf(int)
|
||||
dt = ArrayOf(IntRange(-10,10),1,3)
|
||||
dt = ArrayOf(IntRange(-10, 10))
|
||||
assert dt.as_json == ['array', ['int', -10, 10]]
|
||||
dt = ArrayOf(IntRange(-10, 10), 5)
|
||||
assert dt.as_json == ['array', ['int', -10, 10], 5]
|
||||
|
||||
dt = ArrayOf(IntRange(-10, 10), 1, 3)
|
||||
assert dt.as_json == ['array', ['int', -10, 10], 3, 1]
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate('av')
|
||||
|
||||
assert dt.validate([1,2,3]) == [1,2,3]
|
||||
assert dt.validate([1, 2, 3]) == [1, 2, 3]
|
||||
|
||||
assert dt.export([1,2,3]) == [1,2,3]
|
||||
assert dt.export([1, 2, 3]) == [1, 2, 3]
|
||||
|
||||
|
||||
def test_TupleOf():
|
||||
@ -195,17 +214,17 @@ def test_TupleOf():
|
||||
with pytest.raises(ValueError):
|
||||
TupleOf(2)
|
||||
|
||||
dt = TupleOf(IntRange(-10,10), BoolType())
|
||||
dt = TupleOf(IntRange(-10, 10), BoolType())
|
||||
assert dt.as_json == ['tuple', [['int', -10, 10], ['bool']]]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate([99,'X'])
|
||||
dt.validate([99, 'X'])
|
||||
|
||||
assert dt.validate([1,True]) == [1,True]
|
||||
assert dt.validate([1, True]) == [1, True]
|
||||
|
||||
assert dt.export([1,True]) == [1,True]
|
||||
assert dt.export([1, True]) == [1, True]
|
||||
|
||||
|
||||
def test_StructOf():
|
||||
@ -218,19 +237,20 @@ def test_StructOf():
|
||||
dt = StructOf(a_string=StringType(), an_int=IntRange(0, 999))
|
||||
assert dt.as_json == ['struct', {'a_string': ['string'],
|
||||
'an_int': ['int', 0, 999],
|
||||
}]
|
||||
}]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate([99,'X'])
|
||||
dt.validate([99, 'X'])
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate(dict(a_string='XXX', an_int=1811))
|
||||
|
||||
assert dt.validate(dict(a_string='XXX', an_int=8)) == {'a_string': 'XXX',
|
||||
'an_int': 8}
|
||||
assert dt.export({'an_int':13, 'a_string':'WFEC'}) == {'a_string': 'WFEC',
|
||||
'an_int': 13}
|
||||
assert dt.export({'an_int': 13, 'a_string': 'WFEC'}) == {'a_string': 'WFEC',
|
||||
'an_int': 13}
|
||||
|
||||
|
||||
def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
@ -252,51 +272,46 @@ def test_get_datatype():
|
||||
assert isinstance(get_datatype(['int', -10, 10]), IntRange)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['int',10, -10])
|
||||
get_datatype(['int', 10, -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['int', 1, 2, 3])
|
||||
|
||||
|
||||
assert isinstance(get_datatype(['double']), FloatRange)
|
||||
assert isinstance(get_datatype(['double', -2.718]), FloatRange)
|
||||
assert isinstance(get_datatype(['double', None, 3.14]), FloatRange)
|
||||
assert isinstance(get_datatype(['double', -9.9, 11.1]), FloatRange)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['double',10, -10])
|
||||
get_datatype(['double', 10, -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['double', 1, 2, 3])
|
||||
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['enum'])
|
||||
assert isinstance(get_datatype(['enum', dict(a=-2.718)]), EnumType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['enum',10, -10])
|
||||
get_datatype(['enum', 10, -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['enum', [1, 2, 3]])
|
||||
|
||||
|
||||
assert isinstance(get_datatype(['blob']), BLOBType)
|
||||
assert isinstance(get_datatype(['blob', 1]), BLOBType)
|
||||
assert isinstance(get_datatype(['blob', 1, 10]), BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['blob',10, -10])
|
||||
get_datatype(['blob', 10, -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['blob',10, -10, 1])
|
||||
|
||||
get_datatype(['blob', 10, -10, 1])
|
||||
|
||||
assert isinstance(get_datatype(['string']), StringType)
|
||||
assert isinstance(get_datatype(['string', 1]), StringType)
|
||||
assert isinstance(get_datatype(['string', 1, 10]), StringType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['string',10, -10])
|
||||
get_datatype(['string', 10, -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['string',10, -10, 1])
|
||||
|
||||
get_datatype(['string', 10, -10, 1])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['array'])
|
||||
@ -314,7 +329,6 @@ def test_get_datatype():
|
||||
|
||||
assert isinstance(get_datatype(['array', ['blob'], 1, 10]), ArrayOf)
|
||||
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['tuple'])
|
||||
with pytest.raises(ValueError):
|
||||
@ -322,15 +336,15 @@ def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['tuple', [1], 2, 3])
|
||||
assert isinstance(get_datatype(['tuple', [['blob']]]), TupleOf)
|
||||
assert isinstance(get_datatype(['tuple', [['blob']]]).subtypes[0], BLOBType)
|
||||
assert isinstance(get_datatype(
|
||||
['tuple', [['blob']]]).subtypes[0], BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['tuple', [['blob']], -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['tuple', [['blob']], -10, 10])
|
||||
|
||||
assert isinstance(get_datatype(['tuple', [['blob'],['int']]]), TupleOf)
|
||||
|
||||
assert isinstance(get_datatype(['tuple', [['blob'], ['int']]]), TupleOf)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['struct'])
|
||||
@ -338,12 +352,14 @@ def test_get_datatype():
|
||||
get_datatype(['struct', 1])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['struct', [1], 2, 3])
|
||||
assert isinstance(get_datatype(['struct', {'blob':['blob']}]), StructOf)
|
||||
assert isinstance(get_datatype(['struct', {'blob':['blob']}]).named_subtypes['blob'], BLOBType)
|
||||
assert isinstance(get_datatype(['struct', {'blob': ['blob']}]), StructOf)
|
||||
assert isinstance(get_datatype(
|
||||
['struct', {'blob': ['blob']}]).named_subtypes['blob'], BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['struct', [['blob']], -10])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['struct', [['blob']], -10, 10])
|
||||
|
||||
assert isinstance(get_datatype(['struct', {'blob':['blob'], 'int':['int']}]), StructOf)
|
||||
assert isinstance(get_datatype(
|
||||
['struct', {'blob': ['blob'], 'int':['int']}]), StructOf)
|
||||
|
Reference in New Issue
Block a user