C-Dash: Add multiple telemetry panels
[csit.git] / csit.infra.dash / app / cdash / trending / layout.py
index 14493ff..97181e1 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at:
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at:
@@ -11,6 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 """Plotly Dash HTML layout override.
 """
 
 """Plotly Dash HTML layout override.
 """
 
@@ -25,27 +26,59 @@ from dash import callback_context, no_update, ALL
 from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
-from datetime import datetime
-from copy import deepcopy
-from json import loads, JSONDecodeError
 from ast import literal_eval
 from ast import literal_eval
+from copy import deepcopy
 
 from ..utils.constants import Constants as C
 
 from ..utils.constants import Constants as C
-from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
-    gen_new_url, generate_options
+from ..utils.control_panel import ControlPanel
+from ..utils.trigger import Trigger
+from ..utils.telemetry_data import TelemetryData
+from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
+    generate_options, get_list_group_items, graph_hdrh_latency
 from ..utils.url_processing import url_decode
 from ..utils.url_processing import url_decode
-from ..data.data import Data
-from .graphs import graph_trending, graph_hdrh_latency, \
-    select_trending_data
+from .graphs import graph_trending, select_trending_data, graph_tm_trending
+
+
+# Control panel partameters and their default values.
+CP_PARAMS = {
+    "dd-dut-val": str(),
+    "dd-phy-opt": list(),
+    "dd-phy-dis": True,
+    "dd-phy-val": str(),
+    "dd-area-opt": list(),
+    "dd-area-dis": True,
+    "dd-area-val": str(),
+    "dd-test-opt": list(),
+    "dd-test-dis": True,
+    "dd-test-val": str(),
+    "cl-core-opt": list(),
+    "cl-core-val": list(),
+    "cl-core-all-val": list(),
+    "cl-core-all-opt": C.CL_ALL_DISABLED,
+    "cl-frmsize-opt": list(),
+    "cl-frmsize-val": list(),
+    "cl-frmsize-all-val": list(),
+    "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+    "cl-tsttype-opt": list(),
+    "cl-tsttype-val": list(),
+    "cl-tsttype-all-val": list(),
+    "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+    "btn-add-dis": True,
+    "cl-normalize-val": list()
+}
 
 
 class Layout:
     """The layout of the dash app and the callbacks.
     """
 
 
 
 class Layout:
     """The layout of the dash app and the callbacks.
     """
 
-    def __init__(self, app: Flask, html_layout_file: str,
-        graph_layout_file: str, data_spec_file: str, tooltip_file: str,
-        time_period: str=None) -> None:
+    def __init__(self,
+            app: Flask,
+            data_trending: pd.DataFrame,
+            html_layout_file: str,
+            graph_layout_file: str,
+            tooltip_file: str
+        ) -> None:
         """Initialization:
         - save the input parameters,
         - read and pre-process the data,
         """Initialization:
         - save the input parameters,
         - read and pre-process the data,
@@ -54,64 +87,39 @@ class Layout:
         - read tooltips from the tooltip file.
 
         :param app: Flask application running the dash application.
         - read tooltips from the tooltip file.
 
         :param app: Flask application running the dash application.
+        :param data_trending: Pandas dataframe with trending data.
         :param html_layout_file: Path and name of the file specifying the HTML
             layout of the dash application.
         :param graph_layout_file: Path and name of the file with layout of
             plot.ly graphs.
         :param html_layout_file: Path and name of the file specifying the HTML
             layout of the dash application.
         :param graph_layout_file: Path and name of the file with layout of
             plot.ly graphs.
-        :param data_spec_file: Path and name of the file specifying the data to
-            be read from parquets for this application.
         :param tooltip_file: Path and name of the yaml file specifying the
             tooltips.
         :param tooltip_file: Path and name of the yaml file specifying the
             tooltips.
-        :param time_period: It defines the time period for data read from the
-            parquets in days from now back to the past.
         :type app: Flask
         :type app: Flask
+        :type data_trending: pandas.DataFrame
         :type html_layout_file: str
         :type graph_layout_file: str
         :type html_layout_file: str
         :type graph_layout_file: str
-        :type data_spec_file: str
         :type tooltip_file: str
         :type tooltip_file: str
-        :type time_period: int
         """
 
         # Inputs
         self._app = app
         """
 
         # Inputs
         self._app = app
+        self._data = data_trending
         self._html_layout_file = html_layout_file
         self._graph_layout_file = graph_layout_file
         self._html_layout_file = html_layout_file
         self._graph_layout_file = graph_layout_file
-        self._data_spec_file = data_spec_file
         self._tooltip_file = tooltip_file
         self._tooltip_file = tooltip_file
-        self._time_period = time_period
-
-        # Read the data:
-        data_mrr = Data(
-            data_spec_file=self._data_spec_file,
-            debug=True
-        ).read_trending_mrr(days=self._time_period)
-
-        data_ndrpdr = Data(
-            data_spec_file=self._data_spec_file,
-            debug=True
-        ).read_trending_ndrpdr(days=self._time_period)
-
-        self._data = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
-
-        data_time_period = \
-            (datetime.utcnow() - self._data["start_time"].min()).days
-        if self._time_period > data_time_period:
-            self._time_period = data_time_period
-
 
         # Get structure of tests:
         tbs = dict()
 
         # Get structure of tests:
         tbs = dict()
-        for _, row in self._data[["job", "test_id"]].drop_duplicates().\
-                iterrows():
+        cols = ["job", "test_id", "test_type", "tg_type"]
+        for _, row in self._data[cols].drop_duplicates().iterrows():
             lst_job = row["job"].split("-")
             dut = lst_job[1]
             lst_job = row["job"].split("-")
             dut = lst_job[1]
-            ttype = lst_job[3]
             tbed = "-".join(lst_job[-2:])
             lst_test = row["test_id"].split(".")
             if dut == "dpdk":
                 area = "dpdk"
             else:
             tbed = "-".join(lst_job[-2:])
             lst_test = row["test_id"].split(".")
             if dut == "dpdk":
                 area = "dpdk"
             else:
-                area = "-".join(lst_test[3:-2])
+                area = ".".join(lst_test[3:-2])
             suite = lst_test[-2].replace("2n1l-", "").replace("1n1l-", "").\
                 replace("2n-", "")
             test = lst_test[-1]
             suite = lst_test[-2].replace("2n1l-", "").replace("1n1l-", "").\
                 replace("2n-", "")
             test = lst_test[-1]
@@ -148,18 +156,29 @@ class Layout:
             if framesize.upper() not in \
                     tbs[dut][infra][area][test]["frame-size"]:
                 tbs[dut][infra][area][test]["frame-size"].append(
             if framesize.upper() not in \
                     tbs[dut][infra][area][test]["frame-size"]:
                 tbs[dut][infra][area][test]["frame-size"].append(
-                    framesize.upper())
-            if ttype == "mrr":
+                    framesize.upper()
+                )
+            if row["test_type"] == "mrr":
                 if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
                     tbs[dut][infra][area][test]["test-type"].append("MRR")
                 if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
                     tbs[dut][infra][area][test]["test-type"].append("MRR")
-            elif ttype == "ndrpdr":
+            elif row["test_type"] == "ndrpdr":
                 if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
                     tbs[dut][infra][area][test]["test-type"].extend(
                 if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
                     tbs[dut][infra][area][test]["test-type"].extend(
-                        ("NDR", "PDR"))
+                        ("NDR", "PDR")
+                    )
+            elif row["test_type"] == "hoststack":
+                if row["tg_type"] in ("iperf", "vpp"):
+                    if "BPS" not in tbs[dut][infra][area][test]["test-type"]:
+                        tbs[dut][infra][area][test]["test-type"].append("BPS")
+                elif row["tg_type"] == "ab":
+                    if "CPS" not in tbs[dut][infra][area][test]["test-type"]:
+                        tbs[dut][infra][area][test]["test-type"].extend(
+                            ("CPS", "RPS")
+                        )
         self._spec_tbs = tbs
 
         # Read from files:
         self._spec_tbs = tbs
 
         # Read from files:
-        self._html_layout = ""
+        self._html_layout = str()
         self._graph_layout = None
         self._tooltips = dict()
 
         self._graph_layout = None
         self._tooltips = dict()
 
@@ -199,29 +218,13 @@ class Layout:
             )
 
         # Callbacks:
             )
 
         # Callbacks:
-        if self._app is not None and hasattr(self, 'callbacks'):
+        if self._app is not None and hasattr(self, "callbacks"):
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
-    @property
-    def spec_tbs(self):
-        return self._spec_tbs
-
-    @property
-    def data(self):
-        return self._data
-
-    @property
-    def layout(self):
-        return self._graph_layout
-
-    @property
-    def time_period(self):
-        return self._time_period
-
     def add_content(self):
         """Top level method which generated the web page.
 
     def add_content(self):
         """Top level method which generated the web page.
 
@@ -236,19 +239,29 @@ class Layout:
         :rtype: html.Div
         """
 
         :rtype: html.Div
         """
 
-        if self.html_layout and self.spec_tbs:
+        if self.html_layout and self._spec_tbs:
             return html.Div(
                 id="div-main",
                 className="small",
                 children=[
             return html.Div(
                 id="div-main",
                 className="small",
                 children=[
+                    dcc.Store(id="store"),
+                    dcc.Location(id="url", refresh=False),
                     dbc.Row(
                         id="row-navbar",
                         class_name="g-0",
                         children=[
                     dbc.Row(
                         id="row-navbar",
                         class_name="g-0",
                         children=[
-                            self._add_navbar(),
+                            self._add_navbar()
+                        ]
+                    ),
+                    dbc.Row(
+                        id="row-main",
+                        class_name="g-0",
+                        children=[
+                            self._add_ctrl_col(),
+                            self._add_plotting_col()
                         ]
                     ),
                         ]
                     ),
-                    dcc.Loading(
+                    dbc.Spinner(
                         dbc.Offcanvas(
                             class_name="w-50",
                             id="offcanvas-metadata",
                         dbc.Offcanvas(
                             class_name="w-50",
                             id="offcanvas-metadata",
@@ -257,20 +270,10 @@ class Layout:
                             is_open=False,
                             children=[
                                 dbc.Row(id="metadata-tput-lat"),
                             is_open=False,
                             children=[
                                 dbc.Row(id="metadata-tput-lat"),
-                                dbc.Row(id="metadata-hdrh-graph"),
+                                dbc.Row(id="metadata-hdrh-graph")
                             ]
                             ]
-                        )
-                    ),
-                    dbc.Row(
-                        id="row-main",
-                        class_name="g-0",
-                        children=[
-                            dcc.Store(id="selected-tests"),
-                            dcc.Store(id="control-panel"),
-                            dcc.Location(id="url", refresh=False),
-                            self._add_ctrl_col(),
-                            self._add_plotting_col(),
-                        ]
+                        ),
+                        delay_show=C.SPINNER_DELAY
                     )
                 ]
             )
                     )
                 ]
             )
