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 }