Exemple #1
0
 def find_first_timeout(self):
     nextTimeOut = dates.date2secs(
         self.objectNode.get_child("nextTimeout").get_value())
     now = dates.date2secs(dates.now_iso())
     period = self.objectNode.get_child("periodSeconds").get_value()
     while nextTimeOut < now:
         nextTimeOut = nextTimeOut + period
     self.objectNode.get_child("nextTimeout").set_value(
         dates.epochToIsoString(nextTimeOut))
     status = self.objectNode.get_child("isRunning").set_value(False)
Exemple #2
0
def create(functionNode):
    #hide(functionNode) # if any is already visible, hide it

    model = functionNode.get_model()
    logger = functionNode.get_logger()
    logger.debug("create_envelope()")
    motif = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
        "motif").get_target()
    widget = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
        "widget").get_target()
    name = motif.get_child("variable").get_target().get_name()
    defaults = functionNode.get_parent().get_child(
        "defaultParameters").get_value()

    if motif and widget:
        try:
            model.disable_observers()
            envelope = motif.create_child("envelope", type="folder")
            envelope.create_child(name + "_limitMax", type="timeseries")
            envelope.create_child(name + "_limitMin", type="timeseries")
            envelope.create_child(name + "_expected", type="timeseries")

            #adapt the sampling selection to the selected widget:
            # we take the average sampling period *2 as the default and the 10*average sampling period as max
            start = dates.date2secs(motif.get_child("startTime").get_value())
            end = dates.date2secs(motif.get_child("endTime").get_value())
            ts = motif.get_child("variable").get_target().get_time_series(
                start, end)
            avr = (end - start) / len(ts["values"])
            logger.debug(f"sampling period in motif: {avr}")
            defaults["samplingPeriod"] = [avr, 10 * avr, 2 * avr]

            for k, v in defaults.items(
            ):  #["samplingPeriod","filter","freedom","dynamicFreedom"]:
                envelope.create_child(
                    k,
                    type="const",
                    value=v[2],
                    properties={"validation": {
                        "limits": [v[0], v[1]]
                    }})
        finally:
            model.enable_observers()
            if not _connect(motif, widget):
                logger.error("can't connect motif envelope to widget")
        #also

    #override the value with better values
    #e.g the sampling period we take the average that we have and make 10 steps to the 10 times sampling rate

    #call update from here
    updateNode = functionNode.get_parent().get_child("update")
    update(updateNode)

    return True
Exemple #3
0
def is_inside(annotation, annotations):
    """ check if annotation is completely inside one of the annotations"""
    start = dates.date2secs(annotation.get_child("startTime").get_value())
    end = dates.date2secs(annotation.get_child("endTime").get_value())

    for anno in annotations:
        thisStart = dates.date2secs(anno.get_child("startTime").get_value())
        thisEnd = dates.date2secs(anno.get_child("endTime").get_value())
        if thisStart <= start and thisEnd >= end:
            return True
    return False
Exemple #4
0
def alarm_clock(functionNode):
    # this function never ends until we get the signal "stop"
    # it works on an own thread
    signalNode = functionNode.get_child("control").get_child("signal")
    logger = functionNode.get_logger()

    functionNode.get_child("isRunning").set_value(True)
    while True:
        if signalNode.get_value() == "stop":
            signalNode.set_value("nosignal")
            break

        timeout = dates.date2secs(
            functionNode.get_child("nextTimeout").get_value())
        now = time.time()
        if now > timeout:
            #set the next timeout
            period = functionNode.get_child("periodSeconds").get_value()
            timeout = timeout + period
            functionNode.get_child("nextTimeout").set_value(
                dates.epochToIsoString(timeout))

            for node in functionNode.get_child("target").get_leaves():
                logger.debug(f"alarm_clock executes", node.get_name())
                node.execute()

            counterNode = functionNode.get_child("executionCounter")
            counterNode.set_value(counterNode.get_value() + 1)

        time.sleep(
            5)  #only sleep short to be able to break the loop with the signal

    functionNode.get_child("isRunning").set_value(False)
    return True
Exemple #5
0
    def thread_func(self):
        while self.running:
            status = self.objectNode.get_child("isRunning").get_value()
            if not status:
                self.objectNode.get_child("isRunning").set_value(True)
            timeout = dates.date2secs(
                self.objectNode.get_child("nextTimeout").get_value())
            now = time.time()
            if now > timeout:
                # set the next timeout
                period = self.objectNode.get_child("periodSeconds").get_value()
                timeout = timeout + period
                self.objectNode.get_child("nextTimeout").set_value(
                    dates.epochToIsoString(timeout))

                for node in self.objectNode.get_child("target").get_leaves():
                    self.logger.debug(f"AutoTimerClass executes",
                                      node.get_name())
                    node.execute()

                counterNode = self.objectNode.get_child("executionCounter")
                counterNode.set_value(counterNode.get_value() + 1)

            time.sleep(
                5
            )  # only sleep short to be able to break the loop with the signal
        self.objectNode.get_child("isRunning").set_value(False)
Exemple #6
0
def update(functionNode,startTime=0):
    if functionNode.get_name()!="update":
        functionNode = functionNode.get_parent().get_child("update")
    motif = functionNode.get_parent().get_child("StumpyMASS").get_child("motif").get_target()
    widget = functionNode.get_parent().get_child("StumpyMASS").get_child("widget").get_target()
    logger = functionNode.get_logger()
    start = dates.date2secs(motif.get_child("startTime").get_value())
    end = dates.date2secs(motif.get_child("endTime").get_value())
    times = numpy.arange(start, end)
    ts = motif.get_child("variable").get_target().get_time_series(start,end,resampleTimes = times)
    data = ts["values"]
    if startTime!=0:
        diff = startTime-start
        times=times+diff
        #value offset
        ts =  motif.get_child("variable").get_target().get_time_series(resampleTimes = times)
        dataDiff = ts["values"][0]-data[0]
        data = data +dataDiff
    return True
Exemple #7
0
def get_events():

    evs = ["busy_start", "busy_end", "free_start", "free_end"]
    times = [
        "2015-02-12T09:16:00+01:00", "2015-02-12T15:46:00+01:00",
        "2015-02-12T19:40:00+01:00", "2015-02-13T05:03:00+01:00"
    ]

    return evs, numpy.asarray([dates.date2secs(tim) for tim in times],
                              dtype=numpy.float64)
Exemple #8
0
def jump(functionNode):
    widget = functionNode.get_parent().get_child("StumpyMASS").get_child("widget").get_target()
    widgetStartTime = dates.date2secs(widget.get_child("startTime").get_value())
    widgetEndTime = dates.date2secs(widget.get_child("endTime").get_value())
    #now get the user selection, it will be the index of the results list
    matchIndex=int(functionNode.get_child("match").get_value())
    if matchIndex==-1:
        motif = functionNode.get_parent().get_child("StumpyMASS").get_child("motif").get_target()
        match = {}
        match["epochStart"] = dates.date2secs(motif.get_child("startTime").get_value())
        match["epochEnd"] = dates.date2secs(motif.get_child("endTime").get_value())
    else:
        results = functionNode.get_parent().get_child("StumpyMASS").get_child("results").get_value()
        match = results[matchIndex]
    middle = match["epochStart"]+(match["epochEnd"]-match["epochStart"])/2
    newStart = middle - (widgetEndTime-widgetStartTime)/2
    newEnd = middle + (widgetEndTime - widgetStartTime) / 2
    widget.get_child("startTime").set_value(dates.epochToIsoString(newStart))
    widget.get_child("endTime").set_value(dates.epochToIsoString(newEnd))
    return True
