From c04a7b74e8253f7e53bd6c77848a3a46d2b0eff7 Mon Sep 17 00:00:00 2001 From: TeoV Date: Mon, 25 Sep 2017 16:46:28 +0300 Subject: [PATCH] Modify the struct of StatACD (Sum float64 -> Sum time.Duration) add StatTCD and test for it --- engine/statmetrics.go | 112 +++++++++++++++++++++++++++++++++---- engine/statmetrics_test.go | 85 ++++++++++++++++++++++++++-- utils/consts.go | 5 ++ 3 files changed, 185 insertions(+), 17 deletions(-) diff --git a/engine/statmetrics.go b/engine/statmetrics.go index b65cf8594..32a23379f 100644 --- a/engine/statmetrics.go +++ b/engine/statmetrics.go @@ -20,9 +20,9 @@ package engine import ( "fmt" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" + "time" ) // NewStatMetric instantiates the StatMetric @@ -31,6 +31,7 @@ func NewStatMetric(metricID string) (sm StatMetric, err error) { metrics := map[string]func() (StatMetric, error){ utils.MetaASR: NewASR, utils.MetaACD: NewACD, + utils.MetaTCD: NewTCD, } if _, has := metrics[metricID]; !has { return nil, fmt.Errorf("unsupported metric: %s", metricID) @@ -134,24 +135,24 @@ func (asr *StatASR) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { } func NewACD() (StatMetric, error) { - return &StatACD{Events: make(map[string]float64)}, nil + return &StatACD{Events: make(map[string]time.Duration)}, nil } // ACD implements AverageCallDuration metric type StatACD struct { - Sum float64 + Sum time.Duration Count float64 - Events map[string]float64 // map[EventTenantID]Duration - val *float64 // cached ACD value + Events map[string]time.Duration // map[EventTenantID]Duration + val *float64 // cached ACD value } -// getValue returns asr.val +// getValue returns acr.val func (acd *StatACD) getValue() float64 { if acd.val == nil { if acd.Count == 0 { acd.val = utils.Float64Pointer(float64(STATS_NA)) } else { - acd.val = utils.Float64Pointer(utils.Round(acd.Sum/acd.Count, + acd.val = utils.Float64Pointer(utils.Round(acd.Sum.Seconds()/acd.Count, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) } } @@ -174,16 +175,20 @@ func (acd *StatACD) GetFloat64Value() (v float64) { } func (acd *StatACD) AddEvent(ev *StatEvent) (err error) { - var answered float64 + var value time.Duration if at, err := ev.AnswerTime(config.CgrConfig().DefaultTimezone); err != nil && err != utils.ErrNotFound { return err } else if !at.IsZero() { - duration, _ := ev.Usage(config.CgrConfig().DefaultTimezone) - answered = duration.Seconds() - acd.Sum += duration.Seconds() + if duration, err := ev.Usage(config.CgrConfig().DefaultTimezone); err != nil && + err != utils.ErrNotFound { + return err + } else { + value = duration + acd.Sum += duration + } } - acd.Events[ev.TenantID()] = answered + acd.Events[ev.TenantID()] = value acd.Count += 1 acd.val = nil return @@ -209,3 +214,86 @@ func (acd *StatACD) Marshal(ms Marshaler) (marshaled []byte, err error) { func (acd *StatACD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, acd) } + +func NewTCD() (StatMetric, error) { + return &StatTCD{Events: make(map[string]time.Duration)}, nil +} + +// TCD implements TotalCallDuration metric +type StatTCD struct { + Sum time.Duration + Count float64 + Events map[string]time.Duration // map[EventTenantID]Duration + val *float64 // cached TCD value +} + +// getValue returns tcd.val +func (tcd *StatTCD) getValue() float64 { + if tcd.val == nil { + if tcd.Count == 0 { + tcd.val = utils.Float64Pointer(float64(STATS_NA)) + } else { + tcd.val = utils.Float64Pointer(utils.Round(tcd.Sum.Seconds(), + config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) + } + } + return *tcd.val +} + +func (tcd *StatTCD) GetStringValue(fmtOpts string) (val string) { + if tcd.Count == 0 { + return utils.NOT_AVAILABLE + } + return fmt.Sprintf("%+v", tcd.getValue()) +} + +func (tcd *StatTCD) GetValue() (v interface{}) { + return tcd.getValue() +} + +func (tcd *StatTCD) GetFloat64Value() (v float64) { + return tcd.getValue() +} + +func (tcd *StatTCD) AddEvent(ev *StatEvent) (err error) { + var value time.Duration + if at, err := ev.AnswerTime(config.CgrConfig().DefaultTimezone); err != nil && + err != utils.ErrNotFound { + return err + } else if !at.IsZero() { + if duration, err := ev.Usage(config.CgrConfig().DefaultTimezone); err != nil && + err != utils.ErrNotFound { + return err + } else { + value = duration + tcd.Sum += duration + } + + } + tcd.Events[ev.TenantID()] = value + tcd.Count += 1 + tcd.val = nil + return +} + +func (tcd *StatTCD) RemEvent(evTenantID string) (err error) { + duration, has := tcd.Events[evTenantID] + if !has { + return utils.ErrNotFound + } + if duration != 0 { + tcd.Sum -= duration + } + tcd.Count -= 1 + delete(tcd.Events, evTenantID) + tcd.val = nil + return +} + +func (tcd *StatTCD) Marshal(ms Marshaler) (marshaled []byte, err error) { + return ms.Marshal(tcd) +} + +func (tcd *StatTCD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { + return ms.Unmarshal(marshaled, tcd) +} diff --git a/engine/statmetrics_test.go b/engine/statmetrics_test.go index 0dd646c5e..32845ca8c 100644 --- a/engine/statmetrics_test.go +++ b/engine/statmetrics_test.go @@ -131,26 +131,101 @@ func TestACDGetValue(t *testing.T) { "Usage": time.Duration(10 * time.Second)}} acd.AddEvent(ev) if v := acd.GetValue(); v != 10.0 { - t.Errorf("wrong asr value: %f", v) + t.Errorf("wrong acd value: %f", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} acd.AddEvent(ev2) acd.AddEvent(ev3) if v := acd.GetValue(); v != 3.33333 { - t.Errorf("wrong asr value: %f", v) + t.Errorf("wrong acd value: %f", v) } acd.RemEvent(ev3.TenantID()) if v := acd.GetValue(); v != 5.0 { - t.Errorf("wrong asr value: %f", v) + t.Errorf("wrong acd value: %f", v) } acd.RemEvent(ev.TenantID()) if v := acd.GetValue(); v != 0.0 { - t.Errorf("wrong asr value: %f", v) + t.Errorf("wrong acd value: %f", v) } acd.RemEvent(ev2.TenantID()) if v := acd.GetValue(); v != -1.0 { - t.Errorf("wrong asr value: %f", v) + t.Errorf("wrong acd value: %f", v) + } + +} + +func TestTCDGetStringValue(t *testing.T) { + tcd, _ := NewTCD() + ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", + Fields: map[string]interface{}{ + "Usage": time.Duration(10 * time.Second), + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }} + if strVal := tcd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong tcd value: %s", strVal) + } + tcd.AddEvent(ev) + if strVal := tcd.GetStringValue(""); strVal != "10" { + t.Errorf("wrong tcd value: %s", strVal) + } + ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", + Fields: map[string]interface{}{ + "Usage": time.Duration(10 * time.Second), + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }} + ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} + tcd.AddEvent(ev2) + tcd.AddEvent(ev3) + if strVal := tcd.GetStringValue(""); strVal != "20" { + t.Errorf("wrong tcd value: %s", strVal) + } + tcd.RemEvent(ev2.TenantID()) + if strVal := tcd.GetStringValue(""); strVal != "10" { + t.Errorf("wrong tcd value: %s", strVal) + } + tcd.RemEvent(ev.TenantID()) + if strVal := tcd.GetStringValue(""); strVal != "0" { + t.Errorf("wrong tcd value: %s", strVal) + } + tcd.RemEvent(ev3.TenantID()) + if strVal := tcd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong tcd value: %s", strVal) + } + +} + +func TestTCDGetValue(t *testing.T) { + tcd, _ := NewTCD() + ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", + Fields: map[string]interface{}{ + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + "Usage": time.Duration(10 * time.Second)}} + tcd.AddEvent(ev) + if v := tcd.GetValue(); v != 10.0 { + t.Errorf("wrong tcd value: %f", v) + } + ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", + Fields: map[string]interface{}{ + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + "Usage": time.Duration(5 * time.Second)}} + ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} + tcd.AddEvent(ev2) + tcd.AddEvent(ev3) + if v := tcd.GetValue(); v != 15.000000 { + t.Errorf("wrong tcd value: %f", v) + } + tcd.RemEvent(ev.TenantID()) + if v := tcd.GetValue(); v != 5.000000 { + t.Errorf("wrong tcd value: %f", v) + } + tcd.RemEvent(ev2.TenantID()) + if v := tcd.GetValue(); v != 0.0 { + t.Errorf("wrong tcd value: %f", v) + } + tcd.RemEvent(ev3.TenantID()) + if v := tcd.GetValue(); v != -1.0 { + t.Errorf("wrong tcd value: %f", v) } } diff --git a/utils/consts.go b/utils/consts.go index 473b23bcd..7332eb3fe 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -420,6 +420,11 @@ const ( ID = "ID" MetaASR = "*asr" MetaACD = "*acd" + MetaTCD = "*tcd" + MetaACC = "*acc" + MetaTCC = "*tcc" + MetaPDD = "*pdd" + MetaDDC = "*ddc" CacheDestinations = "destinations" CacheReverseDestinations = "reverse_destinations" CacheRatingPlans = "rating_plans"