Skip to content

Commit

Permalink
sch: init sun event at boot & schedule checks
Browse files Browse the repository at this point in the history
ref. #2629, relative events are not properly initialized
ref. #2626 schedule check <PART1> <PART2> to manually validate schedules
  • Loading branch information
mcspr committed Nov 29, 2024
1 parent eb5041a commit f409248
Showing 1 changed file with 129 additions and 29 deletions.
158 changes: 129 additions & 29 deletions code/espurna/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ void update(datetime::Clock::time_point, const tm& today) {
}

template <typename T>
void update(datetime::Clock::time_point time_point, const tm& today, T compare) {
datetime::Clock::time_point update(datetime::Clock::time_point time_point, const tm& today, T compare) {
auto result = sun::sunrise_sunset(location, today);

const auto reset_sunrise =
Expand All @@ -859,12 +859,13 @@ void update(datetime::Clock::time_point time_point, const tm& today, T compare)
const auto reset_sunset =
!event::is_valid(result.sunset) || compare(time_point, result.sunset);

next_update = event::DefaultTimePoint;
auto out = event::DefaultTimePoint;

tm tmp;
if (reset_sunrise || reset_sunset) {
std::memcpy(&tmp, &today, sizeof(tmp));
next_update = delta_compare(tmp, time_point, compare);

out = delta_compare(tmp, time_point, compare);

const auto other = sun::sunrise_sunset(location, tmp);
if (reset_sunrise && event::is_valid(other.sunrise)) {
Expand All @@ -878,31 +879,45 @@ void update(datetime::Clock::time_point time_point, const tm& today, T compare)

update_event_match(match.rising, result.sunrise);
update_event_match(match.setting, result.sunset);

return out;
}

template <typename T>
void update(time_t timestamp, const tm& today, T&& compare) {
update(datetime::make_time_point(timestamp), today, std::forward<T>(compare));
}

String format_match(const EventMatch& match) {
return datetime::format_local_tz(match.next);
String format_time_point(const event::time_point& time_point) {
return (time_point.time_since_epoch() > datetime::Clock::duration::zero())
? datetime::format_local_tz(time_point)
: STRING_VIEW("value not set").toString();
}

String format_next(const EventMatch& match) {
return format_time_point(match.next);
}

String format_last(const EventMatch& match) {
return format_time_point(match.last);
}

// check() needs current or future events, discard timestamps in the past
// round to minutes when doing so as well, since std::greater<> would compare seconds
struct CheckCompare {
struct CompareAfter {
bool operator()(const event::time_point& lhs, const event::time_point& rhs) {
return event::greater(lhs, rhs);
}
};

template <>
datetime::Clock::time_point delta_compare(tm& out, datetime::Clock::time_point time_point, CheckCompare) {
datetime::Clock::time_point delta_compare_days(tm& out, datetime::Clock::time_point time_point, datetime::Days days) {
return datetime::make_time_point(
datetime::delta_utc(
out, time_point.time_since_epoch(),
datetime::Days{ 1 }));
datetime::delta_utc(out, time_point.time_since_epoch(), days));
}

template <>
datetime::Clock::time_point delta_compare(tm& out, datetime::Clock::time_point time_point, CompareAfter) {
return delta_compare_days(out, time_point, datetime::Days{ 1 });
}

void update_after(const datetime::Context& ctx) {
Expand All @@ -911,17 +926,58 @@ void update_after(const datetime::Context& ctx) {
return;
}

update(time_point, ctx.utc, CheckCompare{});
auto next = update(time_point, ctx.utc, CompareAfter{});

if (event::is_valid(match.rising.next)) {
DEBUG_MSG_P(PSTR("[SCH] Sunrise at %s\n"),
format_match(match.rising).c_str());
datetime::format_local_tz(match.rising.next).c_str());
}

if (event::is_valid(match.setting.next)) {
DEBUG_MSG_P(PSTR("[SCH] Sunset at %s\n"),
format_match(match.setting).c_str());
datetime::format_local_tz(match.setting.next).c_str());
}

const event::time_point unordered[] {
next,
match.rising.next,
match.setting.next,
};

next_update = event::DefaultTimePoint;

for (const auto& value : unordered) {
if (!event::is_valid(value)) {
continue;
}

next_update = event::is_valid(next_update)
? std::min(next_update, value)
: value;
}
}

// relative events need current or past time point
struct CompareBefore {
bool operator()(const event::time_point& lhs, const event::time_point& rhs) {
return event::less(lhs, rhs);
}
};

template <>
datetime::Clock::time_point delta_compare(tm& out, datetime::Clock::time_point time_point, CompareBefore) {
return delta_compare_days(out, time_point, datetime::Days{ -1 });
}

void update_before(const datetime::Context& ctx) {
const auto time_point = event::make_time_point(ctx);
update(time_point, ctx.utc, CompareBefore{});

match.rising.last = match.rising.next;
match.rising.next = event::DefaultTimePoint;

match.setting.last = match.setting.next;
match.setting.next = event::DefaultTimePoint;
}

} // namespace sun
Expand All @@ -935,24 +991,31 @@ namespace terminal {
#if SCHEDULER_SUN_SUPPORT
namespace internal {

String sunrise_sunset(const sun::EventMatch& match) {
if (match.next.time_since_epoch() > datetime::Clock::duration::zero()) {
return sun::format_match(match);
}
struct Datetime {
String last;
String next;
};

return STRING_VIEW("value not set").toString();
Datetime sunrise_sunset(const sun::EventMatch& match) {
return Datetime{
.last = sun::format_last(match),
.next = sun::format_next(match),
};
}

void format_output(::terminal::CommandContext& ctx, const String& prefix, const String& value) {
ctx.output.printf_P(PSTR("- %s%s%s\n"),
void format_output(::terminal::CommandContext& ctx, const String& prefix, const Datetime& datetime) {
ctx.output.printf_P(PSTR("- %s\n last: %s\n next: %s\n"),
prefix.c_str(),
value.length()
? PSTR(" at ")
: " ",
value.c_str());
datetime.last.c_str(),
datetime.next.c_str());
}

void dump_sunrise_sunset(::terminal::CommandContext& ctx) {
if (event::is_valid(sun::next_update)) {
ctx.output.printf_P(PSTR("- Next sunrise & sunset update at %s\n"),
datetime::format_local_tz(sun::next_update).c_str());
}

format_output(ctx,
STRING_VIEW("Sunrise").toString(),
sunrise_sunset(sun::match.rising));
Expand All @@ -965,9 +1028,44 @@ void dump_sunrise_sunset(::terminal::CommandContext& ctx) {
#endif

// SCHEDULE [<ID>]
PROGMEM_STRING(Dump, "SCHEDULE");
// SCHEDULE CHECK [CHUNK(S)...]
PROGMEM_STRING(Entrypoint, "SCHEDULE");

void entrypoint(::terminal::CommandContext&& ctx) {
if (ctx.argv.size() > 2 && (STRING_VIEW("CHECK").equalsIgnoreCase(ctx.argv[1]))) {
String spec;

size_t reserve = 0;
std::for_each(
ctx.argv.begin() + 2,
ctx.argv.end(),
[&](const String& arg) {
reserve += 1 + arg.length();
});
spec.reserve(reserve);

if (ctx.argv.size() > 3) {
for (size_t index = 2; index < ctx.argv.size(); ++index) {
if (index > 2) {
spec += ' ';
}

spec += ctx.argv[index];
}
} else {
spec = std::move(ctx.argv[2]);
}

const auto result = parse_schedule(spec);
if (result.ok) {
terminalOK(ctx);
} else {
terminalError(ctx, STRING_VIEW("Invalid schedule string"));
}

return;
}

void dump(::terminal::CommandContext&& ctx) {
if (ctx.argv.size() != 2) {
settingsDump(ctx, settings::Settings);
return;
Expand Down Expand Up @@ -1036,7 +1134,6 @@ void event(::terminal::CommandContext&& ctx) {
}

#if SCHEDULER_SUN_SUPPORT
ctx.output.print(PSTR("Sun events:\n"));
internal::dump_sunrise_sunset(ctx);
#endif

Expand All @@ -1053,7 +1150,7 @@ void event(::terminal::CommandContext&& ctx) {
}

static constexpr ::terminal::Command Commands[] PROGMEM {
{Dump, dump},
{Entrypoint, entrypoint},
{Event, event},
};

Expand Down Expand Up @@ -2059,6 +2156,9 @@ void tick(NtpTick tick) {
initial = false;
settings::gc(settings::count());
restore::run(ctx);
#if SCHEDULER_SUN_SUPPORT
sun::update_before(ctx);
#endif
}

#if SCHEDULER_SUN_SUPPORT
Expand Down

0 comments on commit f409248

Please sign in to comment.