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

411 lines
18 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-VT2010: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.quelength = 6000000
self.temp1deque = deque(maxlen=self.quelength)
self.temp2deque = deque(maxlen=self.quelength)
self.pressuredeque = deque(maxlen=self.quelength)
# self.simple_ctl = True
# self.set_temp1 = 20
# self.set_temp2 = 20
self.temp_min = 0
self.temp_max = 250
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.last_update_time = 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.temp_min_ctrl, self.tempwt1 = create_text_entry(self, text = "Temperature Y Minimum", maxlength = 5, setvalue = self.temp_min)
temp_minbox = make_filled_vbox((self.tempwt1, self.temp_min_ctrl), 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.temp_max_ctrl, self.tempwt2 = create_text_entry(self, text = "Temperature Y Maximum", maxlength = 5, setvalue = self.temp_max)
temp_maxbox = make_filled_vbox((self.tempwt2, self.temp_max_ctrl), border=1)
self.temp1_indicator = wx.Button(self, label = self.set_name1 + " Heat")
self.temp1_indicator.Disable()
self.temp1_indicator.SetBackgroundColour(wx.NullColour)
self.temp2_indicator = wx.Button(self, label = self.set_name2 + " Heat")
self.temp2_indicator.Disable()
self.temp2_indicator.SetBackgroundColour(wx.NullColour)
indicatorbox = make_filled_vbox((self.temp1_indicator, self.temp2_indicator), 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))
# self.btn_PID = wx.Button(self, label="PID Control")
# self.btn_PID.SetBackgroundColour(wx.NullColour)
# self.btn_simple = wx.Button(self, label="Binary Control")
# self.btn_simple.SetBackgroundColour(wx.Colour(0,200,200))
btn_updatesettemp = wx.Button(self, label="Update Temp Y Axis")
btn_clearplots = wx.Button(self, label="Clear Plots")
# ctlbox = make_filled_vbox((self.btn_PID, self.btn_simple), border=1)
# tempbox = make_filled_hbox(( self.btn_on, self.btn_off, ctlbox, temp1box, temp_minbox, temp2box, temp_maxbox, btn_updatesettemp), border=1)
tempbox = make_filled_hbox((indicatorbox, temp_minbox, temp_maxbox, 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)
# self.btn_PID.Bind(wx.EVT_BUTTON, self.on_click_PID)
# self.btn_simple.Bind(wx.EVT_BUTTON, self.on_click_simple)
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.currentvolt1 = voltage1_PV.get()
self.currentvolt2 = voltage2_PV.get()
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.last_update_time = time.time()
if voltage1_PV.get() > 0.1:
self.temp1_indicator.SetBackgroundColour(wx.Colour(200,0,0))
else:
self.temp1_indicator.SetBackgroundColour(wx.NullColour)
if voltage2_PV.get() > 0.1:
self.temp2_indicator.SetBackgroundColour(wx.Colour(200,0,0))
else:
self.temp2_indicator.SetBackgroundColour(wx.NullColour)
# if self.PID_ctl == True:
# self.change_temp_PID()
# self.tempt1.SetForegroundColour(wx.NullColour)
# self.tempt2.SetForegroundColour(wx.NullColour)
# if self.simple_ctl == True:
# self.change_temp()
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.temp_min 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.temp_max 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]")
self.plottemp.set_ylim([self.temp_min, self.temp_max])
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.temp_min = np.double(self.temp_min_ctrl.GetValue())
self.temp_max = np.double(self.temp_max_ctrl.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_PID(self, _event):
# self.PID_ctl = True
# self.simple_ctl = False
# self.btn_PID.SetBackgroundColour(wx.Colour(0,200,200))
# self.btn_simple.SetBackgroundColour(wx.NullColour)
# def on_click_simple(self, _event):
# self.simple_ctl = True
# self.PID_ctl = False
# self.btn_simple.SetBackgroundColour(wx.Colour(0,200,200))
# self.btn_PID.SetBackgroundColour(wx.NullColour)
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 + " Threshold")
self.tempwt2.SetLabel(n2 + " Threshold")
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)