mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-22 15:48:44 +05:00
working on scheduler
This commit is contained in:
@@ -22,6 +22,8 @@ import (
|
||||
"log"
|
||||
"github.com/cgrates/cgrates/timespans"
|
||||
"flag"
|
||||
"time"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,6 +33,43 @@ var (
|
||||
redispass = flag.String("pass", "", "redis database password")
|
||||
)
|
||||
|
||||
/*
|
||||
Structure to store action timings according to next activation time.
|
||||
*/
|
||||
type actiontimingqueue []*timespans.ActionTiming
|
||||
|
||||
func (atq actiontimingqueue) Len() int {
|
||||
return len(atq)
|
||||
}
|
||||
|
||||
func (atq actiontimingqueue) Swap(i, j int) {
|
||||
atq[i], atq[j] = atq[j], atq[i]
|
||||
}
|
||||
|
||||
func (atq actiontimingqueue) Less(j, i int) bool {
|
||||
return atq[j].GetNextStartTime().Before(atq[i].GetNextStartTime())
|
||||
}
|
||||
|
||||
type scheduler struct {
|
||||
queue actiontimingqueue
|
||||
}
|
||||
|
||||
func (s scheduler) loop() {
|
||||
for {
|
||||
a0 := s.queue[0]
|
||||
now := time.Now()
|
||||
if a0.GetNextStartTime().Equal(now) || a0.GetNextStartTime().Before(now) {
|
||||
a0.Execute()
|
||||
s.queue = append(s.queue, a0)
|
||||
s.queue = s.queue[1:]
|
||||
sort.Sort(s.queue)
|
||||
} else {
|
||||
d := a0.GetNextStartTime().Sub(now)
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
storage, err := timespans.NewRedisStorage(*redisserver, *redisdb)
|
||||
@@ -41,7 +80,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot get action timings:", err)
|
||||
}
|
||||
for _, at := range actionTimings {
|
||||
log.Print(at)
|
||||
}
|
||||
s := scheduler{}
|
||||
s.queue = append(s.queue, actionTimings...)
|
||||
s.loop()
|
||||
}
|
||||
|
||||
@@ -18,7 +18,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package timespans
|
||||
|
||||
import ()
|
||||
import (
|
||||
"time"
|
||||
"log"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
FORMAT = "2006-1-2 15:04:05 MST"
|
||||
)
|
||||
|
||||
// Amount of a trafic of a certain type (TOR)
|
||||
type UnitsCounter struct {
|
||||
@@ -95,3 +104,86 @@ type ActionTiming struct {
|
||||
ActionsId string
|
||||
actions []*Action
|
||||
}
|
||||
|
||||
func (at *ActionTiming) getActions() (a []*Action, err error) {
|
||||
if at.actions == nil {
|
||||
a, err = storageGetter.GetActions(at.ActionsId)
|
||||
at.actions = a
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (at *ActionTiming) GetNextStartTime() (t time.Time, err error) {
|
||||
i := at.Timing
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
z, _ := now.Zone()
|
||||
if i.StartTime != "" {
|
||||
l := fmt.Sprintf("%d-%d-%d %s %s", y, m, d, i.StartTime, z)
|
||||
t, err = time.Parse(FORMAT, l)
|
||||
}
|
||||
|
||||
if i.WeekDays != nil && len(i.WeekDays) > 0 {
|
||||
sort.Sort(i.WeekDays)
|
||||
}
|
||||
|
||||
if i.MonthDays != nil && len(i.MonthDays) > 0 {
|
||||
sort.Sort(i.MonthDays)
|
||||
now := time.Now()
|
||||
x := sort.SearchInts(i.MonthDays, now.Day())
|
||||
d = i.MonthDays[0]
|
||||
if x < len(i.MonthDays) {
|
||||
if i.MonthDays[x] == now.Day() {
|
||||
if now.Before(t) {
|
||||
h, m, s := t.Clock()
|
||||
t = time.Date(now.Year(), now.Month(), now.Day(), h, m, s, 0, time.Local)
|
||||
return
|
||||
}
|
||||
if x+1 < len(i.MonthDays) { // today was found in the list, jump to the next grater day
|
||||
d = i.MonthDays[x+1]
|
||||
}
|
||||
} else { // today was not found in the list, x is the first greater day
|
||||
d = i.MonthDays[x]
|
||||
}
|
||||
}
|
||||
h, m, s := t.Clock()
|
||||
t = time.Date(now.Year(), now.Month(), d, h, m, s, 0, time.Local)
|
||||
}
|
||||
|
||||
if i.Months != nil && len(i.Months) > 0 {
|
||||
sort.Sort(i.Months)
|
||||
now := time.Now()
|
||||
x := sort.Search(len(i.Months), func(x int) bool { return i.Months[x] >= now.Month() })
|
||||
m = i.Months[0]
|
||||
if x < len(i.Months) {
|
||||
if i.Months[x] == now.Month() {
|
||||
if now.Before(t) {
|
||||
h, m, s := t.Clock()
|
||||
t = time.Date(now.Year(), now.Month(), t.Day(), h, m, s, 0, time.Local)
|
||||
return
|
||||
}
|
||||
if x+1 < len(i.Months) { // today was found in the list, jump to the next grater day
|
||||
m = i.Months[x+1]
|
||||
}
|
||||
} else { // today was not found in the list, x is the first greater day
|
||||
m = i.Months[x]
|
||||
}
|
||||
}
|
||||
h, min, s := t.Clock()
|
||||
t = time.Date(now.Year(), m, t.Day(), h, min, s, 0, time.Local)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (at *ActionTiming) Execute() {
|
||||
aac, err := at.getActions()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, a := range aac {
|
||||
log.Print(a)
|
||||
}
|
||||
}
|
||||
|
||||
177
timespans/actions_test.go
Normal file
177
timespans/actions_test.go
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012 Radu Ioan Fericean
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package timespans
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestActionTimingNothing(t *testing.T) {
|
||||
at := &ActionTiming{}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingOnlyHour(t *testing.T) {
|
||||
at := &ActionTiming{Timing: &Interval{StartTime: "10:01:00"}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
/*func TestActionTimingOnlyWeekdays(t *testing.T) {
|
||||
at := &ActionTiming{Timing: &Interval{WeekDays: []time.Weekday{time.Monday}}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingWeekdaysHour(t *testing.T) {
|
||||
at := &ActionTiming{Timing: &Interval{WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}*/
|
||||
|
||||
func TestActionTimingOnlyMonthdays(t *testing.T) {
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
tomorrow := time.Date(y, m, d+1, 0, 0, 0, 0, time.Local)
|
||||
at := &ActionTiming{Timing: &Interval{MonthDays: MonthDays{1, 25, 2, tomorrow.Day()}}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(y, m, tomorrow.Day(), 0, 0, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingHourMonthdays(t *testing.T) {
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
tomorrow := time.Date(y, m, d+1, 0, 0, 0, 0, time.Local)
|
||||
day := now.Day()
|
||||
if now.After(testTime) {
|
||||
day = tomorrow.Day()
|
||||
}
|
||||
at := &ActionTiming{Timing: &Interval{MonthDays: MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(y, m, day, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingOnlyMonths(t *testing.T) {
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
nextMonth := time.Date(y, m+1, d, 0, 0, 0, 0, time.Local)
|
||||
at := &ActionTiming{Timing: &Interval{Months: Months{time.February, time.May, nextMonth.Month()}}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(y, nextMonth.Month(), 1, 0, 0, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingHourMonths(t *testing.T) {
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
nextMonth := time.Date(y, m+1, d, 0, 0, 0, 0, time.Local)
|
||||
month := now.Month()
|
||||
if now.After(testTime) {
|
||||
month = nextMonth.Month()
|
||||
}
|
||||
at := &ActionTiming{Timing: &Interval{Months: Months{now.Month(), nextMonth.Month()}, StartTime: "10:01:00"}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(y, month, d, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTimingHourMonthdaysMonths(t *testing.T) {
|
||||
now := time.Now()
|
||||
y, m, d := now.Date()
|
||||
testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local)
|
||||
nextMonth := time.Date(y, m+1, d, 0, 0, 0, 0, time.Local)
|
||||
tomorrow := time.Date(y, m, d+1, 0, 0, 0, 0, time.Local)
|
||||
day := now.Day()
|
||||
if now.After(testTime) {
|
||||
day = tomorrow.Day()
|
||||
}
|
||||
month := now.Month()
|
||||
if now.After(testTime) {
|
||||
month = nextMonth.Month()
|
||||
}
|
||||
at := &ActionTiming{Timing: &Interval{
|
||||
Months: Months{now.Month(), nextMonth.Month()},
|
||||
MonthDays: MonthDays{now.Day(), tomorrow.Day()},
|
||||
StartTime: "10:01:00",
|
||||
}}
|
||||
st, err := at.GetNextStartTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := time.Date(y, month, day, 10, 1, 0, 0, time.Local)
|
||||
if !st.Equal(expected) {
|
||||
t.Errorf("Expected %v was %v", expected, st)
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,20 @@ import (
|
||||
// Defines months series
|
||||
type Months []time.Month
|
||||
|
||||
func (m Months) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m Months) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
|
||||
func (m Months) Less(j, i int) bool {
|
||||
return m[j] < m[i]
|
||||
}
|
||||
|
||||
// Return true if the specified date is inside the series
|
||||
func (m Months) Contains(month time.Month) (result bool) {
|
||||
result = false
|
||||
for _, ms := range m {
|
||||
if ms == month {
|
||||
result = true
|
||||
@@ -61,6 +72,18 @@ func (m *Months) Parse(input, sep string) {
|
||||
// Defines month days series
|
||||
type MonthDays []int
|
||||
|
||||
func (md MonthDays) Len() int {
|
||||
return len(md)
|
||||
}
|
||||
|
||||
func (md MonthDays) Swap(i, j int) {
|
||||
md[i], md[j] = md[j], md[i]
|
||||
}
|
||||
|
||||
func (md MonthDays) Less(j, i int) bool {
|
||||
return md[j] < md[i]
|
||||
}
|
||||
|
||||
// Return true if the specified date is inside the series
|
||||
func (md MonthDays) Contains(monthDay int) (result bool) {
|
||||
result = false
|
||||
@@ -93,6 +116,18 @@ func (md *MonthDays) Parse(input, sep string) {
|
||||
// Defines week days series
|
||||
type WeekDays []time.Weekday
|
||||
|
||||
func (wd WeekDays) Len() int {
|
||||
return len(wd)
|
||||
}
|
||||
|
||||
func (wd WeekDays) Swap(i, j int) {
|
||||
wd[i], wd[j] = wd[j], wd[i]
|
||||
}
|
||||
|
||||
func (wd WeekDays) Less(j, i int) bool {
|
||||
return wd[j] < wd[i]
|
||||
}
|
||||
|
||||
// Return true if the specified date is inside the series
|
||||
func (wd WeekDays) Contains(weekDay time.Weekday) (result bool) {
|
||||
result = false
|
||||
|
||||
@@ -20,7 +20,7 @@ package timespans
|
||||
|
||||
import (
|
||||
"github.com/simonz05/godis"
|
||||
"encoding/json"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -134,7 +134,7 @@ func (rs *RedisStorage) GetAllActionTimings() (ats []*ActionTiming, err error) {
|
||||
return
|
||||
}
|
||||
for _, v := range values.BytesArray() {
|
||||
var tempAts []*ActionTiming
|
||||
var tempAts []*ActionTiming
|
||||
err = json.Unmarshal(v, &tempAts)
|
||||
ats = append(ats, tempAts...)
|
||||
}
|
||||
|
||||
@@ -47,18 +47,19 @@ var (
|
||||
Structure containing information about user's credit (minutes, cents, sms...).'
|
||||
*/
|
||||
type UserBalance struct {
|
||||
Id string
|
||||
Type string // prepaid-postpaid
|
||||
BalanceMap map[string]float64
|
||||
MinuteBuckets []*MinuteBucket
|
||||
UnitsCounters []*UnitsCounter
|
||||
ActionTriggers []*ActionTrigger
|
||||
Id string
|
||||
Type string // prepaid-postpaid
|
||||
BalanceMap map[string]float64
|
||||
MinuteBuckets []*MinuteBucket
|
||||
UnitsCounters []*UnitsCounter
|
||||
ActionTriggers []*ActionTrigger
|
||||
usedActionTriggers []*ActionTrigger
|
||||
}
|
||||
|
||||
/*
|
||||
Error type for overflowed debit methods.
|
||||
*/
|
||||
type AmountTooBig byte
|
||||
type AmountTooBig struct{}
|
||||
|
||||
func (a AmountTooBig) Error() string {
|
||||
return "Amount excedes balance!"
|
||||
@@ -83,6 +84,11 @@ func (bs bucketsorter) Less(j, i int) bool {
|
||||
bs[i].Price > bs[j].Price
|
||||
}
|
||||
|
||||
func (ub *UserBalance) ResetActionTriggers() {
|
||||
ub.ActionTriggers = append(ub.ActionTriggers, ub.usedActionTriggers...)
|
||||
ub.usedActionTriggers = make([]*ActionTrigger, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns user's available minutes for the specified destination
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user