kappanhang/statuslog.go
2020-10-29 15:11:51 +01:00

244 lines
4.6 KiB
Go

package main
import (
"fmt"
"os"
"sync"
"time"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
)
type statusLogData struct {
line1 string
line2 string
stateStr string
frequency float64
mode string
filter string
startTime time.Time
rttLatency time.Duration
}
type statusLogStruct struct {
ticker *time.Ticker
stopChan chan bool
stopFinishedChan chan bool
mutex sync.Mutex
retransmitsColor *color.Color
lostColor *color.Color
stateStr struct {
unknown string
rx string
tx string
tune string
}
data *statusLogData
}
var statusLog statusLogStruct
func (s *statusLogStruct) reportRTTLatency(l time.Duration) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.data == nil {
return
}
s.data.rttLatency = l
}
func (s *statusLogStruct) reportFrequency(f float64) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.data == nil {
return
}
s.data.frequency = f
}
func (s *statusLogStruct) reportMode(mode, filter string) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.data == nil {
return
}
s.data.mode = mode
s.data.filter = filter
}
func (s *statusLogStruct) reportPTT(ptt, tune bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.data == nil {
return
}
if tune {
s.data.stateStr = s.stateStr.tune
} else if ptt {
s.data.stateStr = s.stateStr.tx
} else {
s.data.stateStr = s.stateStr.rx
}
}
func (s *statusLogStruct) clearInternal() {
fmt.Printf("%c[2K", 27)
}
func (s *statusLogStruct) clear() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.clearInternal()
}
func (s *statusLogStruct) print() {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.isRealtimeInternal() {
s.clearInternal()
fmt.Println(s.data.line1)
s.clearInternal()
fmt.Printf(s.data.line2+"%c[1A", 27)
} else {
log.PrintStatusLog(s.data.line2)
}
}
func (s *statusLogStruct) update() {
s.mutex.Lock()
defer s.mutex.Unlock()
var modeStr string
if s.data.mode != "" {
modeStr = " " + s.data.mode
}
var filterStr string
if s.data.filter != "" {
filterStr = " " + s.data.filter
}
s.data.line1 = fmt.Sprint("state ", s.data.stateStr, " freq: ", fmt.Sprintf("%f", s.data.frequency/1000000),
modeStr, filterStr)
up, down, lost, retransmits := netstat.get()
lostStr := "0"
if lost > 0 {
lostStr = s.lostColor.Sprint(" ", lost, " ")
}
retransmitsStr := "0"
if retransmits > 0 {
retransmitsStr = s.retransmitsColor.Sprint(" ", retransmits, " ")
}
s.data.line2 = fmt.Sprint("up ", time.Since(s.data.startTime).Round(time.Second),
" rtt ", s.data.rttLatency.Milliseconds(), "ms up ",
netstat.formatByteCount(up), "/s down ",
netstat.formatByteCount(down), "/s retx ", retransmitsStr, "/1m lost ", lostStr, "/1m\r")
if s.isRealtimeInternal() {
t := time.Now().Format("2006-01-02T15:04:05.000Z0700")
s.data.line1 = fmt.Sprint(t, " ", s.data.line1)
s.data.line2 = fmt.Sprint(t, " ", s.data.line2)
}
}
func (s *statusLogStruct) loop() {
for {
select {
case <-s.ticker.C:
s.update()
s.print()
case <-s.stopChan:
s.stopFinishedChan <- true
return
}
}
}
func (s *statusLogStruct) isRealtimeInternal() bool {
return statusLogInterval < time.Second
}
func (s *statusLogStruct) isRealtime() bool {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.ticker != nil && s.isRealtimeInternal()
}
func (s *statusLogStruct) isActive() bool {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.ticker != nil
}
func (s *statusLogStruct) startPeriodicPrint() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.initIfNeeded()
s.data = &statusLogData{
stateStr: s.stateStr.unknown,
startTime: time.Now(),
}
s.stopChan = make(chan bool)
s.stopFinishedChan = make(chan bool)
s.ticker = time.NewTicker(statusLogInterval)
go s.loop()
}
func (s *statusLogStruct) stopPeriodicPrint() {
if !s.isActive() {
return
}
s.ticker.Stop()
s.ticker = nil
s.stopChan <- true
<-s.stopFinishedChan
fmt.Println()
fmt.Println()
fmt.Println()
}
func (s *statusLogStruct) initIfNeeded() {
if s.data != nil { // Already initialized?
return
}
if !isatty.IsTerminal(os.Stdout.Fd()) && statusLogInterval < time.Second {
statusLogInterval = time.Second
}
c := color.New(color.FgHiWhite)
c.Add(color.BgWhite)
s.stateStr.unknown = c.Sprint(" ?? ")
c = color.New(color.FgHiWhite)
c.Add(color.BgGreen)
s.stateStr.rx = c.Sprint(" RX ")
c = color.New(color.FgHiWhite, color.BlinkRapid)
c.Add(color.BgRed)
s.stateStr.tx = c.Sprint(" TX ")
s.stateStr.tune = c.Sprint(" TUNE ")
s.retransmitsColor = color.New(color.FgHiWhite)
s.retransmitsColor.Add(color.BgYellow)
s.lostColor = color.New(color.FgHiWhite)
s.lostColor.Add(color.BgRed)
}