Belle II Software  release-06-01-15
ProgressBarModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <framework/modules/core/ProgressBarModule.h>
10 
11 #include <framework/core/Environment.h>
12 #include <framework/utilities/Utils.h>
13 
14 #include <iostream>
15 #include <iomanip>
16 #include <unistd.h>
17 
18 using namespace std;
19 using namespace Belle2;
20 
21 
22 REG_MODULE(ProgressBar)
23 
24 ProgressBarModule::ProgressBarModule() : Module(), m_evtNr(0), m_nTotal(0), m_startTime(0), m_lastPrint(0), m_isTTY{false}
25 {
26  setDescription(R"DOC(
27  Display a progress bar and an estimate of remaining time when number of
28  events is known (e.g. reading from file, or -n switch used).
29 
30  The progress bar uses stderr for its output, so it works best when stdout
31  is piped to a file. However it should also work when printing direct to a
32  terminal.
33 
34  .. versionchanged:: release-03-00-00
35  the module now detects if it outputs to a terminal or into a file and
36  will only update the bar if it has changed and not use any control
37  characters to make log files much more readable than before.
38  )DOC");
39 }
40 
41 void ProgressBarModule::initialize()
42 {
43  m_evtNr = 0;
44  m_nTotal = Environment::Instance().getNumberOfEvents();
45  m_startTime = 0;
46  m_progress = 0;
47  // Check if we run on a tty or if we are writing to a log file. In the second
48  // case we don't use carriage return but just new lines and only update if the progress bar changes.
49  // Done here in case someone fiddles with the file descriptor before we process
50  m_isTTY = isatty(STDERR_FILENO);
51 }
52 
53 void ProgressBarModule::event()
54 {
55  if (m_nTotal == 0)
56  return;
57 
58  double clockSec = Utils::getClock() / 1e9;
59 
60  // To not count intialization time we only print output starting one second
61  // after the first event is processed
62  if (m_startTime == 0) {
63  // first call, let's start here
64  m_startTime = clockSec;
65  m_lastPrint = m_startTime;
66  return;
67  }
68 
69  if (clockSec - m_lastPrint > 1) { //every second
70  double elapsedSec = clockSec - m_startTime;
71  double ratio = double(m_evtNr) / m_nTotal;
72  double time_per_event = elapsedSec / m_evtNr;
73  auto remainingSeconds = (int)std::round((m_nTotal - m_evtNr) * time_per_event);
74 
75  if (remainingSeconds >= 0) {
76  const int bar_length = 50; //characters for bar
77 
78  //If we don't write to a tty only update if the progress bar changes
79  if (!m_isTTY && int(ratio * bar_length) <= m_progress) return;
80  // remember where we were
81  m_progress = ratio * bar_length;
82 
83  cerr << '[';
84  for (int i = 0; i < m_progress; ++i) cerr << '=';
85  cerr << '>';
86  for (int i = m_progress + 1; i < bar_length; ++i) cerr << ' ';
87  cerr << "] " << setw(3) << int(ratio * 100) << "% ";
88  if (remainingSeconds > 3600) {
89  int hours = remainingSeconds / 3600;
90  remainingSeconds %= 3600;
91  cerr << hours << "h";
92  }
93  if (remainingSeconds > 60) {
94  int minutes = remainingSeconds / 60;
95  remainingSeconds %= 60;
96  cerr << minutes << "m";
97  }
98  cerr << remainingSeconds << "s remaining";
99  //some spaces to overwrite other stuff
100  cerr << " ";
101  if (m_isTTY) {
102  //carriage return to go back to beginning of the line
103  cerr << "\r";
104  } else {
105  //not a terminal, just make it an output line
106  cerr << endl;
107  }
108  // make sure it's printed
109  cerr << flush;
110 
111  m_lastPrint = clockSec;
112  }
113  }
114 
115  ++m_evtNr;
116 }
117 
118 void ProgressBarModule::terminate()
119 {
120  cerr << "\n";
121 }
Base class for Modules.
Definition: Module.h:72
Display a progress bar and an estimate of remaining time when number of events is known (e....
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.