From ee413d19c667a430fd10deb12c1f765fc4ed1e56 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 22 Apr 2015 23:16:29 +0300 Subject: [PATCH] added ACC and TCC to LCR what a nice commit message! --- engine/calldesc.go | 30 +++++++++++++++++++++++++++--- engine/lcr.go | 21 +++++++++++++++++---- engine/lcr_test.go | 32 +++++++++++++++++++------------- engine/responder_test.go | 26 +++++++++++++------------- engine/stats_queue.go | 2 ++ 5 files changed, 78 insertions(+), 33 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 279d6ce52..7fb034f14 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -749,7 +749,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { lcrCD.Category = category lcrCD.Account = supplier lcrCD.Subject = supplier - var asrMean, acdMean float64 + var asrMean, acdMean, accMean, tccMean float64 var qosSortParams []string if lcrCost.Entry.Strategy == LCR_STRATEGY_QOS || lcrCost.Entry.Strategy == LCR_STRATEGY_QOS_THRESHOLD { rpfKey := utils.ConcatenatedKey(ratingProfileSearchKey, supplier) @@ -766,6 +766,8 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { } var asrValues sort.Float64Slice var acdValues sort.Float64Slice + var accValues sort.Float64Slice + var tccValues sort.Float64Slice for _, qId := range cdrStatsQueueIds { statValues := make(map[string]float64) if err := stats.GetValues(qId, &statValues); err != nil { @@ -777,18 +779,28 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { if acd, exists := statValues[ACD]; exists { acdValues = append(acdValues, acd) } + if acc, exists := statValues[ACC]; exists { + accValues = append(accValues, acc) + } + if tcc, exists := statValues[TCC]; exists { + tccValues = append(tccValues, tcc) + } } asrValues.Sort() acdValues.Sort() + accValues.Sort() + tccValues.Sort() asrMean = utils.Avg(asrValues) acdMean = utils.Avg(acdValues) + accMean = utils.Avg(accValues) + tccMean = utils.Avg(tccValues) //log.Print(asrValues, acdValues) if lcrCost.Entry.Strategy == LCR_STRATEGY_QOS_THRESHOLD || lcrCost.Entry.Strategy == LCR_STRATEGY_QOS { qosSortParams = lcrCost.Entry.GetParams() } if lcrCost.Entry.Strategy == LCR_STRATEGY_QOS_THRESHOLD { // filter suppliers by qos thresholds - asrMin, asrMax, acdMin, acdMax := lcrCost.Entry.GetQOSLimits() + asrMin, asrMax, acdMin, acdMax, accMin, accMax, tccMin, tccMax := lcrCost.Entry.GetQOSLimits() //log.Print(asrMin, asrMax, acdMin, acdMax) // skip current supplier if off limits if asrMin > 0 && len(asrValues) != 0 && asrValues[0] < asrMin { @@ -803,6 +815,18 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { if acdMax > 0 && len(acdValues) != 0 && acdValues[len(acdValues)-1] > acdMax.Seconds() { continue } + if accMin > 0 && len(accValues) != 0 && accValues[0] < accMin { + continue + } + if accMax > 0 && len(accValues) != 0 && accValues[len(accValues)-1] > accMax { + continue + } + if tccMin > 0 && len(tccValues) != 0 && tccValues[0] < tccMin { + continue + } + if tccMax > 0 && len(tccValues) != 0 && tccValues[len(tccValues)-1] > tccMax { + continue + } } } } @@ -830,7 +854,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { Duration: cc.GetDuration(), } if utils.IsSliceMember([]string{LCR_STRATEGY_QOS, LCR_STRATEGY_QOS_THRESHOLD}, lcrCost.Entry.Strategy) { - supplCost.QOS = map[string]float64{"ASR": asrMean, "ACD": acdMean} + supplCost.QOS = map[string]float64{ASR: asrMean, ACD: acdMean, ACC: accMean, TCC: tccMean} supplCost.qosSortParams = qosSortParams } lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, supplCost) diff --git a/engine/lcr.go b/engine/lcr.go index 0b4d6327f..78c79478f 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -91,10 +91,11 @@ func (lcr *LCR) Sort() { sort.Sort(lcr) } -func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minACD, maxACD time.Duration) { - // MIN_ASR;MAX_ASR;MIN_ACD;MAX_ACD +func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minACD, maxACD time.Duration, minACC, maxACC, minTCC, maxTCC float64) { + // MIN_ASR;MAX_ASR;MIN_ACD;MAX_ACD;MIN_ACC;MAX_ACC;MIN_TCC;MAX_TCC + minASR, maxASR, minACD, maxACD, minACC, maxACC, minTCC, maxTCC = -1, -1, -1, -1, -1, -1, -1, -1 params := strings.Split(le.StrategyParams, utils.INFIELD_SEP) - if len(params) == 4 { + if len(params) == 8 { var err error if minASR, err = strconv.ParseFloat(params[0], 64); err != nil { minASR = -1 @@ -108,6 +109,18 @@ func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minACD, maxACD time. if maxACD, err = time.ParseDuration(params[3]); err != nil { maxACD = -1 } + if minACC, err = strconv.ParseFloat(params[4], 64); err != nil { + minACC = -1 + } + if maxACC, err = strconv.ParseFloat(params[5], 64); err != nil { + maxACC = -1 + } + if minTCC, err = strconv.ParseFloat(params[6], 64); err != nil { + minTCC = -1 + } + if maxTCC, err = strconv.ParseFloat(params[7], 64); err != nil { + maxTCC = -1 + } } return } @@ -124,7 +137,7 @@ func (le *LCREntry) GetParams() []string { } } if len(cleanParams) == 0 && le.Strategy == LCR_STRATEGY_QOS { - return []string{ASR, ACD} // Default QoS stats if none configured + return []string{ASR, ACD, ACC, TCC} // Default QoS stats if none configured } return cleanParams } diff --git a/engine/lcr_test.go b/engine/lcr_test.go index 6131fe383..c1914ab2d 100644 --- a/engine/lcr_test.go +++ b/engine/lcr_test.go @@ -90,33 +90,39 @@ func TestLcrQOSSorterOACD(t *testing.T) { func TestLcrGetQosLimitsAll(t *testing.T) { le := &LCREntry{ - StrategyParams: "1.2;2.3;45s;67m", + StrategyParams: "1.2;2.3;45s;67m;8.9;10.11;12.13;14.15", } - minAsr, maxAsr, minAcd, maxAcd := le.GetQOSLimits() + minAsr, maxAsr, minAcd, maxAcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits() if minAsr != 1.2 || maxAsr != 2.3 || - minAcd != 45*time.Second || maxAcd != 67*time.Minute { - t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd) + minAcd != 45*time.Second || maxAcd != 67*time.Minute || + minAcc != 8.9 || maxAcc != 10.11 || + minTcc != 12.13 || maxTcc != 14.15 { + t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd, minAcc, maxAcc, minTcc, maxTcc) } } func TestLcrGetQosLimitsSome(t *testing.T) { le := &LCREntry{ - StrategyParams: "1.2;;;67m", + StrategyParams: "1.2;;;67m;1;;3;", } - minAsr, maxAsr, minAcd, maxAcd := le.GetQOSLimits() + minAsr, maxAsr, minAcd, maxAcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits() if minAsr != 1.2 || maxAsr != -1 || - minAcd != -1 || maxAcd != 67*time.Minute { + minAcd != -1 || maxAcd != 67*time.Minute || + minAcc != 1 || maxAcc != -1 || + minTcc != 3 || maxTcc != -1 { t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd) } } func TestLcrGetQosLimitsNone(t *testing.T) { le := &LCREntry{ - StrategyParams: ";;;", + StrategyParams: ";;;;;;;", } - minAsr, maxAsr, minAcd, maxAcd := le.GetQOSLimits() + minAsr, maxAsr, minAcd, maxAcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits() if minAsr != -1 || maxAsr != -1 || - minAcd != -1 || maxAcd != -1 { + minAcd != -1 || maxAcd != -1 || + minAcc != -1 || maxAcc != -1 || + minTcc != -1 || maxTcc != -1 { t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd) } } @@ -127,7 +133,7 @@ func TestLcrGetQosSortParamsNone(t *testing.T) { StrategyParams: "", } sort := le.GetParams() - if sort[0] != ASR || sort[1] != ACD { + if sort[0] != ASR || sort[1] != ACD || sort[2] != ACC || sort[3] != TCC { t.Error("Wrong qos sort params parsed: ", sort) } } @@ -135,10 +141,10 @@ func TestLcrGetQosSortParamsNone(t *testing.T) { func TestLcrGetQosSortParamsEmpty(t *testing.T) { le := &LCREntry{ Strategy: LCR_STRATEGY_QOS, - StrategyParams: ";", + StrategyParams: ";;;", } sort := le.GetParams() - if sort[0] != ASR || sort[1] != ACD { + if sort[0] != ASR || sort[1] != ACD || sort[2] != ACC || sort[3] != TCC { t.Error("Wrong qos sort params parsed: ", sort) } } diff --git a/engine/responder_test.go b/engine/responder_test.go index f9b2c649a..376cda54a 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -282,7 +282,7 @@ func TestGetLCR(t *testing.T) { } } danStatsId := "dan12_stats" - rsponder.Stats.AddQueue(&CdrStats{Id: danStatsId, Supplier: []string{"dan12"}, Metrics: []string{ASR, ACD}}, nil) + rsponder.Stats.AddQueue(&CdrStats{Id: danStatsId, Supplier: []string{"dan12"}, Metrics: []string{ASR, ACD, ACC, TCC}}, nil) danRpfl := &RatingProfile{Id: "*out:tenant12:call:dan12", RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{ ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC), @@ -292,7 +292,7 @@ func TestGetLCR(t *testing.T) { }}, } rifStatsId := "rif12_stats" - rsponder.Stats.AddQueue(&CdrStats{Id: rifStatsId, Supplier: []string{"rif12"}, Metrics: []string{ASR, ACD}}, nil) + rsponder.Stats.AddQueue(&CdrStats{Id: rifStatsId, Supplier: []string{"rif12"}, Metrics: []string{ASR, ACD, ACC, TCC}}, nil) rifRpfl := &RatingProfile{Id: "*out:tenant12:call:rif12", RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{ ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC), @@ -302,7 +302,7 @@ func TestGetLCR(t *testing.T) { }}, } ivoStatsId := "ivo12_stats" - rsponder.Stats.AddQueue(&CdrStats{Id: ivoStatsId, Supplier: []string{"ivo12"}, Metrics: []string{ASR, ACD}}, nil) + rsponder.Stats.AddQueue(&CdrStats{Id: ivoStatsId, Supplier: []string{"ivo12"}, Metrics: []string{ASR, ACD, ACC, TCC}}, nil) ivoRpfl := &RatingProfile{Id: "*out:tenant12:call:ivo12", RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{ ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC), @@ -339,7 +339,7 @@ func TestGetLCR(t *testing.T) { &LCRActivation{ ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC), Entries: []*LCREntry{ - &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;", Weight: 10.0}}, + &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;;;;;", Weight: 10.0}}, }, }, } @@ -456,7 +456,7 @@ func TestGetLCR(t *testing.T) { Subject: "dan", } eQTLcr := &LCRCost{ - Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;", Weight: 10.0}, + Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;;;;;", Weight: 10.0}, } var lcrQT LCRCost if err := rsponder.GetLCR(cdQosThreshold, &lcrQT); err != nil { @@ -467,15 +467,15 @@ func TestGetLCR(t *testing.T) { } else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) { t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts, lcrQT.SupplierCosts) } - cdr := &StoredCdr{Supplier: "rif12", AnswerTime: time.Now(), Usage: 3 * time.Minute} + cdr := &StoredCdr{Supplier: "rif12", AnswerTime: time.Now(), Usage: 3 * time.Minute, Cost: 1} rsponder.Stats.AppendCDR(cdr, nil) - cdr = &StoredCdr{Supplier: "dan12", AnswerTime: time.Now(), Usage: 5 * time.Minute} + cdr = &StoredCdr{Supplier: "dan12", AnswerTime: time.Now(), Usage: 5 * time.Minute, Cost: 2} rsponder.Stats.AppendCDR(cdr, nil) eQTLcr = &LCRCost{ - Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;", Weight: 10.0}, + Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;4m;;;;;", Weight: 10.0}, SupplierCosts: []*LCRSupplierCost{ &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, - QOS: map[string]float64{ACD: 300, ASR: 100}, qosSortParams: []string{"35", "4m"}}, + QOS: map[string]float64{ACD: 300, ASR: 100, ACC: 2, TCC: 2}, qosSortParams: []string{"35", "4m"}}, }, } if err := rsponder.GetLCR(cdQosThreshold, &lcrQT); err != nil { @@ -501,9 +501,9 @@ func TestGetLCR(t *testing.T) { eQosLcr := &LCRCost{ Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS, Weight: 10.0}, SupplierCosts: []*LCRSupplierCost{ - &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 300, ASR: 100}, qosSortParams: []string{ASR, ACD}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 180, ASR: 100}, qosSortParams: []string{ASR, ACD}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 0, ASR: 0}, qosSortParams: []string{ASR, ACD}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 300, ASR: 100, ACC: 2, TCC: 2}, qosSortParams: []string{ASR, ACD, ACC, TCC}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 180, ASR: 100, ACC: 1, TCC: 1}, qosSortParams: []string{ASR, ACD, ACC, TCC}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 0, ASR: 0, ACC: 0, TCC: 0}, qosSortParams: []string{ASR, ACD, ACC, TCC}}, }, } var lcrQ LCRCost @@ -513,7 +513,7 @@ func TestGetLCR(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eQosLcr.Entry, lcrQ.Entry) } else if !reflect.DeepEqual(eQosLcr.SupplierCosts, lcrQ.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eQosLcr.SupplierCosts[0], lcrQ.SupplierCosts[0]) + t.Errorf("Expecting: %+v, received: %+v", eQosLcr.SupplierCosts[1], lcrQ.SupplierCosts[1]) for _, sc := range lcrQ.SupplierCosts { log.Printf("%+v", sc) } diff --git a/engine/stats_queue.go b/engine/stats_queue.go index 46d9665fd..37c1673ec 100644 --- a/engine/stats_queue.go +++ b/engine/stats_queue.go @@ -38,6 +38,8 @@ var METRIC_TRIGGER_MAP = map[string]string{ "*max_acd": ACD, "*min_acc": ACC, "*max_acc": ACC, + "*min_tcc": ACC, + "*max_tcc": ACC, } // Simplified cdr structure containing only the necessary info