Exemple #9
0
def jump(functionNode):
    widget = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
        "widget").get_target()
    widgetStartTime = dates.date2secs(
        widget.get_child("startTime").get_value())
    widgetEndTime = dates.date2secs(widget.get_child("endTime").get_value())

    #now get the user selection, it will be the index of the results list
    matchIndex = int(functionNode.get_child("match").get_value())

    if matchIndex == -1:
        motif = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
            "motif").get_target()
        match = {}
        match["epochStart"] = dates.date2secs(
            motif.get_child("startTime").get_value())
        match["epochEnd"] = dates.date2secs(
            motif.get_child("endTime").get_value())
        #update(functionNode) # re-write the band: ! the match will not 100% align in time with the motif: this is because we are searching for motif only with a given step

    else:
        results = functionNode.get_parent().get_child(
            "EnvelopeMiner").get_child("results").get_value()
        match = results[matchIndex]

        #update(functionNode,startTime=match["epochStart"])

    middle = match["epochStart"] + (match["epochEnd"] -
                                    match["epochStart"]) / 2
    newStart = middle - (widgetEndTime - widgetStartTime) / 2
    newEnd = middle + (widgetEndTime - widgetStartTime) / 2
    widget.get_child("startTime").set_value(dates.epochToIsoString(newStart))
    widget.get_child("endTime").set_value(dates.epochToIsoString(newEnd))

    if matchIndex == -1:
        update(functionNode)
    else:
        update(functionNode,
               startTime=match["epochStart"],
               offset=match["offset"])
    return True
Exemple #10
0
def data_cleaning(annotations, order=None, logger=None):
    #condition: we need to have the right order of annotations
    if type(order) is type(None):
        order = ["Step" + str(no) for no in range(19)]  #Step1,Step2,...Step18

    #now take all annotations and order them in time
    annoStartTimes = []
    annos = []
    for anno in annotations:
        if anno.get_child("type").get_value() != "time":
            continue
        #filter out only the ones we want
        if set(order).intersection(set(anno.get_child("tags").get_value())):
            startTime = date2secs(anno.get_child("startTime").get_value())
            annoStartTimes.append(startTime)
            annos.append(anno)
    #now sort them
    sortedIndices = numpy.argsort(annoStartTimes)
    sortedAnnos = list(
        numpy.asarray(annos)[sortedIndices])  # use numpy for fancy indexing

    #now check the order
    invalidTimes = {
        "startTime": [],
        "endTime": []
    }  #list of {"startTime":333,"endTime":999}

    validAnnos = []
    nextAnnoIndex = 0
    currentProcess = []
    for anno in sortedAnnos:
        thisTags = anno.get_child("tags").get_value()
        if not order[nextAnnoIndex] in thisTags:
            #the right order was not kept, reset the search
            if logger:
                logger.debug(
                    f'broken process @ {anno.get_child("startTime").get_value()}'
                )
            currentProcess = []
            nextAnnoIndex = 0

        if order[nextAnnoIndex] in thisTags:
            currentProcess.append(anno)
            nextAnnoIndex = nextAnnoIndex + 1
            if nextAnnoIndex == len(order):
                validAnnos.append(currentProcess)
                currentProcess = []
                nextAnnoIndex = 0
    logger.debug(
        f"Found {len(validAnnos)} with valid Processes with each {len(order)} subprocesses"
    )
    return validAnnos
Exemple #11
0
def check_excalation(functionNode):
    """
        check if we need to send any mail, also create the assessment result
    """
    logger = functionNode.get_logger()
    model = functionNode.get_model()
    logger.info(f">>>> in check_excalation {functionNode.get_browse_path()}")
    sendMailFunction = functionNode.get_child("sendMailFunction").get_target()
    messages = functionNode.get_child("messages").get_leaves()
    assessmentValues = functionNode.get_child("assessmentValues").get_value() #{"default":"green","continue":"yellow","critical":"red","unconfirmed":"red"}
    lookBack = functionNode.get_child("assessmentLookback").get_value()
    levels = {k:idx for idx,k in enumerate(assessmentValues)} # give confirm value get level
    output = {idx:assessmentValues[k] for idx,k in enumerate(assessmentValues)}  #give level get output string
    assessment = 0 # take the least important as start point
    now = time.time()
    for msg in messages:
        mustSend= msg.get_child("mustEscalate")
        if mustSend and mustSend.get_value()== True:
            #prepare a mail from the message
            body = {child.get_name():child.get_value() for child in msg.get_children()}
            logger.debug(f"email body {body}")
            sendMailFunction.get_child("body").set_value(json.dumps(body,indent=4))
            try:
                model.disable_observers()
                mustSend.set_value(False) # flag this message as done
            finally:
                model.enable_observers()
            #now send the email and wait for ready
            try:
                sendMailFunction.execute_synchronous()
            except:
                model.log_error() # must grab the error here, otherwise the whole function breaks
        alarmTime = dates.date2secs(msg.get_child("startTime").get_value())
        if alarmTime>now-lookBack:
            #must consider this alarm
            msgConfirmedString = msg.get_child("confirmed").get_value()
            if msgConfirmedString in levels:
                level = levels[msgConfirmedString]
            else:
                level = 0 # ignore unknown
            if level>assessment:
                assessment = level
    functionNode.get_child("assessment").set_value(output[assessment])
    return True
Exemple #12
0
    def __convert_to_ids__(self,blob):
        """
            convert incoming descriptors to ids
            convert times to epoch
            support the __events name for a default eventnode
        """
        newBlob = {}
        for k, v in blob.items():
            if k == "__time":
                if type(v) is not list:
                    v = [v]
                if type(v[0]) is str:
                    v = [dates.date2secs(t) for t in v] # convert to epoch
                newBlob[k] = numpy.asarray(v)
            else:
                # try to convert
                if k in self.varNameLookup:
                    id = self.varNameLookup[k].get_id()
                    if type(v) is not list:
                        v=[v]
                    newBlob[id]=numpy.asarray(v)
                else:

                    if self.autoCreate:
                        self.logger.warning(f"__convert_to_ids__: cant find {k}.. autocreate it")
                        startPath = self.functionNode.get_child("autoCreateFolder").get_target().get_browse_path()
                        path = startPath+"."+k
                        id = self.model.create_node_from_path(path,properties={"type":"timeseries"})
                        newNode = self.model.get_node(id)
                        self.varNameLookup.update({id:newNode,newNode.get_browse_path():newNode,k:newNode})
                        newBlob[id]=numpy.asarray(v)
                    else:
                        self.logger.warning(f"__convert_to_ids__: cant find {k}, ignore!")


        return newBlob
