Files
seweb/seagraph_new.py
2020-12-04 08:54:24 +01:00

233 lines
10 KiB
Python

from datetime import date
import time
import sys
import os
import logging
import json
class PrettyFloat(float):
def __repr__(self):
return '%.15g' % self
#encode = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
def get_abs_time(times):
now = int(time.time() + 0.999)
oneyear = 365 * 24 * 3600
return [t + now if t < oneyear else t for t in times]
class Scanner(object):
def __init__(self, directory, test_day=None):
self.directory = directory
self.last_time = {}
self.test_day = test_day
def scan(self, variable, timerange, next_point, *args, maxpoints=1000):
"""scan *variable* for *timerange*
args is forwarded to self.next() and contains at least the
initialisation for the result
returns a tuple(<dirty>, result)
<dirty> is true when the last time on the variable has changed
"""
start, to, now = get_abs_time(timerange + [0])
tresolution = (to - start) / float(maxpoints) if maxpoints else 0
old = None
t = 0
for di in range(date.fromtimestamp(start).toordinal(), date.fromtimestamp(to).toordinal() + 1):
d = date.fromordinal(di)
year, mon, day = self.test_day if self.test_day else (d.year, d.month, d.day)
path = self.directory + "logger/%d/%s/%.2d-%.2d.log" % \
(year, variable.lower(), mon, day)
try:
# logging.info("logger path %s", path)
with open(path) as f:
t0 = time.mktime((d.year, d.month, d.day, 0, 0, 0, 0, 0, -1))
for line in f:
if line[0] != '#':
t = t0 + (int(line[0:2]) * 60 + int(line[3:5])) * 60 + int(line[6:8])
if line[-1:] == '\n':
value = line[9:-1]
else:
value = line[9:]
if t < start:
old = value
else:
if old is not None:
next_point(start, old, tresolution, *args)
old = None
self.next(t, value, result)
if t > to:
break
except IOError:
pass
if t < start:
#if t == 0:
# t = start
if old is not None:
self.next(t, old, result)
if t != self.last_time.get(variable,0):
self.last_time[variable] = t
return True, result
return False, result
class NumCurve(object):
def __init__(self, tresolution):
self.result = []
self.tresolution = tresolution
def append(self, t, txtvalue):
try:
value = PrettyFloat(txtvalue)
except:
value = None
self.result.append((PrettyFloat(t), value))
self.value = value
self.last = t
class NumericScanner(Scanner):
def __init__(self, *args, **kwargs):
Scanner.__init__(self, *args, **kwargs)
def get_message(self, variables, timerange, show_empty=True):
tresolution = timerange / x
self.dirty = False
result = {}
for var in variables:
self.last = 0
self.dirty, curve = self.scan(var, timerange, []):
if show_empty or len(curve) > 1:
result[var] = curve
return result
class ColorMap(object):
'''
ColorMap is using official CSS color names, with the exception of Green, as this
is defined differently with X11 colors than in SEA, and used heavily in config files.
Here Green is an alias to Lime (#00FF00) and MidGreen is #008000, which is called Green in CSS.
The function to_code is case insensitive and accepts also names with underscores.
The order is choosen by M. Zolliker for the SEA client, originally only the first 16 were used.
'''
hex_name = (("#FFFFFF","White"), ("#FF0000","Red"), ("#00FF00","Lime"), ("#0000FF","Blue"), ("#FF00FF","Magenta"),
("#FFFF00","Yellow"), ("#00FFFF","Cyan"), ("#000000","Black"), ("#FFA500","Orange"), ("#006400","DarkGreen"),
("#9400D3","DarkViolet"), ("#A52A2A","Brown"), ("#87CEEB","SkyBlue"), ("#808080","Gray"), ("#FF69B4","HotPink"),
("#FFFFE0","LightYellow"), ("#00FF7F","SpringGreen"), ("#000080","Navy"), ("#1E90FF","DodgerBlue"),
("#9ACD32","YellowGreen"), ("#008B8B","DarkCyan"), ("#808000","Olive"), ("#DEB887","BurlyWood"),
("#7B68EE","MediumSlateBlue"), ("#483D8B","DarkSlateBlue"), ("#98FB98","PaleGreen"), ("#FF1493","DeepPink"),
("#FF6347","Tomato"), ("#32CD32","LimeGreen"), ("#DDA0DD","Plum"), ("#7FFF00","Chartreuse"), ("#800080","Purple"),
("#00CED1","DarkTurquoise"), ("#8FBC8F","DarkSeaGreen"), ("#4682B4","SteelBlue"), ("#800000","Maroon"),
("#3CB371","MediumSeaGreen"), ("#FF4500","OrangeRed"), ("#BA55D3","MediumOrchid"), ("#2F4F4F","DarkSlateGray"),
("#CD853F","Peru"), ("#228B22","ForestGreen"), ("#48D1CC","MediumTurquoise"), ("#DC143C","Crimson"),
("#D3D3D3","LightGray"), ("#ADFF2F","GreenYellow"), ("#7FFFD4","Aquamarine"), ("#BC8F8F","RosyBrown"),
("#20B2AA","LightSeaGreen"), ("#C71585","MediumVioletRed"), ("#F0E68C","Khaki"), ("#6495ED","CornflowerBlue"),
("#556B2F","DarkOliveGreen"), ("#CD5C5C","IndianRed "), ("#2E8B57","SeaGreen"), ("#F08080","LightCoral"),
("#8A2BE2","BlueViolet"), ("#AFEEEE","PaleTurquoise"), ("#4169E1","RoyalBlue"), ("#0000CD","MediumBlue"),
("#B8860B","DarkGoldenRod"), ("#00BFFF","DeepSkyBlue"), ("#FFC0CB","Pink"), ("#4B0082","Indigo "), ("#A0522D","Sienna"),
("#FFD700","Gold"), ("#F4A460","SandyBrown"), ("#DAA520","GoldenRod"), ("#DA70D6","Orchid"), ("#E6E6FA","Lavender"),
("#5F9EA0","CadetBlue"), ("#D2691E","Chocolate"), ("#66CDAA","MediumAquaMarine"), ("#6B8E23","OliveDrab"),
("#A9A9A9","DarkGray"), ("#BDB76B","DarkKhaki"), ("#696969","DimGray"), ("#B0C4DE","LightSteelBlue"),
("#191970","MidnightBlue"), ("#FFE4C4","Bisque"), ("#6A5ACD","SlateBlue"), ("#EE82EE","Violet"),
("#8B4513","SaddleBrown"), ("#FF7F50","Coral"), ("#008000","MidGreen"), ("#DB7093","PaleVioletRed"), ("#C0C0C0","Silver"),
("#E0FFFF","LightCyan"), ("#9370DB","MediumPurple"), ("#FF8C00","DarkOrange"), ("#00FA9A","MediumSpringGreen"),
("#E9967A","DarkSalmon"), ("#778899","LightSlateGray"), ("#9932CC","DarkOrchid"), ("#EEE8AA","PaleGoldenRod"),
("#F8F8FF","GhostWhite"), ("#FFA07A","LightSalmon"), ("#ADD8E6","LightBlue"), ("#D8BFD8","Thistle"),
("#FFE4E1","MistyRose"), ("#FFDEAD","NavajoWhite"), ("#40E0D0","Turquoise"), ("#90EE90","LightGreen"),
("#B22222","FireBrick"), ("#008080","Teal"), ("#F0FFF0","HoneyDew"), ("#FFFACD","LemonChiffon"), ("#FFF5EE","SeaShell"),
("#F5F5DC","Beige"), ("#DCDCDC","Gainsboro"), ("#FA8072","Salmon"), ("#8B008B","DarkMagenta"), ("#FFB6C1","LightPink"),
("#708090","SlateGray"), ("#87CEFA","LightSkyBlue"), ("#FFEFD5","PapayaWhip"), ("#D2B48C","Tan"), ("#FFFFF0","Ivory"),
("#F0FFFF","Azure"), ("#F5DEB3","Wheat"), ("#00008B","DarkBlue"), ("#FFDAB9","PeachPuff"), ("#8B0000","DarkRed"),
("#FAF0E6","Linen"), ("#B0E0E6","PowderBlue"), ("#FFE4B5","Moccasin"), ("#F5F5F5","WhiteSmoke"), ("#FFF8DC","Cornsilk"),
("#FFFAFA","Snow"), ("#FFF0F5","LavenderBlush"), ("#FFEBCD","BlanchedAlmond"), ("#F0F8FF","AliceBlue"),
("#FAEBD7","AntiqueWhite"), ("#FDF5E6","OldLace"), ("#FAFAD2","LightGoldenRodYellow"), ("#F5FFFA","MintCream"),
("#FFFAF0","FloralWhite"), ("#7CFC00","LawnGreen"), ("#663399","RebeccaPurple"))
codes = {}
for i, pair in enumerate(hex_name):
codes[pair[0]] = i
low = pair[1].lower()
codes[low] = i
codes[low.replace("gray","grey")] = i
codes["green"] = 2
codes["fuchsia"] = 4
codes["aqua"] = 6
@staticmethod
def to_code(colortext):
try:
return int(colortext)
except ValueError:
return ColorMap.codes.get(colortext.lower().replace("_",""),-1)
@staticmethod
def check_hex(code):
if not code.startswith("#"):
return None
if len(code) == 4: # convert short code to long code
code = code[0:2] + code[1:3] + code[2:4] + code[3]
if len(code) != 7:
return None
try:
int(code[1:]) # we have a valid hex color code
return code
except ValueError:
return None
@staticmethod
def to_hex(code):
try:
return ColorMap.hex_name[code][0]
except IndexError:
return -1
class VarsScanner(Scanner):
colors = {"red":0}
def __init__(self, directory, test_day=None):
Scanner.__init__(self, directory, test_day=test_day)
logging.info('vars dir %s', directory)
def next(self, t, value, result):
logging.info('vars %s', value)
for var in value.strip().split(" "):
vars = var.split("|")
if len(vars) == 1:
vars.append("")
if len(vars) == 2:
vars.append(vars[0])
if len(vars) == 3:
vars.append("")
name, unit, label, color = vars
if not unit in result:
result[unit] = dict(tag = unit, unit = unit.split("_")[0], curves=[])
result[unit]["curves"].append(dict(name=name, label=label, color=color))
def get_message(self, time):
# get last value only
_, result = self.scan("vars", [time, time], {})
for unit, curvegroup in result.items():
color_set = set()
auto_curves = []
for curve in curvegroup["curves"]:
col = curve["color"].strip()
c = ColorMap.to_code(col)
if c < 0:
valid = ColorMap.check_hex(col)
if valid:
curve["original_color"] = col
curve["color"] = valid
else:
auto_curves.append(curve)
curve["original_color"] = col + "?"
else:
color_set.add(c)
curve["original_color"] = col
curve["color"] = ColorMap.to_hex(c)
c = 1 # omit white
for curve in auto_curves:
while c in color_set: c += 1 # find unused color
curve["color"] = ColorMap.to_hex(c)
c += 1
return result