From f3c8ceb418dd9ccf8ea7c0283ea6ba4b8f586a5b Mon Sep 17 00:00:00 2001 From: Jan Kotanski Date: Thu, 5 Sep 2024 13:32:47 +0200 Subject: [PATCH] Issue 708: add support for secop meaning dictionary (#709) * add support for secop meaning dict * fix checks * add tests * update manpages --- ChangeLog | 4 + man/nxscollect.1 | 2 +- man/nxsconfig.1 | 2 +- man/nxscreate.1 | 2 +- man/nxsdata.1 | 2 +- man/nxsetup.1 | 2 +- man/nxsfileinfo.1 | 2 +- man/nxstools.1 | 2 +- nxstools/nxscreator.py | 51 +- nxstools/release.py | 2 +- test/NXSCreateSECoPCPFS_test.py | 1426 +++++++++++++++++++++++++++++-- test/files/secop2.conf | 543 ++++++++++++ 12 files changed, 1932 insertions(+), 108 deletions(-) create mode 100644 test/files/secop2.conf diff --git a/ChangeLog b/ChangeLog index ce8d2d93..3199eb1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-08-08 Jan Kotanski + * add support for secop meaning dictionary (#709) + * tagged as v4.14.0 + 2024-08-08 Jan Kotanski * fix typo in for device_name in nxsetup (#706) * tagged as v4.13.1 diff --git a/man/nxscollect.1 b/man/nxscollect.1 index 3edcd8e8..4f09fdc6 100644 --- a/man/nxscollect.1 +++ b/man/nxscollect.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSCOLLECT" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSCOLLECT" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxscollect \- upload external images into NeXus/HDF5 file .SH DESCRIPTION diff --git a/man/nxsconfig.1 b/man/nxsconfig.1 index 25f96bf4..57b2ef52 100644 --- a/man/nxsconfig.1 +++ b/man/nxsconfig.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSCONFIG" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSCONFIG" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxsconfig \- read NeXus Configuration Server settings .SH DESCRIPTION diff --git a/man/nxscreate.1 b/man/nxscreate.1 index f4fb9c73..98fe1009 100644 --- a/man/nxscreate.1 +++ b/man/nxscreate.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSCREATE" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSCREATE" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxscreate \- create NeXus Configuration component .SH DESCRIPTION diff --git a/man/nxsdata.1 b/man/nxsdata.1 index 50fd7441..8d5a5046 100644 --- a/man/nxsdata.1 +++ b/man/nxsdata.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSDATA" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSDATA" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxsdata \- run NeXus Data Writer .SH DESCRIPTION diff --git a/man/nxsetup.1 b/man/nxsetup.1 index 496f7525..8ea808bf 100644 --- a/man/nxsetup.1 +++ b/man/nxsetup.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSETUP" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSETUP" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxsetup \- set NeXDaTaS Tango Server environment up .SH DESCRIPTION diff --git a/man/nxsfileinfo.1 b/man/nxsfileinfo.1 index bc6514f7..d84e92c6 100644 --- a/man/nxsfileinfo.1 +++ b/man/nxsfileinfo.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSFILEINFO" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSFILEINFO" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxsfileinfo \- show metadata of the NeXus file .SH DESCRIPTION diff --git a/man/nxstools.1 b/man/nxstools.1 index 5a713923..92e181d8 100644 --- a/man/nxstools.1 +++ b/man/nxstools.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "NXSTOOLS" "1" "Jul 18, 2024" "4.13" "NXSTools" +.TH "NXSTOOLS" "1" "Sep 05, 2024" "4.14" "NXSTools" .SH NAME nxstools \- nxstools Documentation .sp diff --git a/nxstools/nxscreator.py b/nxstools/nxscreator.py index c1d1f6bd..b201ea1e 100644 --- a/nxstools/nxscreator.py +++ b/nxstools/nxscreator.py @@ -1893,12 +1893,25 @@ def __createSECoPTree(self, df, name, conf, samplename=None, for mname, mconf in modules.items(): if mname and (not senv or not seenv): if not modulenames or mname in modulenames: - if not senv and "meaning" in mconf.keys(): - senv = NGroup(sample, name or "environment", - "NXenvironment") - if not seenv and "meaning" not in mconf.keys(): - seenv = NGroup(sampleenv, name or "environment", - "NXenvironment") + if "meaning" in mconf.keys() and \ + isinstance(mconf["meaning"], dict): + if "belongs_to" in mconf["meaning"].keys() and \ + mconf["meaning"]["belongs_to"] == "sample": + if not senv: + senv = NGroup(sample, name or "environment", + "NXenvironment") + else: + if not seenv: + seenv = NGroup(sampleenv, + name or "environment", + "NXenvironment") + else: + if not senv and "meaning" in mconf.keys(): + senv = NGroup(sample, name or "environment", + "NXenvironment") + if not seenv and "meaning" not in mconf.keys(): + seenv = NGroup(sampleenv, name or "environment", + "NXenvironment") envs = [senv, seenv] for env in envs: if env: @@ -2047,8 +2060,21 @@ def __createSECoPSensor(self, senv, seenv, name, conf, nodename, """ if 'meaning' in conf.keys(): meaning = conf['meaning'] - env = senv - basename = samplename + if isinstance(meaning, dict): + mdict = meaning + env = seenv + meaning = None + basename = sampleenvname + + if "belongs_to" in mdict.keys() and \ + mdict["belongs_to"] == "sample": + env = senv + basename = samplename + if "function" in mdict.keys(): + meaning = mdict["function"] + else: + env = senv + basename = samplename else: meaning = None env = seenv @@ -2074,6 +2100,15 @@ def __createSECoPSensor(self, senv, seenv, name, conf, nodename, importance = None if len(meaning) > 0: meaning = meaning[0] + if isinstance(meaning, dict): + try: + importance = int(meaning["importance"]) + except Exception: + importance = None + try: + meaning = int(meaning["function"]) + except Exception: + meaning = None if meaning in mnTme.keys(): meaning = mnTme[meaning] field = NField(mgr, 'measurement', 'NX_CHAR') diff --git a/nxstools/release.py b/nxstools/release.py index b36169bb..fcaf0285 100644 --- a/nxstools/release.py +++ b/nxstools/release.py @@ -19,4 +19,4 @@ """ NXS tools release version""" #: (:obj:`str`) package version -__version__ = "4.13.1" +__version__ = "4.14.0" diff --git a/test/NXSCreateSECoPCPFS_test.py b/test/NXSCreateSECoPCPFS_test.py index 7177a82b..dad6927e 100644 --- a/test/NXSCreateSECoPCPFS_test.py +++ b/test/NXSCreateSECoPCPFS_test.py @@ -687,6 +687,543 @@ def __init__(self, methodName): ' \n' '\n') + self.myuni2 = ( + '' + '' + ' ' + ' ' + ' sample' + ' ' + ' uniax_sim.psi.ch' + '' + ' {cpname}' + '' + ' FRAPPY - ' + 'The Python Framework for SECoP (2021.02)' + '' + ' [sim] uniaxial ' + 'pressure device' + ' ' + ' T' + ' ' + 'secop_psi.softcal.Sensor' + ' ' + 'temperature sensor, soft calibration' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t_status' + ' ' + '$datasources.{cpname}_t_status_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t__calib' + ' ' + '$datasources.{cpname}_t__calib_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t__abs' + ' ' + '$datasources.{cpname}_t__abs_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t' + ' ' + '$datasources.{cpname}_t_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + ' ' + ' sample environment' + '' + ' ' + ' uniax_sim.psi.ch' + '' + ' {cpname}' + '' + ' FRAPPY - The Python ' + 'Framework for SECoP (2021.02)' + ' [sim] uniaxial ' + 'pressure device' + ' ' + ' force' + '' + ' ' + 'secop_psi.uniax.Uniax' + ' ' + 'uniax driver' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_status' + ' ' + '$datasources.{cpname}_force_status_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_pollinterval' + '' + ' ' + '$datasources.{cpname}_force_pollinterval_time' + '' + '$datasources.client_start_time' + '' + ' ' + '0.1' + ' ' + '120' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_target' + ' ' + '$datasources.{cpname}_force_target_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__limit' + ' ' + '$datasources.{cpname}_force__limit_time$datasources.client_start_time' + ' 150' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__tolerance' + '' + ' ' + '$datasources.{cpname}_force__tolerance_time$datasources.client_start_time' + '' + '' + ' 10' + ' ' + ' ' + ' $datasources.{cpname}_force__slope' + ' ' + '$datasources.{cpname}_force__slope_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__pid_i' + ' ' + '$datasources.{cpname}_force__pid_i_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__filter_interval' + ' ' + '$datasources.{cpname}_force__filter_interval_time$datasources.client_start_time' + '' + '' + ' 60' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__current_step' + '' + ' ' + '$datasources.{cpname}_force__current_step_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__force_offset' + '' + ' ' + '$datasources.{cpname}_force__force_offset_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__hysteresis' + '' + ' ' + '$datasources.{cpname}_force__hysteresis_time' + '$datasources.client_start_time' + '' + ' 150' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__adjusting' + '' + ' ' + '$datasources.{cpname}_force__adjusting_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__adjusting_current' + ' ' + '$datasources.{cpname}_force__adjusting_current_time$datasources.client_start_time' + '' + '' + ' 2.8' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__safe_step' + '' + ' ' + '$datasources.{cpname}_force__safe_step_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__safe_current' + '' + ' ' + '$datasources.{cpname}_force__safe_current_time$datasources.client_start_time' + '' + '' + ' 2.8' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__low_pos' + '' + ' ' + '$datasources.{cpname}_force__low_pos_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__high_pos' + '' + ' ' + '$datasources.{cpname}_force__high_pos_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force' + ' ' + '$datasources.{cpname}_force_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' drv' + ' ' + 'secop.simulation.SimBase_drv' + ' ' + 'simulated motor' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_status' + ' ' + '$datasources.{cpname}_drv_status_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_pollinterval' + '' + ' ' + '$datasources.{cpname}_drv_pollinterval_time$datasources.client_start_time' + '' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_target' + ' ' + '$datasources.{cpname}_drv_target_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__interval' + '' + ' ' + '$datasources.{cpname}_drv__interval_time$datasources.client_start_time' + ' 1' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__speed' + ' ' + '$datasources.{cpname}_drv__speed_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__safe_current' + '' + ' ' + '$datasources.{cpname}_drv__safe_current_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__move_limit' + '' + ' ' + '$datasources.{cpname}_drv__move_limit_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__maxcurrent' + '' + ' ' + '$datasources.{cpname}_drv__maxcurrent_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__tolerance' + '' + ' ' + '$datasources.{cpname}_drv__tolerance_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv' + ' ' + '$datasources.{cpname}_drv_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' transducer' + '' + ' ' + 'secop_psi.simdpm.DPM3' + ' ' + 'simulated force' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer_status' + '' + ' ' + '$datasources.{cpname}_transducer_status_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer_pollinterval' + '' + ' ' + '$datasources.{cpname}_transducer_pollinterval_time' + '' + '$datasources.client_start_time' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer__jitter' + '' + ' ' + '$datasources.{cpname}_transducer__jitter_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer__hysteresis' + '' + ' ' + '$datasources.{cpname}_transducer__hysteresis_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' $datasources.{cpname}_transducer__friction' + '' + ' ' + '$datasources.{cpname}_transducer__friction_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' $datasources.{cpname}_transducer__slope' + '' + ' ' + '$datasources.{cpname}_transducer__slope_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer__offset' + '' + ' ' + '$datasources.{cpname}_transducer__offset_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_transducer' + ' ' + '$datasources.{cpname}_transducer_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' res' + '' + ' ' + 'secop.simulation.SimBase_res' + ' ' + 'raw temperature sensor on the stick' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_res_status' + ' ' + '$datasources.{cpname}_res_status_time$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_res_pollinterval' + '' + ' ' + '$datasources.{cpname}_res_pollinterval_time$datasources.client_start_time' + '' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_res__jitter' + ' ' + '$datasources.{cpname}_res__jitter_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_res' + ' ' + '$datasources.{cpname}_res_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + '') + self.myunidy = ( '\n' '\n' @@ -1104,165 +1641,713 @@ def __init__(self, methodName): '$datasources.{cpname}_force_time' '' '$datasources.client_start_time' - '\n' - ' \n' + '\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' transducer' + '\n' + ' ' + 'secop_psi.simdpm.DPM3\n' + ' ' + 'simulated force\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer_status' + '\n' + ' ' + '$datasources.{cpname}_transducer_status_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer_pollinterval' + '\n' + ' ' + '$datasources.{cpname}_transducer_pollinterval_time' + '' + '$datasources.client_start_time' + '\n' + ' ' + '0.1\n' + ' ' + '120\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer__jitter' + '\n' + ' ' + '$datasources.{cpname}_transducer__jitter_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer__hysteresis' + '\n' + ' ' + '$datasources.{cpname}_transducer__hysteresis_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' $datasources.{cpname}_transducer__friction' + '\n' + ' ' + '$datasources.{cpname}_transducer__friction_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' $datasources.{cpname}_transducer__slope' + '\n' + ' ' + '$datasources.{cpname}_transducer__slope_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer__offset' + '\n' + ' ' + '$datasources.{cpname}_transducer__offset_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_transducer\n' + ' ' + '$datasources.{cpname}_transducer_time$datasources.client_start_time' + '' + '\n' + ' \n' + ' \n' + ' \n' + ' res' + '\n' + ' ' + 'secop.simulation.SimBase_res\n' + ' ' + 'raw temperature sensor on the stick' + '\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_res_status\n' + ' ' + '$datasources.{cpname}_res_status_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_res_pollinterval' + '\n' + ' ' + '$datasources.{cpname}_res_pollinterval_time' + '' + '$datasources.client_start_time' + '\n' + ' ' + '0.1\n' + ' ' + '120\n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_res__jitter' + '\n' + ' ' + '$datasources.{cpname}_res__jitter_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' \n' + ' ' + '$datasources.{cpname}_res\n' + ' ' + '$datasources.{cpname}_res_time' + '' + '$datasources.client_start_time' + '\n' + ' \n' + ' \n' + ' \n' + ' ' + '$datasources.sampleenv_nxdata' + '\n' + ' \n' + ' \n' + '\n') + + self.myunidy2 = ( + '' + '' + ' ' + ' ' + ' sample' + ' ' + ' uniax_sim.psi.ch' + '' + ' {cpname}' + '' + ' FRAPPY - ' + 'The Python Framework for SECoP (2021.02)' + '' + ' [sim] uniaxial ' + 'pressure device' + ' ' + ' T' + ' ' + 'secop_psi.softcal.Sensor' + ' ' + 'temperature sensor, soft calibration' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t_status' + ' ' + '$datasources.{cpname}_t_status_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t__calib' + ' ' + '$datasources.{cpname}_t__calib_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t__abs' + ' ' + '$datasources.{cpname}_t__abs_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_t' + ' ' + '$datasources.{cpname}_t_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + '$datasources.sample_env_links' + ' ' + '$datasources.sample_log_links' + ' ' + '$datasources.sample_nxdata' + ' ' + ' ' + ' sample environment' + '' + ' ' + ' uniax_sim.psi.ch' + '' + ' {cpname}' + '' + ' FRAPPY - The Python ' + 'Framework for SECoP (2021.02)' + ' [sim] uniaxial ' + 'pressure device' + ' ' + ' force' + ' ' + 'secop_psi.uniax.Uniax' + ' uniax driver' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_status' + ' ' + '$datasources.{cpname}_force_status_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_pollinterval' + '' + ' ' + '$datasources.{cpname}_force_pollinterval_time$datasources.client_start_time' + '' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force_target' + ' ' + '$datasources.{cpname}_force_target_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__limit' + ' ' + '$datasources.{cpname}_force__limit_time$datasources.client_start_time' + ' 150' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__tolerance' + '' + ' ' + '$datasources.{cpname}_force__tolerance_time$datasources.client_start_time' + '' + '' + ' 10' + ' ' + ' ' + ' $datasources.{cpname}_force__slope' + ' ' + '$datasources.{cpname}_force__slope_time$datasources.client_start_time' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__pid_i' + ' ' + '$datasources.{cpname}_force__pid_i_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__filter_interval' + ' ' + '$datasources.{cpname}_force__filter_interval_time$datasources.client_start_time' + '' + '' + ' 60' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__current_step' + '' + ' ' + '$datasources.{cpname}_force__current_step_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__force_offset' + '' + ' ' + '$datasources.{cpname}_force__force_offset_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__hysteresis' + '' + ' ' + '$datasources.{cpname}_force__hysteresis_time$datasources.client_start_time' + '' + '' + ' 150' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__adjusting' + '' + ' ' + '$datasources.{cpname}_force__adjusting_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__adjusting_current' + '' + ' ' + '$datasources.{cpname}_force__adjusting_current_time' + '' + '$datasources.client_start_time' + '' + ' 2.8' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__safe_step' + '' + ' ' + '$datasources.{cpname}_force__safe_step_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__safe_current' + '' + ' ' + '$datasources.{cpname}_force__safe_current_time' + '' + '$datasources.client_start_time' + '' + ' 2.8' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__low_pos' + '' + ' ' + '$datasources.{cpname}_force__low_pos_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force__high_pos' + '' + ' ' + '$datasources.{cpname}_force__high_pos_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_force' + ' ' + '$datasources.{cpname}_force_time$datasources.client_start_time' + ' ' + ' ' + ' ' + ' ' + ' drv' + ' ' + 'secop.simulation.SimBase_drv' + ' ' + 'simulated motor' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_status' + ' ' + '$datasources.{cpname}_drv_status_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_pollinterval' + '' + ' ' + '$datasources.{cpname}_drv_pollinterval_time' + '$datasources.client_start_time' + '' + ' ' + '0.1' + ' ' + '120' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv_target' + ' ' + '$datasources.{cpname}_drv_target_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__interval' + '' + ' ' + '$datasources.{cpname}_drv__interval_time$datasources.client_start_time' + ' 1' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__speed' + ' ' + '$datasources.{cpname}_drv__speed_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__safe_current' + '' + ' ' + '$datasources.{cpname}_drv__safe_current_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__move_limit' + '' + ' ' + '$datasources.{cpname}_drv__move_limit_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__maxcurrent' + '' + ' ' + '$datasources.{cpname}_drv__maxcurrent_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv__tolerance' + '' + ' ' + '$datasources.{cpname}_drv__tolerance_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' + ' ' + '$datasources.{cpname}_drv' + ' ' + '$datasources.{cpname}_drv_time$datasources.client_start_time' + '' + '' + ' ' ' \n' - ' \n' - ' \n' + 'sample_environment/{cpname}/drv/parameters/target/value"/>' + ' ' + ' ' ' transducer' - '\n' + '' ' ' - 'secop_psi.simdpm.DPM3\n' + 'secop_psi.simdpm.DPM3' ' ' - 'simulated force\n' - ' \n' - ' \n' + 'simulated force' + ' ' + ' ' ' ' '$datasources.{cpname}_transducer_status' - '\n' + '' ' ' '$datasources.{cpname}_transducer_status_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' ' '$datasources.{cpname}_transducer_pollinterval' - '\n' - ' ' - '$datasources.{cpname}_transducer_pollinterval_time' + '' + ' $datasources.{cpname}_transducer_pollinterval_time' '' '$datasources.client_start_time' - '\n' - ' ' - '0.1\n' - ' ' - '120\n' - ' \n' - ' \n' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' ' ' '$datasources.{cpname}_transducer__jitter' - '\n' + '' ' ' '$datasources.{cpname}_transducer__jitter_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' ' '$datasources.{cpname}_transducer__hysteresis' - '\n' + '' ' ' '$datasources.{cpname}_transducer__hysteresis_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' $datasources.{cpname}_transducer__friction' - '\n' + '' ' ' '$datasources.{cpname}_transducer__friction_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' $datasources.{cpname}_transducer__slope' - '\n' + '' ' ' '$datasources.{cpname}_transducer__slope_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' ' '$datasources.{cpname}_transducer__offset' - '\n' + '' ' ' '$datasources.{cpname}_transducer__offset_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' - ' \n' + '' + ' ' + ' ' + ' ' ' ' - '$datasources.{cpname}_transducer\n' + '$datasources.{cpname}_transducer' ' ' - '$datasources.{cpname}_transducer_time$datasources.client_start_time' - '' - '\n' - ' \n' - ' \n' - ' \n' + '$datasources.{cpname}_transducer_time' + '' + '$datasources.client_start_time' + '' + ' ' + ' ' + ' ' ' res' - '\n' + '' ' ' - 'secop.simulation.SimBase_res\n' + 'secop.simulation.SimBase_res' ' ' 'raw temperature sensor on the stick' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' ' - '$datasources.{cpname}_res_status\n' + '$datasources.{cpname}_res_status' ' ' '$datasources.{cpname}_res_status_time' '' '$datasources.client_start_time' - '\n' - ' \n' - ' \n' + '' + ' ' + ' ' ' ' - '$datasources.{cpname}_res_pollinterval' - '\n' + '$datasources.{cpname}_res_pollinterval' + '' ' ' '$datasources.{cpname}_res_pollinterval_time' '' '$datasources.client_start_time' - '\n' - ' ' - '0.1\n' - ' ' - '120\n' - ' \n' - ' \n' + '' + ' 0.1' + '' + ' 120' + '' + ' ' + ' ' ' ' - '$datasources.{cpname}_res__jitter' - '\n' + '$datasources.{cpname}_res__jitter' ' ' - '$datasources.{cpname}_res__jitter_time' - '' - '$datasources.client_start_time' - '\n' - ' \n' - ' \n' - ' \n' + '$datasources.{cpname}_res__jitter_time$datasources.client_start_time' + ' ' + ' ' + ' ' ' ' - '$datasources.{cpname}_res\n' + '$datasources.{cpname}_res' ' ' - '$datasources.{cpname}_res_time' - '' - '$datasources.client_start_time' - '\n' - ' \n' - ' \n' - ' \n' + '$datasources.{cpname}_res_time$datasources.client_start_time' + '' + '' + ' ' + ' ' + ' ' ' ' '$datasources.sampleenv_nxdata' - '\n' - ' \n' - ' \n' - '\n') + '' + ' ' + ' ' + '' + ) self.myunids = [ '\n' @@ -2793,6 +3878,47 @@ def test_secopcp_list_mouldes(self): finally: os.remove(fname) + def test_secopcp_list_mouldes_dict(self): + """ test nxsccreate stdcomp file system + """ + fun = sys._getframe().f_code.co_name + print("Run: %s.%s() " % (self.__class__.__name__, fun)) + + fname = '%s/%s%s.json' % ( + os.getcwd(), self.__class__.__name__, fun) + + args = [ + [ + ('nxscreate secopcp -l -j %s %s' + % (fname, self.flags)).split(), + ('nxscreate secopcp --list --json-file %s %s' + % (fname, self.flags)).split(), + ], + ] + + if os.path.isfile(fname): + raise Exception("Test file %s exists" % fname) + shutil.copy("test/files/secop2.conf", fname) + try: + for arg in args: + vl, er = self.runtest(arg[0]) + + # if er: + # self.assertTrue(er.startswith("Info: ")) + # else: + # self.assertEqual('', er) + lines = vl.split("\n") + self.assertEqual(len(lines), 3) + self.assertEqual(lines[-3], "MODULES:") + self.assertEqual( + sorted(lines[-2].split()), + sorted(['force', 'drv', 'transducer', 'res', 'T'])) + self.assertEqual( + lines[-1].split(), + []) + finally: + os.remove(fname) + def ttest_secopcp_create_old(self): """ test nxsccreate stdcomp file system """ @@ -2899,6 +4025,44 @@ def test_secopcp_create(self): ] self.checkxmls(args, fname) + def test_secopcp_create_dict(self): + """ test nxsccreate stdcomp file system + """ + + fun = sys._getframe().f_code.co_name + print("Run: %s.%s() " % (self.__class__.__name__, fun)) + + fname = '%s/%s%s.json' % ( + os.getcwd(), self.__class__.__name__, fun) + + cname = "myuni" + + shutil.copy("test/files/secop2.conf", fname) + + dsl = ["client_start_time"] + dsl.extend(self.secoplist[1:]) + args = [ + [ + [ + ('nxscreate secopcp -c %s -j %s %s' + % (cname, fname, self.flags)).split(), + ('nxscreate secopcp --component %s --json-file %s %s' + % (cname, fname, self.flags)).split(), + ], + [ + [self.secoplist[0].format(cpname=cname)], + [ds.format(cpname=cname) for ds in dsl] + ], + [ + [ + self.myuni2.format(cpname=cname) + ], + [ds.format(cpname=cname) for ds in self.myunids] + ], + ], + ] + self.checkxmls(args, fname) + def test_secopcp_create_dynamic_nxdata(self): """ test nxsccreate stdcomp file system """ @@ -2939,6 +4103,46 @@ def test_secopcp_create_dynamic_nxdata(self): ] self.checkxmls(args, fname) + def test_secopcp_create_dynamic_nxdata_dict(self): + """ test nxsccreate stdcomp file system + """ + + fun = sys._getframe().f_code.co_name + print("Run: %s.%s() " % (self.__class__.__name__, fun)) + + fname = '%s/%s%s.json' % ( + os.getcwd(), self.__class__.__name__, fun) + + cname = "myunids2" + + shutil.copy("test/files/secop2.conf", fname) + + dsl = ["client_start_time"] + dsl.extend(self.secoplist[1:]) + args = [ + [ + [ + ('nxscreate secopcp -c %s -j %s %s --dynamic' + ' --sample-nxdata ' + % (cname, fname, self.flags)).split(), + ('nxscreate secopcp --dynamic --component %s ' + '--sample-nxdata --json-file %s %s' + % (cname, fname, self.flags)).split(), + ], + [ + [self.secoplist[0].format(cpname=cname)], + [ds.format(cpname=cname) for ds in dsl] + ], + [ + [ + self.myunidy2.format(cpname=cname) + ], + [ds.format(cpname=cname) for ds in self.myunids] + ], + ], + ] + self.checkxmls(args, fname) + def test_secopcp_create_def(self): """ test nxscreate stdcomp file system """ @@ -2977,6 +4181,44 @@ def test_secopcp_create_def(self): ] self.checkxmls(args, fname) + def test_secopcp_create_def_dict(self): + """ test nxscreate stdcomp file system + """ + + fun = sys._getframe().f_code.co_name + print("Run: %s.%s() " % (self.__class__.__name__, fun)) + + fname = '%s/%s%s.json' % ( + os.getcwd(), self.__class__.__name__, fun) + + cname = "uniax_sim" + + shutil.copy("test/files/secop2.conf", fname) + + dsl = ["client_start_time"] + dsl.extend(self.secoplist[1:]) + args = [ + [ + [ + ('nxscreate secopcp -j %s %s' + % (fname, self.flags)).split(), + ('nxscreate secopcp --json-file %s %s' + % (fname, self.flags)).split(), + ], + [ + [self.secoplist[0].format(cpname=cname)], + [ds.format(cpname=cname) for ds in dsl] + ], + [ + [ + self.myuni2.format(cpname=cname) + ], + [ds.format(cpname=cname) for ds in self.myunids] + ], + ], + ] + self.checkxmls(args, fname) + if __name__ == '__main__': unittest.main() diff --git a/test/files/secop2.conf b/test/files/secop2.conf new file mode 100644 index 00000000..e94da121 --- /dev/null +++ b/test/files/secop2.conf @@ -0,0 +1,543 @@ +{ + "modules": { + "force": { + "meaning": { + "key": "force", + "link": "http://purl.allotrope.org/ontologies/result#AFR_0001", + "function": "force", + "importance": 40, + "belongs_to": "environment" + }, + "accessibles": { + "value": { + "description": "current value of the module", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": true + }, + "status": { + "description": "current status of the module", + "datainfo": { + "type": "tuple", + "members": [ + { + "type": "enum", + "members": { + "DISABLED": 0, + "IDLE": 100, + "WARN": 200, + "UNSTABLE": 270, + "BUSY": 300, + "ERROR": 400, + "UNKNOWN": 401 + } + }, + { + "type": "string" + } + ] + }, + "readonly": true + }, + "pollinterval": { + "description": "sleeptime between polls", + "datainfo": { + "min": 0.1, + "max": 120, + "type": "double" + }, + "readonly": false + }, + "target": { + "description": "target value of the module", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "stop": { + "description": "cease driving, go to IDLE state", + "datainfo": { + "type": "command" + } + }, + "_factory_reset": { + "description": "", + "datainfo": { + "type": "command" + } + }, + "_limit": { + "description": "abs limit of force", + "datainfo": { + "min": 0, + "max": 150, + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "_tolerance": { + "description": "force tolerance", + "datainfo": { + "min": 0, + "max": 10, + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "_slope": { + "description": "spring constant", + "datainfo": { + "unit": "deg/N", + "type": "double" + }, + "readonly": false + }, + "_pid_i": { + "description": "integral", + "datainfo": { + "type": "double" + }, + "readonly": false + }, + "_filter_interval": { + "description": "filter time", + "datainfo": { + "min": 0, + "max": 60, + "unit": "s", + "type": "double" + }, + "readonly": false + }, + "_current_step": { + "description": "", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": true + }, + "_force_offset": { + "description": "transducer offset", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "_hysteresis": { + "description": "force hysteresis", + "datainfo": { + "min": 0, + "max": 150, + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "_adjusting": { + "description": "", + "datainfo": { + "type": "bool" + }, + "readonly": false + }, + "_adjusting_current": { + "description": "current when adjusting force", + "datainfo": { + "min": 0, + "max": 2.8, + "unit": "A", + "type": "double" + }, + "readonly": false + }, + "_safe_step": { + "description": "max. motor step when adjusting force", + "datainfo": { + "min": 0, + "unit": "deg", + "type": "double" + }, + "readonly": false + }, + "_safe_current": { + "description": "current when moving far", + "datainfo": { + "min": 0, + "max": 2.8, + "unit": "A", + "type": "double" + }, + "readonly": false + }, + "_low_pos": { + "description": "max. position for positive forces", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": false + }, + "_high_pos": { + "description": "min. position for negative forces", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": false + } + }, + "description": "uniax driver", + "implementation": "secop_psi.uniax.Uniax", + "interface_classes": [ + "Drivable" + ] + }, + "drv": { + "meaning": { + "key": "rotation_z", + "link": "http://purl.allotrope.org/ontologies/result#AFR_0002", + "function": "rotation_z", + "importance": 40, + "belongs_to": "environment" + }, + "accessibles": { + "value": { + "description": "current value of the module", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": true + }, + "status": { + "description": "current status of the module", + "datainfo": { + "type": "tuple", + "members": [ + { + "type": "enum", + "members": { + "DISABLED": 0, + "IDLE": 100, + "WARN": 200, + "UNSTABLE": 270, + "BUSY": 300, + "ERROR": 400, + "UNKNOWN": 401 + } + }, + { + "type": "string" + } + ] + }, + "readonly": true + }, + "pollinterval": { + "description": "sleeptime between polls", + "datainfo": { + "min": 0.1, + "max": 120, + "type": "double" + }, + "readonly": false + }, + "target": { + "description": "target value of the module", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": false + }, + "stop": { + "description": "cease driving, go to IDLE state", + "datainfo": { + "type": "command" + } + }, + "_interval": { + "description": "simulation interval", + "datainfo": { + "min": 0, + "max": 1, + "type": "double" + }, + "readonly": false + }, + "_speed": { + "description": "extra_param: speed", + "datainfo": { + "type": "double" + }, + "readonly": false + }, + "_safe_current": { + "description": "extra_param: safe_current", + "datainfo": { + "type": "double" + }, + "readonly": true + }, + "_move_limit": { + "description": "extra_param: move_limit", + "datainfo": { + "type": "double" + }, + "readonly": true + }, + "_maxcurrent": { + "description": "extra_param: maxcurrent", + "datainfo": { + "type": "double" + }, + "readonly": true + }, + "_tolerance": { + "description": "extra_param: tolerance", + "datainfo": { + "type": "double" + }, + "readonly": true + } + }, + "description": "simulated motor", + "implementation": "secop.simulation.SimBase_drv", + "interface_classes": [ + "Drivable" + ] + }, + "transducer": { + "meaning": { + "key": "force", + "link": "http://purl.allotrope.org/ontologies/result#AFR_0003", + "function": "force", + "importance": 42, + "belongs_to": "environment" + }, + "accessibles": { + "value": { + "description": "current value of the module", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": true + }, + "status": { + "description": "current status of the module", + "datainfo": { + "type": "tuple", + "members": [ + { + "type": "enum", + "members": { + "DISABLED": 0, + "IDLE": 100, + "WARN": 200, + "UNSTABLE": 270, + "ERROR": 400, + "UNKNOWN": 401 + } + }, + { + "type": "string" + } + ] + }, + "readonly": true + }, + "pollinterval": { + "description": "sleeptime between polls", + "datainfo": { + "min": 0.1, + "max": 120, + "type": "double" + }, + "readonly": false + }, + "_jitter": { + "description": "simulated jitter", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": false + }, + "_hysteresis": { + "description": "simulated hysteresis", + "datainfo": { + "unit": "deg", + "type": "double" + }, + "readonly": false + }, + "_friction": { + "description": "friction", + "datainfo": { + "unit": "N/deg", + "type": "double" + }, + "readonly": false + }, + "_slope": { + "description": "slope", + "datainfo": { + "unit": "N/deg", + "type": "double" + }, + "readonly": false + }, + "_offset": { + "description": "offset", + "datainfo": { + "unit": "N", + "type": "double" + }, + "readonly": false + } + }, + "description": "simulated force", + "implementation": "secop_psi.simdpm.DPM3", + "interface_classes": [ + "Readable" + ] + }, + "res": { + "meaning": { + "key": "resistance", + "link": "http://purl.allotrope.org/ontologies/result#AFR_0006", + "function": "resistance", + "importance": 40, + "belongs_to": "environment" + }, + "accessibles": { + "value": { + "description": "current value of the module", + "datainfo": { + "unit": "Ohm", + "type": "double" + }, + "readonly": true + }, + "status": { + "description": "current status of the module", + "datainfo": { + "type": "tuple", + "members": [ + { + "type": "enum", + "members": { + "DISABLED": 0, + "IDLE": 100, + "WARN": 200, + "UNSTABLE": 270, + "ERROR": 400, + "UNKNOWN": 401 + } + }, + { + "type": "string" + } + ] + }, + "readonly": true + }, + "pollinterval": { + "description": "sleeptime between polls", + "datainfo": { + "min": 0.1, + "max": 120, + "type": "double" + }, + "readonly": false + }, + "_jitter": { + "description": "extra_param: jitter", + "datainfo": { + "type": "double" + }, + "readonly": true + } + }, + "description": "raw temperature sensor on the stick", + "implementation": "secop.simulation.SimBase_res", + "interface_classes": [ + "Readable" + ] + }, + "T": { + "meaning": { + "key": "sample temperature", + "link": "http://purl.allotrope.org/ontologies/result#AFR_0002149", + "function": "temperature_regulation", + "importance": 40, + "belongs_to": "sample" + }, + "accessibles": { + "value": { + "description": "current value of the module", + "datainfo": { + "unit": "K", + "type": "double" + }, + "readonly": true + }, + "status": { + "description": "current status of the module", + "datainfo": { + "type": "tuple", + "members": [ + { + "type": "enum", + "members": { + "DISABLED": 0, + "IDLE": 100, + "WARN": 200, + "UNSTABLE": 270, + "ERROR": 400, + "UNKNOWN": 401 + } + }, + { + "type": "string" + } + ] + }, + "readonly": true + }, + "_calib": { + "description": "calibration name", + "datainfo": { + "type": "string" + }, + "readonly": false + }, + "_abs": { + "description": "True: take abs(raw) before calib", + "datainfo": { + "type": "bool" + }, + "readonly": false + } + }, + "description": "temperature sensor, soft calibration", + "implementation": "secop_psi.softcal.Sensor", + "interface_classes": [ + "Readable" + ] + } + }, + "equipment_id": "uniax_sim.psi.ch", + "firmware": "FRAPPY - The Python Framework for SECoP", + "version": "2021.02", + "description": "[sim] uniaxial pressure device" +}