dev: добавлены эквидистанты

This commit is contained in:
Андрей Скирченко 2024-11-08 10:50:18 +03:00
parent 11be1685a7
commit 1ece32a57c
12 changed files with 777 additions and 711 deletions

View File

@ -7,7 +7,7 @@
"dist_open_end_2" : 0.050, "dist_open_end_2" : 0.050,
"dist_close_end_1" : 0.005, "dist_close_end_1" : 0.005,
"dist_close_end_2" : 0.005, "dist_close_end_2" : 0.005,
"time_wielding" : 2, "time_wielding" : 1,
"time_command" : 0.060, "time_command" : 0.060,
"time_robot_movement" : 0.2, "time_robot_movement" : 0.2,
"object_thickness" : 4.5e-3, "object_thickness" : 4.5e-3,

View File

@ -14,5 +14,6 @@
"position_start_1" : 0.080, "position_start_1" : 0.080,
"position_start_2" : 0.080, "position_start_2" : 0.080,
"k_prop" : 0.05, "k_prop" : 0.05,
"time_capture" : 100000 "time_capture" : 100000,
"UML_time_scaler": 1000
} }

View File

@ -1 +1 @@
from .plot_window import PlotWindow from .plot_window import PlotWindow

View File

@ -1,148 +1,207 @@
import pyqtgraph as pg import pyqtgraph as pg
import numpy as np import numpy as np
from src.gui import qt_settings as qts from src.gui import qt_settings as qts
from src.OptAlgorithm import OptAlgorithm from src.OptAlgorithm import OptAlgorithm
class PlotWindow: class PlotWindow:
def __init__(self, def __init__(self,
opt: OptAlgorithm, system_config : dict,
bool_dict: dict, operator_config: dict,
float_dict: dict): opt: OptAlgorithm,
pg.setConfigOptions(antialias=True) bool_dict: dict,
self.opt = opt float_dict: dict):
self.bool_dict = bool_dict pg.setConfigOptions(antialias=True)
self.float_dict = float_dict self.opt = opt
self.bool_dict = bool_dict
self.scaler = 1000 self.float_dict = float_dict
self._getIdealTimings() self.scaler = int(system_config['UML_time_scaler'])
self._init_app() self.WeldTime = operator_config['time_wielding'] #[sec]
self.WeldTime = 1.0 #[sec] # TODO: to external config
self.alpha = 100 #[0-255 прозрачность фона]
self._getIdealTimings()
def _init_app(self): self._init_app()
self.app = pg.mkQApp("Plotting") self.alpha = 100 #[0-255 прозрачность фона]
self.win = pg.GraphicsLayoutWidget(show=True, title="")
self.win.resize(1000,600) def _init_app(self):
self.win.setWindowTitle('') self.app = pg.mkQApp("Plotting")
self.win = pg.GraphicsLayoutWidget(show=True, title="")
self.p11, self.l11 = self._init_graph('Electrode force, closure', 'Force', 'N', 'Time', 'ms') self.win.resize(1000,600)
#self.p21, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms') self.win.setWindowTitle('')
#self.p31, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
self.win.nextRow() self.p11, self.l11 = self._init_graph('Electrode force, closure', 'Force', 'N', 'Time', 'ms')
self.p12, self.l12 = self._init_graph('Rotor Position, closure', 'Posicion', 'mm', 'Time', 'ms') #self.p21, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
#self.p22, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms') #self.p31, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
#self.p32, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms') self.win.nextRow()
self.win.nextRow() self.p12, self.l12 = self._init_graph('Rotor Position, closure', 'Posicion', 'mm', 'Time', 'ms')
self.p13, self.l13 = self._init_graph('Rotor Speed, closure', 'Speed', 'mm/s', 'Time', 'ms') #self.p22, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
#self.p23, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms') #self.p32, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
#self.p33, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms') self.win.nextRow()
self.win.nextRow() self.p13, self.l13 = self._init_graph('Rotor Speed, closure', 'Speed', 'mm/s', 'Time', 'ms')
#self.p23, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
self.p12.setXLink(self.p11) #self.p33, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
self.p13.setXLink(self.p11) self.win.nextRow()
self.p11.setAutoVisible(x=False, y=True) self.p12.setXLink(self.p11)
self.p12.setAutoVisible(x=False, y=True) self.p13.setXLink(self.p11)
self.p13.setAutoVisible(x=False, y=True)
self.p11.setAutoVisible(x=False, y=True)
def _init_graph(self, title, Yname, Yunits, Xname, Xunits): self.p12.setAutoVisible(x=False, y=True)
p11 = self.win.addPlot(title = title) self.p13.setAutoVisible(x=False, y=True)
p11.showGrid(x=True, y=True)
p11.setLabel('left', Yname, units=Yunits) def _init_graph(self, title, Yname, Yunits, Xname, Xunits):
p11.setLabel('bottom', Xname, units=Xunits) p11 = self.win.addPlot(title = title)
legend1 = pg.LegendItem((80,60), offset=(70,20)) p11.showGrid(x=True, y=True)
legend1.setParentItem(p11) p11.setLabel('left', Yname, units=Yunits)
return p11, legend1 p11.setLabel('bottom', Xname, units=Xunits)
legend1 = pg.LegendItem((80,60), offset=(70,20))
def updatePlots(self): legend1.setParentItem(p11)
self._plotRealData() return p11, legend1
times = np.arange(20000)/10
self._plotIdealData(times) def updatePlots(self):
self._plotRealData()
def _getIdealTimings(self): times = np.arange(20000)/10
data = self.opt.Ts self._plotIdealData(times)
self.idealTime = [data['tclose'], data['tgrow'], self.opt.getMarkOpen()]
def _getIdealTimings(self):
def _form_idealdatGraph(self, times): data = self.opt.Ts
self.idealPhase0 = (self.idealTime[0])*self.scaler #Подъезд self.idealTime = [data['tclose'], data['tgrow'], self.opt.getMarkOpen()]
self.idealPhase1 = (self.idealTime[0]+ self.idealTime[1])*self.scaler #Сжатие
self.idealPhase2 = (self.idealTime[0]+ self.idealTime[1] + self.WeldTime)*self.scaler #Сварка def _form_idealdatGraph(self, times):
self.idealPhase3 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime)*self.scaler #Разъезд self.idealPhase0 = (self.idealTime[0])*self.scaler #Подъезд
data = [] self.idealPhase1 = (self.idealTime[0]+ self.idealTime[1])*self.scaler #Сжатие
for time in times: self.idealPhase2 = (self.idealTime[0]+ self.idealTime[1] + self.WeldTime)*self.scaler #Сварка
if time <= self.idealPhase0: self.idealPhase3 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime)*self.scaler #Разъезд
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseClose(time/self.scaler) self.idealPhase4 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime + 0.25)*self.scaler #Последнее смыкание #TODO добавить идеальное время вместо 0.25
elif time <= self.idealPhase1: self.x_splitter = np.array([[0, self.idealPhase0],
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseGrow(time/self.scaler-self.idealTime[0]) [self.idealPhase0, self.idealPhase1],
elif time <= self.idealPhase2: [self.idealPhase1, self.idealPhase2],
x_fe, x_me, v_fe, v_me, f = data[-1] [self.idealPhase2, self.idealPhase3],
elif time <= self.idealPhase3: [self.idealPhase3, self.idealPhase4]])
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseOpen(time/self.scaler-self.idealTime[0]-self.idealTime[1]-self.WeldTime) data = []
else: for time in times:
x_fe, x_me, v_fe, v_me, f = 0, 0, 0, 0, 0 if time <= self.idealPhase0:
data.append([x_fe, x_me, v_fe, v_me, f]) x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseClose(time/self.scaler)
data = np.array(data).T elif time <= self.idealPhase1:
return data x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseGrow(time/self.scaler-self.idealTime[0])
elif time <= self.idealPhase2:
def _plotRealData(self): x_fe, x_me, v_fe, v_me, f = data[-1]
for i, (key, dat) in enumerate(self.float_dict.items()): elif time <= self.idealPhase3:
dat = np.array(dat).T x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseOpen(time/self.scaler-self.idealTime[0]-self.idealTime[1]-self.WeldTime)
dat[0] = dat[0]*self.scaler else:
curve = pg.PlotDataItem(dat[0], dat[1], pen=pg.mkPen(color=qts.colors[i], width=2), name=key, autoDownsample=True, downsample=True) x_fe, x_me, v_fe, v_me, f = 0, 0, 0, 0, 0
if 'Electrode Force' in key: data.append([x_fe, x_me, v_fe, v_me, f])
self.p11.addItem(curve) data = np.array(data).T
self.l11.addItem(curve, key) return data
elif 'Rotor Position' in key:
self.p12.addItem(curve) def _plotRealData(self):
self.l12.addItem(curve, key) for i, (key, dat) in enumerate(self.float_dict.items()):
elif 'Rotor Speed' in key: dat = np.array(dat).T
self.p13.addItem(curve) dat[0] = dat[0]*self.scaler
self.l13.addItem(curve, key) curve = pg.PlotDataItem(dat[0], dat[1], pen=pg.mkPen(color=qts.colors[i], width=2), name=key, autoDownsample=True, downsample=True)
return dat[0] if 'Electrode Force' in key:
self.p11.addItem(curve)
def _plotIdealData(self, times): self.l11.addItem(curve, key)
data = self._form_idealdatGraph(times) elif 'Rotor Position' in key:
self.p12.addItem(curve)
x_fe = pg.PlotDataItem(times, data[0]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='x_fe', autoDownsample=True, downsample=True) self.l12.addItem(curve, key)
x_me = pg.PlotDataItem(times, data[1]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='x_me', autoDownsample=True, downsample=True) elif 'Rotor Speed' in key:
v_fe = pg.PlotDataItem(times, data[2]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='v_fe', autoDownsample=True, downsample=True) self.p13.addItem(curve)
v_me = pg.PlotDataItem(times, data[3]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='v_me', autoDownsample=True, downsample=True) self.l13.addItem(curve, key)
f = pg.PlotDataItem(times, data[4], pen=pg.mkPen(color=qts.colors[8], width=2), name='f', autoDownsample=True, downsample=True) return dat[0]
self.p11.addItem(f) def _plotIdealData(self, times):
self.l11.addItem(f, 'Ideal force') data = self._form_idealdatGraph(times)
self.p12.addItem(x_fe) x_fe = pg.PlotDataItem(times, data[0]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='x_fe', autoDownsample=True, downsample=True)
self.l12.addItem(x_fe, 'FE POS') x_me = pg.PlotDataItem(times, data[1]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='x_me', autoDownsample=True, downsample=True)
self.p12.addItem(x_me) v_fe = pg.PlotDataItem(times, data[2]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='v_fe', autoDownsample=True, downsample=True)
self.l12.addItem(x_me, 'ME POS') v_me = pg.PlotDataItem(times, data[3]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='v_me', autoDownsample=True, downsample=True)
f = pg.PlotDataItem(times, data[4], pen=pg.mkPen(color=qts.colors[8], width=2), name='f', autoDownsample=True, downsample=True)
self.p13.addItem(v_fe)
self.l13.addItem(v_fe, 'FE VEL') self.p11.addItem(f)
self.p13.addItem(v_me) self.l11.addItem(f, 'Ideal force')
self.l13.addItem(v_me, 'ME VEL')
self._addBackgroundSplitter() self.p12.addItem(x_fe)
self.l12.addItem(x_fe, 'FE POS')
def _addBackgroundSplitter(self): self.p12.addItem(x_me)
alpha = self.alpha self.l12.addItem(x_me, 'ME POS')
x = [[0, self.idealPhase0],
[self.idealPhase0, self.idealPhase1], self.p13.addItem(v_fe)
[self.idealPhase1, self.idealPhase2], self.l13.addItem(v_fe, 'FE VEL')
[self.idealPhase2, self.idealPhase3]] self.p13.addItem(v_me)
y01 = [10000, 10000] self.l13.addItem(v_me, 'ME VEL')
y0_1 = [-10000, -10000] self._addBackgroundSplitter()
for i, _ in enumerate(x): self._addEquidistances(times, data)
a01 = pg.PlotDataItem(_, y01, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ')
a0_1 = pg.PlotDataItem(_, y0_1, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ') def _addBackgroundSplitter(self):
bg1 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,)) alpha = self.alpha
bg2 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,)) y01 = np.array([10000, 10000])
bg3 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,)) y0_1 = np.array([-10000, -10000])
self.p11.addItem(bg1) for i, _ in enumerate(self.x_splitter):
self.p12.addItem(bg2) a01 = pg.PlotDataItem(_, y01, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ')
self.p13.addItem(bg3) a0_1 = pg.PlotDataItem(_, y0_1, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ')
bg1 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
self.p11.setYRange(-1000, 5000) bg2 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
self.p12.setYRange(-50, 250) bg3 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
self.p13.setYRange(-400, 400) self.p11.addItem(bg1)
self.p12.addItem(bg2)
self.p13.addItem(bg3)
self.p11.setYRange(-1000, 5000)
self.p12.setYRange(-50, 250)
self.p13.setYRange(-400, 400)
def _addEquidistances(self, times, data):
a1, b1, c1 = self._calculate_equidistant('fill', max(data[4]), 2.5, self.x_splitter[1], 3)
self.p11.addItem(a1)
self.p11.addItem(b1)
self.p11.addItem(c1)
a1, b1, c1 = self._calculate_equidistant(times, data[4], 0.75, self.x_splitter[2], 3)
self.p11.addItem(a1)
self.p11.addItem(b1)
self.p11.addItem(c1)
def _makeFiller(self, x1, y1, x2, y2, color):
alpha = self.alpha
eq1 = pg.PlotDataItem(x1, y1, pen=pg.mkPen(color='#000000', width=2))
eq2 = pg.PlotDataItem(x2, y2, pen=pg.mkPen(color='#000000', width=2))
bg = pg.FillBetweenItem(eq1, eq2, qts.RGBA[color]+(alpha,))
return eq1, eq2, bg
def _calculate_equidistant(self, x, y, percent, splitter, color):
if str(x) == 'fill':
x = [splitter[0]+0.001, splitter[0]+0.002, splitter[1]-0.002, splitter[1]-0.001]
y = [y, y, y, y]
if len(x) != len(y):
raise ValueError("x и y должны быть одного размера")
distance = max(y)/100*percent
x_eq1 = []
y_eq1 = []
x_eq2 = []
y_eq2 = []
for i in range(0, len(x) - 1):
if splitter[0]<= x[i] and x[i] <= splitter[1]:
dx = x[i + 1] - x[i]
dy = y[i + 1] - y[i]
length = np.sqrt(dx ** 2 + dy ** 2)
sinA = dy/length
sinB = dx/length
nx = -sinA*distance
ny = sinB*distance
x_eq1.append(x[i] + nx)
y_eq1.append(y[i] + ny)
x_eq2.append(x[i] - nx)
y_eq2.append(y[i] - ny)
return self._makeFiller(np.array(x_eq1), np.array(y_eq1), np.array(x_eq2), np.array(y_eq2), color)

View File

@ -1,255 +1,256 @@
white_style = """ white_style = """
QMainWindow { QMainWindow {
background-color: #ffffff; background-color: #ffffff;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QMessageBox { QMessageBox {
background-color: #ffffff; background-color: #ffffff;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QPushButton { QPushButton {
background-color: #d3d3d3; background-color: #d3d3d3;
color: #000000; color: #000000;
padding: 10px 20px; padding: 10px 20px;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QPushButton:hover:!disabled { QPushButton:hover:!disabled {
background-color: #b0b0b0; background-color: #b0b0b0;
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #a9a9a9; background-color: #a9a9a9;
color: #7f7f7f; color: #7f7f7f;
} }
QCheckBox { QCheckBox {
color: #000000; color: #000000;
font-size: 14px; font-size: 14px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
} }
QLineEdit { QLineEdit {
background-color: #f0f0f0; background-color: #f0f0f0;
color: #000000; color: #000000;
padding: 5px; padding: 5px;
border: 1px solid #a9a9a9; border: 1px solid #a9a9a9;
border-radius: 4px; border-radius: 4px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QLabel { QLabel {
color: #000000; color: #000000;
font-size: 16px; font-size: 16px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
} }
QGroupBox { QGroupBox {
color: #000000; color: #000000;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QRadioButton { QRadioButton {
color: #000000; color: #000000;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
""" """
dark_style = """ dark_style = """
QMainWindow { QMainWindow {
background-color: #0D1117; /* Тёмный, современный цвет для фона */ background-color: #0D1117; /* Тёмный, современный цвет для фона */
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QMessageBox { QMessageBox {
background-color: #161B22; background-color: #161B22;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
} }
QPushButton { QPushButton {
background-color: #FFCC00; /* Яркий жёлтый цвет для акцента */ background-color: #FFCC00; /* Яркий жёлтый цвет для акцента */
color: #0D1117; /* Темный цвет текста для контраста с желтым */ color: #0D1117; /* Темный цвет текста для контраста с желтым */
padding: 12px 25px; padding: 12px 25px;
border: 2px solid #E6B800; border: 2px solid #E6B800;
border-radius: 8px; border-radius: 8px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
QPushButton:hover:!disabled { QPushButton:hover:!disabled {
background-color: #FFD700; /* Светлый желтый цвет для эффекта наведения */ background-color: #FFD700; /* Светлый желтый цвет для эффекта наведения */
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #555555; background-color: #555555;
color: #cccccc; color: #cccccc;
border: none; border: none;
} }
QCheckBox { QCheckBox {
color: #ffffff; color: #ffffff;
font-size: 14px; font-size: 14px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-weight: bold; font-weight: bold;
} }
QLineEdit { QLineEdit {
background-color: #21262D; background-color: #21262D;
color: #ffffff; color: #ffffff;
padding: 8px; padding: 8px;
border: 2px solid #30363D; border: 2px solid #30363D;
border-radius: 6px; border-radius: 6px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
QLabel { QLabel {
color: #ffffff; color: #ffffff;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
} }
QGroupBox { QGroupBox {
color: #ffffff; color: #ffffff;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
border: 1px solid #30363D; border: 1px solid #30363D;
border-radius: 6px; border-radius: 6px;
margin-top: 10px; margin-top: 10px;
} }
QRadioButton { QRadioButton {
color: #ffffff; color: #ffffff;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
QSpinBox { QSpinBox {
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
QDoubleSpinBox { QDoubleSpinBox {
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
""" """
dis_robots = """ dis_robots = """
QPushButton { QPushButton {
background-color: #555555; background-color: #555555;
color: #cccccc; color: #cccccc;
padding: 12px 25px; padding: 12px 25px;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
} }
QWidget#robot { QWidget#robot {
border: none; border: none;
padding: 5px; padding: 5px;
background-color: rgba(33, 33, 33, 100); background-color: rgba(33, 33, 33, 100);
} }
""" """
selected_robot = """ selected_robot = """
QWidget#robot { QWidget#robot {
border: 2px solid #E6B800; border: 2px solid #E6B800;
border-radius: 10px; border-radius: 10px;
padding: 5px; padding: 5px;
} }
""" """
en_button = """ en_button = """
QPushButton { QPushButton {
background-color: #FFCC00; background-color: #FFCC00;
color: #0D1117; color: #0D1117;
padding: 12px 25px; padding: 12px 25px;
border: 2px solid #E6B800; border: 2px solid #E6B800;
border-radius: 8px; border-radius: 8px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
QPushButton:hover:!disabled { QPushButton:hover:!disabled {
background-color: #FFD700; background-color: #FFD700;
} }
""" """
dis_button = """ dis_button = """
QPushButton { QPushButton {
background-color: #555555; background-color: #555555;
color: #cccccc; color: #cccccc;
padding: 12px 25px; padding: 12px 25px;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
} }
""" """
saved_button = """ saved_button = """
QPushButton { QPushButton {
background-color: #28a745; background-color: #28a745;
color: #ffffff; color: #ffffff;
padding: 12px 25px; padding: 12px 25px;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
""" """
start_button = """ start_button = """
QPushButton { QPushButton {
background-color: #FFCC00; background-color: #FFCC00;
color: #0D1117; color: #0D1117;
padding: 20px 40px; padding: 20px 40px;
border: 2px solid #E6B800; border: 2px solid #E6B800;
border-radius: 10px; border-radius: 10px;
font-family: "Segoe UI", sans-serif; font-family: "Segoe UI", sans-serif;
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
background-image: linear-gradient(to bottom, #FFD700, #FFCC00); background-image: linear-gradient(to bottom, #FFD700, #FFCC00);
} }
QPushButton:hover:!disabled { QPushButton:hover:!disabled {
background-color: #FFD700; background-color: #FFD700;
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #E6B800; background-color: #E6B800;
padding-top: 22px; padding-top: 22px;
padding-bottom: 18px; padding-bottom: 18px;
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #555555; background-color: #555555;
color: #cccccc; color: #cccccc;
border: none; border: none;
} }
""" """
colors = [ colors = [
'#FF6F61', # яркий коралловый '#FF6F61', # яркий коралловый
'#6B5B95', # приглушенный фиолетовый '#6B5B95', # приглушенный фиолетовый
'#88B04B', # яркий зеленый '#88B04B', # яркий зеленый
'#F7CAC9', # светлый розовый '#F7CAC9', # светлый розовый
'#92A8D1', # светло-голубой '#92A8D1', # светло-голубой
'#955251', # теплый терракотовый '#955251', # теплый терракотовый
'#B565A7', # лавандовый '#B565A7', # лавандовый
'#009B77', # глубокий бирюзовый '#009B77', # глубокий бирюзовый
'#DD4124', # ярко-красный '#DD4124', # ярко-красный
'#45B8AC' # мягкий мятный '#45B8AC' # мягкий мятный
] ]
RGBA = [(255, 255, 0), RGBA = [(255, 255, 0),
(32, 178, 70), (32, 178, 70),
(255, 69, 0), (255, 69, 0),
(123, 104, 238) (123, 104, 238),
(99, 42, 15)
] ]

View File

@ -1,42 +1,45 @@
import pyqtgraph as pg import pyqtgraph as pg
from utils import read_json, DiagramParser from src.utils import read_json, DiagramParser
from uml import Request, UMLCreator from src.uml import Request, UMLCreator
from OptAlgorithm import OptAlgorithm from src.OptAlgorithm import OptAlgorithm
from gui import PlotWindow from src.gui import PlotWindow
def get_ideal_timings(opt: OptAlgorithm) -> list[float]: def get_ideal_timings(opt: OptAlgorithm) -> list[float]:
data = opt.Ts data = opt.Ts
ideal_time = [data['tclose'], data['tgrow'], opt.getMarkOpen()] ideal_time = [data['tclose'], data['tgrow'], opt.getMarkOpen()]
return ideal_time return ideal_time
def main(): def main():
operator_params = read_json("params/operator_params.json") operator_params = read_json("params/operator_params.json")
system_params = read_json("params/system_params.json") system_params = read_json("params/system_params.json")
opt_algorithm = OptAlgorithm(operator_config=operator_params, system_config=system_params) opt_algorithm = OptAlgorithm(operator_config=operator_params, system_config=system_params)
ideal_times = get_ideal_timings(opt_algorithm) ideal_times = get_ideal_timings(opt_algorithm)
parser = DiagramParser() parser = DiagramParser()
parser.setData("trace_samples/2024_11_01-09_39_17B00.csv") parser.setData("trace_samples/2024_11_01-09_39_17B00.csv")
bool_dict = parser.getBoolDict() bool_dict = parser.getBoolDict()
float_dict = parser.getFloatDict() float_dict = parser.getFloatDict()
request_generator = Request(server_url='http://www.plantuml.com/plantuml/svg/') request_generator = Request(server_url='http://www.plantuml.com/plantuml/svg/')
uml_creator = UMLCreator(request_generator=request_generator, uml_creator = UMLCreator(system_config=system_params,
ideal_time=ideal_times, request_generator=request_generator,
bool_dict=bool_dict, ideal_time=ideal_times,
float_dict=float_dict) bool_dict=bool_dict,
uml_creator.update_uml() float_dict=float_dict)
uml_creator.update_uml()
app = PlotWindow(opt=opt_algorithm,
bool_dict=bool_dict, app = PlotWindow(operator_config=operator_params,
float_dict=float_dict) system_config=system_params,
opt=opt_algorithm,
app.updatePlots() bool_dict=bool_dict,
pg.exec() float_dict=float_dict)
app.updatePlots()
if __name__ == '__main__': pg.exec()
main()
if __name__ == '__main__':
main()

View File

@ -1,2 +1,2 @@
from .request_generator import Request from .request_generator import Request
from .creator import UMLCreator from .creator import UMLCreator

View File

@ -1,98 +1,100 @@
from src.uml.request_generator import Request from src.uml.request_generator import Request
class UMLCreator: class UMLCreator:
def __init__(self, def __init__(self,
request_generator: Request, system_config : dict,
ideal_time: list[float], request_generator: Request,
bool_dict: dict, ideal_time: list[float],
float_dict: dict): bool_dict: dict,
self._request_generator = request_generator float_dict: dict):
self._ideal_time = ideal_time self._request_generator = request_generator
self.bool_dict = bool_dict self._ideal_time = ideal_time
self.float_dict = float_dict self.bool_dict = bool_dict
self.float_dict = float_dict
def _build_data(self): self.scaler = int(system_config['UML_time_scaler'])
scaler = 1000 # TODO: external config?
sig = [
'Electrode Closing Algorithm Execute', # Начало закрытия def _build_data(self):
'Electrode Closing Algorithm Done', sig = [
# 'STEP 3: ME Hold P2 AND Condition Start Force Control', # Конец закрытия и Начало набора усилия 'Electrode Closing Algorithm Execute', # Начало закрытия
'STEP 4: ME Force Control', 'Electrode Closing Algorithm Done',
'Position Control ME', # Начало разъезда или 'Posision Control Activated FE' # 'STEP 3: ME Hold P2 AND Condition Start Force Control', # Конец закрытия и Начало набора усилия
'Position Control FE', # Начало разъезда 'STEP 4: ME Force Control',
'Position Control Completed ME', # Конец разъезда 'Position Control ME', # Начало разъезда или 'Posision Control Activated FE'
'Position Control Completed FE', # Конец разъезда 'Position Control FE', # Начало разъезда
'STEP 4: ME Force Control Complete', # Конец набора усилия 'Position Control Completed ME', # Конец разъезда
] 'Position Control Completed FE', # Конец разъезда
closure = self._get_time([sig[0], sig[1]]) 'STEP 4: ME Force Control Complete', # Конец набора усилия
compression = self._get_time([sig[2], sig[3]]) ]
# opening = self.__getTime([sig[4], sig[5], sig[6], sig[7]]) closure = self._get_time([sig[0], sig[1]])
compression = self._get_time([sig[2], sig[3]])
real_data = [ # opening = self.__getTime([sig[4], sig[5], sig[6], sig[7]])
[closure[0], 'closure #green'],
[closure[1], '{-}'], real_data = [
[compression[0], 'compression #green'], [closure[0], 'closure #green'],
[compression[1], '{-}'], [closure[1], '{-}'],
# [max(opening[0:2]), 'opening #green'], [compression[0], 'compression #green'],
# [max(opening[2:4]), '{-}'], [compression[1], '{-}'],
] # [max(opening[0:2]), 'opening #green'],
# [max(opening[2:4]), '{-}'],
client_data = [ ]
[0.0 * scaler, 'closure'],
[0.165 * scaler, '{-}'], client_data = [
[0.166 * scaler, 'compression'], [0.0 * self.scaler, 'closure'],
[0.176 * scaler, '{-}'], [0.165 * self.scaler, '{-}'],
# [0.180*scaler, 'welding'], [0.166 * self.scaler, 'compression'],
# [0.200*scaler, '{-}'], [0.176 * self.scaler, '{-}'],
[0.210 * scaler, 'opening'], # [0.180*self.scaler, 'welding'],
[0.300 * scaler, '{-}'], # [0.200*self.scaler, '{-}'],
] [0.210 * self.scaler, 'opening'],
[0.300 * self.scaler, '{-}'],
ideal_data = [ ]
[0.0 * scaler, 'closure #yellow'],
[self._ideal_time[0] * scaler, '{-}'], ideal_data = [
[(self._ideal_time[0] + 0.0001) * scaler, 'compression #yellow'], [0.0 * self.scaler, 'closure #yellow'],
[(self._ideal_time[0] + self._ideal_time[1]) * scaler, '{-}'], [self._ideal_time[0] * self.scaler, '{-}'],
# [0.*scaler, 'welding #yellow'], [(self._ideal_time[0] + 0.0001) * self.scaler, 'compression #yellow'],
# [0.*scaler, '{-}'], [(self._ideal_time[0] + self._ideal_time[1]) * self.scaler, '{-}'],
[(self._ideal_time[0] + self._ideal_time[1] + 0.0001) * scaler, 'opening #yellow'], # [0.*self.scaler, 'welding #yellow'],
[(self._ideal_time[0] + self._ideal_time[1] + self._ideal_time[2]) * scaler, '{-}'], # [0.*self.scaler, '{-}'],
] [(self._ideal_time[0] + self._ideal_time[1] + 0.0001) * self.scaler, 'opening #yellow'],
return real_data, client_data, ideal_data, self.bool_dict [(self._ideal_time[0] + self._ideal_time[1] + self._ideal_time[2]) * self.scaler, '{-}'],
]
def _get_time(self, signals = '', states = ''): return real_data, client_data, ideal_data, self.bool_dict
res = []
for i, sig in enumerate(signals): def _get_time(self, signals = '', states = ''):
if states: state = states[i] res = []
else: state = 'high' for i, sig in enumerate(signals):
index1 = next ((i for i, _ in enumerate(self.bool_dict[sig]) if _[1]==state), -1) if states: state = states[i]
time = self.bool_dict[sig][index1][0] else: state = 'high'
res.append(time) index1 = next ((i for i, _ in enumerate(self.bool_dict[sig]) if _[1]==state), -1)
return res time = self.bool_dict[sig][index1][0]
res.append(time)
def _generate_svg(self, real_data, client_data, ideal_data, bool_data) -> None: return res
try:
self._request_generator.clear() def _generate_svg(self, real_data, client_data, ideal_data, bool_data) -> None:
self._request_generator.addLineStyle('biba', 'red', 2) try:
self._request_generator.clear()
for i, [signal, changes] in enumerate(bool_data.items()): self._request_generator.addLineStyle('biba', 'red', 2)
name = 'bool_' + str(i)
self._request_generator.addBinary(name, str(signal), 'biba') for i, [signal, changes] in enumerate(bool_data.items()):
self._request_generator.setTimestamps(name, changes) name = 'bool_' + str(i)
self._request_generator.addBinary(name, str(signal), 'biba')
self._request_generator.addConcise('RD', 'Real data') self._request_generator.setTimestamps(name, changes)
self._request_generator.setTimestamps('RD', real_data)
self._request_generator.addConcise('CD', 'Client data') self._request_generator.addConcise('RD', 'Real data')
self._request_generator.setTimestamps('CD', client_data) self._request_generator.setTimestamps('RD', real_data)
self._request_generator.addConcise('ID', 'Ideal data') self._request_generator.addConcise('CD', 'Client data')
self._request_generator.setTimestamps('ID', ideal_data) self._request_generator.setTimestamps('CD', client_data)
self._request_generator.addConcise('ID', 'Ideal data')
self._request_generator.generateSVG() self._request_generator.setTimestamps('ID', ideal_data)
except Exception as e: self._request_generator.generateSVG()
print(f"SVG generate error: {e}")
except Exception as e:
def update_uml(self) -> None: print(f"SVG generate error: {e}")
real, client, ideal, bool_ = self._build_data()
self._generate_svg(real, client, ideal, bool_) def update_uml(self) -> None:
real, client, ideal, bool_ = self._build_data()
self._generate_svg(real, client, ideal, bool_)

View File

@ -1,99 +1,99 @@
from plantuml import PlantUML from plantuml import PlantUML
from os.path import abspath from os.path import abspath
class Request: class Request:
def __init__(self, server_url: str): def __init__(self, server_url: str):
self._server_url = server_url self._server_url = server_url
self._init_server() self._init_server()
def _init_server(self): def _init_server(self):
self.server = PlantUML(url=self._server_url) self.server = PlantUML(url=self._server_url)
self.clear() self.clear()
def _startUML(self): def _startUML(self):
self.reqArr.append('@startuml') self.reqArr.append('@startuml')
def _endUML(self): def _endUML(self):
self.reqArr.append('@enduml') self.reqArr.append('@enduml')
def clear(self): def clear(self):
self.timestamps = {} self.timestamps = {}
self.reqArr = [] self.reqArr = []
self.variables = [] self.variables = []
self.lineStyles = {} self.lineStyles = {}
self.stringUML = '' self.stringUML = ''
self._startUML() self._startUML()
def addAnalog(self, name, string): def addAnalog(self, name, string):
self.variables.append(f'analog "{string}" as {name}') self.variables.append(f'analog "{string}" as {name}')
def addBinary(self, name, string, style = ''): def addBinary(self, name, string, style = ''):
if style: name = name + '<<' + style + '>>' if style: name = name + '<<' + style + '>>'
self.variables.append(f'binary "{string}" as {name}') self.variables.append(f'binary "{string}" as {name}')
def addClock(self, name, string): def addClock(self, name, string):
self.variables.append(f'clock "{string}" as {name}') self.variables.append(f'clock "{string}" as {name}')
def addConcise(self, name, string): def addConcise(self, name, string):
self.variables.append(f'concise "{string}" as {name}') self.variables.append(f'concise "{string}" as {name}')
def addRobust(self, name, string): def addRobust(self, name, string):
self.variables.append(f'robust "{string}" as {name}') self.variables.append(f'robust "{string}" as {name}')
def appendStr(self, string = ''): def appendStr(self, string = ''):
self.variables.append(string) self.variables.append(string)
def addLineStyle(self, name, color = 'green', thicknes = 1): def addLineStyle(self, name, color = 'green', thicknes = 1):
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}'] self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
def generateSVG(self): def generateSVG(self):
self._compileUML() self._compileUML()
filename = abspath('UML.txt') filename = abspath('UML.txt')
self.server.processes_file(filename, outfile='UML.svg') self.server.processes_file(filename, outfile='UML.svg')
#result = self.server.processes(self.stringUML) #result = self.server.processes(self.stringUML)
#return result #return result
def setTimestamps(self, name, input): def setTimestamps(self, name, input):
for time, state in input: for time, state in input:
try: try:
self.timestamps[f'@{time}'].append(f'{name} is {state}') self.timestamps[f'@{time}'].append(f'{name} is {state}')
except KeyError: except KeyError:
self.timestamps[f'@{time}'] = [f'{name} is {state}'] self.timestamps[f'@{time}'] = [f'{name} is {state}']
def _addTimestamp(self, timecode, vars): def _addTimestamp(self, timecode, vars):
self.reqArr.append(timecode) self.reqArr.append(timecode)
for var in vars: for var in vars:
self.reqArr.append(var) self.reqArr.append(var)
def _addHeader(self): def _addHeader(self):
self.reqArr.append('<style>') self.reqArr.append('<style>')
if self.lineStyles: if self.lineStyles:
self.reqArr.append('timingDiagram {') self.reqArr.append('timingDiagram {')
for name, style in self.lineStyles.items(): for name, style in self.lineStyles.items():
self.reqArr.append(f' .{name} ' + '{') self.reqArr.append(f' .{name} ' + '{')
for param in style: self.reqArr.append(' ' + str(param)) for param in style: self.reqArr.append(' ' + str(param))
self.reqArr.append(' }') self.reqArr.append(' }')
self.reqArr.append('}') self.reqArr.append('}')
self.reqArr.append('</style>') self.reqArr.append('</style>')
def _addVariables(self): def _addVariables(self):
for var in self.variables: self.reqArr.append(str(var)) for var in self.variables: self.reqArr.append(str(var))
def _compileUML(self): def _compileUML(self):
self._addHeader() self._addHeader()
self._addVariables() self._addVariables()
if self.timestamps: if self.timestamps:
for key, item in self.timestamps.items(): for key, item in self.timestamps.items():
self._addTimestamp(key, item) self._addTimestamp(key, item)
self._endUML() self._endUML()
self.stringUML = [line + '\n' for line in self.reqArr] self.stringUML = [line + '\n' for line in self.reqArr]
with open('UML.txt', 'w', encoding='utf-8') as file: with open('UML.txt', 'w', encoding='utf-8') as file:
file.writelines(self.stringUML) file.writelines(self.stringUML)

View File

@ -1,2 +1,2 @@
from .json_tools import read_json from .json_tools import read_json
from .diagram_parser import DiagramParser from .diagram_parser import DiagramParser

View File

@ -1,55 +1,55 @@
import pandas as pd import pandas as pd
import numpy as np import numpy as np
class DiagramParser: class DiagramParser:
def __init__(self, scaler = 1): def __init__(self, scaler = 1):
self.data = pd.DataFrame({}) self.data = pd.DataFrame({})
self.scaler = scaler self.scaler = scaler
def setData(self, path): def setData(self, path):
self.data = pd.read_csv(path) self.data = pd.read_csv(path)
def getBoolDict (self): def getBoolDict (self):
boolDict = {} boolDict = {}
for signalName in self.data.columns: for signalName in self.data.columns:
if type (self.data[signalName].iloc[0]) == np.bool: if type (self.data[signalName].iloc[0]) == np.bool:
boolDict[signalName] = self._getBoolChanges(signalName) boolDict[signalName] = self._getBoolChanges(signalName)
return boolDict return boolDict
def getFloatDict (self): def getFloatDict (self):
floatDict = {} floatDict = {}
for signalName in self.data.columns: for signalName in self.data.columns:
if type (self.data[signalName].iloc[0]) == np.float64: if type (self.data[signalName].iloc[0]) == np.float64:
floatDict[signalName] = self._getFloatChanges(signalName) floatDict[signalName] = self._getFloatChanges(signalName)
return floatDict return floatDict
def _getBoolChanges(self, signalName): def _getBoolChanges(self, signalName):
timeCode = self.data['time'] timeCode = self.data['time']
signal_values = self.data[signalName] signal_values = self.data[signalName]
changes = [] changes = []
if len(signal_values) > 0: if len(signal_values) > 0:
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low']) changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
for i in range(1, len(signal_values)): for i in range(1, len(signal_values)):
if signal_values.iloc[i] != signal_values.iloc[i - 1]: if signal_values.iloc[i] != signal_values.iloc[i - 1]:
changes.append([float(timeCode.iloc[i])*self.scaler, 'high' if bool(signal_values.iloc[i]) else 'low']) changes.append([float(timeCode.iloc[i])*self.scaler, 'high' if bool(signal_values.iloc[i]) else 'low'])
return changes return changes
def _getFloatChanges(self, signalName): def _getFloatChanges(self, signalName):
timeCode = self.data['time'] timeCode = self.data['time']
signal_values = self.data[signalName] signal_values = self.data[signalName]
changes = [] changes = []
if len(signal_values) > 0: if len(signal_values) > 0:
for i in range (len(signal_values)): for i in range (len(signal_values)):
changes.append([float(timeCode.iloc[i]), float(signal_values.iloc[i])]) changes.append([float(timeCode.iloc[i]), float(signal_values.iloc[i])])
return changes return changes

View File

@ -1,11 +1,11 @@
from os import path from os import path
import json import json
def read_json(filepath: str) -> dict: def read_json(filepath: str) -> dict:
if not path.exists(filepath): if not path.exists(filepath):
raise FileNotFoundError(f"JSON file {filepath} not found!") raise FileNotFoundError(f"JSON file {filepath} not found!")
with open(filepath, 'r') as json_file: with open(filepath, 'r') as json_file:
data = json.load(json_file) data = json.load(json_file)
return data return data