Exemple #13
0
 def __convert_to_ids__(self, blob):
     """
         convert incoming descriptors to ids
         convert times to epoch
         support the __events name for a default eventnode
     """
     newBlob = {}
     for k, v in blob.items():
         if k == "__time":
             if type(v) is not list:
                 v = [v]
             if type(v[0]) is str:
                 v = [dates.date2secs(t) for t in v]  # convert to epoch
             newBlob[k] = numpy.asarray(v)
         else:
             # try to convert
             if k in self.varNameLookup:
                 id = self.varNameLookup[k].get_id()
                 if type(v) is not list:
                     v = [v]
                 newBlob[id] = numpy.asarray(v)
             else:
                 self.logger.error(f"__convert_to_ids__: cant find {k}")
     return newBlob
Exemple #14
0
def events_to_annotations(functionNode):
    """
        this function creates annotations from events

    """
    logger = functionNode.get_logger()
    logger.info("==>>>> events_to_annotations "+functionNode.get_browse_path())

    progressNode = functionNode.get_child("control").get_child("progress")
    newAnnosNode = functionNode.get_child("annotations")
    differential = functionNode.get_child("differential").get_value()
    valueNotifyNodeIds = []  #this variable will hold node id to post a value notification (the nodes that were open before)

    interleaved = functionNode.get_child("interleaved")
    if interleaved:
        #if the node is there
        interleaved = interleaved.get_value()

    if differential:
        processedUntil = dates.date2secs(functionNode.get_child("processedUntil").get_value(),ignoreError=False)
        if not processedUntil:
            processedUntil = 0
    else:
        processedUntil = 0
    lastTimeSeen = processedUntil # initialize, this variable with find the latest time entry

    # eventSelection is a dict to translate and select the events from the event series
    # eg {"machine1.init":"Preparation", "machine1.op2":"Printing"}
    # we will select the "machine1.init and the "macine1.op2" events and name the annotation type as Preparation, Printing
    eventSelection = functionNode.get_child("eventSelection").get_value()
    progressNode.set_value(0)
    m = functionNode.get_model()
    startEvents = {v[0]:k for k,v in eventSelection.items()} # always the first event
    endEvents = {v[1]:k for k, v in eventSelection.items()}  # always the second event

    evs = functionNode.get_child("eventSeries").get_targets() # a list of nodes where the events are located

    if not differential:
        #delete all old annos
        annos = newAnnosNode.get_children()
        if annos:
            try:
                m.disable_observers()
                for anno in annos:
                    anno.delete()
            finally:
                m.enable_observers()
            m.notify_observers(newAnnosNode.get_id(), "children")
        openAnnos = [] # no open annotatations per definition

    else:
        #differential process: we don't delete the old but get all open annotations
        openAnnos = []
        annos = newAnnosNode.get_children()
        for anno in annos:
            if "open" in anno.get_child("tags").get_value():
                openAnnotation = {
                    "tags": anno.get_child("tags").get_value(),   #using "tag" to note that an open annotation has only one tag
                    "startTime": anno.get_child("startTime").get_value(),
                    "node": anno
                }
                openAnnos.append(openAnnotation)


    #now collect all events filtered by the selection of events and the time (if differential process)
    #we build up a list of dicts with ["event"."hello", "startTime":1234494., "endTime":2345346.:
    filter = list(startEvents.keys())+list(endEvents.keys())


    #from now on we use these lists
    newAnnotations = []     # the annotations to be created new, a list of dicts, it will also contain "updates" for existing nodes
                            # those update entries will be found by the key "node" which holds the node that has to be updated
    #openAnnos : a list of # {"key":"litu":{"startTime":132412334,"node"} # will also hold the node if we have one already

    #put the open annotations in a fast look up table
    if len(openAnnos) > 1 and not interleaved:
        logger.error("there should be only one open annotation at max in non-interleaved mode")


    #now iterate over all events
    for ev in evs:
        data = ev.get_event_series(eventFilter = filter) #["__time":... "eventStrings"....]
        times = data["__time"]
        eventStrings = data["eventStrings"]
        if differential:
            #also filter by time
            new = times > processedUntil
            times = times[new]
            indexableEventStrings = numpy.asarray(eventStrings,dtype=numpy.str)
            eventStrings = indexableEventStrings[new]

        for index in range(len(times)):
            evStr = eventStrings[index]
            tim = times[index]
            if tim>lastTimeSeen:
                lastTimeSeen = tim
            print(f"ev:{evStr}, tag:{tag}, open Annos {openAnnos}")
            if evStr in startEvents:
                tag = startEvents[evStr]
                #this is a start of a new event
                print("is start")
                if openAnnos:
                    # we have at least on e annotation running already:
                    # in non-interleaved wo submitt all open annotations as anomaly
                    # in interleaved mode we only submit those with the same event as anomaly
                    # if the "open" entry was from an existing annotation in the tree, we also put that
                    # node handle to later use if for updating the annotation and not creating it new

                    newOpenAnnos = []
                    for openAnnotation in openAnnos:
                        if not interleaved or tag in openAnnotation["tags"]:
                            # we must close this open annotation as anomaly
                            # build the anomaly annotations
                            anno = {
                                "type": "time",
                                "endTime": dates.epochToIsoString(tim, zone='Europe/Berlin'),
                                "startTime": openAnnotation["startTime"],
                                "tags": ["anomaly",tag]
                            }
                            if "node" in openAnnotation:
                                anno["node"] = openAnnotation["node"]
                            newAnnotations.append(anno) # put the anomaly node there
                        else:
                            #keep this annotation
                            newOpenAnnos.append(openAnnotation)
                    openAnnos = newOpenAnnos

                #now remember the current as the open one
                openAnnotation = {
                    "startTime":dates.epochToIsoString(tim, zone='Europe/Berlin'),
                    "tags":[tag]
                }
                openAnnos.append(openAnnotation)

            #not an else if, because it can be in both start and end events
            if evStr in endEvents:
                print("is end")
                #this is an end event, see if we have a matching open annotation
                tag = endEvents[evStr]
                newOpenAnnos = []
                for openAnnotation in openAnnos:
                    if tag in openAnnotation["tags"]:
                        #take this annotation, we can close it
                        anno  = {
                            "type": "time",
                            "endTime": dates.epochToIsoString(tim, zone='Europe/Berlin'),
                            "startTime": openAnnotation["startTime"],
                            "tags": [tag]
                        }
                        if "node" in openAnnotation:
                            #  if it was an existing annotation, we also put the anno handle to make an "update"
                            #  instead of a new creation further down
                            anno["node"]=openAnnotation["node"]
                        newAnnotations.append(anno)
                    else:
                        newOpenAnnos.append(openAnnotation)#keep this one
                if len(newOpenAnnos) == len(openAnnos):
                    logger.warning(f"annotation creation ende without start {tim} {evStr}")
                openAnnos = newOpenAnnos
            #print(f"open annotations {openAnnos} new annos {newAnnotations}")



    #now create the annotations
    logger.debug(f"creating {len(newAnnotations)} annotation, have {openAnnos} open annotations")
    #print(f"creating {len(newAnnotations)} annotation, have {len(openAnnos)} open annotations")
    m.disable_observers()
    try:

        if differential:
            # in the open annotations list, we will find
            # - open annotations that have existed before and have not found an update yet and were not closed
            # - new open annotations that have started now
            for openAnnotation in openAnnos:
                nowIso = dates.epochToIsoString(time.time(),zone='Europe/Berlin')
                if "node" in openAnnotation:
                    # this existed before, we just update the endtime
                    node = openAnnotation["node"]
                    node.get_child("endTime").set_value(nowIso) # set the current time as end time
                    valueNotifyNodeIds.append(node.get_child("endTime").get_id())
                else:
                    #this is a new open anno:
                    entry = {
                        "type": "time",
                        "endTime": nowIso,
                        "startTime": openAnnotation["startTime"],
                        "tags": openAnnotation["tags"]+["open"]
                    }
                    newAnnotations.append(entry) # put it to the creation list

        #create and update new annotations
        for anno in newAnnotations:
            if not "node" in anno:
                newAnno = newAnnosNode.create_child(type="annotation")
                for k, v in anno.items():
                    newAnno.create_child(properties={"name": k, "value": v, "type": "const"})
            else:
                #this is an update, typically a "close" or an extension of an open annotation
                node = anno["node"]
                node.get_child("endTime").set_value(anno["endTime"])
                node.get_child("tags").set_value(anno["tags"])
                valueNotifyNodeIds.append(node.get_child("endTime").get_id())

    except Exception as ex:
        logger.error(f"error in events_to_annotations {ex}")

    m.enable_observers()
    m.notify_observers(newAnnosNode.get_id(), "children")
    #also notify the value changes of annotations that existed before
    if valueNotifyNodeIds:
        m.notify_observers(valueNotifyNodeIds,"value")# also notify the value change for the UI to adjust the annotation

    #if time.time()>lastTimeSeen:
    #    lastTimeSeen=time.time()

    isoDate = dates.epochToIsoString(lastTimeSeen,zone="Europe/Berlin")
    functionNode.get_child("processedUntil").set_value(isoDate) # this is for me, next time I can check what has been processed

    return True
