| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- package pb
- import (
- "fmt"
- "math"
- "time"
- "github.com/VividCortex/ewma"
- )
- var speedAddLimit = time.Second / 2
- type speed struct {
- ewma ewma.MovingAverage
- lastStateId uint64
- prevValue, startValue int64
- prevTime, startTime time.Time
- }
- func (s *speed) value(state *State) float64 {
- if s.ewma == nil {
- s.ewma = ewma.NewMovingAverage()
- }
- if state.IsFirst() || state.Id() < s.lastStateId {
- s.reset(state)
- return 0
- }
- if state.Id() == s.lastStateId {
- return s.ewma.Value()
- }
- if state.IsFinished() {
- return s.absValue(state)
- }
- dur := state.Time().Sub(s.prevTime)
- if dur < speedAddLimit {
- return s.ewma.Value()
- }
- diff := math.Abs(float64(state.Value() - s.prevValue))
- lastSpeed := diff / dur.Seconds()
- s.prevTime = state.Time()
- s.prevValue = state.Value()
- s.lastStateId = state.Id()
- s.ewma.Add(lastSpeed)
- return s.ewma.Value()
- }
- func (s *speed) reset(state *State) {
- s.lastStateId = state.Id()
- s.startTime = state.Time()
- s.prevTime = state.Time()
- s.startValue = state.Value()
- s.prevValue = state.Value()
- s.ewma = ewma.NewMovingAverage()
- }
- func (s *speed) absValue(state *State) float64 {
- if dur := state.Time().Sub(s.startTime); dur > 0 {
- return float64(state.Value()) / dur.Seconds()
- }
- return 0
- }
- func getSpeedObj(state *State) (s *speed) {
- if sObj, ok := state.Get(speedObj).(*speed); ok {
- return sObj
- }
- s = new(speed)
- state.Set(speedObj, s)
- return
- }
- // ElementSpeed calculates current speed by EWMA
- // Optionally can take one or two string arguments.
- // First string will be used as value for format speed, default is "%s p/s".
- // Second string will be used when speed not available, default is "? p/s"
- // In template use as follows: {{speed .}} or {{speed . "%s per second"}} or {{speed . "%s ps" "..."}
- var ElementSpeed ElementFunc = func(state *State, args ...string) string {
- sp := getSpeedObj(state).value(state)
- if sp == 0 {
- return argsHelper(args).getNotEmptyOr(1, "? p/s")
- }
- return fmt.Sprintf(argsHelper(args).getNotEmptyOr(0, "%s p/s"), state.Format(int64(round(sp))))
- }
|