made ping continuous to make response quicker

This commit is contained in:
2023-11-15 20:18:43 +01:00
parent 43b89e92ff
commit f5143c56a9
4 changed files with 134 additions and 37 deletions

View File

@@ -4,15 +4,24 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/montanaflynn/stats"
"github.com/prometheus-community/pro-bing"
"math"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
type ContinuousPinger struct {
Pings []*probing.Packet
Lock *sync.RWMutex
}
var (
Waybar = flag.Bool("waybar", false, "output waybar json format")
PingCount = flag.Int("count", 3, "how many pings to average")
PingInterval = flag.Int("i", 5, "ping interval")
PingWarningLimit = flag.Int("warn", 50, "ping warning limit")
PingCritLimit = flag.Int("crit", 100, "ping critical limit")
PacketLossWarnLimit = flag.Int("pwarn", 10, "package-loss warning limit")
@@ -20,6 +29,12 @@ var (
Host = flag.String("host", "google.com", "host to ping")
)
type PingStats struct {
AvgRtt time.Duration
StdDev time.Duration
PacketLoss float64
}
//goland:noinspection ALL
const (
CritColor = "ff5050"
@@ -32,8 +47,8 @@ type WaybarOut struct {
Text string `json:"text"`
}
func formatLine(stats *probing.Statistics) {
if stats.PacketLoss >= 100.0 || stats.PacketsRecv == 0 {
func formatLine(stats *PingStats) {
if stats.PacketLoss >= 100.0 {
// fontawesome/forkawesome doesn't have the fitting icon...
// so this is the utf-8 icon/emoji
fmt.Printf("%%{F#%s}🚫\n", CritColor)
@@ -57,13 +72,13 @@ func formatLine(stats *probing.Statistics) {
packetColor = fmt.Sprintf("%%{F#%s}", CritColor)
}
fmt.Printf("%s\uE4E2 %dms %s\uF1B2 %d%%\n", rttColor, stats.AvgRtt.Milliseconds(), packetColor, int(math.Round(stats.PacketLoss)))
fmt.Printf("%s\uE4E2 %d \uE43C %dms %s\uF1B2 %d%%\n", rttColor, stats.AvgRtt.Milliseconds(), stats.StdDev.Milliseconds(), packetColor, int(math.Round(stats.PacketLoss)))
}
func formatLineWaybar(stats *probing.Statistics) {
func formatLineWaybar(stats *PingStats) {
res := new(WaybarOut)
res.Text = fmt.Sprintf("\uE4E2 %dms \uF1B2 %d%%", int(stats.AvgRtt.Milliseconds()), int(math.Round(stats.PacketLoss)))
res.Text = fmt.Sprintf("\uE4E2 %d \uE43C %dms \uF1B2 %d%%", stats.AvgRtt.Milliseconds(), stats.StdDev.Milliseconds(), int(math.Round(stats.PacketLoss)))
res.Class = "good"
switch {
@@ -82,28 +97,104 @@ func formatLineWaybar(stats *probing.Statistics) {
fmt.Println(string(jOut))
}
func (cp *ContinuousPinger) Push(packet *probing.Packet) {
cp.Lock.Lock()
defer cp.Lock.Unlock()
if len(cp.Pings) >= *PingCount {
cp.Pings = cp.Pings[:len(cp.Pings)-1]
}
cp.Pings = append([]*probing.Packet{packet}, cp.Pings...)
}
func (cp *ContinuousPinger) Stats(dSent, dReceive int) (*PingStats, error) {
cp.Lock.RLock()
defer cp.Lock.RUnlock()
var rttArray []float64
ps := new(PingStats)
var sum int64
var pkgsInArr int
for _, pkg := range cp.Pings {
if pkg != nil {
rttArray = append(rttArray, float64(pkg.Rtt))
sum += int64(pkg.Rtt)
pkgsInArr++
}
}
ps.AvgRtt = time.Duration(sum / int64(pkgsInArr))
ps.PacketLoss = 100.0 - (float64(dReceive) / float64(dSent) * 100.0)
stdDev, err := stats.StandardDeviation(rttArray)
if err != nil {
return nil, err
}
ps.StdDev = time.Duration(stdDev)
return ps, nil
}
func main() {
flag.Parse()
killSignals := make(chan os.Signal, 1)
signal.Notify(killSignals, syscall.SIGINT, syscall.SIGTERM)
for {
pinger, err := probing.NewPinger(*Host)
lp := new(ContinuousPinger)
lp.Pings = make([]*probing.Packet, *PingCount)
lp.Lock = new(sync.RWMutex)
ts := 0
tr := 0
lr := time.Now().UTC()
pinger, err := probing.NewPinger(*Host)
pinger.RecordRtts = false
pinger.OnRecv = func(pkt *probing.Packet) {
lp.Push(pkt)
pStats, err := lp.Stats(pinger.PacketsSent-ts, pinger.PacketsRecv-tr)
lr = time.Now().UTC()
ts = pinger.PacketsSent
tr = pinger.PacketsRecv
if err != nil {
fmt.Println(err)
goto sleep
}
pinger.Count = *PingCount
pinger.Timeout = 3 * time.Second
if *Waybar {
pinger.OnFinish = formatLineWaybar
} else if *Waybar {
formatLineWaybar(pStats)
} else {
pinger.OnFinish = formatLine
formatLine(pStats)
}
if pinger.Run() != nil {
fmt.Println(err)
}
sleep:
time.Sleep(time.Duration(*PingInterval) * time.Second)
}
go func() {
for {
if time.Since(lr).Milliseconds() > 1500 {
pStats, err := lp.Stats(pinger.PacketsSent-ts, pinger.PacketsRecv-tr)
ts = pinger.PacketsSent
tr = pinger.PacketsRecv
if err != nil {
fmt.Println(err)
} else if *Waybar {
formatLineWaybar(pStats)
} else {
formatLine(pStats)
}
}
time.Sleep(time.Second)
}
}()
go func() {
for {
if err = pinger.Run(); err != nil {
fmt.Println(err)
time.Sleep(time.Second)
}
}
}()
killLoop:
for {
select {
case <-killSignals:
break killLoop
}
}
pinger.Stop()
}