mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
RateS with winning rate
This commit is contained in:
@@ -25,19 +25,56 @@ import (
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
)
|
||||
|
||||
func newRatesWithWinner(rt *engine.Rate) (rts *ratesWithWinner) {
|
||||
rts = &ratesWithWinner{
|
||||
rts: make(map[string]*engine.Rate),
|
||||
}
|
||||
rts.add(rt)
|
||||
return
|
||||
}
|
||||
|
||||
// ratesWithWinner computes always the winner based on highest Weight
|
||||
type ratesWithWinner struct {
|
||||
rts map[string]*engine.Rate
|
||||
rt *engine.Rate
|
||||
}
|
||||
|
||||
//add will add the rate to the rates
|
||||
func (rs *ratesWithWinner) add(rt *engine.Rate) {
|
||||
rs.rts[rt.ID] = rt
|
||||
if rs.rt == nil || rs.rt.Weight < rt.Weight {
|
||||
rs.rt = rt
|
||||
}
|
||||
}
|
||||
|
||||
// winner returns the rate with the highest Weight
|
||||
func (rs *ratesWithWinner) winner() *engine.Rate {
|
||||
return rs.rt
|
||||
}
|
||||
|
||||
// has tests if the rateID is present in rates
|
||||
func (rs *ratesWithWinner) has(rtID string) (has bool) {
|
||||
_, has = rs.rts[rtID]
|
||||
return
|
||||
}
|
||||
|
||||
// orderRatesOnIntervals will order the rates based on ActivationInterval and intervalStart of each Rate
|
||||
// there can be only one winning Rate for each interval, prioritized by the Weight
|
||||
func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Duration, isDuration bool) (ordRts []*engine.RateSInterval) {
|
||||
func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Duration, isDuration bool, aTimeVerbosity int) (ordRts []*engine.RateSInterval) {
|
||||
cronSTime := sTime.Add(time.Duration(-1 * time.Minute)) // cron min verbosity is minute
|
||||
endTime := sTime.Add(usage) // cover also the last unit used
|
||||
// index the received rates
|
||||
rtIdx := make(map[time.Time][]*engine.Rate) // map[ActivationTime][]*engine.Rate
|
||||
rtIdx := make(map[time.Time]*ratesWithWinner) // map[ActivationTime]*ratesWithWinner
|
||||
for _, rt := range aRts {
|
||||
nextRunTime := rt.NextActivationTime(cronSTime)
|
||||
if nextRunTime.After(endTime) {
|
||||
continue
|
||||
}
|
||||
rtIdx[nextRunTime] = append(rtIdx[nextRunTime], rt)
|
||||
if _, hasKey := rtIdx[nextRunTime]; !hasKey {
|
||||
rtIdx[nextRunTime] = newRatesWithWinner(rt)
|
||||
continue
|
||||
}
|
||||
rtIdx[nextRunTime].add(rt)
|
||||
}
|
||||
// sort the activation times
|
||||
sortedATimes := make([]time.Time, len(rtIdx))
|
||||
@@ -56,10 +93,94 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Dura
|
||||
}
|
||||
sortedATimes = sortedATimes[i-1:]
|
||||
}
|
||||
// only for duration we will have multiple activationTimes
|
||||
if !isDuration {
|
||||
sortedATimes = sortedATimes[:1]
|
||||
// finalize the sortedATimes
|
||||
if isDuration {
|
||||
// add all the possible ActivationTimes from cron expressions
|
||||
for i := 0; i < aTimeVerbosity; i++ {
|
||||
//fmt.Printf("aTimeVerbosity run: %d, sortedATimes: %+v\n", i, sortedATimes)
|
||||
var altered bool
|
||||
for _, aTime := range sortedATimes {
|
||||
//fmt.Printf("checking aTime: %+v\n", aTime)
|
||||
// each of the rates will be checked against activationTime
|
||||
for _, rt := range aRts {
|
||||
nextRunTime := rt.NextActivationTime(aTime)
|
||||
//fmt.Printf("rate: %s, rt.ActivationStart: %s, on aTime: %+v nextRunTime: %+v\n", rt.ID, rt.ActivationStart, aTime, nextRunTime)
|
||||
if nextRunTime.After(endTime) {
|
||||
//fmt.Println("after endTime")
|
||||
continue
|
||||
}
|
||||
if rtMp, hasRunTime := rtIdx[nextRunTime]; hasRunTime {
|
||||
if rtMp.has(rt.ID) { // rate was already captured
|
||||
//fmt.Printf("already captured!\n")
|
||||
continue
|
||||
}
|
||||
}
|
||||
// try to see if it fits in the activation time list
|
||||
for sTimeIdx, sATime := range sortedATimes {
|
||||
//fmt.Printf("sTimeIdx: %d, sAtime: %+v\n", sTimeIdx, sATime)
|
||||
if sTimeIdx == 0 { // ignore the first one
|
||||
//fmt.Println("first interval")
|
||||
continue
|
||||
}
|
||||
if nextRunTime.After(sATime) {
|
||||
if sTimeIdx != len(sortedATimes)-1 {
|
||||
//fmt.Println("not the last one and higher")
|
||||
continue
|
||||
}
|
||||
// last one and higher
|
||||
if rtIdx[sortedATimes[sTimeIdx]].winner().ID == rt.ID { // activated in last slot
|
||||
//fmt.Println("activated in last slot")
|
||||
continue
|
||||
}
|
||||
}
|
||||
if rtIdx[sortedATimes[sTimeIdx-1]].has(rt.ID) { // activated in previous slot
|
||||
//fmt.Println("activated in previous slot")
|
||||
continue
|
||||
} else {
|
||||
//fmt.Printf("rtIdx: %+v, has no rt with id: %s\n", rtIdx, rt.ID)
|
||||
}
|
||||
// Anything passing here should be a winner
|
||||
if nextRunTime == sATime {
|
||||
//fmt.Println("already in time, adding to rates")
|
||||
rtIdx[nextRunTime].add(rt)
|
||||
altered = true
|
||||
break
|
||||
}
|
||||
if _, has := rtIdx[nextRunTime]; !has {
|
||||
rtIdx[nextRunTime] = newRatesWithWinner(rt)
|
||||
} else {
|
||||
rtIdx[nextRunTime].add(rt)
|
||||
}
|
||||
if sTimeIdx == len(sortedATimes)-1 { // last index, higher than the last ActivationTime
|
||||
//fmt.Println("adding as last")
|
||||
sortedATimes = append(sortedATimes, nextRunTime)
|
||||
} else { // Insert before current index
|
||||
//fmt.Printf("insert in betwee, have before: %+v", sortedATimes)
|
||||
sortedATimes = append(sortedATimes, time.Time{})
|
||||
copy(sortedATimes[sTimeIdx+1:], sortedATimes[sTimeIdx:])
|
||||
sortedATimes[sTimeIdx] = nextRunTime
|
||||
//fmt.Printf(" have after: %+v\n", sortedATimes)
|
||||
}
|
||||
altered = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if altered { // start again from first aTime
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if !altered { // no change was done in a complete iteration, no need of further lookups
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else { // only for duration we will have multiple activationTimes
|
||||
sortedATimes = sortedATimes[:1] // only first ordered activationTime is considered for units
|
||||
}
|
||||
//fmt.Printf("sortedTimes: %+v\n", sortedATimes)
|
||||
// add the Intervals and Increments
|
||||
var usageSIdx, usageEIdx time.Duration
|
||||
for i, at := range sortedATimes {
|
||||
if sTime.Before(at) {
|
||||
@@ -70,26 +191,21 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Dura
|
||||
} else {
|
||||
usageEIdx = usage
|
||||
}
|
||||
// Sort the rates based on their Weight
|
||||
sort.Slice(rtIdx[at], func(i, j int) bool {
|
||||
return rtIdx[at][i].Weight < rtIdx[at][j].Weight
|
||||
})
|
||||
// append the valid increments
|
||||
var rtIcmts []*engine.RateSIncrement
|
||||
for _, rt := range rtIdx[at] {
|
||||
|
||||
for j, ivlRt := range rt.IntervalRates {
|
||||
if ivlRt.IntervalStart > usageEIdx {
|
||||
break
|
||||
}
|
||||
if j != len(rt.IntervalRates)-1 &&
|
||||
rt.IntervalRates[j+1].IntervalStart <= usageSIdx { // not the last one
|
||||
// the next intervalStat is still good for usageSIdx, no need of adding the current one
|
||||
continue
|
||||
}
|
||||
// ready to add the increment
|
||||
rtIcmts = append(rtIcmts,
|
||||
&engine.RateSIncrement{Rate: rt, IntervalRateIndex: j})
|
||||
rt := rtIdx[at].winner()
|
||||
for j, ivlRt := range rt.IntervalRates {
|
||||
if ivlRt.IntervalStart > usageEIdx {
|
||||
break
|
||||
}
|
||||
if j != len(rt.IntervalRates)-1 &&
|
||||
rt.IntervalRates[j+1].IntervalStart <= usageSIdx { // not the last one
|
||||
// the next intervalStat is still good for usageSIdx, no need of adding the current one
|
||||
continue
|
||||
}
|
||||
// ready to add the increment
|
||||
rtIcmts = append(rtIcmts,
|
||||
&engine.RateSIncrement{Rate: rt, IntervalRateIndex: j})
|
||||
}
|
||||
ordRts = append(ordRts, &engine.RateSInterval{Increments: rtIcmts})
|
||||
}
|
||||
|
||||
@@ -60,12 +60,72 @@ func TestOrderRatesOnIntervals(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
//
|
||||
// time.Date(2020, time.December, 23, 23, 59, 05, 0, time.UTC),
|
||||
|
||||
if ordRts := orderRatesOnIntervals(
|
||||
allRts, time.Date(2020, time.June, 28, 18, 56, 05, 0, time.UTC),
|
||||
time.Duration(2*time.Minute), true); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
time.Duration(2*time.Minute), true, 10); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
|
||||
expOrdered = []*engine.RateSInterval{
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rt0,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rtChristmas,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if ordRts := orderRatesOnIntervals(
|
||||
allRts, time.Date(2020, time.December, 23, 23, 59, 05, 0, time.UTC),
|
||||
time.Duration(2*time.Minute), true, 10); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
expOrdered = []*engine.RateSInterval{
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rt0,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rtChristmas,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rt0,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
/*
|
||||
fmt.Println("Third test")
|
||||
if ordRts := orderRatesOnIntervals(
|
||||
allRts, time.Date(2020, time.December, 23, 23, 59, 05, 0, time.UTC),
|
||||
time.Duration(2*time.Minute), true, 10); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user