@@ -280,10 +283,10 @@ class Layout:
                 children=[
                     dbc.Alert(
                         [
                 children=[
                     dbc.Alert(
                         [
-                            "An Error Occured",
+                            "An Error Occured"
                         ],
                         ],
-                        color="danger",
-                    ),
+                        color="danger"
+                    )
                 ]
             )
 
                 ]
             )
 
@@ -309,7 +312,7 @@ class Layout:
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
-            fluid=True,
+            fluid=True
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
@@ -325,49 +328,11 @@ class Layout:
             )
         ])
 
             )
         ])
 
-    def _add_plotting_col(self) -> dbc.Col:
-        """Add column with plots and tables. It is placed on the right side.
-
-        :returns: Column with tables.
-        :rtype: dbc.Col
-        """
-        return dbc.Col(
-            id="col-plotting-area",
-            children=[
-                dcc.Loading(
-                    children=[
-                        dbc.Row(  # Throughput
-                            id="row-graph-tput",
-                            class_name="g-0 p-2",
-                            children=[
-                                C.PLACEHOLDER
-                            ]
-                        ),
-                        dbc.Row(  # Latency
-                            id="row-graph-lat",
-                            class_name="g-0 p-2",
-                            children=[
-                                C.PLACEHOLDER
-                            ]
-                        ),
-                        dbc.Row(  # Download
-                            id="row-btn-download",
-                            class_name="g-0 p-2",
-                            children=[
-                                C.PLACEHOLDER
-                            ]
-                        )
-                    ]
-                )
-            ],
-            width=9,
-        )
-
-    def _add_ctrl_panel(self) -> dbc.Row:
+    def _add_ctrl_panel(self) -> list:
         """Add control panel.
 
         :returns: Control panel.
         """Add control panel.
 
         :returns: Control panel.
-        :rtype: dbc.Row
+        :rtype: list
         """
         return [
             dbc.Row(
         """
         return [
             dbc.Row(
@@ -376,25 +341,26 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-dut", "DUT")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-dut",
+                                    "DUT"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-dut",
-                                placeholder=(
-                                    "Select a Device under Test..."
-                                ),
+                                id={"type": "ctrl-dd", "index": "dut"},
+                                placeholder="Select a Device under Test...",
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
-                                            for k in self.spec_tbs.keys()
+                                            for k in self._spec_tbs.keys()
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -403,19 +369,20 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-infra", "Infra")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-infra",
+                                    "Infra"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-phy",
-                                placeholder=(
-                                    "Select a Physical Test Bed "
-                                    "Topology..."
-                                )
+                                id={"type": "ctrl-dd", "index": "phy"},
+                                placeholder=\
+                                    "Select a Physical Test Bed Topology..."
                             )
                         ],
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -424,17 +391,19 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-area", "Area")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-area",
+                                    "Area"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-area",
-                                placeholder="Select an Area...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "area"},
+                                placeholder="Select an Area..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -443,143 +412,192 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-test", "Test")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-test",
+                                    "Test"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-test",
-                                placeholder="Select a Test...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "test"},
+                                placeholder="Select a Test..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-framesize", "Frame Size"),
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-framesize-all",
-                                options=C.CL_ALL_DISABLED,
-                                inline=True,
-                                switch=False
+                    dbc.InputGroup(
+                        [
+                            dbc.InputGroupText(
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-framesize",
+                                    "Frame Size"
+                                )
                             ),
                             ),
-                        ],
-                        width=3
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-framesize",
-                                inline=True,
-                                switch=False
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "frmsize-all"
+                                        },
+                                        options=C.CL_ALL_DISABLED,
+                                        inline=True,
+                                        class_name="ms-2"
+                                    )
+                                ],
+                                width=2
+                            ),
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "frmsize"
+                                        },
+                                        inline=True
+                                    )
+                                ]
                             )
                             )
-                        ]
+                        ],
+                        style={"align-items": "center"},
+                        size="sm"
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-cores", "Number of Cores"),
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-core-all",
-                                options=C.CL_ALL_DISABLED,
-                                inline=False,
-                                switch=False
+                    dbc.InputGroup(
+                        [
+                            dbc.InputGroupText(
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-cores",
+                                    "Number of Cores"
+                                )
+                            ),
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "core-all"
+                                        },
+                                        options=C.CL_ALL_DISABLED,
+                                        inline=True,
+                                        class_name="ms-2"
+                                    )
+                                ],
+                                width=2
+                            ),
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "core"
+                                        },
+                                        inline=True
+                                    )
+                                ]
                             )
                         ],
                             )
                         ],
-                        width=3
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-core",
-                                inline=True,
-                                switch=False
-                            )
-                        ]
+                        style={"align-items": "center"},
+                        size="sm"
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-ttype", "Test Type"),
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-testtype-all",
-                                options=C.CL_ALL_DISABLED,
-                                inline=True,
-                                switch=False
+                    dbc.InputGroup(
+                        [
+                            dbc.InputGroupText(
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-ttype",
+                                    "Test Type"
+                                )
                             ),
                             ),
-                        ],
-                        width=3
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-testtype",
-                                inline=True,
-                                switch=False
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "tsttype-all"
+                                        },
+                                        options=C.CL_ALL_DISABLED,
+                                        inline=True,
+                                        class_name="ms-2"
+                                    )
+                                ],
+                                width=2
+                            ),
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id={
+                                            "type": "ctrl-cl",
+                                            "index": "tsttype"
+                                        },
+                                        inline=True
+                                    )
+                                ]
                             )
                             )
-                        ]
+                        ],
+                        style={"align-items": "center"},
+                        size="sm"
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-normalize", "Normalize"),
-                    ),
-                    dbc.Col(
-                        children=[
-                            dbc.Checklist(
-                                id="cl-ctrl-normalize",
-                                options=[{
-                                    "value": "normalize",
-                                    "label": (
-                                        "Normalize results to CPU "
-                                        "frequency 2GHz"
-                                    )
-                                }],
-                                value=[],
-                                inline=True,
-                                switch=False
+                    dbc.InputGroup(
+                        [
+                            dbc.InputGroupText(
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-normalize",
+                                    "Normalization"
+                                )
                             ),
                             ),
-                        ]
+                            dbc.Col(
+                                children=[
+                                    dbc.Checklist(
+                                        id="normalize",
+                                        options=[{
+                                            "value": "normalize",
+                                            "label": (
+                                                "Normalize to CPU frequency "
+                                                "2GHz"
+                                            )
+                                        }],
+                                        value=[],
+                                        inline=True,
+                                        class_name="ms-2"
+                                    )
+                                ]
+                            )
+                        ],
+                        style={"align-items": "center"},
+                        size="sm"
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.ButtonGroup(
-                        [
-                            dbc.Button(
-                                id="btn-ctrl-add",
-                                children="Add Selected",
-                                color="info"
-                            )
-                        ]
+                    dbc.Button(
+                        id={"type": "ctrl-btn", "index": "add-test"},
+                        children="Add Selected",
+                        color="info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -588,15 +606,14 @@ class Layout:
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
-                    dbc.Label("Selected tests"),
-                    dbc.Checklist(
-                        class_name="overflow-auto",
-                        id="cl-selected",
-                        options=[],
-                        inline=False,
-                        style={"max-height": "12em"},
+                    dbc.ListGroup(
+                        class_name="overflow-auto p-0",
+                        id="lg-selected",
+                        children=[],
+                        style={"max-height": "20em"},
+                        flush=True
                     )
                     )
-                ],
+                ]
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
@@ -606,115 +623,392 @@ class Layout:
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
-                                id="btn-sel-remove",
+                                id={"type": "ctrl-btn", "index": "rm-test"},
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
-                                id="btn-sel-remove-all",
+                                id={"type": "ctrl-btn", "index": "rm-test-all"},
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
-                            ),
+                            )
                         ]
                     )
                 ]
             ),
                         ]
                     )
                 ]
             ),
+            dbc.Stack(
+                id="row-btns-add-tm",
+                class_name="g-0 p-1",
+                style=C.STYLE_DISABLED,
+                gap=2,
+                children=[
+                    dbc.Button(
+                        "Add Telemetry Panel",
+                        id={"type": "telemetry-btn", "index": "open"},
+                        color="info"
+                    ),
+                    dbc.Button("Show URL", id="plot-btn-url", color="info"),
+                    dbc.Modal(
+                        [
+                            dbc.ModalHeader(dbc.ModalTitle("URL")),
+                            dbc.ModalBody(id="mod-url")
+                        ],
+                        id="plot-mod-url",
+                        size="xl",
+                        is_open=False,
+                        scrollable=True
+                    )
+                ]
+            )
         ]
 
         ]
 
