commit f0f04a2118ccd903b7e90868469421bee89d2795 Author: Thierry Zamofing Date: Thu Jul 7 15:48:22 2022 +0200 initial commit diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..62da6ff --- /dev/null +++ b/Readme.md @@ -0,0 +1,103 @@ +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 + + +https://git.psi.ch/SwissMX/swissmx_cristallina/-/wikis/Instructions%20on%20how%20to%20use%20software + + +caQtDM -macro 'P=SAR-EXPMX' /photonics/home/gac-cristall/Documents/swissmx_cristallina/gui/SwissMX_ChipMotion.ui +caQtDM -macro 'P=SAR-EXPMX' ESB_MX_exp.ui + +Camera: +caget SARES30-CAMS156-SMX-OAV:CAMERASTATUS +caget SARES30-CAMS156-SMX-OAV:PICTURE + +Zoom: +caget SAR-EXPMX-FETURA:POS_RB + + +wlp2s0: MAC: 80:38:fb:d6:01:78 wlan-corp 129.129.64.249 +enx00e04c680519: MAC: 00:e0:4c:68:05:19 + + +import pyqtgraph.examples +pyqtgraph.examples.run() +Benchmark -> VideoSpeedTest + +rsync -vai /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/SwissMX/ saresc-cons-02:/tmp/zamofing_t/ + + +/opt/gfa/python-3.5/latest/bin/python camera.py --base-pv SARES30-CAMS156-SMX-OAV + +BASE=~/Documents/prj/SwissFEL/epics_ioc_modules/LakeShore +cd $BASE/iocBoot/iocLakeShore1 +../../bin/UB20-x86_64/LakeShore st.cmd + + + +[saresc-cons-02 ~]$ ioc records 'SARES30-CAMS156-SMX-OAV:.*' + + +SARES30-CAMS156-SMX-OAV:PICTURE waveform CCD Picure Col SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:FPICTURE waveform CCD Picture SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:RF_FPICTURE waveform Reduced frequency FPICTURE SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:WIDTH ai Nr. of Pixel width SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:HEIGHT ai Nr. of Pixel height SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:EXPOSURE ao Exposure SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:CAMROI_X_START ao ROI X Start SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:CAMROI_X_END ao ROI X End SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:CAMROI_Y_START ao ROI Y Start SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:CAMROI_Y_END ao ROI Y End SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:BINX ao Binning X SARES30-CPCW-CAMS156-SMX-OAV office +SARES30-CAMS156-SMX-OAV:BINY ao Binning Y SARES30-CPCW-CAMS156-SMX-OAV office + +waveform: + Native DBF type: DBF_SHORT + Number of elements: 5000 + +SARES30-CAMS156-SMX-OAV:WIDTH 2448 +SARES30-CAMS156-SMX-OAV:HEIGHT 2048 + +caqtdm camsf +caqtdm -macro 'NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV' /sf/controls/config/qt/Camera/CameraMiniView_RF.ui +caqtdm -macro 'NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV' /sf/controls/config/qt/Camera/CameraExpert_RF.ui + +03 last file: /sf/controls/config/qt/Camera/CameraMiniView_RF.ui, macro: NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV + + +07-07-2022 12:14:03 last file: /sf/controls/config/qt/Camera/CameraMiniView_RF.ui, macro: NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV +mkdir EpicsSim +cd EpicsSim +makeBaseApp.pl -t ioc SwissMxSim +makeBaseApp.pl -a UB20-x86_64 -i -t ioc -p SwissMxSim SwissMxSim + +cd ~/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/EpicsSim/iocBoot/iocSwissMxSim +./st.cmd + +EPICS_CA_ADDR_LIST=localhost +ipython3 +import epics +p = epics.PV('SwissMxSim:PICTURE', auto_monitor=False) +print(p.info) +import numpy as np +a=np.arange(10000,dtype=np.uint16) +p.put(a) +print(p.get()) +https://pyepics.github.io/pyepics/arrays.html + diff --git a/camera.py b/camera.py new file mode 100755 index 0000000..44edebb --- /dev/null +++ b/camera.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +""" +Hi Zac, + +at the moment there is no channel for the min or max values. The reason for this is the binning. With this you change these numbers. +At the end you need 3*2 numbers : + Physical resolution + Max resolution for ROI + Resolution of your image +And the logic how you can change the numbers ;-) + +The simples trick for getting the max resolution is to set binning to 1 and write big numbers in the ROI (REGIONX_END / REGIONY_END) channels . They will be corrected to the max resolution. + +Sorry for this. When you want all numbers, I can think about the export of the whole "mess". Inside the IOC there is the central checking routine. Sometimes it could be helpful to have this in EPICS. + +Best regards + Helge +""" + +import enum +import logging + +import epics +import numpy as np + +logger = logging.getLogger(__name__) +global imv + + +class camera_server(object): + + def __init__(self, basename="ESB-MX-CAM"): + self._pv=pv=dict() + os.environ['EPICS_CA_ADDR_LIST'] = 'localhost' + if basename[-1]!=':': basename+=':' + self._transformations = [] + pv['pic']=epics.PV(basename+"FPICTURE", auto_monitor=True, callback=self.new_frame_callback) + for k,v in (('w','WIDTH'),('h','HEIGHT'),): + pv[k]=epics.PV(basename+v) + return + + def new_frame_callback(self, **kwargs): + """kargs contains: + pvname: the name of the pv + value: the latest value + char_value: string representation of value + count: the number of data elements + ftype: the numerical CA type indicating the data type + type: the python type for the data + status: the status of the PV (1 for OK) + precision: number of decimal places of precision for floating point values + units: string for PV units + severity: PV severity + timestamp: timestamp from CA server. + read_access: read access (True/False) + write_access: write access (True/False) + access: string description of read- and write-access + host: host machine and CA port serving PV + enum_strs: the list of enumeration strings + upper_disp_limit: upper display limit + lower_disp_limit: lower display limit + upper_alarm_limit: upper alarm limit + lower_alarm_limit: lower alarm limit + upper_warning_limit: upper warning limit + lower_warning_limit: lower warning limit + upper_ctrl_limit: upper control limit + lower_ctrl_limit: lower control limit + chid: integer channel ID + cb_info: (index, self) tuple containing callback ID + and the PV object""" + print('new_frame_callback') + try: + pv=self._pv + w, h = int(pv['w'].value), int(pv['h'].value) + pic = pv['pic'].get(count=w * h, as_numpy=True).reshape((h, w)) + if pic.dtype==np.int16: + pic.dtype=np.uint16 + self.pic=pic + print(pic[-1][-1]) + imv.setImage(pic) + except: + logger.warning("failed to fetch image") + + def get_image(self): + pv=self._pv + try: + pic = self.pic + except: + self.new_frame_callback() + pic = self.pic + for t in self._transformations: + if Transforms.ROTATE_90 == t: + pic = np.rot90(pic, 1) + elif Transforms.ROTATE_180 == t: + pic = np.rot90(pic, 2) + elif Transforms.ROTATE_270 == t: + pic = np.rot90(pic, 3) + elif Transforms.FLIP_LR == t: + pic = np.fliplr(pic) + elif Transforms.FLIP_UD == t: + pic = np.flipud(pic) + elif Transforms.TRANSPOSE == t: + pic = pic.transpose() + return pic + + +if __name__ == "__main__": + import time, os, PIL + + logging.basicConfig(format="%(asctime)s $(levelname)s %(message)s", level=logging.DEBUG) + + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--ui", "-u", help="qt test", action="store_true") + parser.add_argument("--base-pv","-b",help="PV prefix for images; default=ESB-MX-CAM",type=str,default="ESB-MX-CAM",) + args = parser.parse_args() + + print('STARTING') + os.environ['EPICS_CA_ADDR_LIST']='localhost' + cam = camera_server(basename=args.base_pv) + #pv = cam._pv + #w, h = int(pv['w'].value), int(pv['h'].value) + #a = np.arange(w*h, dtype=np.uint16) + #pv['pic'].put(a) + #print(pv['pic'].get()) + + if not args.ui: + n = 1 + base = time.strftime("/tmp/image_%Y%m%d_%H%M%S_{:05d}.png") + while True: + fname = base.format(n) + img=cam.get_image() + print(type(img)) + type(img) + print(img,img.dtype) + PIL.Image.fromarray(img).save(fname) + logging.info("acquired {}".format(fname)) + try: + input(" for next image, ctrl-c to abort") + except KeyboardInterrupt: + exit(0) + n += 1 + else: + import sys + from pyqtgraph.Qt import QtCore, QtGui + import pyqtgraph as pg + print(pg.__version__) + + # Interpret image data as row-major instead of col-major + pg.setConfigOptions(imageAxisOrder='row-major') + + app = QtGui.QApplication([]) + + ## Create window with ImageView widget + win = QtGui.QMainWindow() + win.resize(800,800) + imv = pg.ImageView() + win.setCentralWidget(imv) + win.show() + win.setWindowTitle('pyqtgraph example: ImageView') + + ## Create random 3D data set with noisy signals + img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100 + img = img[np.newaxis,:,:] + decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis] + data = np.random.normal(size=(100, 200, 200)) + data += img * decay + data += 2 + + ## Add time-varying signal + sig = np.zeros(data.shape[0]) + sig[30:] += np.exp(-np.linspace(1,10, 70)) + sig[40:] += np.exp(-np.linspace(1,10, 60)) + sig[70:] += np.exp(-np.linspace(1,10, 30)) + + sig = sig[:,np.newaxis,np.newaxis] * 3 + data[:,50:60,30:40] += sig + + + ## Display the data and assign each frame a time value from 1.0 to 3.0 + imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0])) + imv.setImage(cam.get_image()) + + ## Set a custom color map + colors = [ + (0, 0, 0), + (45, 5, 61), + (84, 42, 55), + (150, 87, 60), + (208, 171, 141), + (255, 255, 255) + ] + cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors) + imv.setColorMap(cmap) + + ## Start Qt event loop unless running in interactive mode. + if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): + QtGui.QApplication.instance().exec_() + + + + diff --git a/simCam.py b/simCam.py new file mode 100755 index 0000000..baa6687 --- /dev/null +++ b/simCam.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# *-----------------------------------------------------------------------* +# | | +# | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) | +# | | +# | Author Thierry Zamofing (thierry.zamofing@psi.ch) | +# *-----------------------------------------------------------------------* +import os,epics +import time + +import numpy as np + +print('STARTING') +os.environ['EPICS_CA_ADDR_LIST'] = 'localhost' + +pv=dict() +basename='SwissMxSim:' +#pv['pic'] = epics.PV(basename + "FPICTURE", auto_monitor=True, callback=self.new_frame_callback) +pv['pic'] = epics.PV(basename + "FPICTURE") +for k, v in (('w', 'WIDTH'), ('h', 'HEIGHT'),): + pv[k] = epics.PV(basename + v) + +pv['w'].put(300) +pv['h'].put(400) + +w, h = int(pv['w'].value), int(pv['h'].value) + + +for j in range(100): + for i in range(20): + a = np.arange(w * h, dtype=np.uint16)//i + pv['pic'].put(a) + print(pv['pic'].get()) + time.sleep(.01)