Read data from socket using one callback, update graph with another (plot does not update)

I am building Plotly Dash to display live “lap time” plots from racing events. I am using user socket to receive data encoded in hex characters, filter it, perform some other operations and then display on Dash. It is almost working, except the plot is not refreshing automatically. I’m guessing using callback with 0 interval to read telemetry data stream doesn’t let the second graph update callback work properly. Is there an efficient way to separate the two or what causes it to not refresh? Here’s how it the plot looks:

Currently, I initialize a socket and create two objects, first object for reading incoming data line by line and store it. Second one to put in appropriate format for displaying multiple lines on the graph. I use Interval method with interval 0 for first object, so that incoming stream of data is read constantly (instead of while True loop). Then I use Interval method again to update the graph with interval set to 5000, so it is updated every 5 seconds. Now, the data is being processed correctly (I printed it out to check), the graph gets initialized, but it is not being updated. If I manually reload the Dash webpage it gets updated, but naturally I want it to refresh on its won every 5 seconds.

I read that small interval updates (like for first object) can cause the Dash not to load properly, but even if I change the interval from 0 to higher value same issue stays. Also, I do get the following warning: “127.0.0.1 - - [15/Feb/2022 15:20:24] “POST /_dash-update-component HTTP/1.1” 200 -” which I believe means that plot gets posted, but no change occurs. Please assist!

#DASH APP STRUCTURE

app = dash.Dash(__name__)

app.layout = html.Div([
            html.Div([
                html.Div(id='live-update-data'),
                dcc.Interval(id = 'update-data', interval = 0, n_intervals=0)
                ]),

            html.Div([
                dcc.Graph(id = 'live-graph', animate = True),
                dcc.Interval(id = 'graph-update', interval = 5000, n_intervals=0)
                ])
    ])

@app.callback(
    Output('live-update-data', 'children'),
    [ Input('update-data', 'n_intervals') ]
)

def read_data_socket(n_intervals):

    part = ' '
    msg = []
    msg, part = connection.receiveAll(part)
    for line in msg:
        #print(line)
        #LapTimes
        if line.startswith('$C'):
            line = line.strip()
            lap = re.split('¦', line)
            car = int(lap[5])
            lap_time = int(lap[9],16)/10000 
            lap_num = int(lap[7],16)
  
                
            if car not in lap_times:
                lap_times[car] = []
            if len(lap_times[car]) == 0:
                if lap_time > 0 and lap_time < LAP_FILTER:
                    lap_times[car].append([lap_num, lap_time])
            else:
                if lap_times[car][-1][1] != lap_time and lap_time > 0 and lap_time < LAP_FILTER:
                    lap_times[car].append([lap_num, lap_time])

    return lap_times



@app.callback(
    Output('live-graph', 'figure'),
    [ Input('graph-update', 'n_intervals') ]
)
  
def update_graph_scatter(n_intervals):

    data = []
    maxlap = 0
    miny = 100

    for car in list(lap_times.keys()):
        if len(lap_times[car]) == 0:
            continue
        X = []
        Y = []
        for i in range(0, len(lap_times[car])):
            X.append(lap_times[car][i][0])
            Y.append(lap_times[car][i][1])
        if max(X) > maxlap:
            maxlap = max(X)
        if min(Y) < miny:
            miny = min(Y)
        plot = plotly.graph_objs.Scatter(
                x=X,
                y=Y,
                name=str(car),
                mode= 'lines'
        )
        data.append(plot)

    return {'data':data, 'layout': go.Layout(xaxis=dict(range=[minlap,maxlap]),yaxis = dict(range = [miny,LAP_FILTER]),)}



### MAIN FUNCTION
### STARTS HERE

if __name__ == '__main__':

    LAP_FILTER = 65
    minlap = 1   

    lap_times = {} 

    ip = "64.85.132.67"
    port = 50008
    connection = ICReceiver()
    connection.connect(ip, port)

    app.run_server(debug=False)

Any comments, suggestions, ideas?

You could merge the two callbacks into one. Won’t that make the issue go away?

The issue is that I need to read stream of data nonstop and update plot only every other 5 seconds. Therefore I tried to use two different dcc.Interval methods. It’s my understanding that one callback can’t capture both behaviors. Alternatively, I started looking into non-blocking IO to read data and use Dash with one callback to only plot and update graph.

Have you tried setting animate=False? I have seen other people having issues using this option with Dash.

@tishafok Did you get anywhere with this?
This might be of interest:
https://f1dash.azurewebsites.net/

1 Like