From 05a5bc1d6aa6a00a36c0b5e0910358903c86409e Mon Sep 17 00:00:00 2001 From: Karl Heitmann Date: Wed, 5 Jul 2023 01:32:50 -0400 Subject: [PATCH 1/2] Changes call on main.go from .Plot to .PlotMany --- cmd/asciigraph/main.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cmd/asciigraph/main.go b/cmd/asciigraph/main.go index c40c462..73f764c 100644 --- a/cmd/asciigraph/main.go +++ b/cmd/asciigraph/main.go @@ -9,6 +9,7 @@ import ( "math" "os" "strconv" + "strings" "time" "github.com/guptarohit/asciigraph" @@ -87,7 +88,7 @@ func main() { flag.Parse() - data := make([]float64, 0, 64) + data := make([][]float64, 0, 64) if realTimeDataBuffer == 0 { realTimeDataBuffer = int(width) @@ -102,19 +103,29 @@ func main() { for s.Scan() { word := s.Text() - p, err := strconv.ParseFloat(word, 64) - if err != nil { - log.Printf("ignore %q: cannot parse value", word) - continue + + words := strings.Split(word, ",") + + for i, s := range words { + p, err := strconv.ParseFloat(s, 64) + if err != nil { + log.Printf("ignore %q: cannot parse value", s) + } + if i >= len(data) { + data = append(data, []float64{p}) + } else { + data[i] = append(data[i], p) + } + } - data = append(data, p) + if enableRealTime { if realTimeDataBuffer > 0 && len(data) > realTimeDataBuffer { data = data[len(data)-realTimeDataBuffer:] } if currentTime := time.Now(); currentTime.After(nextFlushTime) || currentTime.Equal(nextFlushTime) { - plot := asciigraph.Plot(data, + plot := asciigraph.PlotMany(data, asciigraph.Height(int(height)), asciigraph.Width(int(width)), asciigraph.Offset(int(offset)), @@ -142,7 +153,7 @@ func main() { log.Fatal("no data") } - plot := asciigraph.Plot(data, + plot := asciigraph.PlotMany(data, asciigraph.Height(int(height)), asciigraph.Width(int(width)), asciigraph.Offset(int(offset)), From e24643ca3edef3eda271db13f45d003caba34099 Mon Sep 17 00:00:00 2001 From: Rohit Gupta Date: Sat, 30 Mar 2024 01:10:15 +0530 Subject: [PATCH 2/2] Add options to specify delimiter and number of series --- README.md | 4 ++++ cmd/asciigraph/main.go | 51 +++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 069bd77..466b7a0 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,8 @@ Options: caption for the graph -cc caption color caption color of the plot + -d delimiter + data delimiter for splitting data points in the input stream (default ",") -f fps set fps to control how frequently graph to be rendered when realtime graph enabled (default 24) -h height @@ -142,6 +144,8 @@ Options: enables realtime graph for data stream -sc series color series color of the plot + -sn number of series + number of series (columns) in the input data (default 1) -ub upper bound upper bound set the maximum value for the vertical axis (ignored if series contains larger values) (default -Inf) -w width diff --git a/cmd/asciigraph/main.go b/cmd/asciigraph/main.go index 73f764c..1cd5737 100644 --- a/cmd/asciigraph/main.go +++ b/cmd/asciigraph/main.go @@ -28,8 +28,10 @@ var ( captionColor asciigraph.AnsiColor axisColor asciigraph.AnsiColor labelColor asciigraph.AnsiColor - lowerBound = math.Inf(1) - upperBound = math.Inf(-1) + lowerBound = math.Inf(1) + upperBound = math.Inf(-1) + delimiter = "," + seriesNum uint = 1 ) func main() { @@ -85,47 +87,54 @@ func main() { }) flag.Float64Var(&lowerBound, "lb", lowerBound, "`lower bound` set the minimum value for the vertical axis (ignored if series contains lower values)") flag.Float64Var(&upperBound, "ub", upperBound, "`upper bound` set the maximum value for the vertical axis (ignored if series contains larger values)") + flag.StringVar(&delimiter, "d", delimiter, "data `delimiter` for splitting data points in the input stream") + flag.UintVar(&seriesNum, "sn", seriesNum, "`number of series` (columns) in the input data") flag.Parse() - data := make([][]float64, 0, 64) + series := make([][]float64, seriesNum) - if realTimeDataBuffer == 0 { + if enableRealTime && realTimeDataBuffer == 0 { realTimeDataBuffer = int(width) } s := bufio.NewScanner(os.Stdin) - s.Split(bufio.ScanWords) + s.Split(bufio.ScanLines) nextFlushTime := time.Now() flushInterval := time.Duration(float64(time.Second) / fps) for s.Scan() { - word := s.Text() + line := s.Text() + points := strings.Split(line, delimiter) - words := strings.Split(word, ",") + if uint(len(points)) < seriesNum { + log.Fatal("number of series in the input data stream is less than the specified series number") + } else if uint(len(points)) > seriesNum { + points = points[:seriesNum] + } - for i, s := range words { - p, err := strconv.ParseFloat(s, 64) + for i, point := range points { + p, err := strconv.ParseFloat(strings.TrimSpace(point), 64) if err != nil { - log.Printf("ignore %q: cannot parse value", s) - } - if i >= len(data) { - data = append(data, []float64{p}) - } else { - data[i] = append(data[i], p) + log.Printf("ignore %q: cannot parse value", point) + p = math.NaN() } + series[i] = append(series[i], p) } - if enableRealTime { - if realTimeDataBuffer > 0 && len(data) > realTimeDataBuffer { - data = data[len(data)-realTimeDataBuffer:] + if realTimeDataBuffer > 0 && len(series[0]) > realTimeDataBuffer { + for i := range series { + seriesLength := len(series[i]) + series[i] = series[i][seriesLength-realTimeDataBuffer:] + } } if currentTime := time.Now(); currentTime.After(nextFlushTime) || currentTime.Equal(nextFlushTime) { - plot := asciigraph.PlotMany(data, + seriesCopy := append([][]float64(nil), series...) + plot := asciigraph.PlotMany(seriesCopy, asciigraph.Height(int(height)), asciigraph.Width(int(width)), asciigraph.Offset(int(offset)), @@ -149,11 +158,11 @@ func main() { log.Fatal(err) } - if len(data) == 0 { + if len(series) == 0 { log.Fatal("no data") } - plot := asciigraph.PlotMany(data, + plot := asciigraph.PlotMany(series, asciigraph.Height(int(height)), asciigraph.Width(int(width)), asciigraph.Offset(int(offset)),