UTI: Display HDRH Latency for chosen sample
[csit.git] / resources / tools / dash / app / pal / trending / graphs.py
index da528a9..0760d9c 100644 (file)
 """
 """
 
-
-import logging
 import plotly.graph_objects as go
 import pandas as pd
 import re
 
+import hdrh.histogram
+import hdrh.codec
+
 from datetime import datetime
 from numpy import isnan
-from dash import no_update
 
 from ..jumpavg import classify
 
 
 _COLORS = (
-    u"#1A1110",
-    u"#DA2647",
-    u"#214FC6",
-    u"#01786F",
-    u"#BD8260",
-    u"#FFD12A",
-    u"#A6E7FF",
-    u"#738276",
-    u"#C95A49",
-    u"#FC5A8D",
-    u"#CEC8EF",
-    u"#391285",
-    u"#6F2DA8",
-    u"#FF878D",
-    u"#45A27D",
-    u"#FFD0B9",
-    u"#FD5240",
-    u"#DB91EF",
-    u"#44D7A8",
-    u"#4F86F7",
-    u"#84DE02",
-    u"#FFCFF1",
-    u"#614051"
+    u"#1A1110", u"#DA2647", u"#214FC6", u"#01786F", u"#BD8260", u"#FFD12A",
+    u"#A6E7FF", u"#738276", u"#C95A49", u"#FC5A8D", u"#CEC8EF", u"#391285",
+    u"#6F2DA8", u"#FF878D", u"#45A27D", u"#FFD0B9", u"#FD5240", u"#DB91EF",
+    u"#44D7A8", u"#4F86F7", u"#84DE02", u"#FFCFF1", u"#614051"
 )
 _ANOMALY_COLOR = {
     u"regression": 0.0,
     u"normal": 0.5,
     u"progression": 1.0
 }
-_COLORSCALE = [
+_COLORSCALE_TPUT = [
     [0.00, u"red"],
     [0.33, u"red"],
     [0.33, u"white"],
@@ -65,16 +46,66 @@ _COLORSCALE = [
     [0.66, u"green"],
     [1.00, u"green"]
 ]
+_TICK_TEXT_TPUT = [u"Regression", u"Normal", u"Progression"]
+_COLORSCALE_LAT = [
+    [0.00, u"green"],
+    [0.33, u"green"],
+    [0.33, u"white"],
+    [0.66, u"white"],
+    [0.66, u"red"],
+    [1.00, u"red"]
+]
+_TICK_TEXT_LAT = [u"Progression", u"Normal", u"Regression"]
 _VALUE = {
     "mrr": "result_receive_rate_rate_avg",
     "ndr": "result_ndr_lower_rate_value",
-    "pdr": "result_pdr_lower_rate_value"
+    "pdr": "result_pdr_lower_rate_value",
+    "pdr-lat": "result_latency_forward_pdr_50_avg"
 }
 _UNIT = {
     "mrr": "result_receive_rate_rate_unit",
     "ndr": "result_ndr_lower_rate_unit",
-    "pdr": "result_pdr_lower_rate_unit"
+    "pdr": "result_pdr_lower_rate_unit",
+    "pdr-lat": "result_latency_forward_pdr_50_unit"
 }
+_LAT_HDRH = (  # Do not change the order
+    "result_latency_forward_pdr_0_hdrh",
+    "result_latency_reverse_pdr_0_hdrh",
+    "result_latency_forward_pdr_10_hdrh",
+    "result_latency_reverse_pdr_10_hdrh",
+    "result_latency_forward_pdr_50_hdrh",
+    "result_latency_reverse_pdr_50_hdrh",
+    "result_latency_forward_pdr_90_hdrh",
+    "result_latency_reverse_pdr_90_hdrh",
+)
+# This value depends on latency stream rate (9001 pps) and duration (5s).
+# Keep it slightly higher to ensure rounding errors to not remove tick mark.
+PERCENTILE_MAX = 99.999501
+
+_GRAPH_LAT_HDRH_DESC = {
+    u"result_latency_forward_pdr_0_hdrh": u"No-load.",
+    u"result_latency_reverse_pdr_0_hdrh": u"No-load.",
+    u"result_latency_forward_pdr_10_hdrh": u"Low-load, 10% PDR.",
+    u"result_latency_reverse_pdr_10_hdrh": u"Low-load, 10% PDR.",
+    u"result_latency_forward_pdr_50_hdrh": u"Mid-load, 50% PDR.",
+    u"result_latency_reverse_pdr_50_hdrh": u"Mid-load, 50% PDR.",
+    u"result_latency_forward_pdr_90_hdrh": u"High-load, 90% PDR.",
+    u"result_latency_reverse_pdr_90_hdrh": u"High-load, 90% PDR."
+}
+
+
+def _get_hdrh_latencies(row: pd.Series, name: str) -> dict:
+    """
+    """
+
+    latencies = {"name": name}
+    for key in _LAT_HDRH:
+        try:
+            latencies[key] = row[key]
+        except KeyError:
+            return None
+
+    return latencies
 
 
 def _classify_anomalies(data):
@@ -127,16 +158,16 @@ def _classify_anomalies(data):
     return classification, avgs, stdevs
 
 
-def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
-    end: datetime):
+def graph_trending_tput(data: pd.DataFrame, sel:dict, layout: dict,
+    start: datetime, end: datetime) -> tuple:
     """
     """
 
     if not sel:
-        return no_update, no_update
+        return None, None
 
     def _generate_traces(ttype: str, name: str, df: pd.DataFrame,
-        start: datetime, end: datetime, color: str):
+        start: datetime, end: datetime, color: str) -> list:
 
         df = df.dropna(subset=[_VALUE[ttype], ])
         if df.empty:
