From: Arjen Baart Date: Wed, 17 Jun 2020 06:43:54 +0000 (+0200) Subject: Calculate event recurrance X-Git-Url: http://www.andromeda.nl/gitweb/?a=commitdiff_plain;h=dc282215a64b51dd6e19062414ee2955374b2878;p=wakeup.git Calculate event recurrance --- diff --git a/configure.ac b/configure.ac index 081ba58..8922558 100644 --- a/configure.ac +++ b/configure.ac @@ -6,6 +6,7 @@ AC_INIT([wakeup], [0.1], [arjen@andromeda.nl]) AC_CONFIG_SRCDIR([src/sunrise.cpp]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign]) +AC_LANG(C++) # Checks for programs. AC_PROG_CXX diff --git a/doc/design.xml b/doc/design.xml index e531d81..6dea068 100644 --- a/doc/design.xml +++ b/doc/design.xml @@ -218,6 +218,33 @@ + + Hardware + + + + + + + + +IO pin assignments: + + Connector pinRPi IOFunction + 3GPIO 2Light switch + 5GPIO 3Red PWM + 7GPIO 4White PWM + 8GPIO 14Green PWM + 10GPIO 15Blue PWM + 35GPIO 19Curtain open out + 36GPIO 16Curtain open in + 37GPIO 26Curtain close out + 38GPIO 20Curtain close in +
+
+ +
+ Modules @@ -373,12 +400,19 @@ When the next event does happen, meaning the time of the occurance is the curren Each action can for example be a change in lights or the opening or closing of curtains. Event methods: -FromXML -ToXML -add_recurrance -next_occurance -add_action -execute_actions + +FromXML +ToXML +add_recurrance +next_occurance +add_action +execute_actions + + +Calculating the next occurance after a certain time is done by adding the number of days or months untill the given time +is passed or untill the end of the recurrence is reached. A recurrence pattern of weeks is converted to a pattern of n * 7 days. +The end of the recurrence is specified as an end time or as a maximum number of occurences. +A recurrence pattern specified as a set of weekdays requires a different calculation. diff --git a/doc/wakeup-classes.svg b/doc/wakeup-classes.svg index 054b46d..379d44f 100644 --- a/doc/wakeup-classes.svg +++ b/doc/wakeup-classes.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.86772068" - inkscape:cx="140.42641" - inkscape:cy="579.0815" + inkscape:zoom="1.2271424" + inkscape:cx="233.1688" + inkscape:cy="743.6865" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -91,14 +91,14 @@ action + x="52.182652" + y="41.636749" + style="stroke-width:0.26458332px">action_sequnce number + style="text-align:start;text-anchor:start;stroke-width:0.26458332px">maximum_number execute + style="stroke-width:0.26458332px">execute() + FromXML() + ToXML() + add_action() + next_occurance() + parameters diff --git a/src/event.cpp b/src/event.cpp index a2f10d8..dde8372 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -3,6 +3,7 @@ void Event::FromXML(xml_element x) { std::vector elems; + std::vector recur; elems = x["start"]; start_time = elems[0].content(); @@ -12,6 +13,42 @@ void Event::FromXML(xml_element x) { add_action(elems[i].content()); } + + recur = x["recurrance"]; + if (recur.size() != 0) + { + String number; + + elems = recur[0]["pattern"]; + pattern = elems[0].content(); + elems = recur[0]["number"]; + number = elems[0].content(); + + // Initialize the recurrance interval according to pattern + + if (pattern == "weekdays") + { + // The number element is a comma separated list of day numbers + + SuperString wkdays = number.split(","); + for (int i = 0; i < ~wkdays; i++) + { + weekdays.insert(int(wkdays[i])); + } + } + else if (pattern == "days") + { + recurrance_interval = UTC(date(long(number), 0, 0), hour(0)); + } + else if (pattern == "months") + { + recurrance_interval = UTC(date(0, long(number), 0), hour(0)); + } + else if (pattern == "years") + { + recurrance_interval = UTC(date(0, 0, long(number)), hour(0)); + } + } } String Event::ToXML(void) @@ -27,6 +64,42 @@ String Event::ToXML(void) xml_text += start_time.format(); xml_text += "\n"; + if (pattern != "none") + { + xml_text += " "; + xml_text += pattern; + xml_text += "\n"; + xml_text += " "; + if (pattern == "weekdays") + { + std::set::iterator it; + for (it = weekdays.begin(); it != weekdays.end(); it++) + { + if (it != weekdays.begin()) + { + xml_text += ","; + } + xml_text += *it; + } + } + else + { + if (date(recurrance_interval).Year() != 0) + { + xml_text += int(date(recurrance_interval).Year()); + } + if (date(recurrance_interval).Month() != 0) + { + xml_text += int(date(recurrance_interval).Month()); + } + if (date(recurrance_interval).Day() != 0) + { + xml_text += int(date(recurrance_interval).Day()); + } + } + xml_text += "\n"; + } + for (act = sequence.begin(); act != sequence.end(); act++) { xml_text += " "; @@ -65,6 +138,40 @@ void Event::add_action(String command) } } +UTC Event::next_occurance(UTC after) +{ + UTC next; + + //std::cout << "Next occ after " << after << ", pattern = " << pattern << "\n"; + next = start_time; + + if (pattern != "none") + { + while (next <= after) + { + if (pattern == "weekdays") + { + while (next <= after || weekdays.find(date(next).WeekDay()) == weekdays.end()) + { + next += UTC(date(1, 0, 0), hour(0)); + } + } + else + { + next += recurrance_interval; + } + } + } + + if (after >= next) + { + // The last event has already passed + next = UTC(date(0,0,0), hour(0)); + } + + return next; +} + std::list read_alarms(const char filename[]) { std::list collected_alarms; diff --git a/src/event.h b/src/event.h index b4cdd27..72f4f3a 100644 --- a/src/event.h +++ b/src/event.h @@ -1,5 +1,6 @@ #include +#include #include #include @@ -9,19 +10,33 @@ class Event { String label; UTC start_time; + + String pattern; // for recurrance + UTC recurrance_interval; + std::set weekdays; + std::list sequence; public: + Event() + { + label = ""; + pattern = "none"; + } + Event(String lbl) { label = lbl; + pattern = "none"; } void FromXML(xml_element x); String ToXML(void); void add_action(String command); + + UTC next_occurance(UTC after); }; std::list read_alarms(const char *filename); diff --git a/test/Makefile.am b/test/Makefile.am index e1a2aa4..6e8b0fe 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,8 @@ TESTS = lightctrl lightctrl-oor lightctrl-fade $(check_PROGRAMS) check_output AM_CPPFLAGS = -I../src LDADD = -lTachyon -lACL -check_PROGRAMS = wakeup_load_events sunrise_test +check_PROGRAMS = wakeup_load_events sunrise_test event_recurrance wakeup_load_events_SOURCES = wakeup_load_events.cpp ../src/event.cpp +event_recurrance_SOURCES = event_recurrance.cpp ../src/event.cpp sunrise_test_SOURCES = sunrise_test.cpp ../src/sunrise.cpp diff --git a/test/event_recurrance.cpp b/test/event_recurrance.cpp new file mode 100644 index 0000000..3f12c0e --- /dev/null +++ b/test/event_recurrance.cpp @@ -0,0 +1,96 @@ +#include "event.h" +#include "assert.h" + +int main() +{ + std::list set_alarms; + std::list::iterator alarm; + UTC time_to_check; + UTC time_expected; + UTC next_time; + + set_alarms = read_alarms("wakeup-02.xml"); + + // The first alarm at 2020-03-22 06:00 does not recurr + + + alarm = set_alarms.begin(); + + // Check before the alarm time + time_to_check = UTC(date(20, 3, 2020), hour(2, 2, 2)); + time_expected = UTC(date(22, 3, 2020), hour(6, 0, 0)); + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n"; + assert(next_time == time_expected); + + // Check after the alarm time + time_to_check = UTC(date(22, 3, 2020), hour(6, 2, 2)); + time_expected = UTC(date(0, 0, 0), hour(0, 0, 0)); + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // The second alarm at 2020-05-24 08:00 recurs weekly + + alarm++; + + // Before the first occurance + + time_to_check = UTC(date(20, 5, 2020), hour(2, 2, 2)); + time_expected = UTC(date(24, 5, 2020), hour(8, 0, 0)); + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // After the first occurance + + time_to_check = UTC(date(24, 5, 2020), hour(8, 2, 2)); + time_expected = UTC(date(31, 5, 2020), hour(8, 0, 0)); + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // Much later after the first occurance + + time_to_check = UTC(date(25, 11, 2020), hour(2, 2, 2)); + time_expected = UTC(date(29, 11, 2020), hour(8, 0, 0)); + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // The third alarm at 2020-04-01 07:00 (Wednesday) recurs every Tuesday, Thursday and Friday + + alarm++; + + // Before the first occurance + + time_to_check = UTC(date(20, 3, 2020), hour(2, 2, 2)); + time_expected = UTC(date( 2, 4, 2020), hour(7, 0, 0)); // the first Thursday + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // After the first occurance + + time_to_check = UTC(date( 2, 4, 2020), hour(7, 2, 2)); + time_expected = UTC(date( 3, 4, 2020), hour(7, 0, 0)); // the next Friday + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + + // After the second occurance + + time_to_check = UTC(date( 3, 4, 2020), hour(7, 2, 2)); + time_expected = UTC(date( 7, 4, 2020), hour(7, 0, 0)); // the next Tuesday + next_time = alarm->next_occurance(time_to_check); + std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n"; + std::cout.flush(); + assert(next_time == time_expected); + +} diff --git a/test/event_recurrance.exp b/test/event_recurrance.exp new file mode 100644 index 0000000..a5dbe76 --- /dev/null +++ b/test/event_recurrance.exp @@ -0,0 +1,9 @@ +Next alarm after 20-03-2020 02:02:02 will be 22-03-2020 06:00:00 +Next alarm after 22-03-2020 06:02:02 will be 00-00-0 00:00:00 +Next alarm after 20-05-2020 02:02:02 will be 24-05-2020 08:00:00 +Next alarm after 24-05-2020 08:02:02 will be 31-05-2020 08:00:00 +Next alarm after 25-11-2020 02:02:02 will be 29-11-2020 08:00:00 +Next alarm after 20-03-2020 02:02:02 will be 02-04-2020 07:00:00. Expected 02-04-2020 07:00:00 +Next alarm after 02-04-2020 07:02:02 will be 03-04-2020 07:00:00. Expected 03-04-2020 07:00:00 +Next alarm after 03-04-2020 07:02:02 will be 07-04-2020 07:00:00. Expected 07-04-2020 07:00:00 +PASS event_recurrance (exit status: 0) diff --git a/test/wakeup-02.xml b/test/wakeup-02.xml new file mode 100644 index 0000000..5e765a4 --- /dev/null +++ b/test/wakeup-02.xml @@ -0,0 +1,23 @@ + + + + 2020-03-22 06:00 + lightcontrol -r 100 -f 200 + + + 2020-05-24 08:00 + + days + 7 + + lightcontrol -g 100 -f 200 + + + 2020-04-02 07:00 + + weekdays + 2,4,5 + + lightcontrol -g 100 -f 200 + + diff --git a/test/wakeup_load_events.cpp b/test/wakeup_load_events.cpp index 219bd1b..6e7a7d0 100644 --- a/test/wakeup_load_events.cpp +++ b/test/wakeup_load_events.cpp @@ -7,4 +7,8 @@ int main() set_alarms = read_alarms("wakeup-01.xml"); std::cout << alarms_to_XML(set_alarms); + + set_alarms = read_alarms("wakeup-02.xml"); + + std::cout << alarms_to_XML(set_alarms); } diff --git a/test/wakeup_load_events.exp b/test/wakeup_load_events.exp index 1acff55..b277957 100644 --- a/test/wakeup_load_events.exp +++ b/test/wakeup_load_events.exp @@ -9,4 +9,23 @@ lightcontrol -r 0 -g 0 -w 0 -f 200 + + + + 2020-03-22 06:00:00 + lightcontrol -r 100 -f 200 + + + 2020-05-24 08:00:00 + days + 7 + lightcontrol -g 100 -f 200 + + + 2020-04-02 07:00:00 + weekdays + 2,4,5 + lightcontrol -g 100 -f 200 + + PASS wakeup_load_events (exit status: 0)