mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-19 22:28:45 +05:00
RateS with initial OrderRatesOnIntervals
This commit is contained in:
@@ -45,8 +45,20 @@ type RateProfile struct {
|
||||
maxCost *utils.Decimal
|
||||
}
|
||||
|
||||
func (rpp *RateProfile) TenantID() string {
|
||||
return utils.ConcatenatedKey(rpp.Tenant, rpp.ID)
|
||||
func (rp *RateProfile) TenantID() string {
|
||||
return utils.ConcatenatedKey(rp.Tenant, rp.ID)
|
||||
}
|
||||
|
||||
func (rp *RateProfile) Compile() (err error) {
|
||||
rp.connFee = utils.NewDecimalFromFloat64(rp.ConnectFee)
|
||||
rp.minCost = utils.NewDecimalFromFloat64(rp.MinCost)
|
||||
rp.minCost = utils.NewDecimalFromFloat64(rp.MaxCost)
|
||||
for _, rtP := range rp.Rates {
|
||||
if err = rtP.Compile(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Route defines rate related information used within a RateProfile
|
||||
@@ -61,6 +73,21 @@ type Rate struct {
|
||||
aTime cron.Schedule // compiled version of activation time as cron.Schedule interface
|
||||
}
|
||||
|
||||
func (rt *Rate) Compile() (err error) {
|
||||
aTime := rt.ActivationStart
|
||||
if aTime == utils.EmptyString {
|
||||
aTime = "* * * * *"
|
||||
}
|
||||
if rt.aTime, err = cron.ParseStandard(aTime); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rt *Rate) NextActivationTime(t time.Time) time.Time {
|
||||
return rt.aTime.Next(t)
|
||||
}
|
||||
|
||||
type IntervalRate struct {
|
||||
IntervalStart time.Duration // Starting point when the Rate kicks in
|
||||
Unit time.Duration // RateUnit
|
||||
@@ -83,6 +110,7 @@ type RateSInterval struct {
|
||||
}
|
||||
|
||||
type RateSIncrement struct {
|
||||
UsageStart time.Duration
|
||||
Rate *Rate
|
||||
Rate *Rate
|
||||
IntervalRateIndex int
|
||||
Usage time.Duration
|
||||
}
|
||||
|
||||
@@ -18,46 +18,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package rates
|
||||
|
||||
/*
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
)
|
||||
|
||||
// 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, isDuration bool) (ordRts []*engine.RateSInterval) {
|
||||
func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Duration, isDuration bool) (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]map[time.Duration][]*engine.Rate) // map[ActivationTime]map[IntervalStart][]*engine.Rate
|
||||
rtIdx := make(map[time.Time][]*engine.Rate) // map[ActivationTime][]*engine.Rate
|
||||
for _, rt := range aRts {
|
||||
var rtATimesKey time.Time
|
||||
if rt.ActivationInterval != nil {
|
||||
rtATimesKey = rt.ActivationInterval.ActivationTime
|
||||
nextRunTime := rt.NextActivationTime(cronSTime)
|
||||
if nextRunTime.After(endTime) {
|
||||
continue
|
||||
}
|
||||
if _, has := rtIdx[rtATimesKey]; !has {
|
||||
rtIdx[rtATimesKey] = make(map[time.Duration][]*engine.Rate)
|
||||
}
|
||||
rtIdx[rtATimesKey][rt.IntervalStart] = append(rtIdx[rtATimesKey][rt.IntervalStart], rt)
|
||||
rtIdx[nextRunTime] = append(rtIdx[nextRunTime], rt)
|
||||
}
|
||||
|
||||
// sort the rates within the duration map
|
||||
for _, durMp := range rtIdx {
|
||||
for _, rts := range durMp {
|
||||
sort.Slice(rts, func(i, j int) bool {
|
||||
return rts[i].Weight > rts[j].Weight
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// sort the IntervalStarts
|
||||
sortedStarts := make(map[time.Time][]time.Duration) // map[ActivationTime]
|
||||
for aTime, durMp := range rtIdx {
|
||||
sortedStarts[aTime] = make([]time.Duration, len(durMp))
|
||||
idxDur := 0
|
||||
for dur := range durMp {
|
||||
sortedStarts[aTime] = append(sortedStarts[aTime], dur)
|
||||
idxDur++
|
||||
}
|
||||
sort.Slice(sortedStarts[aTime], func(i, j int) bool {
|
||||
return sortedStarts[aTime][i] < sortedStarts[aTime][j]
|
||||
})
|
||||
}
|
||||
|
||||
// sort the activation times
|
||||
sortedATimes := make([]time.Time, len(rtIdx))
|
||||
idxATimes := 0
|
||||
@@ -68,7 +49,49 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, isDuration bool
|
||||
sort.Slice(sortedATimes, func(i, j int) bool {
|
||||
return sortedATimes[i].Before(sortedATimes[j])
|
||||
})
|
||||
// start with most recent activationTime lower or equal to sTime
|
||||
for i, aT := range sortedATimes {
|
||||
if !aT.After(sTime) || i == 0 {
|
||||
continue
|
||||
}
|
||||
sortedATimes = sortedATimes[i-1:]
|
||||
}
|
||||
// only for duration we will have multiple activationTimes
|
||||
if !isDuration {
|
||||
sortedATimes = sortedATimes[:1]
|
||||
}
|
||||
var usageSIdx, usageEIdx time.Duration
|
||||
for i, at := range sortedATimes {
|
||||
if sTime.Before(at) {
|
||||
usageSIdx = at.Sub(sTime)
|
||||
}
|
||||
if i != len(sortedATimes)-1 { // not the last one
|
||||
usageEIdx = sortedATimes[i+1].Sub(sTime)
|
||||
} 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
|
||||
})
|
||||
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})
|
||||
}
|
||||
}
|
||||
ordRts = append(ordRts, &engine.RateSInterval{Increments: rtIcmts})
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -18,138 +18,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package rates
|
||||
|
||||
/*
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
func TestOrderRatesOnIntervals(t *testing.T) {
|
||||
allRts := map[time.Duration][]*engine.Rate{
|
||||
time.Duration(0): {
|
||||
&engine.Rate{
|
||||
ID: "RATE0",
|
||||
rt0 := &engine.Rate{
|
||||
ID: "RATE0",
|
||||
Weight: 0,
|
||||
IntervalRates: []*engine.IntervalRate{
|
||||
{
|
||||
IntervalStart: time.Duration(0),
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE50",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
}
|
||||
expOrdered := []*engine.Rate{
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
}
|
||||
if ordRts := orderRatesOnIntervals(allRts); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
allRts = map[time.Duration][]*engine.Rate{
|
||||
time.Duration(1 * time.Minute): {
|
||||
&engine.Rate{
|
||||
ID: "RATE30",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 30,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE70",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 70,
|
||||
},
|
||||
},
|
||||
time.Duration(0): {
|
||||
&engine.Rate{
|
||||
ID: "RATE0",
|
||||
rt0.Compile()
|
||||
rtChristmas := &engine.Rate{
|
||||
ID: "RT_CHRISTMAS",
|
||||
ActivationStart: "* * 24 12 *",
|
||||
Weight: 50,
|
||||
IntervalRates: []*engine.IntervalRate{
|
||||
{
|
||||
IntervalStart: time.Duration(0),
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE50",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 50,
|
||||
},
|
||||
}
|
||||
rtChristmas.Compile()
|
||||
allRts := []*engine.Rate{rt0, rtChristmas}
|
||||
expOrdered := []*engine.RateSInterval{
|
||||
{
|
||||
Increments: []*engine.RateSIncrement{
|
||||
{
|
||||
Rate: rt0,
|
||||
IntervalRateIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expOrdered = []*engine.Rate{
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE70",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 70,
|
||||
},
|
||||
}
|
||||
if ordRts := orderRatesOnIntervals(allRts); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
allRts = map[time.Duration][]*engine.Rate{
|
||||
time.Duration(1 * time.Minute): {
|
||||
&engine.Rate{
|
||||
ID: "RATE30",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 30,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE70",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 70,
|
||||
},
|
||||
},
|
||||
time.Duration(2 * time.Minute): {
|
||||
&engine.Rate{
|
||||
ID: "RATE0",
|
||||
IntervalStart: time.Duration(2 * time.Minute),
|
||||
},
|
||||
},
|
||||
time.Duration(0): {
|
||||
&engine.Rate{
|
||||
ID: "RATE0",
|
||||
IntervalStart: time.Duration(0),
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE50",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
}
|
||||
expOrdered = []*engine.Rate{
|
||||
&engine.Rate{
|
||||
ID: "RATE100",
|
||||
IntervalStart: time.Duration(0),
|
||||
Weight: 100,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE70",
|
||||
IntervalStart: time.Duration(1 * time.Minute),
|
||||
Weight: 70,
|
||||
},
|
||||
&engine.Rate{
|
||||
ID: "RATE0",
|
||||
IntervalStart: time.Duration(2 * time.Minute),
|
||||
},
|
||||
}
|
||||
if ordRts := orderRatesOnIntervals(allRts); !reflect.DeepEqual(expOrdered, ordRts) {
|
||||
//
|
||||
// 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) {
|
||||
t.Errorf("expecting: %s\n, received: %s",
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user