Exemple #15
0
def update(functionNode, startTime=0, offset=None):

    if functionNode.get_name() != "update":
        functionNode = functionNode.get_parent().get_child("update")

    motif = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
        "motif").get_target()
    widget = functionNode.get_parent().get_child("EnvelopeMiner").get_child(
        "widget").get_target()
    logger = functionNode.get_logger()
    #fil = motif.get_child("envelopeFilter")
    #sample = motif.get_child("envelopeSamplingPeriod")

    lMax = None
    lMin = None
    exPe = None
    #children = motif.get_get_children()
    for child in motif.get_child("envelope").get_children():
        if "_limitMax" in child.get_name():
            lMax = child
        elif "_limitMin" in child.get_name():
            lMin = child
        elif "_expected" in child.get_name():
            exPe = child

    #now get the data and write the new envelope

    start = dates.date2secs(motif.get_child("startTime").get_value())
    end = dates.date2secs(motif.get_child("endTime").get_value())

    period = motif.get_child("envelope.samplingPeriod").get_value()
    times = numpy.arange(start, end, period)

    ts = motif.get_child("variable").get_target().get_time_series(
        start, end, resampleTimes=times)
    data = ts["values"]

    #offset the times, values if necessary
    if startTime != 0:
        diff = startTime - start
        times = times + diff
        #value offset
        ts = motif.get_child("variable").get_target().get_time_series(
            resampleTimes=times)
        if type(offset) is type(None):
            dataDiff = ts["values"][0] - data[0]
        else:
            dataDiff = offset
        data = data + dataDiff

    freedom = motif.get_child("envelope.freedom").get_value()
    dynFreedom = motif.get_child("envelope.dynamicFreedom").get_value()

    diff = max(data) - min(data)
    upper = data + diff * freedom * factorY  # add x% of the diff to the upper
    lower = data - diff * freedom * factorY
    expect = data

    #now also shift left and right to make it equally "wide" as "high"

    if 1:
        shift = int(float(len(times)) * freedom * factorX)
        if shift == 0:
            shift = 1  #at least 1
        fillMax = numpy.min(upper)
        fillMin = numpy.max(lower)

        totalShift = shift
        logger.debug(
            f"envelope shift size is {shift}, freedom is {freedom} totallen is{len(times)} shiftsize/len ={float(shift)/float(len(times))}"
        )
        autoStepSize = float(shift) / float(len(times)) / 2
        #then our step size is half the width to make sure we get the original motif

        newupper = numpy.copy(upper)
        newlower = numpy.copy(lower)
        steps = set(list(numpy.linspace(1, totalShift, 8, dtype=int)))
        for shift in steps:
            right = numpy.append(upper[shift:], [fillMax] * shift)
            left = numpy.append([fillMax] * shift, upper[:-shift])
            newupper = numpy.max([left, newupper, right], axis=0)

            right = numpy.append(lower[shift:], [fillMin] * shift)
            left = numpy.append([fillMin] * shift, lower[:-shift])
            newlower = numpy.min([left, newlower, right], axis=0)

    upper = newupper
    lower = newlower

    #xxx todo dynamic freedom

    model = functionNode.get_model()  #get the model Api

    try:
        model.disable_observers()
        numberSamples = len(times)
        motif.get_child("envelope.numberSamples").set_value(
            numberSamples)  # the number of samples
        #also set the possible step size
        #step = motif.get_child("envelope.step").get_value()
        #if step > numberSamples:
        #    step = numberSamples
        #also set the limits
        #motif.get_child("envelope.step").set_properties({"value":step,"validation":{"limits":[1,numberSamples]}})

        if lMax:
            lMax.set_time_series(upper, times)
        if lMin:
            lMin.set_time_series(lower, times)
        if exPe:
            exPe.set_time_series(expect, times)
    finally:
        model.enable_observers()
        #model.notify_observers(motif.get_id(), "children")
        autoStepNode = functionNode.get_child("autoStepSize")
        if not autoStepNode or autoStepNode.get_value() == True:
            logger.debug(f"autostep size enabled, set value {autoStepSize}")
            motif.get_child("envelope.step").set_value(autoStepSize)
        model.notify_observers(lMax.get_id(), "value")

    return True
