186 lines
10 KiB
Python
186 lines
10 KiB
Python
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):
|
|
# return datetime.fromtimestamp(int(timestamp.split("/")[-1])).replace(microsecond=0)
|
|
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"] |