mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
started calldescription testing
This commit is contained in:
@@ -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!")
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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++ {
|
||||
|
||||
@@ -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
|
||||
|
||||
113
timespans/calldesc.go
Normal file
113
timespans/calldesc.go
Normal file
@@ -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
|
||||
}
|
||||
|
||||
5
timespans/calldesc_test.go
Normal file
5
timespans/calldesc_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package timespans
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package timeslots
|
||||
package timespans
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package timeslots
|
||||
package timespans
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package timeslots
|
||||
package timespans
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package timeslots
|
||||
package timespans
|
||||
|
||||
/*
|
||||
Interface for storage providers.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
45
timespans/timespans.go
Normal file
45
timespans/timespans.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
15
timespans/timespans_test.go
Normal file
15
timespans/timespans_test.go
Normal file
@@ -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++ {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user