Skip to content

File NPProgressDisplay.cxx

File List > core > NPProgressDisplay.cxx

Go to the documentation of this file

#include "NPProgressDisplay.h"

// use to figure out the terminal window size
#include <iostream>
#include <sstream>
#include <sys/ioctl.h> //ioctl() and TIOCGWINSZ
#include <unistd.h>    // for STDOUT_FILENO

using namespace nptool;
ProgressDisplay::ProgressDisplay(long long event_to_process, long long size_to_read, std::chrono::milliseconds interval)
    : m_event_to_process(event_to_process), m_size_to_read(size_to_read), m_interval(interval), m_odd_even(true),
      m_begin(std::chrono::system_clock::now()) {}
void ProgressDisplay::MakeLine(bool force) {
  // Check the terminal window size
  struct winsize w;
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);

  // reset the line
  m_line.clear();
  if (!m_odd_even)
    m_line.resize(w.ws_col, '*');
  else
    m_line.resize(w.ws_col, '-');

  // Event base progress display
  if (m_event_to_process > 0) {
    static std::string progress;
    static std::stringstream prg;

    // compute progress
    static double ratio;
    ratio = (double)(m_event_processed) / m_event_to_process;

    // compute evt rate
    static double rate_evt;
    static long long previous_evt = 0;
    static std::string rate_evt_unit = "";
    // compute ETA
    static int second_left, minute_left, hour_left = 0;

    if (!force) {
      rate_evt = 1000 * ((m_event_processed - previous_evt) / ((double)m_interval.count()));
      second_left = (m_event_to_process - m_event_processed) / rate_evt;
      minute_left = hour_left = 0;
      if (second_left >= 3600) {
        hour_left = second_left / 3600;
        second_left -= 3600 * hour_left;
      }
      if (second_left >= 60) {
        minute_left = second_left / 60;
        second_left -= minute_left * 60;
      }

      previous_evt = m_event_processed;
      if (rate_evt > 1e9) {
        rate_evt /= 1e9;
        rate_evt_unit = "G";
      }
      else if (rate_evt > 1e6) {
        rate_evt /= 1e6;
        rate_evt_unit = "M";
      }
      else if (rate_evt > 1e3) {
        rate_evt /= 1e3;
        rate_evt_unit = "k";
      }
      if (rate_evt > 1)
        rate_evt = (int)(rate_evt);
    }

    // compute size rate
    static double rate_size;
    static long long previous_size = 0;
    static std::string rate_size_unit = "";

    if (!force && m_size_read > 0) {
      rate_size = 1000 * ((m_size_read - previous_size) / ((double)m_interval.count()));
      previous_size = m_size_read;
      if (rate_size > 1e9) {
        rate_size /= 1e9;
        rate_size_unit = "G";
      }
      else if (rate_size > 1e6) {
        rate_size /= 1e6;
        rate_size_unit = "M";
      }
      else if (rate_size > 1e3) {
        rate_size /= 1e3;
        rate_size_unit = "k";
      }
      if (rate_size > 1)
        rate_size = (int)(rate_size);
    }

    prg.str("");
    prg << " Progress: " << (int)(ratio * 100) << "%  (" << m_event_processed << "/" << m_event_to_process << ")";
    prg << " | ETA: ";

    if (hour_left)
      prg << hour_left << "h";
    if (minute_left || hour_left)
      prg << minute_left << "m";

    prg << second_left << "s";
    prg << " | Rates: " << rate_evt << " " << rate_evt_unit << "evt/s";

    if (m_size_read > 0)
      prg << " (" << rate_size << " " << rate_size_unit << "b/s)";

    prg << " ";

    progress = prg.str();

    // put progress in the middle
    if (m_line.length() > progress.length())
      m_line.replace(m_line.length() / 2 - progress.length() / 2, progress.length(), progress);
    return;
  }
  // Size base progress display
  else if (m_size_to_read > 0) {
    static std::string progress;
    static std::stringstream prg;

    // compute progress
    static double ratio;
    ratio = (double)(m_size_read) / m_size_to_read;

    // compute size rate
    static double rate_size;
    static long long size_read;
    static long long previous_size = 0;
    static std::string rate_size_unit = "";
    // compute ETA
    static int second_left, minute_left, hour_left = 0;

    if (!force) {
      rate_size = 1000 * ((m_size_read - previous_size) / ((double)m_interval.count()));
      second_left = (m_size_to_read - m_size_read) / rate_size;
      minute_left = hour_left = 0;

      if (second_left >= 3600) {
        hour_left = second_left / 3600;
        second_left -= 3600 * hour_left;
      }
      if (second_left >= 60) {
        minute_left = second_left / 60;
        second_left -= minute_left * 60;
      }

      previous_size = m_size_read;
      if (rate_size > 1e9) {
        rate_size /= 1e9;
        rate_size_unit = "G";
      }
      else if (rate_size > 1e6) {
        rate_size /= 1e6;
        rate_size_unit = "M";
      }
      else if (rate_size > 1e3) {
        rate_size /= 1e3;
        rate_size_unit = "k";
      }
      if (rate_size > 1)
        rate_size = (int)(rate_size);
    }

    // compute evt rate
    static double rate_evt;
    static long long previous_evt = 0;
    static std::string rate_evt_unit = "";
    if (!force) {
      rate_evt = 1000 * ((m_event_processed - previous_evt) / ((double)m_interval.count()));
      previous_evt = m_event_processed;

      if (rate_evt > 1e9) {
        rate_evt /= 1e9;
        rate_evt_unit = "G";
      }
      else if (rate_evt > 1e6) {
        rate_evt /= 1e6;
        rate_evt_unit = "M";
      }
      else if (rate_evt > 1e3) {
        rate_evt /= 1e3;
        rate_evt_unit = "k";
      }
      if (rate_evt > 1)
        rate_evt = (int)(rate_evt);
    }

    prg.str("");
    prg << " Progress: " << (int)(ratio * 100) << "%  ";
    prg << " | ETA: ";

    if (hour_left)
      prg << hour_left << "h";
    if (minute_left || hour_left)
      prg << minute_left << "m";

    prg << second_left << "s";
    prg << " | Rates: " << rate_evt << " " << rate_evt_unit << "evt/s";

    if (m_size_read > 0)
      prg << " (" << rate_size << " " << rate_size_unit << "b/s)";

    prg << " ";

    progress = prg.str();

    // put progress in the middle
    if (m_line.length() > progress.length())
      m_line.replace(m_line.length() / 2 - progress.length() / 2, progress.length(), progress);
    return;
  }

  // target less display (online case typically), only show running
  else {
    static std::string running = "    Running    ";

    // put Running in the middle
    m_line.replace(m_line.length() / 2 - running.length() / 2, running.length(), running);

    return;
  }

  return;
}

void ProgressDisplay::AttemptDisplay(long long event_processed, long long size_read, bool force) {
  auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - m_begin);
  static bool finished = false;
  if (event_processed < 0)
    m_event_processed++;
  else
    m_event_processed = event_processed;
  if (size_read > 0)
    m_size_read = size_read;

  // do the display every interval
  if (diff.count() > m_interval.count() || force) {

    if ((m_event_to_process > 0 && m_event_processed >= m_event_to_process) ||
        (m_size_to_read > 0 && size_read >= m_size_to_read))
      finished = true;

    if (!finished)
      std::cout << "\r" << nptool::cli_yellow;
    else
      std::cout << "\r" << nptool::cli_green;

    MakeLine(force);
    std::cout << m_line << nptool::cli_restore_color << std::flush;
    // reset the timer
    m_begin = std::chrono::system_clock::now();
  }
}
void ProgressDisplay::ForceDisplay(long long event_processed, long long size_read) {
  AttemptDisplay(event_processed, size_read, true);
}