Browse Source

Work on alarmsFor

master
Remi Reuvekamp 5 years ago
parent
commit
a33ca3cd82
  1. 1
      .gitignore
  2. 148
      config.go
  3. 26
      expression/structures.go
  4. 67
      main.go

1
.gitignore

@ -0,0 +1 @@
config.json

148
config.go

@ -17,6 +17,8 @@ type config struct {
Behaviours map[string]behaviour
DateBehaviours map[string][]dateBehaviour
DateTimes map[string][]dateTime
alteredAlarms map[string]*parsedAlarm
}
type dateBehaviour struct {
@ -155,7 +157,7 @@ func (c *config) alarmsFor(from, to time.Time) ([]parsedAlarm, error) {
for _, tm := range tms {
year, week := tm.ISOWeek()
params := map[string]int{
params := expression.Params{
"dayOfWeek": int(tm.Weekday()),
"dayOfMonth": tm.Day(),
"dayOfYear": tm.YearDay(),
@ -164,65 +166,119 @@ func (c *config) alarmsFor(from, to time.Time) ([]parsedAlarm, error) {
"year": year,
}
for i, a := range c.Alarms {
dts, ok := c.DateTimes[a.DateTimeName]
if !ok {
return parsed, fmt.Errorf("Wut? DateTime not found: %s", a.DateTimeName)
}
var pa *parsedAlarm
var err error
var alteredAlarmID string
tName, err := a.determineTime(dts, params, tm)
if err != nil {
return parsed, err
}
if tName == nil {
fmt.Println("Alarm not for today, has no time for today:", a)
continue
}
dur, err := time.ParseDuration(*tName)
if err != nil {
// TODO: Validate duration string wen loading time.
return nil, fmt.Errorf("Wut? Invalid duration time string: %s", *tName)
}
for i, a := range c.Alarms {
alteredAlarmID = fmt.Sprintf("%d%d%d-%d", tm.Year(), tm.Month(), tm.Day(), i)
t := time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, tm.Location()).Add(dur)
if i+1 == len(c.Alarms) && t.Unix() > to.Unix() {
fmt.Println("Debug: Too far into the future:", t)
// Check if there's an altered version of this alarm for this day in memory.
altered, ok := c.alteredAlarms[alteredAlarmID]
if ok {
parsed = append(parsed, *altered)
continue
}
dbs, ok := c.DateBehaviours[a.DateBehaviourName]
if !ok {
return parsed, fmt.Errorf("Wut? DateBehaviour not found: %s", a.DateBehaviourName)
var checkTime int8
if i == 0 {
checkTime = -1
} else if i+1 == len(c.Alarms) {
checkTime = -1
}
bName, err := a.determineBehaviour(dbs, params)
pa, err = a.parse(*c, checkTime, tm, params)
if err != nil {
return parsed, err
}
if bName == nil {
// TODO: Use default behaviour.
fmt.Println("No behaviour :( ")
if pa == nil {
continue
}
b, ok := c.Behaviours[*bName]
if !ok {
return nil, fmt.Errorf("Wut? Behaviour not found: %s", bName)
}
parsed = append(parsed, *pa)
parsed = append(parsed, parsedAlarm{
time: t,
behaviour: b,
})
c.alteredAlarms[alteredAlarmID] = pa
}
}
return parsed, nil
}
// parse this alarm's time and behaviour for datetime tm.
// params are the paramaters using for evaluating the time and behaviour epxression.
// If both the returned parsedAlarm and error and nil, it means this alarm is not
// for today.
func (a alarm) parse(c config, checkTime int8, tm time.Time, params expression.Params) (*parsedAlarm, error) {
const compareTimeFormat = "20060403"
dts, ok := c.DateTimes[a.DateTimeName]
if !ok {
return nil, fmt.Errorf("Wut? DateTime not found: %s", a.DateTimeName)
}
// Determine the time for today.
tName, err := determineTime(dts, params)
if err != nil {
return nil, err
}
if tName == nil {
fmt.Println("No time")
return nil, nil
}
dur, err := time.ParseDuration(*tName)
if err != nil {
// TODO: Validate duration string when loading time.
return nil, fmt.Errorf("Wut? Invalid duration time string: %s", *tName)
}
t := time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, tm.Location()).Add(dur)
// If should check start time, check if time has already happened.
if checkTime == -1 && t.Unix() < tm.Unix() {
fmt.Println("Debug: In the past", t)
return nil, nil
}
// If should check end time, check if time is too far into the future.
if checkTime == 1 && t.Unix() > tm.Unix() {
fmt.Println("Debug: Too far into the future:", t)
return nil, nil
}
// Determine behaviour for today.
dbs, ok := c.DateBehaviours[a.DateBehaviourName]
if !ok {
return nil, fmt.Errorf("Wut? DateBehaviour not found: %s", a.DateBehaviourName)
}
bName, err := determineBehaviour(dbs, params)
if err != nil {
return nil, err
}
if bName == nil {
// TODO: Use default behaviour.
fmt.Println("No behaviour :( ")
return nil, nil
}
b, ok := c.Behaviours[*bName]
if !ok {
return nil, fmt.Errorf("Wut? Behaviour not found: %s", bName)
}
p := parsedAlarm{
time: t,
behaviour: b,
}
return &p, nil
}
// daysRange gives time.Time's for every day between 'from' and 'to', including
// 'from' and 'to' themselves.
// Every time.Time that is not 'to' has the same time (hours, minutes, ...) as 'from'.
func daysRange(from, to time.Time) []time.Time {
fmt.Println(from, to)
days := int(to.Sub(from).Hours()/24 + 1)
times := make([]time.Time, days)
@ -234,12 +290,12 @@ func daysRange(from, to time.Time) []time.Time {
times[i] = copy.AddDate(0, 0, i)
}
fmt.Println(times)
return times
}
func (a alarm) determineTime(dts []dateTime, params map[string]int, day time.Time) (*string, error) {
// determineTime parsed the date expressions of each given dateTime, with the given
// params. The value of the first dateTime that matches is returned.
func determineTime(dts []dateTime, params expression.Params) (*string, error) {
for _, dt := range dts {
result, err := dt.expr.Eval(params)
if err != nil {
@ -251,13 +307,14 @@ func (a alarm) determineTime(dts []dateTime, params map[string]int, day time.Tim
}
return &dt.Time, nil
}
return nil, nil
}
func (a alarm) determineBehaviour(dbs []dateBehaviour, params map[string]int) (*string, error) {
// determineBehaviour parsed the date expressions of each given dateBehaviour, with
// the given params. The name of the first dateBehaviour that matches is returned.
func determineBehaviour(dbs []dateBehaviour, params expression.Params) (*string, error) {
for _, db := range dbs {
result, err := db.expr.Eval(params)
if err != nil {
@ -273,3 +330,6 @@ func (a alarm) determineBehaviour(dbs []dateBehaviour, params map[string]int) (*
return nil, nil
}
func (b behaviour) execute() {
}

26
expression/structures.go

@ -5,6 +5,8 @@ import (
"fmt"
)
type Params map[string]int
// 'Raw' types used between translating the JSON string to a expression using interface type node which can be evaluated.
type rawExpression struct {
@ -32,7 +34,7 @@ func (expr Expression) Expression() node {
return expr.expression
}
func (expr Expression) Eval(params map[string]int) (bool, error) {
func (expr Expression) Eval(params Params) (bool, error) {
if expr.expression == nil {
return false, fmt.Errorf("expession is nil")
}
@ -41,21 +43,21 @@ func (expr Expression) Eval(params map[string]int) (bool, error) {
}
type node interface {
eval(params map[string]int) (bool, error)
eval(params Params) (bool, error)
}
type operator []node
type comparison struct {
parent string
data map[string]int
data Params
}
// evalInt is called by 'aliases' of comparison (equals, smallerThan, greaterThanOrEquals).
// params are the correct/current values of keys that can be checked for.
// The typeStr should contain the comparison alias type used for displaying error messages.
// callback is the function that should perform the comparison itself and return the resulting boolean.
func (c comparison) evalInt(params map[string]int, typeStr string, callback func(int, int) bool) (bool, error) {
func (c comparison) evalInt(params Params, typeStr string, callback func(int, int) bool) (bool, error) {
for key, value := range c.data {
if dataVal, found := params[key]; found {
result := callback(dataVal, value)
@ -91,7 +93,7 @@ func (c comparison) evalInt(params map[string]int, typeStr string, callback func
type and operator
func (a and) eval(params map[string]int) (bool, error) {
func (a and) eval(params Params) (bool, error) {
if len(a) == 0 {
return false, fmt.Errorf("no expressions in \"and\" expression")
}
@ -113,7 +115,7 @@ func (a and) eval(params map[string]int) (bool, error) {
type or operator
func (o or) eval(params map[string]int) (bool, error) {
func (o or) eval(params Params) (bool, error) {
if len(o) == 0 {
return false, fmt.Errorf("no expressions in \"or\" expression")
}
@ -137,7 +139,7 @@ func (o or) eval(params map[string]int) (bool, error) {
type not []node
func (n not) eval(params map[string]int) (bool, error) {
func (n not) eval(params Params) (bool, error) {
if len(n) == 0 {
return false, fmt.Errorf("no expression in \"not\" expression")
}
@ -160,7 +162,7 @@ func (n not) eval(params map[string]int) (bool, error) {
type equals comparison
func (eq equals) eval(params map[string]int) (bool, error) {
func (eq equals) eval(params Params) (bool, error) {
return comparison(eq).evalInt(params, "equals", func(key, value int) bool {
return key == value
})
@ -168,7 +170,7 @@ func (eq equals) eval(params map[string]int) (bool, error) {
type greaterThan comparison
func (gt greaterThan) eval(params map[string]int) (bool, error) {
func (gt greaterThan) eval(params Params) (bool, error) {
return comparison(gt).evalInt(params, "greaterThan", func(key, value int) bool {
return key > value
})
@ -176,7 +178,7 @@ func (gt greaterThan) eval(params map[string]int) (bool, error) {
type greaterThanOrEqual comparison
func (gt greaterThanOrEqual) eval(params map[string]int) (bool, error) {
func (gt greaterThanOrEqual) eval(params Params) (bool, error) {
return comparison(gt).evalInt(params, "greaterThanOrEqual", func(key, value int) bool {
return key >= value
})
@ -184,7 +186,7 @@ func (gt greaterThanOrEqual) eval(params map[string]int) (bool, error) {
type smallerThan comparison
func (st smallerThan) eval(params map[string]int) (bool, error) {
func (st smallerThan) eval(params Params) (bool, error) {
return comparison(st).evalInt(params, "smallerThan", func(key, value int) bool {
return key < value
})
@ -192,7 +194,7 @@ func (st smallerThan) eval(params map[string]int) (bool, error) {
type smallerThanOrEqual comparison
func (st smallerThanOrEqual) eval(params map[string]int) (bool, error) {
func (st smallerThanOrEqual) eval(params Params) (bool, error) {
return comparison(st).evalInt(params, "smallerThanOrEqual", func(key, value int) bool {
return key <= value
})

67
main.go

@ -0,0 +1,67 @@
package main
import (
"fmt"
"log"
"time"
)
func main() {
cfg, isNew, err := loadConfig("config.json")
if err != nil {
log.Fatal(err)
return
}
if isNew {
log.Fatal("Is new")
return
}
stops, err := refreshAlarms(cfg, nil)
// TODO:
// - Things that change parsedAlarms should call refreshAlarms.
// - Also, call refreshAlarms every once in a while I think.
_ = stops
}
func cancelAlarms(stops []chan struct{}) {
for _, s := range stops {
select {
case s <- struct{}{}:
default:
}
}
}
func refreshAlarms(cfg config, old []chan struct{}) ([]chan struct{}, error) {
cancelAlarms(old)
var stops []chan struct{}
now := time.Now()
parsed, err := cfg.alarmsFor(now, now.AddDate(0, 0, 1))
if err != nil {
return stops, err
}
for _, p := range parsed {
sub := p.time.Sub(now)
fmt.Println(sub)
stop := make(chan struct{})
go func() {
tick := time.NewTicker(sub)
select {
case <-stop:
tick.Stop()
return
case <-tick.C:
fmt.Println("Execute behaviour", p.behaviour)
p.behaviour.execute()
}
}()
}
return stops, nil
}
Loading…
Cancel
Save