Exemple #16
0
def replay(functionNode):
    logger = functionNode.get_logger()
    logger.info("==>>>> replay " + functionNode.get_browse_path())
    progressNode = functionNode.get_child("control").get_child("progress")
    progressNode.set_value(0)
    functionNode.get_child("control.signal").set_value(None)

    widget = functionNode.get_child("widget").get_target()
    if widget:
        #try to get the current anno
        annotation = widget.get_child(
            "hasAnnotation.selectedAnnotations").get_target()
        if annotation:
            #remember the anno
            functionNode.get_child("annotation").add_references(annotation,
                                                                deleteAll=True)
    annotation = functionNode.get_child("annotation").get_target()
    if not annotation:
        logger.error("replay:no annotation/widget given")
        return False
    startTime = dates.date2secs(annotation.get_child("startTime").get_value())
    endTime = dates.date2secs(annotation.get_child("endTime").get_value())

    #now get the data and build a table
    varNodes = functionNode.get_child("input").get_leaves()
    nodesLookup = {node.get_id(): node for node in varNodes}

    #we build three array: timepoint, nodes in
    table = pd.DataFrame({"__time": []})

    latestTime = 0
    for var in varNodes:
        if var.get_type() == "timeseries":
            data = var.get_time_series(startTime, endTime)
            new = pd.DataFrame({
                "__time": data["__time"],
                var.get_id(): data["values"]
            })
            table = table.append(new, ignore_index=True)
            #now find the last time
            times = var.get_time_series()["__time"]  #all the data
            if len(times):
                latestTime = max(latestTime, times[-1])
        elif var.get_type() == "eventseries":
            data = var.get_event_series(startTime, endTime)
            new = pd.DataFrame({
                "__time": data["__time"],
                var.get_id(): data["eventstrings"]
            })
            table = table.append(new, ignore_index=True)
            #now find the last time
            times = var.get_event_series()["__time"]  #all the data
            if len(times):
                latestTime = max(latestTime, times[-1])

    #table.sort_values("__time", inplace=True)
    table = remove_dublicate_times(table)
    #print(table)

    #now we have the data sorted and dublicates removed in the pd frame, let's replay
    offset = latestTime - table["__time"].iloc[-1]
    #logger.debug(f"latest Time {dates.epochToIsoString(latestTime)} , offset {offset}")
    repeatoffset = table["__time"].iloc[-1] - table["__time"].iloc[0]
    while 1:
        offset = offset + repeatoffset
        logger.debug(
            f"latest Time {dates.epochToIsoString(latestTime)} , offset {offset}"
        )
        speed = functionNode.get_child("replaySpeed").get_value()
        url = functionNode.get_child("url").get_value()
        format = functionNode.get_child("format").get_value()
        params = functionNode.get_child("parameters").get_value()
        directApi = functionNode.get_child("directApi").get_value()
        if directApi:
            targetNode = functionNode.get_model().get_node(params)
        for index, row in table.iterrows():
            #build the data json
            #all timeseries

            if format == "21_execute":
                timeblob = {}
                eventblob = {}
                for k, v in row.items():
                    if k == "__time":
                        timeblob["__time"] = v + offset
                        eventblob["__time"] = v + offset
                    elif numpy.isfinite(v):
                        if nodesLookup[k].get_type() == "timeseries":
                            timeblob[k] = v
                        else:
                            eventblob[k] = v
                    else:
                        logger.warning("data not finite")

                body = {
                    "node": params,
                    "function": "feed",
                    "parameter": []
                }  #{"type": "timeseries", "data": blob}]}
                if len(timeblob) > 1:
                    body["parameter"].append({
                        "type": "timeseries",
                        "data": timeblob
                    })
                if len(eventblob) > 1:
                    body["parameter"].append({
                        "type": "eventseries",
                        "data": eventblob
                    })
            else:
                logger.warning("replay:unknown format")
                body = {}

            #try to send out
            if not directApi:
                try:
                    if body:
                        res = requests.post(url,
                                            data=json.dumps(body),
                                            timeout=5)
                        logger.debug("sent out replay packet")

                except Exception as ex:
                    logger.error(ex)
            else:
                #send this directly to the node in the tree
                targetNode.get_object().feed(body["parameter"])

            time.sleep(speed)

            if functionNode.get_child("control.signal").get_value() == "stop":
                break

        if functionNode.get_child("control.signal").get_value(
        ) == "stop" or not functionNode.get_child("loop").get_value():
            break
        else:
            logger.debug("loop replay")
    return True
Exemple #17
0
def rca2(functionNode):
    logger = functionNode.get_logger()
    logger.info("==>>>> in rca2 (root cause analysis " +
                functionNode.get_browse_path())
    progressNode = functionNode.get_child("control").get_child("progress")
    progressNode.set_value(0.1)
    m = functionNode.get_model()

    report = '<i>REPORT</i><br><div style="font-size:85%">'

    annotations = functionNode.get_child("annotations").get_leaves()
    #order = ["Step"+str(no) for no in range(1,19)]
    order = ["Phase" + str(no) for no in range(3, 28)]
    order = functionNode.get_child("annotationsOrder").get_value()
    annotations = data_cleaning(annotations, order=order,
                                logger=logger)  #Step1,Step2,...Step18
    report += (f"found {len(annotations)} valid processes <br>")

    #for now, flatten them out
    annotations = [
        subprocess for process in annotations for subprocess in process
    ]

    algo = functionNode.get_child("selectedAlgorithm").get_value()
    target = functionNode.get_child("selectedTarget").get_target()

    progressNode.set_value(0.3)
    #now we are building up the table by iterating all the children in "selection"
    entries = functionNode.get_child("selection").get_children()

    table = {"target": []}
    firstVariable = True

    for entry in entries:
        logger.debug(f"entry {entry.get_name()}")
        #each entry is a combination of variable, tags and feature
        vars = entry.get_child("selectedVariables").get_targets()
        tags = entry.get_child("selectedTags").get_value()
        features = entry.get_child("selectedFeatures").get_value()
        #for iterate over variables
        for var in vars:
            logger.debug(
                f"processing variable: {var.get_name()} with tags {tags} and features {features}"
            )
            #columnName = var.get_name()+str(tags)+m.getRandomId()
            for tag in tags:
                row = 0
                #table[columnName]=[]# make a column
                for idx, anno in enumerate(annotations):
                    if anno.get_child("type").get_value() != "time":
                        continue
                    if tag in anno.get_child("tags").get_value():
                        startTime = anno.get_child("startTime").get_value()
                        endTime = anno.get_child("endTime").get_value()
                        data = var.get_time_series(startTime,
                                                   endTime)["values"]
                        #we take only the values "inside" the annotation
                        if len(data) > 2:
                            data = data[1:-1]
                        #now create the features
                        for feature in features:
                            feat = calc_feature(data, feature)
                            columnName = var.get_name(
                            ) + "_" + tag + "_" + feature
                            if not columnName in table:
                                table[columnName] = []
                            table[columnName].append(feat)

                        targetValue = get_target(
                            target,
                            (date2secs(startTime) + date2secs(endTime)) / 2)
                        if targetValue:
                            if firstVariable:
                                #for the first variable we also write the target
                                table["target"].append(targetValue)
                            else:
                                #for all others we make sure we have the same target value for that case (sanity check)
                                if table["target"][row] != targetValue:
                                    logger.warning(
                                        f'problem target {table["target"][row]} !=> {targetValue}'
                                    )
                            row = row + 1
                        else:
                            logger.warning(
                                f"no corrrect target value for {startTime} - {endTime}"
                            )

                firstVariable = False
    #now we have the table, plot it
    import json
    #print(json.dumps(table,indent=2))
    progressNode.set_value(0.5)
    #try a model

    algo = functionNode.get_child("selectedAlgorithm").get_value()
    if algo == "lasso":
        reg = linear_model.LassoCV()
        report += " using lasso Regression with auto-hyperparams <br>"
    else:
        #default
        report += " using linear Regression <br>"
        reg = linear_model.LinearRegression()  #try rigde, lasso

    columnNames = []
    dataTable = []
    for k, v in table.items():
        if k == "target":
            continue
        dataTable.append(v)
        columnNames.append(k)

    dataTable = numpy.asarray(dataTable)
    x = dataTable.T
    y = table["target"]
    x_train, x_test, y_train, y_test = train_test_split(x, y)
    reg.fit(x_train, y_train)

    print(reg.coef_)
    y_hat = reg.predict(x_test)
    y_repeat = reg.predict(x_train)
    print(f"predict: {y_hat} vs real: {y_test}")

    #check over/underfitting
    r_train = r2_score(y_train, y_repeat)
    r_test = r2_score(y_test, y_hat)

    report += "R<sup>2</sup> train= %.4g, R<sup>2</sup> test = %.4g <br>" % (
        r_train, r_test)

    pearsons = []
    for col in x.T:
        pearsons.append(pearsonr(col, y)[0])

    #and finally the correlations between y and yhat
    y_pearson_train = pearsonr(y_train, y_repeat)[0]
    y_pearson_test = pearsonr(y_test, y_hat)[0]

    report += "pearsonCorr y/y_hat train:%.4g , test:%.4g <br>" % (
        y_pearson_train, y_pearson_test)

    report += "regression coefficients, pearsons correlations:<br>"
    for col, coef, pear in zip(columnNames, reg.coef_, pearsons):
        report += "&nbsp &nbsp %s:%.4g, &nbsp %.4g <br>" % (col, coef, pear)

    #write report
    progressNode.set_value(0.8)
    report += "<div>"  #close the style div
    functionNode.get_child("report").set_value(report)
    #make a plot
    hover1 = HoverTool(tooltips=[('x,y', '$x,$y')], mode='mouse')
    hover1.point_policy = 'snap_to_data'
    hover1.line_policy = "nearest"
    tools = [
        PanTool(),
        WheelZoomTool(),
        BoxZoomTool(),
        ResetTool(),
        SaveTool(), hover1
    ]
    title = "prediction results on " + functionNode.get_child(
        "selectedAlgorithm").get_value()
    fig = figure(title=title, tools=tools, plot_height=400, plot_width=500)
    fig.toolbar.logo = None

    curdoc().theme = Theme(json=themes.darkTheme)
    fig.xaxis.major_label_text_color = themes.darkTickColor
    fig.yaxis.major_label_text_color = themes.darkTickColor
    fig.xaxis.axis_label = target.get_name()
    fig.xaxis.axis_label_text_color = "white"
    fig.yaxis.axis_label = "predicted Values for " + target.get_name()
    fig.yaxis.axis_label_text_color = "white"
    fig.circle(y_train,
               y_repeat,
               size=4,
               line_color="white",
               fill_color="white",
               name="train",
               legend_label="train")
    fig.circle(y_test,
               y_hat,
               line_color="#d9b100",
               fill_color="#d9b100",
               size=4,
               name="test",
               legend_label="test")

    fileName = functionNode.get_child("outputFileName").get_value()
    filePath = os.path.join(myDir, './../web/customui/' + fileName)
    fig.legend.location = "top_left"
    output_file(filePath, mode="inline")
    save(fig)

    return True