-    class ControlPanel:
-        """A class representing the control panel.
-        """
-
-        def __init__(self, panel: dict) -> None:
-            """Initialisation of the control pannel by default values. If
-            particular values are provided (parameter "panel") they are set
-            afterwards.
+    def _add_plotting_col(self) -> dbc.Col:
+        """Add column with plots. It is placed on the right side.
 
 
-            :param panel: Custom values to be set to the control panel.
-            :param default: Default values to be set to the control panel.
-            :type panel: dict
-            :type defaults: dict
-            """
+        :returns: Column with plots.
+        :rtype: dbc.Col
+        """
+        return dbc.Col(
+            id="col-plotting-area",
+            children=[
+                dbc.Spinner(
+                    dbc.Row(
+                        id="plotting-area-trending",
+                        class_name="g-0 p-0",
+                        children=C.PLACEHOLDER
+                    ),
+                    delay_show=C.SPINNER_DELAY
+                ),
+                dbc.Row(
+                    id="plotting-area-telemetry",
+                    class_name="g-0 p-0",
+                    children=C.PLACEHOLDER
+                )
+            ],
+            width=9,
+            style=C.STYLE_DISABLED,
+        )
 
 
-            # Defines also the order of keys
-            self._defaults = {
-                "dd-ctrl-dut-value": str(),
-                "dd-ctrl-phy-options": list(),
-                "dd-ctrl-phy-disabled": True,
-                "dd-ctrl-phy-value": str(),
-                "dd-ctrl-area-options": list(),
-                "dd-ctrl-area-disabled": True,
-                "dd-ctrl-area-value": str(),
-                "dd-ctrl-test-options": list(),
-                "dd-ctrl-test-disabled": True,
-                "dd-ctrl-test-value": str(),
-                "cl-ctrl-core-options": list(),
-                "cl-ctrl-core-value": list(),
-                "cl-ctrl-core-all-value": list(),
-                "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
-                "cl-ctrl-framesize-options": list(),
-                "cl-ctrl-framesize-value": list(),
-                "cl-ctrl-framesize-all-value": list(),
-                "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
-                "cl-ctrl-testtype-options": list(),
-                "cl-ctrl-testtype-value": list(),
-                "cl-ctrl-testtype-all-value": list(),
-                "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
-                "btn-ctrl-add-disabled": True,
-                "cl-normalize-value": list(),
-                "cl-selected-options": list()
-            }
+    def _plotting_area_trending(
+            self,
+            tests: list,
+            normalize: bool
+        ) -> dbc.Col:
+        """Generate the plotting area with all its content.
+
+        :param tests: A list of tests to be displayed in the trending graphs.
+        :param normalize: If True, the data in graphs is normalized.
+        :type tests: list
+        :type normalize: bool
+        :returns: A collumn with trending graphs (tput and latency) in tabs.
+        :rtype: dbc.Col
+        """
+        if not tests:
+            return C.PLACEHOLDER
+
+        figs = graph_trending(self._data, tests, self._graph_layout, normalize)
+
+        if not figs[0]:
+            return C.PLACEHOLDER
+
+        tab_items = [
+            dbc.Tab(
+                children=dcc.Graph(
+                    id={"type": "graph", "index": "tput"},
+                    figure=figs[0]
+                ),
+                label="Throughput",
+                tab_id="tab-tput"
+            )
+        ]
 
 
-            self._panel = deepcopy(self._defaults)
-            if panel:
-                for key in self._defaults:
-                    self._panel[key] = panel[key]
+        if figs[1]:
+            tab_items.append(
+                dbc.Tab(
+                    children=dcc.Graph(
+                        id={"type": "graph", "index": "lat"},
+                        figure=figs[1]
+                    ),
+                    label="Latency",
+                    tab_id="tab-lat"
+                )
+            )
 
 
-        @property
-        def defaults(self) -> dict:
-            return self._defaults
+        trending = [
+            dbc.Row(
+                dbc.Tabs(
+                    children=tab_items,
+                    id="tabs",
+                    active_tab="tab-tput",
+                ),
+                class_name="g-0 p-0"
+            ),
+            dbc.Row(
+                html.Div(
+                    [
+                        dbc.Button(
+                            "Download Data",
+                            id="plot-btn-download",
+                            class_name="me-1",
+                            color="info",
+                            style={"padding": "0rem 1rem"}
+                        ),
+                        dcc.Download(id="download-trending-data")
+                    ],
+                    className="d-grid gap-0 d-md-flex justify-content-md-end"
+                ),
+                class_name="g-0 p-0"
+            )
+        ]
 
 
-        @property
-        def panel(self) -> dict:
-            return self._panel
+        return dbc.Col(
+            children=[
+                dbc.Accordion(
+                    dbc.AccordionItem(trending, title="Trending"),
+                    class_name="g-0 p-1",
+                    start_collapsed=False,
+                    always_open=True,
+                    active_item=["item-0", ]
+                ),
+                dbc.Modal(
+                    [
+                        dbc.ModalHeader(
+                            dbc.ModalTitle("Select a Metric"),
+                            close_button=False
+                        ),
+                        dbc.Spinner(
+                            dbc.ModalBody(self._get_telemetry_step_1()),
+                            delay_show=2 * C.SPINNER_DELAY
+                        ),
+                        dbc.ModalFooter([
+                            dbc.Button(
+                                "Select",
+                                id={"type": "telemetry-btn", "index": "select"},
+                                color="success",
+                                disabled=True
+                            ),
+                            dbc.Button(
+                                "Cancel",
+                                id={"type": "telemetry-btn", "index": "cancel"},
+                                color="info",
+                                disabled=False
+                            ),
+                            dbc.Button(
+                                "Remove All",
+                                id={"type": "telemetry-btn", "index": "rm-all"},
+                                color="danger",
+                                disabled=False
+                            )
+                        ])
+                    ],
+                    id={"type": "plot-mod-telemetry", "index": 0},
+                    size="lg",
+                    is_open=False,
+                    scrollable=False,
+                    backdrop="static",
+                    keyboard=False
+                ),
+                dbc.Modal(
+                    [
+                        dbc.ModalHeader(
+                            dbc.ModalTitle("Select Labels"),
+                            close_button=False
+                        ),
+                        dbc.Spinner(
+                            dbc.ModalBody(self._get_telemetry_step_2()),
+                            delay_show=2 * C.SPINNER_DELAY
+                        ),
+                        dbc.ModalFooter([
+                            dbc.Button(
+                                "Back",
+                                id={"type": "telemetry-btn", "index": "back"},
+                                color="info",
+                                disabled=False
+                            ),
+                            dbc.Button(
+                                "Add Telemetry Panel",
+                                id={"type": "telemetry-btn", "index": "add"},
+                                color="success",
+                                disabled=True
+                            ),
+                            dbc.Button(
+                                "Cancel",
+                                id={"type": "telemetry-btn", "index": "cancel"},
+                                color="info",
+                                disabled=False
+                            )
+                        ])
+                    ],
+                    id={"type": "plot-mod-telemetry", "index": 1},
+                    size="xl",
+                    is_open=False,
+                    scrollable=False,
+                    backdrop="static",
+                    keyboard=False
+                )
+            ]
+        )
 
 
-        def set(self, kwargs: dict) -> None:
-            """Set the values of the Control panel.
+    @staticmethod
+    def _plotting_area_telemetry(graphs: list) -> dbc.Col:
+        """Generate the plotting area with telemetry.
+        """
+        if not graphs:
+            return C.PLACEHOLDER
+        
+        def _plural(iterative):
+            return "s" if len(iterative) > 1 else str()
+
+        panels = list()
+        for idx, graph_set in enumerate(graphs):
+            acc_items = list()
+            for graph in graph_set[0]:
+                graph_name = ", ".join(graph[1])
+                acc_items.append(
+                    dbc.AccordionItem(
+                        dcc.Graph(
+                            id={"type": "graph-telemetry", "index": graph_name},
+                            figure=graph[0]
+                        ),
+                        title=(f"Test{_plural(graph[1])}: {graph_name}"),
+                        class_name="g-0 p-0"
+                    )
+                )
+            panels.append(
+                dbc.AccordionItem(
+                    [
+                        dbc.Row(
+                            dbc.Accordion(
+                                children=acc_items,
+                                class_name="g-0 p-0",
+                                always_open=True,
+                                flush=True,
+                                active_item=\
+                                    [f"item-{i}" for i in range(len(acc_items))]
+                            ),
+                            class_name="g-0 p-0"
+                        ),
+                        dbc.Row(
+                            html.Div(
+                                [
+                                    dbc.Button(
+                                        "Remove",
+                                        id={
+                                            "type": "tm-btn-remove",
+                                            "index": idx
+                                        },
+                                        class_name="me-1",
+                                        color="danger",
+                                        style={"padding": "0rem 1rem"}
+                                    ),
+                                    dbc.Button(
+                                        "Download Data",
+                                        id={
+                                            "type": "tm-btn-download",
+                                            "index": idx
+                                        },
+                                        class_name="me-1",
+                                        color="info",
+                                        style={"padding": "0rem 1rem"}
+                                    )
+                                ],
+                            className=\
+                                "d-grid gap-0 d-md-flex justify-content-md-end"
+                            ),
+                            class_name="g-0 p-0"
+                        )
+                    ],
+                    class_name="g-0 p-0",
+                    title=(
+                        f"Metric{_plural(graph_set[1])}: ",
+                        ", ".join(graph_set[1])
+                    )
+                )
+            )
 
 
-            :param kwargs: key - value pairs to be set.
-            :type kwargs: dict
-            :raises KeyError: If the key in kwargs is not present in the Control
-                panel.
-            """
-            for key, val in kwargs.items():
-                if key in self._panel:
-                    self._panel[key] = val
-                else:
-                    raise KeyError(f"The key {key} is not defined.")
+        return dbc.Col(
+            dbc.Accordion(
+                panels,
+                class_name="g-0 p-1",
+                always_open=True,
+                active_item=[f"item-{i}" for i in range(len(panels))]
+            )
+        )
 
 
-        def get(self, key: str) -> any:
-            """Returns the value of a key from the Control panel.
+    @staticmethod
+    def _get_telemetry_step_1() -> list:
+        """Return the content of the modal window used in the step 1 of metrics
+        selection.
 
 
-            :param key: The key which value should be returned.
-            :type key: str
-            :returns: The value of the key.
-            :rtype: any
-            :raises KeyError: If the key in kwargs is not present in the Control
-                panel.
-            """
-            return self._panel[key]
+        :returns: A list of dbc rows with 'input' and 'search output'.
+        :rtype: list
+        """
+        return [
+            dbc.Row(
+                class_name="g-0 p-1",
+                children=[
+                    dbc.Input(
+                        id={"type": "telemetry-search-in", "index": 0},
+                        placeholder="Start typing a metric name...",
+                        type="text"
+                    )
+                ]
+            ),
+            dbc.Row(
+                class_name="g-0 p-1",
+                children=[
+                    dbc.ListGroup(
+                        class_name="overflow-auto p-0",
+                        id={"type": "telemetry-search-out", "index": 0},
+                        children=[],
+                        style={"max-height": "14em"},
+                        flush=True
+                    )
+                ]
+            )
+        ]
 
 
-        def values(self) -> tuple:
-            """Returns the values from the Control panel as a list.
+    @staticmethod
+    def _get_telemetry_step_2() -> list:
+        """Return the content of the modal window used in the step 2 of metrics
+        selection.
 
 
-            :returns: The values from the Control panel.
-            :rtype: list
-            """
-            return tuple(self._panel.values())
+        :returns: A list of dbc rows with 'container with dynamic dropdowns' and
+            'search output'.
+        :rtype: list
+        """
+        return [
+            dbc.Row(
+                id={"type": "tm-container", "index": 0},
+                class_name="g-0 p-1",
+                children=["Add content here."]
+            ),
+            dbc.Row(
+                class_name="g-0 p-2",
+                children=[
+                    dbc.Checkbox(
+                        id={"type": "cb-all-in-one", "index": 0},
+                        label="All Metrics in one Graph"
+                    ),
+                ]
+            ),
+            dbc.Row(
+                class_name="g-0 p-1",
+                children=[
+                    dbc.Textarea(
+                        id={"type": "tm-list-metrics", "index": 0},
+                        rows=20,
+                        size="sm",
+                        wrap="off",
+                        readonly=True
+                    )
+                ]
+            )
+        ]
 
     def callbacks(self, app):
         """Callbacks for the whole application.
 
     def callbacks(self, app):
         """Callbacks for the whole application.
