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
+}