This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git


The following commit(s) were added to refs/heads/master by this push:
     new b47c5fb  Refactor `metrics thermodynamic` command (#59)
b47c5fb is described below

commit b47c5fb913c7e9e2472ae4837880510a8780731c
Author: Hoshea Jiang <[email protected]>
AuthorDate: Sat Aug 29 23:45:28 2020 +0800

    Refactor `metrics thermodynamic` command (#59)
---
 README.md                                       |   6 +-
 assets/graphqls/metrics/Thermodynamic.graphql   |  13 ++-
 commands/metrics/thermodynamic/thermodynamic.go |  31 +++---
 commands/model/scope.go                         |  52 +++++++++
 display/graph/graph.go                          |   2 +-
 display/graph/heatmap/heatmap.go                | 140 ++++++++++++------------
 graphql/metrics/metrics.go                      |   6 +-
 lib/heatmap.go                                  | 114 -------------------
 8 files changed, 156 insertions(+), 208 deletions(-)

diff --git a/README.md b/README.md
index 3a96ab5..4c634c8 100644
--- a/README.md
+++ b/README.md
@@ -222,12 +222,12 @@ Ascii Graph, like coloring in terminal, so please use 
`json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics thermodynamic --name=thermodynamic name 
--ids=entity-ids</summary>
+<summary>metrics thermodynamic --name=thermodynamic name 
[--scope=scope-of-metrics]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
 | `--name` | Metrics name, defined in 
[OAL](https://github.com/apache/skywalking/blob/master/oap-server/server-bootstrap/src/main/resources/official_analysis.oal),
 such as `service_sla`, etc. |
-| `--ids` | IDs that are required by the metric type, such as service IDs for 
`service_heatmap` |
+| `--scope` | The scope of metrics, which is consistent with `--name`, such as 
`All`, `Service`, etc. |`All`|
 | `--start` | See [Common options](#common-options) | See [Common 
options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common 
options](#common-options) |
 
@@ -567,7 +567,7 @@ $ ./bin/swctl metrics top 5 --name endpoint_sla 
--service-id 3
 
 ```shell
 $ ./bin/swctl metrics thermodynamic --name all_heatmap
-{"nodes":[[0,0,238],[0,1,1],[0,2,39],[0,3,31],[0,4,12],[0,5,13],[0,6,4],[0,7,3],[0,8,3],[0,9,0],[0,10,48],[0,11,3],[0,12,49],[0,13,54],[0,14,11],[0,15,9],[0,16,2],[0,17,4],[0,18,0],[0,19,1],[0,20,186],[1,0,264],[1,1,3],[1,2,51],[1,3,38],[1,4,16],[1,5,14],[1,6,3],[1,7,2],[1,8,1],[1,9,2],[1,10,51],[1,11,1],[1,12,41],[1,13,56],[1,14,16],[1,15,15],[1,16,7],[1,17,7],[1,18,3],[1,19,1],[1,20,174],[2,0,231],[2,1,3],[2,2,42],[2,3,41],[2,4,18],[2,5,4],[2,6,2],[2,7,1],[2,8,2],[2,9,0],[2,10,54],[2,1
 [...]
+{"values":[{"id":"202008290939","values":[473,3,0,0,0,0,0,0,0,0,323,0,4,0,0,0,0,0,0,0,436]},{"id":"202008290940","values":[434,0,0,0,0,0,0,0,0,0,367,0,4,0,0,0,0,0,0,0,427]},{"id":"202008290941","values":[504,0,0,0,0,0,0,0,0,0,410,0,5,0,1,0,0,0,0,0,377]},{"id":"202008290942","values":[445,0,4,0,0,0,0,0,0,0,350,0,0,0,0,0,0,0,0,0,420]},{"id":"202008290943","values":[436,0,1,0,0,0,0,0,0,0,367,0,3,0,0,0,0,0,0,0,404]},{"id":"202008290944","values":[463,0,0,0,0,0,0,0,0,0,353,0,0,0,0,0,0,0,0,0,4
 [...]
 ```
 
 ```shell
diff --git a/assets/graphqls/metrics/Thermodynamic.graphql 
b/assets/graphqls/metrics/Thermodynamic.graphql
index 9ff50bd..ddbbe81 100644
--- a/assets/graphqls/metrics/Thermodynamic.graphql
+++ b/assets/graphqls/metrics/Thermodynamic.graphql
@@ -15,8 +15,15 @@
 # specific language governing permissions and limitations
 # under the License.
 
-query ($metric: MetricCondition!, $duration: Duration!) {
-    result: getThermodynamic(metric: $metric, duration: $duration) {
-        nodes axisYStep
+query ($condition: MetricsCondition!, $duration: Duration!) {
+    result: readHeatMap(condition: $condition, duration: $duration) {
+        values {
+            id
+            values
+        }
+        buckets {
+            min
+            max
+        }
     }
 }
diff --git a/commands/metrics/thermodynamic/thermodynamic.go 
b/commands/metrics/thermodynamic/thermodynamic.go
index 096a921..62268e1 100644
--- a/commands/metrics/thermodynamic/thermodynamic.go
+++ b/commands/metrics/thermodynamic/thermodynamic.go
@@ -31,9 +31,9 @@ import (
 )
 
 var Command = cli.Command{
-       Name:      "thermodynamic",
-       ShortName: "td",
-       Usage:     "Query thermodynamic metrics defined in backend OAL",
+       Name:    "thermodynamic",
+       Aliases: []string{"td", "heatmap", "hp"},
+       Usage:   "Query thermodynamic metrics defined in backend OAL",
        Flags: flags.Flags(
                flags.DurationFlags,
                []cli.Flag{
@@ -42,10 +42,14 @@ var Command = cli.Command{
                                Usage:    "metrics `name`, which should be 
defined in OAL script",
                                Required: true,
                        },
-                       cli.StringFlag{
-                               Name:     "id",
-                               Usage:    "metrics `id` if the metrics require 
one",
-                               Required: false,
+                       cli.GenericFlag{
+                               Name:  "scope",
+                               Usage: "the scope of the query, which follows 
the metrics `name`",
+                               Value: &model.ScopeEnumValue{
+                                       Enum:     schema.AllScope,
+                                       Default:  schema.ScopeAll,
+                                       Selected: schema.ScopeAll,
+                               },
                        },
                },
        ),
@@ -58,12 +62,7 @@ var Command = cli.Command{
                start := ctx.String("start")
                step := ctx.Generic("step")
                metricsName := ctx.String("name")
-
-               var id *string = nil
-               if ctx.String("id") != "" {
-                       idString := ctx.String("id")
-                       id = &idString
-               }
+               scope := ctx.Generic("scope").(*model.ScopeEnumValue).Selected
 
                duration := schema.Duration{
                        Start: start,
@@ -71,9 +70,11 @@ var Command = cli.Command{
                        Step:  step.(*model.StepEnumValue).Selected,
                }
 
-               metricsValues := metrics.Thermodynamic(ctx, 
schema.MetricCondition{
+               metricsValues := metrics.Thermodynamic(ctx, 
schema.MetricsCondition{
                        Name: metricsName,
-                       ID:   id,
+                       Entity: &schema.Entity{
+                               Scope: scope,
+                       },
                }, duration)
 
                return display.Display(ctx, &displayable.Displayable{
diff --git a/commands/model/scope.go b/commands/model/scope.go
new file mode 100644
index 0000000..7adc5e2
--- /dev/null
+++ b/commands/model/scope.go
@@ -0,0 +1,52 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package model
+
+import (
+       "fmt"
+       "strings"
+
+       "github.com/apache/skywalking-cli/graphql/schema"
+)
+
+// ScopeEnumValue defines the values domain of --scope option
+type ScopeEnumValue struct {
+       Enum     []schema.Scope
+       Default  schema.Scope
+       Selected schema.Scope
+}
+
+// Set the --scope value, from raw string to ScopeEnumValue
+func (s *ScopeEnumValue) Set(value string) error {
+       for _, enum := range s.Enum {
+               if strings.EqualFold(enum.String(), value) {
+                       s.Selected = enum
+                       return nil
+               }
+       }
+       scopes := make([]string, len(schema.AllScope))
+       for i, scope := range schema.AllScope {
+               scopes[i] = scope.String()
+       }
+       return fmt.Errorf("allowed scopes are %s", strings.Join(scopes, ", "))
+}
+
+// String representation of the scope
+func (s ScopeEnumValue) String() string {
+       return s.Selected.String()
+}
diff --git a/display/graph/graph.go b/display/graph/graph.go
index ddc37da..73dd51e 100644
--- a/display/graph/graph.go
+++ b/display/graph/graph.go
@@ -36,7 +36,7 @@ import (
 )
 
 type (
-       Thermodynamic      = schema.Thermodynamic
+       Thermodynamic      = schema.HeatMap
        LinearMetrics      = map[string]float64
        MultiLinearMetrics = []LinearMetrics
        Trace              = schema.Trace
diff --git a/display/graph/heatmap/heatmap.go b/display/graph/heatmap/heatmap.go
index 0ca653a..1a40d29 100644
--- a/display/graph/heatmap/heatmap.go
+++ b/display/graph/heatmap/heatmap.go
@@ -18,97 +18,99 @@
 package heatmap
 
 import (
+       "context"
        "fmt"
-       "math"
-       "time"
+       "strings"
 
-       "github.com/apache/skywalking-cli/graphql/utils"
-       "github.com/apache/skywalking-cli/util"
-
-       ui "github.com/gizak/termui/v3"
+       "github.com/mum4k/termdash"
+       "github.com/mum4k/termdash/container"
+       "github.com/mum4k/termdash/container/grid"
+       "github.com/mum4k/termdash/linestyle"
+       "github.com/mum4k/termdash/terminal/termbox"
+       "github.com/mum4k/termdash/terminal/terminalapi"
+       "github.com/mum4k/termdash/widgetapi"
 
        d "github.com/apache/skywalking-cli/display/displayable"
        "github.com/apache/skywalking-cli/graphql/schema"
-       "github.com/apache/skywalking-cli/lib"
+       "github.com/apache/skywalking-cli/graphql/utils"
+       "github.com/apache/skywalking-cli/lib/heatmap"
 )
 
-func Display(displayable *d.Displayable) error {
-       data := displayable.Data.(schema.Thermodynamic)
-
-       nodes := data.Nodes
-       duration := displayable.Duration
-
-       rows, cols, min, max := statistics(nodes)
+const rootID = "root"
 
-       if err := ui.Init(); err != nil {
-               return err
+func NewHeatMapWidget(data schema.HeatMap) (hp *heatmap.HeatMap, err error) {
+       hp, err = heatmap.NewHeatMap()
+       if err != nil {
+               return hp, err
        }
-       defer ui.Close()
 
-       termW, _ := ui.TerminalDimensions()
+       hpColumns := utils.HeatMapToMap(&data)
+       yLabels := utils.BucketsToStrings(data.Buckets)
+       hp.SetColumns(hpColumns)
+       hp.SetYLabels(yLabels)
 
-       hm := lib.NewHeatMap()
-       hm.Title = fmt.Sprintf(" %s ", displayable.Title)
-       hm.XLabels = make([]string, rows)
-       hm.YLabels = make([]string, cols)
-       for i := 0; i < rows; i++ {
-               step := utils.StepDuration[duration.Step]
-               format := utils.StepFormats[duration.Step]
-               startTime, err := time.Parse(format, duration.Start)
+       return
+}
 
-               if err != nil {
-                       return err
-               }
+// layout controls where and how the heat map widget is placed.
+// Here uses the grid layout to center the widget horizontally and vertically.
+func layout(hp widgetapi.Widget) ([]container.Option, error) {
+       const hpColWidthPerc = 85
+       const hpRowHeightPerc = 80
+
+       builder := grid.New()
+       builder.Add(
+               grid.ColWidthPerc((99-hpColWidthPerc)/2), // Use two empty cols 
to center the heatmap.
+               grid.ColWidthPerc(hpColWidthPerc,
+                       grid.RowHeightPerc((99-hpRowHeightPerc)/2), // Use two 
empty rows to center the heatmap.
+                       grid.RowHeightPerc(hpRowHeightPerc, grid.Widget(hp)),
+                       grid.RowHeightPerc((99-hpRowHeightPerc)/2),
+               ),
+               grid.ColWidthPerc((99-hpColWidthPerc)/2),
+       )
+       return builder.Build()
+}
 
-               hm.XLabels[i] = startTime.Add(time.Duration(i) * 
step).Format("15:04")
+func Display(displayable *d.Displayable) error {
+       t, err := termbox.New(termbox.ColorMode(terminalapi.ColorMode256))
+       if err != nil {
+               return err
        }
-       for i := 0; i < cols; i++ {
-               hm.YLabels[i] = fmt.Sprintf("%4d", i*data.AxisYStep)
+       defer t.Close()
+
+       title := fmt.Sprintf("[%s]-PRESS Q TO QUIT", displayable.Title)
+       c, err := container.New(
+               t,
+               container.Border(linestyle.Light),
+               container.BorderTitle(title),
+               container.ID(rootID))
+       if err != nil {
+               return err
        }
 
-       hm.Data = make([][]float64, rows)
-       hm.CellColors = make([][]ui.Color, rows)
-       hm.NumStyles = make([][]ui.Style, rows)
-       for row := 0; row < rows; row++ {
-               hm.Data[row] = make([]float64, cols)
-               hm.CellColors[row] = make([]ui.Color, cols)
-               hm.NumStyles[row] = make([]ui.Style, cols)
+       data := displayable.Data.(schema.HeatMap)
+       hp, err := NewHeatMapWidget(data)
+       if err != nil {
+               return err
        }
 
-       scale := max - min
-       for _, node := range nodes {
-               color := ui.Color(255 - (float64(*node[2])/scale)*23)
-               hm.Data[*node[0]][*node[1]] = float64(*node[2])
-               hm.CellColors[*node[0]][*node[1]] = color
-               hm.NumStyles[*node[0]][*node[1]] = ui.Style{Fg: ui.ColorMagenta}
+       gridOpts, err := layout(hp)
+       if err != nil {
+               return fmt.Errorf("builder.Build => %v", err)
        }
 
-       hm.Formatter = nil
-       hm.XLabelStyles = []ui.Style{{Fg: ui.ColorWhite}}
-       hm.CellGap = 0
-       hm.CellWidth = int(float64(termW) / float64(rows))
-       realWidth := (hm.CellWidth+hm.CellGap)*(rows+1) - hm.CellGap + 5
-       hm.SetRect(int(float64(termW-realWidth)/2), 2, realWidth, cols+5)
-
-       ui.Render(hm)
-
-       events := ui.PollEvents()
-       for e := <-events; e.ID != "q" && e.ID != "<C-c>"; e = <-events {
+       if e := c.Update(rootID, gridOpts...); e != nil {
+               return e
        }
-       return nil
-}
-
-func statistics(nodes [][]*int) (rows, cols int, min, max float64) {
-       min = math.MaxFloat64
 
-       for _, node := range nodes {
-               rows = util.MaxInt(rows, *node[0])
-               cols = util.MaxInt(cols, *node[1])
-               max = math.Max(max, float64(*node[2]))
-               min = math.Min(min, float64(*node[2]))
+       con, cancel := context.WithCancel(context.Background())
+       quitter := func(keyboard *terminalapi.Keyboard) {
+               if strings.EqualFold(keyboard.Key.String(), "q") {
+                       cancel()
+               }
        }
 
-       rows++
-       cols++
-       return
+       err = termdash.Run(con, t, c, termdash.KeyboardSubscriber(quitter))
+
+       return err
 }
diff --git a/graphql/metrics/metrics.go b/graphql/metrics/metrics.go
index 70a0422..0cf7099 100644
--- a/graphql/metrics/metrics.go
+++ b/graphql/metrics/metrics.go
@@ -68,12 +68,12 @@ func MultipleLinearIntValues(ctx *cli.Context, condition 
schema.MetricCondition,
        return response["result"]
 }
 
-func Thermodynamic(ctx *cli.Context, condition schema.MetricCondition, 
duration schema.Duration) schema.Thermodynamic {
-       var response map[string]schema.Thermodynamic
+func Thermodynamic(ctx *cli.Context, condition schema.MetricsCondition, 
duration schema.Duration) schema.HeatMap {
+       var response map[string]schema.HeatMap
 
        request := 
graphql.NewRequest(assets.Read("graphqls/metrics/Thermodynamic.graphql"))
 
-       request.Var("metric", condition)
+       request.Var("condition", condition)
        request.Var("duration", duration)
 
        client.ExecuteQueryOrFail(ctx, request, &response)
diff --git a/lib/heatmap.go b/lib/heatmap.go
deleted file mode 100644
index bf24b86..0000000
--- a/lib/heatmap.go
+++ /dev/null
@@ -1,114 +0,0 @@
-// Licensed to Apache Software Foundation (ASF) under one or more contributor
-// license agreements. See the NOTICE file distributed with
-// this work for additional information regarding copyright
-// ownership. Apache Software Foundation (ASF) licenses this file to you under
-// the Apache License, Version 2.0 (the "License"); you may
-// not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package lib
-
-import (
-       "fmt"
-       im "image"
-
-       ui "github.com/gizak/termui/v3"
-       rw "github.com/mattn/go-runewidth"
-)
-
-type HeatMap struct {
-       ui.Block
-       XLabelStyles []ui.Style
-       CellColors   [][]ui.Color
-       NumStyles    [][]ui.Style
-       Formatter    func(float64) string
-       Data         [][]float64
-       XLabels      []string
-       YLabels      []string
-       CellWidth    int
-       CellGap      int
-}
-
-func NewHeatMap() *HeatMap {
-       return &HeatMap{
-               Block:        *ui.NewBlock(),
-               CellColors:   [][]ui.Color{ui.StandardColors, 
ui.StandardColors},
-               NumStyles:    [][]ui.Style{ui.StandardStyles, 
ui.StandardStyles},
-               Formatter:    func(n float64) string { return fmt.Sprint(n) },
-               XLabelStyles: ui.StandardStyles,
-               CellGap:      1,
-               CellWidth:    3,
-       }
-}
-
-func (hm *HeatMap) Draw(buffer *ui.Buffer) {
-       hm.Block.Draw(buffer)
-
-       cellX := hm.Inner.Min.X
-
-       for i, column := range hm.Data {
-               cellY := 0
-               for j, datum := range column {
-                       buffer.SetString(
-                               hm.YLabels[j],
-                               ui.StyleClear,
-                               im.Pt(hm.Inner.Min.X, (hm.Inner.Max.Y-2)-cellY),
-                       )
-                       for x := cellX + 5; x < ui.MinInt(cellX+hm.CellWidth, 
hm.Inner.Max.X)+5; x++ {
-                               for y := (hm.Inner.Max.Y - 2) - cellY; y > 
(hm.Inner.Max.Y-2)-cellY-1; y-- {
-                                       cell := ui.NewCell(' ', 
ui.NewStyle(ui.ColorClear, color(hm.CellColors, i, j)))
-                                       buffer.SetCell(cell, im.Pt(x, y))
-                               }
-                       }
-
-                       if hm.Formatter != nil {
-                               hm.drawNumber(buffer, datum, i, j, cellX+5, 
cellY)
-                       }
-
-                       cellY++
-               }
-
-               if i < len(hm.XLabels) {
-                       hm.drawLabel(buffer, cellX+5, i)
-               }
-
-               cellX += hm.CellWidth + hm.CellGap
-       }
-}
-
-func (hm *HeatMap) drawLabel(buffer *ui.Buffer, cellX, i int) {
-       labelX := cellX + 
ui.MaxInt(int(float64(hm.CellWidth)/2)-int(float64(rw.StringWidth(hm.XLabels[i]))/2),
 0)
-       buffer.SetString(
-               ui.TrimString(hm.XLabels[i], hm.CellWidth),
-               ui.SelectStyle(hm.XLabelStyles, i),
-               im.Pt(labelX, hm.Inner.Max.Y-1),
-       )
-}
-
-func (hm *HeatMap) drawNumber(buffer *ui.Buffer, datum float64, i, j, cellX, 
cellY int) {
-       x := cellX + int(float64(hm.CellWidth)/2) - 1
-       numberStyle := style(hm.NumStyles, i, j)
-       cellColor := color(hm.CellColors, i, j)
-       buffer.SetString(
-               hm.Formatter(datum),
-               ui.NewStyle(numberStyle.Fg, cellColor, numberStyle.Modifier),
-               im.Pt(x, (hm.Inner.Max.Y-2)-cellY),
-       )
-}
-
-func color(colors [][]ui.Color, i, j int) ui.Color {
-       return colors[i%len(colors)][j%len(colors)]
-}
-
-func style(styles [][]ui.Style, i, j int) ui.Style {
-       return styles[i%len(styles)][j%len(styles)]
-}

Reply via email to