From 3904a1a134effa406c08262f63732f30c9398572 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 13 Jul 2022 16:52:27 +0200 Subject: [PATCH] wip --- Readme.md | 41 ++++- camera.py | 161 +++++++++------- illumination.py | 152 +++++++++++++++ simCam.py => scratch/simCam.py | 0 zoom.py | 327 +++++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 67 deletions(-) create mode 100755 illumination.py rename simCam.py => scratch/simCam.py (100%) create mode 100755 zoom.py diff --git a/Readme.md b/Readme.md index af52a02..e859b3d 100644 --- a/Readme.md +++ b/Readme.md @@ -1,3 +1,13 @@ +pyqtgraph examples +------------------ +``` +ipython3 +import pyqtgraph.examples +pyqtgraph.examples.run() + +``` + + EPICS simulator --------------- ``` @@ -23,9 +33,38 @@ 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 -p SARES30-CAMS156-SMX-OAV + +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 + +``` + + + + 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 diff --git a/camera.py b/camera.py index 3a6e74c..a4107f0 100755 --- a/camera.py +++ b/camera.py @@ -1,4 +1,5 @@ #!/usr/bin/env python + """ Hi Zac, @@ -19,16 +20,21 @@ Best regards import enum import logging +_log = logging.getLogger(__name__) import epics import numpy as np -logger = logging.getLogger(__name__) -class CamMode(enum.IntEnum): +class Camera(enum.IntEnum): OFF = 0 RUNNING = 1 +class CameraStatus(enum.IntEnum): + OFFLINE = 0 + IDLE = 1 + RUNNING = 2 + class epics_cam(object): @@ -36,8 +42,6 @@ class epics_cam(object): self._pv=pv=dict() if prefix[-1]!=':': prefix+=':' self._prefix=prefix - pv_w=self.getPv('WIDTH');pv_h=self.getPv('HEIGHT') - self._sz=(int(pv_w.value), int(pv_h.value)) def getPv(self,name): try: @@ -101,6 +105,31 @@ class epics_cam(object): pic=pic[::trf[0,1],::trf[1,0]].T self.pic=pic + return pic + + def stop(self,v=CameraStatus.IDLE): + if 'pic' in self._pv: + del self._pv['pic'] + if v is not None: + pv_cs = self.getPv('CAMERASTATUS') + pv_cs.put(v, wait=True) + + def run(self,cb=None): + pv_cam=self.getPv('CAMERA') + if pv_cam.value==Camera.OFF: + pv_cs = self.getPv('CAMERASTATUS') + pv_cs.put(CameraStatus.RUNNING, wait=True) + while pv_cam.value==Camera.OFF: + print('wait...');time.sleep(.5) + self.update_size() + if cb is None: + self._pv['pic'] = epics.PV(self._prefix + "FPICTURE") + else: + self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=cb) + + def update_size(self): + pv_w=self.getPv('WIDTH');pv_h=self.getPv('HEIGHT') + self._sz=(int(pv_w.value), int(pv_h.value)) def set_exposure(self,exp): pv_exp=self.getPv('EXPOSURE') @@ -110,7 +139,7 @@ class epics_cam(object): def set_roi(self,rxs,rxe,rys,rye): pv_rxs=self.getPv('REGIONX_START');pv_rxe=self.getPv('REGIONX_END') pv_rys=self.getPv('REGIONY_START');pv_rye=self.getPv('REGIONY_END') - self.update_params((pv_rxs,rxs),(pv_rxe,rxe),(pv_rys,rys),(pv_ryerye)) + self.update_params((pv_rxs,rxs),(pv_rxe,rxe),(pv_rys,rys),(pv_rye,rye)) def set_binning(self,bx,by): pv_bx=self.getPv('BINX');pv_by=self.getPv('BINY') @@ -120,43 +149,71 @@ class epics_cam(object): def update_params(self, *args): """update parameters on camera""" for pv, val in args: - logging.debug("updating {} = {}".format(pv, v)) + if not pv.connected: + _log.info('force connect {}'.format(pv)) + pv.force_connect() #force to connect pv + _log.debug("updating {} = {}".format(pv.pvname, val)) pv.put(val, wait=True) - pv_camera=self.getPv('CAMERA') - pv_set_param=self.getPv("SET_PARAM") - - pv_camera.put(CamMode.OFF, wait=True) - pv_set_param.put(1, wait=True) - pv_camera.put(CamMode.RUNNING, wait=True) - #TODO: update self._sz + pv_cam=self.getPv('CAMERA') + pv_cs = self.getPv('CAMERASTATUS') + #pv_set_param=self.getPv("SET_PARAM") + pv_cam.put(CameraStatus.IDLE, wait=True) + #pv_set_param.put(1, wait=True) + pv_cam.put(CameraStatus.RUNNING, wait=True) + self.update_size() if __name__ == "__main__": - import time, os, PIL - - logging.basicConfig(format="%(asctime)s $(levelname)s %(message)s", level=logging.DEBUG) - + import time, os, PIL.Image, platform, subprocess import argparse + logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') + + def default_app_open(file): + if platform.system() == 'Darwin': # macOS + subprocess.call(('open', file)) + elif platform.system() == 'Windows': # Windows + os.startfile(file) + else: # linux variants + subprocess.call(('xdg-open', file)) + + + parser = argparse.ArgumentParser() - parser.add_argument("--ui", "-u", help="qt test", action="store_true") - parser.add_argument("--prefix","-p",help="PV prefix for images; default=ESB-MX-CAM",type=str,default="ESB-MX-CAM",) + parser.add_argument("--ui", "-u", help="qt test", type=int, default=0) + parser.add_argument("--prefix","-p",help="PV prefix for images: default=%(default)s",type=str,default="SARES30-CAMS156-SMX-OAV",) args = parser.parse_args() - print('STARTING') - os.environ['EPICS_CA_ADDR_LIST']='localhost' + _log.info('STARTING') + if args.prefix=='SwissMxSim': + os.environ['EPICS_CA_ADDR_LIST']='localhost' + else: + os.environ['EPICS_CA_ADDR_LIST'] ='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' + if not args.ui: - cam = epics_cam(prefix=args.base_pv) + cam = epics_cam(prefix=args.prefix) + #sz=(2448,2048) + #ctr=(1200,1400) + #sz=(1200,1000) + #cam.set_roi(int(ctr[0]-sz[0]/2),int(ctr[0]+sz[0]/2),int(ctr[1]-sz[1]/2),int(ctr[1]+sz[1]/2)) + #cam.set_exposure(3) + #cam.set_binning(1,1) + + cam.run() n = 1 - base = time.strftime("/tmp/image_%Y%m%d_%H%M%S_{:05d}.png") + base = "/tmp/image_{:05d}.png" while True: - fname = base.format(n) img=cam.get_image() - print(type(img)) - type(img) + mx= img.max() + if img.dtype==np.uint16 and mx<256: + _log.info('reformat to uint 8') + img=np.uint8(img) print(img,img.dtype) - PIL.Image.fromarray(img).save(fname) - logging.info("acquired {}".format(fname)) + + fn = base.format(n) + PIL.Image.fromarray(img).save(fn) + _log.info('File {} shape:{} dtype:{} max:{}'.format(fn,img.shape,img.dtype,img.max())) + default_app_open(fn) try: input(" for next image, ctrl-c to abort") except KeyboardInterrupt: @@ -167,18 +224,16 @@ if __name__ == "__main__": def __init__(self, prefix="ESB-MX-CAM"): epics_cam.__init__(self,prefix) - self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=self.new_frame_pv_cb) pass def new_frame_pv_cb(self, **kwargs): pv = self._pv sz = self._sz if kwargs['count']==sz[0] * sz[1]: - pic = kwargs['value'].reshape(sz[::-1]) + pic=kwargs['value'].reshape(sz[::-1]) else: - self._sz = (int(pv['w'].value), int(pv['h'].value)) - print('new_frame_pv_cb SIZE ERROR:',pic[-1][-1],kwargs['count']) - return + sz=self.update_size() + pic=kwargs['value'].reshape(sz[::-1]) print('new_frame_pv_cb',pic[-1][-1],kwargs['count']) if pic.dtype==np.int16: pic.dtype=np.uint16 @@ -211,46 +266,20 @@ if __name__ == "__main__": 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])) cam = UIcamera(prefix=args.prefix) - cam.get_image() - imv.setImage(cam.pic) + #cam.set_binning(4,4) + #cam.run() + #cam.get_image() + #imv.setImage(cam.pic, autoRange=True, autoLevels=True) + #cam.stop(None) + cam.run(cam.new_frame_pv_cb) ## Set a custom color map - colors = [ - (0, 0, 0), - (45, 5, 61), - (84, 42, 55), - (150, 87, 60), - (208, 171, 141), - (255, 255, 255) - ] + 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/illumination.py b/illumination.py new file mode 100755 index 0000000..4e40c99 --- /dev/null +++ b/illumination.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +import logging +_log=logging.getLogger(__name__) +import socket + +''' +Please do not leave the LEDs on longer than needed, especially the colored ones. +I guess I have an issue with +power dissipation on my board. For the white LEDs it should be ok. + +The lights communicate in hexidecimal, two 2bytes: +byte 1: + 0x00 get status + 0x01 off + 0x02 on + 0x04 out (unused?) + +byte 2: + 0x01 front white + 0x02 front red + 0x04 front green + 0x08 front blue + 0x10 back white +''' +# commands +stat= 0x00 +off = 0x01 +on = 0x02 + +# leds +front = 0x01 +red = 0x02 +green = 0x04 +blue = 0x08 +back = 0x10 +all = 0xff + + +class IlluminationControl(object): + + def __init__(self,hostname: str,port: int): + self._hostname = hostname + self._port = port + self.connect() + + def connect(self): + """creates a socket and connects to illumination controller""" + # connect to XT-PICO + self._socket=s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + s.connect((self._hostname, self._port)) + + def disconnect(self): + try: + self._socket.close() + except AttributeError: + pass + + def is_on(self, led_flags: int) -> bool: + """check status for light state, see CONSTANTS STATUS_BITS_*""" + try: + s = self.status()[0] + except: + print("error reading status") + return False + return bool(s & led_flags) + + def send_command(self, cmd: bytes): + try: + self._socket.sendall(cmd) + except (BrokenPipeError, OSError) as e: + _log.warning("reconnecting lights") + self.connect() + try: + self._socket.sendall(cmd) + except Exception as e: + raise IlluminationControlException("unrecoverable socket error: {}".format(e.args)) + + def status(self): + self.send_command(bytes((stat,all))) + resp = self._socket.recv(1024) + return resp[0]&0x1f + + def all(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,all))) + + def front(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,front))) + + def back(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,back))) + + def red(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,red))) + + def blue(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,blue))) + + def green(self,val): + cmd = on if val else off + self.send_command(bytes((cmd,green))) + +if __name__ == "__main__": + import argparse + logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') + + parser = argparse.ArgumentParser() + parser.add_argument('-m', '--mode', help='mode string with characters: "frgbka" for front,red,green,blue,back,all') + parser.add_argument("-t", "--test", help="test sequence", action="store_true") + + args = parser.parse_args() + + _log.debug(args) + host = "129.129.221.92" + port = 1003 + leds = IlluminationControl(host,port) + if args.test: + leds.all(False) + print(bin(leds.status()));time.sleep(1) + leds.front(True) + print(bin(leds.status()));time.sleep(1) + leds.front(False); leds.red(True) + print(bin(leds.status()));time.sleep(1) + leds.red(False); leds.green(True) + print(bin(leds.status()));time.sleep(1) + leds.green(False); leds.blue(True) + print(bin(leds.status()));time.sleep(1) + leds.blue(False); leds.back(True) + print(bin(leds.status()));time.sleep(1) + leds.back(False) + print(bin(leds.status()));time.sleep(1) + else: + leds.all(False) + _log.debug('turn off all') + if args.mode is not None: + m=args.mode + l=0 + if 'f' in m: l|=front + if 'r' in m: l|=red + if 'g' in m: l|=green + if 'b' in m: l|=blue + if 'k' in m: l|=back + if 'a' in m: l|=all + leds.send_command(bytes((on,l))) + _log.debug('turn on %s'%bin(l)) + diff --git a/simCam.py b/scratch/simCam.py similarity index 100% rename from simCam.py rename to scratch/simCam.py diff --git a/zoom.py b/zoom.py new file mode 100755 index 0000000..326852c --- /dev/null +++ b/zoom.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python + + +import json +import logging +_log=logging.getLogger(__name__) +import os +import qtawesome +from PyQt5 import QtWidgets, uic +import PyQt5.QtWidgets as qtw +from PyQt5.QtCore import pyqtSignal +from PyQt5.QtWidgets import ( + QApplication, + QPushButton, + QGroupBox, + QGridLayout, + QLabel, + QDoubleSpinBox, + QComboBox, + QSpinBox, + QVBoxLayout, + QHBoxLayout, + QCheckBox, +) +from PyQt5.uic import loadUiType +import epics + +Ui_Zoom, QWidget = loadUiType("epics_widgets/zoom.ui") +MIN_ZOOM = 1 +MAX_ZOOM = 1000 +SPINNER_SINGLE_STEP = 50 +SPINNER_LARGE_STEP = 200 + +class Zoom(QGroupBox, Ui_Zoom): + zoomChanged = pyqtSignal(float) + moveBacklight = pyqtSignal(str) + + def __init__(self, parent=None): + super(Zoom, self).__init__(parent=parent) + self.setupUi(self) + self.setTitle("Sample Viewing") + + def configure(self): + keys = settings.allKeys() + if "sample_viewing/zoom_buttons" not in keys: + settings.setValue("sample_viewing/zoom_buttons", + json.dumps([(1, "1"),(200, "200"),(400, "400"),(600, "600"),(800, "800"),(1000, "1000"),]),) + buttons = json.loads(settings.value("sample_viewing/zoom_buttons")) + + if "backlight/backlight_pv" not in keys: + settings.setValue("backlight/backlight_pv", "SAR-EXPMX:MOT_BLGT") + backlight_pv = settings.value("backlight/backlight_pv") + + if "sample_viewing/zoom_api" not in keys: + settings.setValue("sample_viewing/zoom_api", "rest://pc12818.psi.ch:9999") + zoom_api = settings.value("sample_viewing/zoom_api") + + #self.get_zoom_pv = PV(zoom_api + ":ZOOM-RBV", callback=self.zoom_update_cb) + #self.status_pv = PV(zoom_api + ":ZOOM-STATUS", callback=self.zoom_status_cb) + current_zoom_value = self.get_zoom() + + # zoom widgets + layout = QVBoxLayout() + layout.setSpacing(0) + layout.setContentsMargins(0, 0, 0, 0) + self._button_box.setLayout(layout) + self._zoom_spinner = QSpinBox() + self._zoom_spinner.setRange(MIN_ZOOM, MAX_ZOOM) + self._zoom_spinner.setValue(current_zoom_value) + self._zoom_spinner.editingFinished.connect(self.move_zoom_a) + # self._zoom_spinner.setSingleStep(SPINNER_SINGLE_STEP) + self._zoom_spinner.setSingleStep(SPINNER_LARGE_STEP) + _box = QWidget() + _box.setLayout(QHBoxLayout()) + _box.layout().setSpacing(0) + _box.layout().setContentsMargins(0, 0, 0, 0) + _box.layout().addWidget(QLabel("Zoom Level")) + _box.layout().addWidget(self._zoom_spinner) + layout.addWidget(_box) + _box = QWidget() + _box.setLayout(QGridLayout()) + _box.layout().setContentsMargins(0, 0, 0, 0) + for n, b in enumerate(buttons): + row, col = n // 2, n % 2 + value, label = b + but = QPushButton(label) + but.setCheckable(True) + but.setChecked(value == current_zoom_value) + but.setAutoExclusive(True) + but.setAccessibleName("zoom_button") + but.setObjectName("zoom_{}".format(value)) # used with findChild() + but.pressed.connect(lambda x=value: self.move_zoom(x)) + _box.layout().addWidget(but, row, col, 1, 1) + self._zoom_buttonbox = _box + layout.addWidget(_box) + + # backlight + self._top_grid.setLayout(QVBoxLayout()) + lbox = QWidget() + lbox.setLayout(QHBoxLayout()) + lbox.layout().setSpacing(0) + lbox.layout().setContentsMargins(0, 0, 0, 0) + self._top_grid.layout().addWidget(lbox) + #self.blgt_button = QPushButton(qtawesome.icon("material.lightbulb_outline"), "Backlight") + self.blgt_button = QPushButton( "Backlight") + self.blgt_button.clicked.connect(self.toggle_backlight) + + lbox = QWidget() + lbox.setLayout(QHBoxLayout()) + lbox.layout().setSpacing(0) + lbox.layout().setContentsMargins(0, 0, 0, 0) + self._top_grid.layout().addWidget(lbox) + + lbox.layout().addWidget(self.blgt_button) + leds=QApplication.instance()._illumination + but = QCheckBox("Back") + but.setChecked(leds.is_on(illumination.back)) + but.stateChanged.connect(self.back_toggle) + lbox.layout().addWidget(but) + + but = QCheckBox("Front") + but.setChecked(leds.is_on(illumination.front)) + but.stateChanged.connect(self.front_toggle) + lbox.layout().addWidget(but) + + lbox = QWidget() + lbox.setLayout(QHBoxLayout()) + lbox.layout().setSpacing(0) + lbox.layout().setContentsMargins(0, 0, 0, 0) + self._top_grid.layout().addWidget(lbox) + + but = QCheckBox("Red") + but.setChecked(leds.is_on(illumination.red)) + but.stateChanged.connect(self.red_toggle) + lbox.layout().addWidget(but) + + but = QCheckBox("Green") + but.setChecked(leds.is_on(illumination.green)) + but.stateChanged.connect(self.green_toggle) + lbox.layout().addWidget(but) + + but = QCheckBox("Blue") + but.setChecked(leds.is_on(illumination.blue)) + but.stateChanged.connect(self.blue_toggle) + lbox.layout().addWidget(but) + + lbox = QWidget() + lbox.setLayout(QHBoxLayout()) + lbox.layout().setSpacing(0) + lbox.layout().setContentsMargins(0, 0, 0, 0) + self._top_grid.layout().addWidget(lbox) + + self.slid = None + + cam=QApplication.instance()._camera + grp = QGroupBox("Camera settings") + grid = QGridLayout() + grp.setLayout(grid) + lab = QLabel("Exposure (ms)") + grid.addWidget(lab, 0, 0) + self.slid = QDoubleSpinBox() + self.slid.setValue(cam.get_exposure()) + self.slid.setMinimum(0.001) + self.slid.setMaximum(1000.000) + self.slid.setDecimals(3) + self.slid.setSuffix(" ms") + grid.addWidget(self.slid, 0, 1) + + #grid.addWidget(QLabel("Acq.Mode"), 1, 0) + #cbox = QComboBox() + #for mode in Camera.AcquisitionMode: + # cbox.addItem(mode.name, mode) + #cbox.setCurrentIndex(cam.pv_acqmode.get()) + #cbox.currentIndexChanged.connect(self.update_camera_acqmode) + #grid.addWidget(cbox, 1, 1) + + lbox.layout().addWidget(grp) + self.slid.editingFinished.connect(lambda: self.update_exposure(self.slid.value())) + + def back_toggle(self, state): + leds=QApplication.instance()._illumination + leds.back(state) + + def front_toggle(self, state): + leds=QApplication.instance()._illumination + leds.front(state) + + def red_toggle(self, state): + leds=QApplication.instance()._illumination + leds.red(state) + + def green_toggle(self, state): + leds=QApplication.instance()._illumination + leds.green(state) + + def blue_toggle(self, state): + leds=QApplication.instance()._illumination + leds.blue(state) + + def toggle_backlight(self): + bl=QApplication.instance()._backlight + if bl.is_in() or bl.is_diode(): + self.moveBacklight.emit("out") + self.blgt_button.setText("Move Backlight IN") + else: + self.moveBacklight.emit("in") + self.blgt_button.setText("Move Backlight OUT") + + def update_camera_exposure(self, nv): + cam=QApplication.instance()._camera + cam.set_exposure(nv) + + def update_exposure(self, nv): + _log.debug("update_exposure: {} {}".format(nv, self.slid.singleStep())) + if nv > 100: + self.slid.setSingleStep(50) + elif 100 > nv > 50: + self.slid.setSingleStep(10) + elif 50 > nv > 10: + self.slid.setSingleStep(5) + elif 10 > nv > 2: + self.slid.setSingleStep(1) + elif 2 > nv > 1.19: + self.slid.setSingleStep(0.2) + elif 1 > nv > 0.1: + self.slid.setSingleStep(0.1) + else: + self.slid.setSingleStep(0.01) + _log.debug("update_exposure: {} {}".format(nv, self.slid.singleStep())) + if nv > 0.001: + cam = QApplication.instance()._camera + cam.set_exposure(nv) + + def update_camera_acqmode(self, index): + _log.debug("changing camera mode: {}".format(index)) + cam=QApplication.instance()._camera + cam.auto(index) + + def move_zoom_a(self): + nval = self._zoom_spinner.value() + self.move_zoom(nval) + + def move_zoom(self, value: int): + zoom=QApplication.instance()._zoom + _log.debug("zooming to : {}".format(value)) + self._zoom_spinner.blockSignals(True) + zoom.set(value) + but = "zoom_{:d}".format(value) + _log.debug("looking for button: {}".format(but)) + for bbut in self._zoom_buttonbox.findChildren(QPushButton): + bbut.blockSignals(True) + curbut = bbut.objectName() + checked = curbut == but + bbut.setChecked(checked) # FIXME this is not working + bbut.blockSignals(False) + + self._zoom_spinner.setValue(int(value)) + self._zoom_spinner.blockSignals(False) + self.zoomChanged.emit(float(value)) + + def get_zoom(self) -> int: + zoom=QApplication.instance()._zoom + pos = zoom.get() + _log.debug("get_zoom(epics) => {}".format(pos)) + return pos + + def zoom_update_cb(self, pvname, value, char_value, **kwargs): + self._zoom_spinner.blockSignals(True) + self._zoom_spinner.setValue(value) + self._zoom_spinner.blockSignals(False) + + def zoom_status_cb(self, pvname, value, char_value, **kwargs): + busy = bool(value) + self.setDisabled(busy) + + +class QopticZoom(object): + + def __init__(self, prefix='SAR-EXPMX-FETURA'): + self._pv=pv=dict() + if prefix[-1]!=':': prefix+=':' + self._prefix=prefix + + def getPv(self,name): + try: + pv=self._pv[name] + except KeyError: + prefix=self._prefix + pv=epics.PV(prefix+name) + self._pv[name]=pv + return pv + + def get(self) -> int: + pv=self.getPv('POS_RB') + return pv.get() + + def set(self, val: int): + + pv=self.getPv('POS_SP') + pv.put(val) + + +if __name__ == "__main__": + import sys + import backlight + import illumination + import camera + + from app_config import settings, appsconf + + logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') + + _log.debug('Start') + app = QApplication(sys.argv) + + app._backlight = backlight.Backlight() + app._illumination = illumination.IlluminationControl("129.129.221.92", 1003) + app._zoom = QopticZoom() + app._camera = camera.camera_server('SARES30-CAMS156-SMX-OAV') + + simulated = appsconf["microscope"]["zoom"].get("simulate", False) + + obj=Zoom() + obj.configure() + obj.show() + app.exec_() +