DiaDes  0.1
DIAgnosis of Discrete-Event System
Component2Dot.cc
Go to the documentation of this file.
1 
57 #include<iostream>
58 #include<fstream>
59 #include<list>
60 #include<string>
61 #include<set>
62 #include<boost/program_options.hpp>
63 #include <boost/date_time/posix_time/posix_time.hpp>
64 
65 #include <boost/filesystem.hpp>
66 #include <boost/filesystem/fstream.hpp>
67 
68 
71 
72 #include"CmdInterface.hh"
73 using namespace std;
74 using namespace Diades::Automata;
75 using namespace Diades::Utils;
76 using namespace Diades::CmdInterface;
77 
78 namespace po = boost::program_options;
79 namespace fs = boost::filesystem;
80 
81 
82 /************************************************************************************/
83 
84 typedef enum { OUTPUT=0 } Option;
85 typedef enum { DESCOMP=0 } FileExtension;
86 unsigned numberOfOptions = 1;
88 
89 
91 vector<string> options(numberOfOptions);
92 vector<bool> isSet(numberOfOptions,false);
93 
94 string description="Usage: dd-component-to-dot [file1.des_comp file2.des_comp file3.des_comp...] [--output template]\n\t Compute the dot graph associated to the component files. If no file as parameters,\n\t read the models from the standard input.\n\tIf --output is set with the name 'template' then the written dot files have\n\t the following names: 'template0.dot template1.dot...'\n\tIf no --output is set, write the result to the standard output ";
95 
97 {
98  options[OUTPUT] = "--output";
99  fileExtensions[DESCOMP] = "des_comp";
100 }
101 
102 /************************************************************************************/
103 
104 
105 
106 void exportToDot(const string & filename,
107  int depth,
108  const vector<string> & states,
109  ostream & out);
110 
111 void exportBackwardToDot(const string & filename,
112  int depth,
113  const vector<string> & states,
114  ostream & out);
115 
116 void checkDescompFile(const string & filename);
117 
118 
119 
120 
121 /************************************************************************************/
122 
123 int main(int argc, char * argv[])
124 {
125 
126  try
127  {
128  int depth = -1;
129  std::string output = "";
130  po::options_description desc{"Options"};
131  desc.add_options()
132  ("help,h", "Help about the command line.")
133  ("output,o",po::value<string>(&output), "Output file template (if several files are exported they are written to the files template0.dot template1.dot ...) or output file name if only one file to display (not that if this option is not set, it displays the result on the standard output).")
134  ("depth,d",po::value<int>(&depth), "The maximal depth to visit from the given states (if depth is unset, there is no limit).")
135  ("state,s", po::value<std::vector<std::string>>()->multitoken()->composing(), "State from which the display can start (if this option is unset, the display starts from the initial states of the component. Many states can be added as follows: -s q1 q2 and/or -s q1 -s q2 q3...")
136  ("component,c", po::value<std::vector<std::string>>()->multitoken()->composing(), "Component to display (many components can be added). The option '--component/-c f1.des_comp f2.des_comp' can be simply written as 'f1.des_comp f2.des_comp'.")
137  ("backward,b", po::bool_switch()->default_value(false), "Backward display (visiting source states instead of target states)");
138  po::positional_options_description posDesc;
139  posDesc.add("component", -1);
140  po::command_line_parser parser{argc, argv};
141  parser.options(desc).positional(posDesc).allow_unregistered();
142  po::parsed_options parsed_options = parser.run();
143  po::variables_map vm;
144  store(parsed_options, vm);
145  notify(vm);
146 
147  if(vm.count("help"))
148  {
149  std::cout << "DIADES, LAAS-CNRS, 2006-2015, 7 avenue du Colonel Roche, Toulouse, France" << std::endl;
150  std::cout << "Contact: yannick.pencole@laas.fr" << std::endl;
151 
152  std::cout << "This command exports the given components (des_comp format) to 'graphviz/dot' format." << std::endl << std::endl;
153  std::cout << desc << std::endl;
154  std::cout << "Examples: " << std::endl;
155  std::cout << "\t dd-component-to-dot file.des_comp" << std::endl;
156  std::cout << "\t Print the dot format of the component file.des_comp on the standard output" << std::endl << std::endl;
157  std::cout << "\t dd-component-to-dot file1.des_comp file.des_comp" << std::endl;
158  std::cout << "\t Print the dot format of the components file1.des_comp and file2.des_comp on the standard output" << std::endl << std::endl;
159  std::cout << "\t dd-component-to-dot file.des_comp -o result.dot" << std::endl;
160  std::cout << "\t Print the dot format of the component file.des_comp in the file result.dot" << std::endl << std::endl;
161  std::cout << "\t dd-component-to-dot file1.des_comp file.des_comp -o result" << std::endl;
162  std::cout << "\t Print the dot format of the components file1.des_comp and file2.des_comp in the files result0.dot result1.dot" << std::endl << std::endl;
163 
164  std::cout << "\t dd-component-to-dot file.des_comp -d 10" << std::endl;
165  std::cout << "\t Print the dot format of the subpart of the component file.des_comp on the standard output from the initial states to the depth 10 " << std::endl << std::endl;
166  std::cout << "\t dd-component-to-dot file.des_comp -d 10 -s q1 q3" << std::endl;
167  std::cout << "\t Print the dot format of the subpart of the component file.des_comp on the standard output from states q1 and q3 to the depth 10 " << std::endl << std::endl;
168  std::cout << "\t dd-component-to-dot file.des_comp -d 10 -s q1 q3 " << std::endl;
169  std::cout << "\t Print the dot format of the subpart of the component file.des_comp on the standard output from states q1 and q3 to the depth 10 " << std::endl << std::endl;
170  std::cout << "\t dd-component-to-dot file.des_comp -d 10 -s q1 q3 -b" << std::endl;
171  std::cout << "\t Print the dot format of the subpart of the component file.des_comp on the standard output from states q1 and q3 to the depth 10 in backward mode (predecessors of q1 and q3) " << std::endl << std::endl;
172  }
173  else
174  {
175  if(vm.count("component"))
176  {
177  if(vm["component"].as<std::vector<std::string>>().size() > 1)
178  {
179  if(vm.count("state"))
180  {
181  std::cerr << "ERROR: option state is not allowed when there are several component files to display" << std::endl;
182  exit(1);
183  }
184  }
185  }
186  else
187  {
188  std::cerr << "ERROR: no component file has been found" << std::endl;
189  exit(1);
190  }
191  std::vector<std::string> states;
192  if(vm.count("state"))
193  {
194  states = vm["state"].as<std::vector<std::string>>();
195  }
196  if(output=="")
197  {
198  for(unsigned i = 0; i < vm["component"].as<std::vector<std::string>>().size(); ++i)
199  {
200  checkDescompFile(vm["component"].as<std::vector<std::string>>().operator[](i));
201  if(vm["backward"].as<bool>())
202  {
203  exportBackwardToDot(vm["component"].as<std::vector<std::string>>().operator[](i),
204  depth,states,cout);
205  }
206  else
207  {
208  exportToDot(vm["component"].as<std::vector<std::string>>().operator[](i),
209  depth,states,cout);
210  }
211  }
212  }
213  else
214  {
215 
216  for(unsigned i = 0; i < vm["component"].as<std::vector<std::string>>().size(); ++i)
217  {
218  std::stringstream filename;
219  filename << output << i << ".dot";
220 
221  checkDescompFile(vm["component"].as<std::vector<std::string>>().operator[](i));
222  fs::ofstream file(fs::path(filename.str()));
223  if(vm["backward"].as<bool>())
224  {
225  exportBackwardToDot(vm["component"].as<std::vector<std::string>>().operator[](i),
226  depth,states,file);
227  }
228  else
229  {
230  exportToDot(vm["component"].as<std::vector<std::string>>().operator[](i),
231  depth,states,file);
232  }
233  file.close();
234  }
235  }
236  }
237  }
238 
239  catch (const std::exception &ex)
240  {
241  std::cerr << ex.what() << '\n';
242  }
243  return 0;
244 }
245 
246 
247 
248 
249 
250 /************************************************************************************/
251 
252 void getStartingStates(const Component & component,
253  const vector<string> & states,
254  std::vector<Diades::Automata::State> & startingStates)
255 {
256  startingStates.clear();
257  if(states.empty())
258  {
259  for(unordered_set<State>::const_iterator it = component.initialStateBegin();
260  it != component.initialStateEnd();
261  ++it)
262  {
263  startingStates.push_back(*it);
264  }
265 
266  }
267  else
268  {
269  for(const std::string & stateLabel : states)
270  {
271  startingStates.push_back(component.getState(stateLabel));
272  }
273  }
274 
275 }
276 
277 
278 
279 
280 /************************************************************************************/
281 void exportToDot(const string & filename,
282  int depth,
283  const vector<string> & states,
284  ostream & out)
285 {
286  ObservableComponent component;
287  component.importDesCompModel(filename);
288  if(out)
289  {
290  out << "digraph G {" << std::endl;
291  out << "\tratio=fill;" << std::endl;
292  out << "\tpage=\"8.5,11.6\";" << std::endl;
293  out << "\tsize=\"7.5,10.5\";" << std::endl;
294  std::vector<Diades::Automata::State> startingStates;
295  getStartingStates(component,states,startingStates);
297  Diades::Graph::EdgeMap<bool> visitedTransition;
298  visited.init(component.behaviour(),false);
299  visitedTransition.init(component.behaviour(),false);
300  for(unsigned i = 0; i < startingStates.size(); ++i)
301  {
302  out << "\t" << startingStates[i] << "\t [label=\"";
303  out << component.getLabel(startingStates[i]);
304  out << "\"];" << endl;
305  out << "\t init" << startingStates[i] << "\t [label=\"\",color=white];" << endl;
306  out << "\t init" << startingStates[i] << " -> " << startingStates[i] << ";" << endl;
307  visited[startingStates[i]] = true;
308  }
309 
310  for(unsigned i = 0; i < startingStates.size(); ++i)
311  {
312  std::list< std::pair<State,ObservableComponent::OutputTransitionIterator> > queue;
313  Diades::Graph::NodeMap<int> currentDepth;
315  saturated.init(component.behaviour(),false);
316  currentDepth.init(component.behaviour(),component.numberOfStates(),-1);
317  currentDepth[startingStates[i]] = 0;
318 
319  if(depth != 0)
320  {
321  queue.push_back(std::make_pair(startingStates[i],component.outputTransitionBegin(startingStates[i])));
322  while(!queue.empty())
323  {
324  State currentState = queue.front().first;
325  if( queue.front().second == component.outputTransitionEnd(currentState))
326  {
327  saturated[currentState] = true;
328  queue.pop_front();
329  }
330  else
331  {
332  Transition currentTransition = *queue.front().second;
333  ++queue.front().second;
334  State target = currentTransition.target();
335  if(!visited[target])
336  {
337  out << "\t" << target << "\t [label=\"";
338  out << component.getLabel(target);
339  out << "\"];" << endl;
340  visited[target]=true;
341  }
342  if(currentDepth[target] == -1)
343  {
344  currentDepth[target] = currentDepth[currentState] + 1;
345  }
346  if(!visitedTransition[currentTransition])
347  {
348  out << "\t" << currentState << " -> " << target;
349  out << "[label=\"" << component.getEvent(currentTransition).nickname();
350  if(component.mask().isIdentifiable(component.getEvent(currentTransition)))
351  {
352  out << "\", color=blue];" << endl;
353  }
354  else
355  {
356  if(component.mask().isObservable(component.getEvent(currentTransition)))
357  {
358  out << " -> {";
359  for(ObservableMask::ObservableEventIterator obsIt = component.mask().observableBegin(component.getEvent(currentTransition));
360  obsIt != component.mask().observableEnd(component.getEvent(currentTransition));
361  ++obsIt)
362  {
363  out << obsIt->nickname();
365  ++obsIt2;
366  if(obsIt2 != component.mask().observableEnd(component.getEvent(currentTransition)))
367  {
368  out << ",";
369  }
370  }
371  if(component.mask().isDetectable(component.getEvent(currentTransition)))
372  {
373  out << "}\",color=blue];" << endl;
374  }
375  else
376  {
377  out << "}\",color=green];" << endl;
378  }
379  }
380  else
381  {
382  out << "\"];" << endl;
383  }
384  }
385  visitedTransition[currentTransition] = true;
386  }
387  if(!saturated[target])
388  {
389  if((currentDepth[target] < depth) || (depth==-1))
390  {
391  queue.push_back(std::make_pair(target,component.outputTransitionBegin(target)));
392  }
393  }
394  }
395 
396  }
397  }
398  }
399  out << "}" << endl;
400  }
401  else
402  {
403  std::cerr << "ERROR: cannot write on the select output stream" << std::endl;
404  }
405 }
406 
407 /************************************************************************************/
408 
409 
410 void exportBackwardToDot(const string & filename,
411  int depth,
412  const vector<string> & states,
413  ostream & out)
414 {
415  ObservableComponent component;
416  component.importDesCompModel(filename);
417  if(out)
418  {
419  out << "digraph G {" << std::endl;
420  out << "\tratio=fill;" << std::endl;
421  out << "\tpage=\"8.5,11.6\";" << std::endl;
422  out << "\tsize=\"7.5,10.5\";" << std::endl;
423  std::vector<Diades::Automata::State> startingStates;
424  getStartingStates(component,states,startingStates);
426  Diades::Graph::EdgeMap<bool> visitedTransition;
427  visited.init(component.behaviour(),false);
428  visitedTransition.init(component.behaviour(),false);
429  for(unsigned i = 0; i < startingStates.size(); ++i)
430  {
431  out << "\t" << startingStates[i] << "\t [label=\"";
432  out << component.getLabel(startingStates[i]);
433  out << "\"];" << endl;
434  out << "\t init" << startingStates[i] << "\t [label=\"\",color=white];" << endl;
435  out << "\t init" << startingStates[i] << " -> " << startingStates[i] << ";" << endl;
436  visited[startingStates[i]] = true;
437  }
438 
439  for(unsigned i = 0; i < startingStates.size(); ++i)
440  {
441  std::list< std::pair<State,ObservableComponent::InputTransitionIterator> > queue;
442  Diades::Graph::NodeMap<int> currentDepth;
444  saturated.init(component.behaviour(),false);
445  currentDepth.init(component.behaviour(),component.numberOfStates(),-1);
446  currentDepth[startingStates[i]] = 0;
447 
448  if(depth != 0)
449  {
450  queue.push_back(std::make_pair(startingStates[i],component.inputTransitionBegin(startingStates[i])));
451  while(!queue.empty())
452  {
453  State currentState = queue.front().first;
454  if( queue.front().second == component.inputTransitionEnd(currentState))
455  {
456  saturated[currentState] = true;
457  queue.pop_front();
458  }
459  else
460  {
461  Transition currentTransition = *queue.front().second;
462  ++queue.front().second;
463  State source = currentTransition.source();
464  if(!visited[source])
465  {
466  out << "\t" << source << "\t [label=\"";
467  out << component.getLabel(source);
468  out << "\"];" << endl;
469  visited[source]=true;
470  }
471  if(currentDepth[source] == -1)
472  {
473  currentDepth[source] = currentDepth[currentState] + 1;
474  }
475  if(!visitedTransition[currentTransition])
476  {
477  out << "\t" << source << " -> " << currentState;
478  out << "[label=\"" << component.getEvent(currentTransition).nickname();
479  if(component.mask().isIdentifiable(component.getEvent(currentTransition)))
480  {
481  out << "\", color=blue];" << endl;
482  }
483  else
484  {
485  if(component.mask().isObservable(component.getEvent(currentTransition)))
486  {
487  out << " -> {";
488  for(ObservableMask::ObservableEventIterator obsIt = component.mask().observableBegin(component.getEvent(currentTransition));
489  obsIt != component.mask().observableEnd(component.getEvent(currentTransition));
490  ++obsIt)
491  {
492  out << obsIt->nickname();
494  ++obsIt2;
495  if(obsIt2 != component.mask().observableEnd(component.getEvent(currentTransition)))
496  {
497  out << ",";
498  }
499  }
500  if(component.mask().isDetectable(component.getEvent(currentTransition)))
501  {
502  out << "}\",color=blue];" << endl;
503  }
504  else
505  {
506  out << "}\",color=green];" << endl;
507  }
508  }
509  else
510  {
511  out << "\"];" << endl;
512  }
513  }
514  visitedTransition[currentTransition] = true;
515  }
516  if(!saturated[source])
517  {
518  if((currentDepth[source] < depth) || (depth==-1))
519  {
520  queue.push_back(std::make_pair(source,component.inputTransitionBegin(source)));
521  }
522  }
523  }
524  }
525  }
526  }
527  out << "}" << endl;
528  }
529  else
530  {
531  std::cerr << "ERROR: cannot write on the select output stream" << std::endl;
532  }
533 }
534 
535 
536 
537 
538 /************************************************************************************/
539 void checkDescompFile(const string & filename)
540 {
541  fs::path filepath(filename);
542  if (fs::exists(filepath))
543  {
544  if (fs::is_regular_file(filepath)) // is p a regular file?
545  {
546 
547  }
548  else
549  {
550  std::cerr << "ERROR: " << filepath << " is not a regular file (maybe a directory..." << std::endl;
551  exit(1);
552  }
553  }
554  else
555  {
556  std::cerr << "ERROR: " << filepath << " does not exist" << std::endl;
557  exit(1);
558  }
559  if(!Diades::CmdInterface::fileSuffixOk(filename,"des_comp"))
560  {
561  std::cerr << "ERROR: " << filepath << " does not end with a 'des_comp' suffix. Expecting a component model in the 'des_comp' format." << std::endl;
562  exit(1);
563  }
564 }
565 
Graph::Graph & behaviour()
Definition: Component.hh:215
void checkDescompFile(const string &filename)
unsigned numberOfOptions
Some utilities to deal with the command line.
Diades::Graph::Edge Transition
Definition: Component.hh:46
FileExtension
void exportBackwardToDot(const string &filename, int depth, const vector< string > &states, ostream &out)
Option
void init(Graph &g, SizeType capacity=0, ValueType dflt=ValueType())
Definition: NodeMap.hh:315
const Label & nickname() const
Definition: Event.hh:190
int main(int argc, char *argv[])
vector< string > fileExtensions(numberOfFileExtensions)
STL namespace.
const StateLabel & getLabel(State state) const
Definition: Component.hh:521
InputTransitionIterator inputTransitionBegin(State s) const
Definition: Component.hh:936
StIndexes states
Definition: Run.cc:266
Definition: Run.cc:87
set< Event >::const_iterator ObservableEventIterator
InitialStateIterator initialStateEnd() const
Definition: Component.hh:648
void initialiseOptions()
An observable Component defined as a automaton.
virtual bool importDesCompModel(const string &filename)
unsigned numberOfFileExtensions
InputTransitionIterator inputTransitionEnd(State s) const
Definition: Component.hh:948
InitialStateIterator initialStateBegin() const
Definition: Component.hh:638
unsigned numberOfStates() const
Definition: Component.hh:563
State getState(const StateLabel &label) const
Definition: Component.hh:534
bool fileSuffixOk(const string &modelFile, const string &suffix)
void init(Graph &g, SizeType capacity=0, ValueType dflt=ValueType())
Definition: EdgeMap.hh:314
ObservableEventIterator observableBegin() const
bool isDetectable(const Event &e) const
OutputTransitionIterator outputTransitionBegin(State s) const
Definition: Component.hh:913
bool isIdentifiable(const Event &e) const
void getStartingStates(const Component &component, const vector< string > &states, std::vector< Diades::Automata::State > &startingStates)
const ObservableMask & mask() const
vector< string > options(numberOfOptions)
ObservableEventIterator observableEnd() const
bool isObservable(const Event &e) const
const Event & getEvent(Transition t) const
Definition: Component.hh:304
Diades::Graph::Node State
Definition: BeliefState.hh:36
void exportToDot(const string &filename, int depth, const vector< string > &states, ostream &out)
string description
vector< bool > isSet(numberOfOptions, false)
OutputTransitionIterator outputTransitionEnd(State s) const
Definition: Component.hh:925