From 03bbe421b4f3465a927f17aeda6eefefc2a79190 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 2 Feb 2012 20:31:23 +0200 Subject: [PATCH] started calldescription testing --- cmd/loader/kyoto/kyoto_loader.go | 36 +++++++-- cmd/rater/rater.go | 15 ++-- cmd/stresstest/stresstest.go | 2 +- cmd/stresstest/stresstest.py | 2 +- timespans/calldesc.go | 113 +++++++++++++++++++++++++++ timespans/calldesc_test.go | 5 ++ timespans/interval_test.go | 2 +- timespans/intervals.go | 4 +- timespans/kyoto_storage.go | 2 +- timespans/redis_storage.go | 2 +- timespans/storage_interface.go | 2 +- timespans/timeslots.go | 126 ------------------------------- timespans/timeslots_test.go | 44 ----------- timespans/timespans.go | 45 +++++++++++ timespans/timespans_test.go | 15 ++++ 15 files changed, 223 insertions(+), 192 deletions(-) create mode 100644 timespans/calldesc.go create mode 100644 timespans/calldesc_test.go delete mode 100644 timespans/timeslots.go delete mode 100644 timespans/timeslots_test.go create mode 100644 timespans/timespans.go create mode 100644 timespans/timespans_test.go diff --git a/cmd/loader/kyoto/kyoto_loader.go b/cmd/loader/kyoto/kyoto_loader.go index d0337192d..117d820e2 100644 --- a/cmd/loader/kyoto/kyoto_loader.go +++ b/cmd/loader/kyoto/kyoto_loader.go @@ -4,18 +4,42 @@ import ( "fmt" "github.com/fsouza/gokabinet/kc" "flag" + "time" + "github.com/rif/cgrates/timespans" ) var ( - fileName = flag.String("fileName", "storage.kch", "kyoto storage file") + filename = flag.String("filename", "storage.kch", "kyoto storage file") ) func main() { flag.Parse() - db, _ := kc.Open(*fileName, kc.WRITE) - defer db.Close() - - db.Set("test", "12223") + db, _ := kc.Open(*filename, kc.WRITE) + defer db.Close() + + t1 := time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC) + cd1 := ×pans.CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256"} + cd1.AddInterval(t1, ×pans.Interval{ + WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + EndHour:"18:00", + ConnectFee: 0, + Price: 0.2, + BillingUnit: 1.0}) + cd1.AddInterval(t1, ×pans.Interval{ + WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartHour:"18:00", + ConnectFee: 0, + Price: 0.1, + BillingUnit: 1.0}) + cd1.AddInterval(t1, ×pans.Interval{ + WeekDays: []time.Weekday{time.Saturday, time.Sunday}, + ConnectFee: 0, + Price: 0.1, + BillingUnit: 1.0}) + key := cd1.GetKey() + + value := cd1.EncodeValues() + + db.Set(key, string(value)) fmt.Println("Done!") } - diff --git a/cmd/rater/rater.go b/cmd/rater/rater.go index 5aefe6efe..7264b1198 100644 --- a/cmd/rater/rater.go +++ b/cmd/rater/rater.go @@ -6,7 +6,7 @@ import ( "net" "net/rpc" "os" - "github.com/rif/cgrates/timeslots" + "github.com/rif/cgrates/timespans" ) var ( @@ -16,20 +16,19 @@ var ( ) type Storage struct { - sg timeslots.StorageGetter + sg timespans.StorageGetter } -func NewStorage(nsg timeslots.StorageGetter) *Storage{ +func NewStorage(nsg timespans.StorageGetter) *Storage{ return &Storage{sg: nsg} } /* RPC method providing the rating information from the storage. */ -func (s *Storage) GetCost(in *timeslots.CallDescription, reply *timeslots.CallCost) (err error) { - r, e := timeslots.GetCost(in, s.sg) - *reply, err = *r, e - return e +func (s *Storage) GetCost(in *timespans.CallDescription, reply *timespans.CallCost) (err error) { + *reply, err = in.GetCost(s.sg) + return nil } /* @@ -44,7 +43,7 @@ func (s *Storage) Shutdown(args string, reply *string) (err error) { func main() { flag.Parse() - getter, err := timeslots.NewKyotoStorage("storage.kch") + getter, err := timespans.NewKyotoStorage("storage.kch") //getter, err := NewRedisStorage("tcp:127.0.0.1:6379") //defer getter.Close() if err != nil { diff --git a/cmd/stresstest/stresstest.go b/cmd/stresstest/stresstest.go index 3a494d2f5..0464229ed 100644 --- a/cmd/stresstest/stresstest.go +++ b/cmd/stresstest/stresstest.go @@ -9,7 +9,7 @@ import ( func main(){ client, _ := jsonrpc.Dial("tcp", "localhost:5090") - runs := int(5 * 10e3); + runs := int(5 * 1e4); i:= 0 c := make(chan string) for ; i < runs; i++ { diff --git a/cmd/stresstest/stresstest.py b/cmd/stresstest/stresstest.py index f263e0f7b..5497f993d 100644 --- a/cmd/stresstest/stresstest.py +++ b/cmd/stresstest/stresstest.py @@ -49,6 +49,6 @@ print s.recv(4096) i = 0 result = "" -for i in xrange(5 * int(10e4) + 1): +for i in xrange(5 * int(1e4) + 1): result = rpc.call("Responder.Get", "test") print i, result diff --git a/timespans/calldesc.go b/timespans/calldesc.go new file mode 100644 index 000000000..18f4d0cde --- /dev/null +++ b/timespans/calldesc.go @@ -0,0 +1,113 @@ +package timespans + +import ("time"; "encoding/json"; "fmt"; "log") + +/* +The output structure that will be returned with the call cost information. +*/ +type CallCost struct { + TOR int + CstmId, Subject, DestinationPrefix string + Cost, ConnectFee float64 +// ratesInfo *RatingProfile +} + +/* +The input stucture that contains call information. +*/ +type CallDescriptor struct { + TOR int + CstmId, Subject, DestinationPrefix string + TimeStart, TimeEnd time.Time + ActivationPeriods map[time.Time] []*Interval +} + +func (cd *CallDescriptor) AddInterval(t time.Time, is ...*Interval) { + if cd.ActivationPeriods == nil { + cd.ActivationPeriods = make(map[time.Time] []*Interval) + } + intervals, ok := cd.ActivationPeriods[t] + for _, i := range is { + intervals = append(intervals, i) + } + // if the intervals is new add it to the map + if !ok { + cd.ActivationPeriods[t] = intervals + } +} + +func (cd *CallDescriptor) EncodeValues() []byte { + jo, err := json.Marshal(cd.ActivationPeriods) + if err != nil { + log.Print("Cannot encode intervals: ", err) + } + return jo +} + +func (cd *CallDescriptor) GetKey() string { + return fmt.Sprintf("%s:%s:%s", cd.CstmId, cd.Subject, cd.DestinationPrefix) +} + +func (cd *CallDescriptor) decodeValues(v []byte) { + err := json.Unmarshal(v, &cd.ActivationPeriods) + if err != nil { + log.Print("Cannot decode intervals: ", err) + } +} + +func (cd *CallDescriptor) getActiveIntervals() (is []*Interval) { + now := time.Now() + // add a second in the future to be able to pick the active timestamp + // from the very second it becomes active + sec,_ := time.ParseDuration("1s") + now.Add(sec) + bestTime := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) + for time, intervals := range cd.ActivationPeriods { + if time.After(bestTime) && time.Before(now) { + bestTime = time + is = intervals + } + } + return +} + +func (cd *CallDescriptor) splitInTimeSpans(intervals []*Interval) (timespans []*TimeSpan) { + ts1 := &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd} + timespans = append(timespans, ts1) + + for _, interval := range intervals { + for _, ts := range timespans { + newTs := interval.Split(ts) + if newTs != nil { + timespans = append(timespans, interval.Split(ts)) + } + } + } + return +} + +/* +*/ +func (cd *CallDescriptor) GetCost(sg StorageGetter) (result *CallCost, err error) { + + key := cd.GetKey() + values, err := sg.Get(key) + + cd.decodeValues([]byte(values)) + + intervals := cd.getActiveIntervals() + timespans := cd.splitInTimeSpans(intervals) + + cost := 0.0 + for _, ts := range timespans { + cost += ts.GetCost() + } + cc := &CallCost{TOR: cd.TOR, + CstmId: cd.CstmId, + Subject: cd.Subject, + DestinationPrefix: cd.DestinationPrefix, + Cost: cost, + ConnectFee: timespans[0].Interval.ConnectFee} + return cc, err +} + diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go new file mode 100644 index 000000000..f95621a0c --- /dev/null +++ b/timespans/calldesc_test.go @@ -0,0 +1,5 @@ +package timespans + +import ( + "testing" +) \ No newline at end of file diff --git a/timespans/interval_test.go b/timespans/interval_test.go index fc4be900c..24063aca8 100644 --- a/timespans/interval_test.go +++ b/timespans/interval_test.go @@ -1,4 +1,4 @@ -package timeslots +package timespans import ( "time" diff --git a/timespans/intervals.go b/timespans/intervals.go index 0799231e9..757e20af8 100644 --- a/timespans/intervals.go +++ b/timespans/intervals.go @@ -1,4 +1,4 @@ -package timeslots +package timespans import ( "time" @@ -14,7 +14,7 @@ type Interval struct { MonthDay int WeekDays []time.Weekday StartHour, EndHour string // ##:## format - Ponder, ConnectFee, Price, BillingUnit float32 + Ponder, ConnectFee, Price, BillingUnit float64 } /* diff --git a/timespans/kyoto_storage.go b/timespans/kyoto_storage.go index 7d9b77bdb..a9369ccd3 100644 --- a/timespans/kyoto_storage.go +++ b/timespans/kyoto_storage.go @@ -1,4 +1,4 @@ -package timeslots +package timespans import ( "log" diff --git a/timespans/redis_storage.go b/timespans/redis_storage.go index aa04906ab..ab72be7af 100644 --- a/timespans/redis_storage.go +++ b/timespans/redis_storage.go @@ -1,4 +1,4 @@ -package timeslots +package timespans import ( "log" diff --git a/timespans/storage_interface.go b/timespans/storage_interface.go index 7d870327a..08fcdd4a8 100644 --- a/timespans/storage_interface.go +++ b/timespans/storage_interface.go @@ -1,4 +1,4 @@ -package timeslots +package timespans /* Interface for storage providers. diff --git a/timespans/timeslots.go b/timespans/timeslots.go deleted file mode 100644 index 58a30c97e..000000000 --- a/timespans/timeslots.go +++ /dev/null @@ -1,126 +0,0 @@ -package timeslots - -import ( - "time" - "fmt" - "log" - "encoding/json" -) - -/* -A unit in which a call will be split that has a specific price related interval attached to it. -*/ -type TimeSpan struct { - TimeStart, TimeEnd time.Time - Interval *Interval -} - -/* -Returns the duration of the timespan -*/ -func (ts *TimeSpan) GetDuration() time.Duration { - return ts.TimeEnd.Sub(ts.TimeStart) -} - -/* -Returns the cost of the timespan according to the relevant cost interval. -If the first parameter is true then it adds the connection fee to the cost. -*/ -func (ts *TimeSpan) GetCost(first bool) return (cost float32) { - if ts.Interval.BillingUnit > 0 { - cost = (ts.GetDuration().seconds() / ts.Interval.BillingUnit) * ts.Interval.Price - } else { - cost = ts.GetDuration().seconds() * ts.Interval.Price - } - if first { - cost += ts.Interval.ConnectFee - } - return -} - -/* -will set ne interval as spans's interval if new ponder is greater then span's interval ponder -or if the ponders are equal and new price is lower then spans's interval price -*/ -func (ts *TimeSpan) SetInterval(i *Interval) { - if ts.Interval == nil || ts.Interval.Ponder < i.Ponder { - ts.Interval = i - } - if ts.Interval.Ponder == i.Ponder && i.Price < ts.Interval.Price { - ts.Interval = i - } -} - -/* -A structure containing the time intervals with the cost information and the -ActivationTime when those intervals will be applied. -*/ -type ActivationPeriod struct { - ActivationTime time.Time - Intervals []*Interval -} - -func (c *ActivationPeriod) AddInterval(is ...*Interval) { - for _, i := range is { - c.Intervals = append(c.Intervals, i) - } -} - -/* -The input stucture that contains call information. -*/ -type CallDescription struct { - TOR int - CstmId, Subject, Destination string - TimeStart, TimeEnd time.Time - ActivationPeriods []*ActivationPeriod -} - -/* -Adds an activation period to the internal slice -*/ -func (c *Customer) addActivationPeriod(ap ...*ActivationPeriod) { - for _,a := range ap { - c.ActivationPeriods = append(c.ActivationPeriods, a) - } -} - -func (c *Customer) getKey() string { - return fmt.Sprintf("%s%s%s", c.CstmId, c.Subject, c.DestinationPrefix) -} - -func (c *Customer) encodeValue() []byte { - jo, err := json.Marshal(c.ActivationPeriods) - if err != nil { - log.Print("Cannot encode intervals: ", err) - } - return jo -} - -func (c *Customer) decodeValue(v []byte) { - err := json.Unmarshal(v, &c.ActivationPeriods) - if err != nil { - log.Print("Cannot decode intervals: ", err) - } -} - -/* -*/ -func (cd *CallDescription) GetCost() (result *CallCost) { - ts := &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd} - c := &Customer{CstmId:, Subject:, DestinationPrefix: } -} - - - -/* -The output structure that will be returned with the call cost information. -*/ -type CallCost struct { - TOR int - CstmId, Subject, Prefix string - Cost, ConnectFee float32 -// ratesInfo *RatingProfile -} - - diff --git a/timespans/timeslots_test.go b/timespans/timeslots_test.go deleted file mode 100644 index 8ca4729ae..000000000 --- a/timespans/timeslots_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package timeslots - -import ( - "time" - "testing" -) - -func TestStorageEncoding(t *testing.T){ - i1 := &Interval{Month: time.December, MonthDay: 1, StartHour: "09:00"} - i2 := &Interval{WeekDays: []time.Weekday{time.Sunday}} - i3 := &Interval{Month: time.February, MonthDay: 1, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, StartHour: "14:30", EndHour: "15:00"} - c := &Customer{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256"} - ap := &ActivationPeriod{ActivationTime: time.Now()} - ap.AddInterval(i1, i2, i3) - c.addActivationPeriod(ap) - received := c.encodeValue() - c.decodeValue(received) - f1 := c.ActivationPeriods[0].Intervals[0] - if f1.Month != i1.Month || f1.MonthDay != i1.MonthDay || f1.StartHour != i1.StartHour { - t.Errorf("Decode values are not the same: %v vs %v", f1, i1) - } - f2 := c.ActivationPeriods[0].Intervals[1] - for i,v := range f2.WeekDays { - if v != i2.WeekDays[i] { - t.Errorf("Decode values are not the same: %v vs %v", f2, i2) - } - } -} - -func BenchmarkDecoding(b *testing.B) { - b.StopTimer() - i1 := &Interval{Month: time.December, MonthDay: 1, StartHour: "09:00"} - i2 := &Interval{WeekDays: []time.Weekday{time.Sunday}} - i3 := &Interval{Month: time.February, MonthDay: 1, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, StartHour: "14:30", EndHour: "15:00"} - c := &Customer{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256"} - ap := &ActivationPeriod{ActivationTime: time.Now()} - ap.AddInterval(i1, i2, i3) - c.addActivationPeriod(ap) - received := c.encodeValue() - b.StartTimer() - for i := 0; i < b.N; i++ { - c.decodeValue(received) - } -} \ No newline at end of file diff --git a/timespans/timespans.go b/timespans/timespans.go new file mode 100644 index 000000000..f6f006e8f --- /dev/null +++ b/timespans/timespans.go @@ -0,0 +1,45 @@ +package timespans + +import ( + "time" +) + +/* +A unit in which a call will be split that has a specific price related interval attached to it. +*/ +type TimeSpan struct { + TimeStart, TimeEnd time.Time + Interval *Interval +} + +/* +Returns the duration of the timespan +*/ +func (ts *TimeSpan) GetDuration() time.Duration { + return ts.TimeEnd.Sub(ts.TimeStart) +} + +/* +Returns the cost of the timespan according to the relevant cost interval. +*/ +func (ts *TimeSpan) GetCost() (cost float64) { + if ts.Interval.BillingUnit > 0 { + cost = (ts.GetDuration().Seconds() / ts.Interval.BillingUnit) * ts.Interval.Price + } else { + cost = ts.GetDuration().Seconds() * ts.Interval.Price + } + return +} + +/* +will set ne interval as spans's interval if new ponder is greater then span's interval ponder +or if the ponders are equal and new price is lower then spans's interval price +*/ +func (ts *TimeSpan) SetInterval(i *Interval) { + if ts.Interval == nil || ts.Interval.Ponder < i.Ponder { + ts.Interval = i + } + if ts.Interval.Ponder == i.Ponder && i.Price < ts.Interval.Price { + ts.Interval = i + } +} diff --git a/timespans/timespans_test.go b/timespans/timespans_test.go new file mode 100644 index 000000000..3f6758d9f --- /dev/null +++ b/timespans/timespans_test.go @@ -0,0 +1,15 @@ +package timespans + +import ( + //"time" + "testing" +) + +func TestStorageEncoding(t *testing.T){ + +} + +func BenchmarkDecoding(b *testing.B) { + for i := 0; i < b.N; i++ { + } +} \ No newline at end of file