Exemple #18
0
def rca(functionNode):
    logger = functionNode.get_logger()
    logger.info("==>>>> in rca (root cause analysis " +
                functionNode.get_browse_path())
    progressNode = functionNode.get_child("control").get_child("progress")
    progressNode.set_value(0.1)

    variables = functionNode.get_child("selectedVariables").get_leaves()
    tag = functionNode.get_child("selectedTags").get_value()  #only one tag
    annotations = functionNode.get_child("annotations").get_leaves()
    feature = functionNode.get_child("selectedFeatures").get_value()
    algo = functionNode.get_child("selectedAlgorithms").get_value()
    target = functionNode.get_child("selectedTarget").get_target()

    p = Progress(progressNode)
    p.set_divisor(len(annotations) / 0.5)
    p.set_offset(0.1)
    #now create the data as x-y

    results = {"x": [], "y": []}
    var = variables[0]
    #now iterate over all annotations of the matching type and create feature
    for idx, anno in enumerate(annotations):
        p.set_progress(idx)
        if (anno.get_child("type").get_value()
                == "time") and (tag in anno.get_child("tags").get_value()):
            startTime = anno.get_child("startTime").get_value()
            endTime = anno.get_child("endTime").get_value()
            data = var.get_time_series(startTime, endTime)
            #now create the feature
            feat = calc_feature(data["values"], feature)
            targetValue = get_target(
                target, (date2secs(startTime) + date2secs(endTime)) / 2)
            if feat and targetValue and numpy.isfinite(
                    feat) and numpy.isfinite(targetValue):
                results["x"].append(feat)
                results["y"].append(targetValue)
            else:
                logger.warning(
                    f"no result for {var.get_name} @ {startTime}, anno:{tag}, feat:{feat}, target: {target}"
                )

    #now we have all the x-y

    progressNode.set_value(0.7)
    fig = figure(title="x-y Correlation Plot " + var.get_name(),
                 tools=[PanTool(),
                        WheelZoomTool(),
                        ResetTool(),
                        SaveTool()],
                 plot_height=300,
                 x_axis_label=feature + "(" + var.get_name() + ") @ " + tag,
                 y_axis_label=target.get_name())
    fig.toolbar.logo = None
    curdoc().theme = Theme(json=themes.darkTheme)
    fig.xaxis.major_label_text_color = themes.darkTickColor
    fig.yaxis.major_label_text_color = themes.darkTickColor

    fig.scatter(x=results["x"],
                y=results["y"],
                size=5,
                fill_color="#d9b100",
                marker="o")
    fileName = functionNode.get_child("outputFileName").get_value()
    filePath = os.path.join(myDir, './../web/customui/' + fileName)
    progressNode.set_value(0.8)
    output_file(
        filePath, mode="inline"
    )  #inline: put the bokeh .js into this html, otherwise the default cdn will be taken, might cause CORS problems)
    save(fig)

    #print(results)

    return True
