Diameter UsedUnits support, smg_session saves callcost with SMR cost source

This commit is contained in:
DanB
2015-12-12 11:15:48 +01:00
parent 7119221d68
commit a80216c4ed
5 changed files with 49 additions and 31 deletions

View File

@@ -85,7 +85,7 @@ func loadDictionaries(dictsDir, componentId string) error {
}
// Returns reqType, requestNr and ccTime in seconds
func disectUsageForCCR(usage time.Duration, debitInterval time.Duration, callEnded bool) (reqType, reqNr, ccTime int) {
func disectUsageForCCR(usage time.Duration, debitInterval time.Duration, callEnded bool) (reqType, reqNr, reqCCTime, usedCCTime int) {
usageSecs := usage.Seconds()
debitIntervalSecs := debitInterval.Seconds()
reqType = 1
@@ -103,14 +103,23 @@ func disectUsageForCCR(usage time.Duration, debitInterval time.Duration, callEnd
if callEnded {
ccTimeFloat = math.Mod(usageSecs, debitIntervalSecs)
}
return reqType, reqNr, int(ccTimeFloat)
if reqType == 1 { // Initial does not have usedCCTime
reqCCTime = int(ccTimeFloat)
} else if reqType == 2 {
reqCCTime = int(ccTimeFloat)
usedCCTime = int(math.Mod(usageSecs, debitIntervalSecs))
} else if reqType == 3 {
usedCCTime = int(ccTimeFloat) // Termination does not have requestCCTime
}
return
}
func usageFromCCR(reqType, reqNr, ccTime int, debitIterval time.Duration) time.Duration {
func usageFromCCR(reqType, reqNr, reqCCTime, usedCCTime int, debitIterval time.Duration) time.Duration {
dISecs := debitIterval.Seconds()
var ccTime int
if reqType == 3 {
reqNr -= 1 // decrease request number to reach the real number
ccTime += int(dISecs) * reqNr
ccTime = usedCCTime + (int(dISecs) * reqNr)
} else {
ccTime = int(dISecs)
}
@@ -121,7 +130,7 @@ func usageFromCCR(reqType, reqNr, ccTime int, debitIterval time.Duration) time.D
func storedCdrToCCR(cdr *engine.StoredCdr, originHost, originRealm string, vendorId int, productName string,
firmwareRev int, debitInterval time.Duration, callEnded bool) *CCR {
//sid := "session;" + strconv.Itoa(int(rand.Uint32()))
reqType, reqNr, ccTime := disectUsageForCCR(cdr.Usage, debitInterval, callEnded)
reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(cdr.Usage, debitInterval, callEnded)
ccr := &CCR{SessionId: cdr.CgrId, OriginHost: originHost, OriginRealm: originRealm, DestinationHost: originHost, DestinationRealm: originRealm,
AuthApplicationId: 4, ServiceContextId: cdr.ExtraFields["Service-Context-Id"], CCRequestType: reqType, CCRequestNumber: reqNr, EventTimestamp: cdr.AnswerTime,
ServiceIdentifier: 0}
@@ -131,7 +140,8 @@ func storedCdrToCCR(cdr *engine.StoredCdr, originHost, originRealm string, vendo
}, 1)
ccr.SubscriptionId[0].SubscriptionIdType = 0
ccr.SubscriptionId[0].SubscriptionIdData = cdr.Account
ccr.RequestedServiceUnit.CCTime = ccTime
ccr.RequestedServiceUnit.CCTime = reqCCTime
ccr.UsedServiceUnit.CCTime = usedCCTime
ccr.ServiceInformation.INInformation.CallingPartyAddress = cdr.Account
ccr.ServiceInformation.INInformation.CalledPartyAddress = cdr.Destination
ccr.ServiceInformation.INInformation.RealCalledNumber = cdr.Destination
@@ -176,6 +186,9 @@ type CCR struct {
RequestedServiceUnit struct {
CCTime int `avp:"CC-Time"`
} `avp:"Requested-Service-Unit"`
UsedServiceUnit struct {
CCTime int `avp:"CC-Time"`
} `avp:"Used-Service-Unit"`
ServiceInformation struct {
INInformation struct {
CallingPartyAddress string `avp:"Calling-Party-Address"`
@@ -246,6 +259,11 @@ func (self *CCR) AsDiameterMessage() (*diam.Message, error) {
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(self.RequestedServiceUnit.CCTime))}}); err != nil { // CC-Time
return nil, err
}
if _, err := m.NewAVP("Used-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(self.UsedServiceUnit.CCTime))}}); err != nil { // CC-Time
return nil, err
}
if _, err := m.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
@@ -286,7 +304,7 @@ func avpValAsString(a *diam.AVP) string {
func (self *CCR) metaHandler(tag, arg string) (string, error) {
switch tag {
case META_CCR_USAGE:
usage := usageFromCCR(self.CCRequestType, self.CCRequestNumber, self.RequestedServiceUnit.CCTime, self.debitInterval)
usage := usageFromCCR(self.CCRequestType, self.CCRequestNumber, self.RequestedServiceUnit.CCTime, self.UsedServiceUnit.CCTime, self.debitInterval)
return strconv.FormatFloat(usage.Seconds(), 'f', -1, 64), nil
}
return "", nil

View File

@@ -30,46 +30,46 @@ import (
)
func TestDisectUsageForCCR(t *testing.T) {
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(0)*time.Second, time.Duration(300)*time.Second, false); reqType != 1 || reqNr != 0 || ccTime != 300 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(0)*time.Second, time.Duration(300)*time.Second, false); reqType != 1 || reqNr != 0 || reqCCTime != 300 || usedCCTime != 0 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 0 || ccTime != 300 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 0 || reqCCTime != 300 || usedCCTime != 35 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 3 || ccTime != 300 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 3 || reqCCTime != 300 || usedCCTime != 35 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 1 || ccTime != 35 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 1 || reqCCTime != 0 || usedCCTime != 35 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(610)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 3 || ccTime != 10 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(610)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 3 || reqCCTime != 0 || usedCCTime != 10 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
if reqType, reqNr, ccTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 4 || ccTime != 35 {
t.Error(reqType, reqNr, ccTime)
if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 4 || reqCCTime != 0 || usedCCTime != 35 {
t.Error(reqType, reqNr, reqCCTime, usedCCTime)
}
}
func TestUsageFromCCR(t *testing.T) {
if usage := usageFromCCR(1, 0, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
if usage := usageFromCCR(1, 0, 300, 0, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(2, 0, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
if usage := usageFromCCR(2, 0, 300, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(2, 3, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
if usage := usageFromCCR(2, 3, 300, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second {
t.Error(usage.Seconds())
}
if usage := usageFromCCR(3, 3, 0, 10, time.Duration(300)*time.Second); usage != time.Duration(610)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(3, 3, 10, time.Duration(300)*time.Second); usage != time.Duration(610)*time.Second {
if usage := usageFromCCR(3, 4, 0, 35, time.Duration(300)*time.Second); usage != time.Duration(935)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(3, 4, 35, time.Duration(300)*time.Second); usage != time.Duration(935)*time.Second {
if usage := usageFromCCR(3, 1, 0, 35, time.Duration(300)*time.Second); usage != time.Duration(35)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(3, 1, 35, time.Duration(300)*time.Second); usage != time.Duration(35)*time.Second {
t.Error(usage)
}
if usage := usageFromCCR(1, 0, 360, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second {
if usage := usageFromCCR(1, 0, 360, 0, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second {
t.Error(usage)
}
}

View File

@@ -204,7 +204,7 @@ func (self SMGenericEvent) GetOriginatorIP(fieldName string) string {
}
func (self SMGenericEvent) GetCdrSource() string {
return utils.SMG // Needs to match the one in the SMGEvent.saveOperations
return utils.SMG + "_" + self.GetName()
}
func (self SMGenericEvent) GetExtraFields() map[string]string {

View File

@@ -148,7 +148,7 @@ func TestSMGenericEventAsStoredCdr(t *testing.T) {
smGev["Extra1"] = "Value1"
smGev["Extra2"] = 5
eStoredCdr := &engine.StoredCdr{CgrId: "0711eaa78e53937f1593dabc08c83ea04a915f2e",
TOR: utils.VOICE, AccId: "12345", CdrHost: "10.0.3.15", CdrSource: "SMG", ReqType: utils.META_PREPAID,
TOR: utils.VOICE, AccId: "12345", CdrHost: "10.0.3.15", CdrSource: "SMG_TEST_EVENT", ReqType: utils.META_PREPAID,
Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "account1", Subject: "subject1",
Destination: "+4986517174963", SetupTime: time.Date(2015, 11, 9, 14, 21, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 9, 14, 22, 2, 0, time.UTC),
Usage: time.Duration(83) * time.Second, Pdd: time.Duration(300) * time.Millisecond, Supplier: "supplier1", DisconnectCause: "NORMAL_DISCONNECT",

View File

@@ -206,7 +206,7 @@ func (self *SMGSession) saveOperations() error {
var reply string
err := self.cdrsrv.LogCallCost(&engine.CallCostLog{
CgrId: self.eventStart.GetCgrId(self.timezone),
Source: utils.SMG,
Source: utils.SESSION_MANAGER_SOURCE,
RunId: self.runId,
CallCost: firstCC,
CheckDuplicate: true,