restructure code and use frappy.client for SECoP

- separation of SECoP part from webserver
- use frappy client instead of own code for SECoP communication
This commit is contained in:
l_samenv
2024-10-23 11:10:04 +02:00
parent b708197d27
commit b07ca0bd4f
8 changed files with 597 additions and 1473 deletions

View File

@ -4,24 +4,33 @@ from colors import assign_colors_to_curves
import json
import io
from chart_config import ChartConfig
from base import Instrument
class InfluxGraph:
"""
Class implementing the logic of the different routes that are called by the client to retrieve graph data with InfluxDB.
Class implementing the logic of the different routes that are called by
the client to retrieve graph data with InfluxDB.
Global constants :
HISTORICAL (int) : value that represents the "historical" visualization mode, meaning that the most recent point is not in the visualisation window (no live data is sent).
ACTUAL (int) : value that represents the "actual" visualization mode, wihch is an intermediate state used before going for live mode (the requested time window includes now)
LIVE (int) : value that represents the "live" visualization mode, meaning that new points are sent to the client.
HISTORICAL (int) : value that represents the "historical" visualization mode, meaning that the
most recent point is not in the visualisation window (no live data is sent).
ACTUAL (int) : value that represents the "actual" visualization mode, wihch is an intermediate
state used before going for live mode (the requested time window includes now)
LIVE (int) : value that represents the "live" visualization mode, meaning that new points are
sent to the client.
Attributes :
influx_data_getter (InfluxDataGetter) : the InfluxDataGetter instance that allows to get data out of InfluxDB.
chart_configs ([ChartConfig]) : an array of chart configuration to apply when /getvars is called
livemode (int) : the type of visualization the user is currently in. Can be HISTORICAL, ACTUAL or LIVE.
end_query (int) : the unix timestamp in seconds of the most recent requested point in time of the last query or update.
lastvalues ({(str):((int), (float))}) : a dictionnary where the keys are the variable names, and the values are tuples, where the first
value is the unix timestamp of the most recent value known for this variable, and the second value its corresponding value
variables ({(str):(str)}) : a dictionnary of the current available variables requested by the client. The key is the InfluxDB name of the curve, and the value is its label in the GUI.
end_query (int) : the unix timestamp in seconds of the most recent requested point in time of the last query
or update.
lastvalues ({(str):((int), (float))}) : a dictionnary where the keys are the variable names, and the values
are tuples, where the first value is the unix timestamp of the most recent value known for this variable,
and the second value its corresponding value
variables ({(str):(str)}) : a dictionnary of the current available variables requested by the client.
The key is the InfluxDB name of the curve, and the value is its label in the GUI.
"""
HISTORICAL = 0
ACTUAL = 1
@ -33,13 +42,12 @@ class InfluxGraph:
self.livemode = self.HISTORICAL
self.end_query = 0
self.lastvalues = {}
self.variables = {} # name:label
self.variables = {} # name:label
def get_abs_time(self, times):
"""
Gets the absolute times for the given pontential relative times. If the given timestamps are less than one year, then the value is relative
and converted into an asbolute timestamps
Gets the absolute times for the given potential relative times. If a given timestamp is less than
one year, then the value is relative and converted into an absolute timestamp
Parameters :
times([(float)]) : an array of unix timestamps or relative duration (< 1 year) as floats
@ -58,7 +66,7 @@ class InfluxGraph:
Parameters :
result ({(str):[[(int),(float)]]}) : a dictionnary with the variable names as key, and an array of points,
which are an array containing the timestamp as their first value, and the y-value in float as their second one.
which are a tuple (timestamp, y-value as float)
endtime (int) : the unix timestamp in seconds of the time we want to have data until
"""
for var, c in result.items():
@ -75,12 +83,14 @@ class InfluxGraph:
Parameters :
variables (str) : a comma separataed value string of variable names (influx names) to retrieve
time (str) : a commma separated value string (range) of seconds. They are treated as relative from now if they are lesser than one year.
time (str) : a commma separated value string (range) of seconds. They are treated as relative from now
if they are lesser than one year.
interval (str) : the interval (resolution) of the values to get (string in milliseconds)
Returns :
{"type":"graph-draw", "graph":{(str):[[(int),(float)]]}} : a dictionnary with its "graph-draw" type (so it can be processed by the client), and a "graph" dictionnary with the variable names as key, and an array of points,
which are an array containing the timestamp as their first value, and the y-value in float as their second one.
{"type":"graph-draw", "graph":{(str):[[(int),(float)]]}} : a dictionnary with its "graph-draw" type
(so it can be processed by the client), and a "graph" dictionnary with the variable names as key,
and an array of points as a tuple (timestamp, y-value as float)
"""
time = [float(t) for t in time.split(',')]
start, end, now = self.get_abs_time(time + [0])
@ -103,11 +113,12 @@ class InfluxGraph:
Called when the route /gettime is reached.
Parameters :
time (str="-1800,0") : the given point in time represented by a string, which is a comma separated unix timestamp values list (in seconds). They are treated as relative from now if they are lesser than one year.
time (str="-1800,0") : the given point in time represented by a string, which is a comma separated unix
timestamp values list (in seconds). They are treated as relative from now if they are lesser than one year.
Returns :
{"type":"time", "time":(int)} : a dictionnary with its "time" type (so the data can be processed by the client) and the server unix timestamp in seconds corresponding
to the time asked by the client
{"type":"time", "time":(int)} : a dictionnary with its "time" type (so the data can be processed by the
client) and the server unix timestamp in seconds corresponding to the time asked by the client
"""
time = [float(t) for t in time.split(',')]
return dict(type='time', time= self.get_abs_time(time))
@ -118,13 +129,17 @@ class InfluxGraph:
Called when the route /getvars is reached.
Parameters :
time (str) : the given point in time represented by a string, which is a unix timestamp in seconds. It is treated as relative from now if it is lesser than one year.
time (str) : the given point in time represented by a string, which is a unix timestamp in seconds.
It is treated as relative from now if it is lesser than one year.
userconfiguration (str|None) : the JSON string representing the user configuration
Returns :
{"type":"var_list", "device":(str), "blocks":[{"tag":(str),"unit":(str), "curves":[{"name":(str), "label":(str), "color":(str), "original_color":(str)}]}]} :
a dictionnary with its "var_list" type (so the data can be processed by the client), the device that was currently set at that time, and the available curves with the name of the internal variable,
the color to display for this curve, its original color in SEA, grouped by their tag (which is a category or unit if absent) and their unit (in "blocks")
{"type":"var_list", "device":(str), "blocks":[{"tag":(str),"unit":(str), "curves":
[{"name":(str), "label":(str), "color":(str), "original_color":(str)}]}]}:
a dictionnary with its "var_list" type (so the data can be processed by the client), the device that
was currently set at that time, and the available curves with the name of the internal variable,
the color to display for this curve, its original color in SEA, grouped by their tag (which is a
category or unit if absent) and their unit (in "blocks")
"""
time = [float(t) for t in time.split(',')]
@ -148,7 +163,8 @@ class InfluxGraph:
Sets the current visualisation mode to LIVE if not in HISTORICAL mode.
Called when the route /updategraph is reached.
Returns :
{"type":"accept-graph", "live": bool} : a dict with its "accept-graph" type and a "live" value telling if the server could change its visualization mode to live
{"type":"accept-graph", "live": bool} : a dict with its "accept-graph" type and a "live"
value telling if the server could change its visualization mode to live
"""
logging.info("UPD GRAPH %d", self.livemode)
if self.livemode == self.HISTORICAL:
@ -188,12 +204,16 @@ class InfluxGraph:
def graphpoll(self):
"""
Polls the last known values for all the available variables, and returns only those whose polled values are more recent than the most recent displayed one.
Every plain minute, all the variables are returned with a point having their last known value at the current timestamp to synchronize all the curves on the GUI.
Polls the last known values for all the available variables, and returns only those whose polled values
are more recent than the most recent displayed one.
Every plain minute, all the variables are returned with a point having their last known value at the current
timestamp to synchronize all the curves on the GUI.
Returns :
{"type":"graph-update", "time":(int), "graph":{(str):[[(int),(float)]]}} | None : a dictionnary with its "graph-update" type
(so it can be processed by the client), and a "graph" dictionnary with the variable names as key, and an array of points, which are an array containing the timestamp
{"type":"graph-update", "time":(int), "graph":{(str):[[(int),(float)]]}} | None :
a dictionnary with its "graph-update" type
(so it can be processed by the client), and a "graph" dictionnary with the variable names as key,
and an array of points, which are an array containing the timestamp
as their first value, and the y-value in float as their second one
"""
if self.livemode != self.LIVE:
@ -203,7 +223,8 @@ class InfluxGraph:
result = self.influx_data_getter.poll_last_values(list(self.variables.keys()), self.lastvalues, now)
for variable, values in list(result.items()):
tlast = self.lastvalues.get(variable, (0,))[0]
# removes points older than the last known point (queries are in seconds and might return points already displayed)
# removes points older than the last known point
# (queries are in seconds and might return points already displayed)
while values and values[0][0] <= tlast:
values.pop(0)
if values and values[-1][0] > tlast:
@ -219,3 +240,54 @@ class InfluxGraph:
if len(result) > 0:
return dict(type='graph-update', time=now, graph=result)
return None
class InfluxInstrument(Instrument):
def __init__(self, instr_name, inst_config=None):
self.db = InfluxDB()
self.influx_data_getter = InfluxDataGetter(self.db, inst_name)
self.clients = {}
self.title = instr_name
self.device = self.influx_data_getter.get_device_name(int(datetime.now().timestamp()))
def new_client(self):
return self.register(InfluxClient())
class InfluxParams:
"""Class with dummy routes, in case client side is started with the right part init commands"""
def __init__(self):
self.id = uuid.uuid4().hex[0:15]
self.queue = []
def poll(self):
messages = self.queue
self.queue = []
msg = self.graphpoll()
if msg:
messages.append(msg)
return messages
def info(self):
return ["na"]
def w_getblock(self, path):
return dict(type='draw', title="graph", path=path, components=[])
def w_updateblock(self, path):
return dict(type='accept-block')
def w_console(self):
return dict(type='accept-console')
def w_sendcommand(self, command):
return dict(type='accept-command')
class InfluxClient(InfluxParams, InfluxGraph):
def __init__(self):
InfluxParams.__init__(self)
InfluxGraph.__init__(self, instrument.influx_data_getter, instrument.title)