Exemple #19
0
def envelope_miner(functionNode):

    logger = functionNode.get_logger()
    signal = functionNode.get_child("control.signal")
    logger.info("==>>>> in envelope_miner " + functionNode.get_browse_path())
    progressNode = functionNode.get_child("control").get_child("progress")
    functionNode.get_child("results").set_value([])
    progressNode.set_value(0)
    signal.set_value(None)

    # also make sure we are in the right jump
    # when we have jumped to a different result, the envelope time series are set to different values
    # (the ones from the according match)
    # but now we want to use the envelope time series for the mining so we need to set it back to the
    # motif time with the chosen parameters, we do that with the help of the update function
    update(functionNode)

    motif = functionNode.get_child("motif").get_target()
    variable = motif.get_child("variable").get_target()
    holeSize = functionNode.get_child("holeSize").get_value()
    ts = variable.get_time_series()

    samplePeriod = motif.get_child("envelope.samplingPeriod").get_value()
    samplePointsPerWindow = motif.get_child(
        "envelope.numberSamples").get_value()

    stepSizePercent = motif.get_child(
        "envelope.step").get_value()  # in percent
    if stepSizePercent == 0:
        stepSize = 1  # choose one point for 0% settings
    else:
        stepSize = int(float(samplePointsPerWindow) * float(stepSizePercent))

    if stepSize < 1:
        stepSize = 1  # at least one point advances per step
    samplePointsPerWindow = motif.get_child(
        "envelope.numberSamples").get_value()
    windowMaker = streaming.Windowing(
        samplePeriod=samplePeriod,
        stepSize=stepSize,
        maxHoleSize=holeSize,
        samplePointsPerWindow=samplePointsPerWindow)
    numberOfWindows = (ts["__time"][-1] -
                       ts["__time"][0]) / samplePeriod / stepSize  #approx
    windowTime = samplePointsPerWindow * samplePeriod

    logger.debug(
        f"producing {numberOfWindows} windows, point per window ={samplePointsPerWindow}, stepsize {stepSizePercent*100}% => {stepSize} pt, sample Period {samplePeriod}"
    )
    windowMaker.insert(ts["__time"], ts["values"])

    #old

    #upper = motif.get_child("envelope."+variable.get_name()+"_limitMax").get_time_series()["values"]
    #lower = motif.get_child("envelope."+variable.get_name()+"_limitMin").get_time_series()["values"]
    #expected  = motif.get_child("envelope."+variable.get_name()+"_expected").get_time_series()["values"]

    # get the motif data with times
    motifStart = dates.date2secs(motif.get_child("startTime").get_value())
    motifEnd = dates.date2secs(motif.get_child("endTime").get_value())
    upper = motif.get_child("envelope." + variable.get_name() +
                            "_limitMax").get_time_series(motifStart, motifEnd)
    lower = motif.get_child("envelope." + variable.get_name() +
                            "_limitMin").get_time_series(motifStart, motifEnd)
    expected = motif.get_child("envelope." + variable.get_name() +
                               "_expected").get_time_series(
                                   motifStart, motifEnd)

    if functionNode.get_child("maxNumberOfMatches"):
        maxMatches = functionNode.get_child("maxNumberOfMatches").get_value()
    else:
        maxMatches = None

    matches = []
    i = 0
    last = 0
    for w in windowMaker.iterate():
        # now we have the window w =[t,v] which is of correct length and resampled, let's compare it
        # to the motif
        # first the offset
        offset = w[1][0] - expected["values"][0]
        x = w[1] - offset
        below = upper["values"] - x
        above = x - lower["values"]
        diff = numpy.sum(numpy.power(x - expected["values"], 2))

        if numpy.all(below > 0) and numpy.all(above > 0):
            logger.debug(
                f"match @ {w[1][0]}, iteration: {float(i)/float(numberOfWindows)}"
            )
            matches.append({
                "startTime":
                dates.epochToIsoString(w[0][0], 'Europe/Berlin'),
                "endTime":
                dates.epochToIsoString(w[0][0] + windowTime, 'Europe/Berlin'),
                "match":
                diff,
                "epochStart":
                w[0][0],
                "epochEnd":
                w[0][0] + windowTime,
                "offset":
                offset,
                "format":
                my_date_format(w[0][0]) + "&nbsp&nbsp(match=%2.3f)" % diff
                #"below":list(numpy.copy(below)),
                #"above":list(numpy.copy(above)),
                #"x":list(numpy.copy(x)),
                #"w":list(numpy.copy(w[1])),
                #"upper":list(numpy.copy(upper["values"])),
                #"lower":list(numpy.copy(lower["values"])),
                #"expected":list(numpy.copy(expected["values"]))
            })
            if maxMatches and len(matches) == maxMatches:
                break

        i = i + 1
        progress = round(float(i) / numberOfWindows *
                         20)  #only 5% units on the progress bar
        if progress != last:
            progressNode.set_value(float(i) / numberOfWindows)
            last = progress
        if signal.get_value() == "stop":
            break

    #remove trivial matches inside half of the window len, we just take the best inside that area
    cleanMatches = []
    tNow = 0

    for m in matches:
        dif = m["epochStart"] - tNow
        if dif < (windowTime / 2):
            #check if this is a better match
            if m["match"] < cleanMatches[-1]["match"]:
                #exchange it to the better match
                cleanMatches[-1] = m
            continue
        else:
            cleanMatches.append(m)
            tNow = m["epochStart"]

    #now sort the matches by match value and rescale them
    if cleanMatches == []:
        functionNode.get_child("results").set_value([])
    else:
        matchlist = numpy.asarray([m["match"] for m in cleanMatches])
        scaleMax = numpy.max(matchlist)
        scaleMin = numpy.min(matchlist)
        matchlist = (matchlist - scaleMin) / (scaleMax - scaleMin) * 100
        sortIndices = numpy.argsort([m["match"] for m in cleanMatches])
        sortMatches = []
        for idx in sortIndices:
            m = cleanMatches[idx]
            m["format"] = my_date_format(
                m["epochStart"]) + "&nbsp&nbsp(distance=%.3g)" % matchlist[idx]
            sortMatches.append(cleanMatches[idx])

        #now create the annotations and notify them in one event
        if functionNode.get_child(
                "createAnnotations") and functionNode.get_child(
                    "createAnnotations").get_value():
            myModel = functionNode.get_model()
            myModel.disable_observers()
            annoFolder = functionNode.get_child("annotations")
            if maxMatches:
                _create_annos_from_matches(annoFolder,
                                           sortMatches,
                                           maxMatches=maxMatches)
            myModel.enable_observers()
            if maxMatches != 0:
                myModel.notify_observers(annoFolder.get_id(), "children")

            if maxMatches != 0:
                display_matches(functionNode, True)  # turn on the annotations

        functionNode.get_child("results").set_value(sortMatches)

    progressNode.set_value(1)
    return True
