added DDC - Destination Distinct Count

fixes #106
This commit is contained in:
Radu Ioan Fericean
2015-07-06 12:33:17 +03:00
parent 10ef469fdb
commit 7d90cf3e85
5 changed files with 83 additions and 53 deletions

View File

@@ -829,6 +829,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
var tcdValues sort.Float64Slice
var accValues sort.Float64Slice
var tccValues sort.Float64Slice
var ddcValues sort.Float64Slice
// track if one value is never calculated
asrNeverConsidered := true
pddNeverConsidered := true
@@ -836,6 +837,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
tcdNeverConsidered := true
accNeverConsidered := true
tccNeverConsidered := true
ddcNeverConsidered := true
if utils.IsSliceMember([]string{LCR_STRATEGY_QOS, LCR_STRATEGY_QOS_THRESHOLD}, lcrCost.Entry.Strategy) {
if stats == nil {
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
@@ -909,6 +911,12 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
}
tccNeverConsidered = false
}
if ddc, exists := statValues[TCC]; exists {
if ddc > STATS_NA {
ddcValues = append(ddcValues, ddc)
}
ddcNeverConsidered = false
}
}
if statsErr { // Stats error in loop, to go next supplier
continue
@@ -919,6 +927,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
tcdValues.Sort()
accValues.Sort()
tccValues.Sort()
ddcValues.Sort()
//log.Print(asrValues, acdValues)
if utils.IsSliceMember([]string{LCR_STRATEGY_QOS_THRESHOLD, LCR_STRATEGY_QOS}, lcrCost.Entry.Strategy) {
@@ -926,7 +935,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
}
if lcrCost.Entry.Strategy == LCR_STRATEGY_QOS_THRESHOLD {
// filter suppliers by qos thresholds
asrMin, asrMax, pddMin, pddMax, acdMin, acdMax, tcdMin, tcdMax, accMin, accMax, tccMin, tccMax := lcrCost.Entry.GetQOSLimits()
asrMin, asrMax, pddMin, pddMax, acdMin, acdMax, tcdMin, tcdMax, accMin, accMax, tccMin, tccMax, ddcMin, ddcMax := lcrCost.Entry.GetQOSLimits()
//log.Print(asrMin, asrMax, acdMin, acdMax)
// skip current supplier if off limits
if asrMin > 0 && len(asrValues) != 0 && asrValues[0] < asrMin {
@@ -965,6 +974,12 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
if tccMax > 0 && len(tccValues) != 0 && tccValues[len(tccValues)-1] > tccMax {
continue
}
if ddcMin > 0 && len(ddcValues) != 0 && ddcValues[0] < ddcMin {
continue
}
if ddcMax > 0 && len(ddcValues) != 0 && ddcValues[len(ddcValues)-1] > ddcMax {
continue
}
}
}
}
@@ -1019,6 +1034,9 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
if !tccNeverConsidered {
qos[TCC] = utils.AvgNegative(tccValues)
}
if !ddcNeverConsidered {
qos[DDC] = utils.AvgNegative(ddcValues)
}
if utils.IsSliceMember([]string{LCR_STRATEGY_QOS, LCR_STRATEGY_QOS_THRESHOLD}, lcrCost.Entry.Strategy) {
supplCost.QOS = qos
supplCost.qosSortParams = qosSortParams

View File

@@ -162,11 +162,11 @@ func (lcr *LCR) Sort() {
sort.Sort(lcr)
}
func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minPDD, maxPDD, minACD, maxACD, minTCD, maxTCD time.Duration, minACC, maxACC, minTCC, maxTCC float64) {
// MIN_ASR;MAX_ASR;MIN_PDD;MAX_PDD;MIN_ACD;MAX_ACD;MIN_TCD;MAX_TCD;MIN_ACC;MAX_ACC;MIN_TCC;MAX_TCC
minASR, maxASR, minPDD, maxPDD, minACD, maxACD, minTCD, maxTCD, minACC, maxACC, minTCC, maxTCC = -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minPDD, maxPDD, minACD, maxACD, minTCD, maxTCD time.Duration, minACC, maxACC, minTCC, maxTCC, minDDC, maxDDC float64) {
// MIN_ASR;MAX_ASR;MIN_PDD;MAX_PDD;MIN_ACD;MAX_ACD;MIN_TCD;MAX_TCD;MIN_ACC;MAX_ACC;MIN_TCC;MAX_TCC;MIN_DDC;MAX_DDC
minASR, maxASR, minPDD, maxPDD, minACD, maxACD, minTCD, maxTCD, minACC, maxACC, minTCC, maxTCC, minDDC, maxDDC = -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
params := strings.Split(le.StrategyParams, utils.INFIELD_SEP)
if len(params) == 12 {
if len(params) == 14 {
var err error
if minASR, err = strconv.ParseFloat(params[0], 64); err != nil {
minASR = -1
@@ -204,6 +204,12 @@ func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minPDD, maxPDD, minA
if maxTCC, err = strconv.ParseFloat(params[11], 64); err != nil {
maxTCC = -1
}
if minDDC, err = strconv.ParseFloat(params[12], 64); err != nil {
minDDC = -1
}
if maxDDC, err = strconv.ParseFloat(params[13], 64); err != nil {
maxDDC = -1
}
}
return
}
@@ -220,7 +226,7 @@ func (le *LCREntry) GetParams() []string {
}
}
if len(cleanParams) == 0 && le.Strategy == LCR_STRATEGY_QOS {
return []string{ASR, PDD, ACD, TCD, ACC, TCC} // Default QoS stats if none configured
return []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC} // Default QoS stats if none configured
}
return cleanParams
}

