Files
2026-08-21 21:40:30 +02:00

369 lines
15 KiB
Python

from collections import deque
import random
import wx
import numpy as np
import epics
import datetime
import matplotlib.pyplot as plt
import matplotlib.dates
#from epics.wx import MotorPanel
from slic.gui.widgets import LabeledMathEntry
from slic.gui.widgets import make_filled_hbox, make_filled_vbox, EXPANDING
from slic.gui.widgets.plotting import PlotPanel
from bstrd import BS, bsstream
from epics import caget, caput, cainfo
from scipy.signal import savgol_filter
from decimal import Decimal
import time
def gauss(axis, amp, center, sigma):
return amp*np.exp( -1*(axis - center)**2/(2*sigma**2))
#nshots = 100
#channel_tof = "SATES21-GES1:A1_VALUES"
#ch_tof = BS(channel_tof)
#temp_tof = ch_tof.get()
#print(temp_tof)
#next(bsstream)
temp1_channel = 'SATES20-CWAG-GEP01:TEMP07'
temp2_channel = 'SATES20-CWAG-GEP01:TEMP08'
voltage1_channel = 'SATES20-CWAG-GEP01:DAC07_VOLTS'
voltage2_channel = 'SATES20-CWAG-GEP01:DAC08_VOLTS'
pressure_channel = 'SATES21-VM-VT2020:PRESSURE'
temp1_PV = epics.PV(temp1_channel)
temp2_PV = epics.PV(temp2_channel)
voltage1_PV = epics.PV(voltage1_channel)
voltage2_PV = epics.PV(voltage2_channel)
pressure_PV = epics.PV(pressure_channel)
iso_format = "%Y-%m-%d %H:%M:%S"
def create_text_entry(parent,text = "", maxlength = 5, hint = 0, setvalue = 0):
text = wx.StaticText(parent, -1, text)
textentry = wx.TextCtrl(parent)
textentry.SetMaxLength(maxlength)
textentry.SetHint('%s'%str(hint))
textentry.SetValue('%s'%str(setvalue))
return textentry, text
class MainPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.last_update_time = time.time()
self.quelength = 6000000
self.temp1deque = deque(maxlen=self.quelength)
self.temp2deque = deque(maxlen=self.quelength)
self.pressuredeque = deque(maxlen=self.quelength)
self.onOff = False
self.set_temp1 = 20
self.set_temp2 = 20
self.set_tempw1 = 1
self.set_tempw2 = 1
self.set_name1 = 'Sample'
self.set_name2 = 'Tip'
self.currenttemp1 = temp1_PV.get()
self.currenttemp2 = temp2_PV.get()
self.pressure = pressure_PV.get()
self.prev_error1 = self.set_temp1 - self.currenttemp1
self.sum_error1 = 0
self.ctl1 = 0
self.prev_error2 = self.set_temp2 - self.currenttemp2
self.sum_error2 = 0
self.ctl2 = 0
self.plottemp = plottemp = PlotPanel(self, figsize=(4,4))
plots1 = (plottemp,)
hb_plot1 = make_filled_hbox(plots1)
self.t0 = time.time()
self.tList = deque(maxlen=self.quelength)
self.set_twindow = 60 #Minutes
self.update_interval = 1 #Seconds
self.twindow_listlength = int((float(self.set_twindow*60)) / self.update_interval)
###### ROI box ######
self.temp1, self.tempt1 = create_text_entry(self, text = self.set_name1 + " Set", maxlength = 5, setvalue = self.set_temp1)
temp1box = make_filled_vbox((self.tempt1, self.temp1), border=1)
self.tempw1, self.tempwt1 = create_text_entry(self, text = self.set_name1 + " Window", maxlength = 5, setvalue = self.set_tempw1)
tempw1box = make_filled_vbox((self.tempwt1, self.tempw1), border=1)
self.temp2, self.tempt2 = create_text_entry(self, text = self.set_name2 + " Set", maxlength = 5, setvalue = self.set_temp2)
temp2box = make_filled_vbox((self.tempt2, self.temp2), border=1)
self.tempw2, self.tempwt2 = create_text_entry(self, text = self.set_name2 + " Window", maxlength = 5, setvalue = self.set_tempw2)
tempw2box = make_filled_vbox((self.tempwt2, self.tempw2), border=1)
self.btn_on = wx.Button(self, label="TC ON")
self.btn_off = wx.Button(self, label="TC OFF")
self.btn_on.SetBackgroundColour(wx.NullColour)
self.btn_off.SetBackgroundColour(wx.Colour(0,200,0))
btn_updatesettemp = wx.Button(self, label="Update Temperatures")
btn_clearplots = wx.Button(self, label="Clear Plots")
tempbox = make_filled_hbox(( self.btn_on, self.btn_off, temp1box, tempw1box, temp2box, tempw2box, btn_updatesettemp), border=1)
temp_box = make_filled_vbox((tempbox, hb_plot1))
self.plotpressure = plotpressure = PlotPanel(self, figsize=(4,4))
plots2 = (plotpressure,)
hb_plot2 = make_filled_hbox(plots2)
self.twindow, self.twindowt = create_text_entry(self, text = "Time Window [Minutes]", maxlength = 5, setvalue = self.set_twindow)
set_twindow_btn = wx.Button(self, label="Update Time Window")
self.pressure_min_ctrl, self.pressure_min_text = create_text_entry(self, text = "Pressure Y Minimum", maxlength = 15, setvalue = "1e-9")
self.pressure_max_ctrl, self.pressure_max_text = create_text_entry(self, text = "Pressure Y Maximum", maxlength = 15, setvalue = "1e-2")
set_pressure_btn = wx.Button(self, label="Update Pressure Y Axis")
self.pressure_min = Decimal('1e-9')
self.pressure_max = Decimal('1e-2')
twindow_box = make_filled_vbox((self.twindowt, self.twindow))
press_min_box = make_filled_vbox((self.pressure_min_text, self.pressure_min_ctrl))
press_max_box = make_filled_vbox((self.pressure_max_text, self.pressure_max_ctrl))
self.name1, self.namew1 = create_text_entry(self, text = "Temp 1 Label", maxlength = 20, setvalue = self.set_name1)
self.name2, self.namew2 = create_text_entry(self, text = "Temp 2 Label", maxlength = 20, setvalue = self.set_name2)
btn_updatename = wx.Button(self, label = "Update Labels")
name_box_1 = make_filled_vbox((self.namew1, self.name1), border=1)
name_box_2 = make_filled_vbox((self.namew2, self.name2), border=1)
name_box = make_filled_hbox((name_box_1, name_box_2, btn_updatename, btn_clearplots), border=1)
pressure_entries_box = make_filled_hbox(( twindow_box, press_min_box, press_max_box, set_twindow_btn, set_pressure_btn), border=1)
pressure_box = make_filled_vbox((hb_plot2, pressure_entries_box))
box = make_filled_vbox((temp_box, pressure_box, name_box))
self.SetSizerAndFit(box)
self.btn_on.Bind(wx.EVT_BUTTON, self.on_click_on)
self.btn_off.Bind(wx.EVT_BUTTON, self.on_click_off)
btn_updatesettemp.Bind(wx.EVT_BUTTON, self.on_click_updateTemp) # change function later
btn_clearplots.Bind(wx.EVT_BUTTON, self.on_click_clearPlots)
btn_updatename.Bind(wx.EVT_BUTTON, self.on_click_updateNames)
set_twindow_btn.Bind(wx.EVT_BUTTON, self.on_click_twindow)
set_pressure_btn.Bind(wx.EVT_BUTTON, self.on_click_updatePressure)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_update, self.timer)
self.timer.Start(100)
def on_update(self, _event):
self.time = time.time()
self.currenttemp1 = temp1_PV.get()
self.currenttemp2 = temp2_PV.get()
self.pressure = pressure_PV.get()
if self.time - self.last_update_time > self.update_interval:### Update voltage settings and append to queue every 2 seconds
self.tList = np.append(self.tList, matplotlib.dates.date2num(datetime.datetime.now()))
self.temp1deque.append(self.currenttemp1)
self.temp2deque.append(self.currenttemp2)
self.pressuredeque.append(self.pressure)
self.change_temp_PID()
self.draw_plot()
wx.GetApp().Yield()
def ctlToVolts(self, ctl):
a = 2.03787295
v0 = 1.90207996
value = ctl/a + v0
if value == -np.inf or ctl <= 0:
return 0.0
if ctl >= 1:
return 5.0
else:
return value
def change_temp(self):
## currently open or close transistor, future, implement PID
self.last_update_time = time.time()
if self.currenttemp1 - self.set_temp1 > self.set_tempw1 or self.onOff == False:
voltage1_PV.put(0)
print('Current temp: %0.1f // Set temp %0.1f // Turn OFF voltage'%(self.currenttemp1,self.set_temp1 ))
self.tempt1.SetForegroundColour(wx.Colour(255,0,0))
else:
voltage1_PV.put(5)
print('Current temp: %0.1f // Set temp %0.1f // Turn ON voltage'%(self.currenttemp1,self.set_temp1 ))
self.tempt1.SetForegroundColour(wx.Colour(0,255,0))
if self.currenttemp2 - self.set_temp2 > self.set_tempw2 or self.onOff == False:
voltage2_PV.put(0)
print('Current temp: %0.1f // Set temp %0.1f // Turn OFF voltage'%(self.currenttemp2,self.set_temp2 ))
self.tempt2.SetForegroundColour(wx.Colour(255,0,0))
else:
voltage2_PV.put(5)
print('Current temp: %0.1f // Set temp %0.1f // Turn ON voltage'%(self.currenttemp2,self.set_temp2 ))
self.tempt2.SetForegroundColour(wx.Colour(0,255,0))
print('_____________________________________________________________')
def change_temp_PID(self):
## currently open or close transistor, future, implement PID
self.last_update_time = time.time()
self.T = self.update_interval
self.ctl_error1 = self.set_temp1 - self.currenttemp1
self.ctl_error2 = self.set_temp2 - self.currenttemp2
self.deriv_error1 = self.ctl_error1 - self.prev_error1
self.deriv_error2 = self.ctl_error2 - self.prev_error2
self.kScale = 1
self.kp = 0.4 / self.kScale
self.ki = (0.02 * self.T) / self.kScale
self.kd = (1.0 / self.T) / self.kScale
self.ctl1 = (self.kp * self.ctl_error1) + (self.ki * self.sum_error1) + (self.kd * self.deriv_error1)
self.ctl2 = (self.kp * self.ctl_error2) + (self.ki * self.sum_error2) + (self.kd * self.deriv_error2)
if abs(self.ctl_error1) < 5:
self.sum_error1 += self.ctl_error1
if abs(self.ctl_error1) < 5:
self.sum_error2 += self.ctl_error2
self.prev_error1 = self.ctl_error1
self.prev_error2 = self.ctl_error2
if self.onOff == False:
voltage1_PV.put(0)
voltage2_PV.put(0)
print('Temperature Control Off')
else:
voltage1_PV.put(self.ctlToVolts(self.ctl1))
voltage2_PV.put(self.ctlToVolts(self.ctl2))
print('PID Control 1 = ' + str(round(self.ctl1, 3)) + ' // Voltage 1 = ' + str(round(self.ctlToVolts(self.ctl1), 3)) + ' V')
print('PID Control 2 = ' + str(round(self.ctl2, 3)) + ' // Voltage 2 = ' + str(round(self.ctlToVolts(self.ctl2), 3)) + ' V')
print('_____________________________________________________________')
def draw_plot(self):
self.plottemp.clear()
self.plottemp.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%H:%M.%S'))
self.plottemp.set_xlabel("Time [H:M.S]")
self.plottemp.set_ylabel("Temperature [C]")
if (len(self.tList) < self.twindow_listlength):
self.plottemp.plot(np.squeeze(self.tList).flatten(), np.squeeze(self.temp1deque).flatten(), '.-', label = self.set_name1)
self.plottemp.plot(np.squeeze(self.tList).flatten(), np.squeeze(self.temp2deque).flatten(), '.-', label = self.set_name2)
if self.onOff == True:
self.plottemp.axhline(y = self.set_temp1, color = 'b', alpha = 0.2)
self.plottemp.axhline(y = self.set_temp2, color = 'r', alpha = 0.2)
else:
self.plottemp.plot(np.squeeze(self.tList).flatten()[len(self.tList) - self.twindow_listlength:], np.squeeze(self.temp1deque).flatten()[len(self.tList) - self.twindow_listlength:], '.-', label = self.set_name1)
self.plottemp.plot(np.squeeze(self.tList).flatten()[len(self.tList) - self.twindow_listlength:], np.squeeze(self.temp2deque).flatten()[len(self.tList) - self.twindow_listlength:], '.-', label = self.set_name2)
if self.onOff == True:
self.plottemp.axhline(y = self.set_temp1, color = 'b', alpha = 0.2)
self.plottemp.axhline(y = self.set_temp2, color = 'r', alpha = 0.2)
self.plottemp.legend([self.set_name1 + ": " + str(round(self.currenttemp1, 2)) + " C", self.set_name2 + ": " + str(round(self.currenttemp2, 2)) + " C"], loc='upper left')
self.plottemp.draw()
self.plotpressure.clear()
self.plotpressure.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%H:%M.%S'))
self.plotpressure.set_xlabel("Time [H:M.S]")
self.plotpressure.set_ylabel("Pressure [mBar]")
if (len(self.tList) < self.twindow_listlength):
self.plotpressure.plot(np.squeeze(self.tList).flatten(), np.squeeze(self.pressuredeque).flatten(), '.-', label = 'Pressure')
else:
self.plotpressure.plot(np.squeeze(self.tList).flatten()[len(self.tList) - self.twindow_listlength:], np.squeeze(self.pressuredeque).flatten()[len(self.tList) - self.twindow_listlength:], '.-', label = 'Pressure')
self.plotpressure.set_ylim([self.pressure_min, self.pressure_max])
self.plotpressure.set_yscale('log')
self.plotpressure.legend(loc='upper left')
self.plotpressure.draw()
def on_click_updateTemp(self, _event):
self.set_temp1 = np.double(self.temp1.GetValue())
self.set_temp2 = np.double(self.temp2.GetValue())
self.set_tempw1 = np.double(self.tempw1.GetValue())
self.set_tempw2 = np.double(self.tempw2.GetValue())
self.prev_error1 = self.set_temp1 - self.currenttemp1
self.sum_error1 = 0
self.ctl1 = 0
self.prev_error2 = self.set_temp2 - self.currenttemp2
self.sum_error2 = 0
self.ctl2 = 0
print('Updated temperature setpoints')
def on_click_on(self, _event):
self.prev_error1 = self.set_temp1 - self.currenttemp1
self.sum_error1 = 0
self.ctl1 = 0
self.prev_error2 = self.set_temp2 - self.currenttemp2
self.sum_error2 = 0
self.ctl2 = 0
self.onOff = True
self.btn_on.SetBackgroundColour(wx.Colour(0,200,0))
self.btn_off.SetBackgroundColour(wx.NullColour)
def on_click_off(self, _event):
self.onOff = False
self.btn_on.SetBackgroundColour(wx.NullColour)
self.btn_off.SetBackgroundColour(wx.Colour(0,200,0))
def on_click_clearPlots(self, _event):
self.tList = deque(maxlen=self.quelength)
self.temp1deque = deque(maxlen=self.quelength)
self.temp2deque = deque(maxlen=self.quelength)
self.pressuredeque = deque(maxlen=self.quelength)
def on_click_updatePressure(self, _event):
self.pressure_min = Decimal(self.pressure_min_ctrl.GetValue())
self.pressure_max = Decimal(self.pressure_max_ctrl.GetValue())
def on_click_updateNames(self, _event):
n1 = self.name1.GetValue()
n2 = self.name2.GetValue()
self.tempt1.SetLabel(n1)
self.tempt2.SetLabel(n2)
self.tempwt1.SetLabel(n1 + " Window")
self.tempwt2.SetLabel(n2 + " Window")
self.set_name1 = n1
self.set_name2 = n2
def on_click_twindow(self, _event):
self.set_twindow = self.twindow.GetValue()
self.twindow_listlength = int((float(self.set_twindow)*60) / self.update_interval)