@@ -723,185 +1017,108 @@ class Layout:
         :type app: Flask
         """
 
         :type app: Flask
         """
 
-        def _generate_plotting_area(figs: tuple, url: str) -> tuple:
-            """Generate the plotting area with all its content.
-
-            :param figs: Figures to be placed in the plotting area.
-            :param utl: The URL to be placed in the plotting area bellow the
-                tables.
-            :type figs: tuple of plotly.graph_objects.Figure
-            :type url: str
-            :returns: tuple of elements to be shown in the plotting area.
-            :rtype: tuple(dcc.Graph, dcc.Graph, list(dbc.Col, dbc.Col))
-            """
-
-            (fig_tput, fig_lat) = figs
-
-            row_fig_tput = C.PLACEHOLDER
-            row_fig_lat = C.PLACEHOLDER
-            row_btn_dwnld = C.PLACEHOLDER
-
-            if fig_tput:
-                row_fig_tput = [
-                    dcc.Graph(
-                        id={"type": "graph", "index": "tput"},
-                        figure=fig_tput
-                    )
-                ]
-                row_btn_dwnld = [
-                    dbc.Col(  # Download
-                        width=2,
-                        children=[
-                            dcc.Loading(children=[
-                                dbc.Button(
-                                    id="btn-download-data",
-                                    children=show_tooltip(self._tooltips,
-                                        "help-download", "Download Data"),
-                                    class_name="me-1",
-                                    color="info"
-                                ),
-                                dcc.Download(id="download-data")
-                            ]),
-                        ]
-                    ),
-                    dbc.Col(  # Show URL
-                        width=10,
-                        children=[
-                            dbc.InputGroup(
-                                class_name="me-1",
-                                children=[
-                                    dbc.InputGroupText(
-                                        style=C.URL_STYLE,
-                                        children=show_tooltip(self._tooltips,
-                                            "help-url", "URL", "input-url")
-                                    ),
-                                    dbc.Input(
-                                        id="input-url",
-                                        readonly=True,
-                                        type="url",
-                                        style=C.URL_STYLE,
-                                        value=url
-                                    )
-                                ]
-                            )
-                        ]
-                    )
-                ]
-            if fig_lat:
-                row_fig_lat = [
-                    dcc.Graph(
-                        id={"type": "graph", "index": "lat"},
-                        figure=fig_lat
-                    )
-                ]
-
-            return row_fig_tput, row_fig_lat, row_btn_dwnld
-
         @app.callback(
         @app.callback(
-            Output("control-panel", "data"),  # Store
-            Output("selected-tests", "data"),  # Store
-            Output("row-graph-tput", "children"),
-            Output("row-graph-lat", "children"),
-            Output("row-btn-download", "children"),
+            Output("store", "data"),
+            Output("plotting-area-trending", "children"),
+            Output("plotting-area-telemetry", "children"),
+            Output("col-plotting-area", "style"),
             Output("row-card-sel-tests", "style"),
             Output("row-btns-sel-tests", "style"),
             Output("row-card-sel-tests", "style"),
             Output("row-btns-sel-tests", "style"),
-            Output("dd-ctrl-dut", "value"),
-            Output("dd-ctrl-phy", "options"),
-            Output("dd-ctrl-phy", "disabled"),
-            Output("dd-ctrl-phy", "value"),
-            Output("dd-ctrl-area", "options"),
-            Output("dd-ctrl-area", "disabled"),
-            Output("dd-ctrl-area", "value"),
-            Output("dd-ctrl-test", "options"),
-            Output("dd-ctrl-test", "disabled"),
-            Output("dd-ctrl-test", "value"),
-            Output("cl-ctrl-core", "options"),
-            Output("cl-ctrl-core", "value"),
-            Output("cl-ctrl-core-all", "value"),
-            Output("cl-ctrl-core-all", "options"),
-            Output("cl-ctrl-framesize", "options"),
-            Output("cl-ctrl-framesize", "value"),
-            Output("cl-ctrl-framesize-all", "value"),
-            Output("cl-ctrl-framesize-all", "options"),
-            Output("cl-ctrl-testtype", "options"),
-            Output("cl-ctrl-testtype", "value"),
-            Output("cl-ctrl-testtype-all", "value"),
-            Output("cl-ctrl-testtype-all", "options"),
-            Output("btn-ctrl-add", "disabled"),
-            Output("cl-ctrl-normalize", "value"),
-            Output("cl-selected", "options"),  # User selection
-            State("control-panel", "data"),  # Store
-            State("selected-tests", "data"),  # Store
-            State("cl-selected", "value"),  # User selection
-            Input("dd-ctrl-dut", "value"),
-            Input("dd-ctrl-phy", "value"),
-            Input("dd-ctrl-area", "value"),
-            Input("dd-ctrl-test", "value"),
-            Input("cl-ctrl-core", "value"),
-            Input("cl-ctrl-core-all", "value"),
-            Input("cl-ctrl-framesize", "value"),
-            Input("cl-ctrl-framesize-all", "value"),
-            Input("cl-ctrl-testtype", "value"),
-            Input("cl-ctrl-testtype-all", "value"),
-            Input("cl-ctrl-normalize", "value"),
-            Input("btn-ctrl-add", "n_clicks"),
-            Input("btn-sel-remove", "n_clicks"),
-            Input("btn-sel-remove-all", "n_clicks"),
-            Input("url", "href")
+            Output("row-btns-add-tm", "style"),
+            Output("lg-selected", "children"),
+            Output({"type": "telemetry-search-out", "index": ALL}, "children"),
+            Output({"type": "plot-mod-telemetry", "index": ALL}, "is_open"),
+            Output({"type": "telemetry-btn", "index": ALL}, "disabled"),
+            Output({"type": "tm-container", "index": ALL}, "children"),
+            Output({"type": "tm-list-metrics", "index": ALL}, "value"),
+            Output({"type": "ctrl-dd", "index": "dut"}, "value"),
+            Output({"type": "ctrl-dd", "index": "phy"}, "options"),
+            Output({"type": "ctrl-dd", "index": "phy"}, "disabled"),
+            Output({"type": "ctrl-dd", "index": "phy"}, "value"),
+            Output({"type": "ctrl-dd", "index": "area"}, "options"),
+            Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
+            Output({"type": "ctrl-dd", "index": "area"}, "value"),
+            Output({"type": "ctrl-dd", "index": "test"}, "options"),
+            Output({"type": "ctrl-dd", "index": "test"}, "disabled"),
+            Output({"type": "ctrl-dd", "index": "test"}, "value"),
+            Output({"type": "ctrl-cl", "index": "core"}, "options"),
+            Output({"type": "ctrl-cl", "index": "core"}, "value"),
+            Output({"type": "ctrl-cl", "index": "core-all"}, "value"),
+            Output({"type": "ctrl-cl", "index": "core-all"}, "options"),
+            Output({"type": "ctrl-cl", "index": "frmsize"}, "options"),
+            Output({"type": "ctrl-cl", "index": "frmsize"}, "value"),
+            Output({"type": "ctrl-cl", "index": "frmsize-all"}, "value"),
+            Output({"type": "ctrl-cl", "index": "frmsize-all"}, "options"),
+            Output({"type": "ctrl-cl", "index": "tsttype"}, "options"),
+            Output({"type": "ctrl-cl", "index": "tsttype"}, "value"),
+            Output({"type": "ctrl-cl", "index": "tsttype-all"}, "value"),
+            Output({"type": "ctrl-cl", "index": "tsttype-all"}, "options"),
+            Output({"type": "ctrl-btn", "index": "add-test"}, "disabled"),
+            Output("normalize", "value"),
+
+            State("store", "data"),
+            State({"type": "sel-cl", "index": ALL}, "value"),
+            State({"type": "cb-all-in-one", "index": ALL}, "value"),
+            State({"type": "telemetry-search-out", "index": ALL}, "children"),
+            State({"type": "plot-mod-telemetry", "index": ALL}, "is_open"),
+            State({"type": "telemetry-btn", "index": ALL}, "disabled"),
+            State({"type": "tm-container", "index": ALL}, "children"),
+            State({"type": "tm-list-metrics", "index": ALL}, "value"),
+
+            Input("url", "href"),
+            Input({"type": "tele-cl", "index": ALL}, "value"),
+            Input({"type": "tm-dd", "index": ALL}, "value"),
+
+            Input("normalize", "value"),
+            Input({"type": "telemetry-search-in", "index": ALL}, "value"),
+            Input({"type": "telemetry-btn", "index": ALL}, "n_clicks"),
+            Input({"type": "tm-btn-remove", "index": ALL}, "n_clicks"),
+            Input({"type": "ctrl-dd", "index": ALL}, "value"),
+            Input({"type": "ctrl-cl", "index": ALL}, "value"),
+            Input({"type": "ctrl-btn", "index": ALL}, "n_clicks"),
+
+            prevent_initial_call=True
         )
         )