View File

@@ -94,46 +94,49 @@ func TestLcrQOSSorterOACD(t *testing.T) {
func TestLcrGetQosLimitsAll(t *testing.T) {
le := &LCREntry{
StrategyParams: "1.2;2.3;4;7;45s;67m;16s;17m;8.9;10.11;12.13;14.15",
StrategyParams: "1.2;2.3;4;7;45s;67m;16s;17m;8.9;10.11;12.13;14.15;2;3",
}
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits()
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc := le.GetQOSLimits()
if minAsr != 1.2 || maxAsr != 2.3 ||
minPdd != 4*time.Second || maxPdd != 7*time.Second ||
minAcd != 45*time.Second || maxAcd != 67*time.Minute ||
minTcd != 16*time.Second || maxTcd != 17*time.Minute ||
minAcc != 8.9 || maxAcc != 10.11 ||
minTcc != 12.13 || maxTcc != 14.15 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc)
minTcc != 12.13 || maxTcc != 14.15 ||
minDdc != 2 || maxDdc != 3 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc)
}
}
func TestLcrGetQosLimitsSome(t *testing.T) {
le := &LCREntry{
StrategyParams: "1.2;;3;;;67m;;30m;1;;3;",
StrategyParams: "1.2;;3;;;67m;;30m;1;;3;;;2",
}
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits()
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc := le.GetQOSLimits()
if minAsr != 1.2 || maxAsr != -1 ||
minPdd != 3*time.Second || maxPdd != -1 ||
minAcd != -1 || maxAcd != 67*time.Minute ||
minTcd != -1 || maxTcd != 30*time.Minute ||
minAcc != 1 || maxAcc != -1 ||
minTcc != 3 || maxTcc != -1 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd, minTcd, maxTcd)
minTcc != 3 || maxTcc != -1 ||
minDdc != -1 || maxDdc != 2 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd, minTcd, maxTcd, minTcc, maxTcc, minDdc, maxDdc)
}
}
func TestLcrGetQosLimitsNone(t *testing.T) {
le := &LCREntry{
StrategyParams: ";;;;;;;;;",
StrategyParams: ";;;;;;;;;;;",
}
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc := le.GetQOSLimits()
minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc := le.GetQOSLimits()
if minAsr != -1 || maxAsr != -1 ||
minPdd != -1 || maxPdd != -1 ||
minAcd != -1 || maxAcd != -1 ||
minTcd != -1 || maxTcd != -1 ||
minAcc != -1 || maxAcc != -1 ||
minTcc != -1 || maxTcc != -1 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd)
minTcc != -1 || maxTcc != -1 ||
minDdc != -1 || maxDdc != -1 {
t.Error("Wrong qos limits parsed: ", minAsr, maxAsr, minAcd, maxAcd, minDdc, maxDdc)
}
}

View File

