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

     1  package progressbar
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // Bar 进度条
    10  type Bar struct {
    11  	mu       sync.Mutex
    12  	line     int
    13  	prefix   string
    14  	total    int
    15  	width    int
    16  	bar1     string
    17  	bar2     string
    18  	advance  chan struct{} // for data
    19  	done     chan struct{} // for updateCost
    20  	finished chan struct{} // 等待结束信号
    21  	currents map[string]int
    22  	current  int
    23  	before   int
    24  	rate     int
    25  	speed    int
    26  	cost     int
    27  	estimate int
    28  	fast     int
    29  	slow     int
    30  	srcUnit  string
    31  	dstUnit  string
    32  	change   int
    33  	start    time.Time
    34  }
    35  
    36  const (
    37  	defaultFast       = 20
    38  	defaultSlow       = 5
    39  	defaultTickTimes  = time.Millisecond * 500 // 默认定时刷新进度条的间隔时间
    40  	defaultSleepTimes = time.Millisecond * 100 // 默认sleep时间
    41  )
    42  
    43  func NewBar(line int, prefix string, total int) *Bar {
    44  	if total <= 0 {
    45  		return nil
    46  	}
    47  
    48  	if line <= 0 {
    49  		gMaxLine++
    50  		line = gMaxLine
    51  	}
    52  	if line > gMaxLine {
    53  		gMaxLine = line
    54  	}
    55  
    56  	bar := &Bar{
    57  		line:     line,
    58  		prefix:   prefix,
    59  		total:    total,
    60  		fast:     defaultFast,
    61  		slow:     defaultSlow,
    62  		width:    100,
    63  		advance:  make(chan struct{}, total),
    64  		done:     make(chan struct{}, total),
    65  		finished: make(chan struct{}),
    66  		currents: make(map[string]int),
    67  		change:   1,
    68  		start:    time.Now(),
    69  	}
    70  	bar.initBar(bar.width)
    71  	go bar.updateCost()
    72  	go bar.run()
    73  
    74  	return bar
    75  }
    76  
    77  // SetPrefix 设置进度条前端文字
    78  func (b *Bar) SetPrefix(prefix string) {
    79  	b.mu.Lock()
    80  	defer b.mu.Unlock()
    81  	b.prefix = prefix
    82  }
    83  
    84  func (b *Bar) initBar(width int) {
    85  	b.mu.Lock()
    86  	defer b.mu.Unlock()
    87  	for i := 0; i < width; i++ {
    88  		b.bar1 += "="
    89  		b.bar2 += "-"
    90  	}
    91  }
    92  
    93  func (b *Bar) SetUnit(src string, dst string, change int) {
    94  	b.mu.Lock()
    95  	defer b.mu.Unlock()
    96  	b.srcUnit = src
    97  	b.dstUnit = dst
    98  	b.change = change
    99  
   100  	if b.change == 0 {
   101  		b.change = 1
   102  	}
   103  }
   104  
   105  func (b *Bar) SetSpeedSection(fast, slow int) {
   106  	b.mu.Lock()
   107  	defer b.mu.Unlock()
   108  	if fast > slow {
   109  		b.fast, b.slow = fast, slow
   110  	} else {
   111  		b.fast, b.slow = slow, fast
   112  	}
   113  }
   114  
   115  // Add 进度条前进, 同时也会触发其它信号
   116  func (b *Bar) Add(n ...int) {
   117  	b.mu.Lock()
   118  	defer b.mu.Unlock()
   119  	step := 1
   120  	if len(n) > 0 {
   121  		step = n[0]
   122  	}
   123  	b.current += step
   124  	// 计算速度
   125  	b.computeAndUpdate()
   126  	// 进度条达到100%, 通知工作协程结束并关闭channel
   127  	if b.current >= b.total {
   128  		// 通知updateCost协程结束
   129  		b.done <- struct{}{}
   130  		close(b.done)
   131  	}
   132  }
   133  
   134  func (b *Bar) computeAndUpdate() {
   135  	now := time.Now()
   136  	nowKey := now.Format("20060102150405")
   137  	befKey := now.Add(time.Minute * -1).Format("20060102150405")
   138  	b.currents[nowKey] = b.current
   139  	if v, ok := b.currents[befKey]; ok {
   140  		b.before = v
   141  	}
   142  	delete(b.currents, befKey)
   143  
   144  	b.rate = b.current * 100 / b.total
   145  	b.cost = int(time.Since(b.start) / time.Second)
   146  	if b.cost == 0 {
   147  		b.speed = b.current * 100
   148  	} else if b.before == 0 {
   149  		b.speed = b.current * 100 / b.cost
   150  	} else {
   151  		b.speed = (b.current - b.before) * 100 / 60
   152  	}
   153  
   154  	if b.speed != 0 {
   155  		b.estimate = (b.total - b.current) * 100 / b.speed
   156  	}
   157  	b.advance <- struct{}{} // 发送更新进度条信号
   158  }
   159  
   160  // 发送bar更新信号
   161  func (b *Bar) sendBarUpdateSignal() {
   162  	b.mu.Lock()
   163  	defer b.mu.Unlock()
   164  	// 统计数据
   165  	b.computeAndUpdate()
   166  }
   167  
   168  func (b *Bar) updateCost() {
   169  	for {
   170  		select {
   171  		case <-time.After(defaultTickTimes):
   172  			b.sendBarUpdateSignal()
   173  		case <-b.done:
   174  			// 补全不满100%进度的信号
   175  			for b.rate <= 100 {
   176  				b.sendBarUpdateSignal()
   177  				if b.rate >= 100 {
   178  					break
   179  				}
   180  				time.Sleep(defaultSleepTimes)
   181  			}
   182  			close(b.advance)
   183  			return
   184  		}
   185  	}
   186  }
   187  
   188  // Wait 等待进度条刷新run协程结束
   189  func (b *Bar) Wait() {
   190  	<-b.finished
   191  }
   192  
   193  func (b *Bar) run() {
   194  	defer func() {
   195  		b.finished <- struct{}{}
   196  	}()
   197  	// 只有关闭channel才会结束循环, 且不能对channel加速
   198  	for range b.advance {
   199  		text := b.barMsg()
   200  		barPrintf(b.line, "\r%s", text)
   201  	}
   202  }
   203  
   204  // 重置进度条消息
   205  func (b *Bar) barMsg() string {
   206  	b.mu.Lock()
   207  	defer b.mu.Unlock()
   208  	unit := ""
   209  	change := 1
   210  	if b.srcUnit != "" {
   211  		unit = b.srcUnit
   212  	}
   213  
   214  	if b.dstUnit != "" {
   215  		unit = b.dstUnit
   216  		change = b.change
   217  	}
   218  
   219  	prefix := fmt.Sprintf("%s", b.prefix)
   220  	rate := fmt.Sprintf("%3d%%", b.rate)
   221  	speed := fmt.Sprintf("%3.2f %s ps", 0.01*float64(b.speed)/float64(change), unit)
   222  	cost := b.timeFmt(b.cost)
   223  	estimate := b.timeFmt(b.estimate)
   224  	ct := fmt.Sprintf(" (%d/%d)", b.current, b.total)
   225  	barLen := b.width - len(prefix) - len(rate) - len(speed) - len(cost) - len(estimate) - len(ct) - 10
   226  	bar1Len := barLen * b.rate / 100
   227  	bar2Len := barLen - bar1Len
   228  
   229  	//realBar1 := bar1[:bar1Len]
   230  	realBar1 := b.bar1[:bar1Len]
   231  	var realBar2 string
   232  	if bar2Len > 0 {
   233  		//realBar2 = ">" + bar2[:bar2Len-1]
   234  		realBar2 = ">" + b.bar2[:bar2Len-1]
   235  	}
   236  
   237  	msg := fmt.Sprintf(`%s %s%s [%s%s] %s %s in: %s`, prefix, rate, ct, realBar1, realBar2, speed, cost, estimate)
   238  	switch {
   239  	case b.speed <= b.slow*100:
   240  		return "\033[0;31m" + msg + "\033[0m"
   241  	case b.speed > b.slow*100 && b.speed < b.fast*100:
   242  		return "\033[0;33m" + msg + "\033[0m"
   243  	default:
   244  		return "\033[0;32m" + msg + "\033[0m"
   245  	}
   246  }
   247  
   248  func (b *Bar) timeFmt(cost int) string {
   249  	var h, m, s int
   250  	h = cost / 3600
   251  	m = (cost - h*3600) / 60
   252  	s = cost - h*3600 - m*60
   253  
   254  	return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
   255  }