-        def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
-            dd_dut: str, dd_phy: str, dd_area: str, dd_test: str, cl_core: list,
-            cl_core_all: list, cl_framesize: list, cl_framesize_all: list,
-            cl_testtype: list, cl_testtype_all: list, cl_normalize: list,
-            btn_add: int, btn_remove: int,
-            btn_remove_all: int, href: str) -> tuple:
+        def _update_application(
+                store: dict,
+                lst_sel: list,
+                all_in_one: list,
+                search_out: list,
+                is_open: list,
+                tm_btns_disabled: list,
+                tm_dd: list,
+                list_metrics: list,
+                href: str,
+                cl_metrics: list,
+                tm_dd_in: list,
+                *_
+            ) -> tuple:
             """Update the application when the event is detected.
             """Update the application when the event is detected.
-
-            :param cp_data: Current status of the control panel stored in
-                browser.
-            :param store_sel: List of tests selected by user stored in the
-                browser.
-            :param list_sel: List of tests selected by the user shown in the
-                checklist.
-            :param dd_dut: Input - DUTs.
-            :param dd_phy: Input - topo- arch-nic-driver.
-            :param dd_area: Input - Tested area.
-            :param dd_test: Input - Test.
-            :param cl_core: Input - Number of cores.
-            :param cl_core_all: Input - All numbers of cores.
-            :param cl_framesize: Input - Frame sizes.
-            :param cl_framesize_all: Input - All frame sizes.
-            :param cl_testtype: Input - Test type (NDR, PDR, MRR).
-            :param cl_testtype_all: Input - All test types.
-            :param cl_normalize: Input - Normalize the results.
-            :param btn_add: Input - Button "Add Selected" tests.
-            :param btn_remove: Input - Button "Remove selected" tests.
-            :param btn_remove_all: Input - Button "Remove All" tests.
-            :param href: Input - The URL provided by the browser.
-            :type cp_data: dict
-            :type store_sel: list
-            :type list_sel: list
-            :type dd_dut: str
-            :type dd_phy: str
-            :type dd_area: str
-            :type dd_test: str
-            :type cl_core: list
-            :type cl_core_all: list
-            :type cl_framesize: list
-            :type cl_framesize_all: list
-            :type cl_testtype: list
-            :type cl_testtype_all: list
-            :type cl_normalize: list
-            :type btn_add: int
-            :type btn_remove: int
-            :type btn_remove_all: int
-            :type href: str
-            :returns: New values for web page elements.
-            :rtype: tuple
             """
 
             """
 
-            ctrl_panel = self.ControlPanel(cp_data)
-            norm = cl_normalize
+            if store is None:
+                store = {
+                    "control-panel": dict(),
+                    "selected-tests": list(),
+                    "telemetry-data": dict(),
+                    "selected-metrics": dict(),
+                    "telemetry-panels": list(),
+                    "telemetry-all-in-one": list(),
+                    "url": str()
+                }
+
+            ctrl_panel = ControlPanel(
+                CP_PARAMS,
+                store.get("control-panel", dict())
+            )
+            store_sel = store["selected-tests"]
+            tm_data = store["telemetry-data"]
+            tm_user = store["selected-metrics"]
+            tm_panels = store["telemetry-panels"]
+            tm_all_in_one = store["telemetry-all-in-one"]
+
+            plotting_area_telemetry = no_update
+            on_draw = [False, False]  # 0 --> trending, 1 --> telemetry
 
             # Parse the url:
             parsed_url = url_decode(href)
 
             # Parse the url:
             parsed_url = url_decode(href)
@@ -910,219 +1127,258 @@ class Layout:
             else:
                 url_params = None
 
             else:
                 url_params = None
 
-            row_fig_tput = no_update
-            row_fig_lat = no_update
-            row_btn_dwnld = no_update
-            row_card_sel_tests = no_update
-            row_btns_sel_tests = no_update
-
-            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
-
-            if trigger_id == "dd-ctrl-dut":
+            if tm_user is None:
+                # Telemetry user data
+                # The data provided by user or result of user action
+                tm_user = {
+                    # List of unique metrics:
+                    "unique_metrics": list(),
+                    # List of metrics selected by user:
+                    "selected_metrics": list(),
+                    # Labels from metrics selected by user (key: label name,
+                    # value: list of all possible values):
+                    "unique_labels": dict(),
+                    # Labels selected by the user (subset of 'unique_labels'):
+                    "selected_labels": dict(),
+                    # All unique metrics with labels (output from the step 1)
+                    # converted from pandas dataframe to dictionary.
+                    "unique_metrics_with_labels": dict(),
+                    # Metrics with labels selected by the user using dropdowns.
+                    "selected_metrics_with_labels": dict()
+                }
+            tm = TelemetryData(store_sel) if store_sel else TelemetryData()
+
+            trigger = Trigger(callback_context.triggered)
+            if trigger.type == "url" and url_params:
+                telemetry = None
                 try:
                 try:
-                    options = \
-                        generate_options(sorted(self.spec_tbs[dd_dut].keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-ctrl-dut-value": dd_dut,
-                    "dd-ctrl-phy-value": str(),
-                    "dd-ctrl-phy-options": options,
-                    "dd-ctrl-phy-disabled": disabled,
-                    "dd-ctrl-area-value": str(),
-                    "dd-ctrl-area-options": list(),
-                    "dd-ctrl-area-disabled": True,
-                    "dd-ctrl-test-value": str(),
-                    "dd-ctrl-test-options": list(),
-                    "dd-ctrl-test-disabled": True,
-                    "cl-ctrl-core-options": list(),
-                    "cl-ctrl-core-value": list(),
-                    "cl-ctrl-core-all-value": list(),
-                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-framesize-options": list(),
-                    "cl-ctrl-framesize-value": list(),
-                    "cl-ctrl-framesize-all-value": list(),
-                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-testtype-options": list(),
-                    "cl-ctrl-testtype-value": list(),
-                    "cl-ctrl-testtype-all-value": list(),
-                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
-                })
-            elif trigger_id == "dd-ctrl-phy":
-                try:
-                    dut = ctrl_panel.get("dd-ctrl-dut-value")
-                    phy = self.spec_tbs[dut][dd_phy]
-                    options = [{"label": label(v), "value": v} \
-                        for v in sorted(phy.keys())]
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-ctrl-phy-value": dd_phy,
-                    "dd-ctrl-area-value": str(),
-                    "dd-ctrl-area-options": options,
-                    "dd-ctrl-area-disabled": disabled,
-                    "dd-ctrl-test-value": str(),
-                    "dd-ctrl-test-options": list(),
-                    "dd-ctrl-test-disabled": True,
-                    "cl-ctrl-core-options": list(),
-                    "cl-ctrl-core-value": list(),
-                    "cl-ctrl-core-all-value": list(),
-                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-framesize-options": list(),
-                    "cl-ctrl-framesize-value": list(),
-                    "cl-ctrl-framesize-all-value": list(),
-                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-testtype-options": list(),
-                    "cl-ctrl-testtype-value": list(),
-                    "cl-ctrl-testtype-all-value": list(),
-                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
-                })
-            elif trigger_id == "dd-ctrl-area":
-                try:
-                    dut = ctrl_panel.get("dd-ctrl-dut-value")
-                    phy = ctrl_panel.get("dd-ctrl-phy-value")
-                    area = self.spec_tbs[dut][phy][dd_area]
-                    options = generate_options(sorted(area.keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-ctrl-area-value": dd_area,
-                    "dd-ctrl-test-value": str(),
-                    "dd-ctrl-test-options": options,
-                    "dd-ctrl-test-disabled": disabled,
-                    "cl-ctrl-core-options": list(),
-                    "cl-ctrl-core-value": list(),
-                    "cl-ctrl-core-all-value": list(),
-                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-framesize-options": list(),
-                    "cl-ctrl-framesize-value": list(),
-                    "cl-ctrl-framesize-all-value": list(),
-                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-ctrl-testtype-options": list(),
-                    "cl-ctrl-testtype-value": list(),
-                    "cl-ctrl-testtype-all-value": list(),
-                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
-                })
-            elif trigger_id == "dd-ctrl-test":
-                dut = ctrl_panel.get("dd-ctrl-dut-value")
-                phy = ctrl_panel.get("dd-ctrl-phy-value")
-                area = ctrl_panel.get("dd-ctrl-area-value")
-                if all((dut, phy, area, dd_test, )):
-                    test = self.spec_tbs[dut][phy][area][dd_test]
+                    store_sel = literal_eval(url_params["store_sel"][0])
+                    normalize = literal_eval(url_params["norm"][0])
+                    telemetry = literal_eval(url_params["telemetry"][0])
+                    tm_all_in_one = literal_eval(url_params["all-in-one"][0])
+                    if not isinstance(telemetry, list):
+                        telemetry = [telemetry, ]
+                        tm_all_in_one = [tm_all_in_one, ]
+                except (KeyError, IndexError, AttributeError, ValueError):
+                    pass
+                if store_sel:
+                    last_test = store_sel[-1]
+                    test = self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                        [last_test["area"]][last_test["test"]]
                     ctrl_panel.set({
                     ctrl_panel.set({
-                        "dd-ctrl-test-value": dd_test,
-                        "cl-ctrl-core-options": \
-                            generate_options(sorted(test["core"])),
-                        "cl-ctrl-core-value": list(),
-                        "cl-ctrl-core-all-value": list(),
-                        "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
-                        "cl-ctrl-framesize-options": \
-                            generate_options(sorted(test["frame-size"])),
-                        "cl-ctrl-framesize-value": list(),
-                        "cl-ctrl-framesize-all-value": list(),
-                        "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
-                        "cl-ctrl-testtype-options": \
-                            generate_options(sorted(test["test-type"])),
-                        "cl-ctrl-testtype-value": list(),
-                        "cl-ctrl-testtype-all-value": list(),
-                        "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED,
+                        "dd-dut-val": last_test["dut"],
+                        "dd-phy-val": last_test["phy"],
+                        "dd-phy-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]].keys()
+                        ),
+                        "dd-phy-dis": False,
+                        "dd-area-val": last_test["area"],
+                        "dd-area-opt": [
+                            {"label": label(v), "value": v} for v in sorted(
+                                self._spec_tbs[last_test["dut"]]\
+                                    [last_test["phy"]].keys()
+                            )
+                        ],
+                        "dd-area-dis": False,
+                        "dd-test-val": last_test["test"],
+                        "dd-test-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                                [last_test["area"]].keys()
+                        ),
+                        "dd-test-dis": False,
+                        "cl-core-opt": generate_options(test["core"]),
+                        "cl-core-val": [last_test["core"].upper(), ],
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_ENABLED,
+                        "cl-frmsize-opt": generate_options(test["frame-size"]),
+                        "cl-frmsize-val": [last_test["framesize"].upper(), ],
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                        "cl-tsttype-opt": generate_options(test["test-type"]),
+                        "cl-tsttype-val": [last_test["testtype"].upper(), ],
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                        "cl-normalize-val": normalize,
+                        "btn-add-dis": False
                     })
                     })
