fix transport and display of node-properties

Change-Id: I35a3021768e386a5ce922c8e24128d0bc3a039be
This commit is contained in:
Enrico Faulhaber
2017-09-12 16:10:22 +02:00
parent 7d5b211a0e
commit 96ac437fd3
18 changed files with 292 additions and 225 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -49,7 +49,6 @@ CONFIG = {
}
class lazy_property(object):
"""A property that calculates its value only once."""

View File

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

View File

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