mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 10:06:24 +05:00
192 lines
5.8 KiB
Go
192 lines
5.8 KiB
Go
/*
|
|
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
|
Copyright (C) ITsysCOM GmbH
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/cgrates/birpc/context"
|
|
"github.com/cgrates/cgrates/config"
|
|
"github.com/cgrates/cgrates/utils"
|
|
)
|
|
|
|
func NewChargerService(dm *DataManager, filterS *FilterS,
|
|
cfg *config.CGRConfig, connMgr *ConnManager) *ChargerService {
|
|
return &ChargerService{dm: dm, filterS: filterS,
|
|
cfg: cfg, connMgr: connMgr}
|
|
}
|
|
|
|
// ChargerService is performing charging
|
|
type ChargerService struct {
|
|
dm *DataManager
|
|
filterS *FilterS
|
|
cfg *config.CGRConfig
|
|
connMgr *ConnManager
|
|
}
|
|
|
|
// Shutdown is called to shutdown the service
|
|
func (cS *ChargerService) Shutdown() {
|
|
utils.Logger.Info(fmt.Sprintf("<%s> shutdown initialized", utils.ChargerS))
|
|
utils.Logger.Info(fmt.Sprintf("<%s> shutdown complete", utils.ChargerS))
|
|
}
|
|
|
|
// matchingChargingProfilesForEvent returns ordered list of matching chargers which are active by the time of the function call
|
|
func (cS *ChargerService) matchingChargerProfilesForEvent(tnt string, cgrEv *utils.CGREvent) (cPs ChargerProfiles, err error) {
|
|
evNm := utils.MapStorage{
|
|
utils.MetaReq: cgrEv.Event,
|
|
utils.MetaOpts: cgrEv.APIOpts,
|
|
}
|
|
cpIDs, err := MatchingItemIDsForEvent(evNm,
|
|
cS.cfg.ChargerSCfg().StringIndexedFields,
|
|
cS.cfg.ChargerSCfg().PrefixIndexedFields,
|
|
cS.cfg.ChargerSCfg().SuffixIndexedFields,
|
|
cS.cfg.ChargerSCfg().ExistsIndexedFields,
|
|
cS.dm, utils.CacheChargerFilterIndexes, tnt,
|
|
cS.cfg.ChargerSCfg().IndexedSelects,
|
|
cS.cfg.ChargerSCfg().NestedFields,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
matchingCPs := make(map[string]*ChargerProfile)
|
|
for cpID := range cpIDs {
|
|
cP, err := cS.dm.GetChargerProfile(tnt, cpID, true, true, utils.NonTransactional)
|
|
if err != nil {
|
|
if err == utils.ErrNotFound {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if cP.ActivationInterval != nil && cgrEv.Time != nil &&
|
|
!cP.ActivationInterval.IsActiveAtTime(*cgrEv.Time) { // not active
|
|
continue
|
|
}
|
|
if pass, err := cS.filterS.Pass(tnt, cP.FilterIDs,
|
|
evNm); err != nil {
|
|
return nil, err
|
|
} else if !pass {
|
|
continue
|
|
}
|
|
matchingCPs[cpID] = cP
|
|
}
|
|
if len(matchingCPs) == 0 {
|
|
return nil, utils.ErrNotFound
|
|
}
|
|
cPs = make(ChargerProfiles, len(matchingCPs))
|
|
i := 0
|
|
for _, cP := range matchingCPs {
|
|
cPs[i] = cP
|
|
i++
|
|
}
|
|
cPs.Sort()
|
|
return
|
|
}
|
|
|
|
// ChrgSProcessEventReply is the reply to processEvent
|
|
type ChrgSProcessEventReply struct {
|
|
ChargerSProfile string
|
|
AttributeSProfiles []string
|
|
AlteredFields []string
|
|
CGREvent *utils.CGREvent
|
|
}
|
|
|
|
func (cS *ChargerService) processEvent(tnt string, cgrEv *utils.CGREvent) (rply []*ChrgSProcessEventReply, err error) {
|
|
var cPs ChargerProfiles
|
|
if cPs, err = cS.matchingChargerProfilesForEvent(tnt, cgrEv); err != nil {
|
|
return nil, err
|
|
}
|
|
rply = make([]*ChrgSProcessEventReply, len(cPs))
|
|
for i, cP := range cPs {
|
|
clonedEv := cgrEv.Clone()
|
|
clonedEv.Tenant = tnt
|
|
clonedEv.Event[utils.RunID] = cP.RunID
|
|
clonedEv.APIOpts[utils.MetaSubsys] = utils.MetaChargers
|
|
rply[i] = &ChrgSProcessEventReply{
|
|
ChargerSProfile: cP.ID,
|
|
CGREvent: clonedEv,
|
|
AlteredFields: []string{utils.MetaReqRunID},
|
|
}
|
|
if len(cP.AttributeIDs) == 1 && cP.AttributeIDs[0] == utils.MetaNone {
|
|
continue // AttributeS disabled
|
|
}
|
|
clonedEv.APIOpts[utils.OptsAttributesProfileIDs] = cP.AttributeIDs
|
|
ctx, has := clonedEv.APIOpts[utils.OptsContext]
|
|
clonedEv.APIOpts[utils.OptsContext] = utils.FirstNonEmpty(
|
|
utils.IfaceAsString(ctx),
|
|
utils.MetaChargers)
|
|
var evReply AttrSProcessEventReply
|
|
if err = cS.connMgr.Call(context.TODO(), cS.cfg.ChargerSCfg().AttributeSConns,
|
|
utils.AttributeSv1ProcessEvent, clonedEv, &evReply); err != nil {
|
|
if err.Error() != utils.ErrNotFound.Error() {
|
|
return nil, err
|
|
}
|
|
err = nil
|
|
}
|
|
rply[i].AttributeSProfiles = evReply.MatchedProfiles
|
|
if len(evReply.AlteredFields) != 0 {
|
|
rply[i].AlteredFields = append(rply[i].AlteredFields, evReply.AlteredFields...)
|
|
rply[i].CGREvent = evReply.CGREvent
|
|
}
|
|
if !has && utils.IfaceAsString(rply[i].CGREvent.APIOpts[utils.OptsContext]) == utils.MetaChargers {
|
|
delete(rply[i].CGREvent.APIOpts, utils.OptsContext)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// V1ProcessEvent will process the event received via API and return list of events forked
|
|
func (cS *ChargerService) V1ProcessEvent(ctx *context.Context, args *utils.CGREvent,
|
|
reply *[]*ChrgSProcessEventReply) (err error) {
|
|
if args == nil ||
|
|
args.Event == nil {
|
|
return utils.NewErrMandatoryIeMissing("Event")
|
|
}
|
|
tnt := args.Tenant
|
|
if tnt == utils.EmptyString {
|
|
tnt = cS.cfg.GeneralCfg().DefaultTenant
|
|
}
|
|
rply, err := cS.processEvent(tnt, args)
|
|
if err != nil {
|
|
if err != utils.ErrNotFound {
|
|
err = utils.NewErrServerError(err)
|
|
}
|
|
return err
|
|
}
|
|
*reply = rply
|
|
return
|
|
}
|
|
|
|
// V1GetChargersForEvent exposes the list of ordered matching ChargingProfiles for an event
|
|
func (cS *ChargerService) V1GetChargersForEvent(ctx *context.Context, args *utils.CGREvent,
|
|
rply *ChargerProfiles) (err error) {
|
|
tnt := args.Tenant
|
|
if tnt == utils.EmptyString {
|
|
tnt = cS.cfg.GeneralCfg().DefaultTenant
|
|
}
|
|
cPs, err := cS.matchingChargerProfilesForEvent(tnt, args)
|
|
if err != nil {
|
|
if err != utils.ErrNotFound {
|
|
err = utils.NewErrServerError(err)
|
|
}
|
|
return err
|
|
}
|
|
*rply = cPs
|
|
return
|
|
}
|