147 Commits

Author SHA1 Message Date
38b8a7a118 frappy.client.SecopClient: fix setParameterFromString
add missing datatype.export_value

Change-Id: I824d922fdda1ab548e4625982e1485ec4b758cb9
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36172
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-27 15:56:51 +02:00
d73b8bc774 frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-27 15:56:51 +02:00
ceb6e487a8 frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-27 15:56:51 +02:00
eb08e31b62 frappy.client.SecopClient: fix setParameterFromString
add missing datatype.export_value

Change-Id: I824d922fdda1ab548e4625982e1485ec4b758cb9
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36172
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-27 15:56:51 +02:00
4b88ece3be frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-27 15:56:51 +02:00
c22e41c8e2 frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-27 15:56:51 +02:00
701d1f1735 frappy.client.SecopClient: fix setParameterFromString
add missing datatype.export_value

Change-Id: I824d922fdda1ab548e4625982e1485ec4b758cb9
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36172
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-27 15:56:51 +02:00
400b314fc7 frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-27 15:56:51 +02:00
ce3b3ddfd6 frappy-psi.logo: fix IO.communicate reply 2025-05-27 15:51:17 +02:00
3045a11ff9 dil5/logo: fix valve addresses 2025-05-27 15:51:17 +02:00
de773f084d frappy_psi.logo: add comLog
+ fix bug in frappy_psi/dilution_statemachine.py
2025-05-27 15:51:17 +02:00
564bea63e4 DIL5: improve states in the state machine 2025-05-27 15:51:17 +02:00
bd8a417112 use longer, unique uris for USB serial connections 2025-05-27 15:51:17 +02:00
e6263cbcd0 frappy_psi.pfeiffer_new: calculate crc in PfeifferProtocol 2025-05-27 15:51:17 +02:00
7c06bbeec2 haake: fix haake2 and add pumprunning 2025-05-27 14:48:16 +02:00
d6947711c6 increase omit_unchanged_within to 60 s
+ fix a bug reading this from general config file
2025-05-22 08:35:59 +02:00
0938049e89 [WIP] further work on CCU4
Change-Id: Icdd6e253f276a5ade44fb4103306160fc348556b
2025-05-22 08:35:59 +02:00
0053b7847c frappy.client.interactive: no pathlib needed here
allow the argument of the run function to be a simple string

Change-Id: I1d5de3802b6adc22a01a262d4cb017bf7dd084c5
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36343
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
5c93953183 config: add 'include' and 'override'
including a config file and overriding some properties is
helpful when we do not want to modify the original config
but run it with sligthly different properties.

this involves some redesign a.o.:
- modules are collected in a dict instead of a list in
  order for 'override' to find the related module
- checking for duplicates happens in the Collector

Do not warn when included file does not end with '_cfg.py',
as this may be intentional, in case a file is only used
via 'include' and not as cfg file alone.

+ remove unused method Collector.append
+ complain with specific error message when Node is not given

Change-Id: Id568f04d6d84622ef2547412eb6f288fcebf986f
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36357
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-22 08:35:59 +02:00
Jenkins system
b60ad38fd0 [deb] Release v0.20.5 2025-05-22 08:35:59 +02:00
7b0356b4ee frappy.client.interactive: improve updates while driving
- instead to show first current 'value' and 'status', and then
  the changes, show changes only - this way updates appear
  in the expected order
- for this SecopClient.register_callback needs a 'callimmediately'
  argument

Change-Id: I3e91c2c15bca7fee2eba3b1bf1dd27313da3ae29
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36291
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
7faf328141 client: add SecopClient.execCommandFromString
analogous to setParameterFromString
to be used in simple clients where values of structured datatypes
are just python literals

Change-Id: I4936a3c5c0cac45bd7e875c938ce2700b4f4a979
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36262
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
d07406c2fd frappy_psi/ls370res: various bug fixes
- avoid some error messages on disabled channels
- update value of channel switcher properly
- fix bug in set_param method

Change-Id: I16f5cf2d7abce0c0fae17266f0c8e949e8416b7a
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36263
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
dbd4667c87 frappy.client.SecopClient: fix setParameterFromString
add missing datatype.export_value

Change-Id: I824d922fdda1ab548e4625982e1485ec4b758cb9
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36172
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-22 08:35:59 +02:00
Konstantin Kholostov
6e7514b718 installer: add recipe to build macOS app bundle
Change-Id: Ief4401626db293fecc37f1d1ec72cf295b55fccf
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36060
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
2025-05-22 08:35:59 +02:00
c49f15ce64 Add DIL5 Statemachine and LOGO 2025-05-22 08:35:59 +02:00
3bae6f8d7f [WIP] fi furnace improvements
- still under development

Change-Id: I5fc22f041fb136b549016f510f06ea703122bee5
2025-05-22 08:35:59 +02:00
e46291eef6 change prot for dummy to 5000
Change-Id: If35bbe6783fe133c0c6c87f402ba70aec00fa964
2025-05-22 08:35:59 +02:00
af746f59bb dummy_cfg.py: add test case with big enum
+ fix undefined value in frappy_psi.ls370res.Switcher

Change-Id: I59f2814b945533c487999f9af638e0fb2040e862
2025-05-22 08:35:59 +02:00
a2a4d4332a more demo test cases
- added them to cfg/dummy_cfg.py
+ treat enum correctly in SecopClient.setParameterFromString

Change-Id: Ia5b2d8d3a21c3215cb93d90975086eb9995b1543
2025-05-22 08:35:59 +02:00
aff6a2381a logdif.py: use single key to stop or continue
Change-Id: I53b3254074eda7491dd16bbc39168960b0980e39
2025-05-22 08:35:59 +02:00
eb8eee02d1 follow-up change to 35931: make Proxy a Module
Proxy must be a class, because of the new check for configured
'cls' inheriting from Module. Use Proxy.__new__ to implement
this.

Change-Id: I4bb036afc2ce92187a9049dff0a6f22b20c3a260
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36104
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
2025-05-22 08:35:59 +02:00
9a818ab40b revert 'change to new visibility spec'
nicos is not yet ready for this

Change-Id: Ibfbb7e32e06a6e0616ded8342bc5844fd531944f
2025-05-22 08:35:59 +02:00
PREVENT_DEFAULT
dc87a50c76 add sr830 addons cfg 2025-05-22 08:35:59 +02:00
5974d759b0 frappy_psi.parmod: extract a tuple element as own moudle
Change-Id: I8d904ed21f8a5c16ae71daf30c9a1ea42876b451
2025-05-22 08:35:59 +02:00
396bbe9982 frappy_psi.ionopimax.AnalogInput: set value range on datainfo
Change-Id: I1e3da956e829f69a0af416b7beadb81bd6bc0cb1
2025-05-22 08:35:59 +02:00
ae98fa174c frappy_psi.furnace: special classes PTXgauge and PRtransmitter
move some initialization from cfg file to source code
+ make 'out of calibrated range' and 'sensor break' more generic

Change-Id: I3e92100fdb9c983f82665de9d8e063609cd7af5a
2025-05-22 08:35:59 +02:00
83ad7b1638 change to new visibility spec
+ visibiliy is no longer an EnumType, as this would break
  the specs

Change-Id: I1197c82f31c33c210fdcda0b49a0c38027880d77
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36088
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
257b24a480 make sure unexported modules are initialized
take the opportunity for a small redesign:

- create a new method build_descriptive_data which
  calls secnode.get_modules also on unexported modules.

+ cache descriptive data

Change-Id: I4a0b8ac96108463dc0c800bb11a404206c26b092
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/36089
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
2025-05-22 08:35:59 +02:00
29f0e18c1e better dummy server for seaweb tests
- new config file dummy
- frappy_demo.test.Temp now creates WARN and ERROR status
  and may be disabled

Change-Id: Ibc7bb565f18c2c12cdc2a77bea1ee1bf1cc8bd41
2025-05-22 08:35:59 +02:00
144083a54a ionopimax: bug fixes 2025-05-22 08:35:59 +02:00
748a39f932 frappy_psi.ionopimax redesign
Change-Id: I46b62522c24ad9f0352ba7a784d39ffd1cb79ef3
2025-05-22 08:35:59 +02:00
8cea974f49 [WIP] further fixes for linse-fi 2025-05-22 08:35:59 +02:00
5768f096a5 frappy-play: fix import order 2025-05-22 08:35:59 +02:00
94859fe2ef [WIP] fixes for linse-fi
Change-Id: Iac28e9654a764331cd903896879834cd6127a919
2025-05-22 08:35:59 +02:00
fcf867675e fixes on picontrol and tdkpower
Change-Id: Ia891e7df23d8408b857dac795ed0ad9973ccf993
2025-05-22 08:35:59 +02:00
09dce1aabd fixes on small furnace 2025-05-22 08:35:59 +02:00
eefe271cba add fi_cfg.py (ILL furnace)
Change-Id: I8720dbeb3f29b07eaeae59558c58b2fa87096dc9
2025-05-22 08:35:59 +02:00
c42dd41dec WIP frappy_psi/tdkpower
Change-Id: I80d1beb0fae2a1cdd2aa5fabc5d31c651c2cb3e7
2025-05-22 08:35:59 +02:00
c545e36633 peus-plot: optionally give x-range as 2nd argument
Change-Id: I445ef00487fc34343f5a0333643f61753a1c4948
2025-05-22 08:35:59 +02:00
ef985f094c ultrasound.ControlRoi: fix control mechanism
Change-Id: I9d01de260d3bdc63eb1004ba4f714d38d1c0508c
2025-05-22 08:35:59 +02:00
l_samenv
55d399241c cfg/ls370test update 2025-05-22 08:35:59 +02:00
aea56bb16a improve error messages on module creation
- add name when target and value datatype are incompatible
- check that module class inherits from Module

Change-Id: I4edbdff1c250b64b74b1adf7287f9659dff69b26
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35931
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
3b8f3586cf fix overriding Parameter with value
a property declared in a base class may be overriden
with a parameter in a subclass. this is already allowed.
if then, in the subsubclass it is overridden by a bare value,
it fails.

Patchset 1: add a test for this
Patchset 4: add the fix

Change-Id: Ia5a26076a9ee98439932643a03878342d56f8396
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35932
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
56bee015b3 ultrasound.PE: fix control loop
Change-Id: I2b2bdf4ad48887ae256a68119f53e6a38048ce1e
2025-05-22 08:35:59 +02:00
1febb7bacf [WIP] ccracks / ccu4: split ccu() into nv(), he() and flow()
Change-Id: I346330a5f350bf03eefe86c8e890b59afaaaa231
2025-05-22 08:35:59 +02:00
df4dec6db5 frappy_psi.sensirion: fix a typo
Change-Id: I259151b7a1b908c8289ecb88d2d3d4e6d9e45c12
2025-05-22 08:35:59 +02:00
7feee607e0 ultrasound.PulseEcho: fix issue with roi intervals
now it should be time +- 0.5 * size
2025-05-22 08:35:59 +02:00
7c938a3bb1 frappy_psi/adq_mr (ultrasound): exit on reboot error message
otherwise the error message is confusing
+ remove CR from line endings in adq_mr.py

Change-Id: Ia465a26803a92677383969ff620ef35e58f1a5ec
2025-05-22 08:35:59 +02:00
904dd96f95 ls370res: do not raise in read_rdgrng error when channel is disabled
Change-Id: I565e5cd74cf7f12bfd5eea9e8867117154461017
2025-05-22 08:35:59 +02:00
l_samenv
04fd8743a2 ah2700: make loss its own module 2025-05-22 08:35:59 +02:00
Ultrasound PC
b59a98e4dc bin/us-plot: fix usage message 2025-05-22 08:35:59 +02:00
Ultrasound PC
285c5c330f ultrasound: change control roi0 to a Readable (2)
+ remove cfg/PEUS.py
+ fix equipment_id of PEUS
+ add header to frappy_psi.iqplot
2025-05-22 08:35:59 +02:00
Ultrasound PC
e3e27881d3 ultrasound: change control roi0 to a Readable
+ remove cfg/PEUS.py
+ fix equipment_id of PEUS
2025-05-22 08:35:59 +02:00
Ultrasound PC
d26434f797 improve ultrasound plot clients
- make plot window not to raise to the front on replot
- chmod +x
2025-05-22 08:35:59 +02:00
c3b6aca7bc ultrasound: reworked after tests
- new classes in frappy_psi/ultrasound.py and frappy_psi/adq.mr.py
- add signal plottter
- move clients to bin/ directory

Change-Id: I8db8e5ebc082c346278f09e0e54504e070655f14
2025-05-22 08:35:59 +02:00
l_samenv
71372a450b gas10k / mercury.HeaterUpdate: switch off loop on startup
the class frappy_psi.mercury.HeaterUpdate is used for the output
of a soft pid loop. set target to 0 to switch off the loop
on startup.
2025-05-22 08:35:59 +02:00
l_samenv
79dbfdfad0 further work on needle valve, pump and lakeshore 2025-05-22 08:35:59 +02:00
5c7fe37807 change again how to exit logdif.py
Change-Id: I442ca8c2ee7ca25ff98a0e84df2688a55a0dcec9
2025-05-22 08:35:59 +02:00
e75b8b0b10 stop poller threads on shutdown: cosmetics
cosmetics after gerrit

Change-Id: I4d982f83e3fe5a8c8c821ac718e51b9a58de2a62
2025-05-22 08:35:59 +02:00
8d62375483 frappy_psi.ultrasound: add input_delay and other improvments
Change-Id: I6cb5690d82d96d6775fcb649fc633c4039932463
2025-05-22 08:35:59 +02:00
7c60daa568 frappy_psi.ultrasound: after rework (still wip)
Change-Id: I200cbeca2dd0f030a01a78ba4d38c342c3c8c8e3
2025-05-22 08:35:59 +02:00
fdc868c2d7 frappy-scan: resolve ip numbers to names
Change-Id: I07bf7c274aeb52f2aaa58e8aa2f3bcb2788556ee
2025-05-22 08:35:59 +02:00
b66acd4d73 stop poller threads on shutdown
make sure module methods are not called after shutdownModule

+ fix: when mod.enablePoll is False, pollInfo is None
  therefore we have to check before access

Change-Id: I83b28607b25996376939175be8abf0c5b27bcac1
2025-05-22 08:35:59 +02:00
l_samenv
da24c6244e frappy_psi.drums: changes after test
when trying with Marcel, we needed these fixes
2025-05-22 08:35:59 +02:00
738c4c7a51 config: Mod() should return config dict
this helps for coded configuration

Change-Id: I07bdf72f77082f31ee86192faec63df706dcbf56
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35803
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-22 08:35:59 +02:00
93fa2c818b config: validate value and default of parameters
The Parameter Properties 'value', 'default' and 'constant'
have ValueType, so they are not checked in the setProperty call.
We have to do this explicitly in Module._add_accessible.

Change-Id: I1e35adf2fe539411b4aebacd813adb07497de95b
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35797
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-22 08:35:59 +02:00
Georg Brandl
88eb6e93bd remove wrong <weight> from fonts on Qt6
Change-Id: Ib94b2ed74598b9f54c2361e61bfa940e60bd7c62
2025-05-22 08:35:59 +02:00
Georg Brandl
5242c903ae debian: update compat
Change-Id: I172dff4e0239ce90fe7b1c19fc800ba98f116270
2025-05-22 08:35:59 +02:00
4aec05c86b core: simplify test for methods names
The test for method names 'read_<param>' and 'write_<param>'
without a defined parameter is simplified. We do not check
anymore method names from base classes. Base classes
inheriting from HasAccessible are checked anyway at the
place they are defined.

+ add a test for it
+ move some tests to a new file test_all_modules.py, as
  test_modules.py is getting too long
+ fix missing doc string (frappy.simulation.SimDrivable.stop)

Change-Id: Id8a9afe5c977ae3b1371bd40c6da52be2fc79eb9
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35503
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-05-22 08:35:59 +02:00
a43590a3ff core: alternative approach for optional accessibles
This is meant to replace change 33375.
Optional commands and parameters may be declared with the argument
optional=True. In principle, optional commands are not really needed
to be declared, but doing so is nice for documentation reasons
and for inherited accessible properties.

Optional parameters and commands can not be used and are not
exported als long as they are not overridden in subclasses.

- add a test for this
+ fix an issue with checking for methods like read_<param> without
  <param> being a parameter

Change-Id: Ide5021127a02778e7f2f7162555ec8826f1471cb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35495
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
2025-05-22 08:35:59 +02:00
l_samenv
76e46f7c84 merged changes for lakeshore and ccu4 2025-05-22 08:35:59 +02:00
l_samenv
baf55baffc improve error handling 2025-05-22 08:35:59 +02:00
l_samenv
f7f77b168f gui: add org- and app-name to QtApplication
for a better path name of stored configuration
2025-05-22 08:35:59 +02:00
ebcf95e31e frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-05-22 08:35:59 +02:00
ee29bea821 add units to jtccr 2025-05-21 11:57:18 +02:00
4fc4cd3687 haake: make 2 versions depending on extra T sensor 2025-05-19 11:34:12 +02:00
1145493e81 varioxb: uncomment motor setup with triple quote 2025-05-08 09:07:31 +02:00
3f7ef438e3 ori4: revert removing '.' from rel_path
not sure if this is correct
2025-05-08 09:03:45 +02:00
90dc5359b9 dil2/3/4: make He3 pump visible 2025-05-08 08:59:46 +02:00
6d63c4e0df frappy_psi.phytron: improve clear_errors message 2025-03-20 12:53:15 +01:00
98fa19ce3b WIP new version of ultrasound
Change-Id: Iadb83396a64e277f6f0a37f7a96d92105648c4fe
2025-02-13 09:39:49 +01:00
7f83f76d38 frappy_demo.test: add parameter for testing error messages
Change-Id: Ifbf9d6829be373417d3bf1ff398d2aee283d8c9a
2025-02-13 09:39:49 +01:00
0ab849d0cf config: do not override equipment_id with name
In the previous code, the equipment_id was overridden by the
server name when the interface argument was given over
the commandline. This was leftover from the previous config
file format, where the config files not neccessarly needed
an equipment_id.

Change-Id: I2fc248372a7d2f61cc0690804268d6d066a0a9fa
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35391
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-02-13 09:39:49 +01:00
8ee49caba5 equipment_id for merged configs and routed nodes
Add a new custom module property 'original_id' indicating
the equipment_id the modules originally belongs to.
This property is only given, when distinct from the equipment_id
of the SEC node.
It happens when multiple config files are given, for all modules
but the ones given in the first file, and for routed modules,
when  multiple nodes are routed or own modules are given.

+ fix an issue in router: additional modules were ignore in case
of a single node.

+ small cosmetic changes in config.py reducing IDE complains

Change-Id: If846c47a06158629cef807d22b91f69e4f416563
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35396
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-02-13 09:39:49 +01:00
b1de9218bd take over changes from ultrasound PC
Change-Id: I1eae717a5963e618d87ddf52db991d428a046d24
2025-02-13 09:39:49 +01:00
8eaad86b66 WIP: old oxford devices (ILM, IPS, IGH...)
Change-Id: I4ca0dc6149d257818d300db4d886a1e33e8210be
2025-02-13 09:39:49 +01:00
85400a2777 move start_ramp_to_target to SimpleMagfiield
Change-Id: Iab3fe8738c560bf5ac2f11a4a34428a8ffd6a7c2
2025-02-13 09:39:49 +01:00
dda4afbe5b frappy_psi.ccu4: some smaller updates
Change-Id: I128ac57aad951fd8ad3bdf663c69c85644063645
2025-02-13 09:39:49 +01:00
9b079ddf4b make UPD listener work when 'tcp://' is omitted on interface
'tcp://' may be omitted on interfaces
add missing 'tcp://' earlier in code, so we do not need to check
for missing 'tcp://' again.

Change-Id: Ie9b4dbd168aebdb6edfe71dbd2cfc25e9229fe67
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35321
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2025-02-13 09:39:49 +01:00
898da75b89 fix bug in change 35001 (better error message)
fix bug in error message

Change-Id: I8151d20f840818fc26d42348f73e740cdb20e03d
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35287
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-02-13 09:39:49 +01:00
a7a846dfba frappy_psi.sea: fix case when bool is implemented as text
introduce SeaBool for this

Change-Id: I9c6b6ee7d33f11b173d612efc044fce8a563f626
2025-02-13 09:39:49 +01:00
6da671df62 an error on a write must not send an error update
Change-Id: I07a991bcf26e87121160a2e604f8842eba23ebaf
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35281
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-02-13 09:39:49 +01:00
bdb14af4af pylint: increase max number of positional arguments
Change-Id: Id88270b3c3c1efb56f47def733c1e9c745f1ab18
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35282
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
2025-02-13 09:39:11 +01:00
e57ad9826e better message when a parameter is overridden by an invalid value
happens e.g. then writing status = StatusType(...) instead of
status = Parameter(datatype=StatusType(...)) on the class level

+ improve doc strings

Change-Id: I05a0b0b0da4438a40b525da40018bb5b09fd5303
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35001
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2025-02-13 09:39:11 +01:00
8775103bf8 frappy_psi.pulse: fix fatal errors
even when a module is work in progress, it should not raise
an error on import

Change-Id: I2f91301ba2b0c574ea344c36a74da0f893aa326d
2025-02-13 09:39:11 +01:00
l_samenv
5636a76152 adapt temperature and temperature_regulation importance
- temperature_regulation on VTI should have higher importance (27)
  than temperature on sample stick, when Drivable (25)
2025-02-13 09:39:11 +01:00
l_samenv
745cc69e9e sea cfg: set visibility of calibration points to expert 2025-02-13 09:39:11 +01:00
l_samenv
b4c0a827f0 ma7: use new config type with sea_path and frappy.sea.LscDrivable 2025-02-13 09:39:11 +01:00
d57416a73e frappy_psi.sea: more improvements
- add sea_path property
- add LscDrivable (config of these modules is easier to understand)

Change-Id: I616dc94de6e784f6d8cfcf080d9a8408cbf73d93
2025-02-13 09:39:11 +01:00
l_samenv
8dcf6ca658 sea: fix parameter name mapping
- rel_path = ['tm', '.', 'set'] should mean:

'tm': tm parameters first, with /<obj?/tm as main value
'.': then all parameters directly at top level, except 'set'
'set': all parameters  below 'set'
driving happens at object level

- better name mangling (the 1st appearance of the same shortname
  is kept short)
2025-02-13 09:39:11 +01:00
bc66a314c4 logdif.py: leave on every input except bare return
Change-Id: I3d53c7b45fb9ef09a61be5af13a2cdc4d32d5c7d
2025-02-13 09:38:20 +01:00
6fac63d769 frappy.server: remove comment about opts in SecNode/Dispatcher
The options given in the node configuration may be used
for both SecNode (equipment_id) and Dispatcher (when the
frappy.protocol.router.Router is used as dispatcher).
It is correct that both remove the options known to them.

Change-Id: I2a34073e4e5490dcf8db577d9cb74788c0cb657b
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34989
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:04:26 +01:00
e41692bf2c frappy.server: use server name for SecNode name
no need to configure the name of SecNode and Dispatcher

Change-Id: I5199bbd77c74e4fe56b527a5a565a8285b0d831e
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34988
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:04:26 +01:00
dff3bd2f24 frappy.server bug fix: server name must not be a list
followup error from change 34893
this bug appears in HasComlog, only when comlog is switched on

Change-Id: Ic0db5ae0b0af9981b0c91ebacf2eb6cd704aaa58
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34987
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:04:26 +01:00
b67e5a9260 updated sync_branches for sinq branch
Change-Id: Ic3330c4049b527dc98704fbbd94180dcd4930cb1
2024-11-28 18:04:26 +01:00
4815f4e6b4 follow up change for 'better order of accessibles' (34904)
slight change to make it compatible with py 3.6/3.7, where
reversed(<dict>) was not allowed.

Change-Id: Id440870b5523a866b3afb470ba5db9cd6a9bb0ec
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/35002
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:04:26 +01:00
e8ec9b415a improve lakeshore demo
use super call for read_status

TODO: update tutorial!
Change-Id: I2dd5631908dc370c6e6286587099e25a0e5ee867
2024-11-28 18:03:37 +01:00
5b9e36180e frappy_psi.bkpower: improve doc
Change-Id: I0736d1d8a40b0140bfdbf5aca189b8ddc5b22973
2024-11-28 18:03:37 +01:00
f1b59e4150 fix bug when overriding a property with bare value
the bare value must be converted to a updated property.
add also a test for this

Change-Id: I261daaaa8e12d7f739d8b2e8389c1b871b26c5b3
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34985
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
17070ca732 add sim-server again based on socketserver
- fix ls370test config file
+ fix issues with frappy_psi.ls370res
+ add frappy_psi.ls370sim

Change-Id: Ie61e3ea01c4b9c7c1286426504e50acf9413a8ba
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34957
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Jenkins system
d618fafe4b [deb] Release v0.20.4 2024-11-28 18:03:37 +01:00
Jens Krüger
dd1dfb3094 Lib/config: Create a list of pathes only for confdir
Under some condition (no general config file) it's possible that the
piddir and logdir as well are lists of pathes which creates some errors
during the server start

This problems occurs at least in NICOS test suite where no general
config file is defined.

Change-Id: I94c5db927923834c1546dbc34e2490b07b0bf111
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34952
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Jens Krueger <jens.krueger@tum.de>
2024-11-28 18:03:37 +01:00
8d6617e288 frappy.client: catch all errors in handleError callback
put try/execpt around handleError callback

Change-Id: I3d97f085556665189da848e52a7148248f55eb0e
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34955
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:03:37 +01:00
Jens Krüger
fdec531c99 PSI: Fix import error on ThermoFisher module
Change-Id: I691d8f5057fdb19ba14c109399417a7ee9962637
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34954
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:03:37 +01:00
a246584c4a frappy.lib.multievent: avoid deadlock
use RLock instead of Lock, as queued actions might call
the set/clear methods recursively

Change-Id: Id43aa8669955e6be9f61379d039a4f65eb7b2dc4
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34950
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Georg Brandl
00ef174292 remove unused file
Change-Id: I969bfb22f2196227abe8c5ecef628a15e6eb75f1
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34939
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Jenkins system
ada66f4851 [deb] Release v0.20.3 2024-11-28 18:03:37 +01:00
Alexander Zaft
a9be6475b1 add generalConfig to etc
Change-Id: I768b136c803d5e197e3653d1b84e147b62a97676
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34924
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
2024-11-28 18:03:37 +01:00
Georg Brandl
f380289a84 fixup test for cfg_editor utils to run from non-checkout, and fix names, and remove example code
Change-Id: I6224244392e2a2d0928065ba24abcbe822096084
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34934
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
2024-11-28 18:03:37 +01:00
Jenkins system
528d80652c [deb] Release v0.20.2 2024-11-28 18:03:37 +01:00
Alexander Zaft
7c6df58906 fix frappy-server cfgfiles command
frappy-server <name> errors after 34893

Change-Id: Ifba758fbabc3aef32e20b683f1c1edbfea711a75
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34913
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
2024-11-28 18:03:37 +01:00
Georg Brandl
1851c0ac43 server: better handling of cfgfile argument
No reason to keep stringly-typed data on that level

Change-Id: Iba8d88301bf36ef6051031d1916d1bac84ede546
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34893
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
2024-11-28 18:03:37 +01:00
880d472a4a generalConfig: fix the case when confdir is a list of paths
convert all env variable values containing ':' into a list of paths
+ fix one case where an env variable is not converted to a Path
+ remove unused _gcfg_help

Change-Id: Ibc51ab4606ca51e0e87d0fedfac1aca4952f3270
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34872
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2024-11-28 18:03:37 +01:00
Alexander Zaft
25ff96873b server: service discovery over UDP.
implement RFC-005
- server broadcasts once on startup and answers to broadcasts
- small tool for listening on the port and sending broadcasts

Change-Id: I02d1184d6be62bef6f964eb9d238220aef062e94
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34851
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
2024-11-28 18:03:37 +01:00
Georg Brandl
82881049c4 systemd: enable indication of reloading/stopping
Change-Id: I6dd1b3a50234fb0304fb1a5318f2f22d35d464ec
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34896
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
2024-11-28 18:03:37 +01:00
Alexander Zaft
60c9737cfe server: fix windows ctrl-c
thread.join() blocks indefinetely, not allowing python to handle the
interrupt. Same is true for sleep on windows, but when we only sleep a
second, this is fine. Instead of joining the threads, keep track of them
manually.

Change-Id: I559fe06d9ce005a15388c881e4f076d996aea9dc
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34894
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Alexander Zaft
632db924eb generalconfig: streamlined config discovery
determine generalconfig file location in order:
  - command line argument
  - environment variable
  - git location (../cfg)
  - local location (cwd)
  - global location (/etc/frappy)

Change-Id: Ie34bcbd5188837075ee7bb7d5029d676ae72378e
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34839
Reviewed-by: Bjoern Pedersen <bjoern.pedersen@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Alexander Zaft
261121297b Revert "config: allow using Prop(...)"
This reverts commit ba59bd5498.

Reason for revert: unnecessary

Change-Id: I4bf46a1de2e699049572f376e84fa39db5dae76c
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34888
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
Alexander Zaft
1bd243f3d2 config: fix typo
Change-Id: Ie90993d9b2d387780fa3faa28fd8d4523f7fc866
2024-11-28 18:03:37 +01:00
Alexander Zaft
7c3f9f7196 config: allow using Prop(...)
Still maps to the same logic, but it might be a bit confusing to
configure properties with prop = Param(...)

