1 | % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | :- module(graphical_state_viewer_images,[tcltk_get_image_list/6, | |
5 | ||
6 | tcltk_can_react_to_item_right_click/2, | |
7 | tcltk_react_to_item_right_click/2, | |
8 | ||
9 | tcltk_can_react_to_item_drag/3, | |
10 | tcltk_react_to_item_drag/2, | |
11 | ||
12 | get_animation_images/1, % only get animation image list | |
13 | get_animation_image_grid/6, | |
14 | get_react_to_item_right_click_options/4, | |
15 | react_to_item_right_click/6, | |
16 | ||
17 | html_print_history/1, html_print_history/2 | |
18 | ]). | |
19 | ||
20 | /* ------------------------------------------------------------------ */ | |
21 | ||
22 | :- use_module(probsrc(module_information)). | |
23 | :- module_info(group,visualization). | |
24 | :- module_info(description,'This module can provide a Tk graphical representation of a B state.'). | |
25 | ||
26 | ||
27 | ||
28 | :- use_module(probsrc(bmachine),[b_get_machine_animation_function/2, | |
29 | bmachine_is_precompiled/0, b_get_definition_string_from_machine/2, | |
30 | get_animation_image/2]). | |
31 | ||
32 | /* First argument: image list of pairs: ImageName, ImageFilePath | |
33 | Second argument: list of triplets {type framename label} | |
34 | */ | |
35 | ||
36 | :- use_module(probsrc(error_manager)). | |
37 | :- use_module(probsrc(debug)). | |
38 | :- use_module(probsrc(state_space),[current_expression/2]). | |
39 | :- use_module(probsrc(tools_strings),[ajoin/2]). | |
40 | ||
41 | :- set_prolog_flag(double_quotes, codes). | |
42 | ||
43 | ||
44 | eval_definition_funs([],_,[]). | |
45 | eval_definition_funs([anim_fun(Nr,AnimFunction1)|T],BState,Result) :- | |
46 | eval_definition_fun(AnimFunction1, Nr, BState, FunctionRes1), | |
47 | eval_definition_funs(T,BState,FunctionRes0), | |
48 | override_anim_fun(FunctionRes1,FunctionRes0,Result). | |
49 | ||
50 | :- use_module(probsrc(xtl_interface),[xtl_animation_function_result/2, xtl_animation_image/2]). | |
51 | :- use_module(probsrc(specfile),[animation_mode/1, b_or_z_mode/0]). | |
52 | ||
53 | ||
54 | tcltk_get_image_list(list(ImageList),MinRow,MaxRow,MinCol,MaxCol,list(FramesAndItems)) :- | |
55 | animation_mode(Mode), | |
56 | animation_function_possibly_active(Mode), | |
57 | get_animation_images(Mode,ImageList), | |
58 | current_expression(_CurID,State), | |
59 | evaluate_animation_function(Mode,State,FunctionRes), | |
60 | get_min_max_col(FunctionRes,MinCol,MaxCol), | |
61 | display_animation_function_result(FunctionRes,MinRow,MaxRow,FramesAndItems). | |
62 | ||
63 | :- use_module(probsrc(specfile),[state_corresponds_to_fully_setup_b_machine/2]). | |
64 | ||
65 | animation_function_possibly_active(xtl) :- !. | |
66 | animation_function_possibly_active(_) :- bmachine_is_precompiled, | |
67 | b_get_machine_animation_function(_,_),!. | |
68 | ||
69 | evaluate_animation_function(xtl,State,FunctionRes) :- !, | |
70 | xtl_animation_function_result(State,FunctionRes). | |
71 | evaluate_animation_function(_,State,FunctionRes) :- % Mode will be 'b' | |
72 | state_corresponds_to_fully_setup_b_machine(State,BState), | |
73 | findall(anim_fun(Nr,AF),b_get_machine_animation_function(AF,Nr),AnimFuns), | |
74 | AnimFuns \= [], | |
75 | sort(AnimFuns,SAnimFuns), | |
76 | %print(animation_images(ImageList)), | |
77 | % print('Animation Function: '), | |
78 | % translate:print_bexpr(SAnimFuns), | |
79 | eval_definition_funs(SAnimFuns, BState, FunctionRes). | |
80 | % print('full_fun'(FunctionRes)),nl. | |
81 | ||
82 | display_animation_function_result(FunctionRes,MinRow,MaxRow,FramesAndItems) :- | |
83 | get_min_max_row(FunctionRes,MinRow,MaxRow), | |
84 | debug_println(9,visualization_matrix_min_max(MinRow,MaxRow)), | |
85 | setup_frames(MinRow,MaxRow,Frames), %debug_println(9,frames(Frames)), | |
86 | sort(FunctionRes,SortedFR), | |
87 | get_frame_items(SortedFR,Items), | |
88 | % print(items(Items)),nl, | |
89 | pad_text_frame_items(Items,PItems), | |
90 | append(Frames,PItems,FramesAndItems). | |
91 | ||
92 | ||
93 | :- use_module(probsrc(tools),[ split_last/4, split_chars/3 ]). | |
94 | tcltk_can_react_to_item_right_click(TkPath,list(Names)) :- | |
95 | convert_tk_path_to_coordinates(TkPath,XCoord,YCoord), | |
96 | animation_mode(M), | |
97 | current_state_id(CurID), | |
98 | debug_println(19,start_right_clicked_object(XCoord,YCoord,mode(M),state(CurID))), | |
99 | findall(Name,can_react_to_item_right_click(CurID,M,XCoord,YCoord,_,Name,_),Names). | |
100 | ||
101 | % for ProB 2: | |
102 | get_react_to_item_right_click_options(CurID,XCoord,YCoord,Names) :- | |
103 | animation_mode(M), | |
104 | findall(Name,can_react_to_item_right_click(CurID,M,XCoord,YCoord,_,Name,_),Names). | |
105 | ||
106 | tcltk_react_to_item_right_click(TkPath,ActionStr) :- | |
107 | % print(right_clicked_tk_path(TkPath)),nl, | |
108 | convert_tk_path_to_coordinates(TkPath,XCoord,YCoord), | |
109 | current_state_id(CurID), | |
110 | react_to_item_right_click(CurID,XCoord,YCoord,ActionStr,_,_). | |
111 | ||
112 | :- use_module(probsrc(state_space),[current_state_id/1, transition/4]). | |
113 | :- use_module(probsrc(xtl_interface),[xtl_animation_image_right_click_transition/3]). | |
114 | :- use_module(probsrc(translate),[translate_event_with_src_and_target_id/4]). | |
115 | :- use_module(probsrc(bmachine),[b_get_definition/5,b_get_definition_with_pos/6,type_with_errors/4, b_top_level_operation/1]). | |
116 | :- use_module(library(lists),[maplist/3]). | |
117 | :- use_module(probsrc(preferences),[temporary_set_preference/3,reset_temporary_preference/2]). | |
118 | ||
119 | can_react_to_item_right_click(CurID,Mode,X,Y,TransitionTemplateList,Str,TransitionID) :- | |
120 | get_right_click_template(Mode,CurID,X,Y,TransitionTemplateList), | |
121 | get_template(TransitionTemplateList,Template), % deals with list templates | |
122 | transition(CurID,TransitionTerm,TransitionID,DestID), | |
123 | match_template(Template,TransitionTerm), | |
124 | translate_event_with_src_and_target_id(TransitionTerm,CurID,DestID,Str). | |
125 | ||
126 | match_template(T,T) :- !. | |
127 | match_template(T,'-->'(ActionWoResults,_Results)) :- !, match_template(T,ActionWoResults). | |
128 | match_template(Template,Action) :- | |
129 | atom(Template), % with get_preference(show_eventb_any_arguments,true) we have extra arguments | |
130 | get_operation_name(Action,Template). | |
131 | ||
132 | ||
133 | get_right_click_template(xtl,_,ColX,RowY,Template) :- | |
134 | xtl_animation_image_right_click_transition(RowY,ColX,Template). | |
135 | get_right_click_template(b,CurID,X,Y,Template) :- | |
136 | Def_Name = 'ANIMATION_RIGHT_CLICK', | |
137 | b_get_click_subst_definition(Def_Name,2,_RawSubst,DefPos), | |
138 | temporary_set_preference(allow_local_operation_calls,true,CHNG), | |
139 | Scope = [prob_ids(visible),operation_bodies], % do not include variables; otherwise clash warnings | |
140 | % type definition call: | |
141 | type_with_errors(definition(DefPos,'ANIMATION_RIGHT_CLICK',[integer(Pos,X),integer(Pos,Y)]),Scope,subst,Typed), | |
142 | reset_temporary_preference(allow_local_operation_calls,CHNG), | |
143 | get_b_template_for(CurID,Typed,[],Template). | |
144 | ||
145 | ||
146 | b_get_click_subst_definition(Def_Name,NrArgs,RawSubst,DefPos) :- | |
147 | b_get_definition_with_pos(Def_Name,Kind,DefPos,Args,RawBody,_Deps), | |
148 | length(Args,Len), | |
149 | (Len=NrArgs -> true | |
150 | ; ajoin([Def_Name,' should take ',NrArgs,' arguments, not: '],Msg), | |
151 | add_warning(state_viewer_images,Msg,Len,DefPos), | |
152 | fail | |
153 | ), | |
154 | (Kind=substitution -> RawSubst=RawBody | |
155 | ; translate_expr_to_op_call(RawBody,OP,PosOp,RawSubst) -> | |
156 | add_warning(state_viewer_images,'ANIMATION_(RIGHT_)CLICK is not a substitution, wrap call to operation into BEGIN END block: ',OP,PosOp), | |
157 | fail % TODO: use translated RawSubst instead of typing definition call | |
158 | ; add_warning(state_viewer_images,'ANIMATION_(RIGHT_)CLICK is not a substitution but: ',Kind,DefPos), | |
159 | fail | |
160 | ). | |
161 | ||
162 | % for some reason the parser maps simple operation calls to function call expressions | |
163 | translate_expr_to_op_call(function(Pos1,identifier(Pos2,OP),[A,B]),OP,Pos1, | |
164 | operation_call(Pos1,identifier(Pos2,OP),[A,B])) :- b_top_level_operation(OP). | |
165 | ||
166 | % ANIMATION_RIGHT_CLICK can use the following in B: | |
167 | % ANY, LET: to introduce wild cards; predicates will not (yet) be evaluated !! | |
168 | % IF-ELSIF-ELSE: conditions have to be evaluable using the parameters only | |
169 | % CHOICE: to provide multiple right click actions | |
170 | ||
171 | % Operation Calls: the arguments must evaluable without access to variables and constants at the moment | |
172 | ||
173 | :- use_module(probsrc(specfile), [state_corresponds_to_set_up_constants/2]). | |
174 | get_b_template_for(CurID,Subst,LV,Template) :- | |
175 | visited_expression(CurID,State), | |
176 | state_corresponds_to_set_up_constants(State,BState), | |
177 | % expand_const_and_vars_to_full_store | |
178 | get_b_template(Subst,LV,BState,Template). | |
179 | ||
180 | get_b_template(Typed,LocalVars,State,Template) :- | |
181 | (Typed=b(sequence([A|Rest]),_,_) | |
182 | -> Template = [TA|Rest], % we assume Rest has no guard; currently we only support MCTS_AUTO_PLAY | |
183 | get_b_template(A,LocalVars,State,TA) | |
184 | % TODO: detect MCTS_AUTO_PLAY on its own | |
185 | ; get_b_template2(Typed,LocalVars,State,Template) | |
186 | ). | |
187 | ||
188 | % get template to match against enabled transitions: | |
189 | get_b_template2(b(E,subst,_),LocalVars,State,Template) :- get_b_template_aux(E,LocalVars,State,Template). | |
190 | ||
191 | :- use_module(probsrc(bsyntaxtree), [get_texpr_id/2, def_get_texpr_id/2]). | |
192 | get_b_template_aux(any(Paras,_Pred,Body),LocalVars,State,Template) :- !, | |
193 | append(Paras,LocalVars,LV2), | |
194 | get_b_template2(Body,LV2,State,Template). | |
195 | get_b_template_aux(let(Paras,_Pred,Body),LocalVars,State,Template) :- !, | |
196 | append(Paras,LocalVars,LV2), | |
197 | get_b_template2(Body,LV2,State,Template). | |
198 | get_b_template_aux(var(Paras,Body),LocalVars,State,Template) :- !, | |
199 | append(Paras,LocalVars,LV2), | |
200 | get_b_template2(Body,LV2,State,Template). | |
201 | get_b_template_aux(choice(Options),LocalVars,State,Template) :- !, | |
202 | member(A,Options), | |
203 | get_b_template2(A,LocalVars,State,Template). | |
204 | get_b_template_aux(if(Options),LocalVars,State,Template) :- !, | |
205 | get_b_template_for_if(Options,LocalVars,State,Template). | |
206 | get_b_template_aux(precondition(Test,Body), LocalVars,State,Template) :- !, | |
207 | b_test_boolean_expression_cs(Test,[],State,'ANIMATION_(RIGHT_)CLICK',0), | |
208 | get_b_template2(Body,LocalVars,State,Template). | |
209 | get_b_template_aux(operation_call(TOpID,_Result,Parameters),LocalVars,State,Template) :- !, | |
210 | def_get_texpr_id(TOpID,op(OpID)), | |
211 | % TO DO: deal with Result | |
212 | maplist(eval_parameter(LocalVars,State,OpID),Parameters,TemplateArgs), | |
213 | Template =.. [OpID|TemplateArgs]. | |
214 | get_b_template_aux(Subst,_LocalVars,_State,_) :- | |
215 | add_error('ANIMATION_(RIGHT_)CLICK','Uncovered substitution in ANIMATION_(RIGHT_)CLICK: ',b(Subst,subst,[])),fail. | |
216 | ||
217 | :- use_module(probsrc(b_interpreter),[b_test_boolean_expression_cs/5, b_compute_expression_nowf/6]). | |
218 | get_b_template_for_if([b(if_elsif(Test,Body),_,_)|_], LocalVars,State,Template) :- | |
219 | b_test_boolean_expression_cs(Test,[],State,'ANIMATION_RIGHT_CLICK',0),!, | |
220 | get_b_template2(Body,LocalVars,State,Template). | |
221 | get_b_template_for_if([_|T],LV,State,Template) :- get_b_template_for_if(T,LV,State,Template). | |
222 | ||
223 | eval_parameter(LocalVars,_State,_,Parameter,Result) :- | |
224 | get_texpr_id(Parameter,PID), | |
225 | member(LV,LocalVars), get_texpr_id(LV,PID), | |
226 | !, | |
227 | Result = _. | |
228 | eval_parameter(_LocalVars,State,OpId,Parameter,Result) :- | |
229 | b_compute_expression_nowf(Parameter,[],State,Result,'operation parameter',OpId). | |
230 | ||
231 | ||
232 | react_to_item_right_click(CurID,X,Y,ActionStr,ResTransitionID,ResNewID) :- % print(react(CurID,X,Y,ActionStr)),nl, | |
233 | animation_mode(Mode), | |
234 | ( can_react_to_item_right_click(CurID,Mode,X,Y,TransitionTemplateList,ActionStr,TransitionID), | |
235 | get_template(TransitionTemplateList,TransitionTemplate), | |
236 | tcltk_interface:tcltk_perform_action(CurID,TransitionTerm,TransitionID,NewID), | |
237 | match_template(TransitionTemplate,TransitionTerm) | |
238 | -> debug_format(19,'Performed: ~w~n',[TransitionTemplate]), | |
239 | (get_template_rest(TransitionTemplateList,Rest) | |
240 | -> % there are other actions to execute (automatically after a user-click; useful for chess,...) | |
241 | execute_rest(Rest,TT), % currently only happens in XTL mode | |
242 | ResTransitionID = [TransitionID|TT], ResNewID = unknown | |
243 | % NewID points to first NewID, not to target; TO DO: modify command in case we support XTL in ProB2 UI | |
244 | ; ResTransitionID = TransitionID, ResNewID=NewID | |
245 | ) | |
246 | ; format('Cannot execute transition ~w~n Right-Click on (~w,~w)~n',[ActionStr,X,Y]), | |
247 | fail | |
248 | ). | |
249 | ||
250 | % note: a single click is also a item_drag from an object to itself | |
251 | tcltk_can_react_to_item_drag(FromTkPath,ToTkPath,Image) :- | |
252 | animation_mode(M), | |
253 | convert_tk_path_to_coordinates(FromTkPath,FromX,FromY), | |
254 | convert_tk_path_to_coordinates(ToTkPath,ToX,ToY), | |
255 | can_react_to_item_drag(M,FromX,FromY,ToX,ToY,ImageNr), | |
256 | (ImageNr=none -> Image='' | |
257 | ; get_animation_image_and_label(M,ImageNr,_,Lbl) -> Image=Lbl | |
258 | ; Image=''). | |
259 | ||
260 | tcltk_react_to_item_drag(FromTkPath,ToTkPath) :- | |
261 | convert_tk_path_to_coordinates(FromTkPath,FromX,FromY), | |
262 | convert_tk_path_to_coordinates(ToTkPath,ToX,ToY), | |
263 | debug_println(20,dragged_object(FromX,FromY,ToX,ToY)), | |
264 | animation_mode(M), | |
265 | react_to_item_drag(M,FromX,FromY,ToX,ToY). | |
266 | ||
267 | :- use_module(probsrc(xtl_interface),[xtl_animation_image_click_transition/6]). | |
268 | can_react_to_item_drag(Mode,FromX,FromY,ToX,ToY,ImageNr) :- | |
269 | current_state_id(CurID), | |
270 | get_item_drag_template_and_image(Mode,FromX,FromY,ToX,ToY,TransitionTemplateList,ImageNr), | |
271 | get_template(TransitionTemplateList,TransitionTemplate), | |
272 | transition(CurID,TransitionTemplate,_ActId,_DestID). | |
273 | ||
274 | % transition templates can also be lists; we just check if first action is feasible | |
275 | get_template([H|_],Template) :- !, % in XTL mode we allow lists of actions | |
276 | Template=H. | |
277 | get_template(Template,Template). % in B,TLA,Z,... mode we only execute a single event | |
278 | ||
279 | get_template_rest([_|T],T). | |
280 | ||
281 | get_item_drag_template_and_image(xtl,ColFromX,RowFromY,ToX,ToY,TransitionTemplate,ImageNr) :- !, | |
282 | xtl_animation_image_click_transition(RowFromY,ColFromX,ToY,ToX,TransitionTemplate,ImageNr). | |
283 | get_item_drag_template_and_image(b,FromX,FromY,ToX,ToY,Template,1000) :- | |
284 | % TO DO: provide a way to return image number | |
285 | Def_Name = 'ANIMATION_CLICK', | |
286 | b_get_click_subst_definition(Def_Name,NrArgs,_RawSubst,DefPos), | |
287 | temporary_set_preference(allow_local_operation_calls,true,CHNG), | |
288 | Scope = [prob_ids(visible),operation_bodies], % do not include variables; otherwise clash warnings; variables already included in operation_bodies | |
289 | (NrArgs = 2 -> DArgs = [integer(DefPos,ToX),integer(DefPos,ToY)] | |
290 | ; NrArgs = 4 -> DArgs = [integer(DefPos,FromX),integer(DefPos,FromY),integer(DefPos,ToX),integer(DefPos,ToY)] | |
291 | ; add_warning(state_viewer_images,'ANIMATION_CLICK should take 2 or 4 arguments, not: ',NrArgs,DefPos), | |
292 | fail | |
293 | ), | |
294 | type_with_errors(definition(DefPos,'ANIMATION_CLICK',DArgs),Scope,subst,Typed), | |
295 | reset_temporary_preference(allow_local_operation_calls,CHNG), | |
296 | State = [], % TO DO: get full state like for right click | |
297 | get_b_template(Typed,[],State,Template). | |
298 | ||
299 | ||
300 | react_to_item_drag(Mode,FromX,FromY,ToX,ToY) :- | |
301 | get_item_drag_template_and_image(Mode,FromX,FromY,ToX,ToY,TransitionTemplateList,_), | |
302 | (get_template(TransitionTemplateList,TransitionTemplate), | |
303 | tcltk_interface:tcltk_perform_action(TransitionTemplate,_) | |
304 | -> (get_template_rest(TransitionTemplateList,Rest) | |
305 | -> % there are other actions to execute | |
306 | execute_rest(Rest,_) | |
307 | ; true | |
308 | ) | |
309 | ; format('Cannot execute transition ~w~n Mouse Drag (~w,~w)->(~w,~w)~n', | |
310 | [TransitionTemplateList,FromX,FromY,ToX,ToY]), | |
311 | fail | |
312 | ). | |
313 | ||
314 | % execute remaining templates of action | |
315 | execute_rest([],Res) :- !, Res=[]. | |
316 | execute_rest([H|T],[TransitionID|TT]) :- | |
317 | execute_rest_action(H,TransitionID), !, | |
318 | execute_rest(T,TT). | |
319 | execute_rest(T,[]) :- format('~n*** Cannot execute rest of transition template ~w~n',[T]). | |
320 | ||
321 | execute_rest_action(b(Subst,subst,Info),TransId) :- !, | |
322 | execute_b_rest(Subst,Info,TransId). | |
323 | execute_rest_action(H,TransitionID) :- | |
324 | current_state_id(CurID), %print(try_auto_play(CurID,H)),nl, | |
325 | tcltk_interface:compute_all_transitions_if_necessary, | |
326 | current_state_id(CurID), | |
327 | (H=mcts_auto_play, tcltk_interface:mcts_auto_play_available | |
328 | -> tcltk_interface:tcltk_mcts_auto_play(TransitionID) | |
329 | ; tcltk_interface:tcltk_perform_action(CurID,H,TransitionID,_NewID) | |
330 | ). %print(perf(H,_NewID)),nl, | |
331 | ||
332 | execute_b_rest(external_subst_call('MCTS_AUTO_PLAY',_),Info,TransId) :- !, | |
333 | (\+ tcltk_interface:mcts_auto_play_available -> | |
334 | add_error(state_viewer_images,'MCTS_AUTO_PLAY not available, add GAME_PLAYER, ... defintions','',Info),fail | |
335 | ; tcltk_interface:tcltk_mcts_auto_play(TransId) -> true | |
336 | ; add_message(state_viewer_images,'MCTS_AUTO_PLAY not possible (end of game reached?)','',Info),fail | |
337 | ). | |
338 | execute_b_rest(RestAction,Info,_) :- | |
339 | add_error(state_viewer_images,'Currently only MCTS_AUTO_PLAY supported here:',b(RestAction,subst,Info),Info), | |
340 | fail. | |
341 | ||
342 | % if successful convert a Tk Widget Path like .stateviewer.sviewFrame.frame1.item1_3 | |
343 | % to coordinates of the state_viewer module: (1,3) | |
344 | convert_tk_path_to_coordinates(TkPath,XCoord,YCoord) :- | |
345 | split_last(TkPath,'.',_,NameOfItemClicked), | |
346 | atom_codes(NameOfItemClicked,Codes), | |
347 | get_suffix(Codes,Rest), | |
348 | !, | |
349 | split_chars(Rest,"_",[YCC,XCC]), % note: coordinates reversed in label | |
350 | number_codes(XCoord,XCC), | |
351 | number_codes(YCoord,YCC). | |
352 | convert_tk_path_to_coordinates(TkPath,_,_) :- format('*** Cannot convert TkPath ~w~n',[TkPath]),fail. | |
353 | ||
354 | get_suffix(Codes,Rest) :- append("item",Rest,Codes),!. | |
355 | get_suffix(Codes,Rest) :- append("text",Rest,Codes),!. | |
356 | get_suffix(Codes,Rest) :- append("frame",Rest,Codes),!. | |
357 | ||
358 | :- use_module(probsrc(custom_explicit_sets),[try_expand_custom_set_with_catch/3, is_set_value/2]). | |
359 | ||
360 | eval_definition_fun(AnimFunction,Nr,BState, FunctionRes) :- | |
361 | on_enumeration_warning( | |
362 | b_compute_expression_nowf(AnimFunction,[],BState,FunctionResCl,'ANIMATION_FUNCTION',Nr), | |
363 | fail), | |
364 | %% nl, print('FunctionResult'(FunctionResCl)),nl, | |
365 | is_set_value(FunctionResCl,eval_definition_fun), | |
366 | try_expand_custom_set_with_catch(FunctionResCl, ExpCl, eval_definition_fun), | |
367 | (translate_to_ints(ExpCl, FunctionRes) | |
368 | -> true /* already translate to ints so that override will work properly */ | |
369 | ; check_anim_function(ExpCl),fail | |
370 | ) % ,print('fres'(FunctionRes)),nl | |
371 | . | |
372 | ||
373 | check_anim_function([]) :- !. | |
374 | %check_anim_function(int(_)) :- !. % just a single picture; now gets converted by translate_to_ints | |
375 | check_anim_function([H|_]) :- !, | |
376 | (H = (RowCol,_Img), get_pair(RowCol,Row,Col) -> | |
377 | (get_int(Row,_),get_int(Col,_) -> true | |
378 | ; add_error(state_viewer_images,'Elements of ANIMATION_FUNCTION should be convertible to (INT*INT)*INT: ',H) | |
379 | ) | |
380 | ; H = (Row,ColImg), get_pair(ColImg,Col,_Img) -> | |
381 | (get_int(Row,_),get_int(Col,_) -> true | |
382 | ; add_error(state_viewer_images,'Elements of ANIMATION_FUNCTION should be convertible to INT*(INT*INT): ',H) | |
383 | ) | |
384 | ; add_error(state_viewer_images, | |
385 | 'Elements of the ANIMATION_FUNCTION should be of type (INT*INT)*INT: ',H) | |
386 | ). | |
387 | check_anim_function((A,B)) :- !, | |
388 | add_error(state_viewer_images,'ANIMATION_FUNCTION should be a set of pairs, not a pair: ',(A,B)). | |
389 | check_anim_function(X) :- !, | |
390 | add_error(state_viewer_images,'ANIMATION_FUNCTION should be of type (INT*INT) +-> INT (or similar): ',X). | |
391 | ||
392 | :- use_module(library(avl)). | |
393 | ||
394 | translate_to_ints(In,Out) :- empty_avl(E),translate_to_ints(In,E,Out). | |
395 | translate_to_ints([],_,[]) :-!. | |
396 | translate_to_ints(X,_,Res) :- get_int(X,IX),!,Res=[((1,1),int(IX))]. % a single picture | |
397 | translate_to_ints([H|T], AVL, Res) :- | |
398 | translate_element(H,AVL, NewAVL, Res, NewRes), | |
399 | translate_to_ints(T,NewAVL,NewRes). | |
400 | ||
401 | translate_element((RowCol,Img),AVL, NewAVL, Res, NewRes) :- get_pair(RowCol,Row,Col),!, | |
402 | get_int(Row,RI), get_int(Col,CI), (Img=II), | |
403 | add_image(RI,CI,II, AVL, NewAVL, Res, NewRes). | |
404 | translate_element((Row,ColImg),AVL, NewAVL, Res, NewRes) :- get_pair(ColImg,Col,Img),!, | |
405 | translate_element(((Row,Col),Img), AVL, NewAVL, Res, NewRes). | |
406 | translate_element((Row,Set),AVL,NewAVL,Res,NewRes) :- % Set can be relation or sequence | |
407 | % expand Set and associate elements with Row | |
408 | is_set_value(Set,translate_element), | |
409 | try_expand_custom_set_with_catch(Set, ESet,translate_element), | |
410 | findall((Row,El),member(El,ESet),All), | |
411 | l_translate(All,AVL,NewAVL,Res,NewRes). | |
412 | ||
413 | ||
414 | ||
415 | l_translate([],AVL,AVL,Res,Res). | |
416 | l_translate([H|T],AVL,NewAVL,Res,NewRes) :- translate_element(H,AVL,NewAVL1,Res,NewRes1), | |
417 | l_translate(T,NewAVL1,NewAVL,NewRes1,NewRes). | |
418 | ||
419 | add_image(RI,CI,II, AVL, NewAVL, Res, NewRes) :- | |
420 | (avl_fetch((RI,CI),AVL,ExistingValue) | |
421 | -> add_error(state_viewer_images, | |
422 | 'Animation function defines two images for (Row,Col):[img1,img2] = ',((RI,CI):[ExistingValue,II])), | |
423 | NewAVL = AVL, Res=NewRes | |
424 | ; avl_store((RI,CI),AVL,II,NewAVL), Res=[((RI,CI),II)|NewRes] | |
425 | ). | |
426 | ||
427 | /* a version of override which does not worry about correct typing */ | |
428 | % override_anim_fun(Fun1, Fun2, Res) :: Fun2 overrides Fun1 | |
429 | override_anim_fun([],Fun2,Res) :- !, Res=Fun2. | |
430 | override_anim_fun(int(_),Fun2,Res) :- !, Res=Fun2. | |
431 | override_anim_fun([((RowT,Col),Img)|T],Fun2,Res) :- !, | |
432 | (member(((RowT,Col),_),Fun2) | |
433 | -> Res = ResT % we use version from Fun2 | |
434 | ; Res = [((RowT,Col),Img)|ResT] | |
435 | ), override_anim_fun(T,Fun2,ResT). | |
436 | override_anim_fun(X,F,R) :- | |
437 | add_error(state_viewer_images,'Unknown Format for ANIMATION_FUNCTION_DEFAULT', X), | |
438 | R=F. | |
439 | ||
440 | % get minimum and maximum index for rows | |
441 | get_min_max_row([((Row,_Col),_Img)|T],Min,Max) :- !, | |
442 | number(Row), % this is already in a format for Tcl/Tk; not int(.) | |
443 | get_min_max_row2(T,Row,Row,Min,Max). | |
444 | get_min_max_row([],_,_) :- !, | |
445 | print('**** Empty ANIMATION_FUNCTION result'),nl, fail. | |
446 | get_min_max_row(Res,_,_) :- | |
447 | add_error(state_viewer_images, | |
448 | 'Unknown Format for ANIMATION_FUNCTION result, must be of type INT or (INT*INT)+->INT',Res), | |
449 | fail. | |
450 | ||
451 | get_min_max_row2([],Min,Max,Min,Max). | |
452 | get_min_max_row2([((Row,_Col),_Img)|T],Min,Max,MinR,MaxR) :- | |
453 | (Row<Min -> Min1=Row ; Min1=Min), | |
454 | (Row>Max -> Max1=Row ; Max1=Max), | |
455 | get_min_max_row2(T,Min1,Max1,MinR,MaxR). | |
456 | ||
457 | % get minimum and maximum index for columns | |
458 | get_min_max_col([((_Row,Col),_Img)|T],Min,Max) :- !, | |
459 | number(Col), % this is already in a format for Tcl/Tk; not int(.) | |
460 | get_min_max_col2(T,Col,Col,Min,Max). | |
461 | get_min_max_col2([],Min,Max,Min,Max). | |
462 | get_min_max_col2([((_Row,Col),_Img)|T],Min,Max,MinR,MaxR) :- | |
463 | (Col<Min -> Min1=Col ; Min1=Min), | |
464 | (Col>Max -> Max1=Col ; Max1=Max), | |
465 | get_min_max_col2(T,Min1,Max1,MinR,MaxR). | |
466 | ||
467 | get_int(Nr,R) :- number(Nr),!,R=Nr. | |
468 | get_int(int(R),R). | |
469 | get_int(fd(R,_Name),R). | |
470 | get_int(pred_true /* bool_true */,1). | |
471 | get_int(pred_false /* bool_false */,0). | |
472 | /* can sometimes do unwanted conversions and show images where we do not want them: | |
473 | get_int(string(A),Integer) :- % convert something like "Train1" -> 1 | |
474 | atom_codes(A,Codes), reverse(Codes,RC), | |
475 | get_number_prefix(RC,Prefix), | |
476 | Prefix \= [], reverse(Prefix,RP), | |
477 | number_codes(Integer,RP). | |
478 | ||
479 | get_number_prefix([N|T],[N|RT]) :- N >= 48, N =< 57, % Ascii of a digit 0-9 | |
480 | !, get_number_prefix(T,RT). | |
481 | get_number_prefix(_,[]). | |
482 | */ | |
483 | ||
484 | get_pair((A,B),A,B). | |
485 | get_pair(avl_set(node((int(1),A),true,1,empty,node((int(2),B),true,0,empty,empty))),A,B). % in TLA: <<a,b>> sometimes translated as sequence | |
486 | ||
487 | ||
488 | :- use_module(probsrc(bmachine),[b_get_true_expression_definition/1]). | |
489 | % add extra whitespace to make all text fields the same length | |
490 | % TO DO: maybe only pad within a column; not overall | |
491 | pad_text_frame_items(FrameItems,NewFrameItems) :- b_or_z_mode, | |
492 | (b_get_true_expression_definition('ANIMATION_STR_JUSTIFY_LEFT') ; | |
493 | b_get_true_expression_definition('ANIMATION_STR_JUSTIFY_RIGHT') ), | |
494 | longest_text(FrameItems,0,Len), | |
495 | Len>0, | |
496 | !, | |
497 | %print(longest(Len)),nl, | |
498 | pad_text_frame_items_to(FrameItems,Len,NewFrameItems). | |
499 | pad_text_frame_items(F,F). | |
500 | ||
501 | ||
502 | pad_text_frame_items_to([],_,[]). | |
503 | pad_text_frame_items_to([ItemLabel,Frame,Text|TR],Len,[ItemLabel,Frame,NewText|NTR]) :- | |
504 | (is_text_type(ItemLabel) | |
505 | -> atom_codes(Text,Codes), | |
506 | length(Codes,TLen), | |
507 | AdditionalSpaces is Len-TLen, gen_spaces(AdditionalSpaces,AS), | |
508 | (b_get_true_expression_definition('ANIMATION_STR_JUSTIFY_RIGHT') | |
509 | -> append(AS,Codes,NewCodes) | |
510 | ; append(Codes,AS,NewCodes) % 34 -> ' 39 -> " | |
511 | ), | |
512 | atom_codes(NewText,NewCodes) | |
513 | ; NewText=Text), | |
514 | pad_text_frame_items_to(TR,Len,NTR). | |
515 | ||
516 | gen_spaces(Nr,R) :- Nr<1,R=[]. % 34 -> ' | |
517 | gen_spaces(Nr,[32|T]) :- N1 is Nr-1, gen_spaces(N1,T). | |
518 | ||
519 | ||
520 | % find longest text label; so that we can pad the other ones with spaces | |
521 | longest_text([],R,R). | |
522 | longest_text([ItemLabel,_Frame,Text|TR],Acc,R) :- | |
523 | is_text_type(ItemLabel),!, | |
524 | atom_length(Text,Len), | |
525 | (Len>Acc -> NAcc = Len ; NAcc=Acc), | |
526 | longest_text(TR,NAcc,R). | |
527 | longest_text([_,_,_|TR],Acc,R) :- longest_text(TR,Acc,R). | |
528 | ||
529 | is_text_type(ItemLabel) :- atom_codes(ItemLabel,Codes), append("text",_,Codes). | |
530 | ||
531 | get_frame_items([],[]). | |
532 | get_frame_items([((Row,Col),Img)|T],[ItemLbl,FrameLbl,ImageLbl|TR]) :- | |
533 | convert_nr_into_tk_label(Row,"frame",FrameLbl), | |
534 | (get_int(Img,ImgNr),images(ImgNr,_,ImageLbl) | |
535 | -> convert_2nr_into_tk_label(Row,Col,"item",ItemLbl) | |
536 | ; convert_2nr_into_tk_label(Row,Col,"text",ItemLbl), | |
537 | (get_int(Img,ImgNr),get_animation_string(ImgNr,ImageLbl) -> true | |
538 | ; translate_value_for_tk_text(Img,ImageLbl) | |
539 | ) | |
540 | ), | |
541 | get_frame_items(T,TR). | |
542 | ||
543 | setup_frames(Min,Max,[]) :- Min>Max,!. | |
544 | setup_frames(Min,Max,[frame, FrameLbl, top |T]) :- | |
545 | convert_nr_into_tk_label(Min,"frame",FrameLbl), M1 is Min+1, | |
546 | setup_frames(M1,Max,T). | |
547 | ||
548 | :- dynamic images/3. | |
549 | ||
550 | ||
551 | get_animation_images(Mode,Res) :- clear_animation_images, | |
552 | findall( img(Nr,ImgFilename,Lbl), get_animation_image_and_label(Mode,Nr,ImgFilename,Lbl), List), | |
553 | flatten_img_list(List,Res). | |
554 | clear_animation_images :- retractall(images(_,_,_)). | |
555 | ||
556 | flatten_img_list([],[]). | |
557 | flatten_img_list([img(Nr,ImgFilename,Lbl)|T],[Lbl,ImgFilename|FT]) :- | |
558 | assertz(images(Nr,ImgFilename,Lbl)), flatten_img_list(T,FT). | |
559 | ||
560 | ||
561 | get_animation_image_and_label(Mode,Nr,ImgFilename,Lbl) :- | |
562 | get_animation_image_in_mode(Mode,Nr,ImgFilename), | |
563 | convert_nr_into_tk_label(Nr,"IMG",Lbl). | |
564 | ||
565 | get_animation_image_in_mode(xtl,Nr,S) :- !, xtl_animation_image(Nr,S). | |
566 | get_animation_image_in_mode(b,Nr,S) :- !, get_animation_image(Nr,S). | |
567 | get_animation_image_in_mode(csp_and_b,Nr,S) :- get_animation_image(Nr,S). | |
568 | % csp not covered | |
569 | ||
570 | get_animation_string(Nr,S) :- b_or_z_mode, | |
571 | number_codes(Nr,TailCodes), | |
572 | /* ANIMATION_STR */ | |
573 | atom_codes(Def_Name,[65,78,73,77,65,84,73,79,78,95,83,84,82|TailCodes]), | |
574 | b_get_definition_string_from_machine(Def_Name,S). | |
575 | ||
576 | ||
577 | convert_nr_into_tk_label(Nr,Prefix,Label) :- number_codes(Nr,Codes), | |
578 | append(Prefix,Codes,C), | |
579 | atom_codes(Label,C). | |
580 | ||
581 | :- use_module(library(lists),[append/2]). | |
582 | convert_2nr_into_tk_label(Nr1,Nr2,Prefix,Label) :- | |
583 | number_codes(Nr1,Codes1), number_codes(Nr2,Codes2), | |
584 | append([Prefix,Codes1,"_",Codes2],C), | |
585 | atom_codes(Label,C). | |
586 | ||
587 | % -------------------------- | |
588 | ||
589 | % a function for Java FX or other graphical frameworks | |
590 | % | ?- get_animation_images(L). | |
591 | % L = [image_file(0,'images/empty_box_white.gif'),...,image_file(6,'images/F.gif')] ? | |
592 | ||
593 | get_animation_images(SList) :- | |
594 | animation_mode(Mode), | |
595 | findall( image_file(Nr,File), get_animation_image_in_mode(Mode,Nr,File), List), | |
596 | sort(List,SList). | |
597 | ||
598 | get_animation_image_numbers(SList) :- | |
599 | animation_mode(Mode), | |
600 | findall( Nr, get_animation_image_in_mode(Mode,Nr,_File), List), % TO DO: also get animation strings | |
601 | sort(List,SList). | |
602 | ||
603 | % a function for Java FX or other graphical frameworks | |
604 | % | ?- get_animation_image_grid(1,G,M,X). | |
605 | %G = [entry(1,1,image(0)),entry(1,2,image(0)),entry(1,3,image(0)),entry(1,4,image(0)),...,entry(Row,Col,image(...)),(...,...)|...], | |
606 | %M = 1, | |
607 | %X = 6 ? | |
608 | ||
609 | :- use_module(probsrc(state_space),[visited_expression/2]). | |
610 | :- use_module(library(lists)). | |
611 | get_animation_image_grid(ID,Matrix,MinRow,MaxRow,MinCol,MaxCol) :- | |
612 | animation_mode(Mode), | |
613 | animation_function_possibly_active(Mode), % avoid expanding state; can be expensive | |
614 | visited_expression(ID,State), | |
615 | evaluate_animation_function(Mode,State,FunctionRes), | |
616 | get_min_max_row(FunctionRes,MinRow,MaxRow), | |
617 | get_min_max_col(FunctionRes,MinCol,MaxCol), | |
618 | get_animation_image_numbers(ImageNumbers), | |
619 | maplist(convert_animation_function(Mode,ImageNumbers),FunctionRes,Matrix). | |
620 | ||
621 | ||
622 | :- use_module(library(ordsets),[ord_member/2]). | |
623 | :- use_module(library(codesio),[write_to_codes/2]). | |
624 | ||
625 | convert_animation_function(Mode,ImageNumbers,((Row,Col),Res), | |
626 | entry(Row,Col,TranslatedRes)) :- | |
627 | convert_aux(Mode,ImageNumbers,Res,TranslatedRes). | |
628 | convert_aux(_Mode,ImageNumbers,Img,image(ImgNr)) :- get_int(Img,ImgNr),ord_member(ImgNr,ImageNumbers),!. | |
629 | convert_aux(_Mode,_,Str,text(StringLbl)) :- get_int(Str,StrNr),get_animation_string(StrNr,StringLbl),!. | |
630 | convert_aux(b,_,Value,text(PPValue)) :- translate_value_for_tk_text(Value,PPValue),!. | |
631 | convert_aux(_,_,Value,text(AtomValue)) :- compound(Value), | |
632 | !, | |
633 | write_to_codes(Value,VC), atom_codes(AtomValue,VC). | |
634 | convert_aux(_,_,Value,text(Value)). | |
635 | ||
636 | translate_value_for_tk_text(string(Str),Res) :- !, % show string without quotes | |
637 | Res=Str. % TO DO: do we need to escape something here? | |
638 | translate_value_for_tk_text(Value,PPValue) :- translate:translate_bvalue(Value,PPValue),!. | |
639 | ||
640 | % export to HTML | |
641 | %html_print_animation_image_grid(ID) :- html_print_animation_image_grid(user_output,ID,''). | |
642 | ||
643 | html_print_animation_image_grid(Stream,ID,FilePrefix) :- | |
644 | get_animation_image_grid(ID,Matrix,MinRow,_MaxRow,_MinCol,_MaxCol), | |
645 | sort(Matrix,SortedMatrix), | |
646 | start_tag(Stream,table), | |
647 | start_tag(Stream,tr),animation_mode(Mode), | |
648 | html_print(SortedMatrix,Stream,MinRow,Mode,FilePrefix), | |
649 | end_tag(Stream,table). | |
650 | ||
651 | html_print([],Stream,_,_,_FilePrefix) :- end_tag(Stream,tr). | |
652 | html_print([entry(Row,_Col,Img)|T],Stream,LastRow,Mode,FilePrefix) :- | |
653 | (Row>LastRow -> end_tag(Stream,tr),start_tag(Stream,tr) ; true), | |
654 | % TO DO: insert empty cells when we jump over Columns | |
655 | html_image(Img,Stream,Mode,FilePrefix), | |
656 | html_print(T,Stream,Row,Mode,FilePrefix). | |
657 | ||
658 | html_image(image(ImgNr),Stream,Mode,FilePrefix) :- get_animation_image_in_mode(Mode,ImgNr,File),!, | |
659 | ((FilePrefix='' ; is_absolute_path(File)) | |
660 | -> format(Stream,' <th><img src="~w"></th>~n',[File]) | |
661 | ; format(Stream,' <th><img src="~w~w"></th>~n',[FilePrefix,File]) | |
662 | ). | |
663 | html_image(text(T),Stream,_,_) :- !, | |
664 | format(Stream,' <th><tt>~w</tt></th>~n',[T]). | |
665 | html_image(T,Stream,_,_) :- | |
666 | format(Stream,' <th>ERROR: ~w</th>~n',[T]). | |
667 | ||
668 | start_tag(Stream,T) :- format(Stream,'<~w>~n',[T]). | |
669 | end_tag(Stream,T) :- format(Stream,'</~w>~n',[T]). | |
670 | ||
671 | :- use_module(probsrc(bmachine),[b_get_machine_animation_expression/2]). | |
672 | :- use_module(probsrc(translate),[translate_bvalue/2,translate_bexpression/2]). | |
673 | html_print_animation_expression(Stream,StateID) :- | |
674 | get_animation_expression_label_and_value(StateID,S1,S2), | |
675 | format(Stream,'~w=~w~n',[S1,S2]). | |
676 | html_print_animation_expression(_,_). | |
677 | ||
678 | get_animation_expression_label_and_value(StateID,S1,S2) :- b_or_z_mode, | |
679 | b_get_machine_animation_expression('ANIMATION_EXPRESSION',AExpr), | |
680 | visited_expression(StateID,State), | |
681 | state_corresponds_to_fully_setup_b_machine(State,BState), | |
682 | b_compute_expression_nowf(AExpr,[],BState,AValue,'ANIMATION_EXPRESSION',0), | |
683 | translate_bexpression(AExpr,S1), | |
684 | translate_bvalue(AValue,S2). | |
685 | ||
686 | :- use_module(probsrc(state_space), [transition/4,visited_expression/2, | |
687 | current_expression/2, current_state_id/1, | |
688 | get_action_term_trace/1, get_state_id_trace/1]). | |
689 | ||
690 | ||
691 | html_print_history(File) :- html_print_history(File,3). | |
692 | html_print_history(File,MaxOpDebugLevel) :- | |
693 | get_state_id_trace([root|StateIds]), | |
694 | get_action_term_trace(RAT), reverse(RAT,Actions), | |
695 | % TO DO: adapt path of images so that they work in File | |
696 | (currently_opened_file(SpecFile), atom(SpecFile) | |
697 | -> get_parent_directory(SpecFile,SParent), | |
698 | absolute_file_name(SParent, SAbsParent), %, [relative_to(Directory)]), | |
699 | get_parent_directory(File,TParent), | |
700 | absolute_file_name(TParent, TAbsParent), %, [relative_to(Directory)]), | |
701 | compute_prefix(SAbsParent,TAbsParent,FilePrefix) | |
702 | ; SpecFile=unknown, FilePrefix='.'), | |
703 | open(File,write,Stream,[encoding(utf8)]), | |
704 | html_header(Stream,true), | |
705 | html_info(Stream,Actions,SpecFile), | |
706 | html_print_history2(Stream,1,Actions,StateIds,none,FilePrefix,MaxOpDebugLevel), | |
707 | html_footer(Stream), | |
708 | close(Stream). | |
709 | ||
710 | :- use_module(probsrc(tools_strings),[ajoin_with_sep/3]). | |
711 | :- use_module(probsrc(tools),[split_atom/3,is_absolute_path/1]). | |
712 | ||
713 | % compute a file prefix that needs to be added to relocate relative path for OldPath to paths for NewPath: | |
714 | compute_prefix(OldPath,NewPath,FilePrefix) :- | |
715 | %print(old(OldPath)),nl, print(new(NewPath)),nl, | |
716 | split_atom(OldPath,['/'],O1), | |
717 | split_atom(NewPath,['/'],N1), | |
718 | max_prefix(O1,N1,_CommonPrefix,O2,N2), %print(common(CommonPrefix)),nl, | |
719 | maplist(gendotdot,N2,N3), | |
720 | append(N3,O2,UpdatePath), | |
721 | (UpdatePath=[] -> FilePrefix = '' | |
722 | ; append(UpdatePath,[''],U1), | |
723 | ajoin_with_sep(U1,'/',FilePrefix)), | |
724 | debug_println(9,update_prefix_for_images(FilePrefix)). | |
725 | ||
726 | gendotdot(_,'..'). | |
727 | ||
728 | max_prefix([H|T],[H|T2],[H|PT],Suffix1,Suffix2) :- !, | |
729 | max_prefix(T,T2,PT,Suffix1,Suffix2). | |
730 | max_prefix(S1,S2,[],S1,S2). | |
731 | ||
732 | html_print_state(Stream,ID,FilePrefix) :- | |
733 | (html_print_animation_image_grid(Stream,ID,FilePrefix) | |
734 | -> true ; format(Stream,'No visualisation available for state id ~w~n',[ID])), | |
735 | html_print_animation_expression(Stream,ID). | |
736 | ||
737 | :- use_module(probsrc(translate),[translate_event/2]). | |
738 | html_print_history2(_,_,[],[],_,_,_). | |
739 | html_print_history2(Stream,_,[],[ID|_],_,FilePrefix,_) :- | |
740 | html_print_state(Stream,ID,FilePrefix). | |
741 | html_print_history2(Stream,StepNr,[Action|AT],[ID|T],PreviousID,FilePrefix,MaxOpDebugLevel) :- | |
742 | translate_event(Action,AS), | |
743 | format(Stream,'~n~n<h2>~w. ~w</h2>~n',[StepNr,AS]), flush_output(Stream), | |
744 | html_print_action(Stream,Action,PreviousID,ID,MaxOpDebugLevel), | |
745 | html_print_state(Stream,ID,FilePrefix), | |
746 | S1 is StepNr+1, | |
747 | html_print_history2(Stream,S1,AT,T,ID,FilePrefix,MaxOpDebugLevel). | |
748 | ||
749 | :- use_module(probsrc(specfile),[get_operation_name/2]). %get_operation_arguments | |
750 | :- use_module(extrasrc(bvisual2),[html_debug_operation_for_stateid/4]). | |
751 | html_print_action(Stream,Action,PreviousID,_ID,MaxLevel) :- | |
752 | MaxLevel > 0, PreviousID \= none, | |
753 | get_operation_name(Action,OpName), | |
754 | !, | |
755 | % print operation guards up to MaxLevel nesting, TODO: use parameter values for this action | |
756 | (html_debug_operation_for_stateid(Stream,OpName,PreviousID,MaxLevel) % fails for INITIALISATION, SETUP_CONSTANTS | |
757 | -> true | |
758 | ; true). %add_internal_error('Failed: ',html_debug_operation_for_stateid(OpName,ID,MaxLevel))). | |
759 | html_print_action(_,_,_,_,_). | |
760 | ||
761 | html_header(Stream,Border) :- | |
762 | format(Stream,'<html>~n<head>~n<style>~ntable, th, td {~n',[]), | |
763 | (Border=true -> format(Stream,' border: 1px solid black;~n',[]) ; true), | |
764 | format(Stream,' border-collapse: collapse;~n}~nth, td {~n padding: 0px;~n}~n</style>~n</head>~n<body>',[]). | |
765 | ||
766 | ||
767 | html_footer(Stream) :- | |
768 | format(Stream,'</body>~n</html>~n',[]). | |
769 | ||
770 | :- use_module(probsrc(version), [version_str/1, revision/1, lastchangeddate/1]). | |
771 | :- use_module(library(system),[ datime/1]). | |
772 | :- use_module(probsrc(specfile),[currently_opened_file/1]). | |
773 | html_info(Stream,A,SpecFile) :- | |
774 | format(Stream,'<h2>Information</h2>~n<ul>~n',[]), | |
775 | datime(datime(Year,Month,Day,Hour,Min,_Sec)), | |
776 | format(Stream,' <li>Trace generated on ~w.~w.~w at ~w:~w</li>~n',[Day,Month,Year,Hour,Min]), | |
777 | length(A,Len), | |
778 | format(Stream,' <li>Trace length: ~w</li>~n',[Len]), | |
779 | animation_mode(Mode), | |
780 | format(Stream,' <li>Specification file (~w): ~w</li>~n',[Mode,SpecFile]), | |
781 | ((SpecFile \= unknown,get_file_revision_info(SpecFile,GitRev)) | |
782 | -> format(Stream,' <li>Git revision of file: ~s</li>~n',[GitRev]) ; true), | |
783 | ((SpecFile \= unknown,get_HEAD_revision_info(SpecFile,GitHeadRev)) | |
784 | -> format(Stream,' <li>Git revision of repository HEAD: ~s</li>~n',[GitHeadRev]) ; true), | |
785 | version_str(V), revision(R), lastchangeddate(LD), | |
786 | format(Stream,' <li>ProB version: ~w</li>~n',[V]), | |
787 | format(Stream,' <li>ProB revision: ~w (~w)</li>~n',[R, LD]), | |
788 | format(Stream,'</ul>~n',[]). | |
789 | ||
790 | % b_get_main_filename -> Project Info:, Model Version/Revision ? Can we ask git ? | |
791 | % git ls-files -s VSS_TrainStatus_HL3.mch diff | |
792 | % git rev-parse HEAD | |
793 | ||
794 | :- use_module(probsrc(system_call),[get_command_path/2, system_call_with_options/6]). | |
795 | :- use_module(probsrc(tools),[get_parent_directory/2]). | |
796 | ||
797 | get_file_revision_info(File,GitOutputText) :- | |
798 | get_parent_directory(File,CurWorkingDir), | |
799 | run_git_command(['ls-files','-s',File],CurWorkingDir,GitOutputText). | |
800 | get_HEAD_revision_info(ReferenceFile,GitOutputText) :- | |
801 | get_parent_directory(ReferenceFile,CurWorkingDir), | |
802 | run_git_command(['rev-parse','HEAD'],CurWorkingDir,GitOutputText). | |
803 | ||
804 | ||
805 | run_git_command(COMMAND,CurWorkingDir,GitOutputText) :- | |
806 | get_command_path(git,GitPath), | |
807 | (debug_mode(on) -> format(user_output,'Git: ~w with CWD: ~w~n',[GitPath,CurWorkingDir]) ; true), | |
808 | catch( | |
809 | system_call_with_options(GitPath,COMMAND,[cwd(CurWorkingDir)],GitOutputText,GitError,ExitCode), | |
810 | E, | |
811 | (debug_format(9,'Git exception: ~w~n',[E]),fail)), | |
812 | (debug_mode(on) | |
813 | -> format(user_output,'git command ~w~n:~s~nError: ~s~nExitCode:~w~n', | |
814 | [COMMAND,GitOutputText,GitError,ExitCode]) ; true), | |
815 | ExitCode = exit(0), GitError=[]. | |
816 | ||
817 |