first alpha version vor ARESvis application

This commit is contained in:
2024-08-30 17:23:31 +02:00
parent dfd21755e0
commit 622b2f7628

View File

@@ -35,14 +35,11 @@ bitmask for simulation:
0x80:
"""
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QSlider, QLineEdit,\
QCheckBox, QHBoxLayout, QVBoxLayout, QGroupBox, QGridLayout, QComboBox
from PyQt5.QtGui import QPainter, QColor, QPen, QBrush, QPolygon, QTransform
from PyQt5.QtCore import QPoint, QPointF, Qt
from PyQt5.QtCore import QPoint, QPointF, Qt,pyqtSignal
from pyqtgraph.Qt import QtCore, QtGui
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtW
@@ -52,8 +49,57 @@ import numpy as np
import sys, logging, copy
import epics
if sys.version_info[0] < 3 or sys.version_info[1] < 6:
print(f"Must be using Python 3.6 or newer. Try e.g. /opt/gfa/python-3.8/latest/bin/python /sf/furka/bin/ARESvis")
_log=logging.getLogger(__name__)
import logging
class col:
d = '\033[0m' #default
r = '\033[31m' #red
g = '\033[32m' #green
y = '\033[33m' #yellow
rr= '\033[91m' #red(bright)
gg= '\033[92m' #green(bright)
yy= '\033[93m' #yellow(bright)
b = '\033[1m' #bold
u = '\033[4m' #underline
R = '\033[1;31m' #bold, red
G = '\033[1;32m' #bold, green
Y = '\033[1;33m' #bold, yellow
class logHandler(logging.StreamHandler):
def __init__(self):
logging.StreamHandler.__init__(self)
def emit(self, record):
'''override function of base class'''
try:
msg=self.format(record)
# print(record.__dict__)
if record.levelno<=10:
c=col.g
elif record.levelno<=20:
c=col.y
elif record.levelno<=30:
c=col.yy
elif record.levelno<=40:
c=col.r
else:
c=col.rr+col.b
msg=c+msg+col.d
stream=self.stream
stream.write(msg+self.terminator)
self.flush()
except RecursionError:
raise
except Exception:
self.handleError(record)
class ARESdevice():
_lutDifrBeamPaint=( # (number of difr beam,draw mode,alpha,width)
(4, 2, 190, 0),
@@ -141,11 +187,11 @@ class ARESdevice():
'drx':drx})
if degArm>10:
raise(ValueError('angle arm > 10°'))
raise(ValueError('angle arm > 10deg'))
elif degArm<1:
raise(ValueError('angle arm < 1°'))
raise(ValueError('angle arm < 1deg'))
elif abs(dd)>15:
raise(ValueError('angle bellow to detector > 15°'))
raise(ValueError('angle bellow to detector > 15deg'))
def containsPoint(self,point):
try:
@@ -330,6 +376,14 @@ class ARESdevice():
# qp.setTransform(tf);self.plotOrig(qp)
class WndVisualize(QWidget):
_pv2key={
'SATES30-RIXS:MOT_RY.RBV' :'aArm',
'SATES30-ARES:MOT_JFRY.RBV':'aJFr',
'SATES30-ARES:MOT_2TRY.RBV':'a2Th',
'SATES30-ARES:MOT_DRY.RBV' :'aDet',
'SATES30-ARES:MOT_SRY.RBV' :'aTrg'
}
def __init__(self):
super().__init__()
self.initUI()
@@ -354,7 +408,8 @@ class WndVisualize(QWidget):
('sclA', ( -8, 8), 1),
('sclD', (-8, 8), 1),):
wLb=QLabel(key)
wLb=QLabel(f"<font>{key}</font>",objectName=key) #MOST BE with <font> as it is changed later and else creates a seg fault
wSl=QSlider(QtCore.Qt.Horizontal,objectName=key)
wSl.setFixedWidth(200);wSl.setMinimum(rng[0]);wSl.setMaximum(rng[1])
if key.startswith('scl'):
@@ -371,35 +426,61 @@ class WndVisualize(QWidget):
lg.addWidget(wLb, row, 0)
lg.addWidget(wSl, row, 1);row+=1
#self.event_update.connect(self.cb_update)
self.show()
def cb_update(self,*args,**kwargs):
_log.debug(f'{args} {kwargs}')
def connectEPICS(self):
self._pv=pv=set()
for rec_name in (
'SATES30-RIXS:MOT_RY',
'SATES30-ARES:MOT_JFRY',
'SATES30-ARES:MOT_2TRY',
'SATES30-ARES:MOT_DRY',
'SATES30-ARES:MOT_SRY',):
_log.info('connect PVs')
self._pvDict=pvd=dict()
self._pvConnected=0
for pvn in self._pv2key.keys():
pv=epics.get_pv(pvn,connection_callback=self.OnConnectionChange,callback=self.OnValueChange)
pvd[pvn]=pv
_log.info(f'{pv}')
#epics.Motor checks the record type and will fail if the record is not online
#therefore use epics.Device
#epics.Device will force to connect the PV in Device.add_callback
#therefore use epics.PV.add_callback to acc callback
#as soon as the devices are online, they are connected
#but with epics.Device creating PV is not fully flexible. epics.get_pv proviles connection and value change callbacks that is way more flexible.
#therefore the lowest level of the library (only pvs is the best suited
#m=epics.Motor(rec_name)
#m=epics.Device(rec_name, delim='.',with_poll=False,attrs=('VAL', 'RBV', 'DESC', 'RVAL','LVIO', 'HLS', 'LLS'))
#m.add_callback('RBV', self.OnChangedRBV)
#pv=m.PV('RBV',connect=False)
#pv.add_callback(self.OnChangedRBV)
#pv.connection_callbacks
#if not pv.connected:
# disconnected.add(rec_name)
#print(pv.connected)
#devs.add(m)
m=epics.Motor(rec_name)
pv.add(m)
#_log.debug(m.get_position()) # this is the VAL field
#_log.debug(m.PV('RBV').get()) # this is the RBV field
#_log.debug(m.get('RBV')) # this is the RBV field
#m.add_callback('RBV', self.update_label)
#m.set_callback('RBV', self.emit_signals, {'source_field': 'RBV'})
#/home/zamofing_t/.local/lib/python3.8/site-packages/epics/motor.py
m.add_callback('RBV', self.OnChangedRBV)
#print(pv)
# def update_label(self, **kwargs):
# _log.info(kwargs)
def OnConnectionChange(self, pvname=None, conn=None, **kws):
pvc=self._pvConnected
if conn:
pvc+=1
else:
if pvc>0: pvc-=1
_log.info(f'PV connection {pvc}/{len(self._pvDict)}: {pvname} {conn}')
self._pvConnected=pvc
# def emit_signals(self, **kw):
# _log.info(kw)
app=QApplication.instance()
wGrp=app._wndVisualize._wdGrpDraw
key=self._pv2key[pvname]
wLb=wGrp.findChild(QLabel, key)
if not conn:
v=f"<font color='#a00000'>{key}</font>"
wLb.setText(v)
else:
v=f"<font color='#00a000'>{key}</font>"
wLb.setText(v)
def OnValueChange(self, pvname, value, **kw):
#def OnChangedRBV(self, **kw):
def OnChangedRBV(self, pvname, value, **kw):
#_log.info(kw)
#{
# 'pvname': 'SATES30-ARES:MOT_2TRY.RBV',
@@ -434,19 +515,43 @@ class WndVisualize(QWidget):
# 'cb_info': (1, <PV 'SATES30-ARES:MOT_2TRY.RBV', count=1, type=time_double, access=read-only>)
#}
#_log.info(f"{kw['pvname']}:{kw['value']}")
_log.info(f"{pvname}:{value}")
if pvname=='SATES30-RIXS:MOT_RY.RBV':
self.sldChanged('aArm',value)
elif pvname=='SATES30-ARES:MOT_JFRY.RBV':
self.sldChanged('aJFr',value)
elif pvname=='SATES30-ARES:MOT_2TRY.RBV':
self.sldChanged('a2Th',value)
elif pvname=='SATES30-ARES:MOT_DRY.RBV':
self.sldChanged('aDet',value)
elif pvname=='SATES30-ARES:MOT_SRY.RBV':
self.sldChanged('aTrg',value)
else:
_log.info(f"PV val:{pvname}:{value}")
try:
key=self._pv2key[pvname]
except KeyError as e:
_log.warning(f"can't handle PV: {pvname}:{value}")
return
self.vis_update(key,value,'008000')
def vis_update(self,key,value,col='000000'):
app=QApplication.instance()
wGrp=app._wndVisualize._wdGrpDraw
wSl=wGrp.findChild(QSlider, key)
wSl.blockSignals(True)
wSl.setValue(int(value)) # move the slider without emiting a signal
wSl.blockSignals(False)
self.sldChanged(key,value) # emit the signal
#self.event_update.emit(pvname=pvname, value=None)
wLb=wGrp.findChild(QLabel, key)
v=f"<font color='#{col}'>{key}</font>"
wLb.setText(v)
def liveView(self):
# try to live update all PVs
_log.info('')
p2k=self._pv2key
for pv in self._pvDict.values():
pvn=pv.pvname
key=p2k[pvn]
if pv.connected:
value=pv.get()
self.vis_update(key,value,'008000')
print (pvn,key,value)
else:
print (pvn,key)
pass
def destroy(self, destroyWindow, destroySubWindows): #overloaded function
_log.info('destroy')
@@ -477,6 +582,11 @@ class WndVisualize(QWidget):
g[key]=90-val/sclA
else:
g[key]=val
wGrp=app._wndVisualize._wdGrpDraw
wLb=wGrp.findChild(QLabel, key)
v=f"<font color='#0000ff'>{key}</font>"
wLb.setText(v)#print(wLb,v)
self.update()
def mouseReleaseEvent(self, a0):
@@ -494,6 +604,7 @@ class WndVisualize(QWidget):
wGrp=self._wdGrpDraw
#if wGrp.underMouse(): #draging sliders?
if wGrp.geometry().contains(mousePos):
self.liveView()
self._mouseDrag={'obj':wGrp, 'start':mousePos}
return
dev=app._dev
@@ -513,7 +624,7 @@ class WndVisualize(QWidget):
v=int(round(np.log2(v)*2))
else:
v=devP[key]
wSl.setValue(v)
wSl.setValue(int(v))
self._mouseDrag={'obj':dev,'start':(mousePos,dev._paint['ofs'])}
try:
@@ -578,7 +689,7 @@ class WndVisualize(QWidget):
if __name__ == '__main__':
import argparse
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.basicConfig(level=logging.DEBUG, handlers=[logHandler()], format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
def main():