Change-Id: I6bde6a0b015095a8b765d98cb2780f0d42de7e6e
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34886
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
2024-11-28 18:03:37 +01:00
9074dfda9d fix playground
- fix initialization
- add description

Change-Id: Ic210c26edfec709bafa902e32eae04350d571acd
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34874
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
de32eb09e6 better order of accessibles: 'value' 'status' and 'target' first
- predefined parameters/commands appear first, in the order
  defined in frappy.params.PREDEFINED_ACCESSIBLES
- other (custom) parameters by inheritance order
- remove paramOrder attribute (not used currently)

Change-Id: If4c43189e4837dba057dc0a430ac6c3d1ae10829
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34904
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
2e97f0f0ce frappy_psi.sea: bugfix: revert change of updateEvent to udpateItem
revert some of change 34813
SeaClient is based on ProxyClient, not SecopClient
-> updateItem is not defined there

Change-Id: Ib3049038481917ec7a11b9fb2d285cedff5febbb
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34873
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
2024-11-28 18:03:37 +01:00
0b06acf304 add ori2 2024-11-26 15:16:53 +01:00
dmc
cc90291358 varioxb: fix config, om not yet available 2024-11-26 13:59:05 +01:00
03b4604643 fix description of ts in ma11stick 2024-11-15 09:14:54 +01:00
125 changed files with 1997 additions and 5613 deletions

View File

@@ -24,14 +24,12 @@
import sys
import argparse
import socket
from pathlib import Path
# Add import path for inplace usage
sys.path.insert(0, str(Path(__file__).absolute().parents[1]))
from frappy.client.interactive import init, run, clientenv, interact
from frappy.protocol.discovery import scan
def parseArgv(argv):
@@ -39,9 +37,6 @@ def parseArgv(argv):
parser.add_argument('-i', '--include',
help='file to execute after connecting to the clients', metavar='file',
type=Path, action='append', default=[])
parser.add_argument('-s', '--scan',
help='hosts to scan for (-s subnet for all nodes in subnet)',
action='append', default=[])
parser.add_argument('-o', '--only-execute',
help='Do not go into interactive mode after executing files. \
Has no effect without --include.', action='store_true')
@@ -51,38 +46,9 @@ def parseArgv(argv):
return parser.parse_args(argv)
def own_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(('10.254.254.254', 1))
return s.getsockname()[0]
except Exception:
return '127.0.0.1'
finally:
s.close()
args = parseArgv(sys.argv[1:])
nodes = args.node
hosts = args.scan
if not nodes and not hosts:
hosts = ['localhost']
if hosts:
answers = []
for host in hosts:
ans = scan()
if host == 'subnet': # all in subnet
answers.extend(ans)
else: # filter by ip
ip = socket.gethostbyname(host)
if ip == '127.0.0.1':
ip = own_ip()
answers.extend(a for a in ans if a.address == ip)
nodes.extend(f'{h.hostname}:{h.port}' for h in answers)
success = init(*nodes)
success = init(*args.node)
run_error = ''
file_success = False

View File

@@ -23,20 +23,54 @@
"""SEC node autodiscovery tool."""
import argparse
import json
import os
import select
import socket
import sys
from pathlib import Path
# Add import path for inplace usage
sys.path.append(str(Path(__file__).absolute().parents[1]))
from frappy.protocol.discovery import scan, listen
from collections import namedtuple
from time import time as currenttime
UDP_PORT = 10767
Answer = namedtuple('Answer',
'address, port, equipment_id, firmware, description')
def decode(msg, addr):
msg = msg.decode('utf-8')
try:
data = json.loads(msg)
except Exception:
return None
if not isinstance(data, dict):
return None
if data.get('SECoP') != 'node':
return None
try:
eq_id = data['equipment_id']
fw = data['firmware']
desc = data['description']
port = data['port']
except KeyError:
return None
addr, _scanport = addr
return Answer(addr, port, eq_id, fw, desc)
def print_answer(answer, *, short=False):
try:
hostname = socket.gethostbyaddr(answer.address)[0]
address = hostname
numeric = f' ({answer.address})'
except Exception:
address = answer.address
numeric = ''
if short:
# NOTE: keep this easily parseable!
print(f'{answer.equipment_id} {answer.hostname}:{answer.port}')
print(f'{answer.equipment_id} {address}:{answer.port}')
return
numeric = f' ({answer.address})' if answer.address == answer.hostname else ''
print(f'Found {answer.equipment_id} at {answer.hostname}{numeric}:')
print(f'Found {answer.equipment_id} at {address}{numeric}:')
print(f' Port: {answer.port}')
print(f' Firmware: {answer.firmware}')
desc = answer.description.replace('\n', '\n ')
@@ -44,6 +78,51 @@ def print_answer(answer, *, short=False):
print('-' * 80)
def scan(max_wait=1.0):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# send a general broadcast
try:
s.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
('255.255.255.255', UDP_PORT))
except OSError as e:
print('could not send the broadcast:', e)
# we still keep listening for self-announcements
start = currenttime()
seen = set()
while currenttime() < start + max_wait:
res = select.select([s], [], [], 0.1)
if res[0]:
try:
msg, addr = s.recvfrom(1024)
except socket.error: # pragma: no cover
continue
answer = decode(msg, addr)
if answer is None:
continue
if (answer.address, answer.equipment_id, answer.port) in seen:
continue
seen.add((answer.address, answer.equipment_id, answer.port))
yield answer
def listen(*, short=False):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if os.name == 'nt':
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
else:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(('0.0.0.0', UDP_PORT))
while True:
try:
msg, addr = s.recvfrom(1024)
except KeyboardInterrupt:
break
answer = decode(msg, addr)
if answer:
print_answer(answer, short=short)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--listen', action='store_true',
@@ -57,5 +136,4 @@ if __name__ == '__main__':
for answer in scan():
print_answer(answer, short=short)
if args.listen:
for answer in listen():
print_answer(short=short)
listen(short=short)

View File

@@ -1,40 +0,0 @@
Node('measure.frappy.demo',
'''Measureable demo''',
'tcp://10770',
)
Mod('control',
'frappy_demo.acquisition.Controller',
'simple demo controller',
channels = {'first': 'chan1', 'second': 'chan2', 'third': 'chan3'},
pollinterval = 1,
)
Mod('chan1',
'frappy_demo.acquisition.Channel',
'simple channel demo',
goal = 50,
goal_enable = True,
pollinterval = 1,
)
Mod('chan2',
'frappy_demo.acquisition.Channel',
'simple channel demo',
pollinterval = 1,
)
Mod('chan3',
'frappy_demo.acquisition.Channel',
'simple channel demo',
pollinterval = 1,
)
Mod('single',
'frappy_demo.acquisition.SimpleAcquisition',
'Acquisition demo',
pollinterval = 1,
goal = 20,
goal_enable=True,
acquisition_key='single',
)
Mod('ng',
'frappy_demo.acquisition.NoGoalAcquisition',
'Acquisition demo',
pollinterval = 5,
)

View File

@@ -3,16 +3,20 @@ Node('ah2700.frappy.psi.ch',
)
Mod('cap_io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3002'
'frappy_psi.ah2700.Ah2700IO',
'',
uri='linse-976d-ts:3006',
)
Mod('cap',
'frappy_psi.ahcapbridge.AH2700',
'frappy_psi.ah2700.Capacitance',
'capacitance',
io='cap_io',
loss_module = 'loss',
freq_module = 'freq',
io = 'cap_io',
)
Mod('loss',
'frappy_psi.parmod.Par',
'loss parameter',
read='cap.loss',
unit='deg',
)

View File

@@ -1,20 +0,0 @@
Node('ah2700.frappy.psi.ch',
'Andeen Hagerlin 2700 Capacitance Bridge',
)
Mod('cap_io',
'frappy_psi.ah2700.Ah2700IO',
'',
uri='linse-leiden-ts:3002',
timeout=60,
)
# this creates also cap_freq and cap_loss
Mod('cap',
'frappy_psi.ah2700.Capacitance',
'capacitance',
io = 'cap_io',
loss_name='loss',
freq_name='freq',
)

View File

