From a548b005f7c8328999e215fafdab1d7ee36b29be Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Thu, 31 Oct 2024 15:56:41 +0100 Subject: [PATCH 1/6] documentation cleanup, use mermaid --- Log.md | 449 ++++++++++++++++++++++++++++++++++++++++ Readme.md | 583 +++++----------------------------------------------- SwissMX.jpg | Bin 0 -> 79749 bytes 3 files changed, 505 insertions(+), 527 deletions(-) create mode 100644 Log.md create mode 100644 SwissMX.jpg diff --git a/Log.md b/Log.md new file mode 100644 index 0000000..9e45599 --- /dev/null +++ b/Log.md @@ -0,0 +1,449 @@ + +16.9.22 remote Deltatau test: +----------------------------- +``` +(s.a. /home/zamofing_t/Documents/prj/SwissFEL/PBTools/pbtools/gather/PBGatherPlot.py + +PPMAC=SAR-CPPM-EXPMX1 +rsync -va ~/Documents/prj/SwissFEL/PBTools/pbtools/gather/gather_server root@$PPMAC:/tmp/ + +ssh root@$PPMAC +LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/gather_server +ssh -L 10001:localhost:22 root@$PPMAC 'uname -a' +ssh -L 10002:localhost:2332 root@$PPMAC 'uname -a' +Deltatau host in config: +localhost:10001:10002 (instead SAR-CPPM-EXPMX1) + +ssh gac-cristall@saresc-cons-03 +pw: ValToira_2021 +``` + +4.10.22 final test: +------------------- +``` +zamofing_t@ganymede:~$ ssh gac-cristall@saresc-cons-03 +cd /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/ +# conda env list +conda activate crmx38 +python swissmx.py + +ssh gac-cristall@saresc-cons-03 ls /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX + +git@git.psi.ch:epics_ioc_modules/ESB_MX.git + +git remote add sf-cristallina gac-cristall@saresc-cons-03:/sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX + +git fetch sf-cristallina + +# do local changes and commit stuff +git push sf-cristallina +``` + +22.6.23 debug segmentation fault +-------------------------------- +``` +THE CHRISTALLINA CONTROL ROOM RUNS NORMALLY ON saresc-cons-05 +[saresc-cons-05 ~]$ +ulimit -a +ulimit -c unlimited +python -X faulthandler -X tracemalloc -X dev swissmx.py 2>&1 | tee /tmp/swissmx000.log +reset;tail -c+0 -F /tmp/swissmx000.log + +python -X faulthandler -X tracemalloc -X importtime -X dev swissmx.py --sim 0xff +python -X faulthandler -X tracemalloc -X dev swissmx.py --sim 0xff + +zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX$ +rsync -vai swissmx.py saresc-cons-03:/sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/swissmx_segFault.py + +read: https://docs.python.org/3/library/faulthandler.html + +coredump erzeugen +cat /proc/sys/kernel/core_pattern +sudo echo "/tmp/core" > /proc/sys/kernel/core_pattern + +ulimit -c unlimited +python -c "import ctypes; ctypes.string_at(0)" +python -X faulthandler -c "import ctypes; ctypes.string_at(0)" + +Rene did: +[root@saresc-cons-05 ~]# sysctl -w kernel.core_pattern="/tmp/%e_core_dump.%p" +cat /proc/sys/kernel/core_pattern +/tmp/%e_core_dump.%p + +ll /tmp/python_core_dump.* + +gdb python /tmp/core +bt (for facktrace) + +trying python c code extention: +/home/zamofing_t/Documents/prj/scratch/python/sample_c_extension + +Thread 0x00007fd2e16e8700 (most recent call first): + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata + ... + File "/gfa/.mounts/sf_cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/epics_widgets/MotorTweak.py", line 216 in update_label + ... + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped +``` + +29.6.23 Coredump 18h40 +---------------------- +``` +grep -c '5000/5000' * +swissmx000.log:57 +swissmx001.log:36 +swissmx002.log:35 + +swissmx002.log 35*5000 frames +35*5000/100/60 -> every 29.16min of acquisition a crash + +rsync -vai gac-cristall@saresc-cons-05:/tmp/swissmx* ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/log + +ll /tmp/swissmx* +ll /tmp/python_core_dump.* + +Thread 0x00007fb9b13f7700 (most recent call first): + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 1122 in element_count + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 489 in get + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 981 in nelm + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 579 in wrapper + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 1122 in element_count + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 549 in wrapper + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 871 in current_context + File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped + +conda activate crmx38 +ll /tmp/python_core_dump.* +gdb python /tmp/python_core_dump.21072 +Core was generated by `python -X faulthandler -X tracemalloc -X dev swissmx.py'. +Program terminated with signal 11, Segmentation fault. + +bt +0 write_thread_id.isra.3 (is_current=0, fd=) at /opt/conda/conda-bld/python-split_1648465063888/work/Python/traceback.c:849 +#1 _Py_DumpTracebackThreads () at /opt/conda/conda-bld/python-split_1648465063888/work/Python/traceback.c:914 +#2 0x000055c2b7ef6635 in faulthandler_dump_traceback.isra.2 (fd=fd@entry=2, all_threads=1) at /opt/conda/conda-bld/python-split_1648465063888/work/Modules/faulthandler.c:242 +#3 0x000055c2b7ef67a3 in faulthandler_fatal_error (signum=11) at /opt/conda/conda-bld/python-split_1648465063888/work/Modules/faulthandler.c:348 +#4 +#5 0x00007fb9c7ed4ef4 in QWidgetTextControl::document() const () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#6 0x00007fb9c7e676e1 in ?? () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#7 0x00007fb9c7e69230 in QLabel::paintEvent(QPaintEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#8 0x00007fb9d7fa16d3 in sipQLabel::paintEvent(QPaintEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so +#9 0x00007fb9c7dcc580 in QWidget::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#10 0x00007fb9c7e36203 in QFrame::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#11 0x00007fb9d7fa2d03 in sipQLabel::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so +#12 0x00007fb9c7da20f1 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#13 0x00007fb9d8097afe in sipQApplication::notify(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so +#14 0x00007fb9e6cadd62 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Core.so.5 +#15 0x00007fb9c7dc6de6 in QWidgetPrivate::sendPaintEvent(QRegion const&) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#16 0x00007fb9c7dc774e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, QFlags, QPainter*, QWidgetRepaintManager*) () + from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 +#17 0x00007fb9c7dc8147 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList const&, int, QRegion const&, QPoint const&, QFlags, QPainter*, QWidgetRe +``` + +Localize monitors and callbacks: +``` +grep -Rn 'add_callback' *.py + +swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img) +swissmx.py:745: def cb_update_img(self): +epics_widgets/MotorTweak.py:88: m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'}) +epics_widgets/SmaractMotorTweak.py:100: self._pv_readback.add_callback(self.update_label) +swissmx.py:717: cam.run(self.cb_new_frame_pv) +swissmx.py:743: self.sigNewCamImg.emit() +swissmx.py:745: def cb_update_img(self): +swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img) +camera.py:159: self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=cb) + +-> try to turn of the monitors during ascuisition: +zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX$ +grep -r set_callback * + +https://pyepics.github.io/pyepics/pv.html#automatic-monitoring-of-a-pv + +Try with: +pv.clear_auto_monitor() -> pv.reconnect() + +pv.remove_callback(index=None) -> pv.add_callback(callback=None[, index=None [, with_ctrlvars=True[, **kw]]) +pv.set_callback + +FixTargetFrame -> paint -> _log.debug() + +Turn off jungfrau. +no motion -> wait 1h +constant up-dow motion code -> wait 1h +``` + +23.9.24 spitting/moving repositories +------------------------------------ +``` +https://jira.psi.ch/browse/SFELPHOTON-1337: SwissMX split/ cleanup/move repositories +``` + +---------------------------------------------------------------------- +SCRATCH +======= + +Deploy stuff (22.8.22) quick and dirty (21.9.22 rewworked) +---------------------------------------------------------- +``` +DST=/sf/cristallina/applications/mx/zamofing_t/ + +ssh saresc-cons-03 mkdir $DST +# add '--delete' if needed +rsync -vai ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX saresc-cons-03:$DST -n +rsync -vai ~/Documents/prj/SwissFEL/PBTools saresc-cons-03:$DST -n +ssh saresc-cons-03 saresc-cons-03 chmod -R go+w $DST/ESB_MX/python/SwissMX + +cd /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/ +/opt/gfa/python-3.8/latest/bin/pip install qtawesome --user +/opt/gfa/python-3.8/latest/bin/pip install opencv-python +/opt/gfa/python-3.8/latest/bin/python swissmx.py + +rsync -vai gac-cristall@saresc-cons-03:~/.config/PSI/SwissMX.conf /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/ + +cd $DST +/opt/gfa/python-3.8/latest/bin/python -m pdb swissmx.py +rsync -vai saresc-cons-03:/tmp/image*.png ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/scratch/ +/home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/scratch/autofocus2 +``` + + + +pyqtgraph examples +------------------ +``` +ipython3 +import pyqtgraph.examples +pyqtgraph.examples.run() + +``` + +EPICS simulator +--------------- +``` +This provides very dummy images and motor(to be done) records + +cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/EpicsSim/iocBoot/iocSwissMxSim +./st.cmd +``` + +simulate camera (with EPICS simulator) +-------------------------------------- +``` +cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX +EPICS_CA_ADDR_LIST=localhost +./simCam.py +``` + +test camera display +------------------- +``` +cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX +EPICS_CA_ADDR_LIST=localhost +./camera.py -u -b SwissMxSim +``` + +test at ESC +----------- +``` +https://git.psi.ch/SwissMX/swissmx_cristallina/-/wikis/Instructions%20on%20how%20to%20use%20software + +P=ESB_MX/python/SwissMX/ +ssh saresc-cons-02 mkdir -p /tmp/zamofing_t/$P +rsync -vai ~/Documents/prj/SwissFEL/epics_ioc_modules/$P saresc-cons-02:/tmp/zamofing_t/$P +cd /tmp/zamofing_t/ESB_MX/python/SwissMX +/opt/gfa/python-3.7/2018.12/bin/python camera.py -u 1 -p SARES30-CAMS156-SMX-OAV + +/opt/gfa/python-3.7/2018.12/bin/python swissmx.py + + +caqtdm -macro 'NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV' /sf/controls/config/qt/Camera/CameraExpert_RF.ui + +``` + +try revive Zac code +------------------- +``` +cd /tmp/; git clone https://github.com/malcolmreynolds/transformations.git +cd /tmp/transformations.git +-> modify __init__.py -> .transformation (add dot) +setup.py install --user + + +cd /tmp/; git clone https://github.com/spyder-ide/qtawesome.git +cd /tmp/qtawesome + +#To have epics channels we must be connected to the ESC network +EPICS_CA_ADDR_LIST='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' +cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/oldRepos/app/src +python swissmx.py +``` + +pyqtgraph examples +------------------ +``` +import pyqtgraph.examples +pyqtgraph.examples.run() +``` + + + +-0.952 +pp_comm.py_: + @property + def fast_gather(self): + tries to connect to port and to query_types(), if fails starts the fast gather process. + +triggerSync.c: +root@:/opt/ppmac# LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/triggerSync + +usage: +/tmp/triggerSync pt2ptTime timeOfs mode + +pt2ptTime: time in ms (float value) from point to point == frequency if FEL +timeOfs: time offset in ms (float value) for motion relative to the FEL timing + +The program changes the speed of motion +pshm->Coord[1].DesTimeBase= (default serverPeriod= 0.2?) + +SIMFLAG0 (pshm->Coord[1].Q[10]) -> start trigger +SIMFLAG1 (pshm->Coord[1].Q[11]) -> FEL pulse trigger +//Power PMAC Software Reference Manual.pdf Gate3[i].Chan[j].Status -> page 919 UserFlag +#define FLAG0 (gate3_1->Chan[0].Status&0x800) -> start trigger +#define FLAG1 (gate3_1->Chan[1].Status&0x800) -> FEL pulse trigger + + +mode: + bit0:1: sync mode + bit1:2: simulate start trigger + bit2:4: simulate frame trigger + bit3:8: verbose + +simulate start trigger: +set pshm->Coord[1].Q[10]=1 to simulate a Jungfrau aquire start + +simulate frame trigger +is output to pshm->Coord[1].Q[11] + 1: synchronize real frame and start triggers + 3: synchronize real frame and simulated start triggers + 6: simulated frame and start triggers (no sync) + 7: synchronize simulated frame and start triggers + +in simulate mode: +set pshm->Coord[1].Q[10]=1 to simulate a Jungfrau aquire start +set pshm->Coord[1].Q[10]=2 to stop simulate trigger generation +Coord[1].Q[11] is the simulated frame trigger + +in synchronize mode +Coord[1].Q[0]=-2 : trigsync_func start, Wait for 'arm' trigger +Coord[1].Q[0]=-1 : got 'arm' trigger, wait frame trigger +Coord[1].Q[0]= 0 : got frame trigger 0 +Coord[1].Q[0] is incremented at each trigger +sync task ends when Gather.Enable==0 + + + + +// /tmp/triggerSync 40 11 trigger all 40 ms, simulated start, use real frame triggers, verbose +// /tmp/triggerSync 40 14 trigger all 40 ms, simulated start and frame triggers, no sync, verbose +// /tmp/triggerSync 40 15 trigger all 40 ms, simulated start and frame triggers, with sync, verbose +// /tmp/triggerSync 40 7 trigger all 40 ms, simulated start and frame triggers, with sync, minimal verbose + + +78x78 points =6084pts a 10ms =60 sec. -> 70 sec. +-> we are at 100 Hz !!! + +remote ssh tunnel + start gather_server +--------------------------------------- +``` +PPMAC=SAR-CPPM-EXPMX1 +rsync -va ~/Documents/prj/SwissFEL/PBTools/pbtools/gather/gather_server root@$PPMAC:/tmp/ +lsof -i -n | grep '127.0.0.1:1000' +ssh -L 10001:localhost:22 root@$PPMAC 'uname -a' +ssh -L 10002:localhost:2332 root@$PPMAC 'uname -a' +ssh root@$PPMAC +LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/gather_server +``` +cleanup /tmp/ +------------- +``` +PPMAC=SAR-CPPM-EXPMX1 +ssh root@$PPMAC rm /tmp/gather_server /tmp/triggerSync +ssh root@$PPMAC ls -l /tmp +``` +start debug tools +----------------- +``` +PPMAC=SAR-CPPM-EXPMX1 +PBInspect --host=$PPMAC& +gpasciiCommander --host $PPMAC -i +ssh root@$PPMAC +ssh root@$PPMAC rm /tmp/gather_server /tmp/triggerSync +ssh root@$PPMAC ls -l /tmp +``` +restart IOC +----------- +``` +ssh saresc-cons-03 +PPMAC=SAR-CPPM-EXPMX1 +telnet $PPMAC 50001 +Ctrl-X +dbgf SAR-CPPM-EXPMX1:MOD_VER +caget SAR-CPPM-EXPMX1:MOD_VER +``` +checking versions +----------------- +``` +git: 7a968aac967 +asyn 427.0.2 +motorBase alpha_220518 +asynMotor alpha_220518 +powerPmac alpha_220518 +PB_COMMON 2.0.1 +gpasciiCommander 0.9.0 +ESB_MX 0.0.2 +``` +zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX$ +git loggraph -10 +* 7a968aa 2022-09-20 (HEAD -> master, tag: latest, tag: 0.0.2, psigithub/master) change speeds and directions [Thierry Zamofing] +* 00588f8 2022-09-16 minor changes [Thierry Zamofing] +* d52a6ce 2022-08-30 minor changes [Thierry Zamofing] +* f47e111 2022-05-20 (tag: 0.0.1) add DET_Z motor [Thierry Zamofing] +* 8e5b15e 2022-05-20 wip [Thierry Zamofing] +* 399282c 2019-03-20 enhance triggering [Thierry Zamofing] +* eda8caf 2019-03-19 wip [Thierry Zamofing] +* 0c45705 2019-03-08 optimize [Thierry Zamofing] +* c962ebd 2019-03-06 documentation [Thierry Zamofing] +git reset --hard 399282c +rmake -e LIBVERSION=42.42.42 uninstall install +-> restart IOC +ssh root@$PPMAC rm /tmp/triggerSync +cd python +git dt latest -- shapepath.py +./shapepath.py --host=localhost:10001:10002 +removing test verion +ssh sf-lc7 ls -l /ioc/modules/ESB_MX/ +ssh sf-lc7 rm -rf /ioc/modules/ESB_MX/42.42.42 +IOC locations +------------- +``` +~/Documents/prj/SwissFEL/epics_ioc_boot_sf/ESC_all/ESB_MX_PowerBrick +They are just using the new alphy driver. That should have no impact on the motion config. +``` +test shapepath +-------------- +``` +cd ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX +./shapepath.py --host=localhost:10001:10002 +``` + diff --git a/Readme.md b/Readme.md index a480c74..3fde5b0 100644 --- a/Readme.md +++ b/Readme.md @@ -1,39 +1,57 @@ -Repository structure and dependency (update:23.09.24) ------------------------------------------------------ +SwissMX +======= + +SwissMX is a python application to operate the SwissMX system at cristallina:
+![alt text](SwissMX.jpg "Title") + + + + +Repositories and dependencies (update:23.09.24) +----------------------------------------------- + +|repo |`https://git.psi.ch/`|`~/Documents/prj/SwissFEL/`|description| +|:- |:- |:- |:- | +|SwissMX |[grp-sf_cristallina/SwissMX.git](https://git.psi.ch/grp-sf_cristallina/SwissMX) |apps/SwissMX |Main python user interface fro SwissMX| +|PBSwissMX|[grp-sf_cristallina/PBSwissMX.git](https://git.psi.ch/grp-sf_cristallina/PBSwissMX) |apps/PBSwissMX |PowerBrick documents and tool to generate trajectories and motion programs for SwissMX| +|PBTools |[epics_support_apps/PBTools.git](https://git.psi.ch/epics_support_apps/PBTools) |PBTools |packages needed by PBSwissMX for low level communication to PowerBrick| +|ppmac |[epics_support_apps/ppmac.git](https://git.psi.ch/epics_support_apps/ppmac) |PBTools/ppmac |packages needed by PBTools for lowest level communication to PowerBrick| +|SW_MX |[epics_ioc_modules/SW_MX.git](https://git.psi.ch/epics_ioc_modules/SW_MX) |epics_ioc_modules/SW_MX |IOC modules and `generate.py` for all SwissMX motors| + +```mermaid +flowchart BT + +PBSwissMX --> SwissMX +PBTools --> PBSwissMX +ppmac --> PBTools +SW_MX-->PBSwissMX ``` -https://jira.psi.ch/browse/SFELPHOTON-1337 + -psigithub git@git.psi.ch:grp-sf_cristallina/SwissMX.git --> ~/Documents/prj/SwissFEL/apps/SwissMX - Main python user interface fro SwissMX +#### installation location +|repo | installed at +|:- |:- +|SwissMX | /sf/cristallina/applications/SwissMX/ +|PBSwissMX| /sf/cristallina/applications/SwissMX/PBSwissMX/ +|PBTools | /sf/cristallina/applications/SwissMX/PBTools/ +|ppmac | /sf/cristallina/applications/SwissMX/PBTools/ppmac/ +|SW_MX | /ioc/modules/SW_MX/ and ioc directories -psigithub git@git.psi.ch:grp-sf_cristallina/PBSwissMX.git --> ~/Documents/prj/SwissFEL/apps/PBSwissMX - PowerBrick documents and tool to generate trajectories and motion programs for SwissMX +#### related documentation +- additional module `slic` is needed, which is provided by Sven and covers the JungFrau acquisition framefork +- For **motion/frame synchronization** read: [grp-sf_cristallina/PBSwissMX.git:Readme.md](https://git.psi.ch/grp-sf_cristallina/PBSwissMX/-/blob/master/Readme.md) +- For old but still usefull information read the [Log.md](Log.md) file +- [SFELPHOTON-1337](https://jira.psi.ch/browse/SFELPHOTON-1337): ticket about restructuing repositories -psigithub git@git.psi.ch:epics_support_apps/PBTools.git --> ~/Documents/prj/SwissFEL/PBTools - packages needed by PBSwissMX for low level communication to PowerBrick +--- -psigithub git@git.psi.ch:epics_support_apps/ppmac.git --> ~/Documents/prj/SwissFEL/PBTools/ppmac - packages needed by PBTools for lowest level communication to PowerBrick - -additional module 'slic' is needed, which is provided by Sven and covers the JungFrau acquisition framefork - -For motion/frame synchronization read: - ~/Documents/prj/SwissFEL/apps/PBSwissMX/Readme.md - -``` - -deployment and tests (update: 18.01.24) +deployment and tests (update: 31.10.24) --------------------------------------- - -#initial full deployment: -git commit-amend && git push psigithub -f && make uninstall init update - -Document to start SwissMX in cristallina environment: -https://docs.google.com/document/d/1yEmV_DbRBKQKVCoovjXriNgSjNEBaz50WA0l3yA5jtg/edit#heading=h.z9io692b8tow +#### push local stuff to git ``` -*************************** -* push local stuff to git * -*************************** zamofing_t@ganymede: cd ~/Documents/prj/SwissFEL/apps/SwissMX &&\ git commit-amend &&\ @@ -42,10 +60,9 @@ git push psigithub -f cd ~/Documents/prj/SwissFEL/apps/PBSwissMX &&\ git commit-amend &&\ git push psigithub -f - -************************************* -* switch to latest test environment * -************************************* +``` +#### switch to latest test environment +``` ssh saresc-cons-03 cd /sf/cristallina/applications/SwissMX &&\ @@ -65,511 +82,23 @@ git stash pop #git pull psigithub --ff-only master chmod -R g+w /sf/cristallina/applications/SwissMX - -******************* -* run application * -******************* - +``` +#### run application +``` *** with official python *** additionally needed packages: RH7: [saresc-vcons-01 ~]$ /opt/gfa/python-3.8/latest/bin/pip install --user qtawesome RH8: [saresc-cons-03 ~]$ pip install --user qtawesome - swissmx -#with conda environment: +*** with conda environment *** ssh gac-cristall@saresc-cons-03 (pw:ValToira_2021) cd /sf/cristallina/applications/SwissMX/ # conda env list conda activate crmx38 python swissmx.py --sim 0xc0 ``` - -pyqtgraph examples ------------------- -``` -ipython3 -import pyqtgraph.examples -pyqtgraph.examples.run() - -``` - -EPICS simulator ---------------- -``` -This provides very dummy images and motor(to be done) records - -cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/EpicsSim/iocBoot/iocSwissMxSim -./st.cmd -``` - -simulate camera (with EPICS simulator) --------------------------------------- -``` -cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX -EPICS_CA_ADDR_LIST=localhost -./simCam.py -``` - -test camera display -------------------- -``` -cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX -EPICS_CA_ADDR_LIST=localhost -./camera.py -u -b SwissMxSim -``` - -test at ESC ------------ -``` -https://git.psi.ch/SwissMX/swissmx_cristallina/-/wikis/Instructions%20on%20how%20to%20use%20software - -P=ESB_MX/python/SwissMX/ -ssh saresc-cons-02 mkdir -p /tmp/zamofing_t/$P -rsync -vai ~/Documents/prj/SwissFEL/epics_ioc_modules/$P saresc-cons-02:/tmp/zamofing_t/$P -cd /tmp/zamofing_t/ESB_MX/python/SwissMX -/opt/gfa/python-3.7/2018.12/bin/python camera.py -u 1 -p SARES30-CAMS156-SMX-OAV - -/opt/gfa/python-3.7/2018.12/bin/python swissmx.py - - -caqtdm -macro 'NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV' /sf/controls/config/qt/Camera/CameraExpert_RF.ui - -``` - -try revive Zac code -------------------- -``` -cd /tmp/; git clone https://github.com/malcolmreynolds/transformations.git -cd /tmp/transformations.git --> modify __init__.py -> .transformation (add dot) -setup.py install --user - - -cd /tmp/; git clone https://github.com/spyder-ide/qtawesome.git -cd /tmp/qtawesome - -#To have epics channels we must be connected to the ESC network -EPICS_CA_ADDR_LIST='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' -cd /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/oldRepos/app/src -python swissmx.py -``` - -pyqtgraph examples ------------------- -``` -import pyqtgraph.examples -pyqtgraph.examples.run() -``` - - -Deploy stuff (22.8.22) quick and dirty (21.9.22 rewworked) ----------------------------------------------------------- -``` -DST=/sf/cristallina/applications/mx/zamofing_t/ - -ssh saresc-cons-03 mkdir $DST -# add '--delete' if needed -rsync -vai ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX saresc-cons-03:$DST -n -rsync -vai ~/Documents/prj/SwissFEL/PBTools saresc-cons-03:$DST -n -ssh saresc-cons-03 saresc-cons-03 chmod -R go+w $DST/ESB_MX/python/SwissMX - -cd /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/ -/opt/gfa/python-3.8/latest/bin/pip install qtawesome --user -/opt/gfa/python-3.8/latest/bin/pip install opencv-python -/opt/gfa/python-3.8/latest/bin/python swissmx.py - -rsync -vai gac-cristall@saresc-cons-03:~/.config/PSI/SwissMX.conf /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/ - -cd $DST -/opt/gfa/python-3.8/latest/bin/python -m pdb swissmx.py -rsync -vai saresc-cons-03:/tmp/image*.png ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/scratch/ -/home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/scratch/autofocus2 -``` - -16.9.22 remote Deltatau test: ------------------------------ -``` -(s.a. /home/zamofing_t/Documents/prj/SwissFEL/PBTools/pbtools/gather/PBGatherPlot.py - -PPMAC=SAR-CPPM-EXPMX1 -rsync -va ~/Documents/prj/SwissFEL/PBTools/pbtools/gather/gather_server root@$PPMAC:/tmp/ - -ssh root@$PPMAC -LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/gather_server -ssh -L 10001:localhost:22 root@$PPMAC 'uname -a' -ssh -L 10002:localhost:2332 root@$PPMAC 'uname -a' -Deltatau host in config: -localhost:10001:10002 (instead SAR-CPPM-EXPMX1) - -ssh gac-cristall@saresc-cons-03 -pw: ValToira_2021 -``` - -4.10.22 final test: -------------------- -``` -zamofing_t@ganymede:~$ ssh gac-cristall@saresc-cons-03 -cd /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/ -# conda env list -conda activate crmx38 -python swissmx.py - - -ssh gac-cristall@saresc-cons-03 ls /sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX - - -git@git.psi.ch:epics_ioc_modules/ESB_MX.git - - -git remote add sf-cristallina gac-cristall@saresc-cons-03:/sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX - -git fetch sf-cristallina - -# do local changes and commit stuff -git push sf-cristallina - -``` - -22.6.23 debug segmentation fault --------------------------------- -``` -THE CHRISTALLINA CONTROL ROOM RUNS NORMALLY ON saresc-cons-05 - -[saresc-cons-05 ~]$ -ulimit -a -ulimit -c unlimited -python -X faulthandler -X tracemalloc -X dev swissmx.py 2>&1 | tee /tmp/swissmx000.log -reset;tail -c+0 -F /tmp/swissmx000.log - -python -X faulthandler -X tracemalloc -X importtime -X dev swissmx.py --sim 0xff -python -X faulthandler -X tracemalloc -X dev swissmx.py --sim 0xff - -zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX$ -rsync -vai swissmx.py saresc-cons-03:/sf/cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/swissmx_segFault.py - -read: https://docs.python.org/3/library/faulthandler.html - -coredump erzeugen -cat /proc/sys/kernel/core_pattern -sudo echo "/tmp/core" > /proc/sys/kernel/core_pattern - -ulimit -c unlimited -python -c "import ctypes; ctypes.string_at(0)" -python -X faulthandler -c "import ctypes; ctypes.string_at(0)" - -Rene did: -[root@saresc-cons-05 ~]# sysctl -w kernel.core_pattern="/tmp/%e_core_dump.%p" -cat /proc/sys/kernel/core_pattern -/tmp/%e_core_dump.%p - -ll /tmp/python_core_dump.* - - -gdb python /tmp/core -bt (for facktrace) - -trying python c code extention: -/home/zamofing_t/Documents/prj/scratch/python/sample_c_extension - - -Thread 0x00007fd2e16e8700 (most recent call first): - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata - ... - File "/gfa/.mounts/sf_cristallina/applications/mx/zamofing_t/ESB_MX/python/SwissMX/epics_widgets/MotorTweak.py", line 216 in update_label - ... - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped -``` - -Coredump 29.6.23 18h40 ----------------------- -``` -grep -c '5000/5000' * -swissmx000.log:57 -swissmx001.log:36 -swissmx002.log:35 - -swissmx002.log 35*5000 frames -35*5000/100/60 -> every 29.16min of acquisition a crash - - -rsync -vai gac-cristall@saresc-cons-05:/tmp/swissmx* ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/log - -ll /tmp/swissmx* -ll /tmp/python_core_dump.* - -Thread 0x00007fb9b13f7700 (most recent call first): - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 1122 in element_count - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 489 in get - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 620 in get_with_metadata - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 981 in nelm - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 579 in wrapper - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 1122 in element_count - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 549 in wrapper - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/ca.py", line 871 in current_context - File "/sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/pyepics-3.4.3-py3.9.egg/epics/pv.py", line 48 in wrapped - -conda activate crmx38 -ll /tmp/python_core_dump.* -gdb python /tmp/python_core_dump.21072 -Core was generated by `python -X faulthandler -X tracemalloc -X dev swissmx.py'. -Program terminated with signal 11, Segmentation fault. - -bt -0 write_thread_id.isra.3 (is_current=0, fd=) at /opt/conda/conda-bld/python-split_1648465063888/work/Python/traceback.c:849 -#1 _Py_DumpTracebackThreads () at /opt/conda/conda-bld/python-split_1648465063888/work/Python/traceback.c:914 -#2 0x000055c2b7ef6635 in faulthandler_dump_traceback.isra.2 (fd=fd@entry=2, all_threads=1) at /opt/conda/conda-bld/python-split_1648465063888/work/Modules/faulthandler.c:242 -#3 0x000055c2b7ef67a3 in faulthandler_fatal_error (signum=11) at /opt/conda/conda-bld/python-split_1648465063888/work/Modules/faulthandler.c:348 -#4 -#5 0x00007fb9c7ed4ef4 in QWidgetTextControl::document() const () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#6 0x00007fb9c7e676e1 in ?? () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#7 0x00007fb9c7e69230 in QLabel::paintEvent(QPaintEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#8 0x00007fb9d7fa16d3 in sipQLabel::paintEvent(QPaintEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so -#9 0x00007fb9c7dcc580 in QWidget::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#10 0x00007fb9c7e36203 in QFrame::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#11 0x00007fb9d7fa2d03 in sipQLabel::event(QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so -#12 0x00007fb9c7da20f1 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#13 0x00007fb9d8097afe in sipQApplication::notify(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/QtWidgets.abi3.so -#14 0x00007fb9e6cadd62 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Core.so.5 -#15 0x00007fb9c7dc6de6 in QWidgetPrivate::sendPaintEvent(QRegion const&) () from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#16 0x00007fb9c7dc774e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, QFlags, QPainter*, QWidgetRepaintManager*) () - from /sf/cristallina/applications/conda/envs/crmx38/lib/python3.8/site-packages/PyQt5/../../../libQt5Widgets.so.5 -#17 0x00007fb9c7dc8147 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList const&, int, QRegion const&, QPoint const&, QFlags, QPainter*, QWidgetRe - - -``` - -Localize mionitors and callbacks: -``` - -grep -Rn 'add_callback' *.py - - -swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img) -swissmx.py:745: def cb_update_img(self): -epics_widgets/MotorTweak.py:88: m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'}) -epics_widgets/SmaractMotorTweak.py:100: self._pv_readback.add_callback(self.update_label) -swissmx.py:717: cam.run(self.cb_new_frame_pv) -swissmx.py:743: self.sigNewCamImg.emit() -swissmx.py:745: def cb_update_img(self): -swissmx.py:698: self.sigNewCamImg.connect(self.cb_update_img) -camera.py:159: self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=cb) - - --> try to turn of the monitors during ascuisition: -zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX$ -grep -r set_callback * - -https://pyepics.github.io/pyepics/pv.html#automatic-monitoring-of-a-pv - - -Try with: -pv.clear_auto_monitor() -> pv.reconnect() - -pv.remove_callback(index=None) -> pv.add_callback(callback=None[, index=None [, with_ctrlvars=True[, **kw]]) -pv.set_callback - -FixTargetFrame -> paint -> _log.debug() - - -Turn off jungfrau. -no motion -> wait 1h -constant up-dow motion code -> wait 1h -``` - - -sync and triggering -------------------- -``` -EVR output powerBrick register $(USR_FLAG_ID) -FrontUnivOut4 Gate3[1].Chan[0].UserFlag 5 acquisition start -FrontUnivOut5 Gate3[1].Chan[1].UserFlag 6 frame trigger -FrontUnivOut6 Gate3[1].Chan[2].UserFlag 7 unused - --> Deltatau flags are inverted: FrontUnivOut6(Force Low) -> Gate3[1].Chan[2].UserFlag==1 - -FrontUnivOut4 <- Pulser 0 <- active low <- event 254 (not used any more) -FrontUnivOut5 <- Pulser 1 <- active low <- event 215 (cristallina frame trigger) -FrontUnivOut6 <- Force Low - -MXMotion.py -> setup_sync() - elif sync_mode in(1,2): - #frequence jitter 50Hz Swissgrid: - #https://www.swissgrid.ch/de/home/operation/grid-data/current-data.html# - flag0='Coord[{crdId}].Q[10]'.format(crdId=crdId) if sync_flag&1 else 'Gate3[1].Chan[0].UserFlag' - flag1='Coord[{crdId}].Q[11]'.format(crdId=crdId) if sync_flag&2 else 'Gate3[1].Chan[1].UserFlag' - --> looks at Q10/Q11 for start and sync (in simulation) --> looks at User-Flags from the EVR for start and sync (in real) - -s.a. ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/Readme.md -> EVR -``` - -23.9.24 spitting/moving repositories ------------------------------------- -``` -https://jira.psi.ch/browse/SFELPHOTON-1337: SwissMX split/ cleanup/move repositories -``` - ------------------------------------ SCRATCH ----------------------------------- - --0.952 -pp_comm.py_: - @property - def fast_gather(self): - tries to connect to port and to query_types(), if fails starts the fast gather process. - -triggerSync.c: -root@:/opt/ppmac# LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/triggerSync - -usage: -/tmp/triggerSync pt2ptTime timeOfs mode - -pt2ptTime: time in ms (float value) from point to point == frequency if FEL -timeOfs: time offset in ms (float value) for motion relative to the FEL timing - -The program changes the speed of motion -pshm->Coord[1].DesTimeBase= (default serverPeriod= 0.2?) - -SIMFLAG0 (pshm->Coord[1].Q[10]) -> start trigger -SIMFLAG1 (pshm->Coord[1].Q[11]) -> FEL pulse trigger -//Power PMAC Software Reference Manual.pdf Gate3[i].Chan[j].Status -> page 919 UserFlag -#define FLAG0 (gate3_1->Chan[0].Status&0x800) -> start trigger -#define FLAG1 (gate3_1->Chan[1].Status&0x800) -> FEL pulse trigger - - -mode: - bit0:1: sync mode - bit1:2: simulate start trigger - bit2:4: simulate frame trigger - bit3:8: verbose - -simulate start trigger: -set pshm->Coord[1].Q[10]=1 to simulate a Jungfrau aquire start - -simulate frame trigger -is output to pshm->Coord[1].Q[11] - 1: synchronize real frame and start triggers - 3: synchronize real frame and simulated start triggers - 6: simulated frame and start triggers (no sync) - 7: synchronize simulated frame and start triggers - -in simulate mode: -set pshm->Coord[1].Q[10]=1 to simulate a Jungfrau aquire start -set pshm->Coord[1].Q[10]=2 to stop simulate trigger generation -Coord[1].Q[11] is the simulated frame trigger - -in synchronize mode -Coord[1].Q[0]=-2 : trigsync_func start, Wait for 'arm' trigger -Coord[1].Q[0]=-1 : got 'arm' trigger, wait frame trigger -Coord[1].Q[0]= 0 : got frame trigger 0 -Coord[1].Q[0] is incremented at each trigger -sync task ends when Gather.Enable==0 - - - - -// /tmp/triggerSync 40 11 trigger all 40 ms, simulated start, use real frame triggers, verbose -// /tmp/triggerSync 40 14 trigger all 40 ms, simulated start and frame triggers, no sync, verbose -// /tmp/triggerSync 40 15 trigger all 40 ms, simulated start and frame triggers, with sync, verbose -// /tmp/triggerSync 40 7 trigger all 40 ms, simulated start and frame triggers, with sync, minimal verbose - - -78x78 points =6084pts a 10ms =60 sec. -> 70 sec. --> we are at 100 Hz !!! - -remote ssh tunnel + start gather_server ---------------------------------------- -``` -PPMAC=SAR-CPPM-EXPMX1 -rsync -va ~/Documents/prj/SwissFEL/PBTools/pbtools/gather/gather_server root@$PPMAC:/tmp/ -lsof -i -n | grep '127.0.0.1:1000' -ssh -L 10001:localhost:22 root@$PPMAC 'uname -a' -ssh -L 10002:localhost:2332 root@$PPMAC 'uname -a' -ssh root@$PPMAC -LD_LIBRARY_PATH=/opt/ppmac/libppmac/ /tmp/gather_server -``` -cleanup /tmp/ -------------- -``` -PPMAC=SAR-CPPM-EXPMX1 -ssh root@$PPMAC rm /tmp/gather_server /tmp/triggerSync -ssh root@$PPMAC ls -l /tmp -``` -start debug tools ------------------ -``` -PPMAC=SAR-CPPM-EXPMX1 -PBInspect --host=$PPMAC& -gpasciiCommander --host $PPMAC -i -ssh root@$PPMAC -ssh root@$PPMAC rm /tmp/gather_server /tmp/triggerSync -ssh root@$PPMAC ls -l /tmp -``` -restart IOC ------------ -``` -ssh saresc-cons-03 -PPMAC=SAR-CPPM-EXPMX1 -telnet $PPMAC 50001 -Ctrl-X -dbgf SAR-CPPM-EXPMX1:MOD_VER -caget SAR-CPPM-EXPMX1:MOD_VER -``` -checking versions ------------------ -``` -git: 7a968aac967 -asyn 427.0.2 -motorBase alpha_220518 -asynMotor alpha_220518 -powerPmac alpha_220518 -PB_COMMON 2.0.1 -gpasciiCommander 0.9.0 -ESB_MX 0.0.2 -``` -zamofing_t@ganymede:~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX$ -git loggraph -10 -* 7a968aa 2022-09-20 (HEAD -> master, tag: latest, tag: 0.0.2, psigithub/master) change speeds and directions [Thierry Zamofing] -* 00588f8 2022-09-16 minor changes [Thierry Zamofing] -* d52a6ce 2022-08-30 minor changes [Thierry Zamofing] -* f47e111 2022-05-20 (tag: 0.0.1) add DET_Z motor [Thierry Zamofing] -* 8e5b15e 2022-05-20 wip [Thierry Zamofing] -* 399282c 2019-03-20 enhance triggering [Thierry Zamofing] -* eda8caf 2019-03-19 wip [Thierry Zamofing] -* 0c45705 2019-03-08 optimize [Thierry Zamofing] -* c962ebd 2019-03-06 documentation [Thierry Zamofing] -git reset --hard 399282c -rmake -e LIBVERSION=42.42.42 uninstall install --> restart IOC -ssh root@$PPMAC rm /tmp/triggerSync -cd python -git dt latest -- shapepath.py -./shapepath.py --host=localhost:10001:10002 -removing test verion -ssh sf-lc7 ls -l /ioc/modules/ESB_MX/ -ssh sf-lc7 rm -rf /ioc/modules/ESB_MX/42.42.42 -IOC locations -------------- -``` -~/Documents/prj/SwissFEL/epics_ioc_boot_sf/ESC_all/ESB_MX_PowerBrick -They are just using the new alphy driver. That should have no impact on the motion config. -``` -test shapepath --------------- -``` -cd ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX -./shapepath.py --host=localhost:10001:10002 -``` - +Document to start SwissMX in cristallina environment (maintained by John): +https://docs.google.com/document/d/1yEmV_DbRBKQKVCoovjXriNgSjNEBaz50WA0l3yA5jtg/edit#heading=h.z9io692b8tow diff --git a/SwissMX.jpg b/SwissMX.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c48ecebd1615d1a89626dc67ccba0dd82cf814f GIT binary patch literal 79749 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%0D9isc;Aj?foS(RPIq2{nSnEcNDpIK^ir zP&0Z4v)nfPMfH4f{wz3uu5!xD%ofK@7<9egoCC}+)^5d;b^O7biud|<`|Un*md!@8 z@l9HoS}~MPLL%UC(W@nt#i8cD;tYDL7S^u|o!Cv2jqa{^ZpS^lR3TTD{WiRHBY{4UBo<-nc zW;RnG;#9J;W&17Pshy6GcAuFfSk}5|nZEfNXJ)*ZTryV_0KC~`mw<<**%$g`8{nh>1zJJz0V~}bdUWQ&})X=hB&f3-Q0V6H)YTQ^y`?up&g}sPfW0I{p1qqNs z_J-wo>OOnCyIe`LxEh6P?A&04U;huhWc)#Z`{1iQQa_n&8S5>}u!6p z(Y>@T{OXtQZPzzoL3nEhu@tgnxaN-?cL$r4+G1I9EL8`r^_Xr&h0VDts{Ju6@VJ|Wq(hdY(_3I<8M35&e9lws+8A9;pZ z_Fg(Lk2t?EhVW@X&2Dz^VJb|jt1Qzkgyh=rj;lHO-!~H@(a24v4VJC||K3jDt`q)S zej100&~9?&x8;GL($EdNi2Axs8*>A_CVEz2Iv^?GVT!Y<5R^{`xQq^INVB@8FP~8J zIG0J#AJ~T#WnohcX---Dam_NXnEBJ_O)ZqB#8ay^%RvV3b?ow;SDIr1&e_g@q6cgI zGRf%(m3#9XC2cV`nl}h_w`Hm1*;pSlzJbz^lO*4LCi6O!&NmJX4BWzB$8 zAZqYd+8@(b+MWw3ab_f5vmR*{U64<_(nU|1WPN-p3)dHN&=td_yR*S)yw~%gZCfFo zu0FZ2fAi3JQ~yU9v9xcsxWMd>B8-X`t>cQ+#K7zw6ZIIp|In2bl(LO@7gl;;yl0^l zt*{Hg{}+9TzCqxC4PwP7`dVryBa>~L(#KnuRP+8QnT0;7hoFe|z6N$0;T0HlE2Ry6 zy7h8=))e%x`+5bFk?ofeTH|BaPapZCxD*?zuwo>KxM1m^!9Xne7_Wv9-;Jon!^fv& zWUNgpD)l4o1x3U&JOjTjF_`ObRC7d+R}B{NUTEw9NadmQtUAry9jXswk)C>$jS?=d z3!>Ef$q!PE)83AnYvje-61s0j&ai@-seM7C4$u}KXD6y{)`+7X2mnXdj23-WyuMl> z0~w!&{yxy|n3ZN1fjb^Z?YHh-kMF)tP!ikCjPZIP{y$o<_9)r!YA&Ol3B3!IB_`Tl zTB<>zO?Wn7e%_UAJ+(j{;-F=xE+_vnl(M5jA;<9afRI2<{n?4c3J(8=1_#w-?vye< zGs%n4u_^JM4wwv7j@ZNl?5L){U7uRyWAuIHZ4(y-^u0C1m(?=?YFDhxa?m}jYCSe} zj=d-~JH>=HLiL+uEFy56+$!f4Z(*d@G$`18?jB*@*p09jeJHQ~CRjA>qi{D&33V%AOX(-m zQ@t^mX5b6yp4Q}zmZc(WXeiU(P86oKzxuXM3N^G??Xwrzad60^ywGr< zGkpE|w|gt+&gr$ua2S-%si+|a*%Q`SpGs7>=kzM9^0c%i>1>@Fxq8W20O2@K)UuDr zx*)YZ>#cg7xt6ga!AnM~OHo!wu!9jxx@l#b2bg;hiYE63zszOl^Z_+GPM4aP_p_1> z@Kh{TD#!pRD+fV1$xqEv-b=V^rJ>0x3@sR^I=yZMw+PH-d62fID4m+Z3lEaMx|uM~ zCMX~J$+Y;?E<6K(v3~d6<()%NzmPGpt9(Q^Dd4>vJS)BQyRzyza-!)|yvx#aA!E)2 z)4WbpUV9MeY}W}!F(9AHdn@@unjG;Bq>?(6a!yyk;{sT@hu{Y(u-R-=u%4Whu`vPY zYB_w#;urLd`QrAwVne~i6gS6FBJGNolOnf6 z@%#)1)h!)2cIP8Hm~+T^_H%RNieUPoV5_Sar-JNu?#UV4+l&g)ICYhsM7Vd|^3+HK$&P_ru+| zGP859w(56ieY=b`cgOgFEpE|bUF2iXnre3hxx6cr(Bwjb-sg)RJ2b^#jCaTQ$#l-8 zm?7aK2GO%{mosX2knigMO!`v-v@%#>4bCY?e881<5)G;#0?KR|HMyx!ANiFEp$e1* z=kQnA-&CRal$Msr{4KZ{|7vSgZu?KExc@&!XbF!)y8%QC&D?jDO94rU6jlyhyKVC; zh~pI3UyzD_pn(4R^LUvPaDnbeU7&)s84m70PQ!#5K5jn7D#AED+gV4Nc4*i4?tHK> z>?|l~rrjmQ)_iK*{=_Wx5l8-Piw+Pl(xeE5pFmnQQhd)Qm8}kwl3y+S<8d!40qXPc zOx8oE$(rOr5?~WF_x@%E#6z$3f~y$Imp@pi8W+ViRE4u&FpOI44(W(B>mjWjlKyC@ z-TqpSQd9du{U1HkLM*hAP&!yHa)RG54nDtHle{Vzy1NAK2S?GReS<@m3=7HwnqY_s+Px0xsA|7i>+qslxoNy>TJvq5b zSu6j_-1(1p;tg@VJ*TUX>ZLug^x6no$<})B6;f48!cSPTP*=fEvw1E};C2>A< zNSc)n$Zcp79I5ZwV4amC8aRMIXP>_jI{Ocn*Y}=Y5^}cu(&)5N+c#~>!GZY!Fj)R- zr}irvo_E&j4Z|W3QZo7@Pu71O12*Jk;;s*HC%&btI`3dGE{kGE)<7d`mqm49LhN%! z@ulHPbO-fn2j^9Eb{CZXOP7J)9{h5gSx$lNnJ|k!EKGg>ZRhLXsrm?n{OpA^FE`@elop7 zBlHB6g9Zk=+}2wkAK`%@HwF%u7kuXIW{Wln^51u~xH_fCL}{Zx&K1K>d*GH29qFbE zqxH;Obl+KXFCXut>rz)^1wtf9i*RzE*cwuM1`ZV|BoY}(IEDw#uRhbIe$cy_vuT)k z9i^gIf`St+---8H!Ig0!K3Zue)t0%3xpnt$)Si=~`C2OyHnl`zKkR1v8`;VHceQus zZ|5k(JJo$`uH>PwCB6R1#G(0iBE_TYpO`_Z#8Z4aZs*;lsb>p&-N#%Na#;CXknbh~ z4%p?!Pt7Xy4Ehy+OOWckyM5m$9(S+}(d!BvuI+LAnupbfr6P50F`Z4HQJFpZ#-MU( ziu=IZ*&J?Qs#CKq^jR|hkOjW+t*ob4BJbXByb_)szb=s%KekoI8TBCM-erw`o*HU1 zUe(++)iArp6Zd~Ko)XR;$u)vFDUCq=?e2W(lPlB*ogmT-(P+Qg+K+oNo>eN^pSs;t zlOfc^+jF85nR$)kBZqUR>&)lZ3hWK`7QfcP7^QB<$NudSX0T(a#d^uk*4K@-o#goyC+uhUHx?4UGMoeg@835h z-~7~Dfyp6FL;rb)py;~~d(!?bxKaMn{=kWb%E;w$i*_bHpz#zhpGxUkPA@FW5vs>1 zz&U7di;a49LQw}S159b7kI*O)4i8{nG4eNAwSasUUfr!-ATu-kOYxh=D@X;Z(8_>$ z><6ZL!2aYmaJw1ZT$%URcrl-gTeTjUxx({X^8$QXS=h}^)|yLMd}8vh3UJ`&Wd{MC z;&T&nD+y!+x&Bqe$xziE4GlX9aR?ZmqUU^8`%&uz4u@HQ!PG7ZY-lJ!1>%cI;jM4G zHB@l_bT%_FN2mXsc+z=BNeQy8#hw(T9EN5R5>n5I?PN^l>fHv^3;mc zhF}4n;S6B%&b6K}x}KqD;AV^Yo*0*-Qqj_67-ZCoWSaJQ`Cb*Frs^JK1|K(m*;R)0 zv4q+Mkgm`Eu3aCBK_-U%j-{Jpi3>47VcJA7@5I4fiiprrGTA4Xt{0N(J>YC%CgR%1i=O3Y$EC21A#AOW(^iw<5PKl5RbTqkXc= zGkD2)^9ut|aTD>lAYhnb%0zH)T>6rpzxi6$`81o(Gn`}!SF)P8ji?SlwDb@OjliF* z4AfUBuzy7qVN&y8U$4?%Jv&=9@U`YdVPwuo2(LNw`&mYY7JjMH&wPtMgWJcsKE7lr z^f8X#S9&EPh=ZmA0;q!Ow9ls9iSRUM%0pi0nR6F!IlXsq9ssg*%%eFyHX(7-SbMbL z5`F@jvQmsMD0T+hZLqCj;wZ?hwtMoEnm_w`KD}gI@6@^9GBQ^F?F^@dPKcxQc=T{C zBU@TNB>t?}lwaYTe3}tgSvw=)B4u(XIpR{R>e-zw$PRj;y?HF^KP6b)Ik1z@vG(dt zn#AwN&3NH974WhZN-|J&5qoxSjmCjW1Jg6lr` zn|t-{JkH?xblT_qdY7oeHgA5p<2sK$9++P*F=2Q-DeqoxYNqozZut&vGs)M7YcvIT z>LvlXeO7K8O4-vD_Wt9Zm~UR5VFY<7D-%8Ea&YGH@xaK><1umq3o6ah*lvG)i1a>( zKW<_e`L7NWr;}skFP+`_hVBp;`&~x=3uyv|(#r~kLP6J*=Pfq*wEBJhWr;;{*Ue&-`(H6 zxKG!GvVCAD!2bVqpFMo1Te5U|It%AJ4PfZuJ2@oXQI;v1*bOoIv&05u5W2o8&qQ&pVj@z#4{+zG+%{1-H6Kt?1H&w_o=x6uykPh%_La-F$@ zfKvQxlJQnXkCe2+L&ehD1h_`~R=#sfBdD*K5 z$@>#lpqh7HJ-=K)1<;<_no8AWn*4aX861P!Sd(w~#3-F2xf!SVzKm%HwPpM!%g$8WyTLT^QW*`MnLq?4vZ3=Boqu+$c zFAqsw3b5>==q-krd=rcgSk_I_=D!dT9;L`j7Rhfrs^ZgQaTW{}!A!?f%gvpK$lv-` z&UeRurp{b6YgB^f$*>e;93a027d3;9tVvcmX9v(0&pE(V@ z84i?@06dlRVNCel-_yQAC2UKa0X;zMsDcNt!l;ZLf@qA2pzbCkP?t;H38YC(ETE*n z%HBaOw6!a%OqDg4_p@TcyrWd6`;^%vISHC5O551%8}IP0H;adj??*uV$0;>GOg2tIf50=8qCYzZcO52$PK(ljZWseA+Fb7h2&(0A@6|hJ4p4OdE38!JXO3YXMBJIkCBH8hSIPKJ|09_`A)iiLmYUX{K6w!jlp)_&lT&tz_pqUO6bIYhYeSB?}z9`_oRtu%RS=Nv8ZQyAxs+FU1SrpfEBVf zH9W>eGjh34zl|h3hhHKz?x8cTzEfNG~zNcz^4!!=i=UT3Ssy_1c+|VyPU7QSHL#&}DAwGl);ZlBHV{#ro zSa!-Ccj4g1$uqY+8C#hG*dlXI2JVNG=G!&Yz>E)+t;^dh!UW-f>wWck!(mygAuvG` zoEVuVF`#ZH=>1^m)eEtArUz_!oXvQcun{Cw=cQ1};Jmt;m~vBn@wP61-{|eSjJ_Y_ z{^x?T6Fw6wV-!x?&l|2I)v!s{c;8!V1_R5g(WzoByI!*0${7FRs@p?SQuI8`IAD$X z(G-1=*tBgAXS6M@>I(Ht+?7-CtSOW|`Td?+#iN2VxET6!T@t^_QexDPS6GXyCP#Ko zMMvK`PHa@WPcxew>1j~#+_F^u%CE9rc=CH_Ps3k1-};mBgsH$LVM0>p|ab9$-;t(mzRm9@zowr57u;ntMy$3jHOWnP|C<}4d9UBon{Jj z1=%OJYtTD-5?1!>^O|+7LekLZAs%Ru7Ddyx8Uggk&(o13fEuN%;!9kVHq|^L9Uxg> z+Ci#{L50@v{s5pNKEcTMXthyUdjc~9SySy~BSH13!efDKMCYlk>yuY{4W0xda0 zzbC{*IGW!GYi81s;Bm8bGPUa4$$Yg^c_={x{Vp|m*!2}GO*k7K?vqM4N`nzr={2h* z*BKuyJ#lbkRchCr8;>91&KbU!M0!Dxz*%)uvZO-ps5ba~?NDAHv&O-=3{llqS7UT_ z8QtZR1jC1`-2^C|oGJ|Rm;I|M{-T$E-e9>sr(ZV)pW6MgQxN3;Fb-)Ekpr+}Z1es6 zPw6W9|1jy-@hWqYfYWIUxsM9~v|j7B;@%;F0+O@;{vcxv`LKDCWFI^U%9a3bKq78P zS>2YbS$z6KeMvo9D-!mG=&r7~bSY3%Rw=L1O*fgYPbewJr~i1A4$(~~dJCV^2h3v? zz7?MZQT-m8j}}_P^5?s6;Gj>Ex3kR`pxiZL-t?jG=mk5%y$?kgl_d8v>bQd#spEN1 zWtd<6DMR(?;P>+~N>EAhvf+-C5W2%%?fm}e$+s~c9b6_?b&Q6b3Q;N>hJy!P=kumR zBt+JP+wlI4g8I&-I6wNWAZNVrTKx#Z2dlirnKVW~1_eeA|9GPn<`X4Y+G4uFNlI5w zuS-$`Z(CbkFNzmh9jq>HFm#7rPnXNn8nMHexV?6wr>Wa7o`!&M4y2={Nd?;hu zCV8)?WfBZrco_gaLfxm}H=0L0n(-2vgv{kxcFv9T(*;Gn5Ue&tHAAG+)t~m{N&FdE zIHM+ma@dQt?x(m^qmoo3Q9VtxHjI<`(cM+y7@SAPhKujq$fKH19ITzjmQ9^{KAOtK zcP2VbMxOT7>3YtKlO8_t#!z*U!jOltul_8d z^lbI-IvCYaI;sRnG*qf5CUZ@WUl*lB}d4h$&=e4t${7 zO`YFWaX|;}CAt3e84+^P4;dI(Ju_@W;a z$KLxsjg$~)scp#<@z5IGDnoD2x${|dIUqcKXjsBH;i+_+WZS~rBnU~)T9)BfK;Eau z8&SxsL$m-O_erk@z4;4bg`?GN++mo&Hfg8))`71I-vKw+!L`D^0umA<=Ob2(5rd=t zB^3+VQ<9$EX0}~bL+_@-@EVnwOWd~IPJ{g}7_@%wUk}m$W9vY?+tMk>*Lki4I|?e~ zTCDi+h#G#Tf8YINQ&|R#nWSw0C?S3NYC|Y=vy7ceV7rW0DX%=w@~8Xl-`0dXRf~6- zM3|U!XL6n}3g)DR>ybz`k1xGsz7L(VO*;c}{AeT-JqTSW4;=BTKIs+j6LVE!#-pt- zXkpkxEmSI5Y9sNyH2WW?6U>Kv%^-MIwhGJ42&A&#qC6D9@2(HS({nfUMalkBIMjzI zgwXQQ7DcU1_RfF1l=&FXB@DZ1XOkf5GtB3Boa^gyO+Ey^;nM5 zHn~w+nG#=-uyoe+jTDz!mG-Q*&T!xNd#RrOjgoRVt5fuv{88lvnP(&8GO{sJ+VuMn zN?c7oESejFm7RF~CeBR?kXG7Tey7U;h}B@vqAPv0$8-A!4Xf{QCs#Q6tWXs^AwAN4 zHQPFn<{2FPLK}Z<)(kHj;ch-jT;m$Csw>ceW#5pORxQp~uQh(Qr;x45uq{tg_T8an z?zt;r=gwt%k`O0DLjB_my0G>LmwN>+bze@;4k zHt- zBnTg4D5Uj5u|EPd%(JP%?};(30OC?{bzbML3Kbi0W}Cs0fS*jjVcNb*YJDdJqitAQ zg}$kemMn_)$ee{hFy?fxV%T6l842{lt3V?iKneu~*iH7X`lht7S(}x>DpK$}77L59 zJQc%se*JDYSfBF%vR6Ae*a`Gnyeu>8D@eD$yyOtvzYrXW|A8oK@fjw~`JR%){Q!wd zc0Npnpq=IX_P%T|CWLPvh2%(jCgP>w3v>Mrz*4$Ns@;>kVslZ9Szmr91Cs z;fd-zIgsUSkS}k>fled%wXdL#s(E%J1_!_Do7G6oe9=>7q z`ChY+W1ov(&`Dlib>A&;_4ZK~t7B6}lGV3u^EY25Mv(Z7BkJ?16UkN$^!c9t7Gm^6 z0b{RnafuCYL)hJ;Uvl`ML`~ssk>dc0TU}uK0M*AU$MQnztrvq)RP=lWzwWU#G?Cw3 z8`qJ%6_jrWCIO8B30JOI|$J5a2XCh3kYy2u#FqqaVS1s6nrcq&IAFQ z-v5)yrAB2cd)N$AFS@2!Zg#Y3@?lAL8$`~~(~DYIH0rUb09B!p^9YqQJ>-R+isxOP zk+ecq7!C~ataA1(*k6Rz#np&9S;-q!MK6A3XZLkf%Ohsf^;#&Fop5xoWf)+e^@+Y{ z<3V(BN$o(5gtv}JPAzB z%{b?7wpwVN*JYZI%yN^-P){fv40_fOXYgs%_v|rh&^)) z088JDCz)~KITBOIK6j(Ld-LKaEPZc3$(g-K52KkEdc%vN^dJeb0D%&Jy7TycmE@8~ z!d*lNMmnM^@|X#QHb3u6N^z%o_dO&^T(&vO?*(&~bQ}H#09Ev&-(gO?7AKNw?lQ?Y z0?DcpR~W|y@lJN|Z)+(w&W_?hd_iwxP0jTt1hKH39^rf5U41B0T35$$EGbhPtzTkP&6_y0~c!;7K;w`%-FKX~C!wALt$b!ShUv@5q8 zenl2AC$Tw>;l&vcD|wJPD#RAzO)%nuD7os397ADh@m9n!|D2q^uJP(5-squvpc9!Vj{{53X2M_xk z7~aU!-Cjfev!^00w-BD@{n9TIL53yzkZpr*)y2nC8v8Hzw-+y>~e3Crx$J}5GpYE7n%F7QS{CM=o zxVNlbvrh4!DE-ox^}SHRWWGlz@1x9YH#=eZi~>DY0fWj$?NQgx-x%4Nqvsm&i3;yiNkTf^juQg=oeCt zB*~V(@5pbo5i8S+eY$fzZ&%6@L+-KaZIgDj+j5yY&C%1P@?<`@tpHO{m$zMi?-^@u zm1Vi5rFP;C`(9v;MZ7)evh%)m27BYr*iR>E^y1v#Yw&y)_+NWj@vjDkE>e^`YNN zjQ#CY0WX>4{%(F1M)@nZ#*r^f+<8Bl9=+zivk-b>*HmX@2=u>rFKssr@8Ue7V~izq zcMo{rao*-=l9*yj^wAPfHcl*g?cPg`?dUKwabrSGN!V9lft{>%Hdo8U1t5~I*SuA^ zk4R2}s54WN&w#0Yt41L?w`#gK=$PdPlT?*`-C4!0`gpyzL{ zTyBJrTr@Q_)jX@|z;6_c9;_+;^D2Fw4455 z8fNotj2_h(FY!h-`H{ZWMtNdn%ngBx8zA8OEQiCM`oZ!nWD$JJjq>hqc_vcirS&uvti zSNkF?44a^7Qo4#@ep-tZ!8~N`0z_SVp)?bt3peyx0Es${IxnhrnMOa$&2x@o5h$Wl z`aG}Z3!G?#D8j;Pbm6X!vxQI@3aL5Z1uM4ec{%tf4w(6HAsM{pVh%G>`E4-JrO$GS*C_X73J_B^<1eD%SnV%SU6sI-#VAh z(cO_O=b7MS-%A0buQRJHd*em&)Pg3f63rg%c@H;9^Tzq&;#bS-`r)5^A4nribPvL^ zLL8e7>mL_FB9XH`RiI@jo4E)XBQn2k|K2Ab9=E*{O(f$N8E5N+lh2)xM`Jnmz<2A4)qdu>9QE-^95-_a;7w?_vZd&GJNm)1Z0d|kG!w= zC?B9(>uGuS>RA&a8)uL5qW-ikH~GNj9v}{=>0)z+<>E(5)8cT`^`wQf2uLKs{<0a@ zf{)tv@quyN;HQd?`7a*Hh-hWz4@Nb5S4Q%{Ti-NBl2Ee+O@2n*PLgCaylW0&IXr{y z>WU+9E5zv&hMx5r8Yn)~W3iLEE!-#Sn+!oej6<$5tE*6mnRCZ1C0k1uDDn!8uqaq6 zl)_=ZKOITzLt9m`{b1|Pu8};RUQ{S{%K*Y$a##{V5E>g8-LT(2*C}cn`}O&O>4wXG z={oyW+Cq~36O1=H(lGl*ah7pwWJY%Fti{j01_d)6ZI1laiaH|}=&cfi@vHSF`Ut+k zJ22a|;@jtVeQ1k&1uPKB&eadHu@={oBas~pQ3#Ty*t}fj!2@-n0B9TU>Fo^yXHU+dVjTc5coSF}F+h_0Qe!gA0!SNLKT4vz9x&fwPVi)~Ip!2``* zKbgW~<}F=D)V{UGlCdvqckJOe;sCc>jmv~?04_{>mBtD*BuO%JE(#t#PrU7fSi zD@4oon*P*tp2k2ts62WVJ<8 z(;GOjCntiRb=E4k@ZFk$0J9`p!C9gXL2k7~5uZJvKWMy6MmyueOdcavmGh~s<&5&E z>w8>w;_KoVm4r>3k3BI(%H>W-m=jJbv@n2hd>1WhngUQ{q;^&57fp~E-Cv1WVJMlA znKz`d_)cX|czxe;VDcV|^nza;ylpP{o?ktNIobIxbFyr1u84~5@{2&{(EW~LL<+sl5 zo%{b#_nu)*t=-xv>au_pr3hlBhoVA|&RU{?2@)ivKxk4z3B3!5x)e~U5)u#yxFCcO zLg)bmf{K6;1SEvsqy!-}L5hO9&cye7cRAVC*E0SAnu?Ou2%j^%Fd$;jSE)Hd`dd}*%Y zMFunsXA}xsrP(j5C)xyCO}$-<;v*?p@HQk3B<-;39?*}@21ULj00pr8{mT5(!lRY^h;ns<&0N`92$pTG^ZtyTjw zVxrv*RW0Kl)nmmO5!d}bBtQc@uFN$z+l_6GVa-pH4P%0Jh;Do3{$g%sAews~Qr)?P zLZuerB#Pqz3nNU|2}}v4Dzlr=Z(Ay&NEC^yV`_1I@t+c@LP0YVmS2-V*1_X7wN3A? zf-2(YT67WQqOAC-?zLP-HhFDiY1{^NkUU!eRq3FfWcXsMG=@CwYbnuLE)}w;DD@df z=#-4An_ZY&=5Cd?(ULl{7bg*!X|}uo4>*FR;7Z`kXV$qQaGj}w$mTkT9FJbfR5mQ6 z;E+y}B_1ZeehbbUxXRDHpFda9YD`T^Ft04MybdY0kP5ZgIEjF^Q4>^UoTDe30_q9l zr3P5r!f~TE!Le2YNCpT-w1O&uNgRkOc0utr4Nh_a++_MU*l3HTV{a^@Z?tvh5l&A@ zK>0G9mL59%k_9PnTqduGGJ-i$(abeRii)P8`ulXn5ey7cy<-M~^@VRpz~ZN3k#ibz zvXaS>p7+BwkU1bu-V3ToCQ9{cyRr~5tQR?NXgN$QNv@xBr3rq@Xp4mKBWE-~uzZzU zROQ&qUD_(Fr$Xy|PP4_ssGwk3ua5bP&Nq!EQ!kKmw1zd;Ws2R1`As*+!iY?6P6Bt?%w-W=LBge!exwA zE!S{HV|6SrDtC#p-!;)KDeH#i2V)`lBNA!ZokFKq&NcRL8y(>Kvo|0EAwAH>6t^kGevUeS)0YH`}b+!^+xXrsh1Q>#XKj8>;))V-Lqw zIM|oL!8s2phGA2RZ4~zz;wqz+1BeRu;_|;Bn9CJ#^|z?ZEU0J^-?BQ7sVHBT^;@RY zhu55Ft1bJMY@KP&C#-f6bfMG#a{ zw=x^1oF6u*2DZ2>B8cGB(Nm$s#rDd+V4f6KQiZJv`x)D=Z?hbPNyZImp-7}cB-FkV z!MyCSUvf#D1{V_Zx4^YAk{q+uC25A0inN!FI=qvkM(egLJ=*|NwG^TbE+7Q6R8kNk z>SsYmiJr!l5kynsY&T9QOS%3v3N7@jMT)SLgq6L)G+s1>ormB`pQ@}buPH*^l%yY) zK`>`-nX5Kb4lH;p>)eSvg3q;u+NY^`b|-`%r^sP zmbKf$X{P?AB4iOQYE})d{lq9Id?wJiG!2op)LIYgP)RsVvT_Q3WCdT#wL0W6;bB># zQ-&hoQRU@T6ERfy5jyS47qgoDWfw1CUkU6i-AuRs76=6YvVH$-jZ%yL2HuN4~^$wHX)5-F|*n17*fGTUG~%DL6liSEG#A z?FzbWCV3Ui@A1B)tvx3SZzb~mH|gJRLg@~5$(nW=$)dfR*#W~&(B(xBh&uT;M@8>KeWA5oY6yXo)W#1{0)Z|Bf=5wTjQ zmZmTM9C)xdh5PfG=C)Hcx4BVyeA}ty2Uk^9)Mhh4Zmag?v2G=&152~bcW*{?-5X!* zI+}}IIk36Aekr9k&eMOg4bTbE&nBiK)50d(-H(muZ3M{vzR{##QeVy$rhpPj#f;Vr zW)?cMl07*A$d(U_`qm{!$XEDh9spJn?(6X3eP3G9!-ljYucmrl3CT4SYMh;OdD8;^ zm}v?x>*5^u&!H_2kGdLO7}xux&{k7b7guC|UdbcBZ)ORrk|*L-rg652*4{w6X95#f zy^tGUz_F|Hob)4GnY|gs?J2B#%PE)y%GIhfTn|r-+uZaL*E@_#_(iQV5=%xiKo`h5(h6!z`m*z8GuvmCzPJY6bWVe~6-qM)jFZCFjg)xB+3F$|{LrlMnd1Aa4&aj3DtNNYXrt|yr?oT- z?{z(8|0DZECNX7)oa%pRy8rhbE@+d}a`>^V^6}DAwVneXXN^X$(cbliR1C!d=|PQt zZFjo*>U@6o+}i8Wlc1&W##&`*5-|lz&a@|7bgSh(*jHS%q4tb?K#qLM!KeJAP_2K^ zs>bY64M7@Qp)@#Bx?9b}AR-*a+!iv9R>&uaW>hSJ1t^X%aBpr=RkF?{Qi;Oo=HCK! z?GG$vALmt>jBOj-Xh_s>)-toZl2zi}{KVfJXI?L&tgpc7E*R2e6>p4fn?gx$3Q*|M zk(GrAq3Oqm!VW!~NIECWi7GTE$99|=+K*pMlDe79K!Ble#`twsD8)`5AIlsfZvVVmeakRpfzQij5krdRv!i<_`lt7eBBvqmS>JN{&FjjTdIkrKpj*dad!g?pk%A37dZxbWj7Kq@OrcP7x9;AB| zeE3LvNC$~{48@mUHR05HESrhn!F(o468OeQ$3u9IJ;%f`;qFZZt>%_MQkAg~fYj9f zz<^tY!EOaml{rk##%a-@3~l%FPgO<32Ph~}nlVN5P8$xeemEjowug#Kw2Iin9i{6N z_V_Gq#bDiFwJ-Q6S@|-hbWW4;9d{HBhx5RtoH>Y&0tC)uY&-75Yd1&(H&twPX%`K( z)5tItp836sLQ%y|)mtR2D3cm=9xi&qkK~eOYuDV$du!3uoe(iw0*ComglBGeDaABS zDmDi3XGu5*J#J>otI~yNOz=fA3G4mRZULSUsRAvtD5ok{H*iK0z2x>GK-a(u8i!LX z!d|p438Y+6sI2v{duxPE9rm$p?)N>BLzNl~?lc=T;mh0o$OU`612e*|EaWyg#RMY? zoRPuepupIuh2kDQc0~k>VhA?VEwkP3O)~u}X$XG;4x^%IX3`xOF_mz-fgTN#(1uw`W@(<~PJz0{;f$<_|@LP*zo0;eq%N)J(_-Rgua^W}B zhOdNTGgNhsp08?hbU+?rK@_~&NeZantYnVA?~(azwU0JLK`b-EJ1UCUtUDjrBsXoy zps~BJIMEoQ5-L8Ia9+u%m^GOYiYZz(`D)Rdz3t#MqTocEl!sq}!^9e}o+CE~q_|}t z7|5PzP;m!UNhvHfQT2GyT%)M08?nQr+dR1W21w0n4qeNhD;O@`e3=!l^>lI$`H+%I z1U#Qh{NXs$)5aQ=-Y~Yc?cyvIkda;5zxVFxkQJf$x#^N>jiu+=M{2uriN8<65xk78 zB1XaTrINU4!I~NbG1Wt>g|8H$d@~+k;WXmVj1gZ7p(JjJn@^>U_%&e%!5mm7A&n{@ z+#hM&>n}3rxVD;><4F%D(&DKw@R&6lDLQqMUUx7#Rr)BVAr_7pvzf^sX0xve+Gp>+ zb7!`~jJ>odlf7vCrMdZ5^c|zll=Zx-X5X?88F({YD&unC9%V4jFv<~m4U9}h4V4v@ z5P8hS!jDfOhN|!oh+V}6MGvq)hu7sD79yrS@wDL#l7F=^dte=SJ>P0-tf%aiX!IYJ zpsRftIQdo-8jO4Mdo4PJZ{+T**q?oa*38V%CRV7-A`%|}a_j<~Y3n~{-2Y*k?MeC1 zETI3o&_GDm*Sm)$xzgIy^hK?s4!M&*bSvVN(b?;klloG_K8(H-{jNnS znPEve{fPJD*UdcPm^$;@`tgM;XRr6!yv&#g*P-Zi6N>Qqw*@GOQAG+f-*(rRl($^S z)}0d2LCS&h3F*jpr)FDw)=0y4$-b>~bQHmFP=rzDiJF&Tx2UU&Y-1mt@t~c!jaS5OYc&)Gc#G(1+!WoC_SM!JM zFiE$Ls^|ZnvvEdAzc?L8%#!%)sON?0cF%Krn_EkFcZay}I>h=dkd<;Dgq<-$>*8T% zwVm-&zV@d-MxKbm0+HTDmegr!80q}1;pH_OE~91*rb;Mpgsk(gKeu8#epyY4cqu9y zCo1Qy1Q#E>#vM8*i30-U7N^3yZH9g{-{@`jaK)|Pp4cih9Z7rNVaY+GE0fgAumD?~(GXIzs;;-`?)AHhOOHPx{E+_MzWSYweI_ z!y-;=+5Ml$zP#SAa?{A63M4YeuX2+sp{J*2{qf0a2$uJ8iF6`mU|pq7s7tb;P3JRJ zJ`KD0Wlw=<6zQ+bxVT7joHlk?^U=TZig!MHZ;&{FXe2u}M zKtW%O)v9=dVjjp#B#4c3(gx)fOmj-w}v$VuB0_QlDf`G8hlq zRRO6OvT{w@MH=*EY;cbstFKqq6}R_tjQbNB|9HW^W@}{CZsZ zE4dZ>LL^MIu`l@ZO2JerY>v^Pt5(b~N=T3+lL=BO^S!>$C3mVE0`3XmP5iIF9xGw! z(=sIf7O({fvW0*g=Krm2xH~n4Dl35{jsj*jwsj{s`do@7@l}HbwXU=4ti-eM{go{V z%6hoYAV&5^s&(+Dq;a0S{K-yLi=mXhI9(~kh1n^((ZuwZHmwd5IBLvI68^Wqte(y` zQUCXz^w~lJPjj!+TE0RMzsA~Y+U1fiEJr36kxjJE>+=FW-kt!h zQ})A7;Mn0=_@VZokXj@T1Bh+Fll)(OE<>E?)d}by^Kc>Jxsc@SGxh!=mCxYNMGW-) zhbM;Mk2$}QMawQoqe%G^63uPikooIUqmru=qk=dZ)C>~g8{qdD|88=6l8eQ6Fw89~ zPfHT>r-udMNEA`}ZE6lEBF>!# z#Q>SES*>%co;s$b+x76n_qSu&=-LdrcSaF>f~&sg3yDt zYCg3V4X#tQKa49+HLeAnM#@bbtgOmFA|#_R#bSvyY;*WUg+_+1F7&v4i|GkLASX?0 zj02&kJI%3+j~7YLLseUvpwP8jM|8Jel|`*pUyOCv)ktxbzP98fpVo1~h0^cdwS#a)4&k-+iLbSwLo;pou| zXsc)g^vM=sBLIQCOPb@jWF=1mdLo_>!V4_}^9_qc=Gveaov#sMdrBmUxE?4JF$9|r z%Mp&cJv}8t${P2SX%!Io6`$@nG2|>XIbrRRiMN!uPLQa!w;hWr*lx!Hv$C(} zyPnw`c4u!AnO5a37T%gl)@QZT<5$Np&`6zTi~D^~d*B{J?%xt*s#7G(Yx^wLh4Rj{ zU9dEF{BpTOqc1#C6lGb^6J8|BcX;*e)%|mUQ*>y|Y4%0C*JD5N!=;(o`l|S>;eyfB zNk>kw4Bw!jnH6PHa_^_%kMBvMK`i^a0Dvgg&ff@4$7Y$^amo}Ihg(-@ z-VY;ZncLrXCo;hcGqEA8X7-$8(^Jd1LD~0vH@puUEuuFLQoUWGEPC=c8V!zvMKGQw zrz3(T1&YobJ^Iab4EfQoc2r@EZKrg04f{@r#bj`@eHC5+?KN`drzsk$2HK_etfU#=uWbfUcH+^v)Z}4P%g zR92JU_=OILnuZ4IT#7>g{I>h|C44Sq1Fk-qpfmS?m~|-WWYVkuK43uqVTQuoT6I`C zZ-KdFE8)>`=&+8@N|u?0KXe{`uhIEYKCSmrKx$f_Pygm=0im9l8wxXoI+yiyM57_a z1P-%a$_QQet{D-)@mu_IbJAy02*l#Cia+)O!J1Ual|DwK3o-{rug@)^+a(C(Q36!t zB178=T?-#@P#C&j?PhG$ssURY?Ol;p2s$HokBw47<5Qa1%^RS)&}Z0U-YWKxmQv+m*N41 zr_@vA85t71MSy>mRlafwyf5sZRSqAb-~ayV&tFYIi;)WlC3-2adNpJ$AyjS9;l!pe zapjetSiG|c0S-~Z$>gJ3el+p!`i=nbqKm6vz)ETG3f!m*7P#^oG6z~8(EAY2T3=Qz zBI3)90va@zBpgm{?)nvM&YLx6Y0)&rCA5t#hO!3l)=dwv>(GIv2W};3Sw_h!v>e&W z9$U3js9m9lW?Tle*M_H!?Fa8X-&r-_B223S_+vHz9@!87#X5l0ydCzrojPsM*YJ|j zQqYfKflE6qA|8JWI4#}Z^GiuFV~vNO50OyBC4>wO4R2&OIi9TAd9DtddI(ct#{=S}B}_d{Z8Tc%44}_e zI)0k1Vds@M|8C~+8@#N*&GE+JVK=Gz)g3MCYZT zUTb-*d)vCfRG%MJ3rlcT@WIXM`1!R?h#wJSN=|ESX%sZF^0H(thHS(-LKbvW;E-m( zM2v+I>;&Pl7IATyRLX)JQLCFG>9_7w6yN3Kc7XdRp5q^3sn@Eg`nU-co{4{WJq*l)Y4CI)?rT8q-&PvBGXMBz62ewi`&Jk@0==}T|HP6C`*!~1 zxDV3v37l$HE#w~;5kR-?KR-Pob=vk6g$V0_s8m%|{h)eU>!7rMFU}0K>$22w2K1i7V%_%Wf(f&pi6FXBO0nA_pa2_utvxziQ;9u77#Y=pG(O(p$O zGubhm3!I{W^b16{DYw^8yyMa~h_DkGtSgf_vZtWic3Vl;c}*WH*DGWF8*HmcB54)= zPWO#PJ-A3e#Od%jwN${y<=^Z%b@<5c5QZh#XG(c%+|2`^o)!Yr8V9p9+}Wm>F?$Lq z7>+zIK`DdSR%w-q+F0H2uvO%)e-d}2dDYAgv>;Y2IzNe{Of}Y-E+Ot7?rY&%E)#4< z>vPL!F!%gPDuag-|L~*{T;TGk?PN>a(xS-e*=t`5i%aZ+(>yfX7;4fd8$p+$ie3<_ z)>u42k$vkCDYcdANg9uGR??XcbZ~&jye3sCb)A- z6zrg(DWUUapC;yEN(U9tS=L-L*by{;^GwlHHi;bJYG&JhI40%{$%$Z#ugFNqR5ri% zFah|%wiMFY!8mTajyK=51dV)(%qx4!t^`08?c#!byTsp5a1gW>9~=oWHVB}oX>dyp zfK;7im%O*#L)mYQ^z5-<*Tn+ckG6+Bva_!y2E*<0^R$dQ%$i-HZNt{d^8atYBryOl z$tF`lxGb~dj{prSCctkj$w7)8#(<}3)!#j_fD&$};#=h(GQC-S99bx7lC0xLKsEJp zlwI_mKy{QwCo4nn7?atVu^*q@jM4gF z5cuMMjuA^cpdtf<{ubEyTY&djZ$W-M;;0G0a0aA7UHtQtqrvuolilUv%IT6#&PLd2 zHilXB$t=lcb{t&-IL6gIQg{_~Ky z`x*To;$!yNg`zx`!uIlKb$j}p)hw-{>y>a$NMIC|GDr-cHVEt(*=%@qm0QaO2@AJT^meFufSzQ{lTTr^iz_K6FQ0_G?W+0Kro2uFt=rZ8gbqRZ z`un%LDg1l+6)*Dq< zcrm@h6m_OLCL!V-8avn;EG#`Rq547MGMLF7b>2M7l58DVZ`N-iMnTe{n6C3cEPh(7djN=QvwGohMU`sH;nIjl}m@^QN)HJyGC*I#UxgO#4REcqA!IBUpI|tn4&Cj z!v^?Y-ZT=<0pbn)OR^g6)Eu!9pUKHhHS@zk3@kGh;CA4S#I_HehiJO=Zg-szYTBB7 zG^Z1|K5J+g0&8#=cx@iP4F-Eit3lZK>f#66V$@C;62>%3q!@%02T; zYi^AwiD#EYT)7!;0%ZEra?9s$v$HP{?QY?H{R{S`pm4m?lD=Njt?Aq-(ND5xR_Ve< z3$!!0QC67a!!W%>pSZunzrKyKj8?rGmY@qtkGHK5!-C z$BFM%Y1JL1$ctYKem2i^c0T=SV>&$3X^{3Eao_2S>&kE{CPF~snlLQb&0Nhgj+u!L zFDYm@yK2=-RfJw9*(7qa8%1%jBe*;%)SxTs4_jdgMI)#Rcmw?2EshEch$2+T;Zf@) z<*$8qL4%PdvZ)sSL(j$cg188B281~f4K1UlX|99uf}Rg!bA#=4btw_dg1pN>#7;@$ zgW#R?Iv-s7x>jouI>=(_*h*0W$+P1a^wW(|%($T0Jol783Uf zFdGcM5GP^AVOT12@`muZF6?X8Kl_nBYUiZ*DISBas>=Fy1-CDBr{U_yw3-jQvUch; zto4WG5Pkm^Nc+6oS5+_~8o221H=Ud5EyJvOP5DPRXKUa9(ku{8a!qC2tPyFL9v-;} z)P3@}WbzAmciKO@d~8p^6~gimW3A)Xe~Hx1#~Cgcs%-x9WUApAlHeX3JRk*nd1$>< zCCtzCR~JvoJHgYu9g$t*?B6{;U{k$bmUufXmHt}bf0*aaq5&S#f3`~ykQlQ)zIi-r z`(XON;=w%6Z`hoH9NSSA-E#m=-20TCu6boTbza37e3>{t$iskF(cJQ}xh$e3QL)+E z-$UxTo)ASyy>RB!-a=KhMP!IZZ;0HZw7&(6%QIrUVmS#L4)qj4CoD%}O7TsLge7J} ze`q7~?SY)9!E3#?+1#NDo2nDaGO|y46uI$P6$)%NYS7#W_FiV@Szrb>@96M0&vN%OAg=eGSVGU&Cpe8u!3p z05sdx{d01bIE(^KS*uWl%*QRLm>!iB$p?gRp^I%S@6D&rbIOjJHRr8XG4)&SSRts7 zqd*;<9-iJA=~8$0!x3e|L~E!8U4dq5V{?cGqkwwtbeKw(RI`^a>x#CM2M_6aG!{84 zswk5(8!E@9^bC$@MJV*r6=c2exH046{c@5FtKPRAy;(Q<@60qd?uNvu z%oZB88NEU>8XCEN-~z5?TxR>-WI{1HvZ&d)~HW(QMSS)>*Cd+We0baC!k1PRe5_ zjOTOhP%kpSA3L zbDr{u3Pn&PAhrJf=*Vjeh=%r!h<4fKhkred!uWRy8ZX*m<_2?rlOkRCBvtN-VJ)!xvJ(@aPaTwPrsE8? z&z~HvQ?je8x``*)Vrp?6$Nb@mab%v24$|$=DotfWeU?u=9V&@0C^0itL5Ga35BQoM zmNmSC>&rTYIkL6*a#K9#QM6>n&2@QQEG&SPI)y%+@Fr0*>xNEo-f+j2$xK#qSu)S@ zvZx!1i0dp&u+|XEFW{-Swm0pB0M{S5Z#L!QV=35b_soviLlzC?lF zVd%T${zdX=*4?bIyrfnlAKlC;;CH|ahB8uWF-#kOt*XzxG&IM|(6SvoI!UY+BY}F~ z!BeQIS@QDfZQ*FtfKnsF3n@Yy%F><*48W}W?4jw>|8hoUK_(U0C;X#)cqP8rg!n`# zbLFh}2Mff{W&KZ)8|rtynH~imWLoUp(D;;;-j^48OYw{(jyCLAH?~92dTjf!!CQz+ zH#=xVny95;(0@%lk*;&ngA=8WN{NHBk{P)5f?Ke>g2F@;CM#oUIN-w@ORCUicve9h zTQkD9o&{*AXhGN(K(?*|%iZ?K#s3>ltE2QzEpsjQRsvUAp@7foYwVugvOslNZ25%x zGU(#uC@O%rHRn8x8OxKqeRICn$^=!8m`-Iz45kAa854*?MByit0g+cx=U-0t(j{lW z*KB>|L!6^};BVde7C6Kd)85Bs2J%^KW=v1`nhK*2hfIi24%St^ zu+YK;J1ZOE2&-&zZH&tprM^KjehNgS+nB=pOz3d~MroKV}elU^D#u z{oUq3hIMrVkh+K)aPUA5h1r4EubwGxc-uPnc&_=99+Tg&=BaOmF}<7jJ?(WD3SI5N z@B{@p*NV>!3O^N7p+~5Qknk^&&m&D-l@7w{MD;|qO zH(%czYq=A7tmH{=&Bo}$hOoq>_eR(Zi^07^a5kF!tJfMwTvV`eWqqnhxWD2yatGm6 z*lCj^zl22wUG_zW`(}K(w$OX#9n7xBL0!dWpnUB0oK<*tqnSH3%*+ct@Zvp>fIVxi zF_!o=TniK8eTAoD-~1UwN5}S}3hfr8_jsWS`zGR@+b(pMz~L4?YTm<~huAXF$raIy z#-vm?rbhojeYzXHYM}%^+S+UJbgXGaM5laA5pfihI!+L3 z)SfrigO`@*4aBCHdkt@9OiZPTAIvXrzrw7uegxfiI2UIkj4@!~;%aY>rOFk@R8nMB zAj;k5C9?s8zIPLH@o(CFDH==WGonJQaf)%u-vXz?PUMk)AXK(p`?KD2Oc}&p*6M-z zqS^aGDu{%?7_Ql)k(piBa-Ge=BeL@JEwb-Zo|E@ROz{uVldF(3)N3Wi-h0E{5_$rD zDvM1==xiT7ns2wFXpnb3ta=Y>vo*_R{6e9ss)p~~7{>Cmed}?LJs0mj1YZ8h!c`LU zG!-syxpzf@SjBQB;1h(FmN6Ea;~$-aSw#CJBTZ%alK$JbJJg=%ncSp6hubD8J(jqj zeq@ro?pU}TKysb%+g5HErn=!O#>1rwk~zVL;;ge4ozsAkXB#m+!@Vmq3BSzWwWGfS zs-kE9`^lRAda@?=@Gv!cNqXULee?_@>kBs`mYx zAr4`sf;ah%NChoEJY-4H;V1|Xj~~JfvGP9>ZrbQ2nP>9{1Ge91O9sNk&$Jh79^kf4&8A#G2IwWgB7t>tW!n^oyz5MA1|Mz<0YK*@HR)6mOHOP7DMQdu|&@(Z$rdu%=QmC3TFAi2* z^zc``j=5`^#@(yqN97w2;a}NQHS$ypWZU^>6js32pH^G#-|IVs05`~bocUAWLe^as z&>T%J6HlC6SW3WKKvd!^Y<5ciQT)sQ`&RfdG2GA36D&8Hw~*17ait;xQ|%<{iplM* zyCsqXr3irm$je~0W`*}yiH8R~rMZ#OXZ5Cexv#&K`Kdv#A$MmHw^W7fBc|H~|x$+oaUnqQ;RNc?1RQSA`8X}%d*^}gd={L`C%`q8fpRbcI#R89Myi3sfC2Z4Sn zzi?b&#jl2ohFeBHF50-Hy;AtqLx~c(^ezb&Z6wrzP4O>pm)Yv)uIrc$AG*_y5+jdm zmHhsf=$-KHzS$0yZ`J^#0l51zfy}R}OV4fUhYnc}2NGYiOj1Qqvtd;obSDMwTLr^Z zYt?e%d9Fx3BR-cA^%&6+}=xVdPt=OxY_gSkl}eR$M)alLlIJH zoq`x0d?>5Ib!|mBG=A7j*RGkK0#Ciu!(0vy$zn=dH5*=uRk!p>(OT*G6mB#wZqSrg zGh_A+;~jcx%5Ejtf$yeG)kK|PVRc?KXORVUT|)+);)IclZ5Ky>`}rHZ*BsFBg*yB# z!1*Te&T1(!kk%w27$)0l4hWaLPr8s+ii-8+Y?N6b&a85(9RQ^)M{*yxk2LdkEX z(m!>-53HuO8WJTsj5V^WeW7BjFJp%nz>W7nDG?$myrRPZ&B$d@SxIiz^k{Lv1Hh^r zmp|7{9I+d>y!jbb74bua5xw!}&>j6t%W?O*q{j?GDW_LbmiIh4Cy;qiEGiIOo%J<4 zDdy}^iI+1e)BCf@k=_HD!*Dqp=&thKM``>5R@?XY$wljq9xAITs}6faUaxOSX2MBB zy`B+WwyU+LFxjkkh5Ms>U|;Dzv3hSm0Ho0_bxC@HCa@Dbnv?{7<1Yu zWkWzoC^-`!SNEu5!4+dAAh1Wx2KFt*^mj#^xFTzkDL)#o6;{51BaJ=OU`st5ooyE^(Ss`Do!;n!p z(ELiPX6}R`07#8`~c!ru(Et$B*Ja1Ly%))bfbB3g>hA`T4K)9Rm5-T6?iy z=RlDion@sh`)c4RjlYdfN25eMTxm+m9V+OC(tnQLabAo2kR<7ko6D`m`7}gBJXG$o z9{k(%3-%}jYitP~o^bu^P(|_h!JT#)4}MLv++#u2?MLA9*KV_5$wm}!+p$LZdCgY>Sbc-`#1nUQX?DB?03yxgXeek&Gp>f~-iVxjua$RN%Zu_q*IS!9?ZkGMo z%z0WX)K>8>yGU8=Tl+}zd{7Zj$iH3gg|psJO>8&C$U|mXY;<&My!8kiUI+1Zec}FM z-I!E2N4}|l!(7-58m%9I>FsYiw4!Ey0yC_x8>se@o+bEF8=m27Py2D8>NKDlF!psA1-(9ptM#{X31Lzw=jSPA;ftW(w zTuCzNhb!VpGwOHOOrm7?Oe&qukIc88)vt7Y<@M9u93^(}?)3MDe9b++UzfKE-+j0I zO13r92`C`vTi}H43HD3vq&ORhq|=M{L z&S#gw*Y0cB7AF@H!|p}BtlV=IbCg5T7#E8em1IQOEqvPwBsh@e>eAP&PW?z#nfJ9i zy}x5Eu0f5udZ!pEx8+(j;+qUe!_TXD5A+!attd-c19NL2X@qSt$ zXyO;bKDB}_R773)iZ}iRuK!mP6&^iW=r!n9EDT{GWi8d#y&0!Jwwvpuo;_CB8Ia%S z4lz}NT-mTG8EUn{{4L-{5K$|dbw)t-vj2yyFx6aGGJy7PTaM#3WR z4hmIX>d^77zQPSu${V+tcO}lTgG*OqYomtKoGOgIbnR$AcI}8cMx&M9qV1%GzU3&4 zjt<+R;X@AB^lAVQI5e;1{UY#2p1*{=-|W^6TsXK9IG*)Tlft;pB`0)JUSZa$AFUy8ul{x7R;jyFJeN zE6$pyH0;^XPyf3aDD{b(}8&5+4*&P$3PYcGEM!B_POh7!@pv(IDXz@VqRSVYTEewUiy29$}nOIK;YehDwO|GQ= z(C5lekKe+n_@!ly?1IrM3}Skvk}o?83NYKKiu?3SJn|o_s8Rg&xi4%-baVH|{9m6E zzZQM);eT5EuV?&)z@0mrbI{Y*^ET?gl^5{~Jf(KySC;JYf87H9M~nYisAzHcfXba4 z5qG=Jt(PLtm6pOF`u7}VyNT!Vx=)ZmY={;p4-PHHx+VkXK~ru8H4!I8Z^aJ8*}Qtb z%QW=MWm+@^4udCFc?s~WBrUvUnE>mzYRW+9`a|jWY1iKs8k>@3{$K!56d1V zdIs0U`l!uK1DmD7;DMx{SY-LTa^s($-`8t)Gt=PesmJrqQi!21426ueEq4`MiQL2ty8(?^Y_jYHwKA`&r*s z)s1sJ)`x8tpmHSEpJcZ#7c^0tNVGO%d5=gJH!JB?x^C;Vr%#^a-y)!f_ugHjU#-qO zYR;uPQ*kJU54(( zyL^)aFdvSnpu#s>2;P~m1NHX>E%^+Eo^M+74OjRj za^XVUV5>vjvt6c}0T+IZJlJisG>ti`&feOTI~+nLOP-)4d790n$H?%au;>41UBJb18E zH5o~3Q+fJb;J-%%a^Vbp`JPMiFBqrHe_H>)aPmKljC#&xC?GsEU3@EfC1UfHRdoj; z#8f%Yt#oW$!7JKxEe%ib(qe&@MqhRvPW;e_4mkn1oR)M0!6IZODqd~Y3|g!jy>5cr zJ3QFvOobOYjrMTE-Tv&(Sh>8czgjSw5|Jnvgpbc?FI^~FH1;k1R)`N9zjLM^h}h|9 zXr4B!6o>RoY-XFG`o!>KBdm?DQs17Nd{9l!+e*)E>5g>kR8$K}x`Xbn$BI^&6WQh` zjA4QZRA*)b`85(-Uqu|+lwl^=%t_l5vWPdT$83NnrrZ0L3fK=|TYMrAv&DYjpY+#J zXC<>Sl7pjL*@ON2eisF4h&TasI(G6F-uFK0Ke+R^zz=8~BS5(p9#U6*PT!4t6r>Lt z?n?y(5R&JgoOz;!hV;zeeDRa0L~-5kKCg{3hzqHk_J!r)l$u9(`R(ygs&)~u_Ax@< z_?>@!#^%GJvI*NKK_537^`4s9c$~AmX6Eo=hf^8Ie3g8lJtp*c_XR6Swb(>EQ%BBg zsj}Iv%fy8s7&J>$z8tjmG}SX)?3G7!7_wUf?DfKCq4Qko>P-lAEzO@NdPfo`0|4OYm1M63w%-1hj=b3T9Q5AgjX*aL{Y?e z#>Z3oIJb)#UpTM(KVmugyZymIFCes-TjQ_7-x*&64Kjw& zo9t`PV*!?A?YLWt<$EsIcHLUOFT>%mNuI#FgW8=B2|NkiS5RF^ZU2jh_3k=anus5S z0{iR88*2jlU9vW$YS5yfVigzeQq}#f;N5V`Ln!Ns3KdUFTecHV?*mk=H7}d)8LIQ( zxtz*Fi1VrbV)473?v#@VMD~5-RooR%duHp}E=759gQLA!4E)c8s*r(VImQiSr92kP zt-EJ(OC<=6m`J=p$A_)k;mXGv+|1ne8Wty0PA%f*0n~Fm!L}sQXN%Zmq->;Js-B>H zDv+3)&xtg*r-z6&HAj{l)DP|K8|QQ;$lOOq|2hzD)w9r?O43}12`65Rju{Sl((^DU zF@JjM(4uCsS-8Ry$7KDE)vtpTODZ=91zxSi*~fF?9W)F~M%|2w^rZQe6@AIZSIwrxK?4^rFKiKF#~IOvRgyPV~7Pi)_vJ4mWn zVQ*K2_s|Rb3a6KSpC+7jM!qWxz3m4u_3o}r@KEHv4V#@yNA6y~jLW4N;!yaXr^-?D zIw>J&=GFpwr_9afMf8Ig^hJ9&Ic=sfkrT9*q`wh7^dn6t^Lo4 zYKou{GNnE1KHl)sJ2$Mo$6zcjCm z-6xIz#6bOHRIc01XhIU&MtsiKT7z2ii-eh- zSxVlWJ|D~xT0FTgeE0-R6 z2PQ$>_^v4tH5h*GYNl5{=-JC-m{vE94)FWSGBS#eoWPa98ni#1dzmEt>69iyD+adY z^;X7-7GvMI6lU$s2sk-lERRW!(PdKR?!T9*vn#h$NOZqB0*?=c&0Whar+98Od1o5O z6i667OMbHZs4Se{U-z-%(=02OtZzO&K3*L)YcPLn^jR<>7yNwwjfIlY(1C51hMP=e zcl1x3jb{pkg`!fY-7o0k@xo^KIsM~!U1)a*2JHvJSQzs*o)Qm3{ zyV5kR5oXLQ>#C~g$QW%^z9<`L8n3`*mSlW-pLUJGqXnDNedMxo`^OPh&q2T~lBPsl zOQs_;fJug<6>UiQCXY-4x9{aSR>vaWAo1xaHkt*>o!cE3JHv?kW#b(y26()#F7nvJ zKt;JUr;Guk#0ZDRjHvNbW&SNhQBQ9t(JL?|MWv6iiHn~zPD=N84Yv7#R_cLPlkR1o zDB+MZRpwi3+74d$lEf@zblZ#_T-?=`uDPfE+KugEy!z$Hq&Ic-;K3Bm|9th6Kv>F3 z!6jDQ{d-sa+LeZWDI`41wt8af>=veg^4yO{5{e4`C5pKoK-}xc_RYEHanwi%WVN&9 zjX5jUOC(C z8GQ?6_DOpVzs`+VjuWv4B-VN5rjb434N`7S@kGk-Dv-6H)bQN5oHqj-BtfFpM$Vg0 z0xz0Cme!RI`u6xKGA;29FM6qu8(cfC*;Ly5-Xn8%_b>0%|27f;_gBitwPKqDDRg&Z zn0vFs9^aSI9oVy8ybIj?6ykdZZhtlgf;*f%k?IsWxWfwWgZ{h|J09>59O8(>CyoD# z{6lS+tc-y*occm=PWS&8jDwSq$U_l;1)(g`_W5;ym){oSatheMwn}5eEnB9cY`lk0 ztN|^pLYU1@EvEMWgrek1D0SK`;mf@xP=R>|=I^LOYVdn73J1JjsUyNr)PX&B#4nq_ ztf?qE?JBEPLaBOH+oxtrUw}@K*G-n+e%?WrAAr=V^A0G*^}$Vh>`qz(0G@y6PC#G_ z^RWKmp8x>>NGgkTrgH-Tc_*G5aPpNpqj{*LpP$WtPJozqi{Dh|cm#L0X@`x3clc1< zON(kJ2a&gXGV5jb0@k-oK`dW<{<}x`C7q2(MP|>vz+)H5u~qFg*)l~ zS=)LWf>$0@%<$pKu7@2(af6mGh>GNvG|BE6?Sf=f>$L{EdVLG5E2Q6%jhd=?;F2On9VqH^YnAs(AeuBEd|C@1|3ZBSmr=zw2j<@(dR zNIlDt7NQG{5mNx)8}LyRWDp#$pueS`wFr3mie{u#B4<4;FREbhV4>U*7`>|IpXQU5 z=wo8EkmEf$iit``m*pVtnZgx`TW2Klt(j>FGRc!cUCmhQ(>Yu5NW9abbFL{b8Wc^e z<6C*fAB+!9K6p!%OvsUZ7G&Et&?gO^5y0e<`1?G(NT*+5PYH_|7(!3Oo(f8)G}O{V z+*sb4Gkw99RiENvw(Lr_z-B~ScIkmCZ+_V@ND-yyR@_E+TgxX6gK%?OZ!MqaL`BQG z*8SA7OcXsEUt*&2)IAp8Qb>cIk(JfS*OG)KhBY)BDaAAGHX!^9wlzO&9?&W-QfYEF ze$!6y7{Z(~RHS9chanveg!`1u91xLbseR*9zAsM~+nm6c?dx+LOk8H%v&2@rWroTa zf$LA@`goz}ErG&1)n>KR{imYR5_cDB;4ID}Zjw?K2CHU*rJ%~PQgP8{L3~PEr{k<* zXJ408Al-48muPspiQx->#ATK%UHb-uD0cXn77USM?mp`zb?M2Aw;<~$zN#9DV)SdS2G&vP{~EI*0dF zr#z)9T4B+7`)Qp}w0jy^M^Q_#)wMHVt;RX!@beWdmaQW3p@ixCV+uqQlV)v?(A3H* zd6SLWAGsgj8-maH`e@$hyA`$*m4hTAxbm?)i{Q{j+ z#NVDD`12TZmsxA1LmiRZ3>xNmCOQjMvfu8!A7QL?M+gY~$=qURgt0>8A#Sdne6koW z43Iklj(jfWb{`>&11-RwyGh8)(~%O!)n!=|mGcRJi>5p+Eh(TTlIe1yyI3t^{pj6U z8`BzPucDIN9kfArK=ipa+hk7TX+XY#cT`fS5-c9qtl7_d_-+R2jmg}&gl8AXO#FoF zwg&z1Byop(cfCjUY^`^t%OQo?TPXCNg}aX$4qk6Io5)YPU@v~zOnBD%@>c*F63YwE z8>+aw-Xd};VPyP>X|*snpNY3JAJg4fK5t`osoKwlw?sO}dyK~+G3s2QUxCoB(Ikj1 zRD(W9mn&#T`d&zOun+=%GUgPNlS1e+XQ8RThJ&FN!|rI9{`6aFTs>X+&zL>FsoD!J z?=6>N+Ebf+98eR0VA((Zp_@4;Pk7Jy1CjJ|V4`A}gNmjsH~ zEq3tzQImDFgz(s+8@Jb((!)+^g3^^Lx@6_@Dzm;V?dtPe0Ksuex%v#(e|}N}uAoSL&_ntw1^%izq@jF?dv!>S?rIhrz0x z_71zfjZ*+YFzQI=W-LJfA5Ba?B0l=j(Pkp>gaD_ooUm7qf4qh5R`qWiAk6fb^oE@= zGp|ZIC+#R2`jOio#Am(=exct<-oJP28nZ2i4A*ou)E#TkfiLc!3;em80|KoECc~W{ zIrmCRn;st>kMq_q==BbRSv6Y63UqoO(K)5HHG8_-`^?-A(Y;386^M86pzajwL=PGTx+dB6M;__RFEW?_@g*Rhgfk_aH`(;RvdClzho915u-({CI&uYWx?$=kkmZNs+ z=c)kZ$m5v4he%{DC3kukX1y06d{yiw3 z>Rd1);xL30oil8J2@U~5EB+szLhk&)B~tjs8Qewq3aAh#KYH#kT~<0+98y~PdkFW3 zL9Y`t9s{k$xZdC|{JR?T$p_wZKoxl+Ik1Z0=6=&Hp~hFi|8jDm&|K^FYC)pjXZ!*etOJRR#n5pM9B4;UGb-fKVzb z>no17Z?duuq@EVpo`4?Pst+xvPKF0*tcrm{2HJ)M?OO17Z^Zs$Y zK;TNd|0y>m!SSJ*Uh}wN?yosRHvftV`5lcClq*~Z5l*!n*hvKtX>!yUFf@L5?;F}B z&)*WZ+>?m)7%+CCdU|v&PN06xCOgZ-hOWS}tLgThrqv#3H~biK;A59vkVo>bMP6<- zU_5=rs=@dNKf3rrSyP;Wm0jPcr&;aHIKB#dxz=}+K_}hqQ&D)4&!Q-akB+m4Ld)CI zR>OI$Ndm>Knt@?+;;sBgEU-@cmeqx3HXu7oi!aiazLlHnSR^>0th3}1;^P(Kg8)0Zc42lB%E0T+ z2%d;TLVSQRduPTSAc7|n5L&}pdBDR7!-4By01O2q{=^C6;@rxB^8LDT$o#5Fp23Xg ztGR{W&g|0De|r?MeWYBuY!+CHTpddPmFp_ng25h3BmzDFTRKxyqceZ;idSe&N6fx8 z`WMcBKP!3?R*I_`EU`C#zV zZ*G&gPNT*!zk8ruR;oWpjmb|#(Zaux(MP4N;`Q=+3ZMe-= z*9~UK@gnE@x*raOzutlU?t7*UjuFfjZu!4c|N^nN#y>aJ>E7;V0XVa`u%VlF5cR z(EdT=?8}?dhc@S2;|bXysV(#L7LV7jQMe?uL9>V#b&>mT0DndP^R*H8;_x7T0u$D@ z;r>@3a2fH@<36h{6%?BX-+%N7-!$)Vac(qL*X~{ES766J&*xu(D@oHAjVEXH{}3Xg zs%JZ$f0s%BK4ETr;@}Z*OaXHaQEXK(#R zPDXG89O=3uOn()$*+PF)_r!%e%vUe}_ME<_=kI6c=OJ~$}0Omq#J3E`j|VFue)-Y`itIZKwk~h2WUPuEWR&|%CKGn z%^&C@68s1|3?e4_5t2$x1$4%SW#E>fFuVm^6`N9WBJ#--#ES~1pKWU}+tD2tv37^s zWfDRyVU!(8_jP#ihjLq>jL|Mt?R|-PpL+{>Fn6MP5F!Ux5!)_HIU` z$*g5cXMDQFy&}rR(r+ydtySPib(t+CJE~qb>zcUnt&>B1n|Z9vO8(KUDQNiN&awpo zZi1plpgzI-m*e9JXpIvkwdOABo>z0Cumy~mcD|ZwVs`pf&EmZ|h%`B3NEKA3z2nAt z&zQwyRMSqj%8k5n>nBX~GslqZ_sGz#@As0ggRG}1+kB|owmT`lT{tSN=b{Yxf)$b3 zteK&SZXqt&H@%RAg}K4~A|H-smzTRfD{`(ze#$e1$0t}Ng>($h26`cL$&&0 zu+ZRj`vf#|3{ZD-Qyk9FWbQ;B2KadZ$WOqnL)`!HR;zBQgXVnZ7u~%>{#8S%nz$~> z0q8sk!FDrM{rj2|1@2>J&VMdFaP5ooqp%M~zU1-L-vFP4>K|Ns^!dUjJ^s1DI-xP# zK<+JLP(>m{y9Hxy27o&NKRENt<_3T)h!E({f}(gRSt~k}f2vafvN-bhTLv5*%QsES zx2HlDD~#+R9DcA(w*N9Sm?Nl59J!>LbCdo4QF1$7QP(>DY5=?O{h<4o;|D?*q^zj^ zQ)117?T?VHrxMdy+RH`TKF8%mk0-@`d4@9I%+Jgb&o6!POAJbm%Id?i&6q^KG0$|{ z=#cCL5K=i}>P6Gc`Jpzik%fo8ZxBSq)(uMNoM*4Pg3J5QSrL?XpMQqW(OmxqSJv&x z+}I9kH)Fpv;tm!xE_<%8?i3%&4cmz48c70BE z$L-i+n63b_i+Dm3y9!SsY?SgZrso!`MF%1AZR>(oKHhcZ_0A2YBx4+fceeTMYxBrg|#3pu&O!vt# zl!5E5Y6H~=vWoBw@hC=)1_yB4&0UY1c`Jy zvc3ZA_Y8iCBY{>gmqy;SE$nz#vNYW+RYSVxles+frDk1v)Z+B1w;YRM+vep&<)cJxpNrJOgr7W|?juTI+Nm-4r==HHe*Ik>1H}?@`{u@jBz{^G`L<_8BQsep!KH{TlgUU zPy=d@qo2MjVqE@AfA$k7TgtXVwX?8Bxs5K-88Ef74t~p~#_yj1H@uHD{h`t$U9~R* z8Ok6@B%g!6Mw>oNuGQQ*tU(l|svS;DHrfE&o8T&n!i1y`m7{jJg8eM}`YwFTxLI!&k@Wf2dl}Q>;D>@}>!>^QG{^qidu+Y} z8zQ8?_tIRjm&PCM1Z~4Y%Ra?XCm5A(=^JgH3o09s*hk{QgdH#R#qMf!|($+wAPa`1ZZpW7ky_w(0$Ns}+g>JZ2x7PP<{Px;r zL&_-Kc3j^%6*Q%|Yy>|?^L`x@)Dz<3R{s_FrQoH9;|+4)82NqEZHCpzP4)PR+nHZx zE(nIlZ}|#j6#a}(9PYkLYTE8d<8n}9yj%>9{kLn7t0>* zy-V_S-v9LPe$&5~^M6Zm{(9;E)zNkJaZC0D8pB<_z9{#rcd%Irk z1zV3dSPlNpHY+&5P1c+XrXGVOOr?~vo^AgX5f7Bb_*k(rq6B)XWz5@EMY7kQ(eBM^ znN^H!o<%9%?d#l1d&4fq33o=JNy5su-W?i+DQs`sIu)(7`^?vgK3KN}qFZZzKuRTP z{Y9o9lVB5*KXOD^HY@hkYt}5f%XUL}#uW+wgwz;3Iw!Jkrf1#7=*-u3wbv6AYIh^9 zXg6RmvaVOPlj%fQwTYHE9W~WPy%*Z zI8<$EmZeg{N7nlnB+_nhZuVn=w<`}a8vJpK|&m~ z(=ui-?j(<)T_u|CXbFL7jh8x#VT%Gfb7^-Vz^ba47iA6!Y&5q+OuBz+y?OzMoPY45 zK*7tZ+-dom+5S?JxSOdOy#4|DY>V`3UH7^JM~^@7e3}+FeBvsoCNyxD`2C|phRJ6; zZZUR?e~K#Z*;|9T&A{y>#vOd zzpkSuI-(;g)uhM{Jqcaiv7B0ME{<$`RavCk_olKL!M7R!D&kk$&ra<3HhIXHVY<7O|xo7NGb`!tQ&C#D@&9I6FbjJ=l{t#o_`IgVF4seW9Pfr;XWSqUwruWB8d-P}KYGxC_0u-pJYRQ>*WbcE@>w5E%|t z-ik_F-6%>H=(*Bpmdk1#i<@ssuT^P4#F!m6q6DS1sHUB8OURzRTjaNzfnZGsAN2VZ zC978!26-=HC15FC)PWk>Y!at5&p<*$DiSoB&~qNQrJmg*U_CZu#8W?Ctkk(T6oI&z_03!h*_CpOF zy`!N42RE6|7Re-SzD%}#ND?=Y#}wTkCyEMK#IUmCuH#<6T~0nWj6L#rjj#4UUSa;* zA!{->D>iMp1~WN=Kv9Bw7U6-hlv!--(cKrSq$ZOoOV`f4rITe98Bw`3t*xw$vGPSm z+onzJCFl1WoH^RA&|uY)kw+~GTv%*Z)g-~KbJp>&O`-nC#O%HEy7nlY!&QB|t5lrp zQOGE$HcFT&HOZWDw3qDIj36p&n&y#hS|LS6j!5bR)7d_`(7$d$B=lpDqi0B2o@ELw zAWK%tM4NP)rGy!R_+IGKo%>lW+yBS+$Ff^h$~ z8Rv%H{&kumT32$=1dWo|>>Q|5pZAoo`cig+t1Y}J`?aSRvAdv*oi1P_WJTmxVB19p z?HYbtp{T3TPbmCQa7RbsSxJ)u3=vZL=2LuZe5ZTog9-8&Jq63^r0qe57Yw%F?z*H54|RpMJE-p<67k!B7!Gg;ZWA z==!ImWr@lbQz=|eBpBf`X1N+7elq zn2n2)yfbUKy-*}K*2MGf z%W<&IBp;~oFmVW+N&Wi$CAeO6MutG)1>hIdc8+Vagc3BPnIO#3(da z{ewm`u*>(bde#XRe{6+YMxz=xqWjc?hTb@5$Q2gkH6MlKx1^K=cZOda$$FkW8EQR1 zNXn*)yr@G*SyiF>T*FWL>vk&SCuDzgCyO<7I3?5=?pTtTQhZGcb8+p@SY|vY9r@|1 zqepjgg*RqeLf|05JUBlBt7&3;hXnPgg7<0et68h<`$p{9{H0}*wX0Zx0i+z_4vQh( zD6%Q*JWg{gEL>X>a{9G*?}_0oB=y8G$21YN_$$K1y@k$aA2p9ex1+7Q+&$k?PM))> zx5jZNjG*A~XH$!F)fb*-NEZ%@SQNlj)2I5KsPe2pWqo16=-B;9H7D}BRg|n>3yUAw z4L$9R;BMkk6-G4)>&9b0VOabQY}4{ngLX!OQz0_RY#&yS_^EMtppsRF;mi2;4Y)V< z`5-G{7T(X)NRBzHXV1Q(ZDkuJBV4Ie8$f2Fo`GDv$%Mxv_N>wZVF6Q>JEN&Cos;vz zup4772xdFU}4qC{E0Y#64JmGKTLZ@a-TUAsjQLom#Y zSI4>2)o>x&mBB58#KO1HXhp4{#O^M&rhVSZUDU~Yk3Fvk*V$N*#q=~&W?P$)<}+}{va*T$63Yuv5)uw-URm)zC-NVM5TK_Iw89D!mZ!#*@t=yQ z_o{6*($SeqykpIW;d{}GQZ&b+ho7XhO&VG@`{-?RT#HLx9pLy0OlRvuZZuSTG6l-CE!o=0eg1muhFb-G4#tXtG`*rE<`*L3qFFCqAv}r45s_3}m2_xai zxEh?ewZGwuE&@$xL{wHT-rJS-Si98hv=WS9(tg-Ku`f~oTWusb$ zQWA2g>cMoZfNI8hdSs?hbcGT@Q{2ER5=$%0Z`-Kd;^JDMsg)5$M0sFft<(vb%{U4? zH$i5-!XO>KToAUnvEq4DmYAcm^fX>K9&9qBeFW`&v$X06zoSw}8z%Cqo!*&Xnn(HU z_MZ(5@xj)^;pHtc5dc?I!s}?u4zIkymeEE;hl7WmEp=AKu*Y#=mCBIfeVe5?E=bw+y$U4;S7BSJI(q4l^ oXSx&zce5cY_T)ZGlL1I_+TM3pj@~ literal 0 HcmV?d00001 From b029cd62be4a76504915027180b2d6000240c1de Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 27 Nov 2024 07:55:57 +0100 Subject: [PATCH 2/6] SFELPHOTON-1409: hit-and-return, yaml as param --- swissmx.py | 128 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 48 deletions(-) diff --git a/swissmx.py b/swissmx.py index 7eabb46..3a6c690 100755 --- a/swissmx.py +++ b/swissmx.py @@ -100,7 +100,7 @@ class timestamp(): ts=timestamp() ts.log('Import part 1/8:') import sys, os -import json, re +import yaml import signal, subprocess import matplotlib as mpl import matplotlib.pyplot as plt @@ -1516,7 +1516,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): #self._inspect = self._grid_inspect_area #self._inspect.setPlainText("") - fast_x=self.tweakers["fast_x"]; + fast_x=self.tweakers["fast_x"] fast_y=self.tweakers["fast_y"] fx=fast_x.get_val() fy=fast_y.get_val() @@ -1986,6 +1986,53 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) self._tabs_daq_methods.setCurrentWidget(mft) #set this as the active tabs mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(test)", "Grid()", "SwissMX()", "SwissFEL()"]) + #print(psi_device.shapepath.ShapePath.setup_motion.__doc__) + mft._txtParam.setToolTip('''\ +additional parameter as yaml for object settings and motion generation: +full example for grid() object and motion settings: + size:[3.6,2.664],cnt:[30,22],fiducialSize:0.05,mode:6,scale:.5,ssz:[7,4],smv:[6,4],sdelay:9 +further examples: + object settings: + for Grid(): size:[4,3],cnt:[30,22],fiducialSize:0.1 + motion param: + for continous: cnt:2,scale:.3,dwell:100 + for stop-and-go: tmove:10,twait:20 + for hit-and-return: ssz:(4,6) + +motion generation: + For details read: psi_device.shapepath.ShapePath.setup_motion.__doc__ + mode: select one of following modes: 1,3,4,5,6 + mode settings: + all: cnt:1 + scale:1. + dwell:10 + 5: tmove:10 + twait:20 + 6: ssz:(7,4) + smv:(6,4) (delault defined) + sdelay:9 (delault defined) + +object settings: + Fiducial: + no param + FixTarget(12.5x12.5), FixTarget(23.0x23.0), FixTarget(test): + no param + Grid(): + size:[30,20] + cnt:[30,22] + fiducialSize:0.1 + SwissMX(): + ofs:[.2,.2] + width:10 + fidScl:.02 + fiducial:[[.1,.1],[.1,2.7],[10.3,.1],[10.3, 2.7]] + SwissFEL(): + ofs:[.2,.2] + width:10 + fidScl:.02 + fiducial:[[.1,.1],[[.1,2.2],[10.3,.1],[10.3,2.2]] +''') + mft._btnAdd.clicked.connect(self.module_fix_target_add_obj) mft._btnDelAll.clicked.connect(self.module_fix_target_del_all_obj) mft._btnFit.clicked.connect(self.module_fix_target_fit_fiducial) @@ -2097,8 +2144,9 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) bm_pos=self._goBeamMarker.pos() bm_sz=self._goBeamMarker.size() idx=mft._cbType.currentIndex() - param=mft._txtParam.text() - + param=mft._txtParam.text().replace('(','[').replace(')',']').strip() + if param=='' or param[0]!='{': + param='{'+param+'}' #mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget()", "Grid()", "SwissMX-path"]) #bm_pos_eu=self._goBeamMarker._pos_eu #bm_size_eu=self._goBeamMarker._size_eu @@ -2126,17 +2174,9 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) # fidScl:.02 # fiducial:((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2)) - pLst=param.split('|') - pStr=list() - for p in pLst: - if not p: continue - m=re.match('\s*(.*?)\s*[=:]\s*(.*)\s*', p) - k, v=m.groups() - v=v.replace('(', '[').replace(')', ']') - pStr.append(f'"{k}":{v}') - pStr='{'+','.join(pStr)+'}' - param=json.loads(pStr) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] - except json.decoder.JSONDecodeError as e: + param=param.replace(':', ': ') # allow gen:4 without space + param=yaml.safe_load(param) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] + except BaseException as e: _log.error(f'{e}:{param}') param=dict() if idx==0: @@ -2271,18 +2311,10 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) def daq_collect(self, **kwargs):# points, visualizer_method, visualizer_params): ''' kwargs: - code_gen: 0 pvt motion using ptsTrf - 1 pvt motion using trf and points - 2 pvt motion, compact grid code - 3 pvt motion, compact grid code move and wait - tmove: time to move - twait: time to wait - - grid: grid dictionary with orig, pitch, count trf: transformation matrix points: list of points without transformation - pts_trf: list of points with transformation + pts_trf: list of points with transformation ''' app = QApplication.instance() cfg = app._cfg @@ -2322,28 +2354,22 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) #dlg.setAutoClose(True) #dlg.show() dlg.setLabelText("Setup Gather/Sync");dlg+=5 - code_gen=kwargs.get('code_gen',0) + use_trf=kwargs.get('use_trf', True) # do not use coordinate transformation + if not 'mode' in kwargs: + kwargs['mode']=1 + if not use_trf: + trf=kwargs.pop('trf') + points=kwargs['points'] + kwargs['points']=kwargs.pop('pts_trf') + sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=dt_misc['time_ofs'], timeCor=dt_misc['time_cor']) dlg.setLabelText("Download motion program");dlg+=5 - if code_gen==0: - sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10, points=kwargs['pts_trf']) - elif code_gen==1: - sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10, points=kwargs['points'],trf=kwargs['trf']) - elif code_gen==2: - sp.setup_motion(fnPrg=fn+'.prg', mode=4, scale=1., dwell=10, grid=kwargs['grid'], trf=kwargs['trf']) - elif code_gen==3: - sp.setup_motion(fnPrg=fn+'.prg', mode=5, dwell=10, tmove=kwargs['tmove'] ,twait=kwargs['twait'], grid=kwargs['grid'],trf=kwargs['trf']) + sp.setup_motion(fnPrg=fn+'.prg', **kwargs) sp.setup_gather() try: p=geo._fitPlane - # TODO: Cleanup - if code_gen==0: - # X has inverted sign ! - # Z is in um -> therefore the offset must be multiplied with 1000 ! Z motor has opposite sign ! - #cz=f'{+p[0]:+.18g}X{-p[1]:+.18g}Y{-p[2]*1000:+.18g}' - t=p*np.array((1,1,1000)) - cz=f'{t[0]:+.18g}X{t[1]:+.18g}Y{t[2]:+.18g}' - else: + #TODO: cleanup + if use_trf: trf=kwargs['trf'] # grid-coord -> motor-um #(0,0,1)*trf # trf*'gridpos in um' -> motor pos in mm @@ -2354,25 +2380,31 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) #(0, 0, 1)*trf2*trf3 -> z in um of gridpos(0,0) t=(trf2*trf3)[:,0].A.ravel() cz=f'{t[0]:+.18g}X{t[1]:+.18g}Y{t[2]:+.18g}' + else: + # X has inverted sign ! + # Z is in um -> therefore the offset must be multiplied with 1000 ! Z motor has opposite sign ! + #cz=f'{+p[0]:+.18g}X{-p[1]:+.18g}Y{-p[2]*1000:+.18g}' + t=p*np.array((1,1,1000)) + cz=f'{t[0]:+.18g}X{t[1]:+.18g}Y{t[2]:+.18g}' except AttributeError: cz='0' _log.warning('no plane fitting done. z does not move') trf=kwargs['trf'] - if code_gen==0: - fx='X';fy='Y' + if use_trf: + fx=f'{trf[0, 0]:+.18g}X{trf[1, 0]:+.18g}Y{trf[2, 0]:+.18g}' + fy=f'{trf[0, 1]:+.18g}X{trf[1, 1]:+.18g}Y{trf[2, 1]:+.18g}' else: - fx=f'{trf[0,0]:+.18g}X{trf[1,0]:+.18g}Y{trf[2,0]:+.18g}' - fy=f'{trf[0,1]:+.18g}X{trf[1,1]:+.18g}Y{trf[2,1]:+.18g}' + fx='X';fy='Y' if _log.level==logging.DEBUG: try: t except NameError: t=(np.nan,np.nan,np.nan) fn='/tmp/coord.log' _log.debug(f'write all coordinates to {fn}') - if code_gen==0: - xy1=np.hstack((kwargs["pts_trf"],np.ones((kwargs["pts_trf"].shape[0],1)))) - else: + if use_trf: xy1=np.hstack((kwargs["points"],np.ones((kwargs["points"].shape[0],1)))) + else: + xy1=np.hstack((kwargs["pts_trf"],np.ones((kwargs["pts_trf"].shape[0],1)))) pcz=np.matrix(((t[0],t[1],t[2])))*xy1.T x_y_fx_fy_cz=np.vstack((kwargs["points"].T, kwargs["pts_trf"].T, pcz)).A with open('/tmp/coord.log','w') as fh: From 24b7f1292da24df9cbd3b29d2e9be0a3fffe3d71 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 27 Nov 2024 09:25:51 +0100 Subject: [PATCH 3/6] try to cleanup stuff --- pyqtUsrObj.py | 47 +++++++++++++++++++++++++++++++---------------- swissmx.py | 9 --------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/pyqtUsrObj.py b/pyqtUsrObj.py index 3c17aa0..0b53001 100644 --- a/pyqtUsrObj.py +++ b/pyqtUsrObj.py @@ -518,21 +518,13 @@ class FixTargetFrame(UsrROI): def get_scan_param(self): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' - scan=1 # snake motion Y fast, X slow (default) grid=self._dscr['grid'] - self._dscr['size'] - cnt =np.array(grid['count'],np.int32) - xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) - if scan==0: # snake motion X fast, Y slow - for i in range(1,cnt[1],2): - xx[i]=xx[i][::-1] - else: # scan==1 # snake motion Y fast, X slow (default) - xx=xx.T - yy=yy.T - for i in range(1, cnt[0], 2): - yy[i]=yy[i][::-1] - - pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose() #*pitch + use_trf=self._param.get('use_trf', True) # do not use coordinate transformation + mode=self._param.get('mode',1) + cnt=np.array(grid['count'], np.int32) + num_pts=cnt.prod() + param={'num_pts':num_pts} + param.update(self._param) # TODO: simplify !!! t=self.transform() #obj_info(t) @@ -549,9 +541,32 @@ class FixTargetFrame(UsrROI): trf2*=np.asmatrix(((1000, 0, 0), (0, 1000, 0), (0, 0, 1))) trf3=np.asmatrix(((pitch[0], 0, 0), (0, pitch[1], 0), (pos[0], pos[1], 1))) trf=(trf3*trf2)[:, :2] - param={'grid':grid, 'points':pts, 'trf':trf} - param.update(self._param) + if mode in (1,3): # needs all points, not grid + scan=1 # snake motion Y fast, X slow (default) + xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) + if scan==0: # snake motion X fast, Y slow + for i in range(1,cnt[1],2): + xx[i]=xx[i][::-1] + else: # scan==1 # snake motion Y fast, X slow (default) + xx=xx.T + yy=yy.T + for i in range(1, cnt[0], 2): + yy[i]=yy[i][::-1] + + pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose() #*pitch + + if not use_trf: + pts=(np.hstack((pts, np.ones((pts.shape[0], 1))))*trf).A + param['trf']=trf + param['points']=pts + else: + if use_trf: + param.update({'grid':grid, 'trf':trf}) + else: + g=grid.copy() #has not be tested ! + g['pos']=tuple((np.array((0,0,1))*trf).A.reshape(-1).tolist()) + param.update({'grid':p, 'trf':trf}) return param diff --git a/swissmx.py b/swissmx.py index 3a6c690..b9b8db8 100755 --- a/swissmx.py +++ b/swissmx.py @@ -1535,11 +1535,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): except AttributeError as e: _log.warning(f'no scan parameters for object->skipped:{go}') continue - trf=np.asmatrix(param['trf']) - p=param['points'] - p=(np.hstack((p,np.ones((p.shape[0],1))))*trf).A - param['pts_trf']=p # transformed points in um motor coordinates - param['num_pts']=p.shape[0] vb=self.vb grp=self._goTracked mft=self._moduleFixTarget @@ -2357,10 +2352,6 @@ object settings: use_trf=kwargs.get('use_trf', True) # do not use coordinate transformation if not 'mode' in kwargs: kwargs['mode']=1 - if not use_trf: - trf=kwargs.pop('trf') - points=kwargs['points'] - kwargs['points']=kwargs.pop('pts_trf') sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=dt_misc['time_ofs'], timeCor=dt_misc['time_cor']) dlg.setLabelText("Download motion program");dlg+=5 From cc7d7c8e794b466f504358992916ea31171cc997 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 27 Nov 2024 11:30:06 +0100 Subject: [PATCH 4/6] try to cleanup stuff(2) --- ModuleFixTarget.py | 59 +++++++++++++++++++++++++++++++++++++++++++++- swissmx.py | 41 ++++---------------------------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/ModuleFixTarget.py b/ModuleFixTarget.py index 85d2f52..18999b0 100644 --- a/ModuleFixTarget.py +++ b/ModuleFixTarget.py @@ -16,7 +16,7 @@ This contains a Widget to handle FixTargetFrames and fiducials, calculate final import logging _log=logging.getLogger(__name__) -import json, base64 +import json, base64, yaml import numpy as np import pyqtUsrObj as UsrGO import pyqtgraph as pg @@ -161,6 +161,48 @@ class WndFixTarget(QWidget): act.triggered.connect(self.tree_ctx_delete) tree.addAction(act) + act = QAction("update param", self) + act.triggered.connect(self.tree_ctx_update) + tree.addAction(act) + + def get_param(self): + param=self._txtParam.text().replace('(','[').replace(')',']').strip() + if param=='' or param[0]!='{': + param='{'+param+'}' + #mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget()", "Grid()", "SwissMX-path"]) + #bm_pos_eu=self._goBeamMarker._pos_eu + #bm_size_eu=self._goBeamMarker._size_eu + try: + #parse the parameters: 'key:value [,key:value]' + # as key value separator : and = are allowed + #examples: + #Fiducial: + # no param + #FixTarget(12.5x12.5), FixTarget(23.0x23.0), FixTarget(test): + # code_gen:2 + # code_gen=3 | tmove=10 | twait=30 + #Grid(): + # size:(30, 20) + # cnt:(30, 22) + # fiducialSize:0.1 + #SwissMX(): + # ofs:(.2, .2) + # width:10 + # fidScl:.02 + # fiducial:((.1, .1), (.1, 2.7), (10.3, .1), (10.3, 2.7)) + #SwissFEL(): + # ofs:(.2, .2) + # width:10 + # fidScl:.02 + # fiducial:((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2)) + + param=param.replace(':', ': ') # allow gen:4 without space + param=yaml.safe_load(param) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] + except BaseException as e: + _log.error(f'{e}:{param}') + param=dict() + return param + def tree_get_path(self): path=[] it=self._tree.currentItem() @@ -213,6 +255,21 @@ class WndFixTarget(QWidget): r1.translate(r2.center()-r1.center()) vb.setRange(r1) + def tree_ctx_update(self): + app=QApplication.instance() + path=self.tree_get_path() + if len(path)==1: + try: + wnd=app._mainWnd + except AttributeError: + _log.info('_mainWnd not handeled') + else: + grp=wnd._goTracked + go=grp.childItems()[path[0]] + go._param=self.get_param() + data=grp.childItems() + self._tree.setData(data) + def load_file(self, filename=None): app = QApplication.instance() if filename is None: diff --git a/swissmx.py b/swissmx.py index b9b8db8..4c033e6 100755 --- a/swissmx.py +++ b/swissmx.py @@ -2139,41 +2139,7 @@ object settings: bm_pos=self._goBeamMarker.pos() bm_sz=self._goBeamMarker.size() idx=mft._cbType.currentIndex() - param=mft._txtParam.text().replace('(','[').replace(')',']').strip() - if param=='' or param[0]!='{': - param='{'+param+'}' - #mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget()", "Grid()", "SwissMX-path"]) - #bm_pos_eu=self._goBeamMarker._pos_eu - #bm_size_eu=self._goBeamMarker._size_eu - try: - #parse the parameters: 'key:value [,key:value]' - # as key value separator : and = are allowed - #examples: - #Fiducial: - # no param - #FixTarget(12.5x12.5), FixTarget(23.0x23.0), FixTarget(test): - # code_gen:2 - # code_gen=3 | tmove=10 | twait=30 - #Grid(): - # size:(30, 20) - # cnt:(30, 22) - # fiducialSize:0.1 - #SwissMX(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.7), (10.3, .1), (10.3, 2.7)) - #SwissFEL(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2)) - - param=param.replace(':', ': ') # allow gen:4 without space - param=yaml.safe_load(param) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] - except BaseException as e: - _log.error(f'{e}:{param}') - param=dict() + param=mft.get_param() if idx==0: #go=UsrGO.Fiducial(bm_pos+bm_sz/2-(20, 20), (40, 40),(fx,fy,bz)) l=.120 @@ -2355,7 +2321,10 @@ object settings: sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=dt_misc['time_ofs'], timeCor=dt_misc['time_cor']) dlg.setLabelText("Download motion program");dlg+=5 - sp.setup_motion(fnPrg=fn+'.prg', **kwargs) + try: + sp.setup_motion(fnPrg=fn+'.prg', **kwargs) + except BaseException as e: + _log.error(repr(e));return sp.setup_gather() try: p=geo._fitPlane From 71a473d25d52d6858975e0303ce11146520e9385 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 27 Nov 2024 11:56:16 +0100 Subject: [PATCH 5/6] try to cleanup stuff(2) --- swissmx.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/swissmx.py b/swissmx.py index 4c033e6..e36baa4 100755 --- a/swissmx.py +++ b/swissmx.py @@ -2315,17 +2315,20 @@ object settings: #dlg.setAutoClose(True) #dlg.show() dlg.setLabelText("Setup Gather/Sync");dlg+=5 - use_trf=kwargs.get('use_trf', True) # do not use coordinate transformation - if not 'mode' in kwargs: - kwargs['mode']=1 - + mode=kwargs.pop('mode',1) sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=dt_misc['time_ofs'], timeCor=dt_misc['time_cor']) dlg.setLabelText("Download motion program");dlg+=5 + + use_trf=kwargs.get('use_trf', True) # do not use coordinate transformation + if not use_trf: + trf=kwargs.pop('trf') try: - sp.setup_motion(fnPrg=fn+'.prg', **kwargs) + sp.setup_motion(fnPrg=fn+'.prg', mode=mode, **kwargs) except BaseException as e: _log.error(repr(e));return sp.setup_gather() + if not use_trf: + kwargs['trf']=trf try: p=geo._fitPlane #TODO: cleanup From 0fe59e9ad69e5840e3a16822bf37728bee6d44e8 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 11 Dec 2024 15:26:01 +0100 Subject: [PATCH 6/6] try to cleanup stuff(3) --- ModuleFixTarget.py | 51 +++++++++++---------- Readme.md | 111 +++++++++++++++++++++++++++++++++++++++++++++ pyqtUsrObj.py | 72 ++++++++++++++++++++--------- swissmx.py | 19 ++++---- 4 files changed, 195 insertions(+), 58 deletions(-) diff --git a/ModuleFixTarget.py b/ModuleFixTarget.py index 18999b0..3e706f5 100644 --- a/ModuleFixTarget.py +++ b/ModuleFixTarget.py @@ -16,7 +16,7 @@ This contains a Widget to handle FixTargetFrames and fiducials, calculate final import logging _log=logging.getLogger(__name__) -import json, base64, yaml +import os, json, base64, yaml import numpy as np import pyqtUsrObj as UsrGO import pyqtgraph as pg @@ -47,6 +47,22 @@ class MyJsonEncoder(json.JSONEncoder): return repr(obj) return json.JSONEncoder.default(self, obj) + def iterencode(self, o, _one_shot=False): + list_lvl = 0 + l=super().iterencode(o, _one_shot=_one_shot) + #l=tuple(l);print(''.join(l)) # helpful to debug + for s in l: + if s.startswith('['): + list_lvl += 1 + if list_lvl > 0: + s = s[0]+s[1:].replace('\n', '').strip() + s = s.replace('\n', '').rstrip() + #self.item_separator): + #self.key_separator + if s.endswith(']'): + list_lvl -= 1 + yield s + def MyJsonDecoder(dct): if isinstance(dct, dict): if '__class__' in dct: @@ -173,28 +189,10 @@ class WndFixTarget(QWidget): #bm_pos_eu=self._goBeamMarker._pos_eu #bm_size_eu=self._goBeamMarker._size_eu try: - #parse the parameters: 'key:value [,key:value]' - # as key value separator : and = are allowed - #examples: - #Fiducial: - # no param - #FixTarget(12.5x12.5), FixTarget(23.0x23.0), FixTarget(test): - # code_gen:2 - # code_gen=3 | tmove=10 | twait=30 - #Grid(): - # size:(30, 20) - # cnt:(30, 22) - # fiducialSize:0.1 - #SwissMX(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.7), (10.3, .1), (10.3, 2.7)) - #SwissFEL(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2)) + #parse the parameters: as yaml string. + # allows : without space, allows () as [] + # no {} to define a dictionary + # e.g. 'a:ggf,b:5,c:[5,6.1],d(8,9,3)' param=param.replace(':', ': ') # allow gen:4 without space param=yaml.safe_load(param) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] @@ -376,7 +374,10 @@ class WndFixTarget(QWidget): #df = pd.DataFrame(data) #df.to_csv(filename, float_format="%.6f") #import numpy as np - ext=filename.rsplit('.',1)[1].lower() + base,ext=os.path.splitext(filename) + if not ext.lower(): + ext='json' + filename=base+'.'+ext try: wnd=app._mainWnd except AttributeError: @@ -386,7 +387,7 @@ class WndFixTarget(QWidget): grp=wnd._goTracked data=grp.childItems() - if ext=='json': + if ext.lower()=='json': with open(filename, 'w') as f: json.dump(data, f,cls=MyJsonEncoder, indent=2)#separators=(',', ':') else: diff --git a/Readme.md b/Readme.md index 3fde5b0..aa1d428 100644 --- a/Readme.md +++ b/Readme.md @@ -102,3 +102,114 @@ python swissmx.py --sim 0xc0 ``` Document to start SwissMX in cristallina environment (maintained by John): https://docs.google.com/document/d/1yEmV_DbRBKQKVCoovjXriNgSjNEBaz50WA0l3yA5jtg/edit#heading=h.z9io692b8tow + +## code generation parameters +``` +copied from: PBSwissMX/python/shapepath.py: ShapePath.setup_motion + + generates program and saves to fnPrg + the type of generated program is defined by $ + -> the list af all points that will be moved at, is in 'mot_pts' + + (m)= mandatory + (o)= optional + common kwargs: + scale : (o) scaling velocity (default=1. value=0 would stop at each point + cnt : (o) move path multiple times (default=1) + dwell : (o) dwell time at end (default=100ms) + + mode:0 unused + mode:1 pvt motion point list + common kwargs plus: + points : (m) point list + trf : (o) transformation that will be done on 'points', mot_pts=trf*points + mode:2 unused + mode:3 pvt motion point list using inverse fft velocity + common kwargs plus: + points : (m) point list + trf : (o) transformation that will be done on 'points', mot_pts=trf*points + numPad : (o) number of padding points to reduce aliasing (default=16) + mode:4 pvt motion short code using grid parameters + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid: (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + mode:5 pvt motion 'stop and go' short code using grid parameters. + Instead of continous motion it moves and waits as given in the parameters + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid: (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + tmove: (m) time to move in ms (move start on FEL-trigger + twait: (m) time to wait in ms + (tmove+twait will be rounded to a multiple of fel_per) + mode:6 pvt motion 'hit and return using grid parameters. continous motion on 2n ells to pump then same 2n wells to probe, then go 2 rows down + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid : (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + ssz : (m) section size (in wells) + smv : (o) time(in num of shots) to move to next section (horiz/vert) + default is (ssz[0]-1,ssz[1]) + sdelay: (o) shots count of delay. Default is ssz[0]*ssz[1] + +Examples: + mode:1 + mode:3 + mode:4 + mode:5,tmove:20,twait:30 + mode:6,ssz:(4,3) +``` +## graphical object parameters +``` + FixTarget: + 90*40+480*2=4560 + 60*30+360*2=2520 + 2520-240=2280 + 4560-240=4320 + size in mm, dscr.size in user units (um) + "size:(6,3.5), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" + + grid: + size, fiducialSize in mm: + (60-1)*.120 -> 7.08mm + (45-1)*.120 -> 5.28mm + fiducialSize -> 0.1mm + "size:(7.08,5.28),cnt:(60,45),fiducialSize:.01" + + SwissMX(): + "ofs:[.2,.2],width:10,fidScl:.02,fiducial:[[.1,.1],[.1,2.7],[10.3,.1],[10.3, 2.7]]" + SwissFEL(): + "ofs:[.2,.2],width:10,fidScl:.02,fiducial:[[.1,.1],[.1,2.2],[10.3,.1],[10.3,2.2]]" +``` + +## fully parameter examples: +``` + "mode:4,size:(7.08,5.28), cnt:(60,45), fiducialSize:.01" -> add a grid + + "mode:6,ssz:(4,3), size:(7.08,5.28), cnt:(60,45), fiducialSize:.01" -> add a grid + "mode:6,ssz:(4,3), size:(3,1.5), cnt:(30,15), fiducialSize:.01" -> add a grid + + "mode:6,ssz:(6,8), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" -> add a FixTarget + + +``` + + +## testing hit and return: +``` +-> add a FixTarget + "mode:6,ssz:(6,8), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" -> add a FixTarget +``` diff --git a/pyqtUsrObj.py b/pyqtUsrObj.py index 0b53001..d99afd4 100644 --- a/pyqtUsrObj.py +++ b/pyqtUsrObj.py @@ -251,25 +251,55 @@ class Grid(UsrROI): def get_scan_param(self): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' - scan=1 # snake motion Y fast, X slow (default) - cnt=np.array(self._cnt,np.int32) - sz=np.array(self.size()) - pitch=sz/cnt - xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) - - - if scan==0: # snake motion X fast, Y slow - for i in range(1,cnt[1],2): - xx[i]=xx[i][::-1] - else: # scan==1 # snake motion Y fast, X slow (default) - xx=xx.T - yy=yy.T - for i in range(1, cnt[0], 2): - yy[i]=yy[i][::-1] - pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose()*pitch - param={'points':pts} + cnt=np.array(self._cnt, np.int32) + grid={'pos':tuple(self.pos()), 'pitch':tuple(np.array(self.size())/cnt), 'count':self._cnt} + use_trf=self._param.get('use_trf', True) # do not use coordinate transformation + mode=self._param.get('mode',1) + num_pts=np.array(self._cnt, np.int32).prod() + param={'num_pts':num_pts} param.update(self._param) - assert(param.get('code_gen',0)==0) # this provides fully x,y motor coordinates + + # TODO: simplify !!! + t=self.transform() #obj_info(t) + p=np.array(self.pos()) + s=1#self.size()/self._dscr['size'] + trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) + trf[2,:]=p # shift origin + trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear + + pos=np.array((0,0)) #np.array(grid['pos']) # in um + pitch=np.array(grid['pitch']) # in um + trf2=np.asmatrix(np.identity(3)) + trf2[:, :2]=trf + trf2*=np.asmatrix(((1000, 0, 0), (0, 1000, 0), (0, 0, 1))) + trf3=np.asmatrix(((pitch[0], 0, 0), (0, pitch[1], 0), (pos[0], pos[1], 1))) + trf=(trf3*trf2)[:, :2] + + if mode in (1,3): # needs all points, not grid + scan=1 # snake motion Y fast, X slow (default) + xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) + if scan==0: # snake motion X fast, Y slow + for i in range(1,cnt[1],2): + xx[i]=xx[i][::-1] + else: # scan==1 # snake motion Y fast, X slow (default) + xx=xx.T + yy=yy.T + for i in range(1, cnt[0], 2): + yy[i]=yy[i][::-1] + + pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose() #*pitch + + if not use_trf: + pts=(np.hstack((pts, np.ones((pts.shape[0], 1))))*trf).A + param['trf']=trf + param['points']=pts + else: + if use_trf: + param.update({'grid':grid, 'trf':trf}) + else: + g=grid.copy() #has not be tested ! + g['pos']=tuple((np.array((0,0,1))*trf).A.reshape(-1).tolist()) + param.update({'grid':p, 'trf':trf}) return param @@ -371,11 +401,9 @@ class Path(UsrROI): s=self.size()/self.szOrig trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) trf[2,:]=p # shift origin - #trf[:2, 0]*=s[0];trf[:2, 1]*=s[1] #scaling (before rotation shear) - trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear - + trf[:2,:]=(trf[:2,:].T*s).T # trf*'gridpos in um' -> motor pos in mm - param={'points':self._path,'trf':trf} + param={'num_pts':len(self._path),'trf':trf,'points':self._path} param.update(self._param) return param diff --git a/swissmx.py b/swissmx.py index e36baa4..370d362 100755 --- a/swissmx.py +++ b/swissmx.py @@ -1527,7 +1527,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): if type(go)==UsrGO.Fiducial: continue t=type(go) - if t not in(UsrGO.FixTargetFrame,UsrGO.Path): + if t not in(UsrGO.FixTargetFrame,UsrGO.Path,UsrGO.Grid): _log.warning(f'{t} not supported for FixTargetFrame ->skipped:{go}') continue try: @@ -2025,7 +2025,7 @@ object settings: ofs:[.2,.2] width:10 fidScl:.02 - fiducial:[[.1,.1],[[.1,2.2],[10.3,.1],[10.3,2.2]] + fiducial:[[.1,.1],[.1,2.2],[10.3,.1],[10.3,2.2]] ''') mft._btnAdd.clicked.connect(self.module_fix_target_add_obj) @@ -2146,19 +2146,16 @@ object settings: go=UsrGO.Fiducial((fx-l/2,fy-l/2), (l, l),bz) go.sigRegionChangeFinished.connect(self.cb_fiducial_update_z) elif idx==1: - v=geo.pos2pix((12.5, 0)) - l=np.linalg.norm(v) - l=12.5 - #ctr=bm_pos+bm_sz/2 + #v=geo.pos2pix((12.5, 0));l=np.linalg.norm(v);l=12.5 + sz=param.pop('size',(12.5, 12.5)) go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='12.5x12.5',**param) elif idx==2: - v=geo.pos2pix((23, 0)) - l=np.linalg.norm(v) - l=23 + #v=geo.pos2pix((23, 0));l=np.linalg.norm(v)#l=23 + sz=param.pop('size',(23, 23)) go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='23.0x23.0',**param) elif idx==3: - w,h=(.120*12, .120*8) - go=UsrGO.FixTargetFrame((fx-w/2,fy-h/2), (w, h), tpl='test',**param) + sz=param.pop('size',(.120*12, .120*8)) + go=UsrGO.FixTargetFrame((fx-sz[0]/2,fy-sz[1]/2), sz, tpl='test',**param) elif idx==4: w,h=size=param.pop('size',(30, 20)) cnt=param.pop('cnt',(30, 22))