Innovenergy_trunk/python/gui/templates.py

186 lines
10 KiB
Python
Raw Normal View History

2023-02-16 12:57:06 +00:00
from math import cos
from datetime import datetime
import plotly.graph_objects as go
import pandas as pd
from dash import html, dcc
import plotly.express as px
# TODO DOCUMENT MORE
def find_Device(name: str, devices):
return [dev for dev in devices if dev.name in name]
def find_Device_strict(name: str, devices):
return [dev for dev in devices if dev.name == name]
def now(timestamp):
2023-04-06 12:28:35 +00:00
# return datetime.fromtimestamp(int(timestamp.split("/")[-1])).replace(microsecond=0)
2023-02-16 12:57:06 +00:00
return datetime.now().replace(microsecond=0) #TODO CHANGE ME WHEN GETTING NEW FILES
# Main class for devices used for node graphs and plots
class Device:
def __init__(self, name):
self.name = name
self.data = {} # Todo: Maybe we can remove this altogether
self.graphs = []
self.built = False # Onetime token to build the graphs
self.hidden = True # Careful! True => Hidden, False => Visible
self.index = -1 # This ensures the index is smallest (As it normally is non-negative)
self.position = [0,0] # Position 0,0 is NOT drawn
self.power = [None, None]
self.Buspower = []
self.connection = [] # This is used for invisible devices to draw the graph
# building data from the json timestamped files to a list<dict> with the timestamp as index and all the data as dict
def BuildData(self, data):
values = {}
for key in data[0].keys():
values.update({key: []})
for timestamp in range(0, len(data)):
for key in data[timestamp].keys():
values[key].append(data[timestamp][key])
self.data = values
# This builds the Graphs on app-start and gives back the divs containing said plots
def BuildDiv(self, index):
if self.power == [None,None]:
self.UpdatePower()
if not self.built:
self.BuildGraphs()
self.built = True
self.index = index
return html.Div(id='graphs' + self.name, children=[
html.Div(id={"type": "graphs", "index": self.index}, children=self.graphs)]) #, style={'display': 'inlineBlock'}
def BuildChecklist(self, index):
return html.Div(className="round", children=[
dcc.Checklist([{'label': [self.name.replace('_', ' ').title(), html.Br()], 'value': self.name.replace('_', ' ')}],
persistence=False, id={'type': "topChecklist", 'index': self.index},
style={"paddingBottom": "0px", 'display': 'inlineBlock'}),
dcc.Checklist(options=[{"label": [graph.children.id["role"].split(self.name, 1)[1], html.Br()],
"value": graph.children.id} for graph in self.graphs], value=[],
persistence=False,
id={'type': "checklist", 'index': self.index},
style={"postition": "relative", "paddingLeft": "20px",
"paddingTop": "0px"})])
# If no graphs have been built yet builds graphs and gives back the div '{"index":1,"type":"topChecklist"}'
def UpdateDiv(self):
return self.BuildDiv(self.index)
# Builds all the plots
def BuildGraphs(self):
graphs = []
values = {}
time = []
# Get Data from each saved time
for i, timestamp in enumerate(self.data.keys()):
#time.append(datetime.fromtimestamp(int(timestamp.split("/")[-1])).replace(microsecond=0)) # Todo set me back in!
time.append(datetime.now().replace(microsecond=0)) # Todo Better for demo purposes
for key in self.data[timestamp].keys():
if not key in values.keys():
values.update({key: []})
values[key].append(self.data[timestamp][key])
for key in values.keys():
global fig
if "Ac" in key and not "Actual" in key and not 'Power' in key:
for key2 in values[key][0][0]:
fig = go.Figure()
fig.layout.title =self.name + " Ac " + key2
stackgroup1 = "one"
stackgroup2 = "two"
stackgroup3 = "three"
if key2 == "Current":
stackgroup2 = "one"
stackgroup3 = "one"
fig.add_trace(go.Scatter(x=time, y=[values[key][i][0][key2] for i in range(len(time))], fill='tonexty',name="Phase 1", stackgroup=stackgroup1))
fig.add_trace(go.Scatter(x=time, y=[values[key][i][1][key2] for i in range(len(time))], fill='tonexty', name="Phase 2", stackgroup=stackgroup2))
fig.add_trace(go.Scatter(x=time, y=[values[key][i][2][key2] for i in range(len(time))], fill='tonexty', name="Phase 3", stackgroup=stackgroup3))
fig.update_xaxes(fixedrange=True)
fig.update_yaxes(fixedrange=True, rangemode="tozero")
fig.update_layout(uirevision=False)
graphs.append(html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + "Ac " + key2.replace('/', ' '), "index":self.index*100+len(graphs)}, figure=fig,
config={'displayModeBar': False},
style={"width": "90%",
"float": "right"})))
continue
elif "Dc" in key and not "Actual" in key and not "Power" in key:
for key2 in values[key][0]:
df = pd.DataFrame({
"Zeit": time,
"Wert": [values[key][i][key2] for i in range(len(time))],
})
fig = px.area(df, x="Zeit", y="Wert", title=self.name + " " + key2.replace('/', ' ').title(),
markers=True)
fig.update_xaxes(fixedrange=True)
fig.update_yaxes(fixedrange=True, rangemode="tozero")
fig.update_layout(uirevision=False)
# finally add figure to graphs
graphs.append(html.Div(hidden=self.hidden,children=dcc.Graph(id={"type":"graph", "role": self.name + " " + 'Dc ' + key2.replace('/', ' '), "index":self.index*100+len(graphs)}, figure=fig, # id=self.name + 'Dc ' + key2,
config={'displayModeBar': False},
style={"width": "90%", "float": "right",
"display": "inline-block"})))
continue
elif "Alarm" in key:
fig = go.Figure()
fig.layout.title = self.name + "-Alarms"
for key2 in values[key][0]:
fig.add_trace(go.Scatter(x=time, y=[1 if key2 in values[key][i] else 0 for i in range(len(time))], fill='tonexty', name=key2, stackgroup="one"))
fig.update_xaxes(fixedrange=True)
fig.update_yaxes(fixedrange=True, rangemode="tozero")
fig.update_layout(uirevision=False)
graphs.append(
html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + "Alarms", "index":self.index*100+len(graphs)}, figure=fig,
config={'displayModeBar': False},
style={"width": "90%",
"float": "right"})))
continue
elif any(["Name" in key, "Type" in key, "MainState" in key]):
# Skip we have built this already, or it isn't useful
continue
# check if the data is values or flags/strings
if type(values[key][0]) is float or type(values[key][0]) is int:
df = pd.DataFrame({
"Zeit": time,
"Wert": values[key],
})
fig = px.area(df, x="Zeit", y="Wert", title=self.name + " " + key.replace('/', ' ').title(), markers=True)
fig.update_xaxes(fixedrange=True)
fig.update_yaxes(fixedrange=True, rangemode="tozero")
fig.update_layout(uirevision=False)
#finally add figure to graphs
graphs.append(html.Div(hidden=self.hidden, children=dcc.Graph(id={"type":"graph", "role": self.name + " " + key, "index":self.index*100+len(graphs)}, figure=fig, config = {'displayModeBar': False}, style={"width":"90%", "float":"right", "display":"inline-block"})))
self.graphs = graphs
# This checks what kind of device this is and sets power calculation
def UpdatePower(self):
if not self.data: #or self.data[list(self.data.keys())[0]]['Name'] == "ampt"
return
if "Frequency" in list(self.data[list(self.data.keys())[0]].keys()):
# 3-phase AC
for timestamp in self.data.keys():
self.data[timestamp].update({"Ac Power": self.data[timestamp]["Ac"][0]["Voltage"] * self.data[timestamp]["Ac"][0]["Current"] * cos(self.data[timestamp]["Ac"][0]["Phi"]) + self.data[timestamp]["Ac"][1]["Voltage"] * self.data[timestamp]["Ac"][1]["Current"] * cos(self.data[timestamp]["Ac"][1]["Phi"]) + self.data[timestamp]["Ac"][2]["Voltage"] * self.data[timestamp]["Ac"][2]["Current"] * cos(self.data[timestamp]["Ac"][2]["Phi"])})
self.power[0] = self.data[list(self.data.keys())[-1]]["Ac Power"]
if "Dc" in list(self.data[list(self.data.keys())[0]].keys()) or "Dc48" in list(self.data[list(self.data.keys())[0]].keys()):
# DC
Dc = "Dc48" if "Dc48" in list(self.data[list(self.data.keys())[0]].keys()) else "Dc"
for timestamp in self.data.keys():
self.data[timestamp].update({"Dc Power": self.data[timestamp][Dc]["Voltage"] * self.data[timestamp][Dc]["Current"]})
self.power[1] = self.data[list(self.data.keys())[-1]]["Dc Power"]