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)