14
14
import dash_bootstrap_components as dbc
15
15
from dash import html , dcc
16
16
17
- from grid2game ._utils import (add_callbacks_temporal ,
18
- setupLayout_temporal ,
19
- add_callbacks ,
17
+ from grid2game ._utils import (add_callbacks_temporal ,
18
+ setupLayout_temporal ,
19
+ add_callbacks ,
20
20
setupLayout ,
21
- add_callbacks_action_search ,
22
- setupLayout_action_search ,
21
+ add_callbacks_action_search ,
22
+ setupLayout_action_search ,
23
23
)
24
24
from grid2game .envs import Env
25
25
from grid2game .plot import PlotGrids , PlotTemporalSeries
@@ -82,6 +82,20 @@ def __init__(self,
82
82
os .path .join (os .path .dirname (__file__ ), "assets" )
83
83
)
84
84
85
+ # create the dash app
86
+ self .my_app = dash .Dash (__name__ ,
87
+ server = server if server is not None else True ,
88
+ meta_tags = meta_tags ,
89
+ assets_folder = assets_dir ,
90
+ external_stylesheets = external_stylesheets ,
91
+ external_scripts = external_scripts )
92
+
93
+ # Configure logging after dash initialization.
94
+ # Otherwise dash is resetting logging level to INFO
95
+
96
+ if not logging_level and build_args .logging_level :
97
+ logging_level = build_args .logging_level
98
+
85
99
if logger is None :
86
100
import logging
87
101
self .logger = logging .getLogger (__name__ )
@@ -94,19 +108,14 @@ def __init__(self,
94
108
if logging_level is not None :
95
109
fh .setLevel (logging_level )
96
110
ch .setLevel (logging_level )
111
+ self .logger .setLevel (logging_level )
97
112
self .logger .addHandler (fh )
98
113
self .logger .addHandler (ch )
99
114
else :
100
115
self .logger = logger .getChild ("VizServer" )
101
116
102
- # create the dash app
103
- self .my_app = dash .Dash (__name__ ,
104
- server = server if server is not None else True ,
105
- meta_tags = meta_tags ,
106
- assets_folder = assets_dir ,
107
- external_stylesheets = external_stylesheets ,
108
- external_scripts = external_scripts )
109
117
self .logger .info ("Dash app initialized" )
118
+
110
119
# self.app.config.suppress_callback_exceptions = True
111
120
112
121
# create the grid2op related things
@@ -125,8 +134,14 @@ def __init__(self,
125
134
# read the right config
126
135
g2op_config = self ._make_glop_env_config (build_args )
127
136
137
+ if build_args .test_alarms :
138
+ test_alarms = True
139
+ else :
140
+ test_alarms = False
141
+
128
142
self .env = Env (build_args .env_name ,
129
143
test = build_args .is_test ,
144
+ test_alarms = test_alarms ,
130
145
assistant_path = self .assistant_path ,
131
146
assistant_seed = int (build_args .assistant_seed ) if build_args .assistant_seed is not None else None ,
132
147
logger = self .logger ,
@@ -187,37 +202,37 @@ def __init__(self,
187
202
self .plot_grids .init_figs (self .env .obs , self .env .sim_obs )
188
203
self .real_time = self .plot_grids .figure_rt
189
204
self .forecast = self .plot_grids .figure_forecat
190
-
205
+
191
206
# initialize the layout
192
207
self ._layout_temporal = html .Div (setupLayout_temporal (self ),
193
208
id = "all_temporal" )
194
209
self ._layout_temporal_tab = dcc .Tab (label = 'Temporal view' ,
195
210
value = f'tab-temporal-view' ,
196
211
children = self ._layout_temporal )
197
-
212
+
198
213
self ._layout_action_search = html .Div (setupLayout_action_search (self ),
199
214
id = "all_action_search" )
200
215
self ._layout_action_search_tab = dcc .Tab (label = 'Explore actions' ,
201
216
value = 'tab-explore-action' ,
202
217
children = self ._layout_action_search )
203
-
218
+
204
219
tmp_ = setupLayout (self ,
205
220
self ._layout_temporal_tab ,
206
221
self ._layout_action_search_tab )
207
-
222
+
208
223
self .my_app .layout = tmp_
209
-
224
+
210
225
add_callbacks_temporal (self .my_app , self )
211
226
add_callbacks_action_search (self .my_app , self )
212
227
add_callbacks (self .my_app , self )
213
-
228
+
214
229
self .logger .info ("Viz server initialized" )
215
230
216
231
# last node id (to not plot twice the same stuff to gain time)
217
232
self ._last_node_id = - 1
218
233
219
234
# last action taken
220
- self ._last_action = "assistant"
235
+ self ._last_action = "assistant"
221
236
self ._do_display_action = True
222
237
self ._dropdown_value = "assistant"
223
238
@@ -254,11 +269,11 @@ def run_server(self, debug=False):
254
269
255
270
def change_nb_step_go_fast (self , nb_step_go_fast ):
256
271
if nb_step_go_fast is None :
257
- return dash .no_update ,
272
+ return dash .no_update ,
258
273
259
274
nb = int (nb_step_go_fast )
260
275
self .nb_step_gofast = nb
261
- return f"+ { self .nb_step_gofast } " ,
276
+ return f"+ { self .nb_step_gofast } " ,
262
277
263
278
def unit_clicked (self , line_unit , line_side , load_unit , gen_unit , stor_unit ,
264
279
trigger_rt_graph , trigger_for_graph ):
@@ -296,22 +311,24 @@ def unit_clicked(self, line_unit, line_side, load_unit, gen_unit, stor_unit,
296
311
def _reset_action_to_assistant_if_not_prev (self ):
297
312
if self ._last_action != "prev" :
298
313
self ._next_action_is_assistant ()
299
-
314
+
300
315
# handle the interaction with the grid2op environment
301
- def handle_act_on_env (self ,
302
- step_butt ,
303
- simulate_butt ,
304
- back_butt ,
305
- reset_butt ,
306
- go_butt ,
307
- gofast_clicks ,
308
- until_game_over ,
309
- untilgo_butt ,
310
- self_loop ,
311
- state_trigger_rt ,
312
- state_trigger_for ,
313
- state_trigger_self_loop ,
314
- timer ):
316
+ def handle_act_on_env (
317
+ self ,
318
+ step_butt ,
319
+ simulate_butt ,
320
+ back_butt ,
321
+ reset_butt ,
322
+ go_butt ,
323
+ gofast_clicks ,
324
+ until_game_over ,
325
+ untilgo_butt ,
326
+ self_loop ,
327
+ state_trigger_rt ,
328
+ state_trigger_for ,
329
+ state_trigger_self_loop ,
330
+ timer
331
+ ):
315
332
"""
316
333
dash do not make "synch" callbacks (two callbacks can be called at the same time),
317
334
however, grid2op environments are not "thread safe": accessing them from different "thread"
@@ -344,18 +361,21 @@ def handle_act_on_env(self,
344
361
self ._go_till_go_button_shape = "btn btn-secondary"
345
362
change_graph_title = dash .no_update
346
363
update_progress_bar = 1
364
+ check_issue = dash .no_update
347
365
348
366
# now register the next computation to do, based on the button triggerd
349
367
if button_id == "step-button" :
350
368
self .env .start_computation ()
351
369
self .env .next_computation = "step"
352
370
self .env .next_computation_kwargs = {}
353
371
self .need_update_figures = False
372
+ check_issue = 1
354
373
elif button_id == "go_till_game_over-button" :
355
374
self .env .start_computation ()
356
375
self .env .next_computation = "step_end"
357
376
self .env .next_computation_kwargs = {}
358
377
self .need_update_figures = True
378
+ check_issue = 1
359
379
elif button_id == "reset-button" :
360
380
self .env .start_computation ()
361
381
self .env .next_computation = "reset"
@@ -378,6 +398,7 @@ def handle_act_on_env(self,
378
398
self .env .start_computation ()
379
399
self .env .next_computation = "step_rec_fast"
380
400
self .env .next_computation_kwargs = {"nb_step_gofast" : self .nb_step_gofast }
401
+ check_issue = 1
381
402
elif button_id == "go-button" :
382
403
self .go_clicks += 1
383
404
if self .go_clicks % 2 :
@@ -390,6 +411,7 @@ def handle_act_on_env(self,
390
411
self .env .start_computation ()
391
412
self ._button_shape = "btn btn-secondary"
392
413
self ._gofast_button_shape = "btn btn-secondary"
414
+ check_issue = 1
393
415
self .env .next_computation = "step_rec"
394
416
self .env .next_computation_kwargs = {}
395
417
self .need_update_figures = False
@@ -434,7 +456,7 @@ def handle_act_on_env(self,
434
456
self ._go_till_go_button_shape = "btn btn-secondary"
435
457
self ._gofast_button_shape = "btn btn-secondary"
436
458
self ._button_shape = "btn btn-secondary"
437
-
459
+
438
460
return [display_new_state ,
439
461
self ._button_shape ,
440
462
self ._button_shape ,
@@ -446,7 +468,8 @@ def handle_act_on_env(self,
446
468
i_am_computing_state ,
447
469
i_am_computing_state ,
448
470
change_graph_title ,
449
- update_progress_bar ]
471
+ update_progress_bar ,
472
+ check_issue ]
450
473
451
474
def _wait_for_computing_over (self ):
452
475
i = 0
@@ -458,6 +481,28 @@ def _wait_for_computing_over(self):
458
481
# in this case the user should probably call reset another time !
459
482
raise dash .exceptions .PreventUpdate
460
483
484
+ def check_issue (self , n1 , n_clicks ):
485
+ # make sure the environment has nothing to compute
486
+ while self .env .needs_compute ():
487
+ time .sleep (0.1 )
488
+
489
+ issues = self .env ._current_issues
490
+ if issues :
491
+ len_issues = len (issues )
492
+ if len_issues == 1 :
493
+ issue_text = f"There is { len_issues } issue: "
494
+ else :
495
+ issue_text = f"There are { len_issues } issues: "
496
+ for issue in issues :
497
+ issue_text += f"{ issue } , "
498
+ # Replace last ', ' by '.' to end the sentence
499
+ issue_text = '.' .join (issue_text .rsplit (', ' , 1 ))
500
+ is_open = True
501
+ else :
502
+ is_open = False
503
+
504
+ return [is_open , issue_text ]
505
+
461
506
def change_graph_title (self , change_graph_title ):
462
507
# make sure that the environment has done computing
463
508
self ._wait_for_computing_over ()
@@ -483,7 +528,7 @@ def computation_wrapper(self, display_new_state, recompute_rt_from_timeline):
483
528
# simulate a "state" of the application that depends on the computation
484
529
if not self .env .is_computing ():
485
530
self .env .heavy_compute ()
486
-
531
+
487
532
if self .env .is_computing () and display_new_state != type (self ).GO_MODE :
488
533
# environment is computing I do not update anything
489
534
raise dash .exceptions .PreventUpdate
@@ -513,7 +558,7 @@ def update_rt_fig(self, env_act):
513
558
trigger_for_graph = 1
514
559
else :
515
560
raise dash .exceptions .PreventUpdate
516
-
561
+
517
562
if trigger_rt_graph == 1 :
518
563
self .fig_timeline = self .env .get_timeline_figure ()
519
564
@@ -661,7 +706,7 @@ def _next_action_is_assistant(self):
661
706
self ._last_action = "assistant"
662
707
self ._do_display_action = True
663
708
self ._dropdown_value = "assistant"
664
-
709
+
665
710
def display_action_fun (self ,
666
711
which_action_button ,
667
712
do_display ,
@@ -712,7 +757,7 @@ def display_action_fun(self,
712
757
# i should not display the action
713
758
res = [f"{ self .env .current_action } " , dropdown_value , update_substation_layout_clicked_from_sub ]
714
759
return res
715
-
760
+
716
761
# i need to display the action
717
762
# self._last_action = "manual"
718
763
# dropdown_value = "manual"
@@ -748,7 +793,7 @@ def display_action_fun(self,
748
793
749
794
if not is_modif :
750
795
raise dash .exceptions .PreventUpdate
751
-
796
+
752
797
# TODO optim here to save that if not needed because nothing has changed
753
798
res = [f"{ self .env .current_action } " , self ._dropdown_value , update_substation_layout_clicked_from_sub ]
754
799
return res
@@ -821,7 +866,7 @@ def display_click_data(self,
821
866
button_id == "go-button" or button_id == "gofast-button" or \
822
867
button_id == "back-button" :
823
868
# i never clicked on simulate, step, go, gofast or back
824
- do_display_action = 0
869
+ do_display_action = 0
825
870
self ._last_sub_id = None
826
871
else :
827
872
# I clicked on the graph of the grid
@@ -980,7 +1025,7 @@ def timeline_set_time(self, time_line_graph_clicked):
980
1025
981
1026
def tab_content_display (self , tab ):
982
1027
res = [self ._layout_temporal ]
983
-
1028
+
984
1029
if tab == 'tab-temporal-view' :
985
1030
self .need_update_figures = True
986
1031
return [self ._layout_temporal ]
@@ -991,13 +1036,13 @@ def tab_content_display(self, tab):
991
1036
msg_ = f"Unknown tab { tab } "
992
1037
self .logger .error (msg_ )
993
1038
return res
994
-
1039
+
995
1040
def _aux_tab_as_retrieve_updated_figs (self ):
996
1041
progress_pct = 100. * self ._last_step / self ._last_max_step
997
1042
progress_label = f"{ self ._last_step } / { self ._last_max_step } "
998
1043
self .fig_timeline = self .env .get_timeline_figure ()
999
1044
self .update_obs_fig ()
1000
-
1045
+
1001
1046
pbar_value = progress_pct
1002
1047
pbar_label = progress_label
1003
1048
pbar_color = self ._progress_color
@@ -1006,7 +1051,7 @@ def _aux_tab_as_retrieve_updated_figs(self):
1006
1051
fig_rt = self .real_time
1007
1052
return (pbar_value , pbar_label , pbar_color , fig_timeline ,
1008
1053
dt_label , fig_rt )
1009
-
1054
+
1010
1055
def main_action_search (self ,
1011
1056
refresh_button ,
1012
1057
explore_butt_pressed ,
@@ -1017,9 +1062,9 @@ def main_action_search(self,
1017
1062
raise dash .exceptions .PreventUpdate
1018
1063
else :
1019
1064
button_id = ctx .triggered [0 ]['prop_id' ].split ('.' )[0 ]
1020
-
1065
+
1021
1066
something_clicked = True
1022
-
1067
+
1023
1068
# TODO button color here too !
1024
1069
i_am_computing_state = {'display' : 'block' }
1025
1070
pbar_value = dash .no_update
@@ -1029,25 +1074,25 @@ def main_action_search(self,
1029
1074
dt_label = dash .no_update
1030
1075
fig_rt = dash .no_update
1031
1076
start_computation = 1
1032
-
1077
+
1033
1078
if button_id == "refresh-button_as" :
1034
1079
# (pbar_value, pbar_label, pbar_color, fig_timeline,
1035
1080
# dt_label, fig_rt) = self._aux_tab_as_retrieve_updated_figs()
1036
1081
start_computation = dash .no_update
1037
1082
# hack for it to resynch everything
1038
1083
self .need_update_figures = True
1039
- elif button_id == "explore-button_as" :
1084
+ elif button_id == "explore-button_as" :
1040
1085
self .env .next_computation = "explore"
1041
1086
self .need_update_figures = True
1042
1087
self .env .start_computation ()
1043
1088
else :
1044
1089
something_clicked = False
1045
-
1090
+
1046
1091
if not self .env .needs_compute ():
1047
1092
# don't start the computation if not needed
1048
1093
i_am_computing_state = {'display' : 'none' } # deactivate the "i am computing button"
1049
1094
start_computation = dash .no_update # I am NOT computing I DO update the graphs
1050
-
1095
+
1051
1096
if not self .env .needs_compute () and self .need_update_figures and not something_clicked :
1052
1097
# in this case, this should be the last call to this function after the "explore"
1053
1098
# function is finished
@@ -1058,7 +1103,7 @@ def main_action_search(self,
1058
1103
1059
1104
(pbar_value , pbar_label , pbar_color , fig_timeline ,
1060
1105
dt_label , fig_rt ) = self ._aux_tab_as_retrieve_updated_figs ()
1061
-
1106
+
1062
1107
return [start_computation ,
1063
1108
pbar_value ,
1064
1109
pbar_label ,
0 commit comments