diff --git a/primarysched.go b/primarysched.go index 359eed9..8cd058c 100644 --- a/primarysched.go +++ b/primarysched.go @@ -13,8 +13,26 @@ import ( type primarySched struct { lower Trigger higher Trigger + // whenever the schedule gets in a conflict the LHS induces increment in the RHS conflict + conflicts int + delay int // increasing this will increment the preceedence since this will be applied after a delay } +func (ps *primarySched) Conflicts() int { + return ps.conflicts +} +func (ps *primarySched) AddConflict() Schedule { + ps.conflicts++ + return ps +} +func (ps *primarySched) Delay() int { + return ps.delay +} +func (ps *primarySched) AddDelay(prior int) Schedule { + ps.delay = prior + ps.delay++ + return ps +} func (ps *primarySched) Triggers() (Trigger, Trigger) { return ps.lower, ps.higher } @@ -29,7 +47,7 @@ func (ps *primarySched) Close() { log.Infof("%s Schedule is now closing", ps) } func (ps *primarySched) String() string { - return fmt.Sprintf("%s - %s", tmStrFromUnixSecs(ps.lower.At()), tmStrFromUnixSecs(ps.higher.At())) + return fmt.Sprintf("%s - %s %v %v", tmStrFromUnixSecs(ps.lower.At()), tmStrFromUnixSecs(ps.higher.At()), ps.lower.RelayIDs(), ps.higher.RelayIDs()) } // NearFarTrigger : in context of the current time, this helps to get the triggers that are near or far @@ -72,9 +90,13 @@ func (ps *primarySched) overlapsWith(another Schedule) bool { } else { min, max = hfdur2, hfdur1 } - if (mdptdis > (hfdur1 + hfdur2)) || ((mdptdis + min) < max) { + if outside, inside := (mdptdis > (hfdur1 + hfdur2)), ((mdptdis + min) < max); outside || inside { // case when the schedules are clearing and not interferring with one another // either one schedule is inside the other or on one side + if inside { + // Here we would want to adjust the preceedence too. + another.AddDelay(ps.Delay()) + } return false } // all other cases the schedules are either partially/exactly overlapping diff --git a/rlystate.go b/rlystate.go index b8df66b..dc8c050 100644 --- a/rlystate.go +++ b/rlystate.go @@ -1,7 +1,5 @@ package scheduling -import "fmt" - // RelayState : this is just to hold the state of relay with the identification of the relay // relay should be identified with the same name as required by srvrelay // Storing this as just a byte is also possible, but that is when we want the relay module to work as a block, not when we want to operate on individual relays @@ -39,39 +37,3 @@ func (rs *RelayState) ID() string { func NewRelayState(id string) *RelayState { return &RelayState{byte(0), id} } - -// ================================== Json Relay state is for file reads ============================ -// Making a relay state from a json file - -// JSONRelayState : relaystate but in json format -type JSONRelayState struct { - ON string `json:"on"` - OFF string `json:"off"` - IDs []string `json:"ids"` - Primary bool `json:"primary"` -} - -// ToSchedule : reads from json and pumps up a schedule -// this saves you the trouble of making a schedule via code, -// from a json file it can read up a relaystate and convert that to schedule -func (jrs *JSONRelayState) ToSchedule() (Schedule, error) { - onTm, err := TimeStr(jrs.ON).ToElapsedTm() - if err != nil { - return nil, fmt.Errorf("Failed to read ON time for schedule") - } - offTm, err := TimeStr(jrs.OFF).ToElapsedTm() - if err != nil { - return nil, fmt.Errorf("Failed to read OFF time for schedule") - } - offs := []*RelayState{} - ons := []*RelayState{} - for _, id := range jrs.IDs { - offs = append(offs, &RelayState{byte(0), id}) - ons = append(ons, &RelayState{byte(1), id}) - } - trg1, trg2 := NewTrg(offTm, offs...), NewTrg(onTm, ons...) - if jrs.Primary { - return NewPrimarySchedule(trg1, trg2) - } - return NewPatchSchedule(trg1, trg2) -} diff --git a/sch_test.go b/sch_test.go index 4b109c8..ccc2451 100644 --- a/sch_test.go +++ b/sch_test.go @@ -44,7 +44,6 @@ func TestScheduleRead(t *testing.T) { return } t.Log("Now logging the JSONRelayState") - t.Log(c) for _, s := range c.Schedules { sched, err := s.ToSchedule() if err != nil { @@ -54,3 +53,28 @@ func TestScheduleRead(t *testing.T) { t.Log(sched) } } + +func TestScheduleNrFrTrigger(t *testing.T) { + jsonFile, _ := os.Open("test_sched.json") + // Reading bytes from the file and unmarshalling the same to struct values + bytes, _ := ioutil.ReadAll(jsonFile) + jsonFile.Close() // since this returns a closure, the call to this cannot be deferred + type conf struct { + Schedules []JSONRelayState `json:"schedules"` + } + c := conf{} + json.Unmarshal(bytes, &c) + for _, s := range c.Schedules { + sched, err := s.ToSchedule() + if err != nil { + t.Errorf("Error coverting to schedule objecy %s", err) + return + } + t.Logf("Schedule: %s", sched) + nr, fr, pre, post := sched.NearFarTrigger(elapsedSecondsNow()) + t.Logf("Near trigger: %s", nr) + t.Logf("Far trigger: %s", fr) + t.Logf("Pre sleep: %d", pre) + t.Logf("Post sleep: %d", post) + } +} diff --git a/sched.go b/sched.go index 3348743..cfdc5fd 100644 --- a/sched.go +++ b/sched.go @@ -1,5 +1,9 @@ package scheduling +/*All the public facing interfaces here +Using this package from client code should be easy if you get your head around this file +*/ + import ( "fmt" ) @@ -11,6 +15,10 @@ type Schedule interface { NearFarTrigger(elapsed int) (Trigger, Trigger, int, int) ConflictsWith(another Schedule) bool Midpoint() int + Delay() int + AddDelay(prior int) Schedule + Conflicts() int + AddConflict() Schedule Close() Apply(ok, cancel chan interface{}, send chan []byte, err chan error) } @@ -28,20 +36,52 @@ func sortTriggers(trg1, trg2 Trigger) (l, h Trigger, e error) { return } -// NewPrimarySchedule : makes a new TriggeredSchedul, will take 2 triggers -func NewPrimarySchedule(trg1, trg2 Trigger) (Schedule, error) { +// NewSchedule : makes a new TriggeredSchedul, will take 2 triggers +func NewSchedule(trg1, trg2 Trigger, primary bool) (Schedule, error) { l, h, err := sortTriggers(trg1, trg2) if err != nil { return nil, err } - return &primarySched{l, h}, nil + if !trg1.Intersects(trg2, true) || trg1.Coincides(trg2) { + // When triggers are paired in a schedule they have to be intersecting and not coincident + return nil, fmt.Errorf("%s-%s Triggers for the schedule are either not exactly intersecting or are coinciding", trg1, trg2) + } + if primary { + return &primarySched{l, h}, nil + } + return &patchSchedule{&primarySched{l, h}}, nil + } -// NewPatchSchedule : this makes a new patch schedule object -func NewPatchSchedule(trg1, trg2 Trigger) (Schedule, error) { - l, h, err := sortTriggers(trg1, trg2) +// JSONRelayState : relaystate but in json format +// ================================== Json Relay state is for file reads ============================ +// Making a relay state from a json file +type JSONRelayState struct { + ON string `json:"on"` + OFF string `json:"off"` + IDs []string `json:"ids"` + Primary bool `json:"primary"` +} + +// ToSchedule : reads from json and pumps up a schedule +// this saves you the trouble of making a schedule via code, +// from a json file it can read up a relaystate and convert that to schedule +func (jrs *JSONRelayState) ToSchedule() (Schedule, error) { + onTm, err := TimeStr(jrs.ON).ToElapsedTm() if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to read ON time for schedule") } - return &patchSchedule{&primarySched{l, h}}, nil + offTm, err := TimeStr(jrs.OFF).ToElapsedTm() + if err != nil { + return nil, fmt.Errorf("Failed to read OFF time for schedule") + } + offs := []*RelayState{} + ons := []*RelayState{} + for _, id := range jrs.IDs { + offs = append(offs, &RelayState{byte(0), id}) + ons = append(ons, &RelayState{byte(1), id}) + } + trg1, trg2 := NewTrg(offTm, offs...), NewTrg(onTm, ons...) + return NewSchedule(trg1, trg2, jrs.Primary) + } diff --git a/test_sched.json b/test_sched.json index e3493be..34aea89 100644 --- a/test_sched.json +++ b/test_sched.json @@ -2,8 +2,8 @@ "schedules": [ {"on":"06:30 PM", "off":"06:30 AM","primary":true, "ids":["IN1","IN2","IN3","IN4"]}, {"on":"04:30 PM", "off":"05:29 PM","primary":false, "ids":["IN1"]}, - {"on":"02:30 PM", "off":"04:00 PM","primary":false, "ids":["IN1"]}, - {"on":"04:00 PM", "off":"04:45 PM","primary":false, "ids":["IN1"]}, + {"on":"06:40 PM", "off":"08:40 PM","primary":false, "ids":["IN1"]}, + {"on":"03:00 PM", "off":"04:45 PM","primary":false, "ids":["IN1"]}, {"on":"04:00 PM", "off":"04:45 PM","primary":false, "ids":["IN2"]} ] } \ No newline at end of file diff --git a/trg.go b/trg.go index 4af3027..85b63c6 100644 --- a/trg.go +++ b/trg.go @@ -2,6 +2,7 @@ package scheduling import ( "encoding/json" + "fmt" ) // rlyStateTrg : trigger is just a timestamp and collection of relay state @@ -25,6 +26,9 @@ type Trigger interface { Coincides(other Trigger) bool } +func (tr *rlyStateTrg) String() string { + return fmt.Sprintf("%s, %v", tmStrFromUnixSecs(tr.at), tr.RelayIDs()) +} func (tr *rlyStateTrg) RelayIDs() ComparableSlice { result := ComparableSlice{} for _, item := range tr.rs {