-            elif trigger_id == "cl-ctrl-core":
-                val_sel, val_all = sync_checklists(
-                    options=ctrl_panel.get("cl-ctrl-core-options"),
-                    sel=cl_core,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-ctrl-core-value": val_sel,
-                    "cl-ctrl-core-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-core-all":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-core-options"),
-                    sel=list(),
-                    all=cl_core_all,
-                    id="all"
-                )
-                ctrl_panel.set({
-                    "cl-ctrl-core-value": val_sel,
-                    "cl-ctrl-core-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-framesize":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-framesize-options"),
-                    sel=cl_framesize,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-ctrl-framesize-value": val_sel,
-                    "cl-ctrl-framesize-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-framesize-all":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-framesize-options"),
-                    sel=list(),
-                    all=cl_framesize_all,
-                    id="all"
-                )
-                ctrl_panel.set({
-                    "cl-ctrl-framesize-value": val_sel,
-                    "cl-ctrl-framesize-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-testtype":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-testtype-options"),
-                    sel=cl_testtype,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-ctrl-testtype-value": val_sel,
-                    "cl-ctrl-testtype-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-testtype-all":
+                    on_draw[0] = True
+                    if telemetry:
+                        tm = TelemetryData(store_sel)
+                        tm.from_dataframe(self._data)
+                        tm_data = tm.to_json()
+                        tm.from_json(tm_data)
+                        tm_panels = telemetry
+                        on_draw[1] = True
+            elif trigger.type == "normalize":
+                ctrl_panel.set({"cl-normalize-val": trigger.value})
+                on_draw[0] = True
+            elif trigger.type == "ctrl-dd":
+                if trigger.idx == "dut":
+                    try:
+                        options = generate_options(
+                            self._spec_tbs[trigger.value].keys()
+                        )
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-dut-val": trigger.value,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": options,
+                        "dd-phy-dis": disabled,
+                        "dd-area-val": str(),
+                        "dd-area-opt": list(),
+                        "dd-area-dis": True,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "phy":
+                    try:
+                        dut = ctrl_panel.get("dd-dut-val")
+                        phy = self._spec_tbs[dut][trigger.value]
+                        options = [{"label": label(v), "value": v} \
+                            for v in sorted(phy.keys())]
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-phy-val": trigger.value,
+                        "dd-area-val": str(),
+                        "dd-area-opt": options,
+                        "dd-area-dis": disabled,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })  
+                elif trigger.idx == "area":
+                    try:
+                        dut = ctrl_panel.get("dd-dut-val")
+                        phy = ctrl_panel.get("dd-phy-val")
+                        area = self._spec_tbs[dut][phy][trigger.value]
+                        options = generate_options(area.keys())
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-area-val": trigger.value,
+                        "dd-test-val": str(),
+                        "dd-test-opt": options,
+                        "dd-test-dis": disabled,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "test":
+                    dut = ctrl_panel.get("dd-dut-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    if all((dut, phy, area, trigger.value, )):
+                        test = self._spec_tbs[dut][phy][area][trigger.value]
+                        ctrl_panel.set({
+                            "dd-test-val": trigger.value,
+                            "cl-core-opt": generate_options(test["core"]),
+                            "cl-core-val": list(),
+                            "cl-core-all-val": list(),
+                            "cl-core-all-opt": C.CL_ALL_ENABLED,
+                            "cl-frmsize-opt": \
+                                generate_options(test["frame-size"]),
+                            "cl-frmsize-val": list(),
+                            "cl-frmsize-all-val": list(),
+                            "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                            "cl-tsttype-opt": \
+                                generate_options(test["test-type"]),
+                            "cl-tsttype-val": list(),
+                            "cl-tsttype-all-val": list(),
+                            "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                            "btn-add-dis": True
+                        })
+            elif trigger.type == "ctrl-cl":
+                param = trigger.idx.split("-")[0]
+                if "-all" in trigger.idx:
+                    c_sel, c_all, c_id = list(), trigger.value, "all"
+                else:
+                    c_sel, c_all, c_id = trigger.value, list(), str()
                 val_sel, val_all = sync_checklists(
                 val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-testtype-options"),
-                    sel=list(),
-                    all=cl_testtype_all,
-                    id="all"
+                    options=ctrl_panel.get(f"cl-{param}-opt"),
+                    sel=c_sel,
+                    all=c_all,
+                    id=c_id
                 )
                 ctrl_panel.set({
                 )
                 ctrl_panel.set({
-                    "cl-ctrl-testtype-value": val_sel,
-                    "cl-ctrl-testtype-all-value": val_all,
+                    f"cl-{param}-val": val_sel,
+                    f"cl-{param}-all-val": val_all,
                 })
                 })
-            elif trigger_id == "btn-ctrl-add":
-                _ = btn_add
-                dut = ctrl_panel.get("dd-ctrl-dut-value")
-                phy = ctrl_panel.get("dd-ctrl-phy-value")
-                area = ctrl_panel.get("dd-ctrl-area-value")
-                test = ctrl_panel.get("dd-ctrl-test-value")
-                cores = ctrl_panel.get("cl-ctrl-core-value")
-                framesizes = ctrl_panel.get("cl-ctrl-framesize-value")
-                testtypes = ctrl_panel.get("cl-ctrl-testtype-value")
-                # Add selected test to the list of tests in store:
-                if all((dut, phy, area, test, cores, framesizes, testtypes)):
+                if all((ctrl_panel.get("cl-core-val"), 
+                        ctrl_panel.get("cl-frmsize-val"),
+                        ctrl_panel.get("cl-tsttype-val"), )):
+                    ctrl_panel.set({"btn-add-dis": False})
+                else:
+                    ctrl_panel.set({"btn-add-dis": True})
+            elif trigger.type == "ctrl-btn":
+                on_draw[0] = True
+                if trigger.idx == "add-test":
+                    dut = ctrl_panel.get("dd-dut-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    test = ctrl_panel.get("dd-test-val")
+                    # Add selected test(s) to the list of tests in store:
                     if store_sel is None:
                         store_sel = list()
                     if store_sel is None:
                         store_sel = list()
-                    for core in cores:
-                        for framesize in framesizes:
-                            for ttype in testtypes:
+                    for core in ctrl_panel.get("cl-core-val"):
+                        for framesize in ctrl_panel.get("cl-frmsize-val"):
+                            for ttype in ctrl_panel.get("cl-tsttype-val"):
                                 if dut == "trex":
                                     core = str()
                                 tid = "-".join((
                                 if dut == "trex":
                                     core = str()
                                 tid = "-".join((
-                                    dut, phy.replace('af_xdp', 'af-xdp'), area,
-                                    framesize.lower(), core.lower(), test,
+                                    dut,
+                                    phy.replace('af_xdp', 'af-xdp'),
+                                    area,
+                                    framesize.lower(),
+                                    core.lower(),
+                                    test,
                                     ttype.lower()
                                 ))
                                     ttype.lower()
                                 ))
-                                if tid not in [itm["id"] for itm in store_sel]:
+                                if tid not in [i["id"] for i in store_sel]:
                                     store_sel.append({
                                         "id": tid,
                                         "dut": dut,
                                     store_sel.append({
                                         "id": tid,
                                         "dut": dut,
@@ -1134,126 +1390,317 @@ class Layout:
                                         "testtype": ttype.lower()
                                     })
                     store_sel = sorted(store_sel, key=lambda d: d["id"])
                                         "testtype": ttype.lower()
                                     })
                     store_sel = sorted(store_sel, key=lambda d: d["id"])
-                    row_card_sel_tests = C.STYLE_ENABLED
-                    row_btns_sel_tests = C.STYLE_ENABLED
                     if C.CLEAR_ALL_INPUTS:
                         ctrl_panel.set(ctrl_panel.defaults)
                     if C.CLEAR_ALL_INPUTS:
                         ctrl_panel.set(ctrl_panel.defaults)
-            elif trigger_id == "btn-sel-remove-all":
-                _ = btn_remove_all
-                row_fig_tput = C.PLACEHOLDER
-                row_fig_lat = C.PLACEHOLDER
-                row_btn_dwnld = C.PLACEHOLDER
-                row_card_sel_tests = C.STYLE_DISABLED
-                row_btns_sel_tests = C.STYLE_DISABLED
-                store_sel = list()
-                ctrl_panel.set({"cl-selected-options": list()})
-            elif trigger_id == "btn-sel-remove":
-                _ = btn_remove
-                if list_sel:
+                elif trigger.idx == "rm-test" and lst_sel:
                     new_store_sel = list()
                     new_store_sel = list()
-                    for item in store_sel:
-                        if item["id"] not in list_sel:
+                    for idx, item in enumerate(store_sel):
+                        if not lst_sel[idx]:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
                             new_store_sel.append(item)
                     store_sel = new_store_sel
-            elif trigger_id == "url":
-                if url_params:
-                    try:
-                        store_sel = literal_eval(url_params["store_sel"][0])
-                        norm = literal_eval(url_params["norm"][0])
-                    except (KeyError, IndexError):
-                        pass
-                    if store_sel:
-                        row_card_sel_tests = C.STYLE_ENABLED
-                        row_btns_sel_tests = C.STYLE_ENABLED
-                        last_test = store_sel[-1]
-                        test = self.spec_tbs[last_test["dut"]]\
-                            [last_test["phy"]][last_test["area"]]\
-                                [last_test["test"]]
-                        ctrl_panel.set({
-                            "dd-ctrl-dut-value": last_test["dut"],
-                            "dd-ctrl-phy-value": last_test["phy"],
-                            "dd-ctrl-phy-options": generate_options(sorted(
-                                self.spec_tbs[last_test["dut"]].keys())),
-                            "dd-ctrl-phy-disabled": False,
-                            "dd-ctrl-area-value": last_test["area"],
-                            "dd-ctrl-area-options": [
-                                {"label": label(v), "value": v} \
-                                    for v in sorted(
-                                        self.spec_tbs[last_test["dut"]]\
-                                            [last_test["phy"]].keys())
-                            ],
-                            "dd-ctrl-area-disabled": False,
-                            "dd-ctrl-test-value": last_test["test"],
-                            "dd-ctrl-test-options": generate_options(sorted(
-                                self.spec_tbs[last_test["dut"]]\
-                                    [last_test["phy"]]\
-                                        [last_test["area"]].keys())),
-                            "dd-ctrl-test-disabled": False,
-                            "cl-ctrl-core-options": generate_options(sorted(
-                                test["core"])),
-                            "cl-ctrl-core-value": [last_test["core"].upper(), ],
-                            "cl-ctrl-core-all-value": list(),
-                            "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
-                            "cl-ctrl-framesize-options": generate_options(
-                                sorted(test["frame-size"])),
-                            "cl-ctrl-framesize-value": \
-                                [last_test["framesize"].upper(), ],
-                            "cl-ctrl-framesize-all-value": list(),
-                            "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
-                            "cl-ctrl-testtype-options": generate_options(sorted(
-                                test["test-type"])),
-                            "cl-ctrl-testtype-value": \
-                                [last_test["testtype"].upper(), ],
-                            "cl-ctrl-testtype-all-value": list(),
-                            "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED
-                        })
+                elif trigger.idx == "rm-test-all":
+                    store_sel = list()
+            elif trigger.type == "telemetry-btn":
+                if trigger.idx in ("open", "back"):
+                    tm.from_dataframe(self._data)
+                    tm_data = tm.to_json()
+                    tm_user["unique_metrics"] = tm.unique_metrics
+                    tm_user["selected_metrics"] = list()
+                    tm_user["unique_labels"] = dict()
+                    tm_user["selected_labels"] = dict()
+                    search_out = (
+                        get_list_group_items(tm_user["unique_metrics"],
+                            "tele-cl", False),
+                    )
+                    is_open = (True, False)
+                    tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+                elif trigger.idx == "select":
+                    tm.from_json(tm_data)
+                    if any(cl_metrics):
+                        if not tm_user["selected_metrics"]:
+                            tm_user["selected_metrics"] = \
+                                tm_user["unique_metrics"]
+                        metrics = [a for a, b in \
+                            zip(tm_user["selected_metrics"], cl_metrics) if b]
+                        tm_user["selected_metrics"] = metrics
+                        tm_user["unique_labels"] = \
+                            tm.get_selected_labels(metrics)
+                        tm_user["unique_metrics_with_labels"] = \
+                            tm.unique_metrics_with_labels
+                        list_metrics[0] = tm.str_metrics
+                        tm_dd[0] = _get_dd_container(tm_user["unique_labels"])
+                        if list_metrics[0]:
+                            tm_btns_disabled[1] = True
+                            tm_btns_disabled[4] = False
+                        is_open = (False, True)
+                    else:
+                        tm_user = None
+                        is_open = (False, False)
+                elif trigger.idx == "add":
+                    tm.from_json(tm_data)
+                    tm_panels.append(tm_user["selected_metrics_with_labels"])
+                    tm_all_in_one.append(all_in_one)
+                    is_open = (False, False)
+                    tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+                    on_draw = [True, True]
+                elif trigger.idx == "cancel":
+                    is_open = (False, False)
+                    tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+                elif trigger.idx == "rm-all":
+                    tm_panels = list()
+                    tm_all_in_one = list()
+                    tm_user = None
+                    is_open = (False, False)
+                    tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+                    plotting_area_telemetry = C.PLACEHOLDER
+            elif trigger.type == "telemetry-search-in":
+                tm.from_metrics(tm_user["unique_metrics"])
+                tm_user["selected_metrics"] = \
+                    tm.search_unique_metrics(trigger.value)
+                search_out = (get_list_group_items(
+                    tm_user["selected_metrics"],
+                    type="tele-cl",
+                    colorize=False
+                ), )
+                is_open = (True, False)
+            elif trigger.type == "tele-cl":
+                if any(cl_metrics):
+                    tm_btns_disabled[1] = False
+                else:
+                    tm_btns_disabled[1] = True
+                is_open = (True, False)
+            elif trigger.type == "tm-dd":
+                tm.from_metrics_with_labels(
+                    tm_user["unique_metrics_with_labels"]
+                )
+                selected = dict()
+                previous_itm = None
+                for itm in tm_dd_in:
+                    if itm is None:
+                        show_new = True
+                    elif isinstance(itm, str):
+                        show_new = False
+                        selected[itm] = list()
+                    elif isinstance(itm, list):
+                        if previous_itm is not None:
+                            selected[previous_itm] = itm
+                        show_new = True
+                    previous_itm = itm
+                tm_dd[0] = _get_dd_container(
+                    tm_user["unique_labels"],
+                    selected,
+                    show_new
+                )
+                sel_metrics = tm.filter_selected_metrics_by_labels(selected)
+                tm_user["selected_metrics_with_labels"] = sel_metrics.to_dict()
+                if not sel_metrics.empty:
+                    list_metrics[0] = tm.metrics_to_str(sel_metrics)
+                    tm_btns_disabled[5] = False
+                else:
+                    list_metrics[0] = str()
+            elif trigger.type == "tm-btn-remove":
+                del tm_panels[trigger.idx]
+                del tm_all_in_one[trigger.idx]
+                tm.from_json(tm_data)
+                on_draw = [True, True]
+
+            new_url_params = {
+                "store_sel": store_sel,
+                "norm": ctrl_panel.get("cl-normalize-val")
+            }
+            if tm_panels:
+                new_url_params["telemetry"] = tm_panels
+                new_url_params["all-in-one"] = tm_all_in_one
 
 
-            if trigger_id in ("btn-ctrl-add", "url", "btn-sel-remove",
-                    "cl-ctrl-normalize"):
+            if on_draw[0]:  # Trending
                 if store_sel:
                 if store_sel:
-                    row_fig_tput, row_fig_lat, row_btn_dwnld = \
-                        _generate_plotting_area(
-                            graph_trending(self.data, store_sel, self.layout,
-                                bool(norm)),
-                            gen_new_url(
-                                parsed_url,
-                                {
-                                    "store_sel": store_sel,
-                                    "norm": norm
-                                }
-                            )
-                        )
-                    ctrl_panel.set({
-                        "cl-selected-options": list_tests(store_sel)
-                    })
+                    lg_selected = get_list_group_items(store_sel, "sel-cl")
+                    plotting_area_trending = self._plotting_area_trending(
+                        store_sel,
+                        bool(ctrl_panel.get("cl-normalize-val"))
+                    )
+                    if on_draw[1]:  # Telemetry
+                        tm_graphs = list()
+                        for panel, aio in zip(tm_panels, tm_all_in_one):
+                            tm_graphs.append(graph_tm_trending(
+                                tm.select_tm_trending_data(panel),
+                                self._graph_layout,
+                                bool(aio[0])
+                            ))
+                        plotting_area_telemetry = \
+                            Layout._plotting_area_telemetry(tm_graphs)
+                    col_plotting_area = C.STYLE_ENABLED
+                    row_card_sel_tests = C.STYLE_ENABLED
+                    row_btns_sel_tests = C.STYLE_ENABLED
+                    row_btns_add_tm = C.STYLE_ENABLED
                 else:
                 else:
-                    row_fig_tput = C.PLACEHOLDER
-                    row_fig_lat = C.PLACEHOLDER
-                    row_btn_dwnld = C.PLACEHOLDER
+                    plotting_area_trending = no_update
+                    plotting_area_telemetry = C.PLACEHOLDER
+                    col_plotting_area = C.STYLE_DISABLED
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
+                    row_btns_add_tm = C.STYLE_DISABLED
+                    lg_selected = no_update
                     store_sel = list()
                     store_sel = list()
-                    ctrl_panel.set({"cl-selected-options": list()})
-
-            if ctrl_panel.get("cl-ctrl-core-value") and \
-                    ctrl_panel.get("cl-ctrl-framesize-value") and \
-                    ctrl_panel.get("cl-ctrl-testtype-value"):
-                disabled = False
+                    tm_panels = list()
+                    tm_all_in_one = list()
+                    tm_user = None
             else:
             else:
-                disabled = True
-            ctrl_panel.set({
-                "btn-ctrl-add-disabled": disabled,
-                "cl-normalize-value": norm
-            })
-
+                plotting_area_trending = no_update
+                col_plotting_area = no_update
+                row_card_sel_tests = no_update
+                row_btns_sel_tests = no_update
+                row_btns_add_tm = no_update
+                lg_selected = no_update
+
+            store["url"] = gen_new_url(parsed_url, new_url_params)
+            store["control-panel"] = ctrl_panel.panel
+            store["selected-tests"] = store_sel
+            store["telemetry-data"] = tm_data
+            store["selected-metrics"] = tm_user
+            store["telemetry-panels"] = tm_panels
+            store["telemetry-all-in-one"] = tm_all_in_one
             ret_val = [
             ret_val = [
-                ctrl_panel.panel, store_sel,
-                row_fig_tput, row_fig_lat, row_btn_dwnld,
-                row_card_sel_tests, row_btns_sel_tests
+                store,
+                plotting_area_trending,
+                plotting_area_telemetry,
+                col_plotting_area,
+                row_card_sel_tests,
+                row_btns_sel_tests,
+                row_btns_add_tm,
+                lg_selected,
+                search_out,
+                is_open,
+                tm_btns_disabled,
+                tm_dd,
+                list_metrics
             ]
             ]
-            ret_val.extend(ctrl_panel.values())
+            ret_val.extend(ctrl_panel.values)
             return ret_val
 
             return ret_val
 
+        @app.callback(
+            Output("plot-mod-url", "is_open"),
+            Output("mod-url", "children"),
+            State("store", "data"),
+            State("plot-mod-url", "is_open"),
+            Input("plot-btn-url", "n_clicks")
+        )
+        def toggle_plot_mod_url(store, is_open, n_clicks):
+            """Toggle the modal window with url.
+            """
+            if not store:
+                raise PreventUpdate
+
+            if n_clicks:
+                return not is_open, store.get("url", str())
+            return is_open, store["url"]
+
+        def _get_dd_container(
+                all_labels: dict,
+                selected_labels: dict=dict(),
+                show_new=True
+            ) -> list:
+            """Generate a container with dropdown selection boxes depenting on
+            the input data.
+
+            :param all_labels: A dictionary with unique labels and their
+                possible values.
+            :param selected_labels: A dictionalry with user selected lables and
+                their values.
+            :param show_new: If True, a dropdown selection box to add a new
+                label is displayed.
+            :type all_labels: dict
+            :type selected_labels: dict
+            :type show_new: bool
+            :returns: A list of dbc rows with dropdown selection boxes.
+            :rtype: list
+            """
+
+            def _row(
+                    id: str,
+                    lopts: list=list(),
+                    lval: str=str(),
+                    vopts: list=list(),
+                    vvals: list=list()
+                ) -> dbc.Row:
+                """Generates a dbc row with dropdown boxes.
+
+                :param id: A string added to the dropdown ID.
+                :param lopts: A list of options for 'label' dropdown.
+                :param lval: Value of 'label' dropdown.
+                :param vopts: A list of options for 'value' dropdown.
+                :param vvals: A list of values for 'value' dropdown.
+                :type id: str
+                :type lopts: list
+                :type lval: str
+                :type vopts: list
+                :type vvals: list
+                :returns: dbc row with dropdown boxes.
+                :rtype: dbc.Row
+                """
+                children = list()
+                if lopts:
+                    children.append(
+                        dbc.Col(
+                            width=6,
+                            children=[
+                                dcc.Dropdown(
+                                    id={
+                                        "type": "tm-dd",
+                                        "index": f"label-{id}"
+                                    },
+                                    placeholder="Select a label...",
+                                    optionHeight=20,
+                                    multi=False,
+                                    options=lopts,
+                                    value=lval if lval else None
+                                )
+                            ]
+                        )
+                    )
+                    if vopts:
+                        children.append(
+                            dbc.Col(
+                                width=6,
+                                children=[
+                                    dcc.Dropdown(
+                                        id={
+                                            "type": "tm-dd",
+                                            "index": f"value-{id}"
+                                        },
+                                        placeholder="Select a value...",
+                                        optionHeight=20,
+                                        multi=True,
+                                        options=vopts,
+                                        value=vvals if vvals else None
+                                    )
+                                ]
+                            )
+                        )
+
+                return dbc.Row(class_name="g-0 p-1", children=children)
+
+            container = list()
+
+            # Display rows with items in 'selected_labels'; label on the left,
+            # values on the right:
+            keys_left = list(all_labels.keys())
+            for idx, label in enumerate(selected_labels.keys()):
+                container.append(_row(
+                    id=idx,
+                    lopts=deepcopy(keys_left),
+                    lval=label,
+                    vopts=all_labels[label],
+                    vvals=selected_labels[label]
+                ))
+                keys_left.remove(label)
+
+            # Display row with dd with labels on the left, right side is empty:
+            if show_new and keys_left:
+                container.append(_row(id="new", lopts=keys_left))
+
+            return container
+
         @app.callback(
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
         @app.callback(
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
@@ -1271,14 +1718,13 @@ class Layout:
                 information to show the offcanvas.
             :rtype: tuple(list, list, bool)
             """
                 information to show the offcanvas.
             :rtype: tuple(list, list, bool)
             """
+
+            trigger = Trigger(callback_context.triggered)
+
             try:
             try:
-                trigger_id = loads(
-                    callback_context.triggered[0]["prop_id"].split(".")[0]
-                )["index"]
-                idx = 0 if trigger_id == "tput" else 1
+                idx = 0 if trigger.idx == "tput" else 1
                 graph_data = graph_data[idx]["points"][0]
                 graph_data = graph_data[idx]["points"][0]
-            except (JSONDecodeError, IndexError, KeyError, ValueError,
-                    TypeError):
+            except (IndexError, KeyError, ValueError, TypeError):
                 raise PreventUpdate
 
             metadata = no_update
                 raise PreventUpdate
 
             metadata = no_update
@@ -1289,9 +1735,9 @@ class Layout:
                     [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
                 ) for x in graph_data.get("text", "").split("<br>")
             ]
                     [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
                 ) for x in graph_data.get("text", "").split("<br>")
             ]
-            if trigger_id == "tput":
+            if trigger.idx == "tput":
                 title = "Throughput"
                 title = "Throughput"
-            elif trigger_id == "lat":
+            elif trigger.idx == "lat":
                 title = "Latency"
                 hdrh_data = graph_data.get("customdata", None)
                 if hdrh_data:
                 title = "Latency"
                 hdrh_data = graph_data.get("customdata", None)
                 if hdrh_data:
@@ -1303,12 +1749,15 @@ class Layout:
                                 dcc.Graph(
                                     id="hdrh-latency-graph",
                                     figure=graph_hdrh_latency(
                                 dcc.Graph(
                                     id="hdrh-latency-graph",
                                     figure=graph_hdrh_latency(
-                                        hdrh_data, self.layout
+                                        hdrh_data, self._graph_layout
                                     )
                                 )
                             ])
                         ])
                     ]
                                     )
                                 )
                             ])
                         ])
                     ]
