Black Lives Matter. Please consider donating to Black Girls Code today.

Problems when multithreading

Hi
I’m trying to create a streaming tool. It streams some machines through the OPC server.
I use multithreading. User can add any machine he wants. Each machine will operate through its own thread. But when the number of machines is too big (10 for example), dash components, which operate in the main thread are not reacting on actions. And also, graphs with intervals are not updating.

I recorded too long video, so, I can’t convert it to GIF, sorry. But you can find it through https://drive.google.com/open?id=1YK_4FAZO3rzYrIhS6G46flEjn7bHUM18
In this video, you can see my test system to illustrate the problem. Machines are streamed fine until the 10th Machine. After that graphs are not updating.
In the next message, I’ll add the code.

app.py file:

from flask import Flask
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
import operator
from threading import Thread
from opcua import Client
import time
import os
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from opc_functions import save_opc_values
from plot_functions import plot_data
from view_all import view_layout

SERVER_LINK = 'opc.tcp://localhost:4841/freeopcua/server/'
OBJECTS = ['1:metric_value.1', '2:metric_value.2', '3:metric_value.3']

external_stylesheets = ['https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css']

server = Flask(__name__)
app = dash.Dash(__name__, server=server, external_stylesheets=external_stylesheets)

app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='test', hidden=True),
    html.Div(id='page-content',
             children=view_layout)
])

# Change the title of tabs
@server.route("/")
def main_tab_title():
    app.title = "Anomaly detection"
    return app.index()


@app.callback(Output('graphics_view', 'children'),
              [Input('interval_graph_view', 'n_intervals')])
def plot_machines(n_intervals):
    start_time = time.time()

    print('entered plot_machines')
    machines_list = os.listdir('data')
    div_children = []

    cols = 2
    rows = len(machines_list)//cols
    rows = rows + 1 if len(machines_list)%cols > 0 or rows == 0 else rows
    fig = make_subplots(rows=rows, cols=cols)

    ind = 0
    for machine in machines_list:
        data = pd.read_csv(os.path.join('data', machine))

        # fig = go.Figure()
        row = ind // cols + 1
        col = ind % cols + 1
        fig = plot_data(fig, machine, data,
                        row=row, col=col)
        ind += 1

    div_children.append(
        dcc.Graph(
            # id=f"{machine}_graph",
            figure=fig,
        )
    )

    print('plotted')

    return div_children


@app.callback(Output('machines_list', 'children'),
              [Input('Add_machine', 'n_clicks_timestamp'),
               Input('Delete_machine', 'n_clicks_timestamp')],
              [State('machines_list', 'children')])
def stream_machines(add_clicked, delete_clicked,
                    # States
                    machines_list):
    print('entered stream_machines')
    start_time = time.time()

    add_clicked = add_clicked or 0
    delete_clicked = delete_clicked or 0
    machines_list = machines_list or []

    # find last clicked item
    clicks_dict = {'add': add_clicked,
                   'delete': delete_clicked}
    clicked = max(clicks_dict.items(), key=operator.itemgetter(1))[0]

    if clicks_dict[clicked] != 0:
        if clicked == 'add':
            machine = f'Machine_{len(machines_list)+1} '
            machines_list.append(machine)

            client = Client(SERVER_LINK)
            client.connect()
            root = client.get_root_node()

            p1 = Thread(target=save_opc_values,
                        args=(machine, root, client, OBJECTS),
                        name=machine)
            p1.start()
        elif clicked == 'delete' and len(machines_list) > 0:
            machine_to_drop = machines_list[-1]

            # stop threading
            save_opc_values.stop = machine_to_drop

            # delete saved files
            file_path = os.path.join('data', f'{machine_to_drop}.csv')
            if os.path.exists(file_path):
                os.remove(file_path)

            machines_list = machines_list[:-1]
            time.sleep(3)

    # print('stream_machines time = ', time.time() - start_time)
    return machines_list


if __name__ == '__main__':
    app.run_server(port=80, host='0.0.0.0')

plot function:

def plot_data(fig, machine, df, row, col):
    # Add legend and title
    fig.update_layout(showlegend=True,
                      title={
                          'text': machine,
                          # location of the title in ratio
                          'y': 0.9,
                          'x': 0.5
                      })
    x = df.iloc[:, 0]
    for column in df.columns[1:]:
        fig.add_trace(go.Scatter(x=x,
                                 y=df[column],
                                 name=column
                                 ),
                      row=row, col=col)
    return fig

If you’ll need some other functions, I’ll add.

Really need your help!