1 % Heinrich Heine Universitaet Duesseldorf
2 % (c) 2025-2025 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(rule_validation,[setup_rules_extras/0, reset_rules_extras/0,
6 generate_report/1, generate_report/2, generate_report/3,
7 generate_dependency_graph/1]).
8
9 :- use_module(probsrc(module_information),[module_info/2]).
10 :- module_info(group,rules_dsl).
11 :- module_info(description,'This module provides functionality for the B Rules DSL.').
12
13 :- use_module(library(lists)).
14 :- use_module(library(aggregate),[forall/2]).
15 :- use_module(probsrc(bmachine),[bmachine_is_precompiled/0, get_machine_identifiers/2,
16 b_get_main_filename/1, b_get_definition_string_from_machine/2,
17 get_operation_info/2]).
18 :- use_module(probsrc(debug)).
19 :- use_module(probsrc(error_manager)).
20 :- use_module(probsrc(preferences),[get_preference/2]).
21 :- use_module(probsrc(specfile),[animation_minor_mode/1]).
22 :- use_module(probsrc(tools),[read_string_from_file/2, ajoin/2, get_tail_filename/2, split_filename/3]).
23 :- use_module(probsrc(tools_io),[safe_open_file/4, with_open_stream_to_codes/4]).
24
25 :- dynamic computation/1, rule/1, dependencies/2, tags/2, classification/2, rule_id/2.
26 :- dynamic rule_report_template_file_codes/2.
27
28 assert_from_template(Filename) :-
29 absolute_file_name(rulesdslsrc(Filename), Absolute, []),
30 read_string_from_file(Absolute,String),
31 assertz(rule_report_template_file_codes(Filename,String)).
32
33 :- assert_from_template('validation_report_header.html').
34 :- assert_from_template('validation_report_footer.html').
35
36 write_rule_report_template(HtmlFile,Stream) :-
37 rule_report_template_file_codes(HtmlFile,Codes),
38 format(Stream,'~s~n',[Codes]).
39
40 setup_rules_extras :-
41 reset_rules_extras,
42 (animation_minor_mode(rules_dsl) -> true
43 ; b_get_main_filename(BFile),
44 add_error(rule_validation,'Cannot set up rules DSL mode, current machine is not a rules machine (.rmch): ',BFile),
45 fail),
46 (bmachine_is_precompiled -> true ; add_error(setup_rules_extras,'bmachine not precompiled'), fail),
47 get_machine_identifiers(operations,Ops),
48 get_machine_identifiers(variables,Vars),
49 forall(member(Op,Ops),
50 (extract_rule_operations(Op,Vars),
51 extract_dependencies(Op),
52 extract_operation_attributes(Op))), % tags, classification, rule_id
53 debug_println(19,'% setup for rules DSL complete.').
54
55 reset_rules_extras :-
56 retractall(computation(_)),
57 retractall(rule(_)),
58 retractall(dependencies(_,_)),
59 retractall(tags(_,_)),
60 retractall(classification(_,_)),
61 retractall(rule_id(_,_)).
62
63 extract_rule_operations(Id,Vars) :-
64 counterexample_name_from_id(Id,CId), % we could double check with successful
65 (member(CId,Vars) -> assertz(rule(Id)) ; assertz(computation(Id))).
66
67 counterexample_name_from_id(Id,CId) :- ajoin([Id,'_Counterexamples'],CId).
68 successful_name_from_id(Id,CId) :- ajoin([Id,'_Successful'],CId).
69 unchecked_name_from_id(Id,CId) :- ajoin([Id,'_Unchecked'],CId).
70
71 :- use_module(probsrc(b_operation_guards),[get_operation_propositional_guards/3]).
72 extract_dependencies(OpName) :-
73 get_operation_propositional_guards(OpName,Guards,_RestBody),
74 findall(Id, (member(Guard, Guards), is_rule_comp_dependency(Guard,Id)), Deps),
75 assertz(dependencies(OpName,Deps)).
76
77 is_rule_comp_dependency(b(equal(b(identifier(Id),string,_),b(string('SUCCESS'),string,_)),pred,_), Id) :- rule(Id).
78 is_rule_comp_dependency(b(equal(b(identifier(Id),string,_),b(string('EXECUTED'),string,_)),pred,_), Id) :- computation(Id).
79
80 extract_operation_attributes(OpName) :-
81 get_operation_info(OpName,Infos),
82 (member(rules_info(RInfos),Infos) -> true ; RInfos = []),
83 (member(tags(Tags),RInfos) -> assertz(tags(OpName,Tags)) ; true),
84 (member(classification(Classification),RInfos)
85 -> (classification(Classification,OpList)
86 -> retractall(classification(Classification,_)),
87 assertz(classification(Classification,[OpName|OpList]))
88 ; assertz(classification(Classification,[OpName])))
89 ; true),
90 (member(rule_id(RId),RInfos) -> assertz(rule_id(OpName,RId)) ; true).
91
92 get_computations(Comps) :- findall(X, computation(X), Comps).
93 get_rules(Rules) :- findall(X, rule(X), Rules).
94
95 get_simple_rule_status(BState,RuleName,Status) :-
96 rule(RuleName),
97 member(bind(RuleName,string(Status)),BState).
98 get_rule_status(BState,RuleName,Status,CounterEx,SuccessMsg,UncheckedMsg,FailDep,NotExecDeps) :-
99 rule(RuleName),
100 dependencies(RuleName,Deps),
101 member(bind(RuleName,string(Status)),BState), % rule status: NOT_CHECKED, DISABLED, FAIL, SUCCESS
102 include(get_failed_dependencies(BState),Deps,FailDep),
103 include(get_not_executed_dependencies(BState),Deps,NotExecDeps),
104 get_rule_counterexamples(RuleName,BState,CounterEx),
105 get_rule_success_messages(RuleName,BState,SuccessMsg),
106 get_rule_unchecked_messages(RuleName,BState,UncheckedMsg).
107
108 get_simple_computation_status(BState,CompName,Status) :-
109 computation(CompName),
110 member(bind(CompName,string(Status)),BState).
111
112 get_failed_dependencies(State,Dep) :-
113 rule(Dep),
114 member(bind(Dep,string('FAIL')),State).
115
116 get_not_executed_dependencies(State,Dep) :-
117 member(bind(Dep,string(DepStatus)),State),
118 (DepStatus = 'DISABLED' ;
119 (rule(Dep)
120 -> DepStatus = 'NOT_CHECKED'
121 ; DepStatus = 'NOT_EXECUTED')).
122
123 get_rule_counterexamples(RuleName,State,CounterEx) :-
124 counterexample_name_from_id(RuleName,CId),
125 evaluate_rule_results(CId,State,CounterEx).
126
127 get_rule_success_messages(RuleName,State,SuccessMsg) :-
128 successful_name_from_id(RuleName,SId),
129 evaluate_rule_results(SId,State,SuccessMsg).
130
131 get_rule_unchecked_messages(RuleName,State,UncheckedMsg) :-
132 unchecked_name_from_id(RuleName,UId),
133 evaluate_rule_results(UId,State,UncheckedMsg).
134
135 evaluate_rule_results(Id,State,RuleRes) :-
136 member(bind(Id,RR0),State),
137 custom_explicit_sets:expand_custom_set_to_list(RR0,RR1), % TODO: enumeration warning (infinitely many messages)
138 maplist(extract_rule_result,RR1,RuleRes).
139
140 extract_rule_result((int(ErrorCode),string(ResultStr)),rule_result(ErrorCode,ResultStr)).
141
142 :- use_module(probsrc(state_space),[current_state_id/1,visited_expression/2]).
143 :- use_module(probsrc(specfile),[state_corresponds_to_initialised_b_machine/1,extract_variables_from_state/2]).
144 get_variables_for_export(StateID,VarState) :-
145 visited_expression(StateID,State),
146 state_corresponds_to_initialised_b_machine(State),!,
147 extract_variables_from_state(State,VarState).
148 get_variables_for_export(_,_) :-
149 add_error(rule_validation,'Cannot export rule validation results for uninitialised state'), fail.
150
151 %%%%%%%%% BEGIN REPORT %%%%%%%%%%
152 generate_report(File) :- generate_report(File,[]). % /1 default (for current state)
153 generate_report(File,Options) :- % /2 current state with options
154 current_state_id(ID),
155 generate_report(ID,File,Options).
156 generate_report(StateID,File,Options) :- % /3 any state with options TODO: language option
157 (animation_minor_mode(rules_dsl) -> true
158 ; b_get_main_filename(BFile),
159 add_error(rule_validation,'Cannot create rule validation report, is not a rules machine (.rmch): ',BFile),
160 fail),
161 get_variables_for_export(StateID,VarState),
162 (get_preference(prob_profiling_on,true)
163 -> tcltk_get_operations_profile_info(list(ProfileList)),
164 get_total_walltime(ProfileList,TotalWT)
165 ; ProfileList = [],
166 (member(checktime(TotalWT),Options) -> true ; TotalWT = -1) % option to provide checktime from ProB2 if profiling is off
167 ),
168 safe_open_file(File,write,Stream,[encoding(utf8)]),
169 split_filename(File,_Base,Ext), % infer report mode from file extension
170 (Ext=html -> generate_html_to_stream(Stream,VarState,NOptions,TotalWT,ProfileList) ;
171 Ext=xml -> generate_xml_to_stream(Stream,VarState,NOptions,TotalWT,ProfileList) ;
172 add_error(rule_validation,'unsupported output type for rule validation report: ',Ext)),
173 close(Stream),
174 formatsilent('% created rule validation report at ~w.~n',[File]).
175
176 %%%%%%%%% BEGIN HTML REPORT %%%%%%%%%%
177 :- use_module(probsrc(runtime_profiler),[tcltk_get_operations_profile_info/1]).
178 :- use_module(library(system),[datime/1]).
179 :- use_module(probsrc(tools_strings),[number_codes_min_length/3]).
180 :- use_module(probsrc(version),[version_str/1,revision/1]).
181 generate_html_to_stream(Stream,State,_Options,TotalWT,ProfileList) :-
182 write_rule_report_template('validation_report_header.html',Stream),
183 get_rules(Rules),
184 gen_report_infos(Stream,Rules,State),
185 forall(classification(CName,OpList),gen_classifications(Stream,CName,OpList,State,ProfileList)),
186 format(Stream,' <div class="rule-list normal">~n',[]),
187 forall((member(Rule,Rules), \+ (classification(_,OpList), member(Rule,OpList))), % all rules w/o classification
188 gen_rule_result(Stream,Rule,State,ProfileList)),
189 format(Stream,' </div>~n',[]),
190 (TotalWT > 0 -> convert_ms_s(TotalWT,WTPrint), format(Stream,' Check finished after: ~w~n',[WTPrint]) ; true),
191 version_str(VStr), revision(VRev),
192 format(Stream,' <br>ProB Version: ~w (~w)~n',[VStr,VRev]),
193 datime(datime(Yr,Mon,Day,Hr,Min,_Sec)),
194 number_codes_min_length(Min,2,MC),
195 format(Stream,' <br>Generated on ~w/~w/~w at ~w:~s~n',[Day,Mon,Yr,Hr,MC]),
196 write_rule_report_template('validation_report_footer.html',Stream).
197
198 gen_report_infos(Stream,Rules,BState) :-
199 length(Rules,NrRules),
200 precollect_statuses(Rules,BState,NrC,NrS,NrF,NrD),
201 b_get_main_filename(Filename),
202 get_tail_filename(Filename,MName),
203 format(Stream,' Checked machine: <b>~w</b>~n',[MName]),
204 (b_get_definition_string_from_machine('RULE_REPORT_CONTEXT',RepCtx)
205 -> format(Stream,' <br>With context: <b>~w</b>~n',[RepCtx])
206 ; true),
207 format(Stream,' <div style="line-height:25px;">~n',[]),
208 format(Stream,' <br>Total number of rules: <b>~w</b>~n',[NrRules]),
209 format(Stream,' <br>Number of checked rules: <b>~w</b>~n',[NrC]),
210 format(Stream,' <br>Number of successful rules: <b>~w</b>~n',[NrS]),
211 format(Stream,' <br>Number of failed rules: <b>~w</b>~n',[NrF]),
212 format(Stream,' <br>Number of disabled rules: <b>~w</b>~n',[NrD]),
213 format(Stream,' </div>~n',[]),
214 format(Stream,' <br>~n',[]).
215
216 get_total_walltime([_|T],WT) :- get_total_walltime1(T,WT). % ignore table legend
217 get_total_walltime1([],0).
218 get_total_walltime1([list([_,_,WT0,_,_,_,_])|T],WT) :-
219 get_total_walltime1(T,WT1),
220 WT is WT0+WT1.
221
222 precollect_statuses(Rules,BState,NrC,NrS,NrF,NrD) :-
223 maplist(get_simple_rule_status(BState), Rules, Statuses),
224 count_statuses(Statuses,NrS,NrF,NrD),
225 NrC is NrS+NrF.
226
227 count_statuses([],0,0,0).
228 count_statuses([Status|T],NrS,NrF,NrD) :-
229 count_statuses(T,S,F,D),
230 (Status = 'SUCCESS' -> NrS is S+1 ; NrS = S),
231 (Status = 'FAIL' -> NrF is F+1 ; NrF = F),
232 (Status = 'DISABLED' -> NrD is D+1 ; NrD = D).
233
234 gen_classifications(Stream,CName,OpList,BState,ProfileList) :-
235 format(Stream,' <div class="classification-list">~n',[]),
236 format(Stream,' <div class="classification-header" onclick="openDetails(\'classification_~w\')">~n',[CName]),
237 length(OpList,NrOps),
238 format(Stream,' <b>~w</b> (~w)~n',[CName,NrOps]),
239 format(Stream,' </div>~n',[]),
240 format(Stream,' <div class="rule-list classification" id="classification_~w">~n',[CName]),
241 forall(member(Rule,OpList), gen_rule_result(Stream,Rule,BState,ProfileList)),
242 format(Stream,' </div>~n',[]),
243 format(Stream,' </div>~n',[]),
244 format(Stream,' </div>~n',[]).
245
246 gen_rule_result(Stream,Rule,BState,ProfileList) :-
247 get_rule_status(BState,Rule,Status,CounterEx,SuccessMsg,UncheckedMsg,FailDep,NotExecDep),
248 (Status = 'SUCCESS'
249 -> ((SuccessMsg \= [] ; UncheckedMsg \= [])
250 -> format(Stream,' <div class="rule-header pointer green" onclick="openDetails(\'~w\')">~n',[Rule])
251 ; format(Stream,' <div class="rule-header green">~n',[]))
252 ; Status = 'FAIL'
253 -> format(Stream,' <div class="rule-header pointer red" onclick="openDetails(\'~w\')">~n',[Rule]),
254 format(Stream,' <span class="icon">⚠</span>~n',[])
255 ; Status = 'DISABLED' -> format(Stream,' <div class="rule-header grey">~n',[])
256 ; Status = 'NOT_CHECKED'
257 -> ((FailDep \= [] ; NotExecDep \= [])
258 -> (FailDep \= [] -> C = 'orange' ; C = 'lightgrey'), % FAIL dependencies: orange, else grey
259 P = 'pointer'
260 ; C = 'white', P = '' % no conflicts with dependencies
261 ),
262 format(Stream,' <div class="rule-header ~w ~w" onclick="openDetails(\'~w\')">~n',[P,C,Rule]),
263 (C = 'orange' -> format(Stream,' <span class="icon">⚠</span>~n',[]) ; true)
264 ; format(Stream,' <div class="rule-header">~n',[])
265 ),
266 format(Stream,' ~w',[Rule]),
267 (rule_id(Rule,RuleId)
268 -> format(Stream,' [~w]~n',[RuleId])
269 ; format(Stream,'~n',[])),
270 (tags(Rule,Tags)
271 -> format(Stream,' ~n',[]),
272 forall(member(Tag,Tags), format(Stream,' <div class="tag">~w</div>~n',[Tag])),
273 format(Stream,' ~n',[])
274 ; true),
275 format(Stream,' <b>~w</b>~n',[Status]),
276 (member(list([Rule,_,WT,_,_,_,_]),ProfileList)
277 -> convert_ms_s(WT,WTPrint),
278 format(Stream,' <span style="float: right;"><em>(~w)</em></span>~n',[WTPrint])
279 ; true), % ignore if not available
280 format(Stream,' </div>~n',[]),
281 gen_rule_content_list(Stream,Rule,CounterEx,SuccessMsg,UncheckedMsg,FailDep,NotExecDep),
282 fail.
283 gen_rule_result(_Stream,_,_,_).
284
285 convert_ms_s(Time,CTime) :- Time >= 1000 -> TimeSec is Time/1000, ajoin([TimeSec,' s'],CTime) ; ajoin([Time,' ms'],CTime).
286
287 gen_rule_content_list(Stream,Rule,CounterEx,SuccessMsg,UncheckedMsg,FailDep,NotExecDep) :-
288 length(CounterEx,CELen),
289 length(SuccessMsg,SMLen),
290 length(UncheckedMsg,UCLen),
291 length(FailDep,FDLen),
292 length(NotExecDep,NEDLen),
293 ((CELen > 0 ; SMLen > 0 ; UCLen > 0 ; FDLen > 0 ; NEDLen > 0) % create tag only if at least one child exists
294 -> format(Stream,' <div class="rule-content" id="~w">~n',[Rule]),
295 (CELen > 0
296 -> format(Stream,' <b>~w</b> Violations found:~n',[CELen]),
297 format(Stream,' <ul>~n',[]),
298 forall(member(CE,CounterEx),gen_rule_content_list_aux(Stream,CE)),
299 format(Stream,' </ul>~n',[])
300 ; true
301 ),
302 (SMLen > 0
303 -> format(Stream,' <b>~w</b> successful checks:~n',[SMLen]),
304 format(Stream,' <ul>~n',[]),
305 forall(member(SM,SuccessMsg),gen_rule_content_list_aux(Stream,SM)),
306 format(Stream,' </ul>~n',[])
307 ; true
308 ),
309 (UCLen > 0
310 -> format(Stream,' <b>~w</b> unchecked items:~n',[UCLen]),
311 format(Stream,' <ul>~n',[]),
312 forall(member(UC,UncheckedMsg),gen_rule_content_list_aux(Stream,UC)),
313 format(Stream,' </ul>~n',[])
314 ; true
315 ),
316 (FDLen > 0
317 -> format(Stream,' Rule could not be checked due to ~w failed dependencies:~n',[FDLen]),
318 format(Stream,' <ul>~n',[]),
319 forall(member(FD,FailDep),gen_rule_content_list_aux(Stream,FD)),
320 format(Stream,' </ul>~n',[])
321 ; true
322 ),
323 (NEDLen > 0
324 -> format(Stream,' Rule could not be checked due to ~w unchecked dependencies:~n',[NEDLen]),
325 format(Stream,' <ul>~n',[]),
326 forall(member(NED,NotExecDep),gen_rule_content_list_aux(Stream,NED)),
327 format(Stream,' </ul>~n',[])
328 ; true
329 ),
330 format(Stream,' </div>~n',[])
331 ; true),
332 fail.
333 gen_rule_content_list(_,_,_,_,_,_,_).
334
335 gen_rule_content_list_aux(Stream,rule_result(ErrorCode,Message)) :- !,
336 format(Stream,' <li>(~w) ~w</li>~n',[ErrorCode,Message]).
337 gen_rule_content_list_aux(Stream,Dependency) :-
338 format(Stream,' <li>~w</li>~n',[Dependency]).
339
340 % TODO:
341 % Violations cannot be enumerated (infinitely many).
342 % Successful checks cannot be enumerated (infinitely many).
343 %%%%%%%%% END HTML REPORT %%%%%%%%%%
344
345 %%%%%%%%% BEGIN XML REPORT %%%%%%%%%
346 generate_xml_to_stream(Stream,State,_Options,TotalWT,ProfileList) :-
347 get_rules(Rules),
348 format(Stream,'<ruleValidationReport>~n',[]),
349 gen_xml_report_statistics(Stream,Rules,State,TotalWT),
350 forall(classification(CName,OpList),gen_xml_classifications(Stream,CName,OpList,State,ProfileList)),
351 forall((member(Rule,Rules), \+ (classification(_,OpList), member(Rule,OpList))), % all rules w/o classification
352 gen_xml_rule_result(Stream,Rule,State,ProfileList)),
353 format(Stream,'</ruleValidationReport>~n',[]).
354
355 gen_xml_report_statistics(Stream,Rules,BState,TotalWT) :-
356 length(Rules,NrRules),
357 precollect_statuses(Rules,BState,NrC,NrS,NrF,NrD),
358 format(Stream,' <statistics>~n',[]),
359 b_get_main_filename(Filename),
360 get_tail_filename(Filename,MName),
361 format(Stream,' <checkedMachine>~w</checkedMachine>~n',[MName]),
362 (b_get_definition_string_from_machine('RULE_REPORT_CONTEXT',RepCtx)
363 -> format(Stream,' <context>~w</context>~n',[RepCtx])
364 ; true),
365 format(Stream,' <totalNumber>~w</totalNumber>~n',[NrRules]),
366 format(Stream,' <checked>~w</checked>~n',[NrC]),
367 format(Stream,' <succeeded>~w</succeeded>~n',[NrS]),
368 format(Stream,' <failed>~w</failed>~n',[NrF]),
369 format(Stream,' <disabled>~w</disabled>~n',[NrD]),
370 format(Stream,' <checkTime>~w</checkTime>~n',[TotalWT]),
371 version_str(VStr), revision(VRev),
372 format(Stream,' <proBVersion>~w (~w)</proBVersion>~n',[VStr,VRev]),
373 datime(datime(Yr,Mon,Day,Hr,Min,Sec)),
374 number_codes_min_length(Mon,2,MonC),
375 number_codes_min_length(Day,2,DC),
376 number_codes_min_length(Hr,2,HC),
377 number_codes_min_length(Min,2,MC),
378 number_codes_min_length(Sec,2,SC),
379 format(Stream,' <date>~w-~s-~sT~s:~s:~s</date>~n',[Yr,MonC,DC,HC,MC,SC]),
380 format(Stream,' </statistics>~n',[]).
381
382 gen_xml_classifications(Stream,CName,OpList,BState,ProfileList) :-
383 length(OpList,NrOps),
384 format(Stream,' <classification name="~w" nrRules="~w">~n',[CName,NrOps]),
385 forall(member(Rule,OpList), gen_xml_rule_result(Stream,Rule,BState,ProfileList)),
386 format(Stream,' </classification>~n',[]).
387
388 gen_xml_rule_result(Stream,Rule,BState,ProfileList) :-
389 get_rule_status(BState,Rule,Status,CounterEx,SuccessMsg,UncheckedMsg,FailDep,NotExecDep),
390 (rule_id(Rule,RID)
391 -> format(Stream,' <rule name="~w" status="~w" id="~w">~n',[Rule,Status,RID])
392 ; format(Stream,' <rule name="~w" status="~w">~n',[Rule,Status])),
393 (tags(Rule,Tags)
394 -> forall(member(Tag,Tags), format(Stream,' <tag>~w</tag>~n',[Tag]))
395 ; true),
396 forall(member(rule_result(ErrId,Msg),CounterEx), format(Stream,' <counterExample errorType="~w">~w</counterExample>~n',[ErrId,Msg])),
397 forall(member(rule_result(BodyId,Msg),SuccessMsg), format(Stream,' <successMessage ruleBody="~w">~w</successMessage>~n',[BodyId,Msg])),
398 forall(member(rule_result(BodyId,Msg),UncheckedMsg), format(Stream,' <uncheckedMessage ruleBody="~w">~w</uncheckedMessage>~n',[BodyId,Msg])),
399 forall(member(FD,FailDep), format(Stream,' <failedDependency>~w</failedDependency>~n',[FD])),
400 forall(member(NED,NotExecDep), format(Stream,' <notCheckedDependency>~w</notCheckedDependency>~n',[NED])),
401 (member(list([Rule,_,WT,_,_,_,_]),ProfileList)
402 -> format(Stream,' <walltime>~w</walltime>~n',[WT])
403 ; true), % ignore if not available
404 format(Stream,' </rule>~n',[]),
405 fail.
406 gen_xml_rule_result(_Stream,_,_,_).
407 %%%%%%%%% END XML REPORT %%%%%%%%%%
408 %%%%%%%%% END REPORT %%%%%%%%%%%%%%
409
410 %%%%%%%%% BEGIN DEPENDENCY GRAPH %%%%%%%%%
411 % TODO: tooltip with counterexample?
412 :- use_module(probsrc(state_space),[current_expression/2]).
413 :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/3,use_new_dot_attr_pred/7]).
414 generate_dependency_graph(File) :-
415 (animation_minor_mode(rules_dsl) -> true
416 ; b_get_main_filename(BFile),
417 add_error(rule_validation,'Cannot create rule dependency graph, is not a rules machine (.rmch): ',BFile),
418 fail),
419 current_state_id(CurID),
420 get_variables_for_export(CurID,VarState),
421 gen_dot_graph(File,
422 use_new_dot_attr_pred(rule_validation:rule_graph_node_predicate(VarState)),
423 use_new_dot_attr_pred(rule_validation:rule_graph_trans_predicate(VarState))).
424
425 rule_graph_node_predicate(State,OpName,none,[style/'filled',label/OpName,tooltip/OpName|Attr]) :-
426 dependencies(OpName,_), % all relevant operations
427 get_node_attributes(State,OpName,Attr).
428
429 get_node_attributes(State,OpName,[shape/RShape,fillcolor/StatusColor,tooltip/Tooltip]) :-
430 get_simple_rule_status(State,OpName,Status), !,
431 get_preference(rule_graph_shape,RShape),
432 (Status = 'FAIL' -> get_preference(failed_rule_colour,StatusColor) ;
433 Status = 'SUCCESS' -> get_preference(successful_rule_colour,StatusColor) ;
434 Status = 'DISABLED' -> get_preference(disabled_rule_colour,StatusColor) ;
435 get_preference(not_checked_rule_colour,StatusColor)), % NOT_CHECKED, TODO: maybe UNCHECKED
436 ajoin(['Rule: ',OpName,', Status: ',Status],Tooltip).
437 get_node_attributes(State,OpName,[shape/CompShape,fillcolor/StatusColor,tooltip/Tooltip]) :-
438 get_simple_computation_status(State,OpName,Status), !,
439 get_preference(computation_graph_shape,CompShape),
440 (Status = 'EXECUTED' -> get_preference(executed_computation_colour,StatusColor) ;
441 Status = 'COMPUTATION_DISABLED' -> get_preference(disabled_computation_colour,StatusColor) ;
442 get_preference(not_executed_computation_colour,StatusColor)), % NOT_EXECUTED
443 ajoin(['Computation: ',OpName,', Status: ',Status],Tooltip).
444
445 rule_graph_trans_predicate(State,NodeID,SuccID,[label/''|Attr]) :-
446 dependencies(NodeID,Deps),
447 member(SuccID,Deps),
448 get_trans_attributes(State,SuccID,Attr).
449
450 get_trans_attributes(State,DepName,[color/StatusColor,tooltip/Tooltip]) :-
451 get_simple_rule_status(State,DepName,Status), !,
452 ((Status = 'FAIL' ; Status = 'DISABLED')
453 -> get_preference(failed_dependency_colour,StatusColor), TTStatus = ' is not available.' ;
454 Status = 'SUCCESS' -> get_preference(succeeded_dependency_colour,StatusColor), TTStatus = ' is available.' ;
455 get_preference(normal_dependency_colour,StatusColor), TTStatus = ' is available.'), % NOT_CHECKED
456 ajoin(['Required rule ',DepName,' with status ',Status,TTStatus],Tooltip).
457 get_trans_attributes(State,DepName,[color/StatusColor,tooltip/Tooltip]) :-
458 get_simple_computation_status(State,DepName,Status), !,
459 (Status = 'COMPUTATION_DISABLED' -> get_preference(failed_dependency_colour,StatusColor), TTStatus = ' is not available.' ;
460 get_preference(normal_dependency_colour,StatusColor), TTStatus = ' is available.'),
461 ajoin(['Required computation ',DepName,' with status ',Status,TTStatus],Tooltip).
462 %%%%%%%%% END DEPENDENCY GRAPH %%%%%%%%%