total rework of ARESvis

This commit is contained in:
2024-08-28 18:13:15 +02:00
parent 898ce0d131
commit 2e0600128c

View File

@@ -7,7 +7,15 @@
# *-----------------------------------------------------------------------*
"""
SwissFEL Furka ARES visualization
Furka ARES chamber visualization
self.pv_angles = [epics.PV("SATES30-ARES:MOT_SRY.RBV"), epics.PV("SATES30-ARES:MOT_DRY.RBV"), epics.PV("SATES30-ARES:MOT_2TRY.RBV")]
SATES30-ARES:MOT_2TRY.RBV # 2thetha angle
SATES30-ARES:MOT_DRY.RBV # detector angle
SATES30-RIXS:MOT_RY.RBV # sliding seal
SATES30-ARES:MOT_SRY.RBV # sample rotation
bitmask for simulation:
0x01: EPICS motors
@@ -21,152 +29,437 @@ bitmask for simulation:
"""
import sys, logging
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 pyqtgraph.Qt import QtCore, QtGui
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtW
from PyQt5.uic import loadUiType
import numpy as np
import sys, logging, copy
import epics
_log=logging.getLogger(__name__)
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QSlider
from PyQt5.QtGui import QPainter, QColor, QPen
#import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import numpy as np
class ARESdevice():
_lutDifrBeamPaint=( # (number of difr beam,draw mode,alpha,width)
(4, 2, 190, 0),
(4, 1, 200, 0),
(32, 1, 120, 0),
(8, 1, 196, 0),
(32, 0, 120, 3),
(32, 2, 255, 0),
)
class WndVisualizeARES(QWidget):
_lutColor={0:(255,0,0),1:(0,255,0),2:(255,255,0)}
def __init__(self,name,**kwargs):
self._name=name
self._paint=p={
'ofs':(500, 500), # location of device
'rArm':150, # radius of chamber (RIXS arm)
'r2Th':120, # radius 2Theta platfform
'rDet':80, # radius of detector
'rTrg':10, # radius of target
'szArm':(20, 50),
'aaArm':100, # angle RIXS arm
'aa2Th':110, # angle 2thetha
'aaDet':120, # angle detector
'aaTrg':130, # angle target
'mode':4, #difraction beam paint mode
'szG':(200, 5), # size VLS grating
'szD':(150, 5), # size detector
'sclTrf':(2**(6/2), 2**(0/2)), # scaling transfformation [angle, distance]
}
# 2thetha angle SATES30-ARES:MOT_2TRY
# detector angle SATES30-ARES:MOT_DRY
# sliding seal SATES30-RIXS:MOT_RY
p.update(kwargs)
self._geo=g={
'r1':2000, # distance probe grating
'r2':3500, # distance grating detector
'aa':88, # grating angle
'bb':87, # reflection angle
'cc':22, # detector angle
}
self.setGeometry(g)
def setGeometry(self,geo):
self._geo=geo
p=self._paint
sclA,sclD=p['sclTrf']
p.update({
'r1':int(geo['r1']*sclD),
'r2':int(geo['r2']*sclD),
'aa':int((90-geo['aa'])*sclA),
'bb':int((90-geo['bb'])*sclA),
'cc':int(geo['cc']),
})
def geometry2motor(self):
# returns raw motor positions
# offset detector plane to deflected beam: 34deg
geo=self._geo
r1,r2,aa,bb,cc=geo['r1'],geo['r2'],geo['aa'],geo['bb'],geo['cc']
mt=gtz=gty1=gty2=grx=gtx=dtz=dty1=dty2=drx=None
degArm=90-aa+90-bb
radArm=np.deg2rad(degArm)
gtz=r1
grx=90-aa
dtz=np.cos(radArm)*r2
dty1=dty2=np.sin(radArm)*r2
drx=90-aa+90-bb+cc-34
dd=cc-34 # angle of bellow to detector
geo.update({
'mt':mt,
'gtz':gtz,
'gty1':gty1,
'gty2': gty2,
'grx':grx,
'gtx':gtx,
'dtz':dtz,
'dty1':dty1,
'dty2':dty2,
'drx':drx})
if degArm>10:
raise(ValueError('angle arm > 10°'))
elif degArm<1:
raise(ValueError('angle arm < 1°'))
elif abs(dd)>15:
raise(ValueError('angle bellow to detector > 15°'))
def containsPoint(self,point):
try:
pg=self._polygon
except AttributeError:
return False
return pg.containsPoint(point,Qt.OddEvenFill)
@staticmethod
def plotOrig(qp):
penR=QPen(QtCore.Qt.red, 2, QtCore.Qt.SolidLine)
penG=QPen(QtCore.Qt.green, 2, QtCore.Qt.SolidLine)
pOrig=qp.pen()
qp.setPen(penR)
qp.drawLine(-20, 0, 20, 0)
qp.drawLine( 20, 0, 16, 2)
qp.setPen(penG)
qp.drawLine(0, -20, 0, 20)
qp.drawLine(0, 20, 2, 16)
qp.setPen(pOrig)
def paint(self,qp):
# qp QPainter to paint on
# ofs caanter to draw
# scl scaling for x and y translation of coordinate systems
# paintMode: mode how to paint the diffraction beam
p=self._paint
ofs=p['ofs']
rArm=p['rArm']
r2Th=p['r2Th']
rDet=p['rDet']
rTrg=p['rTrg']
aaArm=p['aaArm']
aa2Th=p['aa2Th']
aaDet=p['aaDet']
aaTrg=p['aaTrg']
sclTrf=p['sclTrf']
# --- prepare transformations ---
# tf0: target not rotated
# tfArm: target center rotated angle aaArm
# tf2Th: target center rotated angle tf2Th
# tfDet: target center rotated angle aaDet
# tfTrg: target center rotated angle aaTrg
tf0=QTransform()
tf0.translate(ofs[0], ofs[1])
tf0.scale(sclTrf[1],sclTrf[1])
tfArm=copy.copy(tf0) # center
tfArm.rotate(aaArm)
tf2Th=copy.copy(tf0) # center
tf2Th.rotate(aa2Th)
tfDet=copy.copy(tf0)
tfDet.rotate(aaDet)
tfTrg=copy.copy(tf0)
tfTrg.rotate(aaTrg)
#tfd.translate(r2,0).rotate(-cc)
penBk=QPen(QtCore.Qt.black, 0, QtCore.Qt.SolidLine)
penWt=QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine)
penYl=QPen(QtCore.Qt.yellow, 1, QtCore.Qt.SolidLine)
penBl=QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine)
penRd=QPen(QtCore.Qt.red, 1, QtCore.Qt.SolidLine)
# --- visualize ---
#qp.setRenderHints(QPainter.HighQualityAntialiasing)
# setup and plot dragable region
#pl=[QPoint(*tf0.map (-rArm , -rArm)),QPoint(*tf0.map (-rArm , +rArm+50)),QPoint(*tf0.map (+rArm+50+aaArm, +rArm+50+aa2Th)),QPoint(*tf0.map(+rArm+50+aaDet , -rArm-50)),]
self._polygon=QPolygon([
QPoint(*tf0.map(-rArm ,-rArm)),
QPoint(*tf0.map(-rArm ,+rArm+100)),
QPoint(*tf0.map(+rArm,+rArm+100)),
QPoint(*tf0.map(+rArm,-rArm)),])
qp.setBrush(QColor(0, 0, 0,64))
qp.drawPolygon(self._polygon)
# plot beam path
qp.setTransform(tf0)
qp.setPen(penBk)
qp.setBrush(QColor(128, 128, 128, 128)) #r,g,b,a
#circles of rtation
qp.drawEllipse(-rArm, -rArm, 2*rArm, 2*rArm) # ARES chamber
qp.drawEllipse(-r2Th, -r2Th, 2*r2Th, 2*r2Th) # 2theta
qp.drawEllipse(-rDet, -rDet, 2*rDet, 2*rDet) # detector
qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
#beam arrow
qp.setPen(QPen(QtCore.Qt.black, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,+rArm+100,0,rArm)
qp.drawPolygon(QPolygon([QPoint(0,rArm),QPoint(-5,rArm+20),QPoint(+5,rArm+20),]))
#self.plotOrig(qp)
#qp.setPen(penRd)
#qp.drawRect(-10, -10+rArm, 20, 20)
#RIXS-arm devices
qp.setTransform(tfArm)
qp.setPen(QPen(QtCore.Qt.red, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,rArm,0,rArm+10)
qp.setPen(penBk)
qp.setBrush(QColor(255,0,0,128))
qp.drawRect(-15, -20+rArm, 5, 50) # foc. mirror 1
qp.drawRect(+10, -20+rArm, 5, 50) # foc. mirror 2
#2-theta devices
qp.setTransform(tf2Th)
qp.setPen(QPen(QtCore.Qt.green, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,r2Th,0,r2Th+10)
qp.setPen(penBk)
qp.setBrush(QColor(0,255,0,128))
qp.drawRect(-20, -20+r2Th, 15, 15) # 2th mount sample
#detector devices
qp.setTransform(tfDet)
qp.setPen(QPen(QtCore.Qt.blue, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,rDet,0,rDet+10)
qp.setPen(penBk)
qp.setBrush(QColor(0,0,255,128))
qp.drawRect(+20, -20+rDet, 10, 15) # detector mount sample
#target devices
qp.setTransform(tfTrg)
qp.setPen(QPen(QtCore.Qt.cyan, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,rTrg,0,rTrg+10)
qp.setPen(penBk)
qp.setBrush(QColor(0,255,255,128))
qp.drawRect(+20, +20+rTrg, 10, 10) # target mount sample
#qp.setPen(penBk)
#qp.drawRect(-10, -10+rArm, 20, 20)
#self.plotOrig(qp)
#qp.setTransform(tf2Th)
#qp.setPen(penYl)
#qp.drawRect(-10, -10+r2Th, 20, 20)
#self.plotOrig(qp)
#qp.setTransform(tfDet)
#qp.setPen(penBl)
#qp.drawRect(-10, -10+r2Th+20, 20, 20)
#qp.setTransform(tfTrg)
#qp.setPen(penBl)
#qp.drawRect(-10, -10+r2Th+20, 20, 20)
#self.plotOrig(qp)
#qp.setCompositionMode(QtGui.QPainter.CompositionMode_Lighten)
#qp.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
#mouse move polygon
#qp.setTransform(QTransform())
#qp.setPen(penRd)
#qp.drawPolygon(self._polygon)
#origin crosses
#for tf in (tf0,tfa,tfab,tfc):#,tfg,tfs):#,tfc,tfs):
# qp.setTransform(tf);self.plotOrig(qp)
class WndVisualize(QWidget):
def __init__(self):
super().__init__()
self._param={'ctr':(200, 320), # location of big chamber
'rCmb':150, # radius of chamber
'rTrg':10, # radius of target
'r2Th':100, # radius 2Theta platfform
'szSeal': (20,50),
'szArm': (80,250),
'aaSeal':70, # RIXS sliding angle
'aaArm':70, # RIXS arm angle
'airPads':0, # air pads (off=0 on=1 undef=2)
'defComp':0, # deformation compensation (off=0 on=1 undef=2)
}
# 2thetha angle SATES30-ARES:MOT_2TRY
# detector angle SATES30-ARES:MOT_DRY
# sliding seal SATES30-RIXS:MOT_RY
self.initUI()
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 850, 800)
self.setWindowTitle('ARES visualize')
#label = QLabel('Python', self)
#label.move(50,50)
#b1 = QPushButton("Button1",self)
#b1.move(400,150)
self._wdGrpDraw=w=QGroupBox("ARES chamber",self)
w.move(10,10)
l=QVBoxLayout(w)
self.setGeometry(560, 100, 1300, 800)
self.setWindowTitle('Visualize')
app=QApplication.instance()
dev=app._dev
self._wdGrpDraw=w=QGroupBox(dev._name,self)
w.move(10,10)
row=0
lg=QGridLayout(w)
pDev=dev._paint
for key, rng, tk in (
('aaArm',(0,360,), 30), # angle ARES sliding seal
('aa2Th',(0,360,), 30), # angle 2thetha
('aaDet',(0,360,), 30), # angle detector
('aaTrg',(0,360,), 30), # angle target
('sclA', ( -8, 8), 1),
('sclD', (-8, 8), 1),):
sld={}
for key,rng,tk in (('aaSeal',(0,180),5),('aaArm',(0,180),5),('airPads',(0,2),1),('defComp',(0,2),1),):
sl=QSlider(QtCore.Qt.Horizontal,objectName=key)
sl.setFixedWidth(200);sl.setMinimum(rng[0]);sl.setMaximum(rng[1])
sl.setValue(self._param[key])
sl.setTickPosition(QSlider.TicksBelow);sl.setTickInterval(tk)
l.addWidget(sl)
sl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
sld[key]=sl
wLb=QLabel(key)
wSl=QSlider(QtCore.Qt.Horizontal,objectName=key)
wSl.setFixedWidth(200);wSl.setMinimum(rng[0]);wSl.setMaximum(rng[1])
if key.startswith('scl'):
if key[-1]=='A':
v=pDev['sclTrf'][0]
else:
v=pDev['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=pDev[key]
wSl.setValue(v)
wSl.setTickPosition(QSlider.TicksBelow);wSl.setTickInterval(tk)
wSl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
self.show()
lg.addWidget(wLb, row, 0)
lg.addWidget(wSl, row, 1);row+=1
self.show()
def destroy(self, destroyWindow, destroySubWindows): #overloaded function
_log.info('destroy')
def closeEvent(self, event): #overloaded function
_log.info('closeEvent')
def sldChanged(self,key,val,*args,**kwargs):
p=self._param
#print(key,val)
if key=='aaArm':
p['aaSeal']=v=p['aaSeal']-p['aaArm']+val
wSl=self._wdGrpDraw.findChild(QSlider, 'aaSeal')
wSl.blockSignals(True)
wSl.setValue(int(v))
wSl.blockSignals(False)
sl=QSlider(QtCore.Qt.Horizontal,objectName=key)
app=QApplication.instance()
dev=app._dev
p=dev._paint
if key.startswith('scl'):
wGrp=self._wdGrpDraw
if key[-1]=='A':
p['sclTrf']=(2**(val/2),p['sclTrf'][1])
else:
p['sclTrf']=(p['sclTrf'][0],2**(val/2))
print(p['sclTrf'])
dev.setGeometry(dev._geo)
else:
p[key]=val
g=dev._geo
if key in ('r1','r2'):
sclD=p['sclTrf'][1]
g[key]=val/sclD
elif key in ('aa', 'bb'):
sclA=p['sclTrf'][0]
g[key]=90-val/sclA
else:
g[key]=val
self.update()
p[key]=val
def mouseReleaseEvent(self, a0):
try:
del self._mouseDrag
except AttributeError:
pass
def mousePressEvent(self, a0):
app=QApplication.instance()
mousePos=a0.pos()
print(a0.type)
if a0.type()!=QtGui.QMouseEvent.MouseButtonPress:
return
wGrp=self._wdGrpDraw
#if wGrp.underMouse(): #draging sliders?
if wGrp.geometry().contains(mousePos):
self._mouseDrag={'obj':wGrp, 'start':mousePos}
return
dev=app._dev
if dev.containsPoint(mousePos):
self._devSel=dev
wGrp=self._wdGrpDraw
wGrp.setTitle(dev._name)
devP=dev._paint
for wSl in wGrp.findChildren(QSlider):
#_log.info(wSl)
key=wSl.objectName()
if key.startswith('scl'):
if key[-1]=='A':
v=devP['sclTrf'][0]
else:
v=devP['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=devP[key]
wSl.setValue(v)
self._mouseDrag={'obj':dev,'start':(mousePos,dev._paint['ofs'])}
try:
_log.info(f'{self._mouseDrag}')
except AttributeError:
_log.info(f'no object to drag')
def mouseMoveEvent(self, a0):
try:
md=self._mouseDrag
except AttributeError:
return
obj=md['obj']
s=md['start']
if obj==self._wdGrpDraw:
p=a0.pos()
md['start']=p
p=obj.geometry().topLeft()+p-s
obj.move(p)
return
p=a0.pos()
ofs=QPoint(*s[1])+p-s[0]
_log.info(f'{p} {ofs}')
obj._paint['ofs']=(ofs.x(),ofs.y())
self.update()
def paintEvent(self, e):
p=self._param
ctr=p['ctr']
aaSeal=p['aaSeal']
aaArm=p['aaArm']
rCmb=p['rCmb']
r2Th=p['r2Th']
rTrg=p['rTrg']
xa,ya=p['szArm']
xs,ys=p['szSeal']
ap=p['airPads']
dc=p['defComp']
qp = QPainter()
qp.begin(self)
qp.translate(ctr[0],ctr[1])
tf0=qp.transform()
qp.setPen(QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine))
qp.setBrush(QColor(155, 155, 155, 128))
br1=qp.brush()
qp.drawEllipse(-rCmb, -rCmb, 2*rCmb, 2*rCmb) # big chamber
qp.drawEllipse(-r2Th, -r2Th, 2*r2Th, 2*r2Th) # big chamber
qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
qp.drawLine(0,-rCmb-50,0,-rTrg) #beam
qp.rotate(-aaSeal)
qp.translate(0,rCmb)
tf1=qp.transform()
xs2=xs//2
xa2=xa//2
ya2=ya//2
d=int(abs(aaSeal-aaArm)*20)
r=min(155+d,255)
gb=max(155-d,0)
qp.setBrush(QColor(r, gb, gb, 255))
qp.drawRect(-xs2, 0, xs, ys) # seal bellow
qp.setBrush(br1)
qp.setTransform(tf0)
qp.rotate(-aaArm)
qp.translate(0,rCmb+ys)
tf2=qp.transform()
qp.drawRect(-xa2, 0, xa, ya) # girder
#qp.drawEllipse(-ya2, 100+150, 20, 20) #pusher left
#qp.drawEllipse( ya2-r, 100+150, 20, 20) #pusher right
#air pad
r,g,b=WndVisualizeARES._lutColor[ap]
rd=13
r3=int(rd*np.sqrt(3))
qp.setBrush(QColor(r, g, b, 255))
qp.drawEllipse(-xa2-rd, ya-4*rd, 2*rd, 2*rd) #left
qp.drawEllipse(-xa2-rd+r3,ya-3*rd, 2*rd, 2*rd) #left
qp.drawEllipse(-xa2-rd+r3,ya-5*rd, 2*rd, 2*rd) #left
qp.drawEllipse( xa2-rd, ya-4*rd, 2*rd, 2*rd) #right
qp.drawEllipse( xa2-rd-r3,ya-3*rd, 2*rd, 2*rd) #right
qp.drawEllipse( xa2-rd-r3,ya-5*rd, 2*rd, 2*rd) #right
qp.drawEllipse( -rd, 3*rd, 2*rd, 2*rd) #grating
qp.drawEllipse( 0, 3*rd-r3, 2*rd, 2*rd) #grating
qp.drawEllipse( -2*rd, 3*rd-r3, 2*rd, 2*rd) #grating
qp.setBrush(br1)
#deformation compensation
r,g,b=WndVisualizeARES._lutColor[dc]
rd=14
qp.setBrush(QColor(r, g, b, 255))
qp.drawEllipse(-xa2, ya2, 2*rd, 2*rd) #air pad left
qp.drawEllipse( xa2-2*rd, ya2, 2*rd, 2*rd) #air pad right
#qp.drawEllipse( -rd, rd, 2*rd, 2*rd) #air pad grating
qp.setBrush(br1)
qp.setRenderHints(QPainter.HighQualityAntialiasing)
app=QApplication.instance()
dev=app._dev
app._dev.paint(qp)
qp.end()
def updateDevice(self, dev):
self._devSel=dev
devP=dev._paint
wGrp=self._wdGrpDraw
wGrp.setTitle(dev._name)
for wSl in wGrp.findChildren(QSlider):
# _log.info(wSl)
key=wSl.objectName()
if key.startswith('scl'):
if key[-1]=='A':
v=devP['sclTrf'][0]
else:
v=devP['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=devP[key]
wSl.blockSignals(True)
wSl.setValue(v)
wSl.blockSignals(False)
self.update()
def OnEvent(self,*args,**kwargs):
#test event
print(f'OnEvent: {args} ,{kwargs}')
if __name__ == '__main__':
@@ -184,12 +477,11 @@ if __name__ == '__main__':
app=QApplication(sys.argv)
app._args=args
#devUS=RIXSdevice('RIXS us', ofs=(750, 200), g=-100, s=-1500, sy=-20, cy=70)
#devDS=RIXSdevice('RIXS ds', ofs=(930, 630))
app._dev=dev=ARESdevice('Furka-ARES')
if args.mode&0x01:
app._wndVis=WndVisualizeARES()
#if args.mode&0x02:
app._wndVisualize=wnd=WndVisualize()
wnd.show()
sys.exit(app.exec_())
main()