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(, result) 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