added README.md, complete the influx for V2

This commit is contained in:
Ryan Tang 2024-07-31 16:16:51 -05:00
parent 760e2387d7
commit 6bbf93bf7d
5 changed files with 266 additions and 26 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
__pycache__
*.txt
*.csv

209
HVGUI.py
View File

@ -3,22 +3,59 @@
import HVLibrary as hv import HVLibrary as hv
import sys import sys
import datetime
import csv
import socket
import time
print("================== SOLARIS HV Controller for CAEN SY4527")
#-----------assign a port, to prevent the script run mulitple time
s = socket.socket()
host = socket.gethostname()
port = 4305
s.bind((host,port))
#------ database
import influxdb_client
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS
with open('INFLUX_TOKEN.txt', 'r') as f:
token = f.readline().replace('\n', '')
org = "Argonne National Laboratory"
databaseIP = "http://192.168.0.200:8086"
write_client = influxdb_client.InfluxDBClient(url=databaseIP, token=token, org=org)
bucket = "CAEN_SY4527"
write_api = write_client.write_api(write_options=ASYNCHRONOUS)
print("------- Database")
print(" IP : " + databaseIP)
print(" Buket : " + bucket)
print(" token : " + token)
pushToDB = False
print("--------- Generating GUI")
#------ GUI
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QCheckBox, QLineEdit, QLabel, QVBoxLayout, QWidget, QTabWidget, QGridLayout, QMessageBox, QFileDialog, QProgressBar from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QCheckBox, QLineEdit, QLabel, QVBoxLayout, QWidget, QTabWidget, QGridLayout, QMessageBox, QFileDialog, QProgressBar
from PyQt6.QtCore import Qt, QThread, QTimer, QObject, pyqtSignal from PyQt6.QtCore import Qt, QThread, QTimer, QObject, pyqtSignal
from functools import partial from functools import partial
ColStrList = ["Name", "Ch", 'On/Off', "Set V [V]", "Set I [uA]", "Out V [V]", "Out I [uA]"]
nMod = 2 nMod = 2
nChPerMod = [48, 48] nChPerMod = [48, 48]
updateTime = 3 #sec updateTime = 3 #sec
######################################
bd = [] bd = []
for k in range(nMod): for k in range(nMod):
bd.append(hv.Board(3 + k)) bd.append(hv.Board(3 + k))
ColStrList = ["Name", "Ch", 'On/Off', "Set V [V]", "Set I [uA]", "Out V [V]", "Out I [uA]"]
NameList = []
V0SetList = [] V0SetList = []
I0SetList = [] I0SetList = []
OnOffList = [] OnOffList = []
@ -26,6 +63,7 @@ outVList = []
outIList = [] outIList = []
for k in range(nMod): for k in range(nMod):
NameList.append(bd[k].GetPV('Name'))
V0SetList.append(bd[k].GetPV('V0Set')) V0SetList.append(bd[k].GetPV('V0Set'))
I0SetList.append(bd[k].GetPV('I0Set')) I0SetList.append(bd[k].GetPV('I0Set'))
OnOffList.append(bd[k].GetPV('Pw')) OnOffList.append(bd[k].GetPV('Pw'))
@ -37,7 +75,7 @@ class MyWindow(QMainWindow):
super().__init__() super().__init__()
self.setWindowTitle("Iseg Controller") self.setWindowTitle("Iseg Controller")
self.setGeometry(100, 100, 500, 200) self.setGeometry(100, 100, 600, 200)
widget = QWidget() widget = QWidget()
layout = QVBoxLayout() layout = QVBoxLayout()
@ -56,6 +94,34 @@ class MyWindow(QMainWindow):
self.timer.start(updateTime*1000) self.timer.start(updateTime*1000)
self.time = 0 self.time = 0
#=========== database and refresh time
gLayout = QGridLayout()
layout.addLayout(gLayout)
# lbIP = QLabel("Database IP : ", self)
# lbIP.setAlignment(Qt.AlignmentFlag.AlignRight)
# gLayout.addWidget(lbIP, 0, 0)
# self.txtIP = QLineEdit(self)
# self.txtIP.setText(IP)
# self.txtIP.textChanged.connect(partial(self.TextChange, self.txtIP))
# self.txtIP.returnPressed.connect(partial(self.UnSetTextColor, self.txtIP))
# gLayout.addWidget(self.txtIP, 0, 1)
lb1 = QLabel("Refresh period [sec] :", self)
lb1.setAlignment(Qt.AlignmentFlag.AlignRight)
gLayout.addWidget(lb1, 1, 0)
self.txtRefresh = QLineEdit(self)
self.txtRefresh.setText(str(updateTime))
self.txtRefresh.textChanged.connect(partial(self.TextChange, self.txtRefresh))
self.txtRefresh.returnPressed.connect(self.SetTimer)
self.txtRefresh.returnPressed.connect(partial(self.UnSetTextColor, self.txtRefresh))
gLayout.addWidget(self.txtRefresh, 1, 1)
self.chkDB = QCheckBox("Enable DataBase Output", self)
gLayout.addWidget(self.chkDB, 0, 2)
#=========== set tab #=========== set tab
self.tabWidget = QTabWidget(self) self.tabWidget = QTabWidget(self)
layout.addWidget(self.tabWidget) layout.addWidget(self.tabWidget)
@ -74,8 +140,13 @@ class MyWindow(QMainWindow):
for j, lb in enumerate(ColStrList) : for j, lb in enumerate(ColStrList) :
#------------ name #------------ name
if j == 0: if j == 0:
self.txtName[k][i].setText(NameList[k][i])
layout_tab.addWidget(self.txtName[k][i], 1+i, j) layout_tab.addWidget(self.txtName[k][i], 1+i, j)
self.txtName[k][i].returnPressed.connect(partial(self.SetName, k, i) )
self.txtName[k][i].returnPressed.connect(partial(self.UnSetTextColor, self.txtName[k][i]))
self.txtName[k][i].textChanged.connect(partial(self.TextChange, self.txtName[k][i]))
#------------ Ch #------------ Ch
if j == 1: if j == 1:
qlb = QLabel(str(i), tab) qlb = QLabel(str(i), tab)
@ -110,7 +181,7 @@ class MyWindow(QMainWindow):
# #------------ V out # #------------ V out
if j == 5: if j == 5:
self.txtVOut[k][i].setText("{:.2f}".format(outVList[k][i])) self.txtVOut[k][i].setText("{:.3f}".format(outVList[k][i]))
self.txtVOut[k][i].setAlignment(Qt.AlignmentFlag.AlignRight) self.txtVOut[k][i].setAlignment(Qt.AlignmentFlag.AlignRight)
layout_tab.addWidget(self.txtVOut[k][i], 1+i, j) layout_tab.addWidget(self.txtVOut[k][i], 1+i, j)
self.txtVOut[k][i].setReadOnly(True) self.txtVOut[k][i].setReadOnly(True)
@ -118,21 +189,41 @@ class MyWindow(QMainWindow):
# #------------ I out # #------------ I out
if j == 6: if j == 6:
self.txtIOut[k][i].setText("{:.2f}".format(outIList[k][i])) self.txtIOut[k][i].setText("{:.3f}".format(outIList[k][i]))
self.txtIOut[k][i].setAlignment(Qt.AlignmentFlag.AlignRight) self.txtIOut[k][i].setAlignment(Qt.AlignmentFlag.AlignRight)
layout_tab.addWidget(self.txtIOut[k][i], 1+i, j) layout_tab.addWidget(self.txtIOut[k][i], 1+i, j)
self.txtIOut[k][i].setReadOnly(True) self.txtIOut[k][i].setReadOnly(True)
self.txtIOut[k][i].setStyleSheet("background-color: lightgrey; color: black;") self.txtIOut[k][i].setStyleSheet("background-color: lightgrey; color: black;")
self.tabWidget.addTab(tab, "Mod-" + str(k)) self.tabWidget.addTab(tab, "Mod-" + str(k+3))
#============= Save setting
sLayout = QGridLayout()
layout.addLayout(sLayout)
bLoad = QPushButton("Load", self)
bLoad.clicked.connect(self.LoadSetting)
sLayout.addWidget(bLoad, 0, 0)
self.txtFile = QLineEdit(self)
self.txtFile.textChanged.connect(partial(self.TextChange, self.txtFile))
sLayout.addWidget(self.txtFile, 0, 1)
bSave = QPushButton("Save", self)
bSave.clicked.connect(self.SaveSetting)
sLayout.addWidget(bSave, 0, 3)
#================================= #=================================
def UnSetTextColor(self, qLineEdit : QLineEdit): def UnSetTextColor(self, qLineEdit : QLineEdit):
qLineEdit.setStyleSheet("") qLineEdit.setStyleSheet("")
def TextChange(self, qLineEdit : QLineEdit): def TextChange(self, qLineEdit : QLineEdit):
qLineEdit.setStyleSheet("color : darkgreen;") qLineEdit.setStyleSheet("color : green;")
def SetTimer(self):
sec = float(self.txtRefresh.text())
self.timer.stop()
self.timer.start(int(sec * 1000))
#--------------------------------- #---------------------------------
def SetOnOff(self, mod, ch): def SetOnOff(self, mod, ch):
@ -147,6 +238,15 @@ class MyWindow(QMainWindow):
print("mod : " + str(mod) + ", ch : " + str(ch) + " | " + str(state) + " | " + str(value)) print("mod : " + str(mod) + ", ch : " + str(ch) + " | " + str(state) + " | " + str(value))
self.chkON[mod][ch].setChecked(value == 1) self.chkON[mod][ch].setChecked(value == 1)
#---------------------------------
def SetName(self, mod, ch):
value = self.txtName[mod][ch].text()
print("mod : " + str(mod) + ", ch : " + str(ch) + " | " + value)
bd[mod].Channel[ch].SetName(value)
newValue = bd[mod].Channel[ch].GetName()
NameList[mod][ch] = newValue
self.txtName[mod][ch].setText(newValue)
#--------------------------------- #---------------------------------
def SetHV(self, mod, ch): def SetHV(self, mod, ch):
value = float(self.txtV[mod][ch].text()) value = float(self.txtV[mod][ch].text())
@ -168,31 +268,100 @@ class MyWindow(QMainWindow):
#--------------------------------- #---------------------------------
def updateTimer(self): def updateTimer(self):
self.time += 1 self.time += 1
print(f'Time: {self.time}') # print(f'Time: {self.time}')
# outVList = mpod.GetAllOutputHV()
# outIList = mpod.GetAllLC()
# # print(outVList)
outVList.clear()
outIList.clear()
for k in range(2): for k in range(2):
outVList.append(bd[k].GetPV('VMon')) outVList.append(bd[k].GetPV('VMon'))
outIList.append(bd[k].GetPV('IMon')) outIList.append(bd[k].GetPV('IMon'))
# if self.chkDB.checkState() == Qt.CheckState.Checked: if self.chkDB.checkState() == Qt.CheckState.Checked:
# points = [] points = []
for k in range(0, nMod): for k in range(0, nMod):
for i in range(bd[k].numCh) : for i in range(bd[k].numCh) :
self.txtVOut[k][i].setText("{:.2f}".format(outVList[k][i])) self.txtVOut[k][i].setText("{:.3f}".format(outVList[k][i]))
self.txtIOut[k][i].setText("{:.2f}".format(outIList[k][i])) self.txtIOut[k][i].setText("{:.3f}".format(outIList[k][i]))
# if self.chkDB.checkState() == Qt.CheckState.Checked: if self.chkDB.checkState() == Qt.CheckState.Checked:
# points.append(Point("Voltage").tag("Ch",int(chList[i] + 100 * k)).field("value",float(outVList[i]))) points.append(Point("Voltage").tag("bd", int(k)).tag("Ch",int(i)).field("value",float(outVList[k][i])))
# points.append(Point("LeakageCurrent").tag("Ch",int(chList[i] + 100 * k)).field("value",float(outIList[i]))) points.append(Point("LeakageCurrent").tag("bd", int(k)).tag("Ch",int(i)).field("value",float(outIList[k][i])))
# if self.chkDB.checkState() == Qt.CheckState.Checked: if self.chkDB.checkState() == Qt.CheckState.Checked:
# write_api.write(bucket=bucket, org=org, record=points) write_api.write(bucket=bucket, org=org, record=points)
def SaveSetting(self):
fileName = self.txtFile.text()
if fileName == "" :
msg_box = QMessageBox()
msg_box.setWindowTitle("Information")
msg_box.setText("Type file name first, then save.")
msg_box.setIcon(QMessageBox.Icon.Information)
msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
msg_box.exec()
return
pos = fileName.rfind('.')
if pos == -1:
fileName = fileName + ".csv"
self.txtFile.setText(fileName)
outfile = open(fileName, "w")
csv_writer = csv.writer(outfile)
for k in range(0, nMod):
for i in range(bd[k].numCh) :
papap = [str(k), str(i), self.txtName[k][i].text(), self.txtV[k][i].text(),self.txtI[k][i].text()]
csv_writer.writerow( papap )
outfile.close()
self.txtFile.setStyleSheet("")
msg_box = QMessageBox()
msg_box.setWindowTitle("Information")
msg_box.setText("Setting saved to " + fileName + " as a csv file.")
msg_box.setIcon(QMessageBox.Icon.Information)
msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
msg_box.exec()
def LoadSetting(self):
file_path, _ = QFileDialog.getOpenFileName(self, "Open File", "", "CSV Files (*.csv);;All Files (*)")
if file_path:
infile = open(file_path, "r")
csv_reader = csv.reader(infile)
row_count = sum(1 for row in csv_reader)
infile.seek(0)
count = 0
for row in csv_reader:
mod = row[0]
ch = row[1]
if mod.isdigit() and ch.isdigit():
chID = int(ch)
modID = int(mod)
NameList[modID][chID] = row[2]
V0SetList[modID][chID] = float(row[3])
I0SetList[modID][chID] = float(row[4])
for k in range(nMod):
bd[k].PutPV('Name', NameList[k])
bd[k].PutPV('V0Set', V0SetList[k])
bd[k].PutPV('I0Set', I0SetList[k])
self.txtFile.setText(file_path)
self.txtFile.setStyleSheet("")
NameList.clear()
V0SetList.clear()
I0SetList.clear()
for k in range(0, nMod):
NameList.append(bd[k].GetPV('Name'))
V0SetList.append(bd[k].GetPV('V0Set'))
I0SetList.append(bd[k].GetPV('I0Set'))
for i in range(bd[k].numCh) :
self.txtName[k][i].setText(NameList[k][i])
self.txtV[k][i].setText(str(V0SetList[k][i]))
self.txtI[k][i].setText(str(I0SetList[k][i]))
self.txtName[k][i].setStyleSheet("")
self.txtV[k][i].setStyleSheet("")
self.txtI[k][i].setStyleSheet("")
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)

