From: Arjen Baart Date: Thu, 25 Mar 2021 07:37:47 +0000 (+0100) Subject: Make multi-threaded X-Git-Url: http://www.andromeda.nl/gitweb/?a=commitdiff_plain;h=cc2b0566dd6107b85097f574c2cba8968ea53c18;p=wakeup.git Make multi-threaded --- diff --git a/depends b/depends new file mode 100644 index 0000000..1840243 --- /dev/null +++ b/depends @@ -0,0 +1 @@ +xmldoc libacl Tachyon diff --git a/doc/design.xml b/doc/design.xml index 6dea068..866f8f2 100644 --- a/doc/design.xml +++ b/doc/design.xml @@ -370,16 +370,16 @@ depending on the times of sunrise and sunset. The wakeup times are specified like calendar events, possibly with a recurrence pattern and an end date. Elements in an event are: - Label - Start time - Recurrence pattern - Number of recurrences - End time - Action sequence + Label + Start time + Recurrence pattern + Number of recurrences + End time + Action sequence A recurrence pattern can be specified with a number of days, weeks or months as well as a set of weekdays. A set of weekdays implies the recurrence will be weekly. -The action for an event can be a light sequence or a curtain control. +The action for an event can be, for example, a light sequence or a curtain control. When an event triggers, a sequence of actions is executed. @@ -390,6 +390,7 @@ Possible actions: Cancel an event Create a sunrise or sunset event Wait a while + Exit diff --git a/doc/wakeup-classes.svg b/doc/wakeup-classes.svg index d75d0c9..e3d67b5 100644 --- a/doc/wakeup-classes.svg +++ b/doc/wakeup-classes.svg @@ -17,7 +17,22 @@ inkscape:version="0.92.1 r15371" sodipodi:docname="wakeup-classes.svg"> + id="defs4521"> + + + + @@ -53,121 +68,30 @@ inkscape:groupmode="layer" id="layer1"> - - Event - label - action_sequnce - start_time - recurrence - Lightstep - lightlevel - fadetime pattern + + + Action - - - execute() FromXML() - ToXML() - add_action() - next_occurance() - parameters + + + + Event + label + action_sequnce + start_time + recurrence + FromXML() + ToXML() + add_action() + next_occurance() + run_sequence() + + + + + + Curtainstep + + + + + Sleepstep + + + + + Exitstep + + + + run_sequence() + xml:space="preserve">command_line() diff --git a/src/action.h b/src/action.h index c1729bb..f366737 100644 --- a/src/action.h +++ b/src/action.h @@ -57,6 +57,21 @@ class Curtainstep : public Action } }; +class Cancelstep : public Action +{ + virtual String command_line(void) + { + String cmd("cancel "); + cmd += parameters; + return cmd; + } + + virtual int execute() + { + return 0; + } +}; + class Exitstep : public Action { virtual String command_line(void) diff --git a/src/event.cpp b/src/event.cpp index 78f6655..1c81ec4 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,3 +1,4 @@ + #include #include "event.h" @@ -13,9 +14,11 @@ void Event::FromXML(xml_element x) std::vector recur; std::map attr; + attr = x.attributes(); label = attr[String("id")]; + seq = new Sequence(label); elems = x["start"]; start_time = elems[0].content(); @@ -63,6 +66,21 @@ void Event::FromXML(xml_element x) } } +String Sequence::ToXML(void) +{ + std::list::iterator act; + String xml_text; + + for (act = sequ.begin(); act != sequ.end(); act++) + { + xml_text += " "; + xml_text += (*act)->command_line(); + xml_text += "\n"; + } + + return xml_text; +} + String Event::ToXML(void) { String xml_text; @@ -112,22 +130,89 @@ String Event::ToXML(void) xml_text += "\n"; } + xml_text += seq->ToXML(); + +/* for (act = sequence.begin(); act != sequence.end(); act++) { xml_text += " "; xml_text += (*act)->command_line(); xml_text += "\n"; } +*/ xml_text += " \n"; return xml_text; } +void Sequence::add_action(String command) +{ + String to_execute, parameters; + int separation; + + separation = command.index(' '); + if (separation > 0) + { + to_execute = command(0, separation); + parameters = command << separation + 1; + } + else + { + to_execute = command; + } + + if (to_execute == "lightcontrol") + { + Lightstep *act; + act = new Lightstep; + + act->set_parameters(parameters); + sequ.push_back(act); + } + else if (to_execute == "curtain") + { + Curtainstep *act; + act = new Curtainstep; + + act->set_parameters(parameters); + sequ.push_back(act); + } + else if (to_execute == "sleep") + { + Sleepstep *act; + act = new Sleepstep; + + act->set_parameters(parameters); + sequ.push_back(act); + } + else if (to_execute == "cancel") + { + Cancelstep *act; + act = new Cancelstep; + + act->set_parameters(parameters); + sequ.push_back(act); + } + else if (to_execute == "exit") + { + Exitstep *act; + act = new Exitstep; + sequ.push_back(act); + } + else + { + Log << "Action " + to_execute + " not recognized"; + } +} + + void Event::add_action(String command) { String to_execute, parameters; int separation; + seq->add_action(command); + separation = command.index(' '); if (separation > 0) { @@ -163,6 +248,14 @@ void Event::add_action(String command) act->set_parameters(parameters); sequence.push_back(act); } + else if (to_execute == "cancel") + { + Cancelstep *act; + act = new Cancelstep; + + act->set_parameters(parameters); + sequence.push_back(act); + } else if (to_execute == "exit") { Exitstep *act; @@ -175,15 +268,41 @@ void Event::add_action(String command) } } -void Event::run_sequence() +void _run_(Sequence * sequence) { std::list::iterator cmd; - for (cmd = sequence.begin(); cmd != sequence.end(); cmd++) + cmd = sequence->sequ.begin(); + while (!sequence->cancel && cmd != sequence->sequ.end()) { - Log << "Executing " + (*cmd)->command_line(); + Log << "[" + sequence->label + "] Executing " + (*cmd)->command_line(); (*cmd)->execute(); + //Log << "[" + sequence->label + "] Cancel state = " + String(sequence->cancel); + cmd++; } + //Log << "[" + sequence->label + "] Thread finished."; +} + +void Sequence::run() +{ + cancel = false; + + runner = std::thread(_run_, this); + runner.detach(); + //Log << "[" + label + "] Thread started."; + +} + +void Sequence::stop(void) +{ + Log << "[" + label + "] Stopping sequence "; + cancel = true; +} + +Sequence *Event::run_sequence() +{ + seq->run(); + return seq; } UTC Event::next_occurance(UTC after, Location loc) diff --git a/src/event.h b/src/event.h index 00ea1a1..37b8617 100644 --- a/src/event.h +++ b/src/event.h @@ -1,12 +1,41 @@ #include #include +#include + #include #include #include "location.h" #include "action.h" +class Sequence +{ + String label; + std::list sequ; + + std::thread runner; + + bool cancel; + +public: + Sequence (String lbl) + { + label = lbl; + } + + void add_action(String command); + String ToXML(void); + String id() + { + return label; + } + + void run(void); + void stop(void); + friend void _run_(Sequence * sequence); +}; + class Event { String label; @@ -17,6 +46,9 @@ class Event std::set weekdays; std::list sequence; + Sequence *seq; + + //std::thread running; public: @@ -46,7 +78,7 @@ public: } void add_action(String command); - void run_sequence(); + Sequence *run_sequence(); UTC next_occurance(UTC after, Location loc); }; diff --git a/src/lightcontrol.cpp b/src/lightcontrol.cpp index f5c0100..6cf1f2e 100644 --- a/src/lightcontrol.cpp +++ b/src/lightcontrol.cpp @@ -10,6 +10,8 @@ #include #include #include +#include + #include #include #include @@ -216,12 +218,26 @@ std::ostringstream report_lights(std::vector lights) * Fade the lights from the start level to the end level in the specified fade time. */ -void lightfade(std::vector start_lvl, std::vector end_lvl, int fade) +void lightfade(std::vector start_lvl, std::vector end_lvl, int fade, double acc) { int maximum_difference; Tachyon timebase; + std::ifstream otherpidfile("lightcontrol.pid"); + if (otherpidfile.good()) + { + pid_t otherpid; + + otherpidfile >> otherpid; + //std::cerr << "Another lightcontrol is running, pid = " << otherpid << "\n"; + kill(otherpid, SIGTERM); + } + + std::ofstream pidfile("lightcontrol.pid"); + pidfile << getpid() << "\n"; + pidfile.close(); + std::ofstream runfile("lightcontrol.run"); runfile << timebase.name() << "\n"; runfile.close(); @@ -243,6 +259,8 @@ void lightfade(std::vector start_lvl, std::vector end_ float step_time = float(fade) / maximum_difference; float time_counter = 0; + timebase.accellerate(acc); + while (time_counter < fade) { std::vector cur_lvl = start_lvl; @@ -267,6 +285,7 @@ void lightfade(std::vector start_lvl, std::vector end_ } light_to_pwm(end_lvl); + remove("lightcontrol.pid"); remove("lightcontrol.run"); } @@ -305,6 +324,7 @@ int main(int argc, char *argv[]) log << start_message + parent_command(); int fade_time = 0; + double accelleration = 1.0; bool change_lights = false; pwm *signals; @@ -345,7 +365,7 @@ int main(int argc, char *argv[]) int option; - while ((option = getopt(argc, argv, "r:g:b:w:f:lvV")) != -1) + while ((option = getopt(argc, argv, "r:g:b:w:f:a:lvV")) != -1) { lvl_ptr p; @@ -376,6 +396,10 @@ int main(int argc, char *argv[]) fade_time = atoi(optarg); break; + case 'a': + accelleration = strtod(optarg, NULL); + break; + case 'l': p = find_level(lightlevels, RED); std::cout << p->level << " "; @@ -411,7 +435,7 @@ int main(int argc, char *argv[]) report << ", fading in " << fade_time << " seconds"; } log << report.str().c_str(); - lightfade(lightlevels, desired_levels, fade_time); + lightfade(lightlevels, desired_levels, fade_time, accelleration); } } diff --git a/src/lightswitch.c b/src/lightswitch.c index d23997f..38796f9 100644 --- a/src/lightswitch.c +++ b/src/lightswitch.c @@ -47,9 +47,16 @@ void setup_io(); #define SWITCH_IN 2 +#define FIRMASK 0x03 + +#define TEST_CYCLES (60 * 5) // 1 minute + +unsigned fir_record[TEST_CYCLES]; int main(int argc, char *argv[]) { + unsigned int fir; // A simple FIR filter + int old_state, new_state; struct timespec interval; @@ -62,14 +69,32 @@ int main(int argc, char *argv[]) // Initialize the IO pins. INP_GPIO(SWITCH_IN); - old_state = GET_GPIO(SWITCH_IN); + // Initialize to switched off state + fir = 0xffff; + old_state = 1; // Off - while (1) + int cycle = 0; + //while (1) + while (cycle < TEST_CYCLES) { - new_state = GET_GPIO(SWITCH_IN); + unsigned input = GET_GPIO(SWITCH_IN) == 0 ? 0 : 1; + + fir <<= 1; + fir |= input; + + //fprintf(stderr, "FIR = 0x%x\r", fir); + if ( (fir & FIRMASK) == 0) + { + new_state = 0; // On + } + if ( (fir & FIRMASK) == FIRMASK) + { + new_state = 1; // Off + } + if (new_state != old_state) { - //fprintf(stderr, "Changed state to %d\n", new_state); + //fprintf(stderr, "Changed state to %d\r", new_state); if (new_state == 0) { system("lightcontrol -r 100 -g 100 -b 100 -w 100"); @@ -81,9 +106,16 @@ int main(int argc, char *argv[]) old_state = new_state; } + fir_record[cycle++] = fir; + nanosleep(&interval, NULL); } + for (int i = 0; i < TEST_CYCLES; i++) + { + printf("FIR = 0x%08x\n", fir_record[i]); + } + return 0; } diff --git a/src/logging.cpp b/src/logging.cpp index e0ddd20..ad27e30 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -1,3 +1,5 @@ +#include + #include #include "logging.h" @@ -9,7 +11,8 @@ logstream & logstream::operator << (const char *msg) timestamp = Now(); - destination << timestamp << ": " << msg << "\n"; + //destination << timestamp << " [" << std::this_thread::get_id() << "]: " << msg << "\n"; + destination << timestamp << " : " << msg << "\n"; destination.flush(); return *this; @@ -21,7 +24,8 @@ logstream & logstream::operator << (const String &msg) timestamp = Now(); - destination << timestamp << ": " << msg << "\n"; + //destination << timestamp << " [" << std::this_thread::get_id() << "]: " << msg << "\n"; + destination << timestamp << " : " << msg << "\n"; destination.flush(); return *this; diff --git a/src/wakeup.cpp b/src/wakeup.cpp index 637d189..d5fbf0c 100644 --- a/src/wakeup.cpp +++ b/src/wakeup.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -25,8 +26,6 @@ std::list calculate_occurances(std::list alarms, Location loc) UTC next_time; next_time = alarm->next_occurance(UTC(Timebase.time()), loc); - //DEBUG - std::cout << "Next alarm after will be " << next_time << ".\n"; if ( next_time != UTC(date(0,0,0), hour(0))) { future_alarms.push_back(*alarm); @@ -90,13 +89,12 @@ int main() double longitude = Config.find_parameter("location", "longitude"); Location local(latitude, longitude); + Sequence *running = 0; while (true) { UTC this_moment = UTC(Timebase.time()); pending_alarms = calculate_occurances(set_alarms, local); - // DEBUG - //std::cout << alarms_to_XML(pending_alarms); // Find the next alarm @@ -116,12 +114,15 @@ int main() } UTC next_time = next_alarm.next_occurance(this_moment, local); - // DEBUG - //std::cout << "Sleep from " << this_moment << " until " << next_time << " (" << next_time - this_moment << " seconds)\n"; + //Log << "Next alarm " + next_alarm.id() + " will be at " + next_time.format(); if (Timebase.nanosleep(next_time - this_moment) == 0) { Log << "Alarm " + next_alarm.id() + " triggered at " + next_time.format(); - next_alarm.run_sequence(); + if (running != 0) + { + running->stop(); // TODO check for a cancel action + } + running = next_alarm.run_sequence(); } else { diff --git a/test/Makefile.am b/test/Makefile.am index 220c0d4..d99a522 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,8 @@ TESTS = lightctrl lightctrl-oor lightctrl-fade \ wakeup_load_events sunrise_test event_recurrance event_run_sequence \ - wakeup_winter sunset_winter \ + wakeup_winter sunset_winter wakeup_spring \ check_output +# wakeup_summer AM_CPPFLAGS = -I../src LDADD = -lTachyon -lACL diff --git a/test/sunset_alarms.xml b/test/sunset_alarms.xml index 4ff475d..44e14b3 100644 --- a/test/sunset_alarms.xml +++ b/test/sunset_alarms.xml @@ -15,7 +15,7 @@ days 1 - lightcontrol -b 50 -w 0 -f 9 + lightcontrol -b 50 -w 0 -f 900 -a 1000 2020-04-02 22:30 @@ -23,7 +23,7 @@ days 1 - lightcontrol -b 0 -f 18 + lightcontrol -b 0 -f 1800 -a 1000 2020-04-08 11:00 diff --git a/test/sunset_winter b/test/sunset_winter index 8f0e32b..340add5 100755 --- a/test/sunset_winter +++ b/test/sunset_winter @@ -19,7 +19,7 @@ sleep 1 read TACHYON_NAME weekdays 1,2,3,4,5 - lightcontrol -r 10 -f 2 - lightcontrol -g 10 -f 2 - lightcontrol -w 10 -f 2 + lightcontrol -r 10 -f 300 -a 1000 + lightcontrol -g 10 -f 300 -a 1000 + lightcontrol -w 10 -f 300 -a 1000 sunrise + cancel lightson_workdays curtain -o 2 -t - lightcontrol -r 0 -g 0 -w 0-f 2 + lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 weekdays 1,2,3,4,5 diff --git a/test/wakeup_spring b/test/wakeup_spring new file mode 100755 index 0000000..fe0ccaf --- /dev/null +++ b/test/wakeup_spring @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Test the wakeup sequence in spring. +# Sunrise happens a few minutes after wakeup time +# +set -m +PATH=../src:$PATH + +# Reset the lights and clear the log files + +lightcontrol -r 0 -g 0 -b 0 -w 0 +> lightcontrol.log +> wakeup.log + +ln -f -s wakeup_empty.xml wakeup_link.xml + +wakeup & +sleep 1 + +read TACHYON_NAME wakeup_spring.log.test + +diff wakeup_spring.log.test wakeup_spring.log.expect + +exit $? diff --git a/test/wakeup_spring.log.expect b/test/wakeup_spring.log.expect new file mode 100644 index 0000000..e7157fa --- /dev/null +++ b/test/wakeup_spring.log.expect @@ -0,0 +1,69 @@ + wakeup started + Reading alarms from wakeup_link.xml + SIGHUP received. + Reading alarms from wakeup_link.xml + Alarm lightson_workdays triggered at 2020-05-07 06:00:00 + [lightson_workdays] Executing lightcontrol -r 10 -f 300 -a 1000 + [lightson_workdays] Executing lightcontrol -g 10 -f 300 -a 1000 + Alarm sunrise triggered at 2020-05-07 06:09:46 + [lightson_workdays] Stopping sequence + [sunrise] Executing cancel lightson_workdays + [sunrise] Executing curtain -o 2 -t + [sunrise] Executing lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 + Alarm open_curtains triggered at 2020-05-07 09:00:00 + [sunrise] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm lightson_workdays triggered at 2020-05-08 06:00:00 + [open_curtains] Stopping sequence + [lightson_workdays] Executing lightcontrol -r 10 -f 300 -a 1000 + [lightson_workdays] Executing lightcontrol -g 10 -f 300 -a 1000 + Alarm sunrise triggered at 2020-05-08 06:08:06 + [lightson_workdays] Stopping sequence + [sunrise] Executing cancel lightson_workdays + [sunrise] Executing curtain -o 2 -t + [sunrise] Executing lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 + Alarm open_curtains triggered at 2020-05-08 09:00:00 + [sunrise] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm open_curtains triggered at 2020-05-09 09:00:00 + [open_curtains] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm open_curtains triggered at 2020-05-10 09:00:00 + [open_curtains] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm lightson_workdays triggered at 2020-05-11 06:00:00 + [open_curtains] Stopping sequence + [lightson_workdays] Executing lightcontrol -r 10 -f 300 -a 1000 + Alarm sunrise triggered at 2020-05-11 06:03:12 + [lightson_workdays] Stopping sequence + [sunrise] Executing cancel lightson_workdays + [sunrise] Executing curtain -o 2 -t + [sunrise] Executing lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 + Alarm open_curtains triggered at 2020-05-11 09:00:00 + [sunrise] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm lightson_workdays triggered at 2020-05-12 06:00:00 + [open_curtains] Stopping sequence + [lightson_workdays] Executing lightcontrol -r 10 -f 300 -a 1000 + Alarm sunrise triggered at 2020-05-12 06:01:37 + [lightson_workdays] Stopping sequence + [sunrise] Executing cancel lightson_workdays + [sunrise] Executing curtain -o 2 -t + [sunrise] Executing lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 + Alarm open_curtains triggered at 2020-05-12 09:00:00 + [sunrise] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm lightson_workdays triggered at 2020-05-13 06:00:00 + [open_curtains] Stopping sequence + [lightson_workdays] Executing lightcontrol -r 10 -f 300 -a 1000 + Alarm sunrise triggered at 2020-05-13 06:00:03 + [lightson_workdays] Stopping sequence + [sunrise] Executing cancel lightson_workdays + [sunrise] Executing curtain -o 2 -t + [sunrise] Executing lightcontrol -r 0 -g 0 -w 0 -f 100 -a 1000 + Alarm open_curtains triggered at 2020-05-13 09:00:00 + [sunrise] Stopping sequence + [open_curtains] Executing curtain -o 1 -t + Alarm end_test triggered at 2020-05-13 11:00:00 + [open_curtains] Stopping sequence + [end_test] Executing exit diff --git a/test/wakeup_summer b/test/wakeup_summer new file mode 100755 index 0000000..442d775 --- /dev/null +++ b/test/wakeup_summer @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Test the wakeup sequence in summer. +# Sunrise happens before the wakeup time. +# +set -m +PATH=../src:$PATH + +# Reset the lights and clear the log files + +lightcontrol -r 0 -g 0 -b 0 -w 0 +> lightcontrol.log +> wakeup.log + +ln -f -s wakeup_empty.xml wakeup_link.xml + +wakeup & +sleep 1 + +read TACHYON_NAME wakeup_summer.log.test + +diff wakeup_summer.log.test wakeup_summer.log.expect + +exit $? diff --git a/test/wakeup_summer.log.expect b/test/wakeup_summer.log.expect new file mode 100644 index 0000000..ff80e7f --- /dev/null +++ b/test/wakeup_summer.log.expect @@ -0,0 +1,11 @@ +wakeup started +Reading alarms from wakeup_link.xml +SIGHUP received. +Reading alarms from wakeup_link.xml +Alarm sunrise triggered at 2020-06-17 05:27:06 +Executing curtain -o 2 -t +Executing lightcontrol -r 0 -g 0 -w 0 -f 2 +Alarm open_curtains triggered at 2020-06-17 09:00:00 +Executing curtain -o 1 -t +Alarm end_test triggered at 2020-06-17 11:00:00 +Executing exit diff --git a/test/wakeup_winter b/test/wakeup_winter index 4970c7c..028601a 100755 --- a/test/wakeup_winter +++ b/test/wakeup_winter @@ -19,7 +19,7 @@ sleep 1 read TACHYON_NAME