mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-24 16:48:45 +05:00
Fix merge conflicts, improving creation of new rpc pool
This commit is contained in:
@@ -100,7 +100,7 @@ func (sm *FSSessionManager) setCgrLcr(ev engine.Event, connId string) error {
|
||||
return err
|
||||
}
|
||||
cd := &engine.CallDescriptor{
|
||||
CgrId: ev.GetCgrId(sm.Timezone()),
|
||||
CgrID: ev.GetCgrId(sm.Timezone()),
|
||||
Direction: ev.GetDirection(utils.META_DEFAULT),
|
||||
Tenant: ev.GetTenant(utils.META_DEFAULT),
|
||||
Category: ev.GetCategory(utils.META_DEFAULT),
|
||||
@@ -149,7 +149,7 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) {
|
||||
// ComputeLcr
|
||||
if ev.ComputeLcr() {
|
||||
cd, err := fsev.AsCallDescriptor()
|
||||
cd.CgrId = fsev.GetCgrId(sm.Timezone())
|
||||
cd.CgrID = fsev.GetCgrId(sm.Timezone())
|
||||
if err != nil {
|
||||
utils.Logger.Info(fmt.Sprintf("<SM-FreeSWITCH> LCR_PREPROCESS_ERROR: %s", err.Error()))
|
||||
sm.unparkCall(ev.GetUUID(), connId, ev.GetCallDestNr(utils.META_DEFAULT), SYSTEM_ERROR)
|
||||
|
||||
@@ -103,7 +103,7 @@ func (self *KamailioSessionManager) onCgrLcrReq(evData []byte, connId string) {
|
||||
|
||||
func (self *KamailioSessionManager) getSuppliers(kev KamEvent) (string, error) {
|
||||
cd, err := kev.AsCallDescriptor()
|
||||
cd.CgrId = kev.GetCgrId(self.timezone)
|
||||
cd.CgrID = kev.GetCgrId(self.timezone)
|
||||
if err != nil {
|
||||
utils.Logger.Info(fmt.Sprintf("<SM-Kamailio> LCR_PREPROCESS_ERROR error: %s", err.Error()))
|
||||
return "", errors.New("LCR_PREPROCESS_ERROR")
|
||||
|
||||
@@ -70,7 +70,7 @@ func NewSession(ev engine.Event, connId string, sm SessionManager) *Session {
|
||||
// the debit loop method (to be stoped by sending somenthing on stopDebit channel)
|
||||
func (s *Session) debitLoop(runIdx int) {
|
||||
nextCd := s.sessionRuns[runIdx].CallDescriptor
|
||||
nextCd.CgrId = s.eventStart.GetCgrId(s.sessionManager.Timezone())
|
||||
nextCd.CgrID = s.eventStart.GetCgrId(s.sessionManager.Timezone())
|
||||
index := 0.0
|
||||
debitPeriod := s.sessionManager.DebitInterval()
|
||||
for {
|
||||
@@ -180,6 +180,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
|
||||
lastCC.Timespans = lastCC.Timespans[:i]
|
||||
} else {
|
||||
ts.SplitByIncrement(lastRefundedIncrementIndex)
|
||||
ts.Cost = ts.CalculateCost()
|
||||
}
|
||||
break // do not go to other timespans
|
||||
} else {
|
||||
@@ -195,7 +196,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
|
||||
// utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration))
|
||||
if len(refundIncrements) > 0 {
|
||||
cd := &engine.CallDescriptor{
|
||||
CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()),
|
||||
CgrID: s.eventStart.GetCgrId(s.sessionManager.Timezone()),
|
||||
Direction: lastCC.Direction,
|
||||
Tenant: lastCC.Tenant,
|
||||
Category: lastCC.Category,
|
||||
@@ -215,6 +216,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
|
||||
}
|
||||
//utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements)))
|
||||
lastCC.Cost -= refundIncrements.GetTotalCost()
|
||||
lastCC.UpdateRatedUsage()
|
||||
lastCC.Timespans.Compress()
|
||||
return nil
|
||||
}
|
||||
@@ -237,6 +239,16 @@ func (s *Session) SaveOperations() {
|
||||
}
|
||||
firstCC.Timespans.Compress()
|
||||
|
||||
firstCC.Round()
|
||||
roundIncrements := firstCC.GetRoundIncrements()
|
||||
if len(roundIncrements) != 0 {
|
||||
cd := firstCC.CreateCallDescriptor()
|
||||
cd.Increments = roundIncrements
|
||||
var response float64
|
||||
if err := s.sessionManager.Rater().Call("Responder.RefundRounding", cd, &response); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SM> ERROR failed to refund rounding: %v", err))
|
||||
}
|
||||
}
|
||||
var reply string
|
||||
err := s.sessionManager.CdrSrv().Call("CdrServer.LogCallCost", &engine.CallCostLog{
|
||||
CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()),
|
||||
|
||||
@@ -89,6 +89,20 @@ func (mc *MockRpcClient) Call(methodName string, arg interface{}, reply interfac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (mc *MockConnector) GetCost(*engine.CallDescriptor, *engine.CallCost) error { return nil }
|
||||
func (mc *MockConnector) Debit(*engine.CallDescriptor, *engine.CallCost) error { return nil }
|
||||
func (mc *MockConnector) MaxDebit(*engine.CallDescriptor, *engine.CallCost) error { return nil }
|
||||
func (mc *MockConnector) RefundIncrements(cd *engine.CallDescriptor, reply *float64) error {
|
||||
mc.refundCd = cd
|
||||
return nil
|
||||
}
|
||||
func (mc *MockConnector) RefundRounding(cd *engine.CallDescriptor, reply *float64) error {
|
||||
return nil
|
||||
}
|
||||
func (mc *MockConnector) GetMaxSessionTime(*engine.CallDescriptor, *float64) error { return nil }
|
||||
func (mc *MockConnector) GetDerivedChargers(*utils.AttrDerivedChargers, *utils.DerivedChargers) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSessionRefund(t *testing.T) {
|
||||
mc := &MockRpcClient{}
|
||||
|
||||
@@ -20,6 +20,7 @@ package sessionmanager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -49,8 +50,9 @@ func (self SMGenericEvent) GetTOR(fieldName string) string {
|
||||
}
|
||||
|
||||
func (self SMGenericEvent) GetCgrId(timezone string) string {
|
||||
setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone)
|
||||
return utils.Sha1(self.GetUUID(), setupTime.UTC().String())
|
||||
//setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone)
|
||||
//return utils.Sha1(self.GetUUID(), setupTime.UTC().String())
|
||||
return utils.Sha1(self.GetUUID())
|
||||
}
|
||||
|
||||
func (self SMGenericEvent) GetUUID() string {
|
||||
@@ -155,7 +157,23 @@ func (self SMGenericEvent) GetUsage(fieldName string) (time.Duration, error) {
|
||||
if fieldName == utils.META_DEFAULT {
|
||||
fieldName = utils.USAGE
|
||||
}
|
||||
result, _ := utils.ConvertIfaceToString(self[fieldName])
|
||||
valIf, hasVal := self[fieldName]
|
||||
if !hasVal {
|
||||
return nilDuration, utils.ErrNotFound
|
||||
}
|
||||
result, _ := utils.ConvertIfaceToString(valIf)
|
||||
return utils.ParseDurationWithSecs(result)
|
||||
}
|
||||
|
||||
func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) {
|
||||
if fieldName == utils.META_DEFAULT {
|
||||
fieldName = utils.LastUsed
|
||||
}
|
||||
valStr, hasVal := self[fieldName]
|
||||
if !hasVal {
|
||||
return nilDuration, utils.ErrNotFound
|
||||
}
|
||||
result, _ := utils.ConvertIfaceToString(valStr)
|
||||
return utils.ParseDurationWithSecs(result)
|
||||
}
|
||||
|
||||
@@ -210,7 +228,7 @@ func (self SMGenericEvent) GetCdrSource() string {
|
||||
func (self SMGenericEvent) GetExtraFields() map[string]string {
|
||||
extraFields := make(map[string]string)
|
||||
for key, val := range self {
|
||||
primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME)
|
||||
primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME, utils.LastUsed)
|
||||
if utils.IsSliceMember(primaryFields, key) {
|
||||
continue
|
||||
}
|
||||
@@ -348,3 +366,16 @@ func (self SMGenericEvent) AsLcrRequest() *engine.LcrRequest {
|
||||
Duration: usageStr,
|
||||
}
|
||||
}
|
||||
|
||||
// AsMapStringString Converts into map[string]string, used for example as pubsub event
|
||||
func (self SMGenericEvent) AsMapStringString() (map[string]string, error) {
|
||||
mp := make(map[string]string)
|
||||
for k, v := range self {
|
||||
if strV, casts := utils.CastIfToString(v); !casts {
|
||||
return nil, fmt.Errorf("Value %+v does not cast to string", v)
|
||||
} else {
|
||||
mp[k] = strV
|
||||
}
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ func TestSMGenericEventParseFields(t *testing.T) {
|
||||
smGev[utils.SETUP_TIME] = "2015-11-09 14:21:24"
|
||||
smGev[utils.ANSWER_TIME] = "2015-11-09 14:22:02"
|
||||
smGev[utils.USAGE] = "1m23s"
|
||||
smGev[utils.LastUsed] = "21s"
|
||||
smGev[utils.PDD] = "300ms"
|
||||
smGev[utils.SUPPLIER] = "supplier1"
|
||||
smGev[utils.DISCONNECT_CAUSE] = "NORMAL_DISCONNECT"
|
||||
@@ -54,7 +55,7 @@ func TestSMGenericEventParseFields(t *testing.T) {
|
||||
if smGev.GetName() != "TEST_EVENT" {
|
||||
t.Error("Unexpected: ", smGev.GetName())
|
||||
}
|
||||
if smGev.GetCgrId("UTC") != "0711eaa78e53937f1593dabc08c83ea04a915f2e" {
|
||||
if smGev.GetCgrId("UTC") != "8cb2237d0679ca88db6464eac60da96345513964" {
|
||||
t.Error("Unexpected: ", smGev.GetCgrId("UTC"))
|
||||
}
|
||||
if smGev.GetUUID() != "12345" {
|
||||
@@ -107,6 +108,11 @@ func TestSMGenericEventParseFields(t *testing.T) {
|
||||
} else if dur != time.Duration(83)*time.Second {
|
||||
t.Error("Unexpected: ", dur)
|
||||
}
|
||||
if lastUsed, err := smGev.GetLastUsed(utils.META_DEFAULT); err != nil {
|
||||
t.Error(err)
|
||||
} else if lastUsed != time.Duration(21)*time.Second {
|
||||
t.Error("Unexpected: ", lastUsed)
|
||||
}
|
||||
if pdd, err := smGev.GetPdd(utils.META_DEFAULT); err != nil {
|
||||
t.Error(err)
|
||||
} else if pdd != time.Duration(300)*time.Millisecond {
|
||||
@@ -147,7 +153,7 @@ func TestSMGenericEventAsStoredCdr(t *testing.T) {
|
||||
smGev[utils.CDRHOST] = "10.0.3.15"
|
||||
smGev["Extra1"] = "Value1"
|
||||
smGev["Extra2"] = 5
|
||||
eStoredCdr := &engine.CDR{CGRID: "0711eaa78e53937f1593dabc08c83ea04a915f2e",
|
||||
eStoredCdr := &engine.CDR{CGRID: "8cb2237d0679ca88db6464eac60da96345513964",
|
||||
ToR: utils.SMS, OriginID: "12345", OriginHost: "10.0.3.15", Source: "SMG_TEST_EVENT", RequestType: 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),
|
||||
|
||||
@@ -138,7 +138,7 @@ func TestSMGMonetaryRefund(t *testing.T) {
|
||||
}
|
||||
var acnt *engine.Account
|
||||
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
|
||||
eAcntVal := 8.699800
|
||||
eAcntVal := 8.700010
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
@@ -268,7 +268,7 @@ func TestSMGMixedRefund(t *testing.T) {
|
||||
//var acnt *engine.Account
|
||||
//attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
|
||||
eVoiceVal := 90.0
|
||||
eMoneyVal := 8.739
|
||||
eMoneyVal := 8.7399
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eVoiceVal ||
|
||||
@@ -305,3 +305,120 @@ func TestSMGMixedRefund(t *testing.T) {
|
||||
t.Logf("After monetary: %f", acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
t.Logf("After voice: %f", acnt.BalanceMap[utils.VOICE].GetTotalValue())
|
||||
}
|
||||
|
||||
func TestSMGLastUsed(t *testing.T) {
|
||||
if !*testIntegration {
|
||||
return
|
||||
}
|
||||
var acnt *engine.Account
|
||||
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
|
||||
eAcntVal := 8.790000
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
smgEv := SMGenericEvent{
|
||||
utils.EVENT_NAME: "TEST_EVENT",
|
||||
utils.TOR: utils.VOICE,
|
||||
utils.ACCID: "12349",
|
||||
utils.DIRECTION: utils.OUT,
|
||||
utils.ACCOUNT: "1001",
|
||||
utils.SUBJECT: "1001",
|
||||
utils.DESTINATION: "1006",
|
||||
utils.CATEGORY: "call",
|
||||
utils.TENANT: "cgrates.org",
|
||||
utils.REQTYPE: utils.META_PREPAID,
|
||||
utils.SETUP_TIME: "2016-01-05 18:30:49",
|
||||
utils.ANSWER_TIME: "2016-01-05 18:31:05",
|
||||
utils.USAGE: "2m",
|
||||
}
|
||||
var maxUsage float64
|
||||
if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if maxUsage != 120 {
|
||||
t.Error("Bad max usage: ", maxUsage)
|
||||
}
|
||||
eAcntVal = 7.39002
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
smgEv = SMGenericEvent{
|
||||
utils.EVENT_NAME: "TEST_EVENT",
|
||||
utils.TOR: utils.VOICE,
|
||||
utils.ACCID: "12349",
|
||||
utils.DIRECTION: utils.OUT,
|
||||
utils.ACCOUNT: "1001",
|
||||
utils.SUBJECT: "1001",
|
||||
utils.DESTINATION: "1006",
|
||||
utils.CATEGORY: "call",
|
||||
utils.TENANT: "cgrates.org",
|
||||
utils.REQTYPE: utils.META_PREPAID,
|
||||
utils.USAGE: "2m",
|
||||
utils.LastUsed: "1m30s",
|
||||
}
|
||||
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if maxUsage != 120 {
|
||||
t.Error("Bad max usage: ", maxUsage)
|
||||
}
|
||||
eAcntVal = 7.09005
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
smgEv = SMGenericEvent{
|
||||
utils.EVENT_NAME: "TEST_EVENT",
|
||||
utils.TOR: utils.VOICE,
|
||||
utils.ACCID: "12349",
|
||||
utils.DIRECTION: utils.OUT,
|
||||
utils.ACCOUNT: "1001",
|
||||
utils.SUBJECT: "1001",
|
||||
utils.DESTINATION: "1006",
|
||||
utils.CATEGORY: "call",
|
||||
utils.TENANT: "cgrates.org",
|
||||
utils.REQTYPE: utils.META_PREPAID,
|
||||
utils.USAGE: "2m",
|
||||
utils.LastUsed: "2m30s",
|
||||
}
|
||||
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if maxUsage != 120 {
|
||||
t.Error("Bad max usage: ", maxUsage)
|
||||
}
|
||||
eAcntVal = 6.5901
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
smgEv = SMGenericEvent{
|
||||
utils.EVENT_NAME: "TEST_EVENT",
|
||||
utils.TOR: utils.VOICE,
|
||||
utils.ACCID: "12349",
|
||||
utils.DIRECTION: utils.OUT,
|
||||
utils.ACCOUNT: "1001",
|
||||
utils.SUBJECT: "1001",
|
||||
utils.DESTINATION: "1006",
|
||||
utils.CATEGORY: "call",
|
||||
utils.TENANT: "cgrates.org",
|
||||
utils.REQTYPE: utils.META_PREPAID,
|
||||
utils.USAGE: "1m",
|
||||
}
|
||||
var rpl string
|
||||
if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK {
|
||||
t.Error(err)
|
||||
}
|
||||
eAcntVal = 7.59
|
||||
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ type SMGSession struct {
|
||||
sessionCds []*engine.CallDescriptor
|
||||
callCosts []*engine.CallCost
|
||||
extraDuration time.Duration // keeps the current duration debited on top of what heas been asked
|
||||
lastUsage time.Duration // Keep record of the last debit for LastUsed functionality
|
||||
totalUsage time.Duration
|
||||
}
|
||||
|
||||
// Called in case of automatic debits
|
||||
@@ -53,7 +55,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) {
|
||||
return
|
||||
default:
|
||||
}
|
||||
if maxDebit, err := self.debit(debitInterval); err != nil {
|
||||
if maxDebit, err := self.debit(debitInterval, nilDuration); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SMGeneric> Could not complete debit opperation on session: %s, error: %s", self.eventStart.GetUUID(), err.Error()))
|
||||
disconnectReason := SYSTEM_ERROR
|
||||
if err.Error() == utils.ErrUnauthorizedDestination.Error() {
|
||||
@@ -76,11 +78,21 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) {
|
||||
}
|
||||
|
||||
// Attempts to debit a duration, returns maximum duration which can be debitted or error
|
||||
func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
|
||||
func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) {
|
||||
lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit
|
||||
if self.cd.DurationIndex != 0 && lastUsed != 0 {
|
||||
if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted
|
||||
lastUsedCorrection = -(self.lastUsage - lastUsed)
|
||||
} else { // We have debitted less than we have consumed, add the difference to duration debitted
|
||||
lastUsedCorrection = lastUsed - self.lastUsage
|
||||
}
|
||||
}
|
||||
// apply the lastUsed correction
|
||||
dur += lastUsedCorrection
|
||||
self.totalUsage += dur // Should reflect the total usage so far
|
||||
// apply correction from previous run
|
||||
dur -= self.extraDuration
|
||||
self.extraDuration = 0
|
||||
|
||||
if self.cd.LoopIndex > 0 {
|
||||
self.cd.TimeStart = self.cd.TimeEnd
|
||||
}
|
||||
@@ -88,6 +100,7 @@ func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
|
||||
self.cd.DurationIndex += dur
|
||||
cc := &engine.CallCost{}
|
||||
if err := self.rater.Call("Responder.MaxDebit", self.cd, cc); err != nil {
|
||||
self.lastUsage = 0
|
||||
return 0, err
|
||||
}
|
||||
// cd corrections
|
||||
@@ -103,17 +116,22 @@ func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
|
||||
self.cd.LoopIndex += 1
|
||||
self.sessionCds = append(self.sessionCds, self.cd.Clone())
|
||||
self.callCosts = append(self.callCosts, cc)
|
||||
ccDuration -= lastUsedCorrection
|
||||
if ccDuration < 0 { // if correction has pushed ccDuration bellow 0
|
||||
ccDuration = 0
|
||||
}
|
||||
self.lastUsage = ccDuration // Reset the lastUsage for later reference
|
||||
return ccDuration, nil
|
||||
}
|
||||
|
||||
// Attempts to refund a duration, error on failure
|
||||
func (self *SMGSession) refund(refundDuration time.Duration) error {
|
||||
initialRefundDuration := refundDuration
|
||||
lastCC := self.callCosts[len(self.callCosts)-1]
|
||||
lastCC.Timespans.Decompress()
|
||||
firstCC := self.callCosts[0] // use merged cc (from close function)
|
||||
firstCC.Timespans.Decompress()
|
||||
var refundIncrements engine.Increments
|
||||
for i := len(lastCC.Timespans) - 1; i >= 0; i-- {
|
||||
ts := lastCC.Timespans[i]
|
||||
for i := len(firstCC.Timespans) - 1; i >= 0; i-- {
|
||||
ts := firstCC.Timespans[i]
|
||||
tsDuration := ts.GetDuration()
|
||||
if refundDuration <= tsDuration {
|
||||
|
||||
@@ -129,17 +147,18 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
|
||||
}
|
||||
}
|
||||
if lastRefundedIncrementIndex == 0 {
|
||||
lastCC.Timespans[i] = nil
|
||||
lastCC.Timespans = lastCC.Timespans[:i]
|
||||
firstCC.Timespans[i] = nil
|
||||
firstCC.Timespans = firstCC.Timespans[:i]
|
||||
} else {
|
||||
ts.SplitByIncrement(lastRefundedIncrementIndex)
|
||||
ts.Cost = ts.CalculateCost()
|
||||
}
|
||||
break // do not go to other timespans
|
||||
} else {
|
||||
refundIncrements = append(refundIncrements, ts.Increments...)
|
||||
// remove the timespan entirely
|
||||
lastCC.Timespans[i] = nil
|
||||
lastCC.Timespans = lastCC.Timespans[:i]
|
||||
firstCC.Timespans[i] = nil
|
||||
firstCC.Timespans = firstCC.Timespans[:i]
|
||||
// continue to the next timespan with what is left to refund
|
||||
refundDuration -= tsDuration
|
||||
}
|
||||
@@ -147,16 +166,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
|
||||
// show only what was actualy refunded (stopped in timespan)
|
||||
// utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration))
|
||||
if len(refundIncrements) > 0 {
|
||||
cd := &engine.CallDescriptor{
|
||||
Direction: lastCC.Direction,
|
||||
Tenant: lastCC.Tenant,
|
||||
Category: lastCC.Category,
|
||||
Subject: lastCC.Subject,
|
||||
Account: lastCC.Account,
|
||||
Destination: lastCC.Destination,
|
||||
TOR: lastCC.TOR,
|
||||
Increments: refundIncrements,
|
||||
}
|
||||
cd := firstCC.CreateCallDescriptor()
|
||||
cd.Increments = refundIncrements
|
||||
cd.Increments.Compress()
|
||||
utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %s", initialRefundDuration, utils.ToJSON(cd)))
|
||||
var response float64
|
||||
@@ -165,17 +176,20 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
//utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements)))
|
||||
lastCC.Cost -= refundIncrements.GetTotalCost()
|
||||
lastCC.Timespans.Compress()
|
||||
firstCC.Cost -= refundIncrements.GetTotalCost()
|
||||
firstCC.UpdateRatedUsage()
|
||||
firstCC.Timespans.Compress()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Session has ended, check debits and refund the extra charged duration
|
||||
func (self *SMGSession) close(endTime time.Time) error {
|
||||
firstCC := self.callCosts[0]
|
||||
for _, cc := range self.callCosts[1:] {
|
||||
firstCC.Merge(cc)
|
||||
}
|
||||
if len(self.callCosts) != 0 { // We have had at least one cost calculation
|
||||
lastCC := self.callCosts[len(self.callCosts)-1]
|
||||
end := lastCC.GetEndTime()
|
||||
end := firstCC.GetEndTime()
|
||||
refundDuration := end.Sub(endTime)
|
||||
self.refund(refundDuration)
|
||||
}
|
||||
@@ -206,11 +220,19 @@ func (self *SMGSession) saveOperations() error {
|
||||
if len(self.callCosts) == 0 {
|
||||
return nil // There are no costs to save, ignore the operation
|
||||
}
|
||||
firstCC := self.callCosts[0]
|
||||
for _, cc := range self.callCosts[1:] {
|
||||
firstCC.Merge(cc)
|
||||
}
|
||||
firstCC := self.callCosts[0] // was merged in close methos
|
||||
firstCC.Timespans.Compress()
|
||||
firstCC.Round()
|
||||
roundIncrements := firstCC.GetRoundIncrements()
|
||||
if len(roundIncrements) != 0 {
|
||||
cd := firstCC.CreateCallDescriptor()
|
||||
cd.Increments = roundIncrements
|
||||
var response float64
|
||||
if err := self.rater.Call("Responder.RefundRounding", cd, &response); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var reply string
|
||||
err := self.cdrsrv.Call("CdrServer.LogCallCost", &engine.CallCostLog{
|
||||
CgrId: self.eventStart.GetCgrId(self.timezone),
|
||||
@@ -231,3 +253,44 @@ func (self *SMGSession) saveOperations() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SMGSession) TotalUsage() time.Duration {
|
||||
return self.totalUsage
|
||||
}
|
||||
|
||||
func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession {
|
||||
sTime, _ := self.eventStart.GetSetupTime(utils.META_DEFAULT, timezone)
|
||||
aTime, _ := self.eventStart.GetAnswerTime(utils.META_DEFAULT, timezone)
|
||||
usage, _ := self.eventStart.GetUsage(utils.META_DEFAULT)
|
||||
pdd, _ := self.eventStart.GetPdd(utils.META_DEFAULT)
|
||||
aSession := &ActiveSession{
|
||||
CgrId: self.eventStart.GetCgrId(timezone),
|
||||
TOR: utils.VOICE,
|
||||
RunId: self.runId,
|
||||
AccId: self.eventStart.GetUUID(),
|
||||
CdrHost: self.eventStart.GetOriginatorIP(utils.META_DEFAULT),
|
||||
CdrSource: self.eventStart.GetCdrSource(),
|
||||
ReqType: self.eventStart.GetReqType(utils.META_DEFAULT),
|
||||
Direction: self.eventStart.GetDirection(utils.META_DEFAULT),
|
||||
Tenant: self.eventStart.GetTenant(utils.META_DEFAULT),
|
||||
Category: self.eventStart.GetCategory(utils.META_DEFAULT),
|
||||
Account: self.eventStart.GetAccount(utils.META_DEFAULT),
|
||||
Subject: self.eventStart.GetSubject(utils.META_DEFAULT),
|
||||
Destination: self.eventStart.GetDestination(utils.META_DEFAULT),
|
||||
SetupTime: sTime,
|
||||
AnswerTime: aTime,
|
||||
Usage: usage,
|
||||
Pdd: pdd,
|
||||
ExtraFields: self.eventStart.GetExtraFields(),
|
||||
Supplier: self.eventStart.GetSupplier(utils.META_DEFAULT),
|
||||
SMId: "CGR-DA",
|
||||
}
|
||||
if self.cd != nil {
|
||||
aSession.LoopIndex = self.cd.LoopIndex
|
||||
aSession.DurationIndex = self.cd.DurationIndex
|
||||
aSession.MaxRate = self.cd.MaxRate
|
||||
aSession.MaxRateUnit = self.cd.MaxRateUnit
|
||||
aSession.MaxCostSoFar = self.cd.MaxCostSoFar
|
||||
}
|
||||
return aSession
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error
|
||||
s := &SMGSession{eventStart: evStart, connId: connId, runId: sessionRun.DerivedCharger.RunID, timezone: self.timezone,
|
||||
rater: self.rater, cdrsrv: self.cdrsrv, cd: sessionRun.CallDescriptor}
|
||||
self.indexSession(sessionId, s)
|
||||
//utils.Logger.Info(fmt.Sprintf("<SMGeneric> Starting session: %s, runId: %s", sessionId, s.runId))
|
||||
if self.cgrCfg.SmGenericConfig.DebitInterval != 0 {
|
||||
s.stopDebit = stopDebitChan
|
||||
go s.debitLoop(self.cgrCfg.SmGenericConfig.DebitInterval)
|
||||
@@ -118,6 +119,7 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error {
|
||||
return nil, nil // Did not find the session so no need to close it anymore
|
||||
}
|
||||
for idx, s := range ss {
|
||||
//utils.Logger.Info(fmt.Sprintf("<SMGeneric> Ending session: %s, runId: %s", sessionId, s.runId))
|
||||
if idx == 0 && s.stopDebit != nil {
|
||||
close(s.stopDebit) // Stop automatic debits
|
||||
}
|
||||
@@ -153,7 +155,7 @@ func (self *SMGeneric) GetMaxUsage(gev SMGenericEvent, clnt *rpc2.Client) (time.
|
||||
func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([]string, error) {
|
||||
gev[utils.EVENT_NAME] = utils.CGR_LCR_REQUEST
|
||||
cd, err := gev.AsLcrRequest().AsCallDescriptor(self.timezone)
|
||||
cd.CgrId = gev.GetCgrId(self.timezone)
|
||||
cd.CgrID = gev.GetCgrId(self.timezone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -170,18 +172,23 @@ func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([
|
||||
|
||||
// Execute debits for usage/maxUsage
|
||||
func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) {
|
||||
evLastUsed, err := gev.GetLastUsed(utils.META_DEFAULT)
|
||||
if err != nil && err != utils.ErrNotFound {
|
||||
return nilDuration, err
|
||||
}
|
||||
evMaxUsage, err := gev.GetMaxUsage(utils.META_DEFAULT, self.cgrCfg.MaxCallDuration)
|
||||
if err != nil {
|
||||
if err == utils.ErrNotFound {
|
||||
err = utils.ErrMandatoryIeMissing
|
||||
}
|
||||
return nilDuration, err
|
||||
}
|
||||
evUuid := gev.GetUUID()
|
||||
for _, s := range self.getSession(evUuid) {
|
||||
if maxDur, err := s.debit(evMaxUsage); err != nil {
|
||||
if maxDur, err := s.debit(evMaxUsage, evLastUsed); err != nil {
|
||||
return nilDuration, err
|
||||
} else {
|
||||
if maxDur < evMaxUsage {
|
||||
evMaxUsage = maxDur
|
||||
}
|
||||
} else if maxDur < evMaxUsage {
|
||||
evMaxUsage = maxDur
|
||||
}
|
||||
}
|
||||
return evMaxUsage, nil
|
||||
@@ -199,7 +206,25 @@ func (self *SMGeneric) SessionStart(gev SMGenericEvent, clnt *rpc2.Client) (time
|
||||
func (self *SMGeneric) SessionEnd(gev SMGenericEvent, clnt *rpc2.Client) error {
|
||||
usage, err := gev.GetUsage(utils.META_DEFAULT)
|
||||
if err != nil {
|
||||
return err
|
||||
if err != utils.ErrNotFound {
|
||||
return err
|
||||
|
||||
}
|
||||
lastUsed, err := gev.GetLastUsed(utils.META_DEFAULT)
|
||||
if err != nil {
|
||||
if err == utils.ErrNotFound {
|
||||
err = utils.ErrMandatoryIeMissing
|
||||
}
|
||||
return err
|
||||
}
|
||||
var s *SMGSession
|
||||
for _, s = range self.getSession(gev.GetUUID()) {
|
||||
break
|
||||
}
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
usage = s.TotalUsage() + lastUsed
|
||||
}
|
||||
if err := self.sessionEnd(gev.GetUUID(), usage); err != nil {
|
||||
return err
|
||||
@@ -215,18 +240,18 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
|
||||
} else if len(sessionRuns) == 0 {
|
||||
return nilDuration, nil
|
||||
}
|
||||
var maxDurInit bool // Avoid differences between default 0 and received 0
|
||||
for _, sR := range sessionRuns {
|
||||
cc := new(engine.CallCost)
|
||||
if err := self.rater.Call("Responder.MaxDebit", sR.CallDescriptor, cc); err != nil {
|
||||
withErrors = true
|
||||
if err = self.rater.Call("Responder.MaxDebit", sR.CallDescriptor, cc); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SMGeneric> Could not Debit CD: %+v, RunID: %s, error: %s", sR.CallDescriptor, sR.DerivedCharger.RunID, err.Error()))
|
||||
break
|
||||
}
|
||||
sR.CallCosts = append(sR.CallCosts, cc) // Save it so we can revert on issues
|
||||
if ccDur := cc.GetDuration(); ccDur == 0 {
|
||||
err = errors.New("INSUFFICIENT_FUNDS")
|
||||
err = utils.ErrInsufficientCredit
|
||||
break
|
||||
} else if ccDur < maxDur {
|
||||
} else if !maxDurInit || ccDur < maxDur {
|
||||
maxDur = ccDur
|
||||
}
|
||||
}
|
||||
@@ -262,7 +287,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
|
||||
cd.Increments.Compress()
|
||||
utils.Logger.Info(fmt.Sprintf("Refunding session run callcost: %s", utils.ToJSON(cd)))
|
||||
var response float64
|
||||
err := self.rater.RefundIncrements(cd, &response)
|
||||
err := self.rater.Call("Responder.RefundIncrements", cd, &response)
|
||||
if err != nil {
|
||||
return nilDuration, err
|
||||
}
|
||||
@@ -281,8 +306,19 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
|
||||
cc.Merge(ccSR)
|
||||
}
|
||||
}
|
||||
cc.Round()
|
||||
roundIncrements := cc.GetRoundIncrements()
|
||||
if len(roundIncrements) != 0 {
|
||||
cd := cc.CreateCallDescriptor()
|
||||
cd.Increments = roundIncrements
|
||||
var response float64
|
||||
if err := self.rater.Call("Responder.RefundRounding", cd, &response); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SM> ERROR failed to refund rounding: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
var reply string
|
||||
if err := self.cdrsrv.Call("CdrServer.LogCallCost", &engine.CallCostLog{
|
||||
if err := self.cdrsrv.Call("CdrsV2.LogCallCost", &engine.CallCostLog{
|
||||
CgrId: gev.GetCgrId(self.timezone),
|
||||
Source: utils.SESSION_MANAGER_SOURCE,
|
||||
RunId: sR.DerivedCharger.RunID,
|
||||
@@ -311,6 +347,15 @@ func (self *SMGeneric) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Used by APIer to retrieve sessions
|
||||
func (self *SMGeneric) Sessions() map[string][]*SMGSession {
|
||||
return self.getSessions()
|
||||
}
|
||||
|
||||
func (self *SMGeneric) Timezone() string {
|
||||
return self.timezone
|
||||
}
|
||||
|
||||
// System shutdown
|
||||
func (self *SMGeneric) Shutdown() error {
|
||||
for ssId := range self.getSessions() { // Force sessions shutdown
|
||||
|
||||
Reference in New Issue
Block a user