View File

@ -98,6 +98,9 @@ class Channel:
return get_value(self.bdCh + "PDown") return get_value(self.bdCh + "PDown")
def SetName(self, name : str):
set_value(self.bdCh + "Name", name)
def SetV0Set(self, volt: float): def SetV0Set(self, volt: float):
set_value(self.bdCh + "V0Set", volt) set_value(self.bdCh + "V0Set", volt)
@ -164,3 +167,13 @@ class Board:
for i in range(48): for i in range(48):
pvList.append('solarisHV:' + self.id + ":" + f"{i:03d}" + ":" + PV_Name) pvList.append('solarisHV:' + self.id + ":" + f"{i:03d}" + ":" + PV_Name)
return epics.caget_many(pvList) return epics.caget_many(pvList)
def PutPV(self, PV_Name : str, ValueList ):
pvList = []
for i in range(48):
pvList.append('solarisHV:' + self.id + ":" + f"{i:03d}" + ":" + PV_Name)
if len(ValueList) == len(pvList):
epics.caput_many(pvList, ValueList)
else:
print("PutPV: Value List size does not match channel number")

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# About
This is a HV controller for the python Qt6 GUI for CAEN SY4527 + A4528 CPU module. It communicate the built-in EPICS server to control and retrive parameters. It can also push the current Voltage and leakage current to influxDB via network connection.
# Require package
in Ubuntu 20+
~>sudo apt install python3-pyqt6 snmp snmp-mibs-downloader curl python3-pip
~>python3 -m pip install pyqt6 influxdb-client
# Code
### HVLibrary.py
this is the library to setup the basic of epics. User need to modify the IP and hostName.
### HVGUI.py
this is the GUI code.
# DataBase connection
The program use the influxdb_client to push data to a influx database. In influxDB V2, a token is needed for secure connection. For security, a token should be save in INFLUX_TOKEN.txt, the program will load it for token. The IP address, org, bucket are hardcoded, so please change it as needed.
# Usage
~>./HVGUI.py

