working on scheduler

This commit is contained in:
Radu Ioan Fericean
2012-06-26 19:15:37 +03:00
parent ff804d497f
commit f61a05011d
6 changed files with 363 additions and 14 deletions

View File

@@ -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()
}

View File

@@ -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
View 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)
}
}

View File

@@ -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

View File

@@ -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...)
}

View File

@@ -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
*/