gitee.com/quant1x/gox@v1.7.6/progressbar/bar.go (about)

     1  package progressbar
     2  
     3  import (
     4  	"fmt"
     5  	"gitee.com/quant1x/gox/api"
     6  	"gitee.com/quant1x/gox/logger"
     7  	"sync"
     8  	"time"
     9  )
    10  
    11  type Bar struct {
    12  	mu       sync.Mutex
    13  	line     int
    14  	prefix   string
    15  	total    int
    16  	width    int
    17  	advance  chan bool
    18  	done     chan bool
    19  	currents map[string]int
    20  	current  int
    21  	before   int
    22  	rate     int
    23  	speed    int
    24  	cost     int
    25  	estimate int
    26  	fast     int
    27  	slow     int
    28  	srcUnit  string
    29  	dstUnit  string
    30  	change   int
    31  	closed   bool
    32  	start    time.Time
    33  }
    34  
    35  var (
    36  	bar1 string
    37  	bar2 string
    38  )
    39  
    40  const (
    41  	defaultFast = 20
    42  	defaultSlow = 5
    43  )
    44  
    45  func initBar(width int) {
    46  	for i := 0; i < width; i++ {
    47  		bar1 += "="
    48  		bar2 += "-"
    49  	}
    50  }
    51  
    52  func NewBar(line int, prefix string, total int) *Bar {
    53  	if total <= 0 {
    54  		return nil
    55  	}
    56  
    57  	if line <= 0 {
    58  		gMaxLine++
    59  		line = gMaxLine
    60  	}
    61  	if line > gMaxLine {
    62  		gMaxLine = line
    63  	}
    64  
    65  	bar := &Bar{
    66  		line:     line,
    67  		prefix:   prefix,
    68  		total:    total,
    69  		fast:     defaultFast,
    70  		slow:     defaultSlow,
    71  		width:    100,
    72  		advance:  make(chan bool),
    73  		done:     make(chan bool),
    74  		currents: make(map[string]int),
    75  		change:   1,
    76  		start:    time.Now(),
    77  	}
    78  
    79  	initBar(bar.width)
    80  	go bar.updateCost()
    81  	go bar.run()
    82  
    83  	return bar
    84  }
    85  
    86  func (b *Bar) SetUnit(src string, dst string, change int) {
    87  	b.srcUnit = src
    88  	b.dstUnit = dst
    89  	b.change = change
    90  
    91  	if b.change == 0 {
    92  		b.change = 1
    93  	}
    94  }
    95  
    96  func (b *Bar) SetSpeedSection(fast, slow int) {
    97  	if fast > slow {
    98  		b.fast, b.slow = fast, slow
    99  	} else {
   100  		b.fast, b.slow = slow, fast
   101  	}
   102  }
   103  
   104  func (b *Bar) Add(n ...int) {
   105  	b.mu.Lock()
   106  	defer b.mu.Unlock()
   107  
   108  	step := 1
   109  	if len(n) > 0 {
   110  		step = n[0]
   111  	}
   112  
   113  	b.current += step
   114  
   115  	lastRate := b.rate
   116  	lastSpeed := b.speed
   117  
   118  	b.count()
   119  
   120  	if lastRate != b.rate || lastSpeed != b.speed {
   121  		b.advance <- true
   122  	}
   123  
   124  	if b.rate >= 100 && !b.closed {
   125  		b.closed = true
   126  		close(b.done)
   127  		close(b.advance)
   128  	}
   129  }
   130  
   131  func (b *Bar) count() {
   132  	now := time.Now()
   133  	nowKey := now.Format("20060102150405")
   134  	befKey := now.Add(time.Minute * -1).Format("20060102150405")
   135  	b.currents[nowKey] = b.current
   136  	if v, ok := b.currents[befKey]; ok {
   137  		b.before = v
   138  	}
   139  	delete(b.currents, befKey)
   140  
   141  	b.rate = b.current * 100 / b.total
   142  	b.cost = int(time.Since(b.start) / time.Second)
   143  	if b.cost == 0 {
   144  		b.speed = b.current * 100
   145  	} else if b.before == 0 {
   146  		b.speed = b.current * 100 / b.cost
   147  	} else {
   148  		b.speed = (b.current - b.before) * 100 / 60
   149  	}
   150  
   151  	if b.speed != 0 {
   152  		b.estimate = (b.total - b.current) * 100 / b.speed
   153  	}
   154  }
   155  
   156  func (b *Bar) updateCost() {
   157  	defer func() {
   158  		// 解析失败以后输出日志, 以备检查
   159  		if err := recover(); err != nil {
   160  			logger.Errorf("updateCost.done error=%+v\n", err)
   161  		}
   162  	}()
   163  	for {
   164  		select {
   165  		case <-time.After(time.Millisecond):
   166  			//b.cost++
   167  			b.mu.Lock()
   168  			b.count()
   169  			b.mu.Unlock()
   170  			if !api.ChanIsClosed(b.advance) {
   171  				b.advance <- true
   172  			}
   173  		case <-b.done:
   174  			return
   175  		}
   176  	}
   177  }
   178  
   179  func (b *Bar) run() {
   180  	defer func() {
   181  		// 解析失败以后输出日志, 以备检查
   182  		if err := recover(); err != nil {
   183  			logger.Errorf("run.advance error=%+v\n", err)
   184  		}
   185  	}()
   186  	for range b.advance {
   187  		barPrintf(b.line, "\r%s", b.barMsg())
   188  	}
   189  }
   190  
   191  func (b *Bar) barMsg() string {
   192  	unit := ""
   193  	change := 1
   194  	if b.srcUnit != "" {
   195  		unit = b.srcUnit
   196  	}
   197  
   198  	if b.dstUnit != "" {
   199  		unit = b.dstUnit
   200  		change = b.change
   201  	}
   202  
   203  	prefix := fmt.Sprintf("%s", b.prefix)
   204  	rate := fmt.Sprintf("%3d%%", b.rate)
   205  	speed := fmt.Sprintf("%3.2f %s ps", 0.01*float64(b.speed)/float64(change), unit)
   206  	cost := b.timeFmt(b.cost)
   207  	estimate := b.timeFmt(b.estimate)
   208  	ct := fmt.Sprintf(" (%d/%d)", b.current, b.total)
   209  	barLen := b.width - len(prefix) - len(rate) - len(speed) - len(cost) - len(estimate) - len(ct) - 10
   210  	bar1Len := barLen * b.rate / 100
   211  	bar2Len := barLen - bar1Len
   212  
   213  	realBar1 := bar1[:bar1Len]
   214  	var realBar2 string
   215  	if bar2Len > 0 {
   216  		realBar2 = ">" + bar2[:bar2Len-1]
   217  	}
   218  
   219  	msg := fmt.Sprintf(`%s %s%s [%s%s] %s %s in: %s`, prefix, rate, ct, realBar1, realBar2, speed, cost, estimate)
   220  	switch {
   221  	case b.speed <= b.slow*100:
   222  		return "\033[0;31m" + msg + "\033[0m"
   223  	case b.speed > b.slow*100 && b.speed < b.fast*100:
   224  		return "\033[0;33m" + msg + "\033[0m"
   225  	default:
   226  		return "\033[0;32m" + msg + "\033[0m"
   227  	}
   228  }
   229  
   230  func (b *Bar) timeFmt(cost int) string {
   231  	var h, m, s int
   232  	h = cost / 3600
   233  	m = (cost - h*3600) / 60
   234  	s = cost - h*3600 - m*60
   235  
   236  	return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
   237  }