github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/bus/statistic.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2024, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package bus
    20  
    21  import (
    22  	"go.uber.org/atomic"
    23  	"strings"
    24  	"time"
    25  )
    26  
    27  type Statistic struct {
    28  	min *atomic.Duration
    29  	max *atomic.Duration
    30  	avg *atomic.Duration
    31  	rps *RPSCounter
    32  }
    33  
    34  func NewStatistic() *Statistic {
    35  	return &Statistic{
    36  		min: atomic.NewDuration(0),
    37  		max: atomic.NewDuration(0),
    38  		avg: atomic.NewDuration(0),
    39  		rps: startRPSCounter(),
    40  	}
    41  }
    42  
    43  func (s *Statistic) setTime(t time.Duration) {
    44  	if s.min.Load() == 0 {
    45  		s.min.Store(t)
    46  	}
    47  	if s.min.Load() > t {
    48  		s.min.Store(t)
    49  	}
    50  	if s.max.Load() == 0 {
    51  		s.max.Store(t)
    52  	}
    53  	if t > s.max.Load() {
    54  		s.max.Store(t)
    55  	}
    56  	s.avg.Store((s.max.Load() + s.min.Load()) / 2)
    57  }
    58  
    59  // StatItem ...
    60  type StatItem struct {
    61  	Topic       string
    62  	Subscribers int
    63  	Min         time.Duration
    64  	Max         time.Duration
    65  	Avg         time.Duration
    66  	Rps         float64
    67  }
    68  
    69  // Stats ...
    70  type Stats []*StatItem
    71  
    72  func (s Stats) Len() int           { return len(s) }
    73  func (s Stats) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    74  func (s Stats) Less(i, j int) bool { return strings.Compare(s[i].Topic, s[j].Topic) == -1 }
    75  
    76  type RPSCounter struct {
    77  	count     *atomic.Int64
    78  	value     *atomic.Float64
    79  	isRunning *atomic.Bool
    80  }
    81  
    82  func startRPSCounter() *RPSCounter {
    83  	counter := &RPSCounter{
    84  		count:     atomic.NewInt64(0),
    85  		value:     atomic.NewFloat64(0),
    86  		isRunning: atomic.NewBool(true),
    87  	}
    88  
    89  	go func() {
    90  		ticker := time.NewTicker(time.Second * 5)
    91  		defer ticker.Stop()
    92  		for counter.isRunning.Load() {
    93  			select {
    94  			case <-ticker.C:
    95  				counter.value.Store(float64(counter.count.Load()) / 5)
    96  				counter.count.Store(0)
    97  			}
    98  		}
    99  	}()
   100  
   101  	return counter
   102  }
   103  
   104  func (c *RPSCounter) Inc() {
   105  	c.count.Inc()
   106  }
   107  
   108  func (c *RPSCounter) Value() float64 {
   109  	return c.value.Load()
   110  }
   111  
   112  func (c *RPSCounter) Stop() {
   113  	c.isRunning.Store(false)
   114  }