Exemple #20
0
def varstatistics(functionNode):
    logger = functionNode.get_logger()
    logger.info("==>>>> statistics " + functionNode.get_browse_path())
    progressNode = functionNode.get_child("control").get_child("progress")
    progressNode.set_value(0)
    #functionNode.get_child("control.signal").set_value(None)

    vars = functionNode.get_child("variable").get_targets()
    widget = functionNode.get_child("widget").get_target()
    bins = functionNode.get_child("bins").get_value()
    tags = functionNode.get_child("annotations").get_value()
    startTime = date2secs(widget.get_child("startTime").get_value())
    endTime = date2secs(widget.get_child("endTime").get_value())

    vars = {var.get_id(): {"node": var} for var in vars}

    #first 30% progress:
    prog = Progress(progressNode)
    progressNode.set_value(0.1)
    prog.set_offset(0.1)
    #prog.set_divisor()

    if tags:
        allAnnoNodes = widget.get_child(
            "hasAnnotation.annotations").get_leaves()
        allAnnos = []
        prog.set_divisor(len(allAnnoNodes) / 0.2)
        for index, node in enumerate(allAnnoNodes):
            prog.set_progress(index)
            if node.get_child("type").get_value() == "time":
                thisTags = node.get_child("tags").get_value()
                if any(tag in tags for tag in thisTags):
                    anno = {}
                    for child in node.get_children():
                        anno[child.get_name()] = child.get_value()
                    if date2secs(anno["startTime"]) >= startTime and date2secs(
                            anno["endTime"]
                    ) <= endTime:  #take this anno only if it is inside the current start/end time
                        allAnnos.append(anno)
        if allAnnos == []:
            give_up(functionNode, "no matching annotations in selected time")
            return False
    else:
        allAnnos = []

    progressNode.set_value(0.3)

    logger.debug(f"statistics annotations to look at: {len(allAnnos)}")
    prog.set_offset(0.3)
    totalAnnos = max(len(allAnnos), 1)
    totalCount = len(vars) * totalAnnos

    prog.set_divisor(totalCount / 0.3)
    totalValids = 0
    for varIndex, var in enumerate(vars):
        info = vars[var]
        if tags:
            #iterate over all start and end times
            values = numpy.asarray([], dtype=numpy.float64)
            for annoIndex, anno in enumerate(allAnnos):
                thisValues = info["node"].get_time_series(
                    anno["startTime"], anno["endTime"])["values"]
                values = numpy.append(values, thisValues)
                myCount = varIndex * totalAnnos + annoIndex
                prog.set_progress(myCount)
        else:
            values = info["node"].get_time_series(startTime, endTime)["values"]

        valids = numpy.count_nonzero(~numpy.isfinite(values))
        totalValids += valids
        hist, edges = numpy.histogram(values, bins=bins)
        hist = hist / len(values)  #normalize
        info["hist"] = hist
        info["edges"] = edges

    #make a plot
    if totalValids == 0:
        give_up(
            functionNode,
            "all Variables are have no data in the time and annotations selected"
        )
        return False

    progressNode.set_value(0.6)

    hover1 = HoverTool(tooltips=[('x,y', '$x,$y')], mode='mouse')
    hover1.point_policy = 'snap_to_data'
    hover1.line_policy = "nearest"

    tools = [
        PanTool(),
        WheelZoomTool(),
        BoxZoomTool(),
        ResetTool(),
        SaveTool(), hover1
    ]

    title = "Statistics of " + str(
        [info["node"].get_name() for var, info in vars.items()])
    if tags:
        title = title + " in annotation: " + str(tags)

    fig = figure(title=title, tools=tools, plot_height=300)
    fig.toolbar.logo = None

    curdoc().theme = Theme(json=themes.darkTheme)
    fig.xaxis.major_label_text_color = themes.darkTickColor
    fig.yaxis.major_label_text_color = themes.darkTickColor

    for index, var in enumerate(vars):
        info = vars[var]
        col = themes.darkLineColors[index]
        hist = info["hist"]
        edges = info["edges"]

        fig.quad(top=hist,
                 bottom=0,
                 left=edges[:-1],
                 right=edges[1:],
                 fill_color=col,
                 line_color=col,
                 alpha=0.8,
                 legend_label=info["node"].get_name())

    fig.legend.location = "top_left"
    fileName = functionNode.get_child("fileName").get_value()
    filePath = os.path.join(myDir, './../web/customui/' + fileName)

    # now make the trend box plot, but only for tags
    # for each variable we create statistics for the annotations and prepare the data
    # {"node":Node(), "boxLower":[], "boxUpper", "mean", "limitUpper", "limitLower"}
    #

    startTime = date2secs(widget.get_child("startTime").get_value(
    ))  #we only take tags that are inside the current zoom of the widgets
    endTime = date2secs(widget.get_child("endTime").get_value())

    boxPlots = []
    allTimes = []
    if tags:
        for index, var in enumerate(vars):
            info = {
                "node": vars[var]["node"],
                "boxLower": [],
                "boxUpper": [],
                "median": [],
                "time": [],
                "limitUpper": [],
                "limitLower": [],
                "mean": []
            }
            for anno in allAnnos:
                data = info["node"].get_time_series(anno["startTime"],
                                                    anno["endTime"])
                if len(data["values"]):
                    data["values"] = data["values"][numpy.isfinite(
                        data["values"])]
                    #remove the nan
                if len(data["values"]):

                    #make the statistics
                    info["time"].append(numpy.median(data["__time"]) * 1000)
                    allTimes.append(numpy.median(data["__time"]) * 1000)
                    info["limitLower"].append(
                        numpy.quantile(data["values"], 0.01))
                    info["limitUpper"].append(
                        numpy.quantile(data["values"], 0.99))
                    info["boxLower"].append(
                        numpy.quantile(data["values"], 0.25))
                    info["boxUpper"].append(
                        numpy.quantile(data["values"], 0.75))
                    info["median"].append(numpy.median(data["values"]))
                    info["mean"].append(numpy.mean(data["values"]))
            boxPlots.append(info)

        format = "%Y-%m-%d-T%H:%M:%S"
        custom = """var local = moment(value).tz('UTC'); return local.format();"""  #%self.server.get_settings()["timeZone"]

        hover = HoverTool(tooltips=[('date', '@x{%F}')],
                          formatters={'@x': CustomJSHover(code=custom)},
                          mode='mouse')
        hover.point_policy = 'snap_to_data'
        hover.line_policy = "nearest"
        tools = [
            PanTool(),
            BoxZoomTool(),
            WheelZoomTool(),
            ResetTool(), hover,
            SaveTool()
        ]

        fig2 = figure(title="trends",
                      tools=tools,
                      plot_height=300,
                      x_axis_type='datetime')
        fig2.xaxis.major_label_text_color = themes.darkTickColor
        fig2.yaxis.major_label_text_color = themes.darkTickColor

        progressNode.set_value(0.7)

        fig2.xaxis.formatter = DatetimeTickFormatter(years=format,
                                                     days=format,
                                                     months=format,
                                                     hours=format,
                                                     hourmin=format,
                                                     minutes=format,
                                                     minsec=format,
                                                     seconds=format)
        fig2.toolbar.logo = None
        #fig2.line([1,2,3],[1,2,3])
        #calc with of vbars
        if len(allAnnos) > 1:
            xTimesStart = min(allTimes)
            xTimesEnd = max(allTimes)
            width = (xTimesEnd - xTimesStart) / 2 / len(allAnnos)
        else:
            width = 1000000

        for index, info in enumerate(boxPlots):
            #each info is for one variable
            col = themes.darkLineColors[index]
            fig2.segment(info["time"],
                         info["limitUpper"],
                         info["time"],
                         info["boxUpper"],
                         line_color=col)
            fig2.segment(info["time"],
                         info["limitLower"],
                         info["time"],
                         info["boxLower"],
                         line_color=col)

            width = 20
            #fig2.vbar(info["time"],width=width,bottom=info["median"],top=info["boxUpper"],fill_color=col,line_color="black",width_units='screen')
            #fig2.vbar(info["time"],width=width,bottom=info["boxLower"],top=info["median"],fill_color=col,line_color="black",width_units='screen')
            #upper box
            sizUpper = numpy.asarray(info["boxUpper"]) - numpy.asarray(
                info["median"])
            medUpper = numpy.asarray(info["median"]) + sizUpper / 2
            fig2.rect(x=info["time"],
                      y=medUpper,
                      width_units='screen',
                      width=20,
                      height=sizUpper,
                      fill_color=col,
                      line_color="black")

            #lower box
            sizLower = numpy.asarray(info["median"]) - numpy.asarray(
                info["boxLower"])
            medLower = numpy.asarray(info["median"]) - sizLower / 2
            fig2.rect(x=info["time"],
                      y=medLower,
                      width_units='screen',
                      width=20,
                      height=sizLower,
                      fill_color=col,
                      line_color="black")

            #sort data for line
            x = numpy.asarray(info["time"])
            y = numpy.asarray(info["mean"])
            order = numpy.argsort(x)
            x = x[order]
            y = y[order]
            fig2.line(x, y, line_color=col)

        progressNode.set_value(0.8)
    else:
        #no fig2
        pass

    output_file(
        filePath, mode="inline"
    )  #inline: put the bokeh .js into this html, otherwise the default cdn will be taken, might cause CORS problems
    if tags:
        save(layout([[fig], [fig2]]))
    else:
        save(fig)

    return True