+            else:
+                raise PreventUpdate
+
             metadata = [
                 dbc.Card(
                     class_name="gy-2 p-0",
             metadata = [
                 dbc.Card(
                     class_name="gy-2 p-0",
@@ -1333,35 +1782,51 @@ class Layout:
             return metadata, graph, True
 
         @app.callback(
             return metadata, graph, True
 
         @app.callback(
-            Output("download-data", "data"),
-            State("selected-tests", "data"),
-            Input("btn-download-data", "n_clicks"),
+            Output("download-trending-data", "data"),
+            State("store", "data"),
+            Input("plot-btn-download", "n_clicks"),
+            Input({"type": "tm-btn-download", "index": ALL}, "n_clicks"),
             prevent_initial_call=True
         )
             prevent_initial_call=True
         )
-        def _download_data(store_sel, n_clicks):
+        def _download_data(store: list, *_) -> dict:
             """Download the data
 
             :param store_sel: List of tests selected by user stored in the
                 browser.
             """Download the data
 
             :param store_sel: List of tests selected by user stored in the
                 browser.
-            :param n_clicks: Number of clicks on the button "Download".
             :type store_sel: list
             :type store_sel: list
-            :type n_clicks: int
             :returns: dict of data frame content (base64 encoded) and meta data
                 used by the Download component.
             :rtype: dict
             """
 
             :returns: dict of data frame content (base64 encoded) and meta data
                 used by the Download component.
             :rtype: dict
             """
 
-            if not n_clicks:
+            if not store:
                 raise PreventUpdate
                 raise PreventUpdate
-
-            if not store_sel:
+            if not store["selected-tests"]:
                 raise PreventUpdate
                 raise PreventUpdate
-
+            
             df = pd.DataFrame()
             df = pd.DataFrame()
-            for itm in store_sel:
-                sel_data = select_trending_data(self.data, itm)
-                if sel_data is None:
-                    continue
-                df = pd.concat([df, sel_data], ignore_index=True)
+            
+            trigger = Trigger(callback_context.triggered)
+            if not trigger.value:
+                raise PreventUpdate
+            
+            if trigger.type == "plot-btn-download":
+                data = list()
+                for itm in store["selected-tests"]:
+                    sel_data = select_trending_data(self._data, itm)
+                    if sel_data is None:
+                        continue
+                    data.append(sel_data)
+                df = pd.concat(data, ignore_index=True, copy=False)
+                file_name = C.TREND_DOWNLOAD_FILE_NAME
+            elif trigger.type == "tm-btn-download":
+                tm = TelemetryData(store["selected-tests"])
+                tm.from_json(store["telemetry-data"])
+                df = tm.select_tm_trending_data(
+                    store["telemetry-panels"][trigger.idx]
+                )
+                file_name = C.TELEMETRY_DOWNLOAD_FILE_NAME
+            else:
+                raise PreventUpdate
 
 
-            return dcc.send_data_frame(df.to_csv, C.TREND_DOWNLOAD_FILE_NAME)
+            return dcc.send_data_frame(df.to_csv, file_name)