Files
telecom-platform/apps/carrier-connector/internal/smdp/selection_algorithm.go
T
nutcas3 b160029e30 refactor: Replace time and ID generation helpers with centralized id package utilities
- Replace getCurrentTime() calls with id.GetCurrentTime() in pricing_integration.go and pricing_service.go
- Replace generateRuleID() with id.GenerateRuleID() in pricing rule creation
- Replace generateEventID() with id.GenerateEventID() in tenant middleware
- Remove local helper functions: generateRuleID, getCurrentTime, generateEventID, generateRandomString, getCurrentTimestamp
- Remove unused time package imports
2026-05-02 05:09:12 +03:00

200 lines
6.2 KiB
Go

package smdp
import (
"math"
"time"
"github.com/sirupsen/logrus"
)
// SelectionCriteria defines parameters for carrier selection
type SelectionCriteria struct {
Region string `json:"region"`
ProfileType string `json:"profile_type"`
Urgency string `json:"urgency"` // "low", "medium", "high"
CostSensitivity float64 `json:"cost_sensitivity"` // 0.0-1.0
PerformanceWeight float64 `json:"performance_weight"` // 0.0-1.0
ReliabilityWeight float64 `json:"reliability_weight"` // 0.0-1.0
}
// CarrierScore represents the scored evaluation of a carrier
type CarrierScore struct {
CarrierID string `json:"carrier_id"`
Carrier *Carrier `json:"carrier"`
TotalScore float64 `json:"total_score"`
PerformanceScore float64 `json:"performance_score"`
ReliabilityScore float64 `json:"reliability_score"`
CostScore float64 `json:"cost_score"`
RegionScore float64 `json:"region_score"`
CapabilityScore float64 `json:"capability_score"`
SelectedAt time.Time `json:"selected_at"`
Reason string `json:"reason"`
}
// SelectionAlgorithm implements intelligent carrier selection
type SelectionAlgorithm struct {
logger *logrus.Logger
history map[string][]CarrierScore // Selection history per carrier
maxHistory int
learningRate float64
mlModel *LearningModel // Machine learning model
}
// NewSelectionAlgorithm creates a new selection algorithm instance
func NewSelectionAlgorithm() *SelectionAlgorithm {
return &SelectionAlgorithm{
logger: logrus.New(),
history: make(map[string][]CarrierScore),
maxHistory: 100,
learningRate: 0.1,
mlModel: NewLearningModel(),
}
}
// calculateWeightedScore combines all scores with weights
func (sa *SelectionAlgorithm) calculateWeightedScore(score *CarrierScore, criteria *SelectionCriteria) float64 {
weights := sa.getWeights(criteria)
total := score.PerformanceScore*weights.performance +
score.ReliabilityScore*weights.reliability +
score.CostScore*weights.cost +
score.RegionScore*weights.region +
score.CapabilityScore*weights.capability
return math.Min(100.0, total)
}
// getWeights returns selection weights based on criteria and ML optimization
func (sa *SelectionAlgorithm) getWeights(criteria *SelectionCriteria) struct {
performance float64
reliability float64
cost float64
region float64
capability float64
} {
// Get ML-optimized weights as base
mlWeights := sa.mlModel.GetOptimizedWeights()
weights := struct {
performance float64
reliability float64
cost float64
region float64
capability float64
}{
performance: mlWeights.Performance,
reliability: mlWeights.Reliability,
cost: mlWeights.Cost,
region: mlWeights.Region,
capability: mlWeights.Capability,
}
// Adjust based on urgency (override ML weights for specific business rules)
switch criteria.Urgency {
case "high":
weights.performance = math.Max(weights.performance, 0.4)
weights.reliability = math.Max(weights.reliability, 0.4)
weights.cost = math.Min(weights.cost, 0.1)
weights.region = math.Min(weights.region, 0.05)
weights.capability = math.Min(weights.capability, 0.05)
case "low":
weights.cost = math.Max(weights.cost, 0.4)
weights.performance = math.Min(weights.performance, 0.2)
weights.reliability = math.Min(weights.reliability, 0.2)
}
// Apply user-defined weights (business preferences override ML)
if criteria.PerformanceWeight > 0 {
weights.performance = criteria.PerformanceWeight
}
if criteria.ReliabilityWeight > 0 {
weights.reliability = criteria.ReliabilityWeight
}
if criteria.CostSensitivity > 0 {
weights.cost = criteria.CostSensitivity
}
// Normalize weights
total := weights.performance + weights.reliability + weights.cost + weights.region + weights.capability
if total > 0 {
weights.performance /= total
weights.reliability /= total
weights.cost /= total
weights.region /= total
weights.capability /= total
}
return weights
}
// recordSelection records the selection for learning
func (sa *SelectionAlgorithm) recordSelection(score *CarrierScore) {
history := sa.history[score.CarrierID]
history = append(history, *score)
// Limit history size
if len(history) > sa.maxHistory {
history = history[1:]
}
sa.history[score.CarrierID] = history
}
// GetSelectionHistory returns selection history for a carrier
func (sa *SelectionAlgorithm) GetSelectionHistory(carrierID string) []CarrierScore {
return sa.history[carrierID]
}
// UpdateLearning updates the algorithm based on feedback using ML model
func (sa *SelectionAlgorithm) UpdateLearning(carrierID string, actualPerformance float64) {
history := sa.history[carrierID]
if len(history) == 0 {
return
}
// Get the most recent selection to get expected score
lastSelection := history[len(history)-1]
expectedScore := lastSelection.TotalScore
// Update ML model with performance feedback
sa.mlModel.UpdateLearning(carrierID, actualPerformance, expectedScore)
// Legacy learning rate adjustment for backward compatibility
if actualPerformance < 50.0 {
sa.learningRate *= 0.95 // Reduce learning rate for poor performance
} else {
sa.learningRate *= 1.05 // Increase learning rate for good performance
}
}
// GetLearningStats returns statistics about the ML learning model
func (sa *SelectionAlgorithm) GetLearningStats() map[string]any {
return sa.mlModel.GetLearningStats()
}
// PredictPerformance predicts expected performance for a carrier
func (sa *SelectionAlgorithm) PredictPerformance(carrierID string, criteria *SelectionCriteria) float64 {
return sa.mlModel.PredictPerformance(carrierID, criteria)
}
// GetCarrierPerformance returns detailed performance metrics for a carrier
func (sa *SelectionAlgorithm) GetCarrierPerformance(carrierID string) *PerformanceMetrics {
return sa.mlModel.GetCarrierPerformance(carrierID)
}
// getHighestPriorityCarrier returns the carrier with the highest priority
func (sa *SelectionAlgorithm) getHighestPriorityCarrier(carriers []*Carrier) *Carrier {
if len(carriers) == 0 {
return nil
}
highestPriority := carriers[0]
for _, carrier := range carriers {
if carrier.Priority > highestPriority.Priority {
highestPriority = carrier
}
}
return highestPriority
}