@@ -149,12 +180,12 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
         )
 
         hover = list()
+        customdata = list()
         for _, row in df.iterrows():
             hover_itm = (
-                f"date: "
-                f"{row['start_time'].strftime('%d-%m-%Y %H:%M:%S')}<br>"
-                f"average [{row[_UNIT[ttype]]}]: "
-                f"{row[_VALUE[ttype]]}<br>"
+                f"date: {row['start_time'].strftime('%d-%m-%Y %H:%M:%S')}<br>"
+                f"<prop> [{row[_UNIT[ttype]]}]: {row[_VALUE[ttype]]}<br>"
+                f"<stdev>"
                 f"{row['dut_type']}-ref: {row['dut_version']}<br>"
                 f"csit-ref: {row['job']}/{row['build']}"
             )
@@ -165,15 +196,25 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
                 )
             else:
                 stdev = ""
-            hover_itm = hover_itm.replace("<stdev>", stdev)
+            hover_itm = hover_itm.replace(
+                "<prop>", "latency" if ttype == "pdr-lat" else "average"
+            ).replace("<stdev>", stdev)
             hover.append(hover_itm)
+            if ttype == "pdr-lat":
+                customdata.append(_get_hdrh_latencies(row, name))
 
         hover_trend = list()
         for avg, stdev in zip(trend_avg, trend_stdev):
