From fc546fe6b61069b39c102801cca89ad03145f84e Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 28 Jul 2014 20:02:51 +0200 Subject: [PATCH] Adding FSMinDurLowBalance, FSLowBalanceAnnFile,FSEmptyBalanceContext, FSEmptyBalanceAnnFile and logic behind to support playing annoucements and transfers before disconnecting prepaid calls within FreeSWITCH SessionManager --- config/config.go | 23 ++++++++++++++++++++++ config/config_test.go | 8 ++++++++ config/test_data.txt | 10 +++++++--- data/conf/cgrates.cfg | 7 +++++-- sessionmanager/fssessionmanager.go | 31 +++++++++++++++++++----------- sessionmanager/osipssm.go | 2 +- sessionmanager/session.go | 10 ++++++++-- sessionmanager/sessionmanager.go | 2 +- 8 files changed, 73 insertions(+), 20 deletions(-) diff --git a/config/config.go b/config/config.go index 8503883e7..d95104fe2 100644 --- a/config/config.go +++ b/config/config.go @@ -117,6 +117,10 @@ type CGRConfig struct { FreeswitchServer string // freeswitch address host:port FreeswitchPass string // FS socket password FreeswitchReconnects int // number of times to attempt reconnect after connect fails + FSMinDurLowBalance time.Duration // Threshold which will trigger low balance warnings + FSLowBalanceAnnFile string // File to be played when low balance is reached + FSEmptyBalanceContext string // If defined, call will be transfered to this context on empty balance + FSEmptyBalanceAnnFile string // File to be played before disconnecting prepaid calls (applies only if no context defined) OsipsListenUdp string // Address where to listen for event datagrams coming from OpenSIPS OsipsMiAddr string // Adress where to reach OpenSIPS mi_datagram module OsipsEvSubscInterval time.Duration // Refresh event subscription at this interval @@ -208,6 +212,10 @@ func (self *CGRConfig) setDefaults() error { self.FreeswitchServer = "127.0.0.1:8021" self.FreeswitchPass = "ClueCon" self.FreeswitchReconnects = 5 + self.FSMinDurLowBalance = time.Duration(5) * time.Second + self.FSLowBalanceAnnFile = "" + self.FSEmptyBalanceContext = "" + self.FSEmptyBalanceAnnFile = "" self.OsipsListenUdp = "127.0.0.1:2020" self.OsipsMiAddr = "127.0.0.1:8020" self.OsipsEvSubscInterval = time.Duration(60) * time.Second @@ -542,6 +550,21 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { if hasOpt = c.HasOption("freeswitch", "reconnects"); hasOpt { cfg.FreeswitchReconnects, _ = c.GetInt("freeswitch", "reconnects") } + if hasOpt = c.HasOption("freeswitch", "min_dur_low_balance"); hasOpt { + minDurStr, _ := c.GetString("freeswitch", "min_dur_low_balance") + if cfg.FSMinDurLowBalance, err = utils.ParseDurationWithSecs(minDurStr); err != nil { + return nil, err + } + } + if hasOpt = c.HasOption("freeswitch", "low_balance_ann_file"); hasOpt { + cfg.FSLowBalanceAnnFile, _ = c.GetString("freeswitch", "low_balance_ann_file") + } + if hasOpt = c.HasOption("freeswitch", "empty_balance_context"); hasOpt { + cfg.FSEmptyBalanceContext, _ = c.GetString("freeswitch", "empty_balance_context") + } + if hasOpt = c.HasOption("freeswitch", "empty_balance_ann_file"); hasOpt { + cfg.FSEmptyBalanceAnnFile, _ = c.GetString("freeswitch", "empty_balance_ann_file") + } if hasOpt = c.HasOption("opensips", "listen_udp"); hasOpt { cfg.OsipsListenUdp, _ = c.GetString("opensips", "listen_udp") } diff --git a/config/config_test.go b/config/config_test.go index 9c02b4aa4..6e483156a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -118,6 +118,10 @@ func TestDefaults(t *testing.T) { eCfg.FreeswitchServer = "127.0.0.1:8021" eCfg.FreeswitchPass = "ClueCon" eCfg.FreeswitchReconnects = 5 + eCfg.FSMinDurLowBalance = time.Duration(5) * time.Second + eCfg.FSLowBalanceAnnFile = "" + eCfg.FSEmptyBalanceContext = "" + eCfg.FSEmptyBalanceAnnFile = "" eCfg.OsipsListenUdp = "127.0.0.1:2020" eCfg.OsipsMiAddr = "127.0.0.1:8020" eCfg.OsipsEvSubscInterval = time.Duration(60) * time.Second @@ -258,6 +262,10 @@ func TestConfigFromFile(t *testing.T) { eCfg.FreeswitchServer = "test" eCfg.FreeswitchPass = "test" eCfg.FreeswitchReconnects = 99 + eCfg.FSMinDurLowBalance = time.Duration(99) * time.Second + eCfg.FSLowBalanceAnnFile = "test" + eCfg.FSEmptyBalanceContext = "test" + eCfg.FSEmptyBalanceAnnFile = "test" eCfg.OsipsListenUdp = "test" eCfg.OsipsMiAddr = "test" eCfg.OsipsEvSubscInterval = time.Duration(99) * time.Second diff --git a/config/test_data.txt b/config/test_data.txt index 88893327e..b033eb440 100644 --- a/config/test_data.txt +++ b/config/test_data.txt @@ -95,9 +95,13 @@ min_call_duration = 98 # Only authorize calls with allowed duration bigger than max_call_duration = 99 # Maximum call duration a prepaid call can last [freeswitch] -server = test # Adress where to connect to FreeSWITCH socket. -passwd = test # FreeSWITCH socket password. -reconnects = 99 # Number of attempts on connect failure. +server = test # Adress where to connect to FreeSWITCH socket. +passwd = test # FreeSWITCH socket password. +reconnects = 99 # Number of attempts on connect failure. +min_dur_low_balance = 99 # Threshold which will trigger low balance warnings +low_balance_ann_file = test # File to be played when low balance is reached +empty_balance_context = test # If defined, call will be transfered to this context on empty balance +empty_balance_ann_file = test # File to be played before disconnecting prepaid calls (applies only if no context defined) [opensips] listen_udp = test # Address where to listen for event datagrams coming from OpenSIPS diff --git a/data/conf/cgrates.cfg b/data/conf/cgrates.cfg index bdd0f18cc..b4a117534 100644 --- a/data/conf/cgrates.cfg +++ b/data/conf/cgrates.cfg @@ -95,14 +95,17 @@ # rater = internal # Address where to reach the Rater # reconnects = 3 # Number of reconnects to rater/cdrs before giving up. # debit_interval = 10 # Interval to perform debits on. -# min_call_duration = 0s Only authorize calls with allowed duration bigger than this +# min_call_duration = 0s # Only authorize calls with allowed duration bigger than this # max_call_duration = 3h # Maximum call duration a prepaid call can last - [freeswitch] # server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket. # passwd = ClueCon # FreeSWITCH socket password. # reconnects = 5 # Number of attempts on connect failure. +# min_dur_low_balance = 5s # Threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval) +# low_balance_ann_file = # File to be played when low balance is reached for prepaid calls +# empty_balance_context = # If defined, prepaid calls will be transfered to this context on empty balance +# empty_balance_ann_file = # File to be played before disconnecting prepaid calls on empty balance (applies only if no context defined) [opensips] # listen_udp = 127.0.0.1:2020 # Address where to listen for datagram events coming from OpenSIPS diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 1645dbdb1..6b924cee6 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -99,14 +99,24 @@ func (sm *FSSessionManager) GetSession(uuid string) *Session { } // Disconnects a session by sending hangup command to freeswitch -func (sm *FSSessionManager) DisconnectSession(uuid string, notify string) { - // engine.Logger.Debug(fmt.Sprintf("Session: %+v", s.uuid)) - _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_notify %s\n\n", uuid, notify)) - if err != nil { - engine.Logger.Err(fmt.Sprintf(" Could not send disconect api notification to freeswitch: %v", err)) +func (sm *FSSessionManager) DisconnectSession(uuid, notify, destnr string) { + if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_notify %s\n\n", uuid, notify)); err != nil { + engine.Logger.Err(fmt.Sprintf(" Could not send disconect api notification to freeswitch: %s", err.Error())) } - err = fsock.FS.SendMsgCmd(uuid, map[string]string{"call-command": "hangup", "hangup-cause": "MANAGER_REQUEST"}) // without + sign - if err != nil { + if notify == INSUFFICIENT_FUNDS { + if len(cfg.FSEmptyBalanceContext) != 0 { + if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_transfer %s %s %s\n\n", uuid, destnr, cfg.FSEmptyBalanceContext)); err != nil { + engine.Logger.Err(" Could not transfer the call to empty balance context") + } + return + } else if len(cfg.FSEmptyBalanceAnnFile) != 0 { + if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_broadcast %s playback!manager_request::%s aleg\n\n", uuid, cfg.FSEmptyBalanceAnnFile)); err != nil { + engine.Logger.Err(fmt.Sprintf(" Could not send uuid_broadcast to freeswitch: %s", err.Error())) + } + return + } + } + if err := fsock.FS.SendMsgCmd(uuid, map[string]string{"call-command": "hangup", "hangup-cause": "MANAGER_REQUEST"}); err != nil { engine.Logger.Err(fmt.Sprintf(" Could not send disconect msg to freeswitch: %v", err)) } return @@ -139,8 +149,7 @@ func (sm *FSSessionManager) unparkCall(uuid, call_dest_nb, notify string) { if err != nil { engine.Logger.Err(" Could not send unpark api notification to freeswitch") } - _, err = fsock.FS.SendApiCmd(fmt.Sprintf("uuid_transfer %s %s\n\n", uuid, call_dest_nb)) - if err != nil { + if _, err = fsock.FS.SendApiCmd(fmt.Sprintf("uuid_transfer %s %s\n\n", uuid, call_dest_nb)); err != nil { engine.Logger.Err(" Could not send unpark api call to freeswitch") } } @@ -224,7 +233,7 @@ func (sm *FSSessionManager) OnChannelPark(ev Event) { func (sm *FSSessionManager) OnChannelAnswer(ev Event) { if ev.MissingParameter() { - sm.DisconnectSession(ev.GetUUID(), MISSING_PARAMETER) + sm.DisconnectSession(ev.GetUUID(), MISSING_PARAMETER, "") } if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_reqtype %s\n\n", ev.GetUUID(), ev.GetReqType(""))); err != nil { engine.Logger.Err(fmt.Sprintf("Error on attempting to overwrite cgr_type in chan variables: %v", err)) @@ -234,7 +243,7 @@ func (sm *FSSessionManager) OnChannelAnswer(ev Event) { var dcs utils.DerivedChargers if err := sm.connector.GetDerivedChargers(attrsDC, &dcs); err != nil { engine.Logger.Err(fmt.Sprintf(" OnAnswer: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error())) - sm.DisconnectSession(ev.GetUUID(), SYSTEM_ERROR) // Disconnect the session since we are not able to process sessions + sm.DisconnectSession(ev.GetUUID(), SYSTEM_ERROR, "") // Disconnect the session since we are not able to process sessions return } dcs, _ = dcs.AppendDefaultRun() diff --git a/sessionmanager/osipssm.go b/sessionmanager/osipssm.go index a9e1b5700..20db4cac8 100644 --- a/sessionmanager/osipssm.go +++ b/sessionmanager/osipssm.go @@ -66,7 +66,7 @@ func (osm *OsipsSessionManager) Connect() (err error) { return errors.New(" Stopped reading events") } -func (osm *OsipsSessionManager) DisconnectSession(uuid string, notify string) { +func (osm *OsipsSessionManager) DisconnectSession(uuid, notify, destnr string) { return } func (osm *OsipsSessionManager) RemoveSession(uuid string) { diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 792902d56..c80ceff4b 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -25,6 +25,7 @@ import ( "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/fsock" ) // Session type holding the call information fields, a session delegate for specific @@ -110,13 +111,18 @@ func (s *Session) debitLoop(runIdx int) { cc := new(engine.CallCost) if err := s.sessionManager.MaxDebit(&nextCd, cc); err != nil { engine.Logger.Err(fmt.Sprintf("Could not complete debit opperation: %v", err)) - s.sessionManager.DisconnectSession(s.uuid, SYSTEM_ERROR) + s.sessionManager.DisconnectSession(s.uuid, SYSTEM_ERROR, "") return } if cc.GetDuration() == 0 { - s.sessionManager.DisconnectSession(s.uuid, INSUFFICIENT_FUNDS) + s.sessionManager.DisconnectSession(s.uuid, INSUFFICIENT_FUNDS, nextCd.Destination) return } + if cc.GetDuration() <= cfg.FSMinDurLowBalance && len(cfg.FSLowBalanceAnnFile) != 0 { + if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_broadcast %s %s aleg\n\n", s.uuid, cfg.FSLowBalanceAnnFile)); err != nil { + engine.Logger.Err(fmt.Sprintf(" Could not send uuid_broadcast to freeswitch: %s", err.Error())) + } + } s.sessionRuns[runIdx].callCosts = append(s.sessionRuns[runIdx].callCosts, cc) nextCd.TimeEnd = cc.GetEndTime() // set debited timeEnd // update call duration with real debited duration diff --git a/sessionmanager/sessionmanager.go b/sessionmanager/sessionmanager.go index d55574521..c05dab8d2 100644 --- a/sessionmanager/sessionmanager.go +++ b/sessionmanager/sessionmanager.go @@ -26,7 +26,7 @@ import ( type SessionManager interface { Connect() error - DisconnectSession(string, string) + DisconnectSession(string, string, string) RemoveSession(string) MaxDebit(*engine.CallDescriptor, *engine.CallCost) error GetDebitPeriod() time.Duration