...

Source file src/golang.conradwood.net/go-easyops/utils/progressreporter.go

Documentation: golang.conradwood.net/go-easyops/utils

     1  package utils
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  /*
    10  * This is a simple command line "progress reporter".
    11  * It's usage is (intentionally) very simple.
    12  * Example:
    13  * pr := utils.ProgressReporter{}
    14  * pr.SetTotal(1000)
    15  * for i:=0;i<1000;i++ {
    16  *    // do something slow
    17  *    DoSomethingSlow()
    18  *    pr.Inc()
    19  *    pr.Print()
    20  * }
    21  * The above sippet will print a rate and an ETA once a second.
    22   */
    23  type ProgressReporter struct {
    24  	lastPrinted time.Time
    25  	eta         time.Time
    26  	etaCalced   time.Time
    27  	rate1       *rateCalculator
    28  	rate2       *rateCalculator
    29  	cur_rc      int       // 1==rate1, 2==rate2
    30  	since_rc    time.Time // when was cur_rc last changed ?
    31  	start       time.Time
    32  	total       uint64
    33  	done        uint64
    34  	RawPrint    bool
    35  	addlock     sync.Mutex
    36  	Prefix      string
    37  }
    38  
    39  func (p *ProgressReporter) SetTotal(total uint64) {
    40  	p.total = total
    41  }
    42  func (p *ProgressReporter) Set(a uint64) {
    43  	p.Add(a - p.done)
    44  }
    45  func (p *ProgressReporter) Inc() {
    46  	p.Add(1)
    47  }
    48  func (p *ProgressReporter) Add(a uint64) {
    49  	p.fixRates()
    50  	p.addlock.Lock()
    51  	defer p.addlock.Unlock()
    52  	p.rate1.Add(a)
    53  	p.rate2.Add(a)
    54  	p.done = p.done + a
    55  }
    56  func (p *ProgressReporter) Eta() time.Time {
    57  	if time.Since(p.etaCalced) < (time.Duration(5) * time.Second) {
    58  		return p.eta
    59  	}
    60  	left := float64(p.total - p.done)
    61  	r := p.Rate()
    62  	secs_to_go := time.Duration(left/r) * time.Second
    63  	p.eta = time.Now().Add(secs_to_go)
    64  	p.etaCalced = time.Now()
    65  	return p.eta
    66  }
    67  
    68  // return true if it actually printed stuff
    69  func (p *ProgressReporter) PrintSingleLine() bool {
    70  	s := p.String()
    71  	if s == "" {
    72  		return false
    73  	}
    74  	fmt.Printf("%c%s", byte(13), s)
    75  	return true
    76  }
    77  func (p *ProgressReporter) Print() bool {
    78  	s := p.String()
    79  	if s == "" {
    80  		return false
    81  	}
    82  	fmt.Println(s)
    83  	return true
    84  }
    85  
    86  func (p *ProgressReporter) fixRates() {
    87  	if p.rate1 == nil {
    88  		p.rate1 = &rateCalculator{name: "calc1", start: time.Now()}
    89  	}
    90  	if p.rate2 == nil {
    91  		p.rate2 = &rateCalculator{name: "calc2", start: time.Now()}
    92  	}
    93  	if p.cur_rc == 0 {
    94  		p.cur_rc = 1
    95  	}
    96  	if time.Since(p.since_rc) > time.Duration(5)*time.Second {
    97  		if p.cur_rc == 1 {
    98  			p.cur_rc = 2
    99  			p.rate1.Reset()
   100  		} else {
   101  			p.cur_rc = 1
   102  			p.rate2.Reset()
   103  		}
   104  		p.since_rc = time.Now()
   105  	}
   106  }
   107  func (p *ProgressReporter) Rate() float64 {
   108  	p.fixRates()
   109  
   110  	var rc *rateCalculator
   111  
   112  	rc = p.rate1
   113  	if p.cur_rc == 2 {
   114  		rc = p.rate2
   115  	}
   116  	//	fmt.Printf("Rate from %s\n", rc.String())
   117  	res := rc.Rate()
   118  	return res
   119  }
   120  func (p *ProgressReporter) String() string {
   121  	if (time.Since(p.lastPrinted)) < (time.Duration(1) * time.Second) {
   122  		return ""
   123  	}
   124  	p.lastPrinted = time.Now()
   125  	eta_s := p.Eta().Format("2006-01-02 15:04:05")
   126  	perc := float32(float32(p.done) / float32(p.total) * float32(100))
   127  	sp := ""
   128  	if p.Prefix != "" {
   129  		sp = fmt.Sprintf("[%s]: ", p.Prefix)
   130  	}
   131  	prefix := fmt.Sprintf("%sProcessing %d", sp, p.done)
   132  	if p.total != 0 {
   133  		prefix = fmt.Sprintf("%sProcessing %d of %d (%2.1f%%), ETA: %v", sp, p.done, p.total, perc, eta_s)
   134  	}
   135  	if p.RawPrint {
   136  		return prefix + fmt.Sprintf(", %.1f/sec", p.Rate())
   137  	} else {
   138  		return prefix + fmt.Sprintf(", %s/sec", PrettyNumber(uint64(p.Rate())))
   139  	}
   140  
   141  }
   142  
   143  type RateCalculator interface {
   144  	Add(a uint64)
   145  	Rate() float64
   146  	Reset()
   147  	String() string
   148  }
   149  
   150  func NewRateCalculator(name string) RateCalculator {
   151  	res := &rateCalculator{name: name, start: time.Now()}
   152  	return res
   153  
   154  }
   155  
   156  type rateCalculator struct {
   157  	start     time.Time
   158  	counter   uint64
   159  	additions int
   160  	resetted  bool
   161  	name      string
   162  }
   163  
   164  func (r *rateCalculator) Add(a uint64) {
   165  	r.additions++
   166  	r.counter = r.counter + a
   167  
   168  }
   169  func (r *rateCalculator) Rate() float64 {
   170  	elapsed := time.Since(r.start).Seconds()
   171  	z := float64(r.counter)
   172  	f := z / (elapsed)
   173  	return f
   174  }
   175  func (r *rateCalculator) Reset() {
   176  	r.start = time.Now()
   177  	r.additions = 0
   178  	r.counter = 0
   179  	r.resetted = false
   180  	//	fmt.Printf("Reset \"%s\"\n", r.name)
   181  }
   182  
   183  func (r *rateCalculator) String() string {
   184  	return fmt.Sprintf("%s (started %0.1f seconds ago, points=%d", r.name, time.Since(r.start).Seconds(), r.additions)
   185  }
   186  

View as plain text