@@ -284,7 +284,7 @@ func TestGetLCR(t *testing.T) {
}
}
danStatsId := "dan12_stats"
rsponder.Stats.AddQueue(&CdrStats{Id: danStatsId, Supplier: []string{"dan12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC}}, nil)
rsponder.Stats.AddQueue(&CdrStats{Id: danStatsId, Supplier: []string{"dan12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, nil)
danRpfl := &RatingProfile{Id: "*out:tenant12:call:dan12",
RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{
ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC),
@@ -294,7 +294,7 @@ func TestGetLCR(t *testing.T) {
}},
}
rifStatsId := "rif12_stats"
rsponder.Stats.AddQueue(&CdrStats{Id: rifStatsId, Supplier: []string{"rif12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC}}, nil)
rsponder.Stats.AddQueue(&CdrStats{Id: rifStatsId, Supplier: []string{"rif12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, nil)
rifRpfl := &RatingProfile{Id: "*out:tenant12:call:rif12",
RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{
ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC),
@@ -304,7 +304,7 @@ func TestGetLCR(t *testing.T) {
}},
}
ivoStatsId := "ivo12_stats"
rsponder.Stats.AddQueue(&CdrStats{Id: ivoStatsId, Supplier: []string{"ivo12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC}}, nil)
rsponder.Stats.AddQueue(&CdrStats{Id: ivoStatsId, Supplier: []string{"ivo12"}, Metrics: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, nil)
ivoRpfl := &RatingProfile{Id: "*out:tenant12:call:ivo12",
RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{
ActivationTime: time.Date(2015, 01, 01, 8, 0, 0, 0, time.UTC),
@@ -341,7 +341,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}},
},
},
}
@@ -459,11 +459,11 @@ 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},
SupplierCosts: []*LCRSupplierCost{
&LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}},
},
}
var lcrQT LCRCost
@@ -480,10 +480,10 @@ func TestGetLCR(t *testing.T) {
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:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, ACD: 300, TCD: 300, ASR: 100, ACC: 2, TCC: 2}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, ACD: 300, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{"35", "4m"}},
},
}
if err := rsponder.GetLCR(cdQosThreshold, &lcrQT); err != nil {
@@ -492,7 +492,7 @@ func TestGetLCR(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry)
} else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) {
t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts[0], lcrQT.SupplierCosts[0])
t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts[1], lcrQT.SupplierCosts[1])
}
// Test *qos strategy here
@@ -509,9 +509,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:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{ACD: -1, PDD: -1, TCD: -1, ASR: -1, ACC: -1, TCC: -1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 300, PDD: -1, TCD: 300, ASR: 100, ACC: 2, TCC: 2}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 180, PDD: -1, TCD: 180, ASR: 100, ACC: 1, TCC: 1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{ACD: -1, PDD: -1, TCD: -1, ASR: -1, ACC: -1, TCC: -1, DDC: -1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 300, PDD: -1, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}},
&LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 180, PDD: -1, TCD: 180, ASR: 100, ACC: 1, TCC: 1, DDC: 1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}},
},
}
var lcrQ LCRCost

View File

@@ -36,6 +36,7 @@ const TCD = "TCD"
const ACC = "ACC"
const TCC = "TCC"
const PDD = "PDD"
const DDC = "DDC"
const STATS_NA = -1
func CreateMetric(metric string) Metric {
@@ -52,6 +53,8 @@ func CreateMetric(metric string) Metric {
return &ACCMetric{}
case TCC:
return &TCCMetric{}
case DDC:
return NewDccMetric()
}
return nil
}
@@ -230,37 +233,37 @@ func (tcc *TCCMetric) GetValue() float64 {
return utils.Round(tcc.sum, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
// DIC - Destination ID Counter
// DDC - Destination Distinct Count
//
type DICMetric struct {
type DCCMetric struct {
destinations map[string]int64
}
func (dic *DICMetric) AddCdr(cdr *QCdr) {
if dic.destinations == nil {
dic.destinations = make(map[string]int64)
}
if count, exists := dic.destinations[cdr.Dest]; exists {
dic.destinations[cdr.Dest] = count + 1
} else {
dic.destinations[cdr.Dest] = 0
func NewDccMetric() *DCCMetric {
return &DCCMetric{
destinations: make(map[string]int64),
}
}
func (dic *DICMetric) RemoveCdr(cdr *QCdr) {
if dic.destinations == nil {
dic.destinations = make(map[string]int64)
}
if count, exists := dic.destinations[cdr.Dest]; exists && count > 1 {
dic.destinations[cdr.Dest] = count - 1
func (dcc *DCCMetric) AddCdr(cdr *QCdr) {
if count, exists := dcc.destinations[cdr.Dest]; exists {
dcc.destinations[cdr.Dest] = count + 1
} else {
dic.destinations[cdr.Dest] = 0
dcc.destinations[cdr.Dest] = 0
}
}
func (dic *DICMetric) GetValue() float64 {
if len(dic.destinations) == 0 {
func (dcc *DCCMetric) RemoveCdr(cdr *QCdr) {
if count, exists := dcc.destinations[cdr.Dest]; exists && count > 1 {
dcc.destinations[cdr.Dest] = count - 1
} else {
dcc.destinations[cdr.Dest] = 0
}
}
func (dcc *DCCMetric) GetValue() float64 {
if len(dcc.destinations) == 0 {
return STATS_NA
}
return float64(len(dic.destinations))
return float64(len(dcc.destinations))
}