mirror of
				https://github.com/optim-enterprises-bv/nDPId.git
				synced 2025-11-02 19:27:48 +00:00 
			
		
		
		
	Improved Plotly/Dash example. It is now somehow informative.
* TCP timeout after FIN/RST: switched back to the value from a35fc1d5ea
 * py-flow-info: reset 'guessed' flag after detection/detection-update received
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
			
			
This commit is contained in:
		
							
								
								
									
										276
									
								
								examples/py-flow-dashboard/plotly_dash.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								examples/py-flow-dashboard/plotly_dash.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,276 @@
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
import dash
 | 
			
		||||
from dash.dependencies import Input, Output, State
 | 
			
		||||
import dash_core_components as dcc
 | 
			
		||||
import dash_html_components as html
 | 
			
		||||
import dash_daq as daq
 | 
			
		||||
import dash_table as dt
 | 
			
		||||
 | 
			
		||||
import plotly.graph_objects as go
 | 
			
		||||
 | 
			
		||||
global shared_flow_dict
 | 
			
		||||
 | 
			
		||||
app = dash.Dash(__name__)
 | 
			
		||||
 | 
			
		||||
def generate_box():
 | 
			
		||||
    return { \
 | 
			
		||||
        'display': 'flex', 'flex-direction': 'row', \
 | 
			
		||||
        'box-shadow': '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)', \
 | 
			
		||||
        'background-color': '#082255' \
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
def generate_led_display(div_id, label_name):
 | 
			
		||||
    return daq.LEDDisplay( \
 | 
			
		||||
        id=div_id, \
 | 
			
		||||
        label={'label': label_name, 'style': {'color': '#C4CDD5'}}, \
 | 
			
		||||
        labelPosition='bottom', \
 | 
			
		||||
        value='0', \
 | 
			
		||||
        backgroundColor='#082255', \
 | 
			
		||||
        color='#C4CDD5', \
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def generate_gauge(div_id, label_name, max_value=10):
 | 
			
		||||
    return daq.Gauge( \
 | 
			
		||||
        id=div_id, \
 | 
			
		||||
        value=0, \
 | 
			
		||||
        label={'label': label_name, 'style': {'color': '#C4CDD5'}}, \
 | 
			
		||||
        max=max_value, \
 | 
			
		||||
        min=0, \
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
app.layout = html.Div([
 | 
			
		||||
    html.Div(children=[
 | 
			
		||||
        dcc.Interval(id="default-interval", interval=1 * 2000, n_intervals=0),
 | 
			
		||||
 | 
			
		||||
        html.Div(children=[
 | 
			
		||||
 | 
			
		||||
            dt.DataTable(
 | 
			
		||||
                id='table-info',
 | 
			
		||||
                columns=[{'id': c.lower(), 'name': c, 'editable': False}
 | 
			
		||||
                         for c in ['Key', 'Value']],
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        ], style={'display': 'flex', 'flex-direction': 'row'}),
 | 
			
		||||
 | 
			
		||||
        html.Div(children=[
 | 
			
		||||
            dcc.Graph(
 | 
			
		||||
                id='piechart-flows',
 | 
			
		||||
                config={
 | 
			
		||||
                    'displayModeBar': False,
 | 
			
		||||
                },
 | 
			
		||||
            ),
 | 
			
		||||
        ], style={'padding': 10, 'flex': 1}),
 | 
			
		||||
 | 
			
		||||
        html.Div(children=[
 | 
			
		||||
            dcc.Graph(
 | 
			
		||||
                id='piechart-midstream-flows',
 | 
			
		||||
                config={
 | 
			
		||||
                    'displayModeBar': False,
 | 
			
		||||
                },
 | 
			
		||||
            ),
 | 
			
		||||
        ], style={'padding': 10, 'flex': 1}),
 | 
			
		||||
 | 
			
		||||
        html.Div(children=[
 | 
			
		||||
            dcc.Graph(
 | 
			
		||||
                id='piechart-risky-flows',
 | 
			
		||||
                config={
 | 
			
		||||
                    'displayModeBar': False,
 | 
			
		||||
                },
 | 
			
		||||
            ),
 | 
			
		||||
        ], style={'padding': 10, 'flex': 1}),
 | 
			
		||||
    ], style=generate_box()),
 | 
			
		||||
 | 
			
		||||
    html.Div(children=[
 | 
			
		||||
        dcc.Interval(id="graph-interval", interval=4 * 1000, n_intervals=0),
 | 
			
		||||
        dcc.Store(id="graph-traces"),
 | 
			
		||||
 | 
			
		||||
        html.Div(children=[
 | 
			
		||||
            dcc.Graph(
 | 
			
		||||
                id="graph-flows",
 | 
			
		||||
                config={
 | 
			
		||||
                    'displayModeBar': False,
 | 
			
		||||
                },
 | 
			
		||||
                style={'height':'60vh'},
 | 
			
		||||
            ),
 | 
			
		||||
        ], style={'padding': 10, 'flex': 1})
 | 
			
		||||
    ], style=generate_box()),
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
def build_gauge(key, max_value=100):
 | 
			
		||||
    gauge_max = int(max(max_value,
 | 
			
		||||
                        shared_flow_dict[key]))
 | 
			
		||||
    grad_green  = [0,                    int(gauge_max * 1/3)]
 | 
			
		||||
    grad_yellow = [int(gauge_max * 1/3), int(gauge_max * 2/3)]
 | 
			
		||||
    grad_red    = [int(gauge_max * 2/3), gauge_max]
 | 
			
		||||
 | 
			
		||||
    grad_dict   = \
 | 
			
		||||
        { \
 | 
			
		||||
            "gradient":True, \
 | 
			
		||||
            "ranges":{ \
 | 
			
		||||
                "green":grad_green, \
 | 
			
		||||
                "yellow":grad_yellow, \
 | 
			
		||||
                "red":grad_red \
 | 
			
		||||
            } \
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    return shared_flow_dict[key], gauge_max, grad_dict
 | 
			
		||||
 | 
			
		||||
def build_piechart(labels, values):
 | 
			
		||||
    lay = dict(
 | 
			
		||||
        plot_bgcolor = '#082255',
 | 
			
		||||
        paper_bgcolor = '#082255',
 | 
			
		||||
        font={"color": "#fff"},
 | 
			
		||||
        autosize=True,
 | 
			
		||||
        height=250,
 | 
			
		||||
        margin = {'autoexpand': False, 'b': 0, 'l': 0, 'r': 0, 't': 0, 'pad': 0},
 | 
			
		||||
        width = 500,
 | 
			
		||||
        uniformtext_minsize = 12,
 | 
			
		||||
        uniformtext_mode = 'hide',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return go.Figure(layout=lay, data=[go.Pie(labels=labels, values=values, textinfo='percent', textposition='inside')])
 | 
			
		||||
 | 
			
		||||
def prettifyBytes(bytes_received):
 | 
			
		||||
    size_names = ['B', 'KB', 'MB', 'GB', 'TB']
 | 
			
		||||
    if bytes_received == 0:
 | 
			
		||||
        i = 0
 | 
			
		||||
    else:
 | 
			
		||||
        i = min(int(math.floor(math.log(bytes_received, 1024))), len(size_names) - 1)
 | 
			
		||||
    p = math.pow(1024, i)
 | 
			
		||||
    s = round(bytes_received / p, 2)
 | 
			
		||||
    return '{:.2f} {}'.format(s, size_names[i])
 | 
			
		||||
 | 
			
		||||
@app.callback(output=[Output('table-info', 'data'),
 | 
			
		||||
                      Output('piechart-flows', 'figure'),
 | 
			
		||||
                      Output('piechart-midstream-flows', 'figure'),
 | 
			
		||||
                      Output('piechart-risky-flows', 'figure')],
 | 
			
		||||
 | 
			
		||||
              inputs=[Input('default-interval', 'n_intervals')])
 | 
			
		||||
def update_led_gauge(n):
 | 
			
		||||
    return [[{'key': 'Total JSON Events',        'value': shared_flow_dict['total-events']},
 | 
			
		||||
             {'key': 'Total JSON Bytes',         'value': prettifyBytes(shared_flow_dict['total-bytes'])},
 | 
			
		||||
             {'key': 'Total Flows',              'value': shared_flow_dict['total-flows']},
 | 
			
		||||
             {'key': 'Total Risky Flows',        'value': shared_flow_dict['total-risky-flows']},
 | 
			
		||||
             {'key': 'Total Midstream Flows',    'value': shared_flow_dict['total-midstream-flows']},
 | 
			
		||||
             {'key': 'Total Guessed Flows',      'value': shared_flow_dict['total-guessed-flows']},
 | 
			
		||||
             {'key': 'Total Not Detected Flows', 'value': shared_flow_dict['total-not-detected-flows']}],
 | 
			
		||||
            build_piechart(['Detected', 'Guessed', 'Undetected', 'Unclassified'],
 | 
			
		||||
                           [shared_flow_dict['current-detected-flows'],
 | 
			
		||||
                            shared_flow_dict['current-guessed-flows'],
 | 
			
		||||
                            shared_flow_dict['current-not-detected-flows'],
 | 
			
		||||
                            shared_flow_dict['current-flows']
 | 
			
		||||
                                - shared_flow_dict['current-detected-flows']
 | 
			
		||||
                                - shared_flow_dict['current-guessed-flows']
 | 
			
		||||
                                - shared_flow_dict['current-not-detected-flows']]),
 | 
			
		||||
            build_piechart(['Midstream', 'Not Midstream'],
 | 
			
		||||
                           [shared_flow_dict['current-midstream-flows'],
 | 
			
		||||
                            shared_flow_dict['current-flows'] -
 | 
			
		||||
                            shared_flow_dict['current-midstream-flows']]),
 | 
			
		||||
            build_piechart(['Risky', 'Not Risky'],
 | 
			
		||||
                           [shared_flow_dict['current-risky-flows'],
 | 
			
		||||
                            shared_flow_dict['current-flows'] -
 | 
			
		||||
                            shared_flow_dict['current-risky-flows']])]
 | 
			
		||||
 | 
			
		||||
@app.callback(output=[Output('graph-flows', 'figure'),
 | 
			
		||||
                      Output('graph-traces', 'data')],
 | 
			
		||||
              inputs=[Input('graph-interval', 'n_intervals'),
 | 
			
		||||
                      Input('graph-interval', 'interval')],
 | 
			
		||||
              state=[State('graph-traces', 'data')])
 | 
			
		||||
def update_graph(n, i, traces):
 | 
			
		||||
    if traces is None:
 | 
			
		||||
        traces = ([], [], [], [], [], [])
 | 
			
		||||
 | 
			
		||||
    max_bins = 50
 | 
			
		||||
 | 
			
		||||
    traces[0].append(shared_flow_dict['current-flows'])
 | 
			
		||||
    traces[1].append(shared_flow_dict['current-risky-flows'])
 | 
			
		||||
    traces[2].append(shared_flow_dict['current-midstream-flows'])
 | 
			
		||||
    traces[3].append(shared_flow_dict['current-guessed-flows'])
 | 
			
		||||
    traces[4].append(shared_flow_dict['current-not-detected-flows'])
 | 
			
		||||
    traces[5].append(shared_flow_dict['current-flows']
 | 
			
		||||
                        - shared_flow_dict['current-detected-flows']
 | 
			
		||||
                        - shared_flow_dict['current-guessed-flows']
 | 
			
		||||
                        - shared_flow_dict['current-not-detected-flows'])
 | 
			
		||||
    if len(traces[0]) > max_bins:
 | 
			
		||||
        traces[0] = traces[0][1:]
 | 
			
		||||
        traces[1] = traces[1][1:]
 | 
			
		||||
        traces[2] = traces[2][1:]
 | 
			
		||||
        traces[3] = traces[3][1:]
 | 
			
		||||
        traces[4] = traces[4][1:]
 | 
			
		||||
        traces[5] = traces[5][1:]
 | 
			
		||||
 | 
			
		||||
    i /= 1000.0
 | 
			
		||||
    x = list(range(max(n - max_bins, 0) * int(i), n * int(i), max(int(i), 1)))
 | 
			
		||||
 | 
			
		||||
    lay = dict(
 | 
			
		||||
        plot_bgcolor = '#082255',
 | 
			
		||||
        paper_bgcolor = '#082255',
 | 
			
		||||
        font={"color": "#fff"},
 | 
			
		||||
        xaxis = {
 | 
			
		||||
            'title': 'Time (sec)',
 | 
			
		||||
            "showgrid": False,
 | 
			
		||||
            "showline": False,
 | 
			
		||||
            "fixedrange": True,
 | 
			
		||||
            "tickmode": 'linear',
 | 
			
		||||
            "dtick": i,
 | 
			
		||||
        },
 | 
			
		||||
        yaxis = {
 | 
			
		||||
            'title': 'Flow Count',
 | 
			
		||||
            "showgrid": False,
 | 
			
		||||
            "showline": False,
 | 
			
		||||
            "zeroline": False,
 | 
			
		||||
            "fixedrange": True,
 | 
			
		||||
            "tickmode": 'linear',
 | 
			
		||||
            "dtick": 10,
 | 
			
		||||
        },
 | 
			
		||||
        autosize=True,
 | 
			
		||||
        bargap=0.01,
 | 
			
		||||
        bargroupgap=0,
 | 
			
		||||
        hovermode="closest",
 | 
			
		||||
        margin = {'b': 0, 'l': 0, 'r': 0, 't': 0, 'pad': 0},
 | 
			
		||||
        legend = {'borderwidth': 0},
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fig = go.Figure(layout=lay)
 | 
			
		||||
    fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='#007ACE', zeroline=True, zerolinewidth=1)
 | 
			
		||||
    fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='#007ACE', zeroline=True, zerolinewidth=1)
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[0],
 | 
			
		||||
        name='Current Active Flows',
 | 
			
		||||
    ))
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[1],
 | 
			
		||||
        name='Current Risky Flows',
 | 
			
		||||
    ))
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[2],
 | 
			
		||||
        name='Current Midstream Flows',
 | 
			
		||||
    ))
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[3],
 | 
			
		||||
        name='Current Guessed Flows',
 | 
			
		||||
    ))
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[4],
 | 
			
		||||
        name='Current Not Detected Flows',
 | 
			
		||||
    ))
 | 
			
		||||
    fig.add_trace(go.Scatter(
 | 
			
		||||
        x=x,
 | 
			
		||||
        y=traces[5],
 | 
			
		||||
        name='Current Unclassified Flows',
 | 
			
		||||
    ))
 | 
			
		||||
 | 
			
		||||
    return [fig, traces]
 | 
			
		||||
 | 
			
		||||
def web_worker(mp_shared_flow_dict):
 | 
			
		||||
    global shared_flow_dict
 | 
			
		||||
 | 
			
		||||
    shared_flow_dict = mp_shared_flow_dict
 | 
			
		||||
 | 
			
		||||
    app.run_server(debug=False)
 | 
			
		||||
		Reference in New Issue
	
	Block a user