-            hover_trend.append(
-                f"trend [pps]: {avg}<br>"
-                f"stdev [pps]: {stdev}"
-            )
+            if ttype == "pdr-lat":
+                hover_trend.append(
+                    f"trend [us]: {avg}<br>"
+                    f"stdev [us]: {stdev}"
+                )
+            else:
+                hover_trend.append(
+                    f"trend [pps]: {avg}<br>"
+                    f"stdev [pps]: {stdev}"
+                )
 
         traces = [
             go.Scatter(  # Samples
@@ -190,6 +231,7 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
                 hoverinfo=u"text+name",
                 showlegend=True,
                 legendgroup=name,
+                customdata=customdata
             ),
             go.Scatter(  # Trend line
                 x=x_axis,
@@ -212,13 +254,12 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
             anomaly_x = list()
             anomaly_y = list()
             anomaly_color = list()
-            ticktext = [u"Regression", u"Normal", u"Progression"]
             for idx, anomaly in enumerate(anomalies):
                 if anomaly in (u"regression", u"progression"):
                     anomaly_x.append(x_axis[idx])
                     anomaly_y.append(trend_avg[idx])
                     anomaly_color.append(_ANOMALY_COLOR[anomaly])
-            anomaly_color.append([0.0, 1.0])
+            anomaly_color.extend([0.0, 0.5, 1.0])
             traces.append(
                 go.Scatter(
                     x=anomaly_x,
@@ -232,7 +273,8 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
                         u"size": 15,
                         u"symbol": u"circle-open",
                         u"color": anomaly_color,
-                        u"colorscale": _COLORSCALE,
+                        u"colorscale": _COLORSCALE_LAT \
+                            if ttype == "pdr-lat" else _COLORSCALE_TPUT,
                         u"showscale": True,
                         u"line": {
                             u"width": 2
@@ -242,12 +284,13 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
                             u"len": 0.8,
                             u"title": u"Circles Marking Data Classification",
                             u"titleside": u"right",
-                            u"titlefont": {
-                                u"size": 14
-                            },
+                            u"titlefont": {
+                                u"size": 14
+                            },
                             u"tickmode": u"array",
                             u"tickvals": [0.167, 0.500, 0.833],
-                            u"ticktext": ticktext,
+                            u"ticktext": _TICK_TEXT_LAT \
+                                if ttype == "pdr-lat" else _TICK_TEXT_TPUT,
                             u"ticks": u"",
                             u"ticklen": 0,
                             u"tickangle": -90,
@@ -260,7 +303,8 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
         return traces
 
     # Generate graph:
-    fig = go.Figure()
+    fig_tput = None
+    fig_lat = None
     for idx, itm in enumerate(sel):
         phy = itm["phy"].split("-")
         if len(phy) == 4:
@@ -293,18 +337,103 @@ def trending_tput(data: pd.DataFrame, sel:dict, layout: dict, start: datetime,
             f"{itm['phy']}-{itm['framesize']}-{itm['core']}-"
             f"{itm['test']}-{itm['testtype']}"
         )
-        for trace in _generate_traces(itm['testtype'], name, df, start, end,
-                _COLORS[idx % len(_COLORS)]):
-            fig.add_trace(trace)
 
-    style={
-        "vertical-align": "top",
-        "display": "inline-block",
-        "width": "80%",
-        "padding": "5px"
-    }
+        traces = _generate_traces(
+            itm["testtype"], name, df, start, end, _COLORS[idx % len(_COLORS)]
+        )
+        if traces:
+            if not fig_tput:
+                fig_tput = go.Figure()
+            fig_tput.add_traces(traces)
+
+        if itm["testtype"] == "pdr":
+            traces = _generate_traces(
+                "pdr-lat", name, df, start, end, _COLORS[idx % len(_COLORS)]
+            )
+            if traces:
+                if not fig_lat:
+                    fig_lat = go.Figure()
+                fig_lat.add_traces(traces)
+
+    if fig_tput:
+        fig_tput.update_layout(layout.get("plot-trending-tput", dict()))
+    if fig_lat:
+        fig_lat.update_layout(layout.get("plot-trending-lat", dict()))
+
+    return fig_tput, fig_lat
+
+
+def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
+    """
+    """
 
-    layout = layout.get("plot-trending", dict())
-    fig.update_layout(layout)
+    fig = None
+
+    try:
+        name = data.pop("name")
+    except (KeyError, AttributeError):
+        return None
+
+    traces = list()
+    for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
+        try:
+            decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
+        except (hdrh.codec.HdrLengthException, TypeError) as err:
+            continue
+        previous_x = 0.0
+        prev_perc = 0.0
+        xaxis = list()
+        yaxis = list()
+        hovertext = list()
+        for item in decoded.get_recorded_iterator():
+            # The real value is "percentile".
+            # For 100%, we cut that down to "x_perc" to avoid
+            # infinity.
+            percentile = item.percentile_level_iterated_to
+            x_perc = min(percentile, PERCENTILE_MAX)
+            xaxis.append(previous_x)
+            yaxis.append(item.value_iterated_to)
+            hovertext.append(
+                f"<b>{_GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+                f"Direction: {(u'W-E', u'E-W')[idx % 2]}<br>"
+                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+                f"Latency: {item.value_iterated_to}uSec"
+            )
+            next_x = 100.0 / (100.0 - x_perc)
+            xaxis.append(next_x)
+            yaxis.append(item.value_iterated_to)
+            hovertext.append(
+                f"<b>{_GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+                f"Direction: {(u'W-E', u'E-W')[idx % 2]}<br>"
+                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+                f"Latency: {item.value_iterated_to}uSec"
+            )
+            previous_x = next_x
+            prev_perc = percentile
+
+        traces.append(
+            go.Scatter(
+                x=xaxis,
+                y=yaxis,
+                name=_GRAPH_LAT_HDRH_DESC[lat_name],
+                mode=u"lines",
+                legendgroup=_GRAPH_LAT_HDRH_DESC[lat_name],
+                showlegend=bool(idx % 2),
+                line=dict(
+                    color=_COLORS[int(idx/2)],
+                    dash=u"solid",
+                    width=1 if idx % 2 else 2
+                ),
+                hovertext=hovertext,
+                hoverinfo=u"text"
+            )
+        )
+    if traces:
+        fig = go.Figure()
+        fig.add_traces(traces)
+        layout_hdrh = layout.get("plot-hdrh-latency", None)
+        if lat_hdrh:
+            layout_hdrh["title"]["text"] = f"<b>{name}</b>"
+            fig.update_layout(layout_hdrh)
 
-    return fig, style
+    return fig