@@ -16,7 +16,7 @@ Mod('ah',
'frappy_psi.sea.SeaReadable', '',
io='sea_addons',
sea_object='cap',
extra_modules = ['cap', 'loss', 'freq']
extra_modules = ['cap', 'loss']
)
Mod('cap',
@@ -32,12 +32,6 @@ Mod('loss',
single_module='ah.loss',
)
Mod('freq',
'frappy_psi.sea.SeaWritable', '',
io='sea_addons',
single_module='ah.freq',
)
Mod('capslope',
'frappy_psi.sea.SeaReadable', '',
io='sea_addons',

View File

@@ -1,31 +0,0 @@
Node('ahtwo.frappy.psi.ch',
'Andeen Hagerlin 2700 and 2550 Capacitance Bridges',
)
# TODO: adapt names (cap, cap2) to your experiment
Mod('cap_io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3002'
)
Mod('cap',
'frappy_psi.ahcapbridge.AH2700',
'capacitance',
io='cap_io',
loss_module = 'loss',
freq_module = 'freq',
)
Mod('cap2_io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3001'
)
Mod('cap2',
'frappy_psi.ahcapbridge.AH2550',
'capacitance',
io='cap2_io',
loss_module = 'loss2',
)

View File

@@ -1,15 +0,0 @@
Node('haake.frappy.psi.ch',
'additional haake waterbath',
)
Mod('haake_io',
'frappy_psi.haake.HaakeIO',
'',
uri='sans-sample-ts:3006',
)
Mod('T2',
'frappy_psi.haake.TemperatureLoop',
'second haake',
io = 'haake_io',
)

View File

@@ -3,15 +3,8 @@ Node('AH2700Test.psi.ch',
'tcp://5000',
)
Mod('io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3002'
)
Mod('cap',
'frappy_psi.ahcapbridge.AH2700',
'frappy_psi.ah2700.Capacitance',
'capacitance',
io='io',
loss_module = 'loss',
freq_module = 'freq',
uri='ldmse3-ts:3015',
)

View File

@@ -1,119 +1,219 @@
# by id (independent of plug location, but may not neccessarly be unique)
# to verify just do:
# ls /dev/serial/by-id
# by ID (independent of plug location)
turbo_uri = '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A601PCGF-if00-port0'
press_uri = '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AH07445U-if00-port0'
itc_uri = '/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0'
lsc_uri = '192.168.1.2:7777'
logo_ip = '192.168.0.3'
# by plug location would also be possible (/dev/serial/by-path)
# by plug location:
#turbo_uri='/dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0-port0'
#press_uri = '/dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0-port0'
#itc_uri = '/dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3:1.0-port0'
# over USB (does not work anymore)
#lsc_uri='serial:///dev/ttyACM1?baudrate=57600+parity=odd+bytesize=7+stopbits=1',
Node('dil5.psi.ch',
'dil5 on linse-dil5',
'dil5 with state machine for condensing and removing',
interface='tcp://5000',
secondary = ['ws://8010']
)
Mod('logo',
Mod('io',
'frappy_psi.logo.IO',
'',
ip_address = logo_ip,
ip_address = "192.168.0.3",
tcap_client = 0x3000,
tsap_server = 0x2000
)
Mod('V1',
'frappy_psi.logo.DigitalActuator',
'frappy_psi.logo.Valve',
'Valves',
io = 'logo',
feedback_addr ="V1025.0",
output_addr ="V1064.3"
io = 'io',
vm_address_input ="V1025.0",
vm_address_output ="V1064.3"
)
Mod('V2',
'frappy_psi.logo.DigitalActuator',
'dil bypass',
io = 'logo',
feedback_addr ="V1024.2",
output_addr ="V1064.0",
'frappy_psi.logo.Valve',
'Valves',
io = 'io',
vm_address_input ="V1024.2",
vm_address_output ="V1064.0",
)
Mod('V4',
'frappy_psi.logo.DigitalActuator',
'compressor to dump',
io = 'logo',
# feedback seems not to work
output_addr ="V1064.7",
target_addr ="V404.1",
'frappy_psi.logo.Valve',
'Valves',
io = 'io',
vm_address_input ="V1024.5",
vm_address_output ="V1064.7",
)
Mod('V5',
'frappy_psi.logo.DigitalActuator',
'compressor input',
io = 'logo',
feedback_addr ="V1024.4",
output_addr ="V1064.2",
'frappy_psi.logo.Valve',
'Valves',
io = 'io',
vm_address_input ="V1024.4",
vm_address_output ="V1064.2"
)
Mod('V9',
'frappy_psi.logo.DelayedActuator',
'dump output',
io = 'logo',
delay_addr = 'VW24',
feedback_addr ="V1024.3",
output_addr ="V1064.5",
target_addr ="V404.3",
'frappy_psi.logo.Valve',
'Valves',
io = 'io',
vm_address_input ="V1024.3",
vm_address_output ="V404.1",
)
Mod('forepump',
'frappy_psi.logo.DigitalActuator',
'forepump',
io = 'logo',
output_addr ="V1064.6",
target_addr ="V404.4",
Mod('pump',
'frappy_psi.logo.FluidMachines',
'Pump',
io = 'io',
vm_address_output ="V414.1"
)
Mod('compressor',
'frappy_psi.logo.DigitalActuator',
'',
io = 'logo',
output_addr ="V1064.4",
target_addr ="V404.2",
'frappy_psi.logo.FluidMachines',
'Compressor',
io = 'io',
vm_address_output ="V400.1"
)
Mod('p2',
'frappy_psi.logo.Pressure',
'pressure after compressor',
io = 'logo',
addr ="VW0",
pollinterval=0.5,
)
'Pressure in mBar',
io = 'io',
vm_address ="VW0",
)
Mod('p1',
'frappy_psi.logo.Pressure',
'dump pressure',
io = 'logo',
addr ="VW28",
pollinterval=0.5,
)
'Pressure in mBar',
io = 'io',
vm_address ="VW2",
)
Mod('p5',
'frappy_psi.logo.Pressure',
'pressure after forepump',
io = 'logo',
addr ="VW4",
pollinterval = 0.5,
)
'Pressure in mBar',
io = 'io',
vm_address ="VW4",
)
Mod('airpressure',
'frappy_psi.logo.Comparator',
Mod('Druckluft',
'frappy_psi.logo.Airpressure',
'Airpressure state',
io = 'logo',
addr ="V1024.7",
threshold = 500,
pollinterval = 0.5,
io = 'io',
vm_address ="VW6",
)
Mod('SF1',
'frappy_psi.logo.safetyfeatureState',
'Safety Feature',
io = 'io',
vm_address ="V410.1",
)
Mod('SF2',
'frappy_psi.logo.safetyfeatureState',
'Safety Feature',
io = 'io',
vm_address ="V406.1",
)
Mod('SF3',
'frappy_psi.logo.safetyfeatureState',
'Safety Feature',
io = 'io',
vm_address ="V408.1",
)
Mod('SF4',
'frappy_psi.logo.safetyfeatureState',
'Safety Feature',
io = 'io',
vm_address ="V412.1",
)
Mod('p2max',
'frappy_psi.logo.safetyfeatureParam',
'Safety Feature Param',
io = 'io',
target = 2000,
vm_address ="VW8",
)
Mod('pcond',
'frappy_psi.logo.safetyfeatureParam',
'Safety Feature Param',
io = 'io',
target = 1800,
vm_address ="VW10",
)
Mod('p5min',
'frappy_psi.logo.safetyfeatureParam',
'Safety Feature Param',
io = 'io',
target = 0,
vm_address ="VW12",
)
Mod('p5max',
'frappy_psi.logo.safetyfeatureParam',
'Safety Feature Param',
io = 'io',
target = 900,
vm_address ="VW14",
)
Mod('io_ls273',
'frappy_psi.ls372.StringIO',
'io for Ls372',
uri=lsc_uri,
)
Mod('sw',
'frappy_psi.ls372.Switcher',
'channel switcher',
io = 'io_ls273',
)
Mod('T_mix',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 5',
channel = 5,
switcher = 'sw',
)
Mod('T_ivc',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 2',
channel = 2,
switcher = 'sw',
)
Mod('T_still',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 3',
channel = 3,
switcher = 'sw',
)
Mod('T_sorb',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 1',
channel = 1,
switcher = 'sw',
)
Mod('T_cp',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 4',
channel = 4,
switcher = 'sw',
)
Mod('io_pfeiffer',
'frappy_psi.pfeiffer_new.PfeifferProtocol',
@@ -261,80 +361,41 @@ Mod('T_cond',
io='itc',
)
Mod('safety',
'frappy_psi.dilution.Interlock',
'interlock mechanism',
io='logo',
dil='dil',
)
Mod('dil',
'frappy_psi.dilution.DIL5',
'dilution state machine and parameters',
Mod('stateMachine',
'frappy_psi.dilution_statemachine.DIL5',
'Statemachine',
condenseline_pressure = "p2",
condense_valve = "V9",
dump_valve = "V4",
forepump = "forepump",
circulate_pump = "pump",
compressor = "compressor",
turbopump = "turbopump",
condenseline_valve = "V1",
circuitshort_valve = "V2",
still_pressure = "p4",
still_pressure_turbo = "p3",
still_pressure = "p3",
#ls372 = "res1",
dump_pressure = "p1",
condensing_p_low = 1200,
condensing_p_high = 1500,
V5 = "V5",
p1 = "p1",
MV10 = 'MV10',
MV13 ='MV13',
MV8 = 'MV8',
MVB = 'MVB',
MV2 = 'MV2',
MV1 = 'MV1',
MV3a = 'MV3a',
MV3b = 'MV3b',
GV1 = 'GV1',
MV14 = 'MV14',
MV12 = 'MV12',
MV11 = 'MV11',
MV9 = 'MV9',
GV2 = 'GV2',
condensing_p_low = 150,
condensing_p_high = 250
)
## Dilution lakeshore Temperature controller
Mod('io_ls273',
'frappy_psi.ls372.StringIO',
'io for Ls372',
uri=lsc_uri,
)
Mod('sw',
'frappy_psi.ls372.Switcher',
'channel switcher',
io = 'io_ls273',
)
Mod('T_ivc',
'frappy_psi.ls372.TemperatureChannel',
'mix temperature chan 2',
channel = 2,
switcher = 'sw',
)
Mod('T_still',
'frappy_psi.ls372.TemperatureChannel',
'mix temperature chan 3',
channel = 3,
switcher = 'sw',
)
Mod('T_sorb',
'frappy_psi.ls372.TemperatureChannel',
'mix temperature chan 1',
channel = 1,
switcher = 'sw',
)
Mod('T_cp',
'frappy_psi.ls372.TemperatureChannel',
'mix temperature chan 4',
channel = 4,
switcher = 'sw',
)
Mod('T_mix',
'frappy_psi.ls372.TemperatureLoop',
'mix temperature chan 5',
channel = 5,
htrrng = '1mA',
switcher = 'sw',
)

View File

@@ -1,16 +0,0 @@
Node('dilhtrtest.psi.ch',
'dilhtr test',
'tcp://5000',
)
Mod('io',
'frappy_psi.dilhtr.IO',
'dilhtr communication',
uri='serial:///dev/tty.usbserial-21440?baudrate=9600',
)
Mod('heater',
'frappy_psi.dilhtr.Heater',
'dilhtr box',
io='io',
)

View File

@@ -45,16 +45,17 @@ Mod('T4',
)
Mod('T',
'frappy_psi.furnace.PIctrl',
'controlled temperature ',
'frappy_psi.picontrol.PI',
'controlled Temperature',
input_module='T_main',
output_module='htr',
value = Param(unit='degC'),
input_module = 'T_htr',
output_module = 't_out',
output_min = 0,
output_max = 100,
p = 1,
i = 0.01,
)
# relais='relais',
p=0.1,
i=0.01,
)
Mod('htr_io',
'frappy_psi.tdkpower.IO',
@@ -83,7 +84,7 @@ Mod('flowswitch',
true_level='low',
)
Mod('interlock',
Mod('interlocks',
'frappy_psi.furnace.Interlocks',
'interlock parameters',
main_T='T_main',
@@ -96,7 +97,7 @@ Mod('interlock',
wall_limit=50,
main_T_limit = 1400,
extra_T_limit = 1400,
vacuum_limit=0.001,
vacuum_limit=0.01,
)
Mod('p',

View File

@@ -4,63 +4,64 @@ Node('fs.psi.ch',
)
Mod('T',
'frappy_psi.furnace.PI2',
'frappy_psi.picontrol.PI2',
'controlled Temperature on sample (2nd loop)',
value = Param(unit='degC'),
meaning = ['temperature', 30],
input_module = 'T_sam',
output_module = 'T_reg',
input = 'T_sample',
output = 'T_reg',
relais = 'relais',
p = 1.2,
i = 0.005,
)
Mod('T_reg',
'frappy_psi.furnace.PIctrl',
'frappy_psi.picontrol.PI',
'controlled Temperature on heater',
value = Param(unit='degC'),
input_module = 'T_htr',
output_module = 't_out',
output_min = 0,
output_max = 100,
input = 'T_htr',
output = 't_out',
relais = 'relais',
p = 1,
i = 0.003,
)
#Mod('p_reg',
# 'frappy_psi.furnace.PI',
# 'controlled pressure',
# input_module = 'p',
# output_module = 't_out',
# p = 1,
# i = 0.005,
# )
Mod('p_reg',
'frappy_psi.picontrol.PI',
'controlled pressure',
input = 'p',
output = 'p_out',
relais = 'relais',
p = 1,
i = 0.005,
)
Mod('T_htr',
'frappy_psi.furnace.PRtransmitter',
'frappy_psi.ionopimax.CurrentInput',
'heater temperature',
addr = 'ai4',
valuerange = (0, 1372),
value = Param(unit='degC'),
)
Mod('T_sam',
'frappy_psi.furnace.PRtransmitter',
Mod('T_sample',
'frappy_psi.ionopimax.CurrentInput',
'sample temperature',
addr = 'ai2',
addr = 'ai3',
valuerange = (0, 1372),
value = Param(unit='degC'),
)
Mod('T_extra',
'frappy_psi.furnace.PRtransmitter',
'frappy_psi.ionopimax.CurrentInput',
'extra temperature',
addr = 'ai3',
addr = 'ai2',
valuerange = (0, 1372),
value = Param(unit='degC'),
)
Mod('T_wall',
'frappy_psi.ionopimax.VoltageInput',
'furnace wall temperature',
@@ -85,38 +86,35 @@ Mod('htr',
Mod('t_out',
'frappy_psi.bkpower.Output',
'heater output',
# p_value = 'p_out',
p_value = 'p_out',
io = 'htr_io',
maxvolt = 50,
maxcurrent = 2,
)
Mod('relay',
Mod('relais',
'frappy_psi.ionopimax.DigitalOutput',
'relais for power output',
addr = 'o2',
)
Mod('interlock',
Mod('interlocks',
'frappy_psi.furnace.Interlocks',
'interlock parameters',
input = 'T_htr',
wall_T = 'T_wall',
htr_T = 'T_htr',
main_T = 'T_sam',
reg_T = 'T_reg',
main_T = 'T_sample',
extra_T = 'T_extra',
htr = 't_out',
vacuum = 'p',
relay = 'relay',
relais = 'relais',
control = 'T',
wall_limit = 60,
vacuum_limit = 0.001,
disabled_checks = 'T_extra',
wall_limit = 100,
vacuum_limit = 0.1,
)
Mod('p',
'frappy_psi.furnace.PKRgauge',
'frappy_psi.ionopimax.LogVoltageInput',
'pressure reading',
addr = 'av1',
rawrange = (1.82, 8.6),

View File

@@ -1,94 +0,0 @@
Node('gas10ka.psi.ch',
'10kBar Gas pressure stick',
interface='tcp://5010',
)
Mod('io',
'frappy_psi.logo.IO',
'',
ip_address = "192.168.1.1",
tcap_client = 0x3000,
tsap_server = 0x2000
)
Mod('R_pt10k',
'frappy_psi.logo.Resistor',
'raw sensor value of T_p10k',
io = 'io',
addr = "VW0",
)
Mod('T_pt10k',
'frappy_psi.softcal.Sensor',
'temperature close to sample',
value=Param(unit='K'),
rawsensor='R_pt10k',
calcurve='pt10000e',
)
Mod('R_top',
'frappy_psi.logo.Resistor',
'raw sensor value of T_top',
io = 'io',
addr = "VW2",
)
Mod('T_top',
'frappy_psi.softcal.Sensor',
'capillary temperature at highest position',
value=Param(unit='K'),
rawsensor='R_top',
calcurve='pt1000e',
)
Mod('R_mid',
'frappy_psi.logo.Resistor',
'raw sensor value of T_mid',
io = 'io',
addr = "VW6",
)
Mod('T_mid',
'frappy_psi.softcal.Sensor',
'capillary temperature at mid position',
value=Param(unit='K'),
rawsensor='R_mid',
calcurve='pt1000e',
)
Mod('R_bot',
'frappy_psi.logo.Resistor',
'raw sensor value of T_bot',
io = 'io',
addr = "VW4",
)
Mod('T_bot',
'frappy_psi.softcal.Sensor',
'capillary temperature at lower position',
value=Param(unit='K'),
rawsensor='R_bot',
calcurve='pt1000e',
)
Mod('R_sam_cx',
'frappy_psi.logo.Resistor',
'sensor',
io = 'io',
addr = "VW16",
)
Mod('T_sam_cx',
'frappy_psi.softcal.Sensor',
'?',
value=Param(unit='K'),
rawsensor='R_sam_cx',
calcurve='X174785',
)
Mod('heater',
'frappy_psi.capillary_heater.Heater',
'the capillary heater',
io = 'io',
)

View File

@@ -10,6 +10,7 @@ Mod('sea_main',
Mod('te',
'frappy_psi.sea.SeaDrivable', '',
io = 'sea_main',
meaning=['temperature', 20],
sea_object = 'te',
meaning=('temperature', 11),
)

View File

@@ -13,11 +13,9 @@ Mod('th',
io = 'sea_main',
sea_object = 'th',
extra_modules=['t2'],
value=Param(unit='degC'),
)
Mod('ts',
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
single_module='th.t2',
value=Param(unit='degC'),
)

View File

@@ -1,15 +1,17 @@
Node('haake.frappy.psi.ch',
'additional haake waterbath',
Node('haake.config.sea.psi.ch',
'Haake thermostat',
)
Mod('haake_io',
'frappy_psi.haake.HaakeIO',
'',
uri='sans-sample-ts:3006',
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for haakeuro.config',
config = 'haake.config',
service = 'main',
)
Mod('T2',
'frappy_psi.haake.TemperatureLoop',
'second haake',
io = 'haake_io',
Mod('th',
'frappy_psi.sea.SeaDrivable', '',
meaning = ('temperature', 10),
io = 'sea_main',
sea_object = 'th',
extra_modules=['t2'],
value=Param(unit='degC'),
)

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('pauto',

View File

@@ -14,7 +14,7 @@ Mod('tt',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
rel_paths=['main', '.', 'set'],
rel_paths=['tt', 'set'],
value=Param(unit='K'),
)

View File

@@ -1,224 +0,0 @@
Node('leiden.psi.ch',
'''Leiden Dilution''',
)
ah2700_uri = 'linse-leiden-ts:3002' # used in cfg/addons/ahtwo_cfg.pt
ls370_uri = 'linse-leiden-ts:3004' # used in ~/sea/tcl/leiden.config
tcs_uri = 'linse-leiden-ts:3005'
#nanov_uri = 'linse-leiden-ts:3006' # used in ~/sea/tcl/leiden.config
k2601b_uri = 'linse-leiden-ts:3006' # used for HC experiment as heater
dilhtr_uri = 'linse-leiden-ts:3007'
srbridge_uri = 'linse-leiden-ts:3008'
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for leiden.config',
config = 'leiden.config',
service = 'main',
)
for name in ['T3K', 'Tstill', 'T50mK', 'Tmxlow', 'Tmxhigh', 'Tmxcx', 'Tblueo',
'Tpt50', 'Tpt3high', 'Tpt3low', 'Twhite', 'Tgreen']:
mname = name.replace('T','T_')
Mod(mname,
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
sea_object='tt',
rel_paths=[name],
value=Param(unit='K'),
extra_modules = ['raw'],
)
Mod(name.replace('T', 'R_'),
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='Ohm'),
single_module=f'{mname}.raw'
)
#Mod('cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io = 'sea_main',
# sea_object = 'cmn',
# extra_modules = ['u1', 'u2', 'temp'],
#)
#Mod('T_cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='K'),
# single_module='cmn.temp',
#)
#Mod('V_fixp',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='V'),
# single_module='cmn.u2',
#)
#Mod('V_cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='V'),
# single_module='cmn.u1',
#)
Mod('tcs_io',
'frappy_psi.tcs.IO',
'tcs communication',
uri=tcs_uri,
)
Mod('still_htr',
'frappy_psi.tcs.Heater',
'still heater',
io='tcs_io',
channel=2,
)
Mod('mix_io',
'frappy_psi.dilhtr.IO',
'dilhtr communication',
uri=dilhtr_uri,
)
Mod('mix_htr',
'frappy_psi.dilhtr.WrappedHeater',
'mixing chamber heater',
io='mix_io',
)
Mod('drive_mix',
'frappy_psi.picontrol.PIctrl',
'controlled mix ch. temperature',
input_module = 'T_mxlow',
output_module = 'mix_htr',
output_min = 0,
output_max = 0.02,
p = 5,
itime = 60,
)
#Mod('drive_cmn',
# 'frappy_psi.picontrol.PIctrl',
# 'controlled cmn temperature',
# input_module = 'T_cmn',
# output_module = 'mix_htr',
# output_min = 0,
# output_max = 3e-2,
# p = 2,
# itime = 120,
# )
#Mod('drive_fixp',
# 'frappy_psi.picontrol.PI',
# 'controlled fixpoint voltage',
# value=Param(unit='V'),
# input_module = 'V_fixp',
# output_module = 'drive_mix',
# output_min = 0.0,
# output_max = 0.01,
# p = 1,
# itime = 120,
# )
Mod('simio',
'frappy_psi.bridge.BridgeIO',
'communication to sim900',
uri=srbridge_uri,
)
Mod('res1',
'frappy_psi.bridge.Resistance',
'please add description',
io='simio',
port=1,
)
Mod('res2',
'frappy_psi.bridge.Resistance',
'please add description',
io='simio',
port=3,
)
Mod('phase1',
'frappy_psi.bridge.Phase',
'please add description',
resistance='res1',
)
Mod('phase2',
'frappy_psi.bridge.Phase',
'please add description',
resistance='res2',
)
Mod('dev1',
'frappy_psi.bridge.Deviation',
'please add description',
resistance='res1',
)
Mod('dev2',
'frappy_psi.bridge.Deviation',
'please add description',
resistance='res2',
)
Mod('vsource_io',
'frappy_psi.k2601b.K2601bIO',
'source meter',
# uri = '129.129.156.90:5025',
uri = k2601b_uri,
)
Mod('source',
'frappy_psi.k2601b.SourceMeter'
'',
description = "keithley sourcemeter",
mode = 2,
vlimit = 0.5,
ilimit = .0005,
io = 'vsource_io',
)
Mod('hvolt',
'frappy_psi.k2601b.Voltage'
'',
description = "Heater Voltage",
active = False,
limit = 1.0,
target = 0.0,
sourcemeter = 'source',
io = 'vsource_io',
)
Mod('hcur',
'frappy_psi.k2601b.Current'
'',
description = "Heater Current Source",
active = True,
limit = 0.0001,
target = 0.0,
sourcemeter = 'source',
io = 'vsource_io',
)
Mod('hres',
'frappy_psi.k2601b.Resistivity'
'',
description = "Heater Resistance",
io = 'vsource_io',
)
Mod('hpow',
'frappy_psi.k2601b.Power'
'',
description = "Heater Power",
io = 'vsource_io',
)

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -8,12 +8,11 @@ Mod('sea_main',
service = 'main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
io='sea_main',
'frappy_psi.sea.SeaDrivable', '',
io = 'sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
sea_object = 'tt',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',
'frappy_psi.sea.SeaReadable', '',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('th',

View File

@@ -10,13 +10,12 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
)
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('th',
'frappy_psi.sea.SeaReadable',

View File

@@ -15,12 +15,11 @@ Mod('sea_main',
#)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
io='sea_main',
'frappy_psi.sea.SeaDrivable', '',
meaning=['temperature_regulation', 27],
io='sea_main',
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('th',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -2,6 +2,8 @@ Node('mb11.psi.ch',
'MB11 11 Tesla - 100 mm cryomagnet',
)
sea_cfg = 'mb11.config'
Mod('itc1',
'frappy_psi.mercury.IO',
'ITC for heat exchanger and pressures',

228
cfg/main/mb11std_cfg.py Normal file
View File

@@ -0,0 +1,228 @@
Node('mb11.psi.ch',
'MB11 11 Tesla - 100 mm cryomagnet',
)
Mod('itc1',
'frappy_psi.mercury.IO',
'ITC for heat exchanger and pressures',
uri='mb11-ts:3001',
)
Mod('itc2',
'frappy_psi.mercury.IO',
'ITC for neck and nv heaters',
uri='mb11-ts:3002',
)
Mod('ips',
'frappy_psi.mercury.IO',
'IPS for magnet and levels',
uri='mb11-ts:3003',
)
Mod('T_stat',
'frappy_psi.mercury.TemperatureAutoFlow',
'static heat exchanger temperature',
meaning=['temperature_regulation', 27],
output_module='htr_stat',
needle_valve='p_stat',
slot='DB6.T1',
io='itc1',
tolerance=0.1,
flowpars=((1,5), (2, 20)),
)
Mod('htr_stat',
'frappy_psi.mercury.HeaterOutput',
'static heat exchanger heater',
slot='DB1.H1',
io='itc1',
)
Mod('ts',
'frappy_psi.mercury.TemperatureLoop',
'sample temperature',
output_module='htr_sample',
slot='MB1.T1',
io='itc1',
tolerance=1.0,
visibility='expert',
)
Mod('htr_sample',
'frappy_psi.mercury.HeaterOutput',
'sample stick heater power',
slot='MB0.H1',
io='itc1',
)
Mod('p_stat',
'frappy_psi.mercury.PressureLoop',
'static needle valve pressure',
output_module='pos_stat',
settling_time=60.0,
slot='DB5.P1',
io='itc1',
tolerance=1.0,
value=Param(
unit='mbar_flow',
),
)
Mod('pos_stat',
'frappy_psi.mercury.ValvePos',
'static needle valve position',
slot='DB5.P1,DB3.G1',
io='itc1',
)
Mod('T_dyn',
'frappy_psi.mercury.TemperatureAutoFlow',
'dynamic heat exchanger temperature',
output_module='htr_dyn',
needle_valve='p_dyn',
slot='DB7.T1',
io='itc1',
tolerance=0.1,
)
Mod('htr_dyn',
'frappy_psi.mercury.HeaterOutput',
'dynamic heat exchanger heater',
slot='DB2.H1',
io='itc1',
)
Mod('p_dyn',
'frappy_psi.mercury.PressureLoop',
'dynamic needle valve pressure',
output_module='pos_dyn',
settling_time=60.0,
slot='DB8.P1',
io='itc1',
tolerance=1.0,
value=Param(
unit='mbar_flow',
),
)
Mod('pos_dyn',
'frappy_psi.mercury.ValvePos',
'dynamic needle valve position',
slot='DB8.P1,DB4.G1',
io='itc1',
)
Mod('mf',
'frappy_psi.ips_mercury.Field',
'magnetic field',
slot='GRPZ',
io='ips',
tolerance=0.001,
wait_stable_field=60.0,
target=Param(
max=11.0,
),
persistent_limit=11.1,
)
Mod('lev',
'frappy_psi.mercury.HeLevel',
'LHe level',
slot='DB1.L1',
io='ips',
)
Mod('n2lev',
'frappy_psi.mercury.N2Level',
'LN2 level',
slot='DB1.L1',
io='ips',
)
Mod('T_neck1',
'frappy_psi.mercury.TemperatureLoop',
'neck heater 1 temperature',
output_module='htr_neck1',
slot='MB1.T1',
io='itc2',
tolerance=1.0,
)
Mod('htr_neck1',
'frappy_psi.mercury.HeaterOutput',
'neck heater 1 power',
slot='MB0.H1',
io='itc2',
)
Mod('T_neck2',
'frappy_psi.mercury.TemperatureLoop',
'neck heater 2 temperature',
output_module='htr_neck2',
slot='DB6.T1',
io='itc2',
tolerance=1.0,
)
Mod('htr_neck2',
'frappy_psi.mercury.HeaterOutput',
'neck heater 2 power',
slot='DB1.H1',
io='itc2',
)
Mod('T_nvs',
'frappy_psi.mercury.TemperatureLoop',
'static needle valve temperature',
output_module='htr_nvs',
slot='DB7.T1',
io='itc2',
tolerance=0.1,
)
Mod('htr_nvs',
'frappy_psi.mercury.HeaterOutput',
'static needle valve heater power',
slot='DB2.H1',
io='itc2',
)
Mod('T_nvd',
'frappy_psi.mercury.TemperatureLoop',
'dynamic needle valve heater temperature',
output_module='htr_nvd',
slot='DB8.T1',
io='itc2',
tolerance=0.1,
)
Mod('htr_nvd',
'frappy_psi.mercury.HeaterOutput',
'dynamic needle valve heater power',
slot='DB3.H1',
io='itc2',
)
Mod('T_coil',
'frappy_psi.mercury.TemperatureSensor',
'coil temperature',
slot='MB1.T1',
io='ips',
)
Mod('om_io',
'frappy_psi.phytron.PhytronIO',
'dom motor IO',
uri='mb11-ts.psi.ch:3004',
)
Mod('om',
'frappy_psi.phytron.Motor',
'stick rotation, typically used for omega',
io='om_io',
target_min=-360,
target_max=360,
encoder_mode='NO',
target=Param(min=-360, max=360),
)

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['.', 'tm', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
io='sea_main',
'frappy_psi.sea.SeaDrivable', '',
io = 'sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
sea_object = 'tt',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

17
cfg/main/ori7test_cfg.py Normal file
View File

@@ -0,0 +1,17 @@
from frappy_psi.ccracks import Rack
Node('ori7test.psi.ch',
'ORI7 test',
'tcp://5000'
)
rack = Rack(Mod)
rack.lakeshore()
rack.sensor('Ts', channel='C', calcurve='x186350')
rack.loop('T', channel='B', calcurve='x174786', output_module='htr', target=10)
rack.heater('htr', output_no=1, max_heater='100W', resistance=25)
rack.he()
rack.n2()
rack.flow(min_open_pulse=0.03)

View File

@@ -1,20 +0,0 @@
Node('TFA10.psi.ch',
'thermofisher water bath',
'tcp://5000',
)
Mod('io',
'frappy_psi.thermofisher.ThermFishIO',
'connection for ThermoFisher A10',
uri='tcp://ldm-fi-ts:3002',
)
Mod('T',
'frappy_psi.thermofisher.TemperatureLoopA10',
'holder temperature',
io='io',
meaning=['temperature', 20],
target=Param(max=100),
tolerance=0.5,
settling_time=20,
)

View File

@@ -10,12 +10,11 @@ Mod('sea_main',
)
Mod('tt',
'frappy_psi.sea.LscDrivable', '',
'frappy_psi.sea.SeaDrivable', '',
io='sea_main',
meaning=['temperature_regulation', 27],
sea_object='tt',
sensor_path='tm',
set_path='set',
rel_paths=['tm', '.', 'set', 'dblctrl'],
)
Mod('cc',

View File

@@ -1,100 +0,0 @@
Node('muwaba.psi.ch',
'multi waterbath',
'tcp://5000',
)
Mod('wio_1',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB0?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('wio_2',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB1?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('wio_3',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB2?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('Tbath_1',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_1',
io='wio_1',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('Tbath_2',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_2',
io='wio_2',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('Tbath_3',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_3',
io='wio_3',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('valve_1',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o1',
target = 0,
)
Mod('valve_2',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o2',
target = 0,
)
Mod('valve_3',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o3',
target = 0,
)
Mod('temp_sensor_tc',
'frappy_psi.ionopimax.SimpleVoltageInput',
'temperatur_sensor_sample',
rawrange = (0.0, 10.0),
valuerange = (5.0, 90.0),
addr = 'ai1_mv',
meaning = ['temperature', 20],
value = Param(unit='degC'),
)
Mod('temp_sensor_pt1000',
'frappy_psi.ionopimax.SimpleVoltageInput',
'temperatur_sensor_sample',
rawrange = (0.0, 10.0),
valuerange = (5.0, 90.0),
value = Param(unit='degC'),
addr = 'ai2_mv',
)
Mod('switcher',
'frappy_psi.muwaba.Switcher',
'waterbath switcher',
valve1 = 'valve_1',
valve2 = 'valve_2',
valve3 = 'valve_3',
)

View File

@@ -1,17 +0,0 @@
Node('haake.config.sea.psi.ch',
'Haake thermostat',
)
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for haakeuro.config',
config = 'haake.config',
service = 'main',
)
Mod('th',
'frappy_psi.sea.SeaDrivable', '',
meaning = ('temperature', 10),
io = 'sea_main',
sea_object = 'th',
extra_modules=['t2'],
value=Param(unit='degC'),
)

View File

@@ -1,21 +1,14 @@
{"capff": {"base": "/capff", "params": [
{"path": "", "type": "none", "kids": 7},
{"path": "send", "type": "text", "readonly": false, "cmd": "capff send", "visibility": 3},
{"cap": {"base": "/cap", "params": [{"path": "", "type": "none", "kids": 8},
{"path": "send", "type": "text", "readonly": false, "cmd": "cap send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "cap", "type": "float"},
{"path": "loss", "type": "float"},
{"path": "period", "type": "float", "readonly": false, "cmd": "capff period"},
{"path": "V", "type": "float", "readonly": false, "cmd": "capff V"},
{"path": "average", "type": "int", "readonly": false, "cmd": "capff average"}]},
"capslopeff": {"base": "/capslopeff", "params": [
{"path": "", "type": "float", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "capslopeff send", "visibility": 3},
{"path": "period", "type": "float", "readonly": false, "cmd": "cap period"},
{"path": "V", "type": "float", "readonly": false, "cmd": "cap V"},
{"path": "average", "type": "int", "readonly": false, "cmd": "cap average"}]}, "capslope": {"base": "/capslope", "params": [{"path": "", "type": "float", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "capslope send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "node", "type": "text", "readonly": false, "cmd": "capslopeff node"},
{"path": "unit", "type": "float", "readonly": false, "cmd": "capslopeff unit", "description": "unit=60: mainunits/minutes, unit=1: mainunits/sec"},
{"path": "ref", "type": "float", "readonly": false, "cmd": "capslopeff ref"},
{"path": "bufperiod", "type": "float", "readonly": false, "cmd": "capslopeff bufperiod"}]},
"addonlock_ah2550": {"base": "/addonlock_ah2550", "params": [
{"path": "", "type": "text", "readonly": false, "cmd": "addonlock_ah2550 = "}]}}
{"path": "node", "type": "text", "readonly": false, "cmd": "capslope node"},
{"path": "unit", "type": "float", "readonly": false, "cmd": "capslope unit", "description": "unit=60: mainunits/minutes, unit=1: mainunits/sec"},
{"path": "ref", "type": "float", "readonly": false, "cmd": "capslope ref"},
{"path": "buffersize", "type": "float", "readonly": false, "cmd": "capslope buffersize"}]}}

View File

@@ -1,5 +1,4 @@
{"cap": {"base": "/cap", "params": [
{"path": "", "type": "none", "kids": 8},
{"cap": {"base": "/cap", "params": [{"path": "", "type": "none", "kids": 8},
{"path": "send", "type": "text", "readonly": false, "cmd": "cap send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "cap", "type": "float"},
@@ -7,16 +6,10 @@
{"path": "period", "type": "float", "readonly": false, "cmd": "cap period"},
{"path": "freq", "type": "float", "readonly": false, "cmd": "cap freq"},
{"path": "V", "type": "float", "readonly": false, "cmd": "cap V"},
{"path": "average", "type": "int", "readonly": false, "cmd": "cap average"}]},
"capslope": {"base": "/capslope", "params": [
{"path": "", "type": "float", "kids": 6},
{"path": "average", "type": "int", "readonly": false, "cmd": "cap average"}]}, "capslope": {"base": "/capslope", "params": [{"path": "", "type": "float", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "capslope send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "node", "type": "text", "readonly": false, "cmd": "capslope node"},
{"path": "unit", "type": "float", "readonly": false, "cmd": "capslope unit", "description": "unit=60: mainunits/minutes, unit=1: mainunits/sec"},
{"path": "ref", "type": "float", "readonly": false, "cmd": "capslope ref"},
{"path": "bufperiod", "type": "float", "readonly": false, "cmd": "capslope bufperiod"}]},
"addonlock_ah2700": {"base": "/addonlock_ah2700", "params": [
{"path": "", "type": "text", "readonly": false, "cmd": "addonlock_ah2700 = "}]}}
{"path": "buffersize", "type": "float", "readonly": false, "cmd": "capslope buffersize"}]}}

View File

@@ -1,29 +0,0 @@
{"cp2800": {"base": "/cp2800", "params": [
{"path": "", "type": "bool", "readonly": false, "cmd": "cp2800", "kids": 27},
{"path": "send", "type": "text", "readonly": false, "cmd": "cp2800 send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "comp_running_hrs", "type": "float"},
{"path": "cpu_t", "type": "float"},
{"path": "motor_current_a", "type": "float"},
{"path": "inp_water_t", "type": "float"},
{"path": "inp_water_t_min", "type": "float"},
{"path": "inp_water_t_max", "type": "float"},
{"path": "out_water_t", "type": "float"},
{"path": "out_water_t_min", "type": "float"},
{"path": "out_water_t_max", "type": "float"},
{"path": "helium_t", "type": "float"},
{"path": "helium_t_min", "type": "float"},
{"path": "helium_t_max", "type": "float"},
{"path": "oil_t", "type": "float"},
{"path": "oil_t_min", "type": "float"},
{"path": "oil_t_max", "type": "float"},
{"path": "high_side_p", "type": "float"},
{"path": "high_side_p_min", "type": "float"},
{"path": "high_side_p_max", "type": "float"},
{"path": "high_side_p_avg", "type": "float"},
{"path": "low_side_p", "type": "float"},
{"path": "low_side_p_min", "type": "float"},
{"path": "low_side_p_max", "type": "float"},
{"path": "low_side_p_avg", "type": "float"},
{"path": "high_side_delta_p_avg", "type": "float"},
{"path": "high_side_bounce", "type": "float"}]}}

View File

@@ -1,14 +0,0 @@
Node('cp1000.addon.sea.psi.ch',
'''dry system''',
)
Mod('sea_addons',
'frappy_psi.sea.SeaClient',
'addons sea connection for cp1000.addon',
config = 'cp1000.addon',
service = 'addons',
)
Mod('cp2800',
'frappy_psi.sea.SeaWritable', '',
io = 'sea_addons',
sea_object = 'cp2800',
)

View File

@@ -18,7 +18,7 @@
{"path": "heaterselect", "type": "enum", "enum": {"sample": 0, "mix": 1, "mix(temporarely)": 2}, "readonly": false, "cmd": "ts heaterselect"},
{"path": "control", "type": "enum", "enum": {"off": 0, "sample": 6, "mix": 5, "samplehtr": 8}, "readonly": false, "cmd": "ts control", "description": "click off to reload list"},
{"path": "heatermode", "type": "enum", "enum": {"disabled": -1, "off": 0, "on": 1}, "readonly": false, "cmd": "ts heatermode"},
{"path": "heaterrange", "type": "enum", "enum": {"off": 0, "2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "heaterrange", "type": "enum", "enum": {"2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "autoheater", "type": "bool", "readonly": false, "cmd": "ts autoheater", "description": "automatic heater range", "kids": 12},
{"path": "autoheater/wlp0", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp0", "description": "weak link base temperature (used for auto heater)"},
{"path": "autoheater/wlp1", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp1", "description": "weak link temperature at 1 uW (used for auto heater)"},

View File

@@ -18,7 +18,7 @@
{"path": "heaterselect", "type": "enum", "enum": {"sample": 0, "mix": 1, "mix(temporarely)": 2}, "readonly": false, "cmd": "ts heaterselect"},
{"path": "control", "type": "enum", "enum": {"off": 0, "sample": 6, "mix": 5, "samplehtr": 8}, "readonly": false, "cmd": "ts control", "description": "click off to reload list"},
{"path": "heatermode", "type": "enum", "enum": {"disabled": -1, "off": 0, "on": 1}, "readonly": false, "cmd": "ts heatermode"},
{"path": "heaterrange", "type": "enum", "enum": {"off": 0, "2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "heaterrange", "type": "enum", "enum": {"2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "autoheater", "type": "bool", "readonly": false, "cmd": "ts autoheater", "description": "automatic heater range", "kids": 12},
{"path": "autoheater/wlp0", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp0", "description": "weak link base temperature (used for auto heater)"},
{"path": "autoheater/wlp1", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp1", "description": "weak link temperature at 1 uW (used for auto heater)"},

View File

@@ -18,7 +18,7 @@
{"path": "heaterselect", "type": "enum", "enum": {"sample": 0, "mix": 1, "mix(temporarely)": 2}, "readonly": false, "cmd": "ts heaterselect"},
{"path": "control", "type": "enum", "enum": {"off": 0, "sample": 6, "mix": 5, "samplehtr": 8}, "readonly": false, "cmd": "ts control", "description": "click off to reload list"},
{"path": "heatermode", "type": "enum", "enum": {"disabled": -1, "off": 0, "on": 1}, "readonly": false, "cmd": "ts heatermode"},
{"path": "heaterrange", "type": "enum", "enum": {"off": 0, "2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "heaterrange", "type": "enum", "enum": {"2uW": 1, "20uW": 2, "200uW": 3, "2mW": 4, "20mW": 5}, "readonly": false, "cmd": "ts heaterrange"},
{"path": "autoheater", "type": "bool", "readonly": false, "cmd": "ts autoheater", "description": "automatic heater range", "kids": 12},
{"path": "autoheater/wlp0", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp0", "description": "weak link base temperature (used for auto heater)"},
{"path": "autoheater/wlp1", "type": "float", "readonly": false, "cmd": "ts autoheater/wlp1", "description": "weak link temperature at 1 uW (used for auto heater)"},

View File

@@ -1,213 +0,0 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "int", "kids": 18},
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "autoscan", "type": "bool", "readonly": false, "cmd": "tt autoscan", "kids": 4},
{"path": "autoscan/synchronized", "type": "bool", "readonly": false, "cmd": "tt autoscan/synchronized"},
{"path": "autoscan/interval", "type": "text", "readonly": false, "cmd": "tt autoscan/interval"},
{"path": "autoscan/pause", "type": "text", "readonly": false, "cmd": "tt autoscan/pause"},
{"path": "autoscan/dwell", "type": "text", "readonly": false, "cmd": "tt autoscan/dwell"},
{"path": "T3K", "type": "float", "kids": 14},
{"path": "T3K/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T3K/active"},
{"path": "T3K/autorange", "type": "bool", "readonly": false, "cmd": "tt T3K/autorange", "description": "autorange (common for all channels)"},
{"path": "T3K/range", "type": "text", "readonly": false, "cmd": "tt T3K/range", "description": "resistance range in Ohm"},
{"path": "T3K/range_num", "type": "int"},
{"path": "T3K/excitation", "type": "text", "readonly": false, "cmd": "tt T3K/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T3K/excitation_num", "type": "int"},
{"path": "T3K/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T3K/pause", "type": "int", "readonly": false, "cmd": "tt T3K/pause", "description": "pause time [sec] after channel change"},
{"path": "T3K/filter", "type": "int", "readonly": false, "cmd": "tt T3K/filter", "description": "filter average time [sec]"},
{"path": "T3K/dwell", "type": "int", "readonly": false, "cmd": "tt T3K/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T3K/status", "type": "text"},
{"path": "T3K/curve", "type": "text", "readonly": false, "cmd": "tt T3K/curve", "kids": 1},
{"path": "T3K/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T3K/curve/points", "visibility": 3},
{"path": "T3K/alarm", "type": "float", "readonly": false, "cmd": "tt T3K/alarm"},
{"path": "T3K/raw", "type": "float"},
{"path": "Tstill", "type": "float", "kids": 14},
{"path": "Tstill/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tstill/active"},
{"path": "Tstill/autorange", "type": "bool", "readonly": false, "cmd": "tt Tstill/autorange", "description": "autorange (common for all channels)"},
{"path": "Tstill/range", "type": "text", "readonly": false, "cmd": "tt Tstill/range", "description": "resistance range in Ohm"},
{"path": "Tstill/range_num", "type": "int"},
{"path": "Tstill/excitation", "type": "text", "readonly": false, "cmd": "tt Tstill/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tstill/excitation_num", "type": "int"},
{"path": "Tstill/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tstill/pause", "type": "int", "readonly": false, "cmd": "tt Tstill/pause", "description": "pause time [sec] after channel change"},
{"path": "Tstill/filter", "type": "int", "readonly": false, "cmd": "tt Tstill/filter", "description": "filter average time [sec]"},
{"path": "Tstill/dwell", "type": "int", "readonly": false, "cmd": "tt Tstill/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tstill/status", "type": "text"},
{"path": "Tstill/curve", "type": "text", "readonly": false, "cmd": "tt Tstill/curve", "kids": 1},
{"path": "Tstill/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tstill/curve/points", "visibility": 3},
{"path": "Tstill/alarm", "type": "float", "readonly": false, "cmd": "tt Tstill/alarm"},
{"path": "Tstill/raw", "type": "float"},
{"path": "T50mK", "type": "float", "kids": 14},
{"path": "T50mK/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T50mK/active"},
{"path": "T50mK/autorange", "type": "bool", "readonly": false, "cmd": "tt T50mK/autorange", "description": "autorange (common for all channels)"},
{"path": "T50mK/range", "type": "text", "readonly": false, "cmd": "tt T50mK/range", "description": "resistance range in Ohm"},
{"path": "T50mK/range_num", "type": "int"},
{"path": "T50mK/excitation", "type": "text", "readonly": false, "cmd": "tt T50mK/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T50mK/excitation_num", "type": "int"},
{"path": "T50mK/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T50mK/pause", "type": "int", "readonly": false, "cmd": "tt T50mK/pause", "description": "pause time [sec] after channel change"},
{"path": "T50mK/filter", "type": "int", "readonly": false, "cmd": "tt T50mK/filter", "description": "filter average time [sec]"},
{"path": "T50mK/dwell", "type": "int", "readonly": false, "cmd": "tt T50mK/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T50mK/status", "type": "text"},
{"path": "T50mK/curve", "type": "text", "readonly": false, "cmd": "tt T50mK/curve", "kids": 1},
{"path": "T50mK/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T50mK/curve/points", "visibility": 3},
{"path": "T50mK/alarm", "type": "float", "readonly": false, "cmd": "tt T50mK/alarm"},
{"path": "T50mK/raw", "type": "float"},
{"path": "Tmxlow", "type": "float", "kids": 14},
{"path": "Tmxlow/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxlow/active"},
{"path": "Tmxlow/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxlow/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxlow/range", "type": "text", "readonly": false, "cmd": "tt Tmxlow/range", "description": "resistance range in Ohm"},
{"path": "Tmxlow/range_num", "type": "int"},
{"path": "Tmxlow/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxlow/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxlow/excitation_num", "type": "int"},
{"path": "Tmxlow/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxlow/pause", "type": "int", "readonly": false, "cmd": "tt Tmxlow/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxlow/filter", "type": "int", "readonly": false, "cmd": "tt Tmxlow/filter", "description": "filter average time [sec]"},
{"path": "Tmxlow/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxlow/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxlow/status", "type": "text"},
{"path": "Tmxlow/curve", "type": "text", "readonly": false, "cmd": "tt Tmxlow/curve", "kids": 1},
{"path": "Tmxlow/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxlow/curve/points", "visibility": 3},
{"path": "Tmxlow/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxlow/alarm"},
{"path": "Tmxlow/raw", "type": "float"},
{"path": "Tmxhigh", "type": "float", "kids": 14},
{"path": "Tmxhigh/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxhigh/active"},
{"path": "Tmxhigh/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxhigh/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxhigh/range", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/range", "description": "resistance range in Ohm"},
{"path": "Tmxhigh/range_num", "type": "int"},
{"path": "Tmxhigh/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxhigh/excitation_num", "type": "int"},
{"path": "Tmxhigh/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxhigh/pause", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxhigh/filter", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/filter", "description": "filter average time [sec]"},
{"path": "Tmxhigh/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxhigh/status", "type": "text"},
{"path": "Tmxhigh/curve", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/curve", "kids": 1},
{"path": "Tmxhigh/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxhigh/curve/points", "visibility": 3},
{"path": "Tmxhigh/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxhigh/alarm"},
{"path": "Tmxhigh/raw", "type": "float"},
{"path": "Tmxcx", "type": "float", "kids": 14},
{"path": "Tmxcx/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxcx/active"},
{"path": "Tmxcx/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxcx/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxcx/range", "type": "text", "readonly": false, "cmd": "tt Tmxcx/range", "description": "resistance range in Ohm"},
{"path": "Tmxcx/range_num", "type": "int"},
{"path": "Tmxcx/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxcx/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxcx/excitation_num", "type": "int"},
{"path": "Tmxcx/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxcx/pause", "type": "int", "readonly": false, "cmd": "tt Tmxcx/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxcx/filter", "type": "int", "readonly": false, "cmd": "tt Tmxcx/filter", "description": "filter average time [sec]"},
{"path": "Tmxcx/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxcx/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxcx/status", "type": "text"},
{"path": "Tmxcx/curve", "type": "text", "readonly": false, "cmd": "tt Tmxcx/curve", "kids": 1},
{"path": "Tmxcx/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxcx/curve/points", "visibility": 3},
{"path": "Tmxcx/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxcx/alarm"},
{"path": "Tmxcx/raw", "type": "float"},
{"path": "Tblueo", "type": "float", "kids": 14},
{"path": "Tblueo/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tblueo/active"},
{"path": "Tblueo/autorange", "type": "bool", "readonly": false, "cmd": "tt Tblueo/autorange", "description": "autorange (common for all channels)"},
{"path": "Tblueo/range", "type": "text", "readonly": false, "cmd": "tt Tblueo/range", "description": "resistance range in Ohm"},
{"path": "Tblueo/range_num", "type": "int"},
{"path": "Tblueo/excitation", "type": "text", "readonly": false, "cmd": "tt Tblueo/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tblueo/excitation_num", "type": "int"},
{"path": "Tblueo/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tblueo/pause", "type": "int", "readonly": false, "cmd": "tt Tblueo/pause", "description": "pause time [sec] after channel change"},
{"path": "Tblueo/filter", "type": "int", "readonly": false, "cmd": "tt Tblueo/filter", "description": "filter average time [sec]"},
{"path": "Tblueo/dwell", "type": "int", "readonly": false, "cmd": "tt Tblueo/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tblueo/status", "type": "text"},
{"path": "Tblueo/curve", "type": "text", "readonly": false, "cmd": "tt Tblueo/curve", "kids": 1},
{"path": "Tblueo/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tblueo/curve/points", "visibility": 3},
{"path": "Tblueo/alarm", "type": "float", "readonly": false, "cmd": "tt Tblueo/alarm"},
{"path": "Tblueo/raw", "type": "float"},
{"path": "Tpt50", "type": "float", "kids": 14},
{"path": "Tpt50/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt50/active"},
{"path": "Tpt50/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt50/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt50/range", "type": "text", "readonly": false, "cmd": "tt Tpt50/range", "description": "resistance range in Ohm"},
{"path": "Tpt50/range_num", "type": "int"},
{"path": "Tpt50/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt50/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt50/excitation_num", "type": "int"},
{"path": "Tpt50/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt50/pause", "type": "int", "readonly": false, "cmd": "tt Tpt50/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt50/filter", "type": "int", "readonly": false, "cmd": "tt Tpt50/filter", "description": "filter average time [sec]"},
{"path": "Tpt50/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt50/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt50/status", "type": "text"},
{"path": "Tpt50/curve", "type": "text", "readonly": false, "cmd": "tt Tpt50/curve", "kids": 1},
{"path": "Tpt50/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt50/curve/points", "visibility": 3},
{"path": "Tpt50/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt50/alarm"},
{"path": "Tpt50/raw", "type": "float"},
{"path": "Tpt3high", "type": "float", "kids": 14},
{"path": "Tpt3high/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3high/active"},
{"path": "Tpt3high/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3high/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3high/range", "type": "text", "readonly": false, "cmd": "tt Tpt3high/range", "description": "resistance range in Ohm"},
{"path": "Tpt3high/range_num", "type": "int"},
{"path": "Tpt3high/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3high/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3high/excitation_num", "type": "int"},
{"path": "Tpt3high/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3high/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3high/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3high/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3high/filter", "description": "filter average time [sec]"},
{"path": "Tpt3high/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3high/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3high/status", "type": "text"},
{"path": "Tpt3high/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3high/curve", "kids": 1},
{"path": "Tpt3high/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3high/curve/points", "visibility": 3},
{"path": "Tpt3high/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3high/alarm"},
{"path": "Tpt3high/raw", "type": "float"},
{"path": "Tpt3low", "type": "float", "kids": 14},
{"path": "Tpt3low/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3low/active"},
{"path": "Tpt3low/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3low/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3low/range", "type": "text", "readonly": false, "cmd": "tt Tpt3low/range", "description": "resistance range in Ohm"},
{"path": "Tpt3low/range_num", "type": "int"},
{"path": "Tpt3low/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3low/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3low/excitation_num", "type": "int"},
{"path": "Tpt3low/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3low/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3low/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3low/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3low/filter", "description": "filter average time [sec]"},
{"path": "Tpt3low/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3low/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3low/status", "type": "text"},
{"path": "Tpt3low/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3low/curve", "kids": 1},
{"path": "Tpt3low/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3low/curve/points", "visibility": 3},
{"path": "Tpt3low/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3low/alarm"},
{"path": "Tpt3low/raw", "type": "float"},
{"path": "Twhite", "type": "float", "kids": 14},
{"path": "Twhite/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Twhite/active"},
{"path": "Twhite/autorange", "type": "bool", "readonly": false, "cmd": "tt Twhite/autorange", "description": "autorange (common for all channels)"},
{"path": "Twhite/range", "type": "text", "readonly": false, "cmd": "tt Twhite/range", "description": "resistance range in Ohm"},
{"path": "Twhite/range_num", "type": "int"},
{"path": "Twhite/excitation", "type": "text", "readonly": false, "cmd": "tt Twhite/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Twhite/excitation_num", "type": "int"},
{"path": "Twhite/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Twhite/pause", "type": "int", "readonly": false, "cmd": "tt Twhite/pause", "description": "pause time [sec] after channel change"},
{"path": "Twhite/filter", "type": "int", "readonly": false, "cmd": "tt Twhite/filter", "description": "filter average time [sec]"},
{"path": "Twhite/dwell", "type": "int", "readonly": false, "cmd": "tt Twhite/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Twhite/status", "type": "text"},
{"path": "Twhite/curve", "type": "text", "readonly": false, "cmd": "tt Twhite/curve", "kids": 1},
{"path": "Twhite/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Twhite/curve/points", "visibility": 3},
{"path": "Twhite/alarm", "type": "float", "readonly": false, "cmd": "tt Twhite/alarm"},
{"path": "Twhite/raw", "type": "float"},
{"path": "Tgreen", "type": "float", "kids": 14},
{"path": "Tgreen/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tgreen/active"},
{"path": "Tgreen/autorange", "type": "bool", "readonly": false, "cmd": "tt Tgreen/autorange", "description": "autorange (common for all channels)"},
{"path": "Tgreen/range", "type": "text", "readonly": false, "cmd": "tt Tgreen/range", "description": "resistance range in Ohm"},
{"path": "Tgreen/range_num", "type": "int"},
{"path": "Tgreen/excitation", "type": "text", "readonly": false, "cmd": "tt Tgreen/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tgreen/excitation_num", "type": "int"},
{"path": "Tgreen/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tgreen/pause", "type": "int", "readonly": false, "cmd": "tt Tgreen/pause", "description": "pause time [sec] after channel change"},
{"path": "Tgreen/filter", "type": "int", "readonly": false, "cmd": "tt Tgreen/filter", "description": "filter average time [sec]"},
{"path": "Tgreen/dwell", "type": "int", "readonly": false, "cmd": "tt Tgreen/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tgreen/status", "type": "text"},
{"path": "Tgreen/curve", "type": "text", "readonly": false, "cmd": "tt Tgreen/curve", "kids": 1},
{"path": "Tgreen/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tgreen/curve/points", "visibility": 3},
{"path": "Tgreen/alarm", "type": "float", "readonly": false, "cmd": "tt Tgreen/alarm"},
{"path": "Tgreen/raw", "type": "float"},
{"path": "analog2", "type": "float", "readonly": false, "cmd": "tt analog2"},
{"path": "remote", "type": "bool"},
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"}]},
"cmn": {"base": "/cmn", "params": [
{"path": "", "type": "none", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "cmn send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "u1", "type": "float"},
{"path": "temp", "type": "float"},
{"path": "u2", "type": "float"},
{"path": "chan", "type": "enum", "enum": {"auto": 0, "chan1": 1, "chan2": 2}, "readonly": false, "cmd": "cmn chan"}]}}

View File

@@ -1,213 +0,0 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "int", "kids": 18},
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "autoscan", "type": "bool", "readonly": false, "cmd": "tt autoscan", "kids": 4},
{"path": "autoscan/synchronized", "type": "bool", "readonly": false, "cmd": "tt autoscan/synchronized"},
{"path": "autoscan/interval", "type": "text", "readonly": false, "cmd": "tt autoscan/interval"},
{"path": "autoscan/pause", "type": "text", "readonly": false, "cmd": "tt autoscan/pause"},
{"path": "autoscan/dwell", "type": "text", "readonly": false, "cmd": "tt autoscan/dwell"},
{"path": "T3K", "type": "float", "kids": 14},
{"path": "T3K/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T3K/active"},
{"path": "T3K/autorange", "type": "bool", "readonly": false, "cmd": "tt T3K/autorange", "description": "autorange (common for all channels)"},
{"path": "T3K/range", "type": "text", "readonly": false, "cmd": "tt T3K/range", "description": "resistance range in Ohm"},
{"path": "T3K/range_num", "type": "int"},
{"path": "T3K/excitation", "type": "text", "readonly": false, "cmd": "tt T3K/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T3K/excitation_num", "type": "int"},
{"path": "T3K/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T3K/pause", "type": "int", "readonly": false, "cmd": "tt T3K/pause", "description": "pause time [sec] after channel change"},
{"path": "T3K/filter", "type": "int", "readonly": false, "cmd": "tt T3K/filter", "description": "filter average time [sec]"},
{"path": "T3K/dwell", "type": "int", "readonly": false, "cmd": "tt T3K/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T3K/status", "type": "text"},
{"path": "T3K/curve", "type": "text", "readonly": false, "cmd": "tt T3K/curve", "kids": 1},
{"path": "T3K/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T3K/curve/points", "visibility": 3},
{"path": "T3K/alarm", "type": "float", "readonly": false, "cmd": "tt T3K/alarm"},
{"path": "T3K/raw", "type": "float"},
{"path": "Tstill", "type": "float", "kids": 14},
{"path": "Tstill/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tstill/active"},
{"path": "Tstill/autorange", "type": "bool", "readonly": false, "cmd": "tt Tstill/autorange", "description": "autorange (common for all channels)"},
{"path": "Tstill/range", "type": "text", "readonly": false, "cmd": "tt Tstill/range", "description": "resistance range in Ohm"},
{"path": "Tstill/range_num", "type": "int"},
{"path": "Tstill/excitation", "type": "text", "readonly": false, "cmd": "tt Tstill/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tstill/excitation_num", "type": "int"},
{"path": "Tstill/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tstill/pause", "type": "int", "readonly": false, "cmd": "tt Tstill/pause", "description": "pause time [sec] after channel change"},
{"path": "Tstill/filter", "type": "int", "readonly": false, "cmd": "tt Tstill/filter", "description": "filter average time [sec]"},
{"path": "Tstill/dwell", "type": "int", "readonly": false, "cmd": "tt Tstill/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tstill/status", "type": "text"},
{"path": "Tstill/curve", "type": "text", "readonly": false, "cmd": "tt Tstill/curve", "kids": 1},
{"path": "Tstill/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tstill/curve/points", "visibility": 3},
{"path": "Tstill/alarm", "type": "float", "readonly": false, "cmd": "tt Tstill/alarm"},
{"path": "Tstill/raw", "type": "float"},
{"path": "T50mK", "type": "float", "kids": 14},
{"path": "T50mK/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T50mK/active"},
{"path": "T50mK/autorange", "type": "bool", "readonly": false, "cmd": "tt T50mK/autorange", "description": "autorange (common for all channels)"},
{"path": "T50mK/range", "type": "text", "readonly": false, "cmd": "tt T50mK/range", "description": "resistance range in Ohm"},
{"path": "T50mK/range_num", "type": "int"},
{"path": "T50mK/excitation", "type": "text", "readonly": false, "cmd": "tt T50mK/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T50mK/excitation_num", "type": "int"},
{"path": "T50mK/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T50mK/pause", "type": "int", "readonly": false, "cmd": "tt T50mK/pause", "description": "pause time [sec] after channel change"},
{"path": "T50mK/filter", "type": "int", "readonly": false, "cmd": "tt T50mK/filter", "description": "filter average time [sec]"},
{"path": "T50mK/dwell", "type": "int", "readonly": false, "cmd": "tt T50mK/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T50mK/status", "type": "text"},
{"path": "T50mK/curve", "type": "text", "readonly": false, "cmd": "tt T50mK/curve", "kids": 1},
{"path": "T50mK/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T50mK/curve/points", "visibility": 3},
{"path": "T50mK/alarm", "type": "float", "readonly": false, "cmd": "tt T50mK/alarm"},
{"path": "T50mK/raw", "type": "float"},
{"path": "Tmxlow", "type": "float", "kids": 14},
{"path": "Tmxlow/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxlow/active"},
{"path": "Tmxlow/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxlow/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxlow/range", "type": "text", "readonly": false, "cmd": "tt Tmxlow/range", "description": "resistance range in Ohm"},
{"path": "Tmxlow/range_num", "type": "int"},
{"path": "Tmxlow/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxlow/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxlow/excitation_num", "type": "int"},
{"path": "Tmxlow/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxlow/pause", "type": "int", "readonly": false, "cmd": "tt Tmxlow/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxlow/filter", "type": "int", "readonly": false, "cmd": "tt Tmxlow/filter", "description": "filter average time [sec]"},
{"path": "Tmxlow/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxlow/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxlow/status", "type": "text"},
{"path": "Tmxlow/curve", "type": "text", "readonly": false, "cmd": "tt Tmxlow/curve", "kids": 1},
{"path": "Tmxlow/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxlow/curve/points", "visibility": 3},
{"path": "Tmxlow/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxlow/alarm"},
{"path": "Tmxlow/raw", "type": "float"},
{"path": "Tmxhigh", "type": "float", "kids": 14},
{"path": "Tmxhigh/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxhigh/active"},
{"path": "Tmxhigh/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxhigh/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxhigh/range", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/range", "description": "resistance range in Ohm"},
{"path": "Tmxhigh/range_num", "type": "int"},
{"path": "Tmxhigh/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxhigh/excitation_num", "type": "int"},
{"path": "Tmxhigh/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxhigh/pause", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxhigh/filter", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/filter", "description": "filter average time [sec]"},
{"path": "Tmxhigh/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxhigh/status", "type": "text"},
{"path": "Tmxhigh/curve", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/curve", "kids": 1},
{"path": "Tmxhigh/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxhigh/curve/points", "visibility": 3},
{"path": "Tmxhigh/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxhigh/alarm"},
{"path": "Tmxhigh/raw", "type": "float"},
{"path": "Tmxcx", "type": "float", "kids": 14},
{"path": "Tmxcx/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxcx/active"},
{"path": "Tmxcx/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxcx/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxcx/range", "type": "text", "readonly": false, "cmd": "tt Tmxcx/range", "description": "resistance range in Ohm"},
{"path": "Tmxcx/range_num", "type": "int"},
{"path": "Tmxcx/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxcx/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxcx/excitation_num", "type": "int"},
{"path": "Tmxcx/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxcx/pause", "type": "int", "readonly": false, "cmd": "tt Tmxcx/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxcx/filter", "type": "int", "readonly": false, "cmd": "tt Tmxcx/filter", "description": "filter average time [sec]"},
{"path": "Tmxcx/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxcx/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxcx/status", "type": "text"},
{"path": "Tmxcx/curve", "type": "text", "readonly": false, "cmd": "tt Tmxcx/curve", "kids": 1},
{"path": "Tmxcx/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxcx/curve/points", "visibility": 3},
{"path": "Tmxcx/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxcx/alarm"},
{"path": "Tmxcx/raw", "type": "float"},
{"path": "Tblueo", "type": "float", "kids": 14},
{"path": "Tblueo/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tblueo/active"},
{"path": "Tblueo/autorange", "type": "bool", "readonly": false, "cmd": "tt Tblueo/autorange", "description": "autorange (common for all channels)"},
{"path": "Tblueo/range", "type": "text", "readonly": false, "cmd": "tt Tblueo/range", "description": "resistance range in Ohm"},
{"path": "Tblueo/range_num", "type": "int"},
{"path": "Tblueo/excitation", "type": "text", "readonly": false, "cmd": "tt Tblueo/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tblueo/excitation_num", "type": "int"},
{"path": "Tblueo/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tblueo/pause", "type": "int", "readonly": false, "cmd": "tt Tblueo/pause", "description": "pause time [sec] after channel change"},
{"path": "Tblueo/filter", "type": "int", "readonly": false, "cmd": "tt Tblueo/filter", "description": "filter average time [sec]"},
{"path": "Tblueo/dwell", "type": "int", "readonly": false, "cmd": "tt Tblueo/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tblueo/status", "type": "text"},
{"path": "Tblueo/curve", "type": "text", "readonly": false, "cmd": "tt Tblueo/curve", "kids": 1},
{"path": "Tblueo/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tblueo/curve/points", "visibility": 3},
{"path": "Tblueo/alarm", "type": "float", "readonly": false, "cmd": "tt Tblueo/alarm"},
{"path": "Tblueo/raw", "type": "float"},
{"path": "Tpt50", "type": "float", "kids": 14},
{"path": "Tpt50/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt50/active"},
{"path": "Tpt50/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt50/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt50/range", "type": "text", "readonly": false, "cmd": "tt Tpt50/range", "description": "resistance range in Ohm"},
{"path": "Tpt50/range_num", "type": "int"},
{"path": "Tpt50/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt50/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt50/excitation_num", "type": "int"},
{"path": "Tpt50/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt50/pause", "type": "int", "readonly": false, "cmd": "tt Tpt50/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt50/filter", "type": "int", "readonly": false, "cmd": "tt Tpt50/filter", "description": "filter average time [sec]"},
{"path": "Tpt50/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt50/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt50/status", "type": "text"},
{"path": "Tpt50/curve", "type": "text", "readonly": false, "cmd": "tt Tpt50/curve", "kids": 1},
{"path": "Tpt50/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt50/curve/points", "visibility": 3},
{"path": "Tpt50/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt50/alarm"},
{"path": "Tpt50/raw", "type": "float"},
{"path": "Tpt3high", "type": "float", "kids": 14},
{"path": "Tpt3high/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3high/active"},
{"path": "Tpt3high/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3high/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3high/range", "type": "text", "readonly": false, "cmd": "tt Tpt3high/range", "description": "resistance range in Ohm"},
{"path": "Tpt3high/range_num", "type": "int"},
{"path": "Tpt3high/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3high/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3high/excitation_num", "type": "int"},
{"path": "Tpt3high/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3high/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3high/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3high/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3high/filter", "description": "filter average time [sec]"},
{"path": "Tpt3high/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3high/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3high/status", "type": "text"},
{"path": "Tpt3high/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3high/curve", "kids": 1},
{"path": "Tpt3high/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3high/curve/points", "visibility": 3},
{"path": "Tpt3high/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3high/alarm"},
{"path": "Tpt3high/raw", "type": "float"},
{"path": "Tpt3low", "type": "float", "kids": 14},
{"path": "Tpt3low/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3low/active"},
{"path": "Tpt3low/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3low/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3low/range", "type": "text", "readonly": false, "cmd": "tt Tpt3low/range", "description": "resistance range in Ohm"},
{"path": "Tpt3low/range_num", "type": "int"},
{"path": "Tpt3low/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3low/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3low/excitation_num", "type": "int"},
{"path": "Tpt3low/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3low/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3low/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3low/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3low/filter", "description": "filter average time [sec]"},
{"path": "Tpt3low/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3low/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3low/status", "type": "text"},
{"path": "Tpt3low/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3low/curve", "kids": 1},
{"path": "Tpt3low/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3low/curve/points", "visibility": 3},
{"path": "Tpt3low/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3low/alarm"},
{"path": "Tpt3low/raw", "type": "float"},
{"path": "Twhite", "type": "float", "kids": 14},
{"path": "Twhite/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Twhite/active"},
{"path": "Twhite/autorange", "type": "bool", "readonly": false, "cmd": "tt Twhite/autorange", "description": "autorange (common for all channels)"},
{"path": "Twhite/range", "type": "text", "readonly": false, "cmd": "tt Twhite/range", "description": "resistance range in Ohm"},
{"path": "Twhite/range_num", "type": "int"},
{"path": "Twhite/excitation", "type": "text", "readonly": false, "cmd": "tt Twhite/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Twhite/excitation_num", "type": "int"},
{"path": "Twhite/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Twhite/pause", "type": "int", "readonly": false, "cmd": "tt Twhite/pause", "description": "pause time [sec] after channel change"},
{"path": "Twhite/filter", "type": "int", "readonly": false, "cmd": "tt Twhite/filter", "description": "filter average time [sec]"},
{"path": "Twhite/dwell", "type": "int", "readonly": false, "cmd": "tt Twhite/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Twhite/status", "type": "text"},
{"path": "Twhite/curve", "type": "text", "readonly": false, "cmd": "tt Twhite/curve", "kids": 1},
{"path": "Twhite/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Twhite/curve/points", "visibility": 3},
{"path": "Twhite/alarm", "type": "float", "readonly": false, "cmd": "tt Twhite/alarm"},
{"path": "Twhite/raw", "type": "float"},
{"path": "Tgreen", "type": "float", "kids": 14},
{"path": "Tgreen/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tgreen/active"},
{"path": "Tgreen/autorange", "type": "bool", "readonly": false, "cmd": "tt Tgreen/autorange", "description": "autorange (common for all channels)"},
{"path": "Tgreen/range", "type": "text", "readonly": false, "cmd": "tt Tgreen/range", "description": "resistance range in Ohm"},
{"path": "Tgreen/range_num", "type": "int"},
{"path": "Tgreen/excitation", "type": "text", "readonly": false, "cmd": "tt Tgreen/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tgreen/excitation_num", "type": "int"},
{"path": "Tgreen/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tgreen/pause", "type": "int", "readonly": false, "cmd": "tt Tgreen/pause", "description": "pause time [sec] after channel change"},
{"path": "Tgreen/filter", "type": "int", "readonly": false, "cmd": "tt Tgreen/filter", "description": "filter average time [sec]"},
{"path": "Tgreen/dwell", "type": "int", "readonly": false, "cmd": "tt Tgreen/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tgreen/status", "type": "text"},
{"path": "Tgreen/curve", "type": "text", "readonly": false, "cmd": "tt Tgreen/curve", "kids": 1},
{"path": "Tgreen/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tgreen/curve/points", "visibility": 3},
{"path": "Tgreen/alarm", "type": "float", "readonly": false, "cmd": "tt Tgreen/alarm"},
{"path": "Tgreen/raw", "type": "float"},
{"path": "analog2", "type": "float", "readonly": false, "cmd": "tt analog2"},
{"path": "remote", "type": "bool"},
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"}]},
"cmn": {"base": "/cmn", "params": [
{"path": "", "type": "none", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "cmn send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "u1", "type": "float"},
{"path": "temp", "type": "float"},
{"path": "u2", "type": "float"},
{"path": "chan", "type": "enum", "enum": {"auto": 0, "chan1": 1, "chan2": 2}, "readonly": false, "cmd": "cmn chan"}]}}

View File

@@ -284,8 +284,8 @@
{"path": "eeprom", "type": "enum", "enum": {"ok": 0, "dirty": 1, "save": 2, "load": 3}, "readonly": false, "cmd": "hemot eeprom"},
{"path": "customadr", "type": "text", "readonly": false, "cmd": "hemot customadr"},
{"path": "custompar", "type": "float", "readonly": false, "cmd": "hemot custompar"}]},
"ln2fill": {"base": "/ln2fill", "params": [
"
ln2fill": {"base": "/ln2fill", "params": [
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 14},
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},

View File

@@ -1,50 +1,50 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run tt", "description": "tt", "kids": 18},
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "status", "type": "text", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "is_running", "type": "int", "readonly": false, "cmd": "tt is_running", "visibility": 3},
{"path": "mainloop", "type": "text", "readonly": false, "cmd": "tt mainloop", "visibility": 3},
{"path": "target", "type": "float"},
{"path": "running", "type": "int"},
{"path": "target", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "running", "type": "int", "readonly": false, "cmd": "run tt"},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "tt tolerance"},
{"path": "maxwait", "type": "float", "readonly": false, "cmd": "tt maxwait"},
{"path": "settle", "type": "float", "readonly": false, "cmd": "tt settle"},
{"path": "log", "type": "text", "readonly": false, "cmd": "tt log", "visibility": 3, "kids": 4},
{"path": "log/mean", "type": "float", "visibility": 3},
{"path": "log/m2", "type": "float", "visibility": 3},
{"path": "log/stddev", "type": "float", "visibility": 3},
{"path": "log/n", "type": "float", "visibility": 3},
{"path": "log/mean", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/m2", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/stddev", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "log/n", "type": "float", "readonly": false, "cmd": "run tt", "visibility": 3},
{"path": "dblctrl", "type": "bool", "readonly": false, "cmd": "tt dblctrl", "kids": 9},
{"path": "dblctrl/tshift", "type": "float", "readonly": false, "cmd": "tt dblctrl/tshift"},
{"path": "dblctrl/mode", "type": "enum", "enum": {"disabled": -1, "inactive": 0, "stable": 1, "up": 2, "down": 3}, "readonly": false, "cmd": "tt dblctrl/mode"},
{"path": "dblctrl/shift_up", "type": "float"},
{"path": "dblctrl/shift_lo", "type": "float"},
{"path": "dblctrl/t_min", "type": "float"},
{"path": "dblctrl/t_max", "type": "float"},
{"path": "dblctrl/shift_up", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "dblctrl/shift_lo", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "dblctrl/t_min", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "dblctrl/t_max", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "dblctrl/int2", "type": "float", "readonly": false, "cmd": "tt dblctrl/int2"},
{"path": "dblctrl/prop_up", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_up"},
{"path": "dblctrl/prop_lo", "type": "float", "readonly": false, "cmd": "tt dblctrl/prop_lo"},
{"path": "tm", "type": "float", "kids": 4},
{"path": "tm", "type": "float", "readonly": false, "cmd": "run tt", "kids": 4},
{"path": "tm/curve", "type": "text", "readonly": false, "cmd": "tt tm/curve", "kids": 1},
{"path": "tm/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt tm/curve/points", "visibility": 3},
{"path": "tm/alarm", "type": "float", "readonly": false, "cmd": "tt tm/alarm"},
{"path": "tm/stddev", "type": "float"},
{"path": "tm/raw", "type": "float"},
{"path": "ts", "type": "float", "kids": 4},
{"path": "tm/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "tm/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "ts", "type": "float", "readonly": false, "cmd": "run tt", "kids": 4},
{"path": "ts/curve", "type": "text", "readonly": false, "cmd": "tt ts/curve", "kids": 1},
{"path": "ts/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts/curve/points", "visibility": 3},
{"path": "ts/alarm", "type": "float", "readonly": false, "cmd": "tt ts/alarm"},
{"path": "ts/stddev", "type": "float"},
{"path": "ts/raw", "type": "float"},
{"path": "ts_2", "type": "float", "visibility": 3, "kids": 4},
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "visibility": 3, "kids": 1},
{"path": "ts/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "ts/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "ts_2", "type": "float", "readonly": false, "cmd": "run tt", "kids": 4},
{"path": "ts_2/curve", "type": "text", "readonly": false, "cmd": "tt ts_2/curve", "kids": 1},
{"path": "ts_2/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt ts_2/curve/points", "visibility": 3},
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm", "visibility": 3},
{"path": "ts_2/stddev", "type": "float", "visibility": 3},
{"path": "ts_2/raw", "type": "float", "visibility": 3},
{"path": "ts_2/alarm", "type": "float", "readonly": false, "cmd": "tt ts_2/alarm"},
{"path": "ts_2/stddev", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "ts_2/raw", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "set", "type": "float", "readonly": false, "cmd": "tt set", "kids": 18},
{"path": "set/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt set/mode"},
{"path": "set/reg", "type": "float"},
{"path": "set/reg", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "set/ramp", "type": "float", "readonly": false, "cmd": "tt set/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
{"path": "set/wramp", "type": "float", "readonly": false, "cmd": "tt set/wramp"},
{"path": "set/smooth", "type": "float", "readonly": false, "cmd": "tt set/smooth", "description": "smooth time (minutes)"},
@@ -53,17 +53,17 @@
{"path": "set/resist", "type": "float", "readonly": false, "cmd": "tt set/resist"},
{"path": "set/maxheater", "type": "text", "readonly": false, "cmd": "tt set/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
{"path": "set/linearpower", "type": "float", "readonly": false, "cmd": "tt set/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
{"path": "set/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
{"path": "set/maxpowerlim", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum power limit (before any booster or converter)"},
{"path": "set/maxpower", "type": "float", "readonly": false, "cmd": "tt set/maxpower", "description": "maximum power [W]"},
{"path": "set/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
{"path": "set/maxcurrent", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum current before any booster or converter"},
{"path": "set/manualpower", "type": "float", "readonly": false, "cmd": "tt set/manualpower"},
{"path": "set/power", "type": "float"},
{"path": "set/power", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "set/prop", "type": "float", "readonly": false, "cmd": "tt set/prop", "description": "bigger means more gain"},
{"path": "set/integ", "type": "float", "readonly": false, "cmd": "tt set/integ", "description": "bigger means faster"},
{"path": "set/deriv", "type": "float", "readonly": false, "cmd": "tt set/deriv"},
{"path": "setsamp", "type": "float", "readonly": false, "cmd": "tt setsamp", "kids": 18},
{"path": "setsamp/mode", "type": "enum", "enum": {"disabled": -1, "off": 0, "controlling": 1, "manual": 2}, "readonly": false, "cmd": "tt setsamp/mode"},
{"path": "setsamp/reg", "type": "float"},
{"path": "setsamp/reg", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "setsamp/ramp", "type": "float", "readonly": false, "cmd": "tt setsamp/ramp", "description": "maximum ramp in K/min (0: ramp off)"},
{"path": "setsamp/wramp", "type": "float", "readonly": false, "cmd": "tt setsamp/wramp"},
{"path": "setsamp/smooth", "type": "float", "readonly": false, "cmd": "tt setsamp/smooth", "description": "smooth time (minutes)"},
@@ -72,16 +72,16 @@
{"path": "setsamp/resist", "type": "float", "readonly": false, "cmd": "tt setsamp/resist"},
{"path": "setsamp/maxheater", "type": "text", "readonly": false, "cmd": "tt setsamp/maxheater", "description": "maximum heater limit, units should be given without space: W, mW, A, mA"},
{"path": "setsamp/linearpower", "type": "float", "readonly": false, "cmd": "tt setsamp/linearpower", "description": "when not 0, it is the maximum effective power, and the power is linear to the heater output"},
{"path": "setsamp/maxpowerlim", "type": "float", "description": "the maximum power limit (before any booster or converter)"},
{"path": "setsamp/maxpowerlim", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum power limit (before any booster or converter)"},
{"path": "setsamp/maxpower", "type": "float", "readonly": false, "cmd": "tt setsamp/maxpower", "description": "maximum power [W]"},
{"path": "setsamp/maxcurrent", "type": "float", "description": "the maximum current before any booster or converter"},
{"path": "setsamp/maxcurrent", "type": "float", "readonly": false, "cmd": "run tt", "description": "the maximum current before any booster or converter"},
{"path": "setsamp/manualpower", "type": "float", "readonly": false, "cmd": "tt setsamp/manualpower"},
{"path": "setsamp/power", "type": "float"},
{"path": "setsamp/power", "type": "float", "readonly": false, "cmd": "run tt"},
{"path": "setsamp/prop", "type": "float", "readonly": false, "cmd": "tt setsamp/prop", "description": "bigger means more gain"},
{"path": "setsamp/integ", "type": "float", "readonly": false, "cmd": "tt setsamp/integ", "description": "bigger means faster"},
{"path": "setsamp/deriv", "type": "float", "readonly": false, "cmd": "tt setsamp/deriv"},
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"},
{"path": "remote", "type": "bool"}]},
{"path": "remote", "type": "bool", "readonly": false, "cmd": "run tt"}]},
"cc": {"base": "/cc", "params": [
{"path": "", "type": "bool", "kids": 96},
@@ -108,7 +108,7 @@
{"path": "mcr", "type": "float"},
{"path": "mot", "type": "float"},
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open"},
{"path": "hav", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc hav"},
{"path": "hav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc hav"},
{"path": "h", "type": "float"},
{"path": "hr", "type": "float"},
{"path": "hc", "type": "float"},
@@ -132,26 +132,26 @@
{"path": "hms", "type": "float"},
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit"},
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft"},
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 6}, "readonly": false, "cmd": "cc hea"},
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch"},
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0"},
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos."},
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos."},
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)"},
{"path": "h0", "type": "float"},
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "h1", "type": "float"},
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "h2", "type": "float"},
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "h3", "type": "float"},
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "h4", "type": "float"},
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "h5", "type": "float"},
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}},
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 2}, "readonly": false, "cmd": "cc hea"},
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
{"path": "h0", "type": "float", "visibility": 3},
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h1", "type": "float", "visibility": 3},
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h2", "type": "float", "visibility": 3},
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h3", "type": "float", "visibility": 3},
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h4", "type": "float", "visibility": 3},
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h5", "type": "float", "visibility": 3},
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "hfb", "type": "float"},
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
{"path": "nav", "type": "enum", "type": "enum", "enum": {"none": 0, "int": 1, "ext": 2}, "readonly": false, "cmd": "cc nav"},
{"path": "nu", "type": "float"},
{"path": "nl", "type": "float"},
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth"},
@@ -183,16 +183,15 @@
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]},
"nv": {"base": "/nv", "params": [
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 12},
{"path": "", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "close": 3, "open": 4}, "readonly": false, "cmd": "nv", "kids": 11},
{"path": "send", "type": "text", "readonly": false, "cmd": "nv send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "motstat", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}},
{"path": "flow", "type": "float"},
{"path": "set", "type": "float", "readonly": false, "cmd": "nv set"},
{"path": "flowmax", "type": "float", "readonly": false, "cmd": "nv flowmax"},
{"path": "flowp", "type": "float", "description": "flow calculated from pressure before pump"},
{"path": "flowp", "type": "float"},
{"path": "span", "type": "float"},
{"path": "use_pressure", "type": "bool", "readonly": false, "cmd": "nv use_pressure", "description": "use pressure instead of flow meter for control"},
{"path": "ctrl", "type": "none", "kids": 13},
{"path": "ctrl/regtext", "type": "text"},
{"path": "ctrl/prop_o", "type": "float", "readonly": false, "cmd": "nv ctrl/prop_o", "description": "prop [sec/mbar] when opening. above 4 mbar a 10 times lower value is used"},
@@ -236,34 +235,15 @@
{"path": "calib/ln_per_min_per_mbar", "type": "float", "readonly": false, "cmd": "nv calib/ln_per_min_per_mbar"},
{"path": "calib/mbar_offset", "type": "float", "readonly": false, "cmd": "nv calib/mbar_offset"}]},
"hefill": {"base": "/hefill", "params": [
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "readonly": false, "cmd": "hefill", "kids": 16},
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "state", "type": "text"},
{"path": "readpath", "type": "text", "readonly": false, "cmd": "hefill readpath", "visibility": 3},
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "hefill lowlevel"},
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "hefill highlevel"},
{"path": "smooth", "type": "float"},
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "hefill minfillminutes"},
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "hefill maxfillminutes"},
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "hefill minholdhours"},
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "hefill maxholdhours"},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "hefill tolerance"},
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
{"path": "vessellimit", "type": "float", "readonly": false, "cmd": "hefill vessellimit"},
{"path": "vext", "type": "float"}]},
"hepump": {"base": "/hepump", "params": [
{"path": "", "type": "enum", "enum": {"neodry": 8, "xds35_auto": 0, "xds35_manual": 1, "sv65": 2, "other": 3, "no": -1}, "readonly": false, "cmd": "hepump", "description": "xds35: scroll pump, sv65: leybold", "kids": 10},
{"path": "send", "type": "text", "readonly": false, "cmd": "hepump send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "running", "type": "bool", "readonly": false, "cmd": "hepump running"},
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco", "visibility": 3},
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto", "visibility": 3},
{"path": "eco", "type": "bool", "readonly": false, "cmd": "hepump eco"},
{"path": "auto", "type": "bool", "readonly": false, "cmd": "hepump auto"},
{"path": "valve", "type": "enum", "enum": {"closed": 0, "closing": 1, "opening": 2, "opened": 3, "undefined": 4}, "readonly": false, "cmd": "hepump valve"},
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2", "visibility": 3},
{"path": "eco_t_lim", "type": "float", "readonly": false, "cmd": "hepump eco_t_lim", "description": "switch off eco mode when T_set < eco_t_lim and T < eco_t_lim * 2"},
{"path": "calib", "type": "float", "readonly": false, "cmd": "hepump calib", "visibility": 3},
{"path": "health", "type": "float"}]},
@@ -311,11 +291,11 @@
{"path": "save", "type": "bool", "readonly": false, "cmd": "nvflow save", "description": "unchecked: current calib is not saved. set checked: save calib"}]},
"ln2fill": {"base": "/ln2fill", "params": [
{"path": "", "type": "enum", "enum": {"watching": 0, "filling": 1, "inactive": 2, "manualfill": 3}, "readonly": false, "cmd": "ln2fill", "kids": 14},
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "ln2fill", "kids": 14},
{"path": "send", "type": "text", "readonly": false, "cmd": "ln2fill send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "state", "type": "text"},
{"path": "readpath", "type": "text", "readonly": false, "cmd": "ln2fill readpath", "visibility": 3},
{"path": "readlevel", "type": "text", "readonly": false, "cmd": "ln2fill readlevel", "visibility": 3},
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "ln2fill lowlevel"},
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "ln2fill highlevel"},
{"path": "smooth", "type": "float"},
@@ -327,33 +307,52 @@
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "ln2fill badreadingminutes"},
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "ln2fill tubecoolingminutes"}]},
"hefill": {"base": "/hefill", "params": [
{"path": "", "type": "enum", "enum": {"watching": 0, "fill": 1, "inactive": 2}, "readonly": false, "cmd": "hefill", "kids": 16},
{"path": "send", "type": "text", "readonly": false, "cmd": "hefill send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "state", "type": "text"},
{"path": "readlevel", "type": "text", "readonly": false, "cmd": "hefill readlevel", "visibility": 3},
{"path": "lowlevel", "type": "float", "readonly": false, "cmd": "hefill lowlevel"},
{"path": "highlevel", "type": "float", "readonly": false, "cmd": "hefill highlevel"},
{"path": "smooth", "type": "float"},
{"path": "minfillminutes", "type": "float", "readonly": false, "cmd": "hefill minfillminutes"},
{"path": "maxfillminutes", "type": "float", "readonly": false, "cmd": "hefill maxfillminutes"},
{"path": "minholdhours", "type": "float", "readonly": false, "cmd": "hefill minholdhours"},
{"path": "maxholdhours", "type": "float", "readonly": false, "cmd": "hefill maxholdhours"},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "hefill tolerance"},
{"path": "badreadingminutes", "type": "float", "readonly": false, "cmd": "hefill badreadingminutes"},
{"path": "tubecoolingminutes", "type": "float", "readonly": false, "cmd": "hefill tubecoolingminutes"},
{"path": "vessellimit", "type": "float", "readonly": false, "cmd": "hefill vessellimit"},
{"path": "vext", "type": "float"}]},
"mf": {"base": "/mf", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run mf", "kids": 26},
{"path": "persmode", "type": "int", "readonly": false, "cmd": "mf persmode"},
{"path": "perswitch", "type": "int"},
{"path": "perswitch", "type": "int", "readonly": false, "cmd": "run mf"},
{"path": "nowait", "type": "int", "readonly": false, "cmd": "mf nowait"},
{"path": "maxlimit", "type": "float", "visibility": 3},
{"path": "maxlimit", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "limit", "type": "float", "readonly": false, "cmd": "mf limit"},
{"path": "ramp", "type": "float", "readonly": false, "cmd": "mf ramp"},
{"path": "perscurrent", "type": "float", "readonly": false, "cmd": "mf perscurrent"},
{"path": "perslimit", "type": "float", "readonly": false, "cmd": "mf perslimit"},
{"path": "perswait", "type": "int", "readonly": false, "cmd": "mf perswait"},
{"path": "persdelay", "type": "int", "readonly": false, "cmd": "mf persdelay"},
{"path": "current", "type": "float"},
{"path": "measured", "type": "float"},
{"path": "voltage", "type": "float"},
{"path": "lastfield", "type": "float", "visibility": 3},
{"path": "ampRamp", "type": "float", "visibility": 3},
{"path": "inductance", "type": "float", "visibility": 3},
{"path": "current", "type": "float", "readonly": false, "cmd": "run mf"},
{"path": "measured", "type": "float", "readonly": false, "cmd": "run mf"},
{"path": "voltage", "type": "float", "readonly": false, "cmd": "run mf"},
{"path": "lastfield", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "ampRamp", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "inductance", "type": "float", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "trainedTo", "type": "float", "readonly": false, "cmd": "mf trainedTo"},
{"path": "trainMode", "type": "int"},
{"path": "trainMode", "type": "int", "readonly": false, "cmd": "run mf"},
{"path": "external", "type": "int", "readonly": false, "cmd": "mf external"},
{"path": "startScript", "type": "text", "readonly": false, "cmd": "mf startScript", "visibility": 3},
{"path": "is_running", "type": "int", "readonly": false, "cmd": "mf is_running", "visibility": 3},
{"path": "is_running", "type": "int", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "verbose", "type": "int", "readonly": false, "cmd": "mf verbose", "visibility": 3},
{"path": "driver", "type": "text", "visibility": 3},
{"path": "creationCmd", "type": "text", "visibility": 3},
{"path": "targetValue", "type": "float"},
{"path": "driver", "type": "text", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "creationCmd", "type": "text", "readonly": false, "cmd": "run mf", "visibility": 3},
{"path": "targetValue", "type": "float", "readonly": false, "cmd": "run mf"},
{"path": "status", "type": "text", "readonly": false, "cmd": "mf status", "visibility": 3}]},
"lev": {"base": "/lev", "params": [
@@ -363,22 +362,7 @@
{"path": "mode", "type": "enum", "enum": {"slow": 0, "fast (switches to slow automatically after filling)": 1}, "readonly": false, "cmd": "lev mode"},
{"path": "n2", "type": "float"}]},
"table": {"base": "/table", "params": [
{"path": "", "type": "none", "kids": 17},
{"path": "send", "type": "text", "readonly": false, "cmd": "table send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "fix_tt_set_prop", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_prop"},
{"path": "val_tt_set_prop", "type": "float"},
{"path": "tbl_tt_set_prop", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_prop", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
{"path": "fix_tt_set_integ", "type": "bool", "readonly": false, "cmd": "table fix_tt_set_integ"},
{"path": "val_tt_set_integ", "type": "float"},
{"path": "tbl_tt_set_integ", "type": "text", "readonly": false, "cmd": "table tbl_tt_set_integ", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
{"path": "fix_tt_dblctrl_int2", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_int2"},
{"path": "val_tt_dblctrl_int2", "type": "float"},
{"path": "tbl_tt_dblctrl_int2", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_int2", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
{"path": "fix_tt_dblctrl_prop_up", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_up"},
{"path": "val_tt_dblctrl_prop_up", "type": "float"},
{"path": "tbl_tt_dblctrl_prop_up", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_up", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."},
{"path": "fix_tt_dblctrl_prop_lo", "type": "bool", "readonly": false, "cmd": "table fix_tt_dblctrl_prop_lo"},
{"path": "val_tt_dblctrl_prop_lo", "type": "float"},
{"path": "tbl_tt_dblctrl_prop_lo", "type": "text", "readonly": false, "cmd": "table tbl_tt_dblctrl_prop_lo", "description": "enter value pair separated with colon T1:par1 T2:par2 ..."}]}}
"prep0": {"base": "/prep0", "params": [
{"path": "", "type": "text", "readonly": false, "cmd": "prep0", "kids": 2},
{"path": "send", "type": "text", "readonly": false, "cmd": "prep0 send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3}]}}

View File

@@ -1,20 +1,20 @@
# pylint: skip-file
Node('ccr12',
'[sim] CCR12 box of MLZ Sample environment group\n'
'\n'
'Contains a Lakeshore 336 and an PLC controlling the compressor\n'
'and some valves.\n'
'\n'
'This is an improved version, how we think it should be.\n',
'[sim] CCR12 box of MLZ Sample environment group'
''
'Contains a Lakeshore 336 and an PLC controlling the compressor'
'and some valves.'
''
'This is an improved version, how we think it should be.',
'tcp://10767',
)
Mod('T_ccr12',
'frappy.simulation.SimDrivable',
'Main temperature control node of CCR12.\n'
'\n'
'Switches between regulation on stick and regulation on tube depending on temperature requested.\n'
'May also pump gas for higher temperatures, if configured.\n'
'Main temperature control node of CCR12.'
''
'Switches between regulation on stick and regulation on tube depending on temperature requested.'
'May also pump gas for higher temperatures, if configured.'
'Manual switching of the regulation node is supported via the regulationmode parameter.',
value = Param(default=300,
datatype={"type":"double", "min":0, "max":600, "unit":"K"}),

View File

@@ -54,7 +54,7 @@ Mod('T',
'frappy_psi.softcal.Sensor',
'temperature sensor, soft calibration',
rawsensor='res',
calcurve='X132254',
calib='X132254',
value=Param(
unit='K',
),

View File

@@ -16,5 +16,5 @@ Mod('T2',
'',
value = Param(unit = 'K'),
rawsensor = 'r2',
calcurve = 'X131346',
calib = 'X131346',
)

View File

@@ -43,5 +43,5 @@ Mod('ts',
'calibrated value for ts',
value = Param(unit = 'K'),
rawsensor = 'tsraw',
calcurve = 'X133834',
calib = 'X133834',
)

View File

@@ -38,6 +38,6 @@ Mod('T_sample',
output_module='htr_sample',
p=1,
i=0.01,
calcurve='X161269',
calib='X161269',
value=Param(unit='K'),
)

View File

@@ -9,23 +9,10 @@ Mod('sea_stick',
service='stick',
)
Mod('ts_sea',
'frappy_psi.sea.SeaReadable',
'readable sample stick T',
io='sea_stick',
json_file='ma7.config.json',
sea_object='tt',
rel_paths=['ts', 'setsamp'],
)
Mod('ts',
'frappy_psi.parmod.Converging',
'drivable stick T using setsamp',
unit='K',
read='ts_sea.value',
write='ts_sea.setsamp',
meaning=['temperature', 20],
settling_time=20,
tolerance=1,
'frappy_psi.sea.SeaReadable', '',
meaning=['temperature', 30],
io='sea_stick',
sea_path='tt/ts',
json_file='ma7.config.json',
)

View File

@@ -1,33 +1,3 @@
import os
Node('mb11.stick.sea.psi.ch',
'MB11 standard sample stick (do not use)',
)
frappy_main_port = os.environ.get('FRAPPY_MAIN_PORT', 0)
Mod('itc1_',
'frappy.core.Proxy',
'itc1 on main frappy server',
remote_class = 'frappy_psi.mercury.IO',
uri = f'tcp://localhost:{frappy_main_port}',
module='itc1',
# export = False,
)
Mod('T_sample',
'frappy_psi.mercury.TemperatureLoop',
'T at sample stick sensor',
meaning=['temperature', 30],
io='itc1_',
slot='MB1.T1',
)
Mod('htr_sample',
'frappy_psi.mercury.HeaterOutput',
'sample stick heater power',
slot='MB0.H1',
io='itc1_',
)

View File

@@ -1,34 +0,0 @@
Node('mspare.stick.sea.psi.ch',
'MA generic sample stick',
)
Mod('sea_stick',
'frappy_psi.sea.SeaClient',
'SEA stick connection',
config='mspare.stick',
service='stick',
)
Mod('ts',
'frappy_psi.sea.SeaReadable',
'sample stick temperature',
io='sea_stick',
json_file='ma6.config.json',
sea_object='tt',
rel_paths=['ts', 'setsamp'],
meaning=['temperature', 30],
)
"""
Mod('ts',
'frappy_psi.parmod.Converging',
'drivable stick T using setsamp',
meaning=['temperature', 25],
unit='K',
read='tsam.value',
write='tsam.setsamp',
settling_time=20,
tolerance=1,
)
"""

View File

@@ -1,24 +0,0 @@
Node('tcstest.psi.ch',
'heater tcs test',
'tcp://5000',
)
Mod('io',
'frappy_psi.tcs.IO',
'tcs communication',
uri='linse-leiden-ts:3005',
)
Mod('still_htr',
'frappy_psi.tcs.Heater',
'still heater',
io='io',
channel=2,
)
Mod('mix_htr',
'frappy_psi.tcs.Heater',
'mixing chamber heater',
io='io',
channel=3,
)

View File

@@ -1,15 +0,0 @@
Node('softcal.function.test',
'test the function class',
'tcp://5000',
)
Mod('sim_writable',
'frappy_demo.test.SimpleWritable',
'simulation of a writable for function test',
)
Mod('function',
'frappy_psi.softcal.Function',
'function test',
rawsensor = 'sim_writable',
)

View File

@@ -1,81 +0,0 @@
Node('thermofischer.psi.ch',
'thermofischer_waterbath',
'tcp://5000',
)
Mod('wio_1',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB0?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('wio_2',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB1?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('wio_3',
'frappy_psi.thermofisher.ThermFishIO',
'connection for water bath',
uri='serial:///dev/ttyUSB2?baudrate=19200', # 3001 = Port 1, 3002 = Port 2 ...
)
Mod('Tbath_1',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_1',
io='wio_1',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('Tbath_2',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_2',
io='wio_2',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('Tbath_3',
'frappy_psi.thermofisher.TemperatureLoopA10',
'water_bath_3',
io='wio_3',
control_active=0,
target=25,
tolerance=0.1,
settling_time=20,
)
Mod('valve_1',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o1',
target = 0,
)
Mod('valve_2',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o2',
target = 0,
)
Mod('valve_3',
'frappy_psi.ionopimax.DigitalOutput',
'valve_for_fast_water_temperature_changing',
addr = 'o3',
target = 0,
)
Mod('temp_sensor_1',
'frappy_psi.ionopimax.SimpleVoltageInput',
'temperatur_sensor_sample',
rawrange = (0.0, 10.0),
valuerange = (-40.0, 150.0),
addr = 'ai1_mv',
)

View File

@@ -52,7 +52,7 @@ Mod('T',
'frappy_psi.softcal.Sensor',
'sample T',
rawsensor='res',
calcurve='X132254',
calib='X132254',
value=Param(
unit='K',
),

17
debian/changelog vendored
View File

@@ -1,20 +1,3 @@
frappy-core (0.20.7) stable; urgency=medium
* fix debian install
-- Georg Brandl <jenkins@frm2.tum.de> Fri, 25 Jul 2025 13:22:54 +0200
frappy-core (0.20.6) stable; urgency=medium
[ Markus Zolliker ]
* config: add 'include' and 'override'
* frappy.client.interactive: no pathlib needed here
[ Georg Brandl ]
* install systemd units to /usr/lib
-- Markus Zolliker <jenkins@frm2.tum.de> Thu, 24 Jul 2025 22:26:02 +0200
frappy-core (0.20.5) stable; urgency=medium
[ Markus Zolliker ]

1
debian/control vendored
View File

@@ -9,6 +9,7 @@ Build-Depends: debhelper-compat (= 13),
python3-setuptools,
python3-docutils,
python3-sphinx,
python3-sip-dev,
python3-pyqt5,
python3-mlzlog,
python3-numpy,

View File

@@ -10,6 +10,6 @@ usr/lib/python3.*/dist-packages/frappy/protocol
usr/lib/python3.*/dist-packages/frappy_core-*
usr/lib/python3.*/dist-packages/frappy/RELEASE-VERSION
usr/lib/python3.*/dist-packages/frappy_demo
usr/lib/systemd
lib/systemd
var/log/frappy
etc/generalConfig.cfg etc/frappy
etc/frappy/generalConfig.cfg

16
debian/rules vendored
View File

@@ -1,13 +1,19 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# needed for bookworm compatibility!
override_dh_installsystemd:
ln -s usr/lib debian/frappy-core/lib
dh_installsystemd
rm debian/frappy-core/lib
export PYBUILD_NAME=frappy
export PYBUILD_TEST_PYTEST=1
override_dh_install:
rmdir debian/tmp
mv debian/python3-frappy debian/tmp
install -m644 -Dt debian/tmp/etc/frappy etc/generalConfig.cfg
dh_install -i -O--buildsystem=pybuild
dh_missing --fail-missing
%:
dh $@ --with python3 --buildsystem=pybuild

View File

@@ -35,7 +35,7 @@ def main():
generalConfig.init()
config_dir = generalConfig['confdir']
frappy_unit = '/usr/lib/systemd/system/frappy@.service'
frappy_unit = '/lib/systemd/system/frappy@.service'
wants_dir = normal_dir + '/frappy.target.wants'
all_servers = [base[:-4] if base.endswith('_cfg') else base for (base, ext) in

View File

@@ -1,75 +0,0 @@
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
from frappy.core import Parameter, Property
from frappy.datatypes import ValueType
class AddrParam(Parameter):
"""parameter with an address field
instead of implementing read_<param> and write_<param>, just implement
addressed_read and addressed_write.
"""
addr = Property('address', ValueType())
class AddrMixin:
"""mixin for addressed parameters
in case a read_<param> and/or write_<param> are not implemented,
they are created with a call to addressed_read and/or addressed_write
"""
def __init_subclass__(cls):
for aname, aobj in list(cls.__dict__.items()):
if isinstance(aobj, AddrParam):
methodname = f'read_{aname}'
if not hasattr(cls, methodname):
def rfunc(self, pname=aname):
return self.addressed_read(self.accessibles[pname])
setattr(cls, methodname, rfunc)
if not aobj.readonly:
methodname = f'write_{aname}'
if not hasattr(cls, methodname):
def wfunc(self, value, pname=aname):
return self.addressed_write(self.accessibles[pname], value)
setattr(cls, methodname, wfunc)
super().__init_subclass__()
def addressed_read(self, pobj):
"""addressed read
:param pobj: the AddrParam
:return: the value read
"""
return getattr(self, pobj.name)
def addressed_write(self, pobj, value):
"""addressed write
:param pobj: the AddrParam
:param value: the value to be written
:return: the value written or None
"""

View File

@@ -1,131 +0,0 @@
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
# Markus Zolliker <markus.zolliker@psi.ch>
# Alexander Zaft <a.zaft@fz-juelich.de>
#
# *****************************************************************************
from frappy.errors import ConfigError
from frappy.modulebase import Module
from frappy.datatypes import StringType, ValueType
from frappy.properties import Property
class Attached(Property):
"""a special property, defining an attached module
assign a module name to this property in the cfg file,
and the server will create an attribute with this module
When mandatory is set to False, and there is no value or an empty string
given in the config file, the value of the attribute will be None.
"""
def __init__(self, basecls=Module, description='attached module', mandatory=True):
self.basecls = basecls
super().__init__(description, StringType(), mandatory=mandatory)
def __get__(self, obj, owner):
if obj is None:
return self
modobj = obj.attachedModules.get(self.name)
if not modobj:
modulename = super().__get__(obj, owner)
if not modulename:
return None # happens when mandatory=False and modulename is not given
modobj = obj.secNode.get_module(modulename)
if not modobj:
raise ConfigError(f'attached module {self.name}={modulename!r} '
f'does not exist')
if not isinstance(modobj, self.basecls):
raise ConfigError(f'attached module {self.name}={modobj.name!r} '
f'must inherit from {self.basecls.__qualname__!r}')
obj.attachedModules[self.name] = modobj
return modobj
def copy(self):
return Attached(self.basecls, self.description, self.mandatory)
class DictWithFlag(dict):
flag = False
class AttachDictType(ValueType):
"""a custom datatype for a dict <key> of names or modules"""
def __init__(self):
super().__init__(DictWithFlag)
def copy(self):
return AttachDictType()
def export_value(self, value):
"""export either names or the name attribute
to treat bare names and modules the same
"""
return {k: getattr(v, 'name', v) for k, v in value.items()}
class AttachedDict(Property):
def __init__(self, description='attached modules', elements=None, optional=None, basecls=None,
**kwds):
"""a mapping of attached modules
:param elements: None or a dict <key> of <basecls> for mandatory elements
:param optional: None or a dict <key> of <basecls> for optional elements
:param basecls: None or a base class for arbitrary keys
if not given, only keys given in parameters 'elements' and 'optional' are allowed
:param description: the property description
<key> might also be a number or any other immutable
"""
self.elements = elements or {}
self.basecls = basecls
self.baseclasses = {**self.elements, **(optional or {})}
super().__init__(description, AttachDictType(), default={}, **kwds)
def __get__(self, obj, owner):
if obj is None:
return self
attach_dict = super().__get__(obj, owner) or DictWithFlag({})
if attach_dict.flag:
return attach_dict
for key, modulename in attach_dict.items():
basecls = self.baseclasses.get(key, self.basecls)
if basecls is None:
raise ConfigError(f'unknown key {key!r} for attached modules {self.name}')
modobj = obj.secNode.get_module(modulename)
if modobj is None:
raise ConfigError(f'attached modules {self.name}: '
f'{key}={modulename!r} does not exist')
if not isinstance(modobj, basecls):
raise ConfigError(f'attached modules {self.name}: '
f'module {key}={modulename!r} must inherit '
f'from {basecls.__qualname__!r}')
obj.attachedModules[self.name, key] = attach_dict[key] = modobj
missing_keys = set(self.elements) - set(attach_dict)
if missing_keys:
raise ConfigError(f'attached modules {self.name}: '
f"missing {', '.join(missing_keys)} ")
attach_dict.flag = True
return attach_dict
def copy(self):
return AttachedDict(self.elements, self.baseclasses, self.basecls, self.description)

View File

@@ -29,7 +29,7 @@ import os
import traceback
import threading
import logging
from pathlib import Path
from os.path import expanduser
from frappy.lib import delayed_import
from frappy.client import SecopClient, UnregisterCallback
from frappy.errors import SECoPError
@@ -64,8 +64,6 @@ LOG_LEVELS = {
'off': logging.ERROR+1}
CLR = '\r\x1b[K' # code to move to the left and clear current line
UNDEF = object()
class Handler(logging.StreamHandler):
def emit(self, record):
@@ -215,8 +213,8 @@ class Module:
clientenv.raise_with_short_traceback(error)
return value
def __call__(self, target=UNDEF):
if target is UNDEF:
def __call__(self, target=None):
if target is None:
return self.read()
watch_params = ['value', 'status']
for pname in watch_params:
@@ -224,15 +222,7 @@ class Module:
updateEvent=self._watch_parameter,
callimmediately=False)
if 'go' in self._commands:
if 'goal' in self._parameters and target is not None:
self.goal_enable = True
self.goal = target
if 'target' in self._parameters:
self.target = target
self.go()
elif 'target' in self._parameters:
self.target = target # this sets self._is_driving
self.target = target # this sets self._is_driving
def loop():
while self._is_driving:
@@ -240,12 +230,9 @@ class Module:
self._driving_event.clear()
try:
loop()
except KeyboardInterrupt:
except KeyboardInterrupt as e:
self._secnode.log.info('-- interrupted --')
try:
self.stop()
except Exception as e:
print(f'while stopping: {e!r}')
self.stop()
try:
loop() # wait for stopping to be finished
except KeyboardInterrupt:
@@ -255,8 +242,8 @@ class Module:
finally:
self._secnode.readParameter(self._name, 'value')
for pname in watch_params:
self._secnode.unregister_callback(
(self._name, pname), updateEvent=self._watch_parameter)
self._secnode.unregister_callback((self._name, pname),
updateEvent=self._watch_parameter)
return self.value
def __repr__(self):
@@ -310,8 +297,6 @@ class Param:
class Command:
_obj = None
def __init__(self, name, modname, secnode):
self.name = name
self.modname = modname
@@ -326,14 +311,11 @@ class Command:
result, _ = self.exec(self.modname, self.name, *args)
else:
result, _ = self.exec(self.modname, self.name, args or None)
if self.name == 'go' and self._obj:
self._obj._is_driving = self._obj._isBusy()
return result
def __get__(self, obj, owner=None):
if obj is None:
return self
self._obj = obj
return self.call
@@ -515,7 +497,7 @@ class Console(code.InteractiveConsole):
history = None
if readline:
try:
history = Path(f'~/.local/state/frappy-{name}-history').expanduser()
history = expanduser(f'~/.config/frappy/{name}-history')
readline.read_history_file(history)
except FileNotFoundError:
pass
@@ -523,7 +505,6 @@ class Console(code.InteractiveConsole):
self.interact('', '')
finally:
if history:
history.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
readline.write_history_file(history)
def raw_input(self, prompt=""):

View File

@@ -29,8 +29,8 @@ from frappy.datatypes import ArrayOf, BLOBType, BoolType, EnumType, \
FloatRange, IntRange, ScaledInteger, StringType, StructOf, TupleOf, StatusType
from frappy.lib.enum import Enum
from frappy.modulebase import Done, Module, Feature
from frappy.modules import Communicator, Drivable, Readable, Writable
from frappy.attached import Attached, AttachedDict
from frappy.modules import Attached, Communicator, \
Drivable, Readable, Writable
from frappy.params import Command, Parameter, Limit
from frappy.properties import Property
from frappy.proxy import Proxy, SecNode, proxy_class

View File

@@ -1,214 +0,0 @@
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
from frappy.datatypes import BoolType, EnumType, Enum
from frappy.core import Parameter, Attached
class WrapControlledBy:
"""mixin to add controlled_by to writable modules
Create a wrapper class inheriting from this mixin
to add controlled_by to a Writable module
Usage:
class Enhanced(WrapControlledBy, BaseWritable):
pass
# typically nothing else has to be implemented
from a module with output (inheriting from HasOutput), the
method update_target must be called for internal updates
"""
controlled_by = Parameter('source of target value', EnumType(members={'self': 0}), default=0)
target = Parameter() # make sure target is a parameter
inputCallbacks = ()
def register_input(self, name, deactivate_control):
"""register input
:param name: the name of the module (for controlled_by enum)
:param deactivate_control: a method on the input module to switch off control
called by <controller module>.initModule
"""
if not self.inputCallbacks:
self.inputCallbacks = {}
self.inputCallbacks[name] = deactivate_control
prev_enum = self.parameters['controlled_by'].datatype.export_datatype()['members']
# add enum member, using autoincrement feature of Enum
self.parameters['controlled_by'].datatype = EnumType(Enum(prev_enum, **{name: None}))
self.log.info('enumtype %r', self.parameters['controlled_by'].datatype)
def write_controlled_by(self, modulename):
result = modulename
if modulename in ('self', self.name):
# inform the deactivate_control methods, that we have already switched
self.controlled_by = result = 'self'
for name, deactivate_control in self.inputCallbacks.items():
if name != modulename:
deactivate_control(modulename)
return result
def self_controlled(self):
"""method to change controlled_by to self
to be called from the write_target method
"""
if self.controlled_by != 0:
self.write_controlled_by('self')
def set_off(self):
"""to be overridden if the off state should be different from the default
on a FloatRange() the default value is 0
"""
zero = self.parameters['target'].datatype.default
try:
self.internal_set_target(zero)
except Exception as e:
self.target = zero
def update_target(self, module, value):
"""update internal target value
as write_target would switch to manual mode, the controlling module
has to use this method to update the value
override and super call, if other actions are needed
"""
if self.controlled_by != module:
deactivate_control = self.inputCallbacks.get(self.controlled_by)
if deactivate_control:
deactivate_control(module)
self.controlled_by = module
target = self.internal_set_target(value)
self.target = value if target is None else target
def write_target(self, target):
self.self_controlled()
return self.internal_set_target(target)
def internal_set_target(self, target):
# we need this additional indirection:
# super().write_target must refer to an inherited base class
# which is after WrapControlledBy in the method resolution order
return super().write_target(target)
class HasControlledBy(WrapControlledBy):
"""mixin for controlled_by functionality
Create a wrapper class inheriting from this mixin
to add controlled_by to a Writable module
Usage:
class Enhanced(HasControlledBy, BaseWritable):
def set_target(self, value):
# implement here hardware access for setting target
# do not override write_target!
from a module with output (inheriting from HasOutput), the
method update_target must be called for internal updates
"""
def set_target(self, value):
"""to be overridden for setting target of HW"""
raise NotImplementedError
def internal_set_target(self, value):
# we need this additional indirection:
# self.write_target must refer to a base class which
# is before HasControlledBy in the method resolution order
return self.set_target(value)
def set_off(self):
"""typically needs to be overridden"""
class HasOutputModule:
"""mixin for modules having an output module
this module will call the update_target method of an output module
"""
# mandatory=False: it should be possible to configure a module with fixed control
output_module = Attached(WrapControlledBy, mandatory=False)
control_active = Parameter('control mode', BoolType(), default=False)
target = Parameter() # make sure target is a parameter
def initModule(self):
super().initModule()
if self.output_module:
self.output_module.register_input(self.name, self.deactivate_control)
def write_control_active(self, value):
"""override with supercall if needed
control_active is readonly by default, as specified in the SECoP standard.
This method is meant to be called internally.
However, it is possible to override control_active with readonly=False
and this is quite useful IMHO in some situations
"""
out = self.output_module
if out:
if value:
if out.controlled_by != self.name:
# deactivate control an all modules controlling our output_module
out.write_controlled_by(self.name)
else:
if out.controlled_by == self.name:
out.set_off() # this sets out.controlled_by to 0 (=self)
def set_control_active(self, active):
"""to be overridden for switching hw control
TODO: remove this legacy method (replaced by write_control_active)
"""
self.control_active = active
def activate_control(self):
"""method to switch control_active on
TODO: remove this legacy method (replaced by write_control_active)
"""
self.write_control_active(True)
def deactivate_control(self, source=None):
"""called when another module takes over control
registered to be called from the controlled module(s)
"""
if self.control_active:
self.write_control_active(False)
self.log.warning(f'switched to manual mode by {source or self.name}')
def write_target(self, target):
self.write_control_active(True)
return self.set_target(target)
def set_target(self, target):
"""to be overridden"""
raise NotImplementedError

View File

@@ -53,9 +53,13 @@ def shortrepr(value):
return r
class SimpleDataType(HasProperties):
"""base class for simple datatypes, used in properties only"""
# base class for all DataTypes
class DataType(HasProperties):
"""base class for all data types"""
IS_COMMAND = False
unit = ''
default = None
client = False # used on the client side
def __call__(self, value):
"""convert given value to our datatype and validate
@@ -101,10 +105,38 @@ class SimpleDataType(HasProperties):
"""
return self.format_value(value, False)
def export_datatype(self):
"""return a python object which after jsonifying identifies this datatype"""
raise ProgrammingError(
f"{type(self).__name__} is not able to be exported to SECoP. "
f"It is intended for internal use only."
)
def export_value(self, value):
"""if needed, reformat value for transport"""
return value
def import_value(self, value):
"""opposite of export_value, reformat from transport to internal repr
note: for importing from gui/configfile/commandline use :meth:`from_string`
instead.
"""
return self(value)
def format_value(self, value, unit=True):
"""format a value of this type into a string
This is intended for 'nice' formatting for humans and is NOT
the opposite of :meth:`from_string`
possible values of unit:
- True: use the string of the datatype
- False: return a value interpretable by ast.literal_eval (internal use only)
- any other string: use as unit (internal use only)
"""
raise NotImplementedError
def set_properties(self, **kwds):
"""init datatype properties"""
try:
@@ -129,34 +161,6 @@ class SimpleDataType(HasProperties):
# looks like the simplest way to make a deep copy
return get_datatype(self.export_datatype())
class DataType(SimpleDataType):
"""base class for data types used in parameters and commands"""
IS_COMMAND = False
unit = ''
client = False # used on the client side
def import_value(self, value):
"""opposite of export_value, reformat from transport to internal repr
note: for importing from gui/configfile/commandline use :meth:`from_string`
instead.
"""
return self(value)
def format_value(self, value, unit=True):
"""format a value of this type into a string
This is intended for 'nice' formatting for humans and is NOT
the opposite of :meth:`from_string`
possible values of unit:
- True: use the string of the datatype
- False: return a value interpretable by ast.literal_eval (internal use only)
- any other string: use as unit (internal use only)
"""
raise NotImplementedError
def compatible(self, other):
"""check other for compatibility
@@ -165,10 +169,6 @@ class DataType(SimpleDataType):
"""
raise NotImplementedError
def export_datatype(self):
"""return a python object which after jsonifying identifies this datatype"""
raise NotImplementedError
def set_main_unit(self, unit):
"""replace $ in unit by argument"""
@@ -238,7 +238,7 @@ class FloatRange(HasUnit, DataType):
self.default = 0 if self.min <= 0 <= self.max else self.min
super().checkProperties()
if '%' not in self.fmtstr:
raise ConfigError('Invalid fmtstr')
raise ConfigError('Invalid fmtstr!')
def export_datatype(self):
return self.get_info(type='double')
@@ -411,7 +411,7 @@ class ScaledInteger(HasUnit, DataType):
# check values
if '%' not in self.fmtstr:
raise ConfigError('Invalid fmtstr')
raise ConfigError('Invalid fmtstr!')
# Remark: Datatype.copy() will round min, max to a multiple of self.scale
# this should be o.k.
@@ -557,9 +557,9 @@ class BLOBType(DataType):
internally treated as bytes
"""
minbytes = Property('minimum number of bytes', IntRange(0, UNLIMITED), extname='minbytes',
minbytes = Property('minimum number of bytes', IntRange(0), extname='minbytes',
default=0)
maxbytes = Property('maximum number of bytes', IntRange(0, UNLIMITED), extname='maxbytes',
maxbytes = Property('maximum number of bytes', IntRange(0), extname='maxbytes',
mandatory=True)
def __init__(self, minbytes=0, maxbytes=None):
@@ -587,10 +587,10 @@ class BLOBType(DataType):
size = len(value)
if size < self.minbytes:
raise RangeError(
f'blob {shortrepr(value)!r} must be at least {self.minbytes} bytes long (got {size})')
f'{value!r} must be at least {self.minbytes} bytes long!')
if size > self.maxbytes:
raise RangeError(
f'blob {shortrepr(value)!r} must be at most {self.maxbytes} bytes long (got {size})')
f'{value!r} must be at most {self.maxbytes} bytes long!')
return value
def export_value(self, value):
@@ -646,22 +646,22 @@ class StringType(DataType):
def __call__(self, value):
"""accepts strings only"""
if not isinstance(value, str):
raise WrongTypeError(f'{shortrepr(value)} has the wrong type')
raise WrongTypeError(f'{shortrepr(value)} has the wrong type!')
if not self.isUTF8:
try:
value.encode('ascii')
except UnicodeEncodeError:
raise RangeError(f'{shortrepr(value)} contains non-ascii character') from None
raise RangeError(f'{shortrepr(value)} contains non-ascii character!') from None
size = len(value)
if size < self.minchars:
raise RangeError(
f'string {shortrepr(value)} must be at least {self.minchars} chars long (got {size})')
f'{shortrepr(value)} must be at least {self.minchars} chars long!')
if size > self.maxchars:
raise RangeError(
f'string {shortrepr(value)} must be at most {self.maxchars} chars long (got {size})')
f'{shortrepr(value)} must be at most {self.maxchars} chars long!')
if '\0' in value:
raise RangeError(
'strings are not allowed to embed a null byte, Use a Blob instead')
'Strings are not allowed to embed a \\0! Use a Blob instead!')
return value
def export_value(self, value):
@@ -724,12 +724,12 @@ class BoolType(DataType):
return False
if value in ['1', 'True', 'true', 'yes', 'on']:
return True
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value')
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
def __call__(self, value):
if value in (0, 1):
return bool(value)
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value')
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
def export_value(self, value):
"""returns a python object fit for serialisation"""
@@ -755,16 +755,16 @@ class ArrayOf(DataType):
:param members: the datatype of the elements
"""
minlen = Property('minimum number of elements', IntRange(0, UNLIMITED), extname='minlen',
minlen = Property('minimum number of elements', IntRange(0), extname='minlen',
default=0)
maxlen = Property('maximum number of elements', IntRange(0, UNLIMITED), extname='maxlen',
maxlen = Property('maximum number of elements', IntRange(0), extname='maxlen',
mandatory=True)
def __init__(self, members, minlen=0, maxlen=None):
super().__init__()
if not isinstance(members, DataType):
raise ProgrammingError(
'ArrayOf only works with a DataType as first argument')
'ArrayOf only works with a DataType as first argument!')
# one argument -> exactly that size
# argument default to 100
if maxlen is None:
@@ -807,16 +807,15 @@ class ArrayOf(DataType):
def check_type(self, value):
try:
size = len(value)
# check number of elements
if self.minlen is not None and size < self.minlen:
if self.minlen is not None and len(value) < self.minlen:
raise RangeError(
f'array must have at least {self.minlen} elements (has {size})')
if self.maxlen is not None and size > self.maxlen:
f'array too small, needs at least {self.minlen} elements!')
if self.maxlen is not None and len(value) > self.maxlen:
raise RangeError(
f'array must have at most {self.maxlen} elements (has {size})')
f'array too big, holds at most {self.maxlen} elements!')
except TypeError:
raise WrongTypeError(f'{type(value).__name__} can not be converted to ArrayOf DataType') from None
raise WrongTypeError(f'{type(value).__name__} can not be converted to ArrayOf DataType!') from None
def __call__(self, value):
"""accepts any sequence, converts to tuple (immutable!)"""
@@ -882,11 +881,11 @@ class TupleOf(DataType):
def __init__(self, *members):
super().__init__()
if not members:
raise ProgrammingError('Empty tuples are not allowed')
raise ProgrammingError('Empty tuples are not allowed!')
for subtype in members:
if not isinstance(subtype, DataType):
raise ProgrammingError(
'TupleOf only works with DataType objs as arguments')
'TupleOf only works with DataType objs as arguments!')
self.members = members
self.default = tuple(el.default for el in members)
@@ -905,7 +904,7 @@ class TupleOf(DataType):
if len(value) == len(self.members):
return
except TypeError:
raise WrongTypeError(f'{type(value).__name__} can not be converted to TupleOf DataType') from None
raise WrongTypeError(f'{type(value).__name__} can not be converted to TupleOf DataType!') from None
raise WrongTypeError(f'tuple needs {len(self.members)} elements')
def __call__(self, value):
@@ -970,16 +969,16 @@ class StructOf(DataType):
super().__init__()
self.members = members
if not members:
raise ProgrammingError('Empty structs are not allowed')
raise ProgrammingError('Empty structs are not allowed!')
self.optional = list(members if optional is None else optional)
for name, subtype in list(members.items()):
if not isinstance(subtype, DataType):
raise ProgrammingError(
'StructOf only works with named DataType objs as keyworded arguments')
'StructOf only works with named DataType objs as keyworded arguments!')
for name in self.optional:
if name not in members:
raise ProgrammingError(
'Only members of StructOf may be declared as optional')
'Only members of StructOf may be declared as optional!')
self.default = dict((k, el.default) for k, el in members.items())
def copy(self):
@@ -1083,10 +1082,10 @@ class CommandType(DataType):
super().__init__()
if argument is not None:
if not isinstance(argument, DataType):
raise ProgrammingError('CommandType: Argument type must be a DataType')
raise ProgrammingError('CommandType: Argument type must be a DataType!')
if result is not None:
if not isinstance(result, DataType):
raise ProgrammingError('CommandType: Result type must be a DataType')
raise ProgrammingError('CommandType: Result type must be a DataType!')
self.argument = argument
self.result = result
@@ -1108,10 +1107,10 @@ class CommandType(DataType):
raise ProgrammingError('commands can not be converted to a value')
def export_value(self, value):
raise ProgrammingError('values of type command can not be transported')
raise ProgrammingError('values of type command can not be transported!')
def import_value(self, value):
raise ProgrammingError('values of type command can not be transported')
raise ProgrammingError('values of type command can not be transported!')
def from_string(self, text):
raise ProgrammingError('a string can not be converted to a command')
@@ -1132,23 +1131,10 @@ class CommandType(DataType):
# internally used datatypes (i.e. only for programming the SEC-node)
class DefaultType(DataType):
"""datatype used as default for parameters
needs some minimal interface to avoid errors when
the datatype of a parameter is not yet defined
"""
def __call__(self, value):
return value
def setProperty(self, key, value):
"""silently ignored"""
class DataTypeType(SimpleDataType):
class DataTypeType(DataType):
def __call__(self, value):
"""accepts a datatype"""
if isinstance(value, SimpleDataType):
if isinstance(value, DataType):
return value
#TODO: not needed anymore?
try:
@@ -1169,7 +1155,7 @@ class DataTypeType(SimpleDataType):
raise NotImplementedError
class ValueType(SimpleDataType):
class ValueType(DataType):
"""Can take any python value.
The optional (callable) validator can be used to restrict values to a
@@ -1192,7 +1178,7 @@ class ValueType(SimpleDataType):
try:
return self.validator(value)
except Exception as e:
raise ConfigError(f'Validator {self.validator} raised {e!r} for value {shortrepr(value)}') from e
raise ConfigError(f'Validator {self.validator} raised {e!r} for value {value}') from e
return value
def copy(self):
@@ -1202,8 +1188,23 @@ class ValueType(SimpleDataType):
"""if needed, reformat value for transport"""
return value
def import_value(self, value):
"""opposite of export_value, reformat from transport to internal repr
class NoneOr(SimpleDataType):
note: for importing from gui/configfile/commandline use :meth:`from_string`
instead.
"""
raise NotImplementedError
def setProperty(self, key, value):
"""silently ignored
as ValueType is used for the datatype default, this makes code
shorter for cases, where the datatype may not yet be defined
"""
class NoneOr(DataType):
"""validates a None or smth. else"""
default = None
@@ -1221,7 +1222,7 @@ class NoneOr(SimpleDataType):
return self.other.export_value(value)
class OrType(SimpleDataType):
class OrType(DataType):
def __init__(self, *types):
super().__init__()
self.types = types

View File

@@ -33,6 +33,7 @@ class SECoPError(RuntimeError):
clsname2class = {} # needed to convert error reports back to classes
name = 'InternalError'
name2class = {}
report_error = True
raising_methods = None
def __init_subclass__(cls):
@@ -75,7 +76,7 @@ class SECoPError(RuntimeError):
if mlist and stripped:
mlist = mlist[:-1] # do not pop, as this would change self.raising_methods
prefix = '' if self.name2class.get(self.name) == type(self) else type(self).__name__
prefix = (prefix + ''.join(' in ' + m for m in mlist)).strip()
prefix += ''.join(' in ' + m for m in mlist).strip()
if prefix:
return f'{prefix}: {super().__str__()}'
return super().__str__()

View File

@@ -30,7 +30,7 @@ import time
from frappy.datatypes import ArrayOf, BLOBType, BoolType, FloatRange, \
IntRange, StringType, StructOf, TupleOf, ValueType
from frappy.errors import CommunicationFailedError, ConfigError, \
ProgrammingError, SECoPError, SilentCommunicationFailedError as SilentError
ProgrammingError, SilentCommunicationFailedError as SilentError
from frappy.lib import generalConfig
from frappy.lib.asynconn import AsynConn, ConnectionClosed
from frappy.modules import Attached, Command, Communicator, Module, \
@@ -125,7 +125,7 @@ class IOBase(Communicator):
_reconnectCallbacks = None
_conn = None
_last_error = None # this is None only until the first connection success
_last_error = None
_lock = None
_last_connect_attempt = 0
@@ -167,17 +167,14 @@ class IOBase(Communicator):
try:
self.connectStart()
if self._last_error:
# we do not get here before the first connect success
self.log.info('connected')
self._last_error = 'connected'
self.callCallbacks()
self._last_error = 'connected'
return self.is_connected
except Exception as e:
if repr(e) != self._last_error:
self._last_error = repr(e)
self.log.error(self._last_error)
if not isinstance(e, CommunicationFailedError):
# when this happens on startup, assume it is not worth to continue
self.secNode.error_count += 1
raise SilentError(repr(e)) from e
return self.is_connected
@@ -196,7 +193,7 @@ class IOBase(Communicator):
now = time.time()
if now >= self._last_connect_attempt + self.pollinterval:
# we do not try to reconnect more often than pollinterval
self._last_connect_attempt = now
_last_connect_attempt = now
if self.read_is_connected():
return
raise SilentError('disconnected') from None
@@ -221,23 +218,13 @@ class IOBase(Communicator):
def communicate(self, command):
return NotImplementedError
@Command
def reconnect(self):
"""close and open connection
sometimes a reconnect helps to heal a broken connection
"""
self.closeConnection()
self.read_is_connected() # this always tries to reconnect
class StringIO(IOBase):
"""line oriented communicator
self-healing is assured by polling the parameter 'is_connected'
self healing is assured by polling the parameter 'is_connected'
"""
end_of_line = Property('end_of_line character or tuple(eol_read, eol_write)',
datatype=ValueType(),
end_of_line = Property('end_of_line character', datatype=ValueType(),
default='\n', settable=True)
encoding = Property('used encoding', datatype=StringType(),
default='ascii', settable=True)
@@ -310,7 +297,6 @@ class StringIO(IOBase):
"""
command = command.encode(self.encoding)
self.check_connection()
new_error = 'no error' # in case of success (must not be None)
try:
with self._lock:
# read garbage and wait before send
@@ -318,14 +304,12 @@ class StringIO(IOBase):
cmds = command.split(self._eol_write)
else:
cmds = [command]
# do not skip garbage when no reply is expected
skip_garbage = not noreply
garbage = None
try:
for cmd in cmds:
if self.wait_before:
time.sleep(self.wait_before)
if skip_garbage:
skip_garbage = False # read garbage only once
if garbage is None: # read garbage only once
garbage = self._conn.flush_recv()
if garbage:
self.comLog('garbage: %r', garbage)
@@ -341,19 +325,12 @@ class StringIO(IOBase):
self.comLog('< %s', reply)
return reply
except Exception as e:
new_error = 'disconnected' if self._conn is None else repr(e)
if new_error != self._last_error:
# suppress subsequent equal error messages
# this is in addition to the mechanism in Module.callPollFunc
# as the same error would appear potentially in a lot of
# methods
if isinstance(e, SECoPError):
self.log.error(new_error)
else:
self.log.exception(new_error)
raise SilentError(new_error) from e
finally:
self._last_error = new_error
if self._conn is None:
raise SilentError('disconnected') from None
if repr(e) != self._last_error:
self._last_error = repr(e)
self.log.error(self._last_error)
raise SilentError(repr(e)) from e
@Command(StringType())
def writeline(self, command):
@@ -420,34 +397,6 @@ class StringIO(IOBase):
time.sleep(delay)
return replies
@Command(result=StringType())
def readline(self):
"""read a line, if available within self.timeout
remark: the call might return earlier, when an other
thread consumed the data in parallel
"""
self.check_connection()
try:
if self._conn.read_ready(self.timeout):
with self._lock: # important: lock only after waiting
reply = self._conn.readline(0)
if reply:
reply = reply.decode(self.encoding)
self.comLog('< %s', reply)
return reply
return ''
except ConnectionClosed:
self.closeConnection()
raise CommunicationFailedError('disconnected') from None
except Exception as e:
if self._conn is None:
raise SilentError('disconnected') from None
if repr(e) != self._last_error:
self._last_error = repr(e)
self.log.error(self._last_error)
raise SilentError(repr(e)) from e
def make_regexp(string):
"""create a bytes regexp pattern from a string describing a bytes pattern
@@ -511,7 +460,6 @@ class BytesIO(IOBase):
def communicate(self, request, replylen): # pylint: disable=arguments-differ
"""send a request and receive (at least) <replylen> bytes as reply"""
self.check_connection()
new_error = 'no error' # in case of success (must not be None)
try:
with self._lock:
# read garbage and wait before send
@@ -530,19 +478,12 @@ class BytesIO(IOBase):
self.comLog('< %s', hexify(reply))
return self.getFullReply(request, reply)
except Exception as e:
new_error = 'disconnected' if self._conn is None else repr(e)
if new_error != self._last_error:
# suppress subsequent equal error messages
# this is in addition to the mechanism in Module.callPollFunc
# as the same error would appear potentially in a lot of
# methods
if isinstance(e, SECoPError):
self.log.error(new_error)
else:
self.log.exception(new_error)
raise SilentError(new_error) from e
finally:
self._last_error = new_error
if self._conn is None:
raise SilentError('disconnected') from None
if repr(e) != self._last_error:
self._last_error = str(e)
self.log.error(self._last_error)
raise SilentError(repr(e)) from e
@Command(StructOf(requests=ArrayOf(TupleOf(BLOBType(), IntRange(0), FloatRange(0, unit='s')))),
result=ArrayOf(BLOBType()))

View File

@@ -28,10 +28,10 @@ synchronous IO (see frappy.io)
"""
import ast
import select
import socket
import time
import re
from select import select
from frappy.errors import CommunicationFailedError, ConfigError
from frappy.lib import closeSocket, parse_host_port, SECoP_DEFAULT_PORT
@@ -50,7 +50,8 @@ class AsynConn:
timeout = 1 # inter byte timeout
scheme = None
SCHEME_MAP = {}
_connection = None # None means disconnected
connection = None # is not None, if connected
HOSTNAMEPAT = re.compile(r'[a-z0-9_.-]+$', re.IGNORECASE) # roughly checking if it is a valid hostname
def __new__(cls, uri, end_of_line=b'\n', default_settings=None):
scheme = uri.split('://')[0]
@@ -84,12 +85,6 @@ class AsynConn:
if cls.scheme:
cls.SCHEME_MAP[cls.scheme] = cls
@property
def connection(self):
if self._connection is None:
raise ConnectionClosed()
return self._connection
def shutdown(self):
"""prepare connection for disconnect, can be empty"""
@@ -111,34 +106,25 @@ class AsynConn:
"""
raise NotImplementedError
def recv_nowait(self):
"""return bytes in buffer without waiting"""
raise NotImplementedError
def flush_recv(self):
"""flush all available bytes (return them)"""
result = self._rxbuffer + self.recv_nowait()
self._rxbuffer = b''
return result
raise NotImplementedError
def readline(self, timeout=None):
"""read one line
Return either a complete line or None if not enough is available
within 1 sec (self.timeout).
With timeout=0 no waiting happens at all.
If a non-zero timeout is given, a timeout error is raised instead
of returning None.
The timeout resolution is self.timeout (1 sec by default)
return either a complete line or None if no data available within 1 sec (self.timeout)
if a non-zero timeout is given, a timeout error is raised instead of returning None
the timeout effectively used will not be lower than self.timeout (1 sec)
"""
end = time.time() + timeout if timeout else 0
recv = self.recv_nowait if timeout == 0 else self.recv
if timeout:
end = time.time() + timeout
while True:
splitted = self._rxbuffer.split(self.end_of_line, 1)
if len(splitted) == 2:
line, self._rxbuffer = splitted
return line
data = recv()
data = self.recv()
if not data:
if timeout:
if time.time() < end:
@@ -150,17 +136,14 @@ class AsynConn:
def readbytes(self, nbytes, timeout=None):
"""read a fixed number of bytes
Return either <nbytes> bytes or None if not enough data is available
within 1 sec (self.timeout).
With timeout=0 no waiting happens at all.
If a non-zero timeout is given, a timeout error is raised instead
of returning None
The timeout resolution is self.timeout (1 sec by default)
return either <nbytes> bytes or None if not enough data available within 1 sec (self.timeout)
if a non-zero timeout is given, a timeout error is raised instead of returning None
the timeout effectively used will not be lower than self.timeout (1 sec)
"""
end = time.time() + timeout if timeout else 0
recv = self.recv_nowait if timeout == 0 else self.recv
if timeout:
end = time.time() + timeout
while len(self._rxbuffer) < nbytes:
data = recv()
data = self.recv()
if not data:
if timeout:
if time.time() < end:
@@ -175,9 +158,6 @@ class AsynConn:
def writeline(self, line):
self.send(line + self.end_of_line)
def read_ready(self, timeout=1):
return bool(select([self.connection], [], [], timeout)[0])
class AsynTcp(AsynConn):
"""a tcp/ip connection
@@ -194,38 +174,45 @@ class AsynTcp(AsynConn):
if uri.startswith('tcp://'):
uri = uri[6:]
try:
host, port = parse_host_port(
uri, self.default_settings.get('port', SECoP_DEFAULT_PORT))
self._connection = socket.create_connection(
(host, port), timeout=self.timeout)
host, port = parse_host_port(uri, self.default_settings.get('port', SECoP_DEFAULT_PORT))
self.connection = socket.create_connection((host, port), timeout=self.timeout)
except (ConnectionRefusedError, socket.gaierror, socket.timeout) as e:
# indicate that retrying might make sense
raise CommunicationFailedError(f'can not connect to {host}:{port}, {e}') from None
def shutdown(self):
if self._connection:
if self.connection:
try:
self.connection.shutdown(socket.SHUT_RDWR)
except OSError:
pass # in case socket is already disconnected
def disconnect(self):
if self._connection:
if self.connection:
closeSocket(self.connection)
self._connection = None
self.connection = None
def send(self, data):
"""send data (bytes!)"""
# remark: will raise socket.timeout when output buffer is full and blocked for self.timeout
# remark: will raise socket.timeout when output buffer is full and blocked for 1 sec
self.connection.sendall(data)
def flush_recv(self):
"""flush recv buffer"""
data = [self._rxbuffer]
while select.select([self.connection], [], [], 0)[0]:
data.append(self.recv())
self._rxbuffer = b''
return b''.join(data)
def recv(self):
"""return bytes in the recv buffer
or bytes received within self.timeout
or bytes received within 1 sec
"""
try:
data = self.connection.recv(1024*1024)
data = self.connection.recv(8192)
if data:
return data
except (socket.timeout, TimeoutError):
@@ -238,12 +225,6 @@ class AsynTcp(AsynConn):
# help in this case.
raise ConnectionClosed() # marks end of connection
def recv_nowait(self):
"""return bytes in the recv buffer"""
if select([self.connection], [], [], 0)[0]:
return self.recv()
return b''
class AsynSerial(AsynConn):
"""a serial connection using pyserial
@@ -298,41 +279,31 @@ class AsynSerial(AsynConn):
if 'timeout' not in options:
options['timeout'] = self.timeout
try:
self._connection = Serial(dev, **options)
self.connection = Serial(dev, **options)
except ValueError as e:
raise ConfigError(e) from None
# TODO: turn exceptions into ConnectionFailedError, where a retry makes sense
def disconnect(self):
if self._connection:
if self.connection:
self.connection.close()
self._connection = None
self.connection = None
def send(self, data):
"""send data (bytes!)"""
self.connection.write(data)
def flush_recv(self):
result = self._rxbuffer + self.connection.read(self.connection.in_waiting)
self._rxbuffer = b''
return result
def recv(self):
"""return bytes received within self.timeout"""
"""return bytes received within 1 sec"""
if not self.connection: # disconnect() might have been called in between
raise ConnectionClosed()
n = self.connection.in_waiting
if n:
return self.connection.read(n)
data = self.connection.read(1)
return data + self.connection.read(self.connection.in_waiting)
def recv_nowait(self):
n = self.connection.in_waiting
return self.connection.read(n) if n else b''
if not hasattr(Serial, 'fileno'):
# select is not supported for serial objects (Windows)
# create a less efficient workaround
def read_ready(self, timeout=1):
if self.connection.in_waiting:
return True
deadline = time.time() + timeout
while time.time() < deadline:
if self.connection.in_waiting:
return True
time.sleep(0.05)
return False

View File

@@ -1,90 +0,0 @@
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
# Anik Stark <anik.stark@psi.ch>
# https://stackoverflow.com/questions/43836866/safely-evaluate-simple-string-equation
#
# *****************************************************************************
import math
import ast
import operator as op
class MathParser:
_operators2method = {
ast.Add: op.add,
ast.Sub: op.sub,
ast.BitXor: op.xor,
ast.Or: op.or_,
ast.And: op.and_,
ast.Mod: op.mod,
ast.Mult: op.mul,
ast.Div: op.truediv,
ast.Pow: op.pow,
ast.FloorDiv: op.floordiv,
ast.USub: op.neg,
ast.UAdd: lambda a:a}
def __init__(self, math=True, **kwargs):
self._vars = kwargs
if not math:
self._alt_name = self._no_alt_name
def _Name(self, name):
try:
return self._vars[name] # look up in user-provided dict
except KeyError:
return self._alt_name(name) # fall back to math functions
@staticmethod
def _alt_name(name):
if name.startswith("_"): # prevent access to hidden names
raise NameError(f"{name!r}")
try:
return getattr(math, name)
except AttributeError:
raise NameError(f"{name!r}")
@staticmethod
def _no_alt_name(name):
raise NameError(f"{name!r}")
def eval_(self, node):
if isinstance(node, ast.Expression):
return self.eval_(node.body)
if isinstance(node, ast.Constant): # return the number
return node.value
if isinstance(node, ast.Name): # return variable or math function
return self._Name(node.id)
if isinstance(node, ast.BinOp): # evaluate binary operations
method = self._operators2method[type(node.op)]
return method( self.eval_(node.left), self.eval_(node.right))
if isinstance(node, ast.UnaryOp): # handle operators
method = self._operators2method[type(node.op)]
return method( self.eval_(node.operand) )
if isinstance(node, ast.Attribute): # handle attributes (e.g. math.cos)
return getattr(self.eval_(node.value), node.attr)
if isinstance(node, ast.Call): # evaluate the function and its arguments, calls function
return self.eval_(node.func)(
*(self.eval_(a) for a in node.args),
**{k.arg:self.eval_(k.value) for k in node.keywords})
raise TypeError(node)
def calculate(self, expr, **kwargs):
self._vars.update(kwargs)
return self.eval_(ast.parse(expr, mode='eval'))

View File

@@ -32,11 +32,15 @@ from frappy.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
NoneOr
from frappy.errors import BadValueError, CommunicationFailedError, ConfigError, \
ProgrammingError, SECoPError, secop_error, RangeError
from frappy.lib import formatException, mkthread, UniqueObject, generalConfig
from frappy.lib import formatException, mkthread, UniqueObject
from frappy.params import Accessible, Command, Parameter, Limit, PREDEFINED_ACCESSIBLES
from frappy.properties import HasProperties, Property
from frappy.logging import RemoteLogHandler
# TODO: resolve cirular import
# from .interfaces import SECoP_BASE_CLASSES
# WORKAROUND:
SECoP_BASE_CLASSES = ['Readable', 'Writable', 'Drivable', 'Communicator']
PREDEF_ORDER = list(PREDEFINED_ACCESSIBLES)
Done = UniqueObject('Done')
@@ -244,7 +248,7 @@ class PollInfo:
self.interval = pollinterval
self.last_main = 0
self.last_slow = 0
self.pending_errors = {}
self.pending_errors = set()
self.polled_parameters = []
self.fast_flag = False
self.trigger_event = trigger_event
@@ -306,8 +310,8 @@ class Module(HasAccessibles):
default='user', extname='visibility')
implementation = Property('internal name of the implementation class of the module', StringType(),
extname='implementation')
interface_classes = Property('offical interface-classes of the module', ArrayOf(StringType()),
extname='interface_classes', default=[])
interface_classes = Property('offical highest interface-class of the module', ArrayOf(StringType()),
extname='interface_classes')
features = Property('list of features', ArrayOf(StringType()), extname='features')
pollinterval = Property('poll interval for parameters handled by doPoll', FloatRange(0.1, 120), default=5)
slowinterval = Property('poll interval for other parameters', FloatRange(0.1, 120), default=15)
@@ -320,9 +324,6 @@ class Module(HasAccessibles):
pollInfo = None
triggerPoll = None # trigger event for polls. used on io modules and modules without io
__poller = None # the poller thread, if used
SECoP_CLASS = None
SECoP_BASE_CLASSES = [] # predefined SECoP base classes
SECoP_CLASSES = [] # all predefined SECoP interface classes
def __init__(self, name, logger, cfgdict, srv):
# remember the secnode for interacting with other modules and the
@@ -339,8 +340,8 @@ class Module(HasAccessibles):
self.updateLock = threading.RLock() # for announceUpdate
self.polledModules = [] # modules polled by thread started in self.startModules
self.attachedModules = {}
self.errors = []
self._isinitialized = False
self._initfailed = False
self.updateCallback = srv.dispatcher.announce_update
# handle module properties
@@ -366,13 +367,17 @@ class Module(HasAccessibles):
else:
self.setProperty(key, value)
except BadValueError:
self.logError(f'{key}: value {value!r} does not match {self.propertyDict[key].datatype!r}!')
self.errors.append(f'{key}: value {value!r} does not match {self.propertyDict[key].datatype!r}!')
# 3) set automatic properties
mycls, = self.__class__.__bases__ # skip the wrapper class
myclassname = f'{mycls.__module__}.{mycls.__name__}'
self.implementation = myclassname
# list of only the 'highest' secop module class
self.interface_classes = [
b.__name__ for b in mycls.__mro__ if b.__name__ in SECoP_BASE_CLASSES][:1]
# handle Features
self.features = [b.__name__ for b in mycls.__mro__ if Feature in b.__bases__]
@@ -393,7 +398,7 @@ class Module(HasAccessibles):
# 3) complain about names not found as accessible or property names
if cfgdict:
self.logError(
self.errors.append(
f"{', '.join(cfgdict.keys())} does not exist (use one of"
f" {', '.join(list(self.accessibles) + list(self.propertyDict))})")
@@ -409,17 +414,18 @@ class Module(HasAccessibles):
self.applyMainUnit(mainunit)
# 6) check complete configuration of * properties
try:
self.checkProperties()
except ProgrammingError:
raise
except SECoPError as e:
self.logError(str(e))
for aname, aobj in self.accessibles.items():
if not self.errors:
try:
aobj.checkProperties()
except SECoPError as e:
self.logError(f'{aname}: {e}')
self.checkProperties()
except ConfigError as e:
self.errors.append(str(e))
for aname, aobj in self.accessibles.items():
try:
aobj.checkProperties()
except (ConfigError, ProgrammingError) as e:
self.errors.append(f'{aname}: {e}')
if self.errors:
raise ConfigError(self.errors)
# helper cfg-editor
def __iter__(self):
@@ -447,19 +453,15 @@ class Module(HasAccessibles):
self.commands[name] = accessible
if cfg is not None:
try:
# apply datatype first
datatype = cfg.pop('datatype', None)
if datatype is not None:
accessible.setProperty('datatype', datatype)
for propname, propvalue in cfg.items():
if propname in {'value', 'default', 'constant'}:
# these properties have ValueType(), but should be checked for datatype
accessible.datatype(cfg[propname])
accessible.setProperty(propname, propvalue)
except KeyError:
self.logError(f"'{name}' has no property '{propname}'")
except SECoPError as e:
self.logError(type(e)(f'{name}.{propname}: {e}'))
self.errors.append(f"'{name}' has no property '{propname}'")
except BadValueError as e:
self.errors.append(f'{name}.{propname}: {str(e)}')
if isinstance(accessible, Parameter):
self._handle_writes(name, accessible)
@@ -473,17 +475,17 @@ class Module(HasAccessibles):
basepname = pname.rpartition('_')[0]
baseparam = self.parameters.get(basepname)
if not baseparam:
self.logError(f'limit {pname!r} is given, but not {basepname!r}')
self.errors.append(f'limit {pname!r} is given, but not {basepname!r}')
return
if baseparam.datatype is None:
return # an error will be reported on baseparam
pobj.set_datatype(baseparam.datatype)
if not pobj.hasDatatype():
self.logError(f'{pname} needs a datatype')
self.errors.append(f'{pname} needs a datatype')
return
if pobj.value is None:
if pobj.needscfg:
self.logError(f'{pname!r} has no default value and was not given in config!')
self.errors.append(f'{pname!r} has no default value and was not given in config!')
if pobj.default is None:
# we do not want to call the setter for this parameter for now,
# this should happen on the first read
@@ -531,6 +533,7 @@ class Module(HasAccessibles):
pobj.value = value
if err:
if secop_error(err) == pobj.readerror:
err.report_error = False
return # no updates for repeated errors
err = secop_error(err)
value_err = value, err
@@ -607,6 +610,8 @@ class Module(HasAccessibles):
registers it in the server for waiting
<timeout> defaults to 30 seconds
"""
# we do not need self.errors any longer. should we delete it?
# del self.errors
if self.polledModules:
self.__poller = mkthread(self.__pollThread, self.polledModules, start_events.get_trigger())
self.startModuleDone = True
@@ -665,31 +670,29 @@ class Module(HasAccessibles):
self.pollInfo.interval = fast_interval if flag else self.pollinterval
self.pollInfo.trigger()
def callPollFunc(self, rfunc, pollname=None, raise_com_failed=False):
def callPollFunc(self, rfunc, raise_com_failed=False):
"""call read method with proper error handling"""
try:
name = pollname or rfunc.__name__
rfunc()
if self.pollInfo.pending_errors.pop(name, None):
self.log.info('%s: o.k.', name)
if rfunc.__name__ in self.pollInfo.pending_errors:
self.log.info('%s: o.k.', rfunc.__name__)
self.pollInfo.pending_errors.discard(rfunc.__name__)
except Exception as e:
prev = self.pollInfo.pending_errors.get(name)
if isinstance(e, SECoPError):
if pollname:
e.raising_methods.append(pollname)
self.log.debug('%s failed with %r', pollname, e)
if raise_com_failed and isinstance(e, CommunicationFailedError):
raise
efmt = None if e.silent else e.format(False)
if efmt != prev:
self.log.error('%s', efmt)
else:
# not a SECoPError: this is proabably a programming error
efmt = repr(e)
if efmt != prev:
if getattr(e, 'report_error', True):
name = rfunc.__name__
self.pollInfo.pending_errors.add(name) # trigger o.k. message after error is resolved
if isinstance(e, SECoPError):
e.raising_methods.append(name)
if e.silent:
self.log.debug('%s', e.format(False))
else:
self.log.error('%s', e.format(False))
if raise_com_failed and isinstance(e, CommunicationFailedError):
raise
else:
# not a SECoPError: this is proabably a programming error
# we want to log the traceback
self.log.exception('%s', efmt)
self.pollInfo.pending_errors[name] = efmt
self.log.error('%s', formatException())
def __pollThread(self, modules, started_callback):
"""poll thread body
@@ -720,42 +723,35 @@ class Module(HasAccessibles):
rfunc = getattr(mobj, 'read_' + pname)
if rfunc.poll:
pinfo.polled_parameters.append((mobj, rfunc, pobj))
try:
for mobj in modules:
# TODO when needed: here we might add a call to a method :meth:`beforeWriteInit`
mobj.writeInitParams()
mobj.initialReads()
# call all read functions a first time
for m in polled_modules:
for mobj, rfunc, _ in m.pollInfo.polled_parameters:
mobj.callPollFunc(rfunc, raise_com_failed=True)
# TODO when needed: here we might add calls to a method :meth:`afterInitPolls`
except CommunicationFailedError as e:
# when communication failed, probably all parameters and may be more modules are affected.
# as this would take a lot of time (summed up timeouts), we do not continue
# trying and let the server accept connections, further polls might success later
if started_callback:
self.log.error('communication failure on startup: %s', e)
started_callback()
started_callback = None
while True:
try:
for mobj in modules:
# TODO when needed: here we might add a call to a method :meth:`beforeWriteInit`
mobj.writeInitParams()
mobj.initialReads()
# call all read functions a first time
for m in polled_modules:
for mobj, rfunc, _ in m.pollInfo.polled_parameters:
mobj.callPollFunc(rfunc, raise_com_failed=True)
# TODO when needed: here we might add calls to a method :meth:`afterInitPolls`
break
except CommunicationFailedError as e:
# when communication failed, probably all parameters and may be more modules are affected.
# as this would take a lot of time (summed up timeouts), we do not continue
# trying and let the server accept connections, further polls might success later
if started_callback:
self.log.error('communication failure on startup: %s', e)
started_callback()
started_callback = None
self.triggerPoll.wait(0.1) # wait for reconnection or max 10 sec.
break
if started_callback:
started_callback()
if not polled_modules: # no polls needed - exit thread
return
to_poll = ()
report_day = time.localtime().tm_min
while modules: # modules will be cleared on shutdown
now = time.time()
today = time.localtime().tm_min
if today != report_day:
report_day = today
for mobj in modules:
pending = mobj.pollInfo.pending_errors
if pending:
self.log.info('%d pending errors', len(pending))
# this will trigger again logging these errors
# or logging o.k. on success
pending.update((k, 'x') for k in pending)
wait_time = 999
for mobj in modules:
pinfo = mobj.pollInfo
@@ -775,7 +771,7 @@ class Module(HasAccessibles):
pinfo.last_main = (now // pinfo.interval) * pinfo.interval
except ZeroDivisionError:
pinfo.last_main = now
mobj.callPollFunc(mobj.doPoll, f'{mobj.name}.doPoll')
mobj.callPollFunc(mobj.doPoll)
now = time.time()
# find ONE due slow poll and call it
loop = True
@@ -871,15 +867,3 @@ class Module(HasAccessibles):
raise RangeError(f'{pname} below {pname}_min')
if value > max_:
raise RangeError(f'{pname} above {pname}_max')
def logError(self, error):
"""log error or raise, depending on generalConfig settings
:param error: an exception or a str (considered as ConfigError)
to be used during startup
"""
if generalConfig.raise_config_errors:
raise ConfigError(error) if isinstance(error, str) else error
self.log.error(str(error))
self.secNode.error_count += 1

View File

@@ -22,24 +22,20 @@
# *****************************************************************************
"""Define base classes for real Modules implemented in the server"""
from frappy.datatypes import BoolType, FloatRange, StatusType, StringType
from frappy.datatypes import FloatRange, \
StatusType, StringType
from frappy.errors import ConfigError, ProgrammingError
from frappy.lib.enum import Enum
from frappy.logging import HasComlog
from frappy.params import Command, Parameter
from frappy.properties import Property
from frappy.logging import HasComlog
from .modulebase import Module
from .attached import AttachedDict
# import compatibility:
# pylint: disable=unused-import
from .properties import Property
from .attached import Attached
class Readable(Module):
"""basic readable module"""
interface_classes = ['Readable']
# pylint: disable=invalid-name
Status = Enum('Status',
IDLE=StatusType.IDLE,
@@ -59,7 +55,6 @@ class Readable(Module):
class Writable(Readable):
"""basic writable module"""
interface_classes = ['Writable']
target = Parameter('target value of the module',
default=0, readonly=False, datatype=FloatRange(unit='$'))
@@ -79,7 +74,7 @@ class Writable(Readable):
class Drivable(Writable):
"""basic drivable module"""
interface_classes = ['Drivable']
status = Parameter(datatype=StatusType(Readable, 'BUSY')) # extend Readable.status
def isBusy(self, status=None):
@@ -101,80 +96,8 @@ class Drivable(Writable):
"""not implemented - this is a no-op"""
class AcquisitionChannel(Readable):
"""A Readable which is part of a data acquisition."""
interface_classes = ['AcquisitionChannel', 'Readable']
# copy Readable.status and extend it with BUSY
status = Parameter(datatype=StatusType(Readable, 'BUSY'))
goal = Parameter('stops the data acquisition when it is reached',
FloatRange(), default=0, readonly=False, optional=True)
goal_enable = Parameter('enable goal', BoolType(), readonly=False,
default=False, optional=True)
# clear is no longer part of the proposed spec, so it does not appear
# as optional command here. however, a subclass may still implement it
class AcquisitionController(Module):
"""Controls other modules.
Controls the data acquisition from AcquisitionChannels.
"""
interface_classes = ['AcquisitionController']
# channels might be configured to an arbitrary number of channels with arbitrary roles
# - to forbid the use fo arbitrary roles, override base=None
# - to restrict roles and base classes override elements={<key>: <basecls>}
# and/or optional={<key>: <basecls>}
channels = AttachedDict('mapping of role to module name for attached channels',
elements=None, optional=None,
basecls=AcquisitionChannel,
extname='acquisition_channels')
status = Drivable.status
isBusy = Drivable.isBusy
# add pollinterval parameter to enable faster polling of the status
pollinterval = Readable.pollinterval
def doPoll(self):
self.read_status()
@Command()
def go(self):
"""Start the acquisition. No-op if the controller is already Busy."""
raise NotImplementedError()
@Command(optional=True)
def prepare(self):
"""Prepare the hardware so 'go' can trigger immediately."""
@Command(optional=True)
def hold(self):
"""Pause the operation.
The next go will continue without clearing any channels or resetting hardware."""
@Command(optional=True)
def stop(self):
"""Stop the data acquisition or operation."""
class Acquisition(AcquisitionController, AcquisitionChannel): # pylint: disable=abstract-method
"""Combines AcquisitionController and AcquisitionChannel into one Module
for the special case where there is only one channel.
remark: when using multiple inheritance, Acquisition must appear
before any base class inheriting from AcquisitionController
"""
interface_classes = ['Acquisition', 'Readable']
channels = None # remove property
acquisition_key = Property('acquisition role (equivalent to NICOS preset name)',
StringType(), export=True, default='')
doPoll = Readable.doPoll
class Communicator(HasComlog, Module):
"""basic abstract communication module"""
interface_classes = ['Communicator']
@Command(StringType(), result=StringType())
def communicate(self, command):
@@ -184,3 +107,41 @@ class Communicator(HasComlog, Module):
:return: the reply
"""
raise NotImplementedError()
SECoP_BASE_CLASSES = {Readable, Writable, Drivable, Communicator}
class Attached(Property):
"""a special property, defining an attached module
assign a module name to this property in the cfg file,
and the server will create an attribute with this module
When mandatory is set to False, and there is no value or an empty string
given in the config file, the value of the attribute will be None.
"""
def __init__(self, basecls=Module, description='attached module', mandatory=True):
self.basecls = basecls
super().__init__(description, StringType(), mandatory=mandatory)
def __get__(self, obj, owner):
if obj is None:
return self
modobj = obj.attachedModules.get(self.name)
if not modobj:
modulename = super().__get__(obj, owner)
if not modulename:
return None # happens when mandatory=False and modulename is not given
modobj = obj.secNode.get_module(modulename)
if not modobj:
raise ConfigError(f'attached module {self.name}={modulename!r} '
f'does not exist')
if not isinstance(modobj, self.basecls):
raise ConfigError(f'attached module {self.name}={modobj.name!r} '
f'must inherit from {self.basecls.__qualname__!r}')
obj.attachedModules[self.name] = modobj
return modobj
def copy(self):
return Attached(self.basecls, self.description, self.mandatory)

View File

@@ -23,9 +23,10 @@
import inspect
from frappy.datatypes import ArrayOf, BoolType, CommandType, DataType, \
DataTypeType, DefaultType, EnumType, FloatRange, NoneOr, OrType, StringType, \
StructOf, TextType, TupleOf, ValueType
DataTypeType, EnumType, FloatRange, NoneOr, OrType, StringType, StructOf, \
TextType, TupleOf, ValueType
from frappy.errors import BadValueError, ProgrammingError, WrongTypeError
from frappy.lib import generalConfig
from frappy.properties import HasProperties, Property
@@ -143,7 +144,7 @@ class Parameter(Accessible):
extname='description', mandatory=True, export='always')
datatype = Property(
'datatype of the Parameter (SECoP datainfo)', DataTypeType(),
extname='datainfo', mandatory=True, export='always', default=DefaultType())
extname='datainfo', mandatory=True, export='always', default=ValueType())
readonly = Property(
'not changeable via SECoP (default True)', BoolType(),
extname='readonly', default=True, export='always')
@@ -232,13 +233,13 @@ class Parameter(Accessible):
try:
return instance.parameters[self.name].value
except KeyError:
raise ProgrammingError(f'optional parameter {self.name} is not implemented') from None
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
def __set__(self, obj, value):
try:
obj.announceUpdate(self.name, value)
except KeyError:
raise ProgrammingError(f'optional parameter {self.name} is not implemented') from None
raise ProgrammingError(f'optional parameter {self.name} it is not implemented') from None
def __set_name__(self, owner, name):
self.name = name
@@ -341,9 +342,7 @@ class Parameter(Accessible):
try:
self.datatype.setProperty(key, value)
except KeyError:
raise ProgrammingError(
f'cannot set {key} on parameter with datatype'
f' {type(self.datatype).__name__}') from None
raise ProgrammingError(f'cannot set {key} on parameter with datatype {type(self.datatype).__name__}') from None
except BadValueError as e:
raise ProgrammingError(f'property {key}: {str(e)}') from None

View File

@@ -94,7 +94,6 @@ logger = MainLogger()
class Playground(Server):
def __init__(self, **kwds): # pylint: disable=super-init-not-called
self.name = 'playground'
for modname, cfg in kwds.items():
cfg.setdefault('description', modname)
self.log = logger.log

View File

@@ -141,10 +141,6 @@ class HasProperties(HasDescriptors):
for pn, po in list(properties.items()):
value = getattr(cls, pn, po)
if not isinstance(value, Property): # attribute may be a bare value
if value is None:
# this allows to remove a property defined in a base class
cls.propertyDict.pop(pn)
continue
po = po.copy()
try:
# try to apply bare value to Property
@@ -159,7 +155,7 @@ class HasProperties(HasDescriptors):
def checkProperties(self):
"""validates properties and checks for min... <= max..."""
for pn, po in self.propertyDict.items():
if po.mandatory or pn in self.propertyValues:
if po.mandatory:
try:
self.propertyValues[pn] = po.datatype.validate(self.propertyValues[pn])
except (KeyError, BadValueError):

View File

@@ -23,9 +23,6 @@
import os
import json
import socket
import select
from time import monotonic
from collections import namedtuple
from frappy.lib import closeSocket
from frappy.protocol.interface.tcp import format_address
@@ -35,79 +32,6 @@ UDP_PORT = 10767
MAX_MESSAGE_LEN = 508
Answer = namedtuple('Answer',
'address, hostname, port, equipment_id, firmware, description')
def decode(msg, addr):
msg = msg.decode('utf-8')
try:
data = json.loads(msg)
except Exception:
return None
if not isinstance(data, dict):
return None
if data.get('SECoP') != 'node':
return None
try:
eq_id = data['equipment_id']
fw = data['firmware']
desc = data['description']
port = data['port']
except KeyError:
return None
try:
hostname = socket.gethostbyaddr(addr[0])[0]
except Exception:
hostname = addr[0]
return Answer(addr[0], hostname, port, eq_id, fw, desc)
def scan(max_wait=1.0):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# send a general broadcast
try:
s.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
('255.255.255.255', UDP_PORT))
except OSError as e:
print('could not send the broadcast:', e)
# we still keep listening for self-announcements
seen = set()
start = monotonic()
while monotonic() < start + max_wait:
res = select.select([s], [], [], 0.1)
if res[0]:
try:
msg, addr = s.recvfrom(1024)
except socket.error: # pragma: no cover
continue
answer = decode(msg, addr)
if answer is None:
continue
if (answer.address, answer.equipment_id, answer.port) in seen:
continue
seen.add((answer.address, answer.equipment_id, answer.port))
yield answer
def listen():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
if os.name == 'nt':
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
else:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(('0.0.0.0', UDP_PORT))
while True:
try:
msg, addr = s.recvfrom(1024)
except KeyboardInterrupt:
break
answer = decode(msg, addr)
if answer:
yield answer
class UDPListener:
def __init__(self, equipment_id, description, ifaces, logger, *,
startup_broadcast=True):

View File

@@ -20,12 +20,12 @@
# *****************************************************************************
import time
import traceback
from collections import OrderedDict
from frappy.dynamic import Pinata
from frappy.errors import NoSuchModuleError, NoSuchParameterError, SECoPError, \
ConfigError, ProgrammingError
from frappy.lib import get_class, generalConfig
from frappy.errors import ConfigError, NoSuchModuleError, NoSuchParameterError
from frappy.lib import get_class
from frappy.version import get_version
from frappy.modules import Module
@@ -38,7 +38,6 @@ class SecNode:
- get_module(modulename) returns the requested module or None if there is
no suitable configuration on the server
"""
raise_config_errors = False # collect catchable errors instead of raising
def __init__(self, name, logger, options, srv):
self.equipment_id = options.pop('equipment_id', name)
@@ -47,7 +46,11 @@ class SecNode:
self.modules = {}
self.log = logger
self.srv = srv
self.error_count = 0 # count catchable errors during initialization
# set of modules that failed creation
self.failed_modules = set()
# list of errors that occured during initialization
self.errors = []
self.traceback_counter = 0
self.name = name
def add_secnode_property(self, prop, value):
@@ -55,18 +58,6 @@ class SecNode:
in the description."""
self.nodeprops[prop] = value
def logError(self, error):
"""log error or raise, depending on generalConfig settings
:param error: an exception or a str (considered as ConfigError)
to be used during startup
"""
if generalConfig.raise_config_errors:
raise ConfigError(error) if isinstance(error, str) else error
self.log.error(str(error))
self.error_count += 1
def get_secnode_property(self, prop):
"""Get SECNode property.
@@ -85,18 +76,20 @@ class SecNode:
# also call earlyInit on the modules
self.log.debug('initializing module %r', modulename)
modobj.earlyInit()
if not modobj.earlyInitDone:
self.logError(ProgrammingError(
f'module {modulename}: '
'Module.earlyInit was not called, probably missing super call'))
modobj.earlyInitDone = True
modobj.initModule()
if not modobj.initModuleDone:
self.logError(ProgrammingError(
f'module {modulename}: '
'Module.initModule was not called, probably missing super call'))
modobj.initModuleDone = True
try:
modobj.earlyInit()
if not modobj.earlyInitDone:
self.errors.append(f'{modobj.earlyInit.__qualname__} was not '
f'called, probably missing super call')
modobj.initModule()
if not modobj.initModuleDone:
self.errors.append(f'{modobj.initModule.__qualname__} was not '
f'called, probably missing super call')
except Exception as e:
if self.traceback_counter == 0:
self.log.exception(traceback.format_exc())
self.traceback_counter += 1
self.errors.append(f'error initializing {modulename}: {e!r}')
modobj._isinitialized = True
self.log.debug('initialized module %r', modulename)
return modobj
@@ -122,22 +115,51 @@ class SecNode:
raise NoSuchModuleError(f'Module {modulename!r} does not exist on '
f'this SEC-Node!')
opts = dict(opts)
classname = opts.pop('cls')
try:
pymodule = None
try: # pylint: disable=no-else-return
classname = opts.pop('cls')
if isinstance(classname, str):
pymodule = classname.rpartition('.')[0]
if pymodule in self.failed_modules:
# creation has failed already once, do not try again
return None
cls = get_class(classname)
else:
pymodule = classname.__module__
if pymodule in self.failed_modules:
# creation has failed already once, do not try again
return None
cls = classname
if not issubclass(cls, Module):
self.logError(f'{cls.__name__} is not a Module')
self.errors.append(f'{cls.__name__} is not a Module')
return None
except AttributeError as e:
except Exception as e:
if str(e) == 'no such class':
self.logError(f'{classname} not found')
return None
raise
modobj = cls(modulename, self.log.parent.getChild(modulename),
opts, self.srv)
self.errors.append(f'{classname} not found')
else:
self.failed_modules.add(pymodule)
if self.traceback_counter == 0:
self.log.exception(traceback.format_exc())
self.traceback_counter += 1
self.errors.append(f'error importing {classname}')
return None
else:
try:
modobj = cls(modulename, self.log.parent.getChild(modulename),
opts, self.srv)
except ConfigError as e:
self.errors.append(f'error creating module {modulename}:')
for errtxt in e.args[0] if isinstance(e.args[0], list) else [e.args[0]]:
self.errors.append(' ' + errtxt)
modobj = None
except Exception as e:
if self.traceback_counter == 0:
self.log.exception(traceback.format_exc())
self.traceback_counter += 1
self.errors.append(f'error creating {modulename}')
modobj = None
if modobj:
self.add_module(modobj, modulename)
return modobj
def create_modules(self):
@@ -168,19 +190,6 @@ class SecNode:
self.log.info('Pinata %s found %d modules',
modname, len(pinata_modules))
todos.extend(pinata_modules)
# initialize all modules
for modname in self.modules:
modobj = self.get_module(modname)
# check attached modules for existence
# normal properties are retrieved too, but this does not harm
for prop in modobj.propertyDict:
try:
getattr(modobj, prop)
except SECoPError as e:
if self.raise_config_errors:
raise
self.error_count += 1
modobj.logError(e)
def export_accessibles(self, modobj):
self.log.debug('export_accessibles(%r)', modobj.name)
@@ -258,7 +267,7 @@ class SecNode:
now = time.time()
deadline = now + 0.5 # should be long enough for most read functions to finish
for mod in self.modules.values():
mod.joinPollThread(max(0.0, deadline - now))
mod.joinPollThread(max(0, deadline - now))
now = time.time()
for name in self._getSortedModules():
self.modules[name].shutdownModule()

View File

@@ -31,7 +31,7 @@ import time
import mlzlog
from frappy.config import load_config
from frappy.errors import ConfigError, ProgrammingError
from frappy.errors import ConfigError
from frappy.lib import formatException, generalConfig, get_class, mkthread
from frappy.lib.multievent import MultiEvent
from frappy.logging import init_remote_logging
@@ -39,8 +39,6 @@ from frappy.params import PREDEFINED_ACCESSIBLES
from frappy.secnode import SecNode
from frappy.protocol.discovery import UDPListener
generalConfig.set_default('raise_config_errors', False)
try:
from daemon import DaemonContext
try:
@@ -301,35 +299,33 @@ class Server:
for k in list(opts):
self.secnode.add_secnode_property(k, opts.pop(k))
try:
self.secnode.create_modules()
# initialize modules by calling self.secnode.get_module for all of them
# this is done in build_descriptive_data even for unexported modules
self.secnode.build_descriptive_data()
# =========== All modules are initialized ===========
self.secnode.create_modules()
# initialize modules by calling self.secnode.get_module for all of them
# this is done in build_descriptive_data even for unexported modules
self.secnode.build_descriptive_data()
# =========== All modules are initialized ===========
if not self._testonly:
start_events = MultiEvent(default_timeout=30)
for modname, modobj in self.secnode.modules.items():
# startModule must return either a timeout value or None (default 30 sec)
start_events.name = f'module {modname}'
if self.secnode.error_count:
# do not start poll thread. check for startModuleDone only
modobj.polledModules = []
modobj.startModule(start_events)
if not modobj.startModuleDone:
self.secnode.log_error(ProgrammingError(
f'module {modname}: '
'Module.startModule was not called, probably missing super call'))
except Exception as e:
if self.secnode.error_count:
raise type(e)(
f'{e.args[0]} - see also {self.secnode.error_count}'
' errors logged above') from e
raise
if self.secnode.error_count:
self.log.error('%d errors during initialisation', self.secnode.error_count)
# all errors from initialization process
errors = self.secnode.errors
if not self._testonly:
start_events = MultiEvent(default_timeout=30)
for modname, modobj in self.secnode.modules.items():
# startModule must return either a timeout value or None (default 30 sec)
start_events.name = f'module {modname}'
modobj.startModule(start_events)
if not modobj.startModuleDone:
errors.append(f'{modobj.startModule.__qualname__} was not called, probably missing super call')
if errors:
for errtxt in errors:
for line in errtxt.split('\n'):
self.log.error(line)
# print a list of config errors to stderr
sys.stderr.write('\n'.join(errors))
sys.stderr.write('\n')
sys.exit(1)
if self._testonly:
return
self.log.info('waiting for modules being started')
@@ -338,9 +334,6 @@ class Server:
# some timeout happened
for name in start_events.waiting_for():
self.log.warning('timeout when starting %s', name)
if self.secnode.error_count:
self.log.error('%d errors during startup', self.secnode.error_count)
sys.exit(1)
self.log.info('all modules started')
history_path = os.environ.get('FRAPPY_HISTORY')
if history_path:

View File

@@ -1,241 +0,0 @@
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Alexander Zaft <a.zaft@fz-juelich.de>
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
import time
import random
import threading
from frappy.lib import clamp, mkthread
from frappy.core import IntRange, Parameter, ArrayOf, TupleOf, FloatRange, \
IDLE, ERROR, BUSY
from frappy.modules import AcquisitionController, AcquisitionChannel, Acquisition
from frappy.params import Command
class AcquisitionSimulation:
def __init__(self, keys):
self.values = {k: 0 for k in keys}
self.err = None
self._stopflag = threading.Event()
self.run_acquisition = threading.Event()
self.lock = threading.Lock()
self.need_reset = False
self._thread = None
self.start()
def start(self):
if self.need_reset:
self.reset()
if self._thread is None:
self._thread = mkthread(self.threadfun)
def threadfun(self):
self.sim_interval = 1
self.err = None
try:
self.__sim()
except Exception as e:
self.err = str(e)
# the thread stops here, but will be restarted with the go command
self._thread = None
def __sim(self):
timestamp = time.time()
delay = 0
while not self._stopflag.wait(delay):
self.run_acquisition.wait()
t = time.time()
diff = t - timestamp
if diff < self.sim_interval:
delay = clamp(0.1, self.sim_interval, 10)
continue
delay = 0
with self.lock:
self.values = {k: v + max(0., random.normalvariate(4., 1.))
for k, v in self.values.items()}
timestamp = t
def reset(self):
with self.lock:
for key in self.values:
self.values[key] = 0
self.need_reset = False
def shutdown(self):
# unblock thread:
self._stopflag.set()
self.run_acquisition.set()
if self._thread and self._thread.is_alive():
self._thread.join()
class Controller(AcquisitionController):
_status = None # for sticky status values
def init_ac(self):
self.ac = AcquisitionSimulation(m.name for m in self.channels.values())
self.ac.reset()
for key, channel in self.channels.items():
self.log.debug('register %s: %s', key, channel.name)
channel.register_acq(self.ac)
def initModule(self):
super().initModule()
self.init_ac()
def read_status(self):
with self.ac.lock:
if self.ac.err:
status = self.Status.ERROR, self.ac.err
elif self.ac.run_acquisition.is_set():
status = self.Status.BUSY, 'running acquisition'
else:
status = self._status or (self.Status.IDLE, '')
for chan in self.channels.values():
chan.read_status()
return status
def go(self):
self.ac.start() # restart sim thread if it failed
self.ac.run_acquisition.set()
self._status = None
self.read_status()
def hold(self):
self.ac.run_acquisition.clear()
self._status = IDLE, 'paused'
self.read_status()
def stop(self):
self.ac.run_acquisition.clear()
self.ac.need_reset = True
self._status = IDLE, 'stopped'
@Command()
def clear(self):
"""clear all channels"""
self.ac.reset()
def shutdownModule(self):
self.ac.shutdown()
class Channel(AcquisitionChannel):
_status = None # for sticky status values
# activate optional parameters:
goal = Parameter()
goal_enable = Parameter()
def register_acq(self, ac):
self.ac = ac
def read_value(self):
with self.ac.lock:
try:
ret = self.ac.values[self.name]
except KeyError:
return -1
if self.goal_enable and self.goal < ret:
if self.ac.run_acquisition.is_set():
self.ac.run_acquisition.clear()
self.ac.need_reset = True
self._status = IDLE, 'hit goal'
else:
self._status = None
return ret
def read_status(self):
if self.ac.err:
return ERROR, self.ac.err
if self.ac.run_acquisition.is_set():
return BUSY, 'running acquisition'
return self._status or (IDLE, '')
@Command()
def clear(self):
"""clear this channel"""
with self.ac.lock:
try:
self.ac.values[self.name] = 0.
except KeyError:
pass
self.read_value()
class SimpleAcquisition(Acquisition, Controller, Channel):
def init_ac(self):
self.channels = {}
self.ac = AcquisitionSimulation([self.name])
self.ac.reset()
class NoGoalAcquisition(Acquisition):
_value = 0
_deadline = 0
def read_value(self):
return self._value
def read_status(self):
if self.status[0] == BUSY:
overtime = time.time() - self._deadline
if overtime < 0:
return BUSY, ''
self.setFastPoll(False)
self._value = overtime
self.read_value()
return IDLE, ''
def go(self):
self._value = 0
self.status = BUSY, 'started'
self.setFastPoll(True, 0.1)
self._deadline = time.time() + 1
self.read_status()
# TODO
class MatrixChannel(AcquisitionChannel):
roi = Parameter('region of interest',
ArrayOf(TupleOf(IntRange(), IntRange()), 0, 1),
default=[], readonly=False)
def initModule(self):
self.data = [0.] * 128
def read_value(self):
# mean of data or roi
if self.roi:
b, e = self.roi[0]
else:
b, e = 0, len(self.data) - 1
return self.data[b:e] / (e - b)
def write_roi(self, roi):
pass
@Command(result=ArrayOf(FloatRange()))
def get_data(self):
return self.data
# axes
# binning
def clear(self):
raise NotImplementedError()

View File

@@ -248,10 +248,6 @@ class SampleTemp(Drivable):
# note: setting self.target to the new value is done after this....
# note: we may also return the read-back value from the hw here
def stop(self):
"""Stop and keep temperature at current position."""
self.target = self.value
def _thread(self):
loopdelay = 1
while True:

View File

@@ -53,7 +53,6 @@ class WithAtt(Readable):
def read_value(self):
return self.att.read_value()
class LN2(Readable):
"""Just a readable.

128
frappy_psi/ah2700.py Normal file
View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
# *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
"""Andeen Hagerling capacitance bridge
two modules: the capacitance itself and the loss angle
in the configuration file, only the capacitance module needs to be configured,
while the loss module will be created automatically.
the name of the loss module may be configured, or disabled by choosing
an empty name
"""
from frappy.core import FloatRange, HasIO, Parameter, Readable, StringIO, nopoll, \
Attached, Property, StringType
from frappy.dynamic import Pinata
class Ah2700IO(StringIO):
end_of_line = '\r\n'
timeout = 5
class Capacitance(HasIO, Pinata, Readable):
value = Parameter('capacitance', FloatRange(unit='pF'))
freq = Parameter('frequency', FloatRange(unit='Hz'), readonly=False, default=0)
voltage = Parameter('voltage', FloatRange(unit='V'), readonly=False, default=0)
loss_name = Property('''name of loss module (default: <name>_loss)
configure '' to disable the creation of the loss module
''',
StringType(), default='$_loss')
ioClass = Ah2700IO
loss = 0 # not a parameter
def scanModules(self):
if self.loss_name:
# if loss_name is not empty, we tell the framework to create
# a module for the loss with this name, and config below
yield self.loss_name.replace('$', self.name), {
'cls': Loss,
'description': f'loss value of {self.name}',
'cap': self.name}
def parse_reply(self, reply):
if reply.startswith('SI'): # this is an echo
self.communicate('SERIAL ECHO OFF')
reply = self.communicate('SI')
if not reply.startswith('F='): # this is probably an error message like "LOSS TOO HIGH"
self.status = self.Status.ERROR, reply
return self.value
self.status = self.Status.IDLE, ''
# examples of replies:
# 'F= 1000.0 HZ C= 0.000001 PF L> 0.0 DS V= 15.0 V'
# 'F= 1000.0 HZ C= 0.0000059 PF L=-0.4 DS V= 15.0 V OVEN'
# 'LOSS TOO HIGH'
# make sure there is always a space after '=' and '>'
# split() ignores multiple white space
reply = reply.replace('=', '= ').replace('>', '> ').split()
_, freq, _, _, cap, _, _, loss, lossunit, _, volt = reply[:11]
self.freq = freq
self.voltage = volt
if lossunit == 'DS':
self.loss = loss
else: # the unit was wrong, we want DS = tan(delta), not NS = nanoSiemens
reply = self.communicate('UN DS').split() # UN DS returns a reply similar to SI
try:
self.loss = reply[7]
except IndexError:
pass # don't worry, loss will be updated next time
return cap
def read_value(self):
return self.parse_reply(self.communicate('SI')) # SI = single trigger
@nopoll
def read_freq(self):
self.read_value()
return self.freq
@nopoll
def read_voltage(self):
self.read_value()
return self.voltage
def write_freq(self, value):
self.value = self.parse_reply(self.communicate(f'FR {value:g};SI'))
return self.freq
def write_voltage(self, value):
self.value = self.parse_reply(self.communicate(f'V {value:g};SI'))
return self.voltage
class Loss(Readable):
cap = Attached()
value = Parameter('loss', FloatRange(unit='deg'), default=0)
def initModule(self):
super().initModule()
self.cap.registerCallbacks(self, ['status']) # auto update status
def update_value(self, _):
# value is always changed shortly after loss
self.value = self.cap.loss
@nopoll
def read_value(self):
self.cap.read_value()
return self.cap.loss

View File

@@ -1,450 +0,0 @@
# *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
"""Andeen Hagerling capacitance bridge
The speciality of this capacitance bridge is, that a measurement might take
between factions of seconds up to more than half an hour.
creates up to two additional modules for 'loss' and 'freq'
in the configuration file, only the capacitance module needs to be configured,
while the loss module will be created automatically.
the name of the loss (and freq) module may be configured, or disabled by
choosing an empty name
"""
import re
import time
import threading
from frappy.core import HasIO, Parameter, Readable, StringIO, \
Attached, Property, Command, Writable, BUSY, IDLE, WARN
from frappy.datatypes import FloatRange, IntRange, StringType, TupleOf
from frappy.modules import Acquisition
from frappy.dynamic import Pinata
from frappy.errors import IsBusyError, CommunicationFailedError, HardwareError
CONTINUOUS = 0
STARTING = 1
RUNNING = 2
FINISHED = 3
class IO(StringIO):
end_of_line = ('\r\n', '\r')
timeout = 5
sent_command = False # used to detect that communicate was called directly
ECHO = re.compile('>|AV |VO |FR |SI |SH ') # this is recognized as an echo
MEAS = None # overriden by the module
@Command(StringType(), result=StringType())
def communicate(self, command, noreply=False):
"""communicate and remind that a command was sent"""
# this is also called by writeline
self.sent_command = True
for _ in range(3):
reply = super().communicate(command, noreply)
reply = reply and reply.strip()
if self.check_echo_off(reply):
return reply
raise CommunicationFailedError('detected echo but can not switch off')
def check_echo_off(self, reply):
if self.ECHO.match(reply or ''):
super().writeline('\rSERIAL ECHO OFF;UN 2')
for _ in range(3):
reply = self.readline()
if self.MEAS.match(reply or ''):
# this is a meas reply
break
if reply == 'NO DATA FOUND':
break
return False
return True
class AHBase(HasIO, Pinata, Acquisition):
value = Parameter('capacitance', FloatRange(unit='pF'))
freq = Parameter('frequency', FloatRange(unit='Hz'), default=1000)
voltage = Parameter('upper voltage limit',
FloatRange(0, 15, unit='V', fmtstr='%.1f'),
readonly=False, default=0)
loss = Parameter('loss',
FloatRange(unit=''), default=0)
averexp = Parameter('average exponent - roughly log2 of number of samples averaged',
IntRange(0, 15), readonly=False, default=0)
goal = Parameter('value for averexp for the next go()',
IntRange(0, 15), readonly=False, default=0)
meas_time = Parameter('measured measuring time',
FloatRange(unit='s', fmtstr='%.4g'), default=0)
calculated_time = Parameter('calculated measuring time',
FloatRange(unit='s', fmtstr='%.4g'), default=0)
loss_module = Property(
'''name of loss module (default: <name>_loss)
configure '' to disable the creation of the loss module
''', StringType(), default='')
pollinterval = Parameter('minimum pollinterval - the polling rate is determined by averaging',
value=0.1)
export = True # for a Pinata module, the default is False!
ioClass = IO
_error = ''
_last_start = None
_params = None
_mode = CONTINUOUS # or RUNNING or FINISHED
_cont_deadline = 0 # when to switch back to continuous after finished
_averexp_deadline = 0 # to make sure averexp is polled periodically
_lossunit = 'undefined'
# to be overridden:
PATTERN = None # a list of patterns to parse replies
MEAS_PAT = None # the pattern to parse the measurement reply
UNIT = None # our desired loss unit
MODEL_PAT = None
MODEL = None
def scanModules(self):
if self.loss_module:
# if loss_module is not empty, we tell the framework to create
# a module for the loss with this name, and config below
yield self.loss_module.replace('$', self.name), {
'cls': Loss,
'description': f'loss value of {self.name}',
'cap': self.name}
def initModule(self):
super().initModule()
self._lock = threading.RLock()
self.io.MEAS = self.MEAS_PAT
self.io.checkHWIdent = self.checkHWIdent
def checkHWIdent(self):
for _ in range(3):
if self.MODEL_PAT.match(self.communicate('SH MODEL')):
return
raise CommunicationFailedError(f'we are not connected to a {self.MODEL}')
def initialReads(self):
# UN 2 does also return the results of the last measurement
# (including the frequency for AH2700)
self.freq = self.get_param('FR', 'freq')
self.set_lossunit()
self.verify_averexp()
self.goal = self.averexp
self.single_meas()
def communicate(self, command):
reply = self.io.communicate(command)
self.io.sent_command = False
return reply
def set_lossunit(self):
self._lossunit = self.UNIT
reply = self.communicate('UN 2')
# this should be a measurement reply
mdict = self.get_meas_reply(reply)
unit = mdict.get('lossunit', 'undefined')
if unit == self.UNIT:
if self._lossunit != self.UNIT:
self.log.warn('changed loss unit from %r to %r', self._lossunit, self.UNIT)
else:
self.log.warn('bad reply for UN 2: %r', reply)
self._lossunit = unit
def change_param(self, short, value, param):
if self._mode == RUNNING:
raise IsBusyError('can not change parameters while measuring')
with self._lock:
for _ in range(3):
reply = self.communicate(f'{short} {value};SH {short}')
match = self.PATTERN[param].match(reply)
if match:
result = match.group(1)
self.retrigger_meas()
return float(result)
self.retrigger_meas()
raise CommunicationFailedError(f'can not change {param} to {value}')
def get_param(self, short, param):
with self._lock:
for _ in range(3):
reply = self.communicate(f'SH {short}')
match = self.PATTERN[param].match(reply)
if match:
result = match.group(1)
self.retrigger_meas()
return float(result)
self.retrigger_meas()
raise CommunicationFailedError(f'can not get {param}')
def retrigger_meas(self):
if self._mode == CONTINUOUS:
self.single_meas()
def single_meas(self):
self._last_start = time.time()
self.io.writeline('SI')
self.io.sent_command = False
def get_meas_reply(self, reply):
match = self.MEAS_PAT.match(reply)
if match:
return match.groupdict()
return {}
def doPoll(self):
# this typically waits longer than the low pollinterval
# -> after returning, doPoll is called again immediately
reply = self.io.readline()
if reply:
meas = self.get_meas_reply(reply)
if meas:
self.update_meas(**meas)
else:
self.io.check_echo_off(reply)
self.retrigger_meas()
elif self._mode == FINISHED and time.time() > self._cont_deadline:
self._mode = CONTINUOUS
self.status = IDLE, ''
self.single_meas()
elif self.io.sent_command:
# self.io.communicate was called directly
# -> we have to retrigger SI again
if self._mode == CONTINUOUS:
self.single_meas()
elif self._mode == RUNNING:
self.finish(WARN, 'interrupted')
def update_freq(self, value):
self.freq = value
self._calculate_time(self.averexp, value)
def update_averexp(self, value):
self.averexp = value
self._averexp_deadline = time.time() + 15
self._calculate_time(value, self.freq)
def update_meas(self, cap, loss, lossunit, voltage, error, freq=None):
"""update given arguments from a measurement reply (these are strings!)"""
self._error = error
if self._error:
status = WARN, self._error
else:
status = IDLE, '' if self._mode == CONTINUOUS else 'finished'
now = time.time()
if self._mode == RUNNING:
self.finish(*status)
elif status != self.status:
self.status = status
if freq:
self.freq = float(freq)
self._calculate_time(self.averexp, self.freq)
self.value = float(cap)
self.voltage = float(voltage)
if lossunit != self.UNIT:
self.set_lossunit()
self.retrigger_meas()
return
self.loss = float(loss)
if self._last_start:
self.meas_time = now - self._last_start
self._last_start = 0
if now > self._averexp_deadline and self._mode == CONTINUOUS:
self.verify_averexp()
else:
self.retrigger_meas()
def read_loss(self):
if self._lossunit != self.UNIT:
raise HardwareError(f'bad loss unit: {self._lossunit!r}')
return self.loss
def write_voltage(self, value):
return round(self.change_param('VO', f'{value:.1f}', 'voltage'), 1)
def write_averexp(self, value):
self.update_averexp(self.change_param('AV', f'{value}', 'averexp'))
def verify_averexp(self):
# we do not want to use read_averexp for this,
# as it will stop the measurement when polled
self.update_averexp(self.get_param('AV', 'averexp'))
def _calculate_time(self, averexp, freq):
self.calculated_time = self.calculate_time(averexp, freq)
def go(self):
"""start acquisition"""
prevmode = self._mode
self._mode = STARTING
if prevmode != FINISHED or time.time() > self._averexp_deadline:
# this also makes sure we catch a previous meas reply
self.verify_averexp()
if self.averexp != self.goal:
self.write_averexp(self.goal)
self.status = BUSY, 'started'
self.single_meas()
self._mode = RUNNING
def finish(self, statuscode, statustext):
self.status = statuscode, statustext
self._mode = FINISHED
self._cont_deadline = time.time() + 5
def stop(self):
"""stops measurement"""
if self._mode == RUNNING:
self.verify_averexp()
self.finish(WARN, 'stopped')
def calculate_time(self, averexp, freq):
"""estimate measuring time"""
raise NotImplementedError
class Loss(Readable):
cap = Attached()
value = Parameter('loss', FloatRange(unit=''), default=0)
def initModule(self):
super().initModule()
self.cap.addCallback('loss', self.update_loss) # auto update status
def update_freq(self, freq):
self.freq = float(freq)
def update_loss(self, loss):
self.value = float(loss)
class Freq(Writable):
cap = Attached()
value = Parameter('', FloatRange(unit='Hz'), default=0)
def initModule(self):
super().initModule()
self.cap.addCallback('freq', self.update_freq) # auto update status
def update_freq(self, freq):
self.value = freq
def write_target(self, target):
self.cap.write_freq(target)
class AH2550(AHBase):
PATTERN = {
'averexp': re.compile(r'AVERAGE_AVEREXP *([0-9]*)'),
'voltage': re.compile(r'VOLTAGE_HIGHEST *([0-9.E+-]+)'),
'freq': re.compile(r'FREQUENCY *([0-9.E+-]+)'),
}
MEAS_PAT = re.compile(
r'C= *(?P<cap>[0-9.E+-]+) *PF,'
r'L= *(?P<loss>[0-9.E+-]+) *(?P<lossunit>[A-Z]*),'
r'V= *(?P<voltage>[0-9.E+-]+) *V,A,*(?P<error>.*)$'
)
UNIT = 'DF'
MODEL_PAT = re.compile('ILLEGAL WORD: MODEL')
MODEL = 'AH2550'
# empirically determined - may vary with noise
# differs drastically from the table in the manual
MEAS_TIME_CONST = [0.2, 0.3, 0.4, 1.0, 1.3, 1.6, 2.2, 3.3,
5.5, 8.3, 14, 25, 47, 91, 180, 360]
def _calculate_time(self, averexp, freq):
self.calculated_time = self.calculate_time(averexp)
@Command(TupleOf(IntRange(0, 15)), result=FloatRange())
def calculate_time(self, averexp):
"""calculate estimated measuring time"""
return self.MEAS_TIME_CONST[int(averexp)]
class AH2700(AHBase):
freq = Parameter(datatype=FloatRange(50, 20000, unit='Hz', fmtstr='%.1f'),
readonly=False)
freq_module = Property('''name of freq module
default: not created
''',
StringType(), default='')
PATTERN = {
'averexp': re.compile(r'AVERAGE *AVEREXP=([0-9]*)'),
'voltage': re.compile(r'VOLTAGE HIGHEST *([0-9.E+-]+)'),
'freq': re.compile(r'FREQUENCY *([0-9.E+-]+)'),
}
MEAS_PAT = re.compile(
r'F= *(?P<freq>[0-9.E+-]+) *HZ '
r'C= *(?P<cap>[0-9.E+-]+) *PF '
r'L= *(?P<loss>[0-9.E+-]+) *(?P<lossunit>[A-Z]*) '
f'V= *(?P<voltage>[0-9.E+-]+) *V *(?P<error>.*)$'
)
UNIT = 'DS'
MODEL_PAT = re.compile('MODEL/OPTIONS *AH2700')
MODEL = 'AH2700'
def scanModules(self):
yield from super().scanModules()
if self.freq_module:
yield self.freq_module.replace('$', self.name), {
'cls': Freq,
'description': f'freq module of {self.name}',
'cap': self.name}
def write_freq(self, value):
self.change_param('FR', f'{value:g}', 'freq')
self.update_freq(value)
return round(value, 1)
# empirically determined - may vary with noise
# differs drastically from the table in the manual
MEAS_TIME_CONST = [
# (upper freq limit, meas time @ avrexp=7 )
(75, 20.8),
(150, 10.8),
(270, 6.42),
(550, 3.14),
(1100, 3.53),
(4500, 1.82),
(20000, 1.31),
]
@Command(TupleOf(IntRange(0, 15), FloatRange(50, 20000)),
result=FloatRange())
def calculate_time(self, averexp, freq):
"""calculate estimated measuring time
from time efficiency considerations averexp > 7 is recommended
especially for freq < 550 no time is saved with averexp <= 7
"""
for f, c in self.MEAS_TIME_CONST:
if f > freq:
const = c
break
else:
const = self.MEAS_TIME_CONST[-1][1]
if averexp >= 8:
result = 0.8 + (const - 0.8) * (0.5 + 2 ** (averexp - 8))
elif freq < 550:
result = const
else:
result = 0.6 + 2 ** (averexp - 7) * (const - 0.8)
return round(result, 1)

View File

@@ -47,7 +47,6 @@ Mod('out',
from frappy.core import StringIO, Readable, Parameter, FloatRange, Writable, HasIO, BoolType
from frappy.ctrlby import HasControlledBy
# define the IO class
@@ -66,10 +65,10 @@ class Power(HasIO, Readable):
return volt*current
class Output(HasIO, HasControlledBy, Writable):
class Output(HasIO, Writable):
value = Parameter(datatype=FloatRange(0,100,unit='%'), default=0)
target = Parameter(datatype=FloatRange(0,100,unit='%'))
p_value = Parameter('?', datatype=FloatRange(0,100,unit='%'), default=0)
p_value = Parameter(datatype=FloatRange(0,100,unit='%'), default=0)
maxvolt = Parameter('voltage at 100%',datatype=FloatRange(0,60,unit='V'),default=50,readonly=False)
maxcurrent = Parameter('current at 100%',datatype=FloatRange(0,5,unit='A'),default=2,readonly=False)
output_enable = Parameter('control on/off', BoolType(), readonly=False)
@@ -78,7 +77,7 @@ class Output(HasIO, HasControlledBy, Writable):
super().initModule()
self.write_output_enable(False)
def set_target(self, target):
def write_target(self, target):
self.write_output_enable(target != 0)
self.communicate(f'VOLT{round(max(8,(target)**0.5 * self.maxvolt)):03d}')
self.communicate(f'CURR{round((target)**0.5* 10 * self.maxcurrent):03d}')

View File

@@ -84,7 +84,7 @@ class Base(HasIO):
class Resistance(Base, Readable):
value = Parameter('resistance', datatype=FloatRange, unit='Ohm')
value = Parameter('resistance', datatype=FloatRange, unit='ohm')
output_offset = Parameter('resistance deviation', datatype=FloatRange, unit='Ohm', readonly=False)
phase_hold = Parameter('phase hold', EnumType('phase hold', off=0, on=1))
@@ -93,7 +93,7 @@ class Resistance(Base, Readable):
irange = Parameter('resistance range index', EnumType('resistance range index',
{name: idx for idx, name in enumerate(RES_RANGE)}),
readonly=False)
range = Parameter('resistance range value', FloatRange(2e-2, 2e7), unit='Ohm', readonly=False)
range = Parameter('resistance range value', FloatRange(2e-2, 2e7), unit='Om', readonly=False)
TIME_CONST = ['0.3s', '1s', '3s', '10s', '30s', '100s', '300s']
itc = Parameter('time constant index',

Some files were not shown because too many files have changed in this diff Show More