35
test.py
View File

@ -52,6 +52,10 @@ import HVLibrary as hv
# hv.epics.caput('solarisHV:03:015:Pw', 1) # hv.epics.caput('solarisHV:03:015:Pw', 1)
# hv.epics.cainfo('solarisHV:03:015:Pw') # hv.epics.cainfo('solarisHV:03:015:Pw')
# hv.epics.cainfo('solarisHV:03:015:VMon')
# hv.epics.cainfo('solarisHV:03:015:V0Set')
# hv.epics.cainfo('solarisHV:03:015:Status')
# hv.epics.cainfo('solarisHV:03:BdStatus')
# m1 = hv.epics.caget('solarisHV:03:015:Pw', use_monitor= False) # m1 = hv.epics.caget('solarisHV:03:015:Pw', use_monitor= False)
@ -66,10 +70,31 @@ import HVLibrary as hv
# hv.epics.camonitor('solarisHV:03:015:Pw') # hv.epics.camonitor('solarisHV:03:015:Pw')
pvList = [] # pvList = []
for i in range(48): # pvValue = []
pvList.append('solarisHV:03:' + f"{i:03d}" + ":VMon") # for i in range(10):
# pvList.append('solarisHV:03:' + f"{i:03d}" + ":V0Set")
# pvValue.append(10)
haha = hv.epics.caget_many(pvList) # # haha = hv.epics.caget_many(pvList)
# haha = hv.epics.caput_many(pvList, pvValue)
# print(haha)
import influxdb_client
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS
ip = "192.168.0.200:8086"
write_client = influxdb_client.InfluxDBClient(url=ip)
bucket = "HV"
write_api = write_client.write_api(write_options=ASYNCHRONOUS)
points = []
points.append(Point("Voltage").tag("Bd", 3).tag("Ch", 0).field("value",float(10)))
write_api.write(bucket=bucket, record=points)
print(haha)