github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/agent/mcorpc/aggregate/average.go (about)

     1  // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package aggregate
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"strconv"
    11  	"sync"
    12  )
    13  
    14  // AverageAggregator averages seen values
    15  type AverageAggregator struct {
    16  	sum    float64
    17  	count  int
    18  	args   []any
    19  	format string
    20  
    21  	sync.Mutex
    22  }
    23  
    24  // NewAverageAggregator creates a new AverageAggregator with the specific options supplied
    25  func NewAverageAggregator(args []any) (*AverageAggregator, error) {
    26  	agg := &AverageAggregator{
    27  		args:   args,
    28  		format: parseFormatFromArgs(args),
    29  	}
    30  
    31  	return agg, nil
    32  }
    33  
    34  // Type is the type of Aggregator
    35  func (a *AverageAggregator) Type() string {
    36  	return "average"
    37  }
    38  
    39  // ProcessValue processes and tracks the specific value
    40  func (a *AverageAggregator) ProcessValue(v any) error {
    41  	a.Lock()
    42  	defer a.Unlock()
    43  
    44  	if a.processInt(v) {
    45  		return nil
    46  	}
    47  
    48  	if a.processInt64(v) {
    49  		return nil
    50  	}
    51  
    52  	if a.processFloat(v) {
    53  		return nil
    54  	}
    55  
    56  	if a.processString(v) {
    57  		return nil
    58  	}
    59  
    60  	return fmt.Errorf("unsupported data type for average aggregator")
    61  }
    62  
    63  // ResultJSON return the results in JSON format preserving types
    64  func (a *AverageAggregator) ResultJSON() ([]byte, error) {
    65  	a.Lock()
    66  	defer a.Unlock()
    67  
    68  	avg := a.sum / float64(a.count)
    69  
    70  	return json.Marshal(map[string]float64{
    71  		"average": avg,
    72  	})
    73  }
    74  
    75  // ResultStrings returns a map of results in string format
    76  func (a *AverageAggregator) ResultStrings() (map[string]string, error) {
    77  	a.Lock()
    78  	defer a.Unlock()
    79  
    80  	avg := a.sum / float64(a.count)
    81  
    82  	return map[string]string{"Average": fmt.Sprintf("%f", avg)}, nil
    83  }
    84  
    85  // ResultFormattedStrings return the results in a formatted way, if no format is given a calculated value is used
    86  func (a *AverageAggregator) ResultFormattedStrings(format string) ([]string, error) {
    87  	a.Lock()
    88  	defer a.Unlock()
    89  
    90  	avg := a.sum / float64(a.count)
    91  
    92  	if format == "" {
    93  		if a.format != "" {
    94  			format = a.format
    95  		} else {
    96  			format = "Average: %.3f"
    97  		}
    98  	}
    99  
   100  	return []string{fmt.Sprintf(format, avg)}, nil
   101  }
   102  
   103  func (a *AverageAggregator) processString(v any) bool {
   104  	s, ok := v.(string)
   105  	if !ok {
   106  		return false
   107  	}
   108  
   109  	f, err := strconv.ParseFloat(s, 64)
   110  	if err != nil {
   111  		return false
   112  	}
   113  
   114  	a.sum = a.sum + f
   115  	a.count++
   116  
   117  	return true
   118  }
   119  
   120  func (a *AverageAggregator) processFloat(v any) bool {
   121  	f, ok := v.(float64)
   122  	if !ok {
   123  		return false
   124  	}
   125  
   126  	a.sum = a.sum + f
   127  	a.count++
   128  
   129  	return true
   130  }
   131  
   132  func (a *AverageAggregator) processInt64(v any) bool {
   133  	i, ok := v.(int64)
   134  	if !ok {
   135  		return false
   136  	}
   137  
   138  	a.sum = a.sum + float64(i)
   139  	a.count++
   140  
   141  	return true
   142  }
   143  
   144  func (a *AverageAggregator) processInt(v any) bool {
   145  	i, ok := v.(int)
   146  	if !ok {
   147  		return false
   148  	}
   149  
   150  	a.sum = a.sum + float64(i)
   151  	a.count++
   152  
   153  	return true
   154  }