diff --git a/cmd/cgr-balancer/cgr-balanncer.go b/cmd/cgr-balancer/cgr-balanncer.go index 4545111c4..6194fde3b 100644 --- a/cmd/cgr-balancer/cgr-balanncer.go +++ b/cmd/cgr-balancer/cgr-balanncer.go @@ -27,21 +27,25 @@ import ( "log" "runtime" "time" + "sync" ) var ( - raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)") - jsonRpcAddress = flag.String("jsonrpcaddr", "127.0.0.1:2001", "Json RPC server address (localhost:2001)") - httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:2002)") - freeswitchsrv = flag.String("freeswitchsrv", "localhost:8021", "freeswitch address host:port") - freeswitchpass = flag.String("freeswitchpass", "ClueCon", "freeswitch address host:port") - bal *balancer.Balancer + raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)") + jsonRpcAddress = flag.String("jsonrpcaddr", "127.0.0.1:2001", "Json RPC server address (localhost:2001)") + httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:2002)") + freeswitchsrv = flag.String("freeswitchsrv", "localhost:8021", "freeswitch address host:port") + freeswitchpass = flag.String("freeswitchpass", "ClueCon", "freeswitch address host:port") + bal *balancer.Balancer + balancerRWMutex sync.RWMutex ) /* The function that gets the information from the raters using balancer. */ func GetCallCost(key *timespans.CallDescriptor, method string) (reply *timespans.CallCost) { + balancerRWMutex.RLock() + defer balancerRWMutex err := errors.New("") //not nil value for err != nil { client := bal.Balance() diff --git a/cmd/cgr-scheduler/cgr-scheduler.go b/cmd/cgr-scheduler/cgr-scheduler.go index 2a629eae1..9ed18e82a 100644 --- a/cmd/cgr-scheduler/cgr-scheduler.go +++ b/cmd/cgr-scheduler/cgr-scheduler.go @@ -40,25 +40,8 @@ var ( s = scheduler{} ) -/* -Structure to store action timings according to next activation time. -*/ -type actiontimingqueue []*timespans.ActionTiming - -func (atq actiontimingqueue) Len() int { - return len(atq) -} - -func (atq actiontimingqueue) Swap(i, j int) { - atq[i], atq[j] = atq[j], atq[i] -} - -func (atq actiontimingqueue) Less(j, i int) bool { - return atq[j].GetNextStartTime().Before(atq[i].GetNextStartTime()) -} - type scheduler struct { - queue actiontimingqueue + queue timespans.ActionTimingPriotityList } func (s scheduler) loop() { @@ -110,7 +93,7 @@ func loadActionTimings() { log.Fatalf("Cannot get action timings:", err) } // recreate the queue - s.queue = actiontimingqueue{} + s.queue = timespans.ActionTimingPriotityList{} for _, at := range actionTimings { if at.IsOneTimeRun() { log.Print("Executing: ", at) diff --git a/data/ActionTimings.csv b/data/ActionTimings.csv index 4b27b5e52..bce85bdf1 100644 --- a/data/ActionTimings.csv +++ b/data/ActionTimings.csv @@ -1,7 +1,7 @@ -Tag,ActionsTag,TimingTag -STANDARD_ABO,SOME,WEEKLY_SAME_TIME -STANDARD_ABO,SOME,WEEKLY_SAME_TIME -STANDARD_ABO,SOME,WEEKLY_SAME_TIME -STANDARD_ABO,SOME,ONE_TIME_RUN -STANDARD_ABO,SOME,FIRST_DAY_OF_MONTH -MORE_MINUTES,MINI,ONE_TIME_RUN \ No newline at end of file +Tag,ActionsTag,TimingTag, Weight +STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10 +STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10 +STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10 +STANDARD_ABO,SOME,ONE_TIME_RUN,10 +STANDARD_ABO,SOME,FIRST_DAY_OF_MONTH,10 +MORE_MINUTES,MINI,ONE_TIME_RUN,10 \ No newline at end of file diff --git a/data/ActionTriggers.csv b/data/ActionTriggers.csv index 2550d4d19..2077633c7 100644 --- a/data/ActionTriggers.csv +++ b/data/ActionTriggers.csv @@ -1,5 +1,5 @@ -Tag,BalanceTag,ThresholdValue,DestinationTag,ActionsTag -STANDARD_TRIGGER,MONETARY,30,*all,SOME_1 -STANDARD_TRIGGER,SMS,30,*all,SOME_2 -STANDARD_TRIGGER,MINUTES,10,GERMANY_O2,SOME_1 -STANDARD_TRIGGER,MINUTES,200,GERMANY,SOME_2 \ No newline at end of file +Tag,BalanceTag,ThresholdValue,DestinationTag,ActionsTag,Weight +STANDARD_TRIGGER,MONETARY,30,*all,SOME_1,10 +STANDARD_TRIGGER,SMS,30,*all,SOME_2,10 +STANDARD_TRIGGER,MINUTES,10,GERMANY_O2,SOME_1,10 +STANDARD_TRIGGER,MINUTES,200,GERMANY,SOME_2,10 \ No newline at end of file diff --git a/data/Actions.csv b/data/Actions.csv index 1e9a4f0a7..419078983 100644 --- a/data/Actions.csv +++ b/data/Actions.csv @@ -1,9 +1,9 @@ -Tag,Action,BalanceTag,Units,DestinationTag,PriceType,PriceValue,Weight -SOME,TOPUP_RESET,MONETARY,10,*all,,, -SOME,TOPUP_RESET,SMS,100,*all,,, -SOME,TOPUP_RESET,INTERNET,1000,*all,,, -SOME,POSTPAID_RESET,MONETARY,10,*all,,, -SOME,DEBIT,MONETARY,5,*all,,, -SOME_1,DEBIT,MINUTES,10,GERMANY_O2,PERCENT,25,10 -SOME_2,TOPUP_RESET,MINUTES,1000,GERMANY,ABSOLUTE,0.2,10 -MINI,TOPUP,MINUTES,100,NAT,ABSOLUTE,0,10 \ No newline at end of file +Tag,Action,BalanceTag,Units,DestinationTag,PriceType,PriceValue,MinutesWeight,Weight +SOME,TOPUP_RESET,MONETARY,10,*all,,,,10 +SOME,TOPUP_RESET,SMS,100,*all,,,,10 +SOME,TOPUP_RESET,INTERNET,1000,*all,,,,10 +SOME,POSTPAID_RESET,MONETARY,10,*all,,,,10 +SOME,DEBIT,MONETARY,5,*all,,,,10 +SOME_1,DEBIT,MINUTES,10,GERMANY_O2,PERCENT,25,10,10 +SOME_2,TOPUP_RESET,MINUTES,1000,GERMANY,ABSOLUTE,0.2,10,10 +MINI,TOPUP,MINUTES,100,NAT,ABSOLUTE,0,10,10 \ No newline at end of file diff --git a/timespans/action.go b/timespans/action.go new file mode 100644 index 000000000..f5bea5c96 --- /dev/null +++ b/timespans/action.go @@ -0,0 +1,127 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012 Radu Ioan Fericean + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package timespans + +import ( + "log" + "sort" +) + +/* +Structure to be filled for each tariff plan with the bonus value for received calls minutes. +*/ +type Action struct { + ActionType string + BalanceId string + Units float64 + Weight float64 + MinuteBucket *MinuteBucket +} + +type actionTypeFunc func(*UserBalance, *Action) error + +var ( + actionTypeFuncMap = map[string]actionTypeFunc{ + "LOG": logAction, + "RESET_TRIGGERS": resetTriggersAction, + "SET_POSTPAID": setPostpaidAction, + "RESET_POSTPAID": resetPostpaidAction, + "SET_PREPAID": setPrepaidAction, + "RESET_PREPAID": resetPrepaidAction, + "TOPUP_RESET": topupResetAction, + "TOPUP": topupAction, + "DEBIT": debitAction, + "RESET_COUNTERS": resetCountersAction, + } +) + +func logAction(ub *UserBalance, a *Action) (err error) { + log.Printf("%v %v %v", a.BalanceId, a.Units, a.MinuteBucket) + return +} + +func resetTriggersAction(ub *UserBalance, a *Action) (err error) { + ub.resetActionTriggers() + return +} + +func setPostpaidAction(ub *UserBalance, a *Action) (err error) { + ub.Type = UB_TYPE_POSTPAID + return +} + +func resetPostpaidAction(ub *UserBalance, a *Action) (err error) { + ub.Type = UB_TYPE_POSTPAID + return +} + +func setPrepaidAction(ub *UserBalance, a *Action) (err error) { + ub.Type = UB_TYPE_PREPAID + return +} + +func resetPrepaidAction(ub *UserBalance, a *Action) (err error) { + ub.Type = UB_TYPE_PREPAID + return +} + +func topupResetAction(ub *UserBalance, a *Action) (err error) { + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]float64) + } + ub.BalanceMap[a.BalanceId] = a.Units + return +} + +func topupAction(ub *UserBalance, a *Action) (err error) { + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]float64) + } + ub.BalanceMap[a.BalanceId] += a.Units + ub.addMinuteBucket(a.MinuteBucket) + return +} + +func debitAction(ub *UserBalance, a *Action) (err error) { + return +} + +func resetCountersAction(ub *UserBalance, a *Action) (err error) { + //ub.UnitsCounters + return +} + +// Structure to store actions according to weight +type ActionPriotityList []*Action + +func (apl ActionPriotityList) Len() int { + return len(apl) +} + +func (apl ActionPriotityList) Swap(i, j int) { + apl[i], apl[j] = apl[j], apl[i] +} + +func (apl ActionPriotityList) Less(i, j int) bool { + return apl[i].Weight < apl[j].Weight +} + +func (apl ActionPriotityList) Sort() { + sort.Sort(apl) +} diff --git a/timespans/actions.go b/timespans/action_timing.go similarity index 59% rename from timespans/actions.go rename to timespans/action_timing.go index 54bf4d01d..6110924d8 100644 --- a/timespans/actions.go +++ b/timespans/action_timing.go @@ -29,183 +29,13 @@ const ( FORMAT = "2006-1-2 15:04:05 MST" ) -// Amount of a trafic of a certain type -type UnitsCounter struct { - Direction string - BalanceId string - Units float64 - Weight float64 - MinuteBuckets []*MinuteBucket -} - -// Structure to store actions according to weight -type countersorter []*UnitsCounter - -func (s countersorter) Len() int { - return len(s) -} - -func (s countersorter) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s countersorter) Less(i, j int) bool { - return s[i].Weight < s[j].Weight -} - -/* -Structure to be filled for each tariff plan with the bonus value for received calls minutes. -*/ -type Action struct { - ActionType string - BalanceId string - Units float64 - MinuteBucket *MinuteBucket -} - -type actionTypeFunc func(*UserBalance, *Action) error - -var ( - actionTypeFuncMap = map[string]actionTypeFunc{ - "LOG": logAction, - "RESET_TRIGGERS": resetTriggersAction, - "SET_POSTPAID": setPostpaidAction, - "RESET_POSTPAID": resetPostpaidAction, - "SET_PREPAID": setPrepaidAction, - "RESET_PREPAID": resetPrepaidAction, - "TOPUP_RESET": topupResetAction, - "TOPUP": topupAction, - "DEBIT": debitAction, - "RESET_COUNTERS": resetCountersAction, - } -) - -func logAction(ub *UserBalance, a *Action) (err error) { - log.Printf("%v %v %v", a.BalanceId, a.Units, a.MinuteBucket) - return -} - -func resetTriggersAction(ub *UserBalance, a *Action) (err error) { - ub.resetActionTriggers() - return -} - -func setPostpaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_POSTPAID - return -} - -func resetPostpaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_POSTPAID - return -} - -func setPrepaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_PREPAID - return -} - -func resetPrepaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_PREPAID - return -} - -func topupResetAction(ub *UserBalance, a *Action) (err error) { - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]float64) - } - ub.BalanceMap[a.BalanceId] = a.Units - return storageGetter.SetUserBalance(ub) -} - -func topupAction(ub *UserBalance, a *Action) (err error) { - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]float64) - } - ub.BalanceMap[a.BalanceId] += a.Units - ub.addMinuteBucket(a.MinuteBucket) - return storageGetter.SetUserBalance(ub) -} - -func debitAction(ub *UserBalance, a *Action) (err error) { - return -} - -func resetCountersAction(ub *UserBalance, a *Action) (err error) { - //ub.UnitsCounters - return -} - -type ActionTrigger struct { - BalanceId string - ThresholdValue float64 - DestinationId string - Priority float64 - ActionsId string - executed bool -} - -func (at *ActionTrigger) Execute(ub *UserBalance) (err error) { - aac, err := storageGetter.GetActions(at.ActionsId) - if err != nil { - log.Print("Failed to get actions: ", err) - return - } - for _, a := range aac { - actionFunction, exists := actionTypeFuncMap[a.ActionType] - if !exists { - log.Printf("Function type %v not available, aborting execution!", a.ActionType) - return - } - err = actionFunction(ub, a) - } - at.executed = true - return -} - -// Structure to store actions according to weight -type ActionTriggerPriotityList []*ActionTrigger - -func (atpl ActionTriggerPriotityList) Len() int { - return len(atpl) -} - -func (atpl ActionTriggerPriotityList) Swap(i, j int) { - atpl[i], atpl[j] = atpl[j], atpl[i] -} - -func (atpl ActionTriggerPriotityList) Less(i, j int) bool { - return atpl[i].Priority < atpl[j].Priority -} - -func (atpl ActionTriggerPriotityList) Sort() { - sort.Sort(atpl) -} - type ActionTiming struct { Tag string // informative purpos only UserBalanceIds []string Timing *Interval + Weight float64 ActionsId string - actions []*Action -} - -func (at *ActionTiming) getActions() (as []*Action, err error) { - if at.actions == nil { - at.actions, err = storageGetter.GetActions(at.ActionsId) - } - return at.actions, err -} - -func (at *ActionTiming) getUserBalances() (ubs []*UserBalance) { - for _, ubId := range at.UserBalanceIds { - ub, err := storageGetter.GetUserBalance(ubId) - if err != nil { - log.Printf("Could not get user balances for therse id: %v. Skipping!", ubId) - } - ubs = append(ubs, ub) - } - return + actions ActionPriotityList } func (at *ActionTiming) GetNextStartTime() (t time.Time) { @@ -298,7 +128,28 @@ MONTHS: return } +func (at *ActionTiming) getActions() (as []*Action, err error) { + if at.actions == nil { + at.actions, err = storageGetter.GetActions(at.ActionsId) + } + at.actions.Sort() + return at.actions, err +} + +func (at *ActionTiming) getUserBalances() (ubs []*UserBalance) { + for _, ubId := range at.UserBalanceIds { + ub, err := storageGetter.GetUserBalance(ubId) + if err != nil { + log.Printf("Could not get user balances for therse id: %v. Skipping!", ubId) + } + ubs = append(ubs, ub) + } + return +} + func (at *ActionTiming) Execute() (err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() aac, err := at.getActions() if err != nil { log.Print("Failed to get actions: ", err) @@ -312,6 +163,7 @@ func (at *ActionTiming) Execute() (err error) { } for _, ub := range at.getUserBalances() { err = actionFunction(ub, a) + storageGetter.SetUserBalance(ub) } } return @@ -324,3 +176,22 @@ func (at *ActionTiming) IsOneTimeRun() bool { } return len(i.Months) == 0 && len(i.MonthDays) == 0 && len(i.WeekDays) == 0 } + +// Structure to store actions according to weight +type ActionTimingPriotityList []*ActionTiming + +func (atpl ActionTimingPriotityList) Len() int { + return len(atpl) +} + +func (atpl ActionTimingPriotityList) Swap(i, j int) { + atpl[i], atpl[j] = atpl[j], atpl[i] +} + +func (atpl ActionTimingPriotityList) Less(i, j int) bool { + return atpl[i].GetNextStartTime().Before(atpl[j].GetNextStartTime()) || atpl[i].Weight < atpl[j].Weight +} + +func (atpl ActionTimingPriotityList) Sort() { + sort.Sort(atpl) +} diff --git a/timespans/action_trigger.go b/timespans/action_trigger.go new file mode 100644 index 000000000..ee7bcff00 --- /dev/null +++ b/timespans/action_trigger.go @@ -0,0 +1,75 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012 Radu Ioan Fericean + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package timespans + +import ( + "log" + "sort" +) + +type ActionTrigger struct { + BalanceId string + ThresholdValue float64 + DestinationId string + Weight float64 + ActionsId string + executed bool +} + +func (at *ActionTrigger) Execute(ub *UserBalance) (err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() + var aac ActionPriotityList + aac, err = storageGetter.GetActions(at.ActionsId) + aac.Sort() + if err != nil { + log.Print("Failed to get actions: ", err) + return + } + for _, a := range aac { + actionFunction, exists := actionTypeFuncMap[a.ActionType] + if !exists { + log.Printf("Function type %v not available, aborting execution!", a.ActionType) + return + } + err = actionFunction(ub, a) + } + at.executed = true + storageGetter.SetUserBalance(ub) + return +} + +// Structure to store actions according to weight +type ActionTriggerPriotityList []*ActionTrigger + +func (atpl ActionTriggerPriotityList) Len() int { + return len(atpl) +} + +func (atpl ActionTriggerPriotityList) Swap(i, j int) { + atpl[i], atpl[j] = atpl[j], atpl[i] +} + +func (atpl ActionTriggerPriotityList) Less(i, j int) bool { + return atpl[i].Weight < atpl[j].Weight +} + +func (atpl ActionTriggerPriotityList) Sort() { + sort.Sort(atpl) +} diff --git a/timespans/actions_test.go b/timespans/actions_test.go index b527d412b..e2a5e3674 100644 --- a/timespans/actions_test.go +++ b/timespans/actions_test.go @@ -232,9 +232,9 @@ func TestActionTimingLogFunction(t *testing.T) { } func TestActionTriggerPriotityList(t *testing.T) { - at1 := &ActionTrigger{Priority: 10} - at2 := &ActionTrigger{Priority: 20} - at3 := &ActionTrigger{Priority: 30} + at1 := &ActionTrigger{Weight: 10} + at2 := &ActionTrigger{Weight: 20} + at3 := &ActionTrigger{Weight: 30} var atpl ActionTriggerPriotityList atpl = append(atpl, at2, at1, at3) atpl.Sort() diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 9ec90e66f..d83c203e5 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -175,6 +175,8 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { Splits the received timespan into sub time spans according to the activation periods intervals. */ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeSpan) { + userBalancesRWMutex.RLock() + defer userBalancesRWMutex.RUnlock() timespans = append(timespans, firstSpan) // split on (free) minute buckets if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { @@ -289,6 +291,8 @@ and will decrease it by 10% for nine times. So if the user has little credit it If the user has no credit then it will return 0. */ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) { + userBalancesRWMutex.RLock() + defer userBalancesRWMutex.RUnlock() _, err = cd.SearchStorageForPrefix() now := time.Now() availableCredit, availableSeconds := 0.0, 0.0 @@ -331,6 +335,8 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) { // Interface method used to add/substract an amount of cents or bonus seconds (as returned by GetCost method) // from user's money balance. func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() cc, err = cd.GetCost() if err != nil { log.Printf("error getting cost %v", err) @@ -353,6 +359,8 @@ Interface method used to add/substract an amount of cents from user's money bala The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitCents() (left float64, err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { return userBalance.debitMoneyBalance(cd.Amount), nil } @@ -364,6 +372,8 @@ Interface method used to add/substract an amount of units from user's sms balanc The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitSMS() (left float64, err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { return userBalance.debitSMSBuget(cd.Amount) } @@ -375,6 +385,8 @@ Interface method used to add/substract an amount of seconds from user's minutes The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitSeconds() (err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { return userBalance.debitMinutesBalance(cd.Amount, cd.Destination) } @@ -388,6 +400,8 @@ specified in the tariff plan is applied. The amount filed has to be filled in call descriptor. */ // func (cd *CallDescriptor) AddRecievedCallSeconds() (err error) { +// userBalancesRWMutex.Lock() +// defer userBalancesRWMutex.Unlock() // if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { // return userBalance.addReceivedCallSeconds(INBOUND, cd.TOR, cd.Destination, cd.Amount) // } @@ -398,6 +412,8 @@ The amount filed has to be filled in call descriptor. Resets user balances value to the amounts specified in the tariff plan. */ /*func (cd *CallDescriptor) ResetUserBalance() (err error) { + userBalancesRWMutex.Lock() + defer userBalancesRWMutex.Unlock() if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { return userBalance.resetUserBalance() } diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go index dae54a5a1..224cb60b8 100644 --- a/timespans/calldesc_test.go +++ b/timespans/calldesc_test.go @@ -199,23 +199,6 @@ func TestMaxSessionTimeNoCredit(t *testing.T) { } } -/*func TestGetCostWithVolumeDiscount(t *testing.T) { - storageGetter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10) - defer storageGetter.Close() - vd1 := &VolumeDiscount{100, 10} - vd2 := &VolumeDiscount{500, 20} - seara := &TariffPlan{Id: "seara", SmsCredit: 100, VolumeDiscountThresholds: []*VolumeDiscount{vd1, vd2}} - rifsBalance := &UserBalance{Id: "rif", Credit: 21, tariffPlan: seara, ResetDayOfTheMonth: 10, VolumeDiscountSeconds: 105} - t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) - t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2, userBalance: rifsBalance} - callCost, err := cd.GetCost() - if callCost.Cost != 54.0 || err != nil { - t.Errorf("Expected %v was %v", 54.0, callCost) - } -} -*/ - func TestApAddAPIfNotPresent(t *testing.T) { ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)} diff --git a/timespans/userbalance.go b/timespans/userbalance.go index c58788697..6ccbf57ad 100644 --- a/timespans/userbalance.go +++ b/timespans/userbalance.go @@ -19,7 +19,8 @@ along with this program. If not, see package timespans import ( -// "log" + // "log" + "sync" ) const ( @@ -40,7 +41,8 @@ const ( ) var ( - storageGetter StorageGetter + storageGetter StorageGetter + userBalancesRWMutex sync.RWMutex ) /* @@ -263,3 +265,26 @@ Resets the user balance items to their tariff plan values. return } */ +// Amount of a trafic of a certain type +type UnitsCounter struct { + Direction string + BalanceId string + Units float64 + Weight float64 + MinuteBuckets []*MinuteBucket +} + +// Structure to store actions according to weight +type countersorter []*UnitsCounter + +func (s countersorter) Len() int { + return len(s) +} + +func (s countersorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s countersorter) Less(i, j int) bool { + return s[i].Weight < s[j].Weight +}