dev: добавлены эквидистанты
This commit is contained in:
parent
11be1685a7
commit
1ece32a57c
@ -7,7 +7,7 @@
|
||||
"dist_open_end_2" : 0.050,
|
||||
"dist_close_end_1" : 0.005,
|
||||
"dist_close_end_2" : 0.005,
|
||||
"time_wielding" : 2,
|
||||
"time_wielding" : 1,
|
||||
"time_command" : 0.060,
|
||||
"time_robot_movement" : 0.2,
|
||||
"object_thickness" : 4.5e-3,
|
||||
|
||||
@ -14,5 +14,6 @@
|
||||
"position_start_1" : 0.080,
|
||||
"position_start_2" : 0.080,
|
||||
"k_prop" : 0.05,
|
||||
"time_capture" : 100000
|
||||
"time_capture" : 100000,
|
||||
"UML_time_scaler": 1000
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
from .plot_window import PlotWindow
|
||||
from .plot_window import PlotWindow
|
||||
|
||||
@ -1,148 +1,207 @@
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
from src.gui import qt_settings as qts
|
||||
from src.OptAlgorithm import OptAlgorithm
|
||||
|
||||
|
||||
class PlotWindow:
|
||||
def __init__(self,
|
||||
opt: OptAlgorithm,
|
||||
bool_dict: dict,
|
||||
float_dict: dict):
|
||||
pg.setConfigOptions(antialias=True)
|
||||
self.opt = opt
|
||||
self.bool_dict = bool_dict
|
||||
self.float_dict = float_dict
|
||||
|
||||
self.scaler = 1000
|
||||
self._getIdealTimings()
|
||||
self._init_app()
|
||||
self.WeldTime = 1.0 #[sec] # TODO: to external config
|
||||
self.alpha = 100 #[0-255 прозрачность фона]
|
||||
|
||||
def _init_app(self):
|
||||
self.app = pg.mkQApp("Plotting")
|
||||
self.win = pg.GraphicsLayoutWidget(show=True, title="")
|
||||
self.win.resize(1000,600)
|
||||
self.win.setWindowTitle('')
|
||||
|
||||
self.p11, self.l11 = self._init_graph('Electrode force, closure', 'Force', 'N', 'Time', 'ms')
|
||||
#self.p21, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
|
||||
#self.p31, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
|
||||
self.win.nextRow()
|
||||
self.p12, self.l12 = self._init_graph('Rotor Position, closure', 'Posicion', 'mm', 'Time', 'ms')
|
||||
#self.p22, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
|
||||
#self.p32, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
|
||||
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.p33, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
|
||||
self.win.nextRow()
|
||||
|
||||
self.p12.setXLink(self.p11)
|
||||
self.p13.setXLink(self.p11)
|
||||
|
||||
self.p11.setAutoVisible(x=False, y=True)
|
||||
self.p12.setAutoVisible(x=False, y=True)
|
||||
self.p13.setAutoVisible(x=False, y=True)
|
||||
|
||||
def _init_graph(self, title, Yname, Yunits, Xname, Xunits):
|
||||
p11 = self.win.addPlot(title = title)
|
||||
p11.showGrid(x=True, y=True)
|
||||
p11.setLabel('left', Yname, units=Yunits)
|
||||
p11.setLabel('bottom', Xname, units=Xunits)
|
||||
legend1 = pg.LegendItem((80,60), offset=(70,20))
|
||||
legend1.setParentItem(p11)
|
||||
return p11, legend1
|
||||
|
||||
def updatePlots(self):
|
||||
self._plotRealData()
|
||||
times = np.arange(20000)/10
|
||||
self._plotIdealData(times)
|
||||
|
||||
def _getIdealTimings(self):
|
||||
data = self.opt.Ts
|
||||
self.idealTime = [data['tclose'], data['tgrow'], self.opt.getMarkOpen()]
|
||||
|
||||
def _form_idealdatGraph(self, times):
|
||||
self.idealPhase0 = (self.idealTime[0])*self.scaler #Подъезд
|
||||
self.idealPhase1 = (self.idealTime[0]+ self.idealTime[1])*self.scaler #Сжатие
|
||||
self.idealPhase2 = (self.idealTime[0]+ self.idealTime[1] + self.WeldTime)*self.scaler #Сварка
|
||||
self.idealPhase3 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime)*self.scaler #Разъезд
|
||||
data = []
|
||||
for time in times:
|
||||
if time <= self.idealPhase0:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseClose(time/self.scaler)
|
||||
elif time <= self.idealPhase1:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseGrow(time/self.scaler-self.idealTime[0])
|
||||
elif time <= self.idealPhase2:
|
||||
x_fe, x_me, v_fe, v_me, f = data[-1]
|
||||
elif time <= self.idealPhase3:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseOpen(time/self.scaler-self.idealTime[0]-self.idealTime[1]-self.WeldTime)
|
||||
else:
|
||||
x_fe, x_me, v_fe, v_me, f = 0, 0, 0, 0, 0
|
||||
data.append([x_fe, x_me, v_fe, v_me, f])
|
||||
data = np.array(data).T
|
||||
return data
|
||||
|
||||
def _plotRealData(self):
|
||||
for i, (key, dat) in enumerate(self.float_dict.items()):
|
||||
dat = np.array(dat).T
|
||||
dat[0] = dat[0]*self.scaler
|
||||
curve = pg.PlotDataItem(dat[0], dat[1], pen=pg.mkPen(color=qts.colors[i], width=2), name=key, autoDownsample=True, downsample=True)
|
||||
if 'Electrode Force' in key:
|
||||
self.p11.addItem(curve)
|
||||
self.l11.addItem(curve, key)
|
||||
elif 'Rotor Position' in key:
|
||||
self.p12.addItem(curve)
|
||||
self.l12.addItem(curve, key)
|
||||
elif 'Rotor Speed' in key:
|
||||
self.p13.addItem(curve)
|
||||
self.l13.addItem(curve, key)
|
||||
return dat[0]
|
||||
|
||||
def _plotIdealData(self, times):
|
||||
data = self._form_idealdatGraph(times)
|
||||
|
||||
x_fe = pg.PlotDataItem(times, data[0]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='x_fe', autoDownsample=True, downsample=True)
|
||||
x_me = pg.PlotDataItem(times, data[1]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='x_me', autoDownsample=True, downsample=True)
|
||||
v_fe = pg.PlotDataItem(times, data[2]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='v_fe', autoDownsample=True, downsample=True)
|
||||
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.p11.addItem(f)
|
||||
self.l11.addItem(f, 'Ideal force')
|
||||
|
||||
self.p12.addItem(x_fe)
|
||||
self.l12.addItem(x_fe, 'FE POS')
|
||||
self.p12.addItem(x_me)
|
||||
self.l12.addItem(x_me, 'ME POS')
|
||||
|
||||
self.p13.addItem(v_fe)
|
||||
self.l13.addItem(v_fe, 'FE VEL')
|
||||
self.p13.addItem(v_me)
|
||||
self.l13.addItem(v_me, 'ME VEL')
|
||||
self._addBackgroundSplitter()
|
||||
|
||||
def _addBackgroundSplitter(self):
|
||||
alpha = self.alpha
|
||||
x = [[0, self.idealPhase0],
|
||||
[self.idealPhase0, self.idealPhase1],
|
||||
[self.idealPhase1, self.idealPhase2],
|
||||
[self.idealPhase2, self.idealPhase3]]
|
||||
y01 = [10000, 10000]
|
||||
y0_1 = [-10000, -10000]
|
||||
for i, _ in enumerate(x):
|
||||
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=' ')
|
||||
bg1 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
bg2 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
bg3 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
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)
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
from src.gui import qt_settings as qts
|
||||
from src.OptAlgorithm import OptAlgorithm
|
||||
|
||||
|
||||
class PlotWindow:
|
||||
def __init__(self,
|
||||
system_config : dict,
|
||||
operator_config: dict,
|
||||
opt: OptAlgorithm,
|
||||
bool_dict: dict,
|
||||
float_dict: dict):
|
||||
pg.setConfigOptions(antialias=True)
|
||||
self.opt = opt
|
||||
self.bool_dict = bool_dict
|
||||
self.float_dict = float_dict
|
||||
self.scaler = int(system_config['UML_time_scaler'])
|
||||
self.WeldTime = operator_config['time_wielding'] #[sec]
|
||||
|
||||
|
||||
self._getIdealTimings()
|
||||
self._init_app()
|
||||
self.alpha = 100 #[0-255 прозрачность фона]
|
||||
|
||||
def _init_app(self):
|
||||
self.app = pg.mkQApp("Plotting")
|
||||
self.win = pg.GraphicsLayoutWidget(show=True, title="")
|
||||
self.win.resize(1000,600)
|
||||
self.win.setWindowTitle('')
|
||||
|
||||
self.p11, self.l11 = self._init_graph('Electrode force, closure', 'Force', 'N', 'Time', 'ms')
|
||||
#self.p21, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
|
||||
#self.p31, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
|
||||
self.win.nextRow()
|
||||
self.p12, self.l12 = self._init_graph('Rotor Position, closure', 'Posicion', 'mm', 'Time', 'ms')
|
||||
#self.p22, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
|
||||
#self.p32, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
|
||||
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.p33, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
|
||||
self.win.nextRow()
|
||||
|
||||
self.p12.setXLink(self.p11)
|
||||
self.p13.setXLink(self.p11)
|
||||
|
||||
self.p11.setAutoVisible(x=False, y=True)
|
||||
self.p12.setAutoVisible(x=False, y=True)
|
||||
self.p13.setAutoVisible(x=False, y=True)
|
||||
|
||||
def _init_graph(self, title, Yname, Yunits, Xname, Xunits):
|
||||
p11 = self.win.addPlot(title = title)
|
||||
p11.showGrid(x=True, y=True)
|
||||
p11.setLabel('left', Yname, units=Yunits)
|
||||
p11.setLabel('bottom', Xname, units=Xunits)
|
||||
legend1 = pg.LegendItem((80,60), offset=(70,20))
|
||||
legend1.setParentItem(p11)
|
||||
return p11, legend1
|
||||
|
||||
def updatePlots(self):
|
||||
self._plotRealData()
|
||||
times = np.arange(20000)/10
|
||||
self._plotIdealData(times)
|
||||
|
||||
def _getIdealTimings(self):
|
||||
data = self.opt.Ts
|
||||
self.idealTime = [data['tclose'], data['tgrow'], self.opt.getMarkOpen()]
|
||||
|
||||
def _form_idealdatGraph(self, times):
|
||||
self.idealPhase0 = (self.idealTime[0])*self.scaler #Подъезд
|
||||
self.idealPhase1 = (self.idealTime[0]+ self.idealTime[1])*self.scaler #Сжатие
|
||||
self.idealPhase2 = (self.idealTime[0]+ self.idealTime[1] + self.WeldTime)*self.scaler #Сварка
|
||||
self.idealPhase3 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime)*self.scaler #Разъезд
|
||||
self.idealPhase4 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime + 0.25)*self.scaler #Последнее смыкание #TODO добавить идеальное время вместо 0.25
|
||||
self.x_splitter = np.array([[0, self.idealPhase0],
|
||||
[self.idealPhase0, self.idealPhase1],
|
||||
[self.idealPhase1, self.idealPhase2],
|
||||
[self.idealPhase2, self.idealPhase3],
|
||||
[self.idealPhase3, self.idealPhase4]])
|
||||
data = []
|
||||
for time in times:
|
||||
if time <= self.idealPhase0:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseClose(time/self.scaler)
|
||||
elif time <= self.idealPhase1:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseGrow(time/self.scaler-self.idealTime[0])
|
||||
elif time <= self.idealPhase2:
|
||||
x_fe, x_me, v_fe, v_me, f = data[-1]
|
||||
elif time <= self.idealPhase3:
|
||||
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseOpen(time/self.scaler-self.idealTime[0]-self.idealTime[1]-self.WeldTime)
|
||||
else:
|
||||
x_fe, x_me, v_fe, v_me, f = 0, 0, 0, 0, 0
|
||||
data.append([x_fe, x_me, v_fe, v_me, f])
|
||||
data = np.array(data).T
|
||||
return data
|
||||
|
||||
def _plotRealData(self):
|
||||
for i, (key, dat) in enumerate(self.float_dict.items()):
|
||||
dat = np.array(dat).T
|
||||
dat[0] = dat[0]*self.scaler
|
||||
curve = pg.PlotDataItem(dat[0], dat[1], pen=pg.mkPen(color=qts.colors[i], width=2), name=key, autoDownsample=True, downsample=True)
|
||||
if 'Electrode Force' in key:
|
||||
self.p11.addItem(curve)
|
||||
self.l11.addItem(curve, key)
|
||||
elif 'Rotor Position' in key:
|
||||
self.p12.addItem(curve)
|
||||
self.l12.addItem(curve, key)
|
||||
elif 'Rotor Speed' in key:
|
||||
self.p13.addItem(curve)
|
||||
self.l13.addItem(curve, key)
|
||||
return dat[0]
|
||||
|
||||
def _plotIdealData(self, times):
|
||||
data = self._form_idealdatGraph(times)
|
||||
|
||||
x_fe = pg.PlotDataItem(times, data[0]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='x_fe', autoDownsample=True, downsample=True)
|
||||
x_me = pg.PlotDataItem(times, data[1]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='x_me', autoDownsample=True, downsample=True)
|
||||
v_fe = pg.PlotDataItem(times, data[2]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='v_fe', autoDownsample=True, downsample=True)
|
||||
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.p11.addItem(f)
|
||||
self.l11.addItem(f, 'Ideal force')
|
||||
|
||||
self.p12.addItem(x_fe)
|
||||
self.l12.addItem(x_fe, 'FE POS')
|
||||
self.p12.addItem(x_me)
|
||||
self.l12.addItem(x_me, 'ME POS')
|
||||
|
||||
self.p13.addItem(v_fe)
|
||||
self.l13.addItem(v_fe, 'FE VEL')
|
||||
self.p13.addItem(v_me)
|
||||
self.l13.addItem(v_me, 'ME VEL')
|
||||
self._addBackgroundSplitter()
|
||||
self._addEquidistances(times, data)
|
||||
|
||||
def _addBackgroundSplitter(self):
|
||||
alpha = self.alpha
|
||||
y01 = np.array([10000, 10000])
|
||||
y0_1 = np.array([-10000, -10000])
|
||||
for i, _ in enumerate(self.x_splitter):
|
||||
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=' ')
|
||||
bg1 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
bg2 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
bg3 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
|
||||
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)
|
||||
@ -1,255 +1,256 @@
|
||||
white_style = """
|
||||
QMainWindow {
|
||||
background-color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QMessageBox {
|
||||
background-color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #d3d3d3;
|
||||
color: #000000;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #b0b0b0;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #a9a9a9;
|
||||
color: #7f7f7f;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #000000;
|
||||
font-size: 14px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #f0f0f0;
|
||||
color: #000000;
|
||||
padding: 5px;
|
||||
border: 1px solid #a9a9a9;
|
||||
border-radius: 4px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLabel {
|
||||
color: #000000;
|
||||
font-size: 16px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QGroupBox {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QRadioButton {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
"""
|
||||
|
||||
dark_style = """
|
||||
QMainWindow {
|
||||
background-color: #0D1117; /* Тёмный, современный цвет для фона */
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QMessageBox {
|
||||
background-color: #161B22;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #FFCC00; /* Яркий жёлтый цвет для акцента */
|
||||
color: #0D1117; /* Темный цвет текста для контраста с желтым */
|
||||
padding: 12px 25px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700; /* Светлый желтый цвет для эффекта наведения */
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
border: none;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #21262D;
|
||||
color: #ffffff;
|
||||
padding: 8px;
|
||||
border: 2px solid #30363D;
|
||||
border-radius: 6px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QLabel {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QGroupBox {
|
||||
color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #30363D;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
QRadioButton {
|
||||
color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QSpinBox {
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QDoubleSpinBox {
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
|
||||
dis_robots = """
|
||||
QPushButton {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
QWidget#robot {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
background-color: rgba(33, 33, 33, 100);
|
||||
}
|
||||
"""
|
||||
|
||||
selected_robot = """
|
||||
QWidget#robot {
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
en_button = """
|
||||
QPushButton {
|
||||
background-color: #FFCC00;
|
||||
color: #0D1117;
|
||||
padding: 12px 25px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
dis_button = """
|
||||
QPushButton {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
saved_button = """
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: #ffffff;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
start_button = """
|
||||
QPushButton {
|
||||
background-color: #FFCC00;
|
||||
color: #0D1117;
|
||||
padding: 20px 40px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 10px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
background-image: linear-gradient(to bottom, #FFD700, #FFCC00);
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #E6B800;
|
||||
padding-top: 22px;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
border: none;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
colors = [
|
||||
'#FF6F61', # яркий коралловый
|
||||
'#6B5B95', # приглушенный фиолетовый
|
||||
'#88B04B', # яркий зеленый
|
||||
'#F7CAC9', # светлый розовый
|
||||
'#92A8D1', # светло-голубой
|
||||
'#955251', # теплый терракотовый
|
||||
'#B565A7', # лавандовый
|
||||
'#009B77', # глубокий бирюзовый
|
||||
'#DD4124', # ярко-красный
|
||||
'#45B8AC' # мягкий мятный
|
||||
]
|
||||
|
||||
RGBA = [(255, 255, 0),
|
||||
(32, 178, 70),
|
||||
(255, 69, 0),
|
||||
(123, 104, 238)
|
||||
white_style = """
|
||||
QMainWindow {
|
||||
background-color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QMessageBox {
|
||||
background-color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #d3d3d3;
|
||||
color: #000000;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #b0b0b0;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #a9a9a9;
|
||||
color: #7f7f7f;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #000000;
|
||||
font-size: 14px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #f0f0f0;
|
||||
color: #000000;
|
||||
padding: 5px;
|
||||
border: 1px solid #a9a9a9;
|
||||
border-radius: 4px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLabel {
|
||||
color: #000000;
|
||||
font-size: 16px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QGroupBox {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QRadioButton {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
"""
|
||||
|
||||
dark_style = """
|
||||
QMainWindow {
|
||||
background-color: #0D1117; /* Тёмный, современный цвет для фона */
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QMessageBox {
|
||||
background-color: #161B22;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #FFCC00; /* Яркий жёлтый цвет для акцента */
|
||||
color: #0D1117; /* Темный цвет текста для контраста с желтым */
|
||||
padding: 12px 25px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700; /* Светлый желтый цвет для эффекта наведения */
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
border: none;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #21262D;
|
||||
color: #ffffff;
|
||||
padding: 8px;
|
||||
border: 2px solid #30363D;
|
||||
border-radius: 6px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QLabel {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
QGroupBox {
|
||||
color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #30363D;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
QRadioButton {
|
||||
color: #ffffff;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QSpinBox {
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QDoubleSpinBox {
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
|
||||
dis_robots = """
|
||||
QPushButton {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
QWidget#robot {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
background-color: rgba(33, 33, 33, 100);
|
||||
}
|
||||
"""
|
||||
|
||||
selected_robot = """
|
||||
QWidget#robot {
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
en_button = """
|
||||
QPushButton {
|
||||
background-color: #FFCC00;
|
||||
color: #0D1117;
|
||||
padding: 12px 25px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
dis_button = """
|
||||
QPushButton {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
saved_button = """
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: #ffffff;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
start_button = """
|
||||
QPushButton {
|
||||
background-color: #FFCC00;
|
||||
color: #0D1117;
|
||||
padding: 20px 40px;
|
||||
border: 2px solid #E6B800;
|
||||
border-radius: 10px;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
background-image: linear-gradient(to bottom, #FFD700, #FFCC00);
|
||||
}
|
||||
QPushButton:hover:!disabled {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #E6B800;
|
||||
padding-top: 22px;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #555555;
|
||||
color: #cccccc;
|
||||
border: none;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
colors = [
|
||||
'#FF6F61', # яркий коралловый
|
||||
'#6B5B95', # приглушенный фиолетовый
|
||||
'#88B04B', # яркий зеленый
|
||||
'#F7CAC9', # светлый розовый
|
||||
'#92A8D1', # светло-голубой
|
||||
'#955251', # теплый терракотовый
|
||||
'#B565A7', # лавандовый
|
||||
'#009B77', # глубокий бирюзовый
|
||||
'#DD4124', # ярко-красный
|
||||
'#45B8AC' # мягкий мятный
|
||||
]
|
||||
|
||||
RGBA = [(255, 255, 0),
|
||||
(32, 178, 70),
|
||||
(255, 69, 0),
|
||||
(123, 104, 238),
|
||||
(99, 42, 15)
|
||||
]
|
||||
87
src/main.py
87
src/main.py
@ -1,42 +1,45 @@
|
||||
import pyqtgraph as pg
|
||||
|
||||
from utils import read_json, DiagramParser
|
||||
from uml import Request, UMLCreator
|
||||
from OptAlgorithm import OptAlgorithm
|
||||
from gui import PlotWindow
|
||||
|
||||
|
||||
def get_ideal_timings(opt: OptAlgorithm) -> list[float]:
|
||||
data = opt.Ts
|
||||
ideal_time = [data['tclose'], data['tgrow'], opt.getMarkOpen()]
|
||||
return ideal_time
|
||||
|
||||
|
||||
def main():
|
||||
operator_params = read_json("params/operator_params.json")
|
||||
system_params = read_json("params/system_params.json")
|
||||
opt_algorithm = OptAlgorithm(operator_config=operator_params, system_config=system_params)
|
||||
ideal_times = get_ideal_timings(opt_algorithm)
|
||||
|
||||
parser = DiagramParser()
|
||||
parser.setData("trace_samples/2024_11_01-09_39_17B00.csv")
|
||||
bool_dict = parser.getBoolDict()
|
||||
float_dict = parser.getFloatDict()
|
||||
|
||||
request_generator = Request(server_url='http://www.plantuml.com/plantuml/svg/')
|
||||
uml_creator = UMLCreator(request_generator=request_generator,
|
||||
ideal_time=ideal_times,
|
||||
bool_dict=bool_dict,
|
||||
float_dict=float_dict)
|
||||
uml_creator.update_uml()
|
||||
|
||||
app = PlotWindow(opt=opt_algorithm,
|
||||
bool_dict=bool_dict,
|
||||
float_dict=float_dict)
|
||||
|
||||
app.updatePlots()
|
||||
pg.exec()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
import pyqtgraph as pg
|
||||
|
||||
from src.utils import read_json, DiagramParser
|
||||
from src.uml import Request, UMLCreator
|
||||
from src.OptAlgorithm import OptAlgorithm
|
||||
from src.gui import PlotWindow
|
||||
|
||||
|
||||
def get_ideal_timings(opt: OptAlgorithm) -> list[float]:
|
||||
data = opt.Ts
|
||||
ideal_time = [data['tclose'], data['tgrow'], opt.getMarkOpen()]
|
||||
return ideal_time
|
||||
|
||||
|
||||
def main():
|
||||
operator_params = read_json("params/operator_params.json")
|
||||
system_params = read_json("params/system_params.json")
|
||||
opt_algorithm = OptAlgorithm(operator_config=operator_params, system_config=system_params)
|
||||
ideal_times = get_ideal_timings(opt_algorithm)
|
||||
|
||||
parser = DiagramParser()
|
||||
parser.setData("trace_samples/2024_11_01-09_39_17B00.csv")
|
||||
bool_dict = parser.getBoolDict()
|
||||
float_dict = parser.getFloatDict()
|
||||
|
||||
request_generator = Request(server_url='http://www.plantuml.com/plantuml/svg/')
|
||||
uml_creator = UMLCreator(system_config=system_params,
|
||||
request_generator=request_generator,
|
||||
ideal_time=ideal_times,
|
||||
bool_dict=bool_dict,
|
||||
float_dict=float_dict)
|
||||
uml_creator.update_uml()
|
||||
|
||||
app = PlotWindow(operator_config=operator_params,
|
||||
system_config=system_params,
|
||||
opt=opt_algorithm,
|
||||
bool_dict=bool_dict,
|
||||
float_dict=float_dict)
|
||||
|
||||
app.updatePlots()
|
||||
pg.exec()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
from .request_generator import Request
|
||||
from .request_generator import Request
|
||||
from .creator import UMLCreator
|
||||
@ -1,98 +1,100 @@
|
||||
from src.uml.request_generator import Request
|
||||
|
||||
|
||||
class UMLCreator:
|
||||
def __init__(self,
|
||||
request_generator: Request,
|
||||
ideal_time: list[float],
|
||||
bool_dict: dict,
|
||||
float_dict: dict):
|
||||
self._request_generator = request_generator
|
||||
self._ideal_time = ideal_time
|
||||
self.bool_dict = bool_dict
|
||||
self.float_dict = float_dict
|
||||
|
||||
def _build_data(self):
|
||||
scaler = 1000 # TODO: external config?
|
||||
sig = [
|
||||
'Electrode Closing Algorithm Execute', # Начало закрытия
|
||||
'Electrode Closing Algorithm Done',
|
||||
# 'STEP 3: ME Hold P2 AND Condition Start Force Control', # Конец закрытия и Начало набора усилия
|
||||
'STEP 4: ME Force Control',
|
||||
'Position Control ME', # Начало разъезда или 'Posision Control Activated FE'
|
||||
'Position Control FE', # Начало разъезда
|
||||
'Position Control Completed ME', # Конец разъезда
|
||||
'Position Control Completed FE', # Конец разъезда
|
||||
'STEP 4: ME Force Control Complete', # Конец набора усилия
|
||||
]
|
||||
closure = self._get_time([sig[0], sig[1]])
|
||||
compression = self._get_time([sig[2], sig[3]])
|
||||
# opening = self.__getTime([sig[4], sig[5], sig[6], sig[7]])
|
||||
|
||||
real_data = [
|
||||
[closure[0], 'closure #green'],
|
||||
[closure[1], '{-}'],
|
||||
[compression[0], 'compression #green'],
|
||||
[compression[1], '{-}'],
|
||||
# [max(opening[0:2]), 'opening #green'],
|
||||
# [max(opening[2:4]), '{-}'],
|
||||
]
|
||||
|
||||
client_data = [
|
||||
[0.0 * scaler, 'closure'],
|
||||
[0.165 * scaler, '{-}'],
|
||||
[0.166 * scaler, 'compression'],
|
||||
[0.176 * scaler, '{-}'],
|
||||
# [0.180*scaler, 'welding'],
|
||||
# [0.200*scaler, '{-}'],
|
||||
[0.210 * scaler, 'opening'],
|
||||
[0.300 * scaler, '{-}'],
|
||||
]
|
||||
|
||||
ideal_data = [
|
||||
[0.0 * scaler, 'closure #yellow'],
|
||||
[self._ideal_time[0] * scaler, '{-}'],
|
||||
[(self._ideal_time[0] + 0.0001) * scaler, 'compression #yellow'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1]) * scaler, '{-}'],
|
||||
# [0.*scaler, 'welding #yellow'],
|
||||
# [0.*scaler, '{-}'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1] + 0.0001) * scaler, 'opening #yellow'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1] + self._ideal_time[2]) * scaler, '{-}'],
|
||||
]
|
||||
return real_data, client_data, ideal_data, self.bool_dict
|
||||
|
||||
def _get_time(self, signals = '', states = ''):
|
||||
res = []
|
||||
for i, sig in enumerate(signals):
|
||||
if states: state = states[i]
|
||||
else: state = 'high'
|
||||
index1 = next ((i for i, _ in enumerate(self.bool_dict[sig]) if _[1]==state), -1)
|
||||
time = self.bool_dict[sig][index1][0]
|
||||
res.append(time)
|
||||
return res
|
||||
|
||||
def _generate_svg(self, real_data, client_data, ideal_data, bool_data) -> None:
|
||||
try:
|
||||
self._request_generator.clear()
|
||||
self._request_generator.addLineStyle('biba', 'red', 2)
|
||||
|
||||
for i, [signal, changes] in enumerate(bool_data.items()):
|
||||
name = 'bool_' + str(i)
|
||||
self._request_generator.addBinary(name, str(signal), 'biba')
|
||||
self._request_generator.setTimestamps(name, changes)
|
||||
|
||||
self._request_generator.addConcise('RD', 'Real data')
|
||||
self._request_generator.setTimestamps('RD', real_data)
|
||||
self._request_generator.addConcise('CD', 'Client data')
|
||||
self._request_generator.setTimestamps('CD', client_data)
|
||||
self._request_generator.addConcise('ID', 'Ideal data')
|
||||
self._request_generator.setTimestamps('ID', ideal_data)
|
||||
|
||||
self._request_generator.generateSVG()
|
||||
|
||||
except Exception as e:
|
||||
print(f"SVG generate error: {e}")
|
||||
|
||||
def update_uml(self) -> None:
|
||||
real, client, ideal, bool_ = self._build_data()
|
||||
self._generate_svg(real, client, ideal, bool_)
|
||||
from src.uml.request_generator import Request
|
||||
|
||||
|
||||
class UMLCreator:
|
||||
def __init__(self,
|
||||
system_config : dict,
|
||||
request_generator: Request,
|
||||
ideal_time: list[float],
|
||||
bool_dict: dict,
|
||||
float_dict: dict):
|
||||
self._request_generator = request_generator
|
||||
self._ideal_time = ideal_time
|
||||
self.bool_dict = bool_dict
|
||||
self.float_dict = float_dict
|
||||
self.scaler = int(system_config['UML_time_scaler'])
|
||||
|
||||
|
||||
def _build_data(self):
|
||||
sig = [
|
||||
'Electrode Closing Algorithm Execute', # Начало закрытия
|
||||
'Electrode Closing Algorithm Done',
|
||||
# 'STEP 3: ME Hold P2 AND Condition Start Force Control', # Конец закрытия и Начало набора усилия
|
||||
'STEP 4: ME Force Control',
|
||||
'Position Control ME', # Начало разъезда или 'Posision Control Activated FE'
|
||||
'Position Control FE', # Начало разъезда
|
||||
'Position Control Completed ME', # Конец разъезда
|
||||
'Position Control Completed FE', # Конец разъезда
|
||||
'STEP 4: ME Force Control Complete', # Конец набора усилия
|
||||
]
|
||||
closure = self._get_time([sig[0], sig[1]])
|
||||
compression = self._get_time([sig[2], sig[3]])
|
||||
# opening = self.__getTime([sig[4], sig[5], sig[6], sig[7]])
|
||||
|
||||
real_data = [
|
||||
[closure[0], 'closure #green'],
|
||||
[closure[1], '{-}'],
|
||||
[compression[0], 'compression #green'],
|
||||
[compression[1], '{-}'],
|
||||
# [max(opening[0:2]), 'opening #green'],
|
||||
# [max(opening[2:4]), '{-}'],
|
||||
]
|
||||
|
||||
client_data = [
|
||||
[0.0 * self.scaler, 'closure'],
|
||||
[0.165 * self.scaler, '{-}'],
|
||||
[0.166 * self.scaler, 'compression'],
|
||||
[0.176 * self.scaler, '{-}'],
|
||||
# [0.180*self.scaler, 'welding'],
|
||||
# [0.200*self.scaler, '{-}'],
|
||||
[0.210 * self.scaler, 'opening'],
|
||||
[0.300 * self.scaler, '{-}'],
|
||||
]
|
||||
|
||||
ideal_data = [
|
||||
[0.0 * self.scaler, 'closure #yellow'],
|
||||
[self._ideal_time[0] * self.scaler, '{-}'],
|
||||
[(self._ideal_time[0] + 0.0001) * self.scaler, 'compression #yellow'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1]) * self.scaler, '{-}'],
|
||||
# [0.*self.scaler, 'welding #yellow'],
|
||||
# [0.*self.scaler, '{-}'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1] + 0.0001) * self.scaler, 'opening #yellow'],
|
||||
[(self._ideal_time[0] + self._ideal_time[1] + self._ideal_time[2]) * self.scaler, '{-}'],
|
||||
]
|
||||
return real_data, client_data, ideal_data, self.bool_dict
|
||||
|
||||
def _get_time(self, signals = '', states = ''):
|
||||
res = []
|
||||
for i, sig in enumerate(signals):
|
||||
if states: state = states[i]
|
||||
else: state = 'high'
|
||||
index1 = next ((i for i, _ in enumerate(self.bool_dict[sig]) if _[1]==state), -1)
|
||||
time = self.bool_dict[sig][index1][0]
|
||||
res.append(time)
|
||||
return res
|
||||
|
||||
def _generate_svg(self, real_data, client_data, ideal_data, bool_data) -> None:
|
||||
try:
|
||||
self._request_generator.clear()
|
||||
self._request_generator.addLineStyle('biba', 'red', 2)
|
||||
|
||||
for i, [signal, changes] in enumerate(bool_data.items()):
|
||||
name = 'bool_' + str(i)
|
||||
self._request_generator.addBinary(name, str(signal), 'biba')
|
||||
self._request_generator.setTimestamps(name, changes)
|
||||
|
||||
self._request_generator.addConcise('RD', 'Real data')
|
||||
self._request_generator.setTimestamps('RD', real_data)
|
||||
self._request_generator.addConcise('CD', 'Client data')
|
||||
self._request_generator.setTimestamps('CD', client_data)
|
||||
self._request_generator.addConcise('ID', 'Ideal data')
|
||||
self._request_generator.setTimestamps('ID', ideal_data)
|
||||
|
||||
self._request_generator.generateSVG()
|
||||
|
||||
except Exception as e:
|
||||
print(f"SVG generate error: {e}")
|
||||
|
||||
def update_uml(self) -> None:
|
||||
real, client, ideal, bool_ = self._build_data()
|
||||
self._generate_svg(real, client, ideal, bool_)
|
||||
|
||||
@ -1,99 +1,99 @@
|
||||
from plantuml import PlantUML
|
||||
from os.path import abspath
|
||||
|
||||
|
||||
class Request:
|
||||
def __init__(self, server_url: str):
|
||||
self._server_url = server_url
|
||||
|
||||
self._init_server()
|
||||
|
||||
def _init_server(self):
|
||||
self.server = PlantUML(url=self._server_url)
|
||||
self.clear()
|
||||
|
||||
def _startUML(self):
|
||||
self.reqArr.append('@startuml')
|
||||
|
||||
def _endUML(self):
|
||||
self.reqArr.append('@enduml')
|
||||
|
||||
def clear(self):
|
||||
self.timestamps = {}
|
||||
self.reqArr = []
|
||||
self.variables = []
|
||||
self.lineStyles = {}
|
||||
self.stringUML = ''
|
||||
self._startUML()
|
||||
|
||||
def addAnalog(self, name, string):
|
||||
self.variables.append(f'analog "{string}" as {name}')
|
||||
|
||||
def addBinary(self, name, string, style = ''):
|
||||
if style: name = name + '<<' + style + '>>'
|
||||
self.variables.append(f'binary "{string}" as {name}')
|
||||
|
||||
def addClock(self, name, string):
|
||||
self.variables.append(f'clock "{string}" as {name}')
|
||||
|
||||
def addConcise(self, name, string):
|
||||
self.variables.append(f'concise "{string}" as {name}')
|
||||
|
||||
def addRobust(self, name, string):
|
||||
self.variables.append(f'robust "{string}" as {name}')
|
||||
|
||||
def appendStr(self, string = ''):
|
||||
self.variables.append(string)
|
||||
|
||||
def addLineStyle(self, name, color = 'green', thicknes = 1):
|
||||
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
|
||||
|
||||
def generateSVG(self):
|
||||
self._compileUML()
|
||||
filename = abspath('UML.txt')
|
||||
self.server.processes_file(filename, outfile='UML.svg')
|
||||
#result = self.server.processes(self.stringUML)
|
||||
#return result
|
||||
|
||||
def setTimestamps(self, name, input):
|
||||
for time, state in input:
|
||||
try:
|
||||
self.timestamps[f'@{time}'].append(f'{name} is {state}')
|
||||
except KeyError:
|
||||
self.timestamps[f'@{time}'] = [f'{name} is {state}']
|
||||
|
||||
def _addTimestamp(self, timecode, vars):
|
||||
self.reqArr.append(timecode)
|
||||
for var in vars:
|
||||
self.reqArr.append(var)
|
||||
|
||||
def _addHeader(self):
|
||||
self.reqArr.append('<style>')
|
||||
if self.lineStyles:
|
||||
self.reqArr.append('timingDiagram {')
|
||||
for name, style in self.lineStyles.items():
|
||||
self.reqArr.append(f' .{name} ' + '{')
|
||||
for param in style: self.reqArr.append(' ' + str(param))
|
||||
self.reqArr.append(' }')
|
||||
self.reqArr.append('}')
|
||||
|
||||
self.reqArr.append('</style>')
|
||||
|
||||
def _addVariables(self):
|
||||
for var in self.variables: self.reqArr.append(str(var))
|
||||
|
||||
def _compileUML(self):
|
||||
self._addHeader()
|
||||
self._addVariables()
|
||||
|
||||
if self.timestamps:
|
||||
for key, item in self.timestamps.items():
|
||||
self._addTimestamp(key, item)
|
||||
|
||||
self._endUML()
|
||||
self.stringUML = [line + '\n' for line in self.reqArr]
|
||||
with open('UML.txt', 'w', encoding='utf-8') as file:
|
||||
file.writelines(self.stringUML)
|
||||
|
||||
|
||||
|
||||
from plantuml import PlantUML
|
||||
from os.path import abspath
|
||||
|
||||
|
||||
class Request:
|
||||
def __init__(self, server_url: str):
|
||||
self._server_url = server_url
|
||||
|
||||
self._init_server()
|
||||
|
||||
def _init_server(self):
|
||||
self.server = PlantUML(url=self._server_url)
|
||||
self.clear()
|
||||
|
||||
def _startUML(self):
|
||||
self.reqArr.append('@startuml')
|
||||
|
||||
def _endUML(self):
|
||||
self.reqArr.append('@enduml')
|
||||
|
||||
def clear(self):
|
||||
self.timestamps = {}
|
||||
self.reqArr = []
|
||||
self.variables = []
|
||||
self.lineStyles = {}
|
||||
self.stringUML = ''
|
||||
self._startUML()
|
||||
|
||||
def addAnalog(self, name, string):
|
||||
self.variables.append(f'analog "{string}" as {name}')
|
||||
|
||||
def addBinary(self, name, string, style = ''):
|
||||
if style: name = name + '<<' + style + '>>'
|
||||
self.variables.append(f'binary "{string}" as {name}')
|
||||
|
||||
def addClock(self, name, string):
|
||||
self.variables.append(f'clock "{string}" as {name}')
|
||||
|
||||
def addConcise(self, name, string):
|
||||
self.variables.append(f'concise "{string}" as {name}')
|
||||
|
||||
def addRobust(self, name, string):
|
||||
self.variables.append(f'robust "{string}" as {name}')
|
||||
|
||||
def appendStr(self, string = ''):
|
||||
self.variables.append(string)
|
||||
|
||||
def addLineStyle(self, name, color = 'green', thicknes = 1):
|
||||
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
|
||||
|
||||
def generateSVG(self):
|
||||
self._compileUML()
|
||||
filename = abspath('UML.txt')
|
||||
self.server.processes_file(filename, outfile='UML.svg')
|
||||
#result = self.server.processes(self.stringUML)
|
||||
#return result
|
||||
|
||||
def setTimestamps(self, name, input):
|
||||
for time, state in input:
|
||||
try:
|
||||
self.timestamps[f'@{time}'].append(f'{name} is {state}')
|
||||
except KeyError:
|
||||
self.timestamps[f'@{time}'] = [f'{name} is {state}']
|
||||
|
||||
def _addTimestamp(self, timecode, vars):
|
||||
self.reqArr.append(timecode)
|
||||
for var in vars:
|
||||
self.reqArr.append(var)
|
||||
|
||||
def _addHeader(self):
|
||||
self.reqArr.append('<style>')
|
||||
if self.lineStyles:
|
||||
self.reqArr.append('timingDiagram {')
|
||||
for name, style in self.lineStyles.items():
|
||||
self.reqArr.append(f' .{name} ' + '{')
|
||||
for param in style: self.reqArr.append(' ' + str(param))
|
||||
self.reqArr.append(' }')
|
||||
self.reqArr.append('}')
|
||||
|
||||
self.reqArr.append('</style>')
|
||||
|
||||
def _addVariables(self):
|
||||
for var in self.variables: self.reqArr.append(str(var))
|
||||
|
||||
def _compileUML(self):
|
||||
self._addHeader()
|
||||
self._addVariables()
|
||||
|
||||
if self.timestamps:
|
||||
for key, item in self.timestamps.items():
|
||||
self._addTimestamp(key, item)
|
||||
|
||||
self._endUML()
|
||||
self.stringUML = [line + '\n' for line in self.reqArr]
|
||||
with open('UML.txt', 'w', encoding='utf-8') as file:
|
||||
file.writelines(self.stringUML)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
from .json_tools import read_json
|
||||
from .json_tools import read_json
|
||||
from .diagram_parser import DiagramParser
|
||||
@ -1,55 +1,55 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
class DiagramParser:
|
||||
def __init__(self, scaler = 1):
|
||||
self.data = pd.DataFrame({})
|
||||
self.scaler = scaler
|
||||
|
||||
def setData(self, path):
|
||||
self.data = pd.read_csv(path)
|
||||
|
||||
def getBoolDict (self):
|
||||
boolDict = {}
|
||||
for signalName in self.data.columns:
|
||||
if type (self.data[signalName].iloc[0]) == np.bool:
|
||||
boolDict[signalName] = self._getBoolChanges(signalName)
|
||||
return boolDict
|
||||
|
||||
def getFloatDict (self):
|
||||
floatDict = {}
|
||||
for signalName in self.data.columns:
|
||||
if type (self.data[signalName].iloc[0]) == np.float64:
|
||||
floatDict[signalName] = self._getFloatChanges(signalName)
|
||||
return floatDict
|
||||
|
||||
|
||||
def _getBoolChanges(self, signalName):
|
||||
timeCode = self.data['time']
|
||||
signal_values = self.data[signalName]
|
||||
changes = []
|
||||
|
||||
if len(signal_values) > 0:
|
||||
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
|
||||
|
||||
for i in range(1, len(signal_values)):
|
||||
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'])
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def _getFloatChanges(self, signalName):
|
||||
timeCode = self.data['time']
|
||||
signal_values = self.data[signalName]
|
||||
changes = []
|
||||
|
||||
if len(signal_values) > 0:
|
||||
for i in range (len(signal_values)):
|
||||
changes.append([float(timeCode.iloc[i]), float(signal_values.iloc[i])])
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
class DiagramParser:
|
||||
def __init__(self, scaler = 1):
|
||||
self.data = pd.DataFrame({})
|
||||
self.scaler = scaler
|
||||
|
||||
def setData(self, path):
|
||||
self.data = pd.read_csv(path)
|
||||
|
||||
def getBoolDict (self):
|
||||
boolDict = {}
|
||||
for signalName in self.data.columns:
|
||||
if type (self.data[signalName].iloc[0]) == np.bool:
|
||||
boolDict[signalName] = self._getBoolChanges(signalName)
|
||||
return boolDict
|
||||
|
||||
def getFloatDict (self):
|
||||
floatDict = {}
|
||||
for signalName in self.data.columns:
|
||||
if type (self.data[signalName].iloc[0]) == np.float64:
|
||||
floatDict[signalName] = self._getFloatChanges(signalName)
|
||||
return floatDict
|
||||
|
||||
|
||||
def _getBoolChanges(self, signalName):
|
||||
timeCode = self.data['time']
|
||||
signal_values = self.data[signalName]
|
||||
changes = []
|
||||
|
||||
if len(signal_values) > 0:
|
||||
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
|
||||
|
||||
for i in range(1, len(signal_values)):
|
||||
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'])
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def _getFloatChanges(self, signalName):
|
||||
timeCode = self.data['time']
|
||||
signal_values = self.data[signalName]
|
||||
changes = []
|
||||
|
||||
if len(signal_values) > 0:
|
||||
for i in range (len(signal_values)):
|
||||
changes.append([float(timeCode.iloc[i]), float(signal_values.iloc[i])])
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
from os import path
|
||||
import json
|
||||
|
||||
|
||||
def read_json(filepath: str) -> dict:
|
||||
if not path.exists(filepath):
|
||||
raise FileNotFoundError(f"JSON file {filepath} not found!")
|
||||
|
||||
with open(filepath, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
return data
|
||||
from os import path
|
||||
import json
|
||||
|
||||
|
||||
def read_json(filepath: str) -> dict:
|
||||
if not path.exists(filepath):
|
||||
raise FileNotFoundError(f"JSON file {filepath} not found!")
|
||||
|
||||
with open(filepath, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
return data
|
||||
|
||||
Loading…
Reference in New Issue
Block a user