mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-25 16:05:19 +00:00
99_JsonMod.pm: cron lib changed
git-svn-id: https://svn.fhem.de/fhem/trunk@27916 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
50970250af
commit
4346285493
@ -27,6 +27,7 @@ use feature qw( lexical_subs );
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
use FHEM::Scheduler::Cron;
|
||||||
use HttpUtils;
|
use HttpUtils;
|
||||||
use List::Util qw( any );
|
use List::Util qw( any );
|
||||||
use Text::Balanced qw ( extract_codeblock extract_delimited extract_bracketed );
|
use Text::Balanced qw ( extract_codeblock extract_delimited extract_bracketed );
|
||||||
@ -78,7 +79,7 @@ sub JsonMod_Define {
|
|||||||
$hash->{'SVN'} = $cvsid;
|
$hash->{'SVN'} = $cvsid;
|
||||||
$hash->{'CONFIG'}->{'IN_REQUEST'} = 0;
|
$hash->{'CONFIG'}->{'IN_REQUEST'} = 0;
|
||||||
$hash->{'CONFIG'}->{'CRON'} = \'0 * * * *';
|
$hash->{'CONFIG'}->{'CRON'} = \'0 * * * *';
|
||||||
$hash->{'CRON'} = JsonMod::Cron->new();
|
# $hash->{'CRON'} = JsonMod::Cron->new();
|
||||||
|
|
||||||
return "no FUUID, is fhem up to date?" if (not $hash->{'FUUID'});
|
return "no FUUID, is fhem up to date?" if (not $hash->{'FUUID'});
|
||||||
return "wrong source definition" if ($source !~ m/^(https:|http:|file:|system:)/);
|
return "wrong source definition" if ($source !~ m/^(https:|http:|file:|system:)/);
|
||||||
@ -115,6 +116,7 @@ sub JsonMod_Run {
|
|||||||
|
|
||||||
my $cron = AttrVal($name, 'interval', '0 * * * *');
|
my $cron = AttrVal($name, 'interval', '0 * * * *');
|
||||||
$hash->{'CONFIG'}->{'CRON'} = \$cron;
|
$hash->{'CONFIG'}->{'CRON'} = \$cron;
|
||||||
|
$hash->{'CRON'} = FHEM::Scheduler::Cron->new($cron);
|
||||||
JsonMod_StartTimer($hash);
|
JsonMod_StartTimer($hash);
|
||||||
JsonMod_ApiRequest($hash) if AttrVal($name, 'update-on-start', 0);
|
JsonMod_ApiRequest($hash) if AttrVal($name, 'update-on-start', 0);
|
||||||
return;
|
return;
|
||||||
@ -161,14 +163,16 @@ sub JsonMod_Attr {
|
|||||||
};
|
};
|
||||||
if ($attrName eq 'interval') {
|
if ($attrName eq 'interval') {
|
||||||
if (split(/ /, $attrValue) == 5) {
|
if (split(/ /, $attrValue) == 5) {
|
||||||
if ($hash->{'CRON'}->validate($attrValue)) {
|
my $err;
|
||||||
|
($hash->{'CRON'}, $err) = FHEM::Scheduler::Cron->new($attrValue);
|
||||||
|
unless ($err) {
|
||||||
$hash->{'CONFIG'}->{'CRON'} = \$attrValue;
|
$hash->{'CONFIG'}->{'CRON'} = \$attrValue;
|
||||||
return if (!$init_done);
|
return if (!$init_done);
|
||||||
JsonMod_StopTimer($hash);
|
JsonMod_StopTimer($hash);
|
||||||
JsonMod_StartTimer($hash) unless IsDisabled($name);
|
JsonMod_StartTimer($hash) unless IsDisabled($name);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
return "wrong interval expression (cron)"
|
return $err;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return "wrong interval expression";
|
return "wrong interval expression";
|
||||||
@ -558,10 +562,12 @@ sub JsonMod_StartTimer {
|
|||||||
my @t = localtime(Time::HiRes::time());
|
my @t = localtime(Time::HiRes::time());
|
||||||
$t[4] += 1;
|
$t[4] += 1;
|
||||||
$t[5] += 1900;
|
$t[5] += 1900;
|
||||||
my @r = $hash->{'CRON'}->next($cron, @t);
|
my ($r, $err) = $hash->{'CRON'}->next(sprintf('%04d%02d%02d%02d%02d%02d', $t[5], $t[4], $t[3], $t[2], $t[1], $t[0]));
|
||||||
my $ts = timelocal(0, $r[0], $r[1], $r[2], $r[3] -1, $r[4] -1900);
|
# todo check err
|
||||||
$hash->{'NEXT'} = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $r[4], $r[3], $r[2], $r[1], $r[0], 0);
|
my @u = ($r =~ m/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})/);
|
||||||
JsonMod_Logger($hash, 4, 'next request: %04d.%02d.%02d %02d:%02d:%02d', $r[4], $r[3], $r[2], $r[1], $r[0], 0);
|
my $ts = timelocal($u[5], $u[4], $u[3], $u[2], $u[1] -1, $u[0] -1900);
|
||||||
|
$hash->{'NEXT'} = sprintf('%04d-%02d-%02d %02d:%02d:%02d', @u);
|
||||||
|
JsonMod_Logger($hash, 4, 'next request: %04d.%02d.%02d %02d:%02d:%02d', @u);
|
||||||
InternalTimer($ts, \&JsonMod_DoTimer, $hash);
|
InternalTimer($ts, \&JsonMod_DoTimer, $hash);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -1614,261 +1620,6 @@ sub DESTROY {
|
|||||||
delete $self->{'node'};
|
delete $self->{'node'};
|
||||||
};
|
};
|
||||||
|
|
||||||
package JsonMod::Cron;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use utf8;
|
|
||||||
use Time::Local qw ( timelocal );
|
|
||||||
|
|
||||||
no warnings qw( experimental::lexical_subs );
|
|
||||||
|
|
||||||
# static and helper
|
|
||||||
sub normalizeTime {
|
|
||||||
my ($m, $h, $d) = @_;
|
|
||||||
$d //= 0;
|
|
||||||
if ($m > 59) { $h += int($m / 60); $m %= 60; };
|
|
||||||
if ($h > 23) { $d += int($h / 24); $h %= 24; };
|
|
||||||
return ($m, $h, $d);
|
|
||||||
};
|
|
||||||
|
|
||||||
sub normalizeDate {
|
|
||||||
my ($d, $m, $y, $o) = @_;
|
|
||||||
$o //= 0;
|
|
||||||
my $time = timelocal(0, 0, 12, $d, $m -1, $y -1900);
|
|
||||||
$time += $o * 86400;
|
|
||||||
my @t = localtime($time);
|
|
||||||
# plus DST, wday (SUN=0..6), yday (0..364|5)
|
|
||||||
return ($t[3], $t[4] +1, $t[5] +1900, $t[8], $t[6], $t[7]);
|
|
||||||
};
|
|
||||||
|
|
||||||
# class
|
|
||||||
sub new {
|
|
||||||
my ($class) = @_;
|
|
||||||
my $self = {};
|
|
||||||
|
|
||||||
bless $self, $class;
|
|
||||||
return $self;
|
|
||||||
};
|
|
||||||
|
|
||||||
sub setCron {
|
|
||||||
my ($self, $cron) = @_;
|
|
||||||
@{$self->{'CRONLIST'}} = split / /, $cron //= '';
|
|
||||||
return if (scalar @{$self->{'CRONLIST'}} != 5);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
sub parseMinuteEntry {
|
|
||||||
my ($self, $in, $now) = @_;
|
|
||||||
my ($res, $start, $stop, $step);
|
|
||||||
|
|
||||||
($step) = ($in =~ m/\/([0-9]|[0-5][0-9])$/);
|
|
||||||
($start, $stop) = ($in =~ m/^([*]|[0-9]|[0-5][0-9])(?:-([0-9]|[0-5][0-9]))?(?:\/(?:[0-9]|[0-5][0-9]))?$/);
|
|
||||||
return if (not defined($start) or ($start eq '*' and defined($stop))); # syntax error
|
|
||||||
|
|
||||||
$stop = (defined($step) or ($start eq '*'))?59:$start if (not defined($stop));
|
|
||||||
$start = 0 if $start eq '*';
|
|
||||||
return if ($start > $stop); # syntax error
|
|
||||||
return $start if ($now < $start); # literal start
|
|
||||||
|
|
||||||
$res = $step //= 1;
|
|
||||||
$res = $res - (((($now - $start) % 60) + $res) % $res);
|
|
||||||
$res = $now + $res;
|
|
||||||
|
|
||||||
return $start + 60 if ($res > $stop); # carry over
|
|
||||||
return $res; # regular next
|
|
||||||
};
|
|
||||||
|
|
||||||
sub parseHourEntry {
|
|
||||||
my ($self, $in, $now) = @_;
|
|
||||||
my ($res, $start, $stop, $step);
|
|
||||||
|
|
||||||
($step) = ($in =~ m/\/([0-9]|[0,1][0-9]|2[0-3])$/);
|
|
||||||
($start, $stop) = ($in =~ m/^([*]|[0-9]|[0,1][0-9]|2[0-3])(?:-([0-9]|[0,1][0-9]|2[0-3]))?(?:\/(?:[*]|[0-9]|[0,1][0-9]|2[0-3]))?$/);
|
|
||||||
return if (not defined($start) or ($start eq '*' and defined($stop))); # syntax error
|
|
||||||
|
|
||||||
$stop = (defined($step) or ($start eq '*'))?23:$start if (not defined($stop));
|
|
||||||
$start = 0 if $start eq '*';
|
|
||||||
return if ($start > $stop); # syntax error
|
|
||||||
return $start if ($now < $start); # literal start
|
|
||||||
|
|
||||||
$res = $step //= 1;
|
|
||||||
$res = ($now - $start) % $res;
|
|
||||||
|
|
||||||
return $now if ($res == 0) and ($now <= $stop); # current hour
|
|
||||||
$res = $now + $step - $res;
|
|
||||||
return $start + 24 if ($res > $stop); # carry over
|
|
||||||
return $res; # regular next
|
|
||||||
};
|
|
||||||
|
|
||||||
sub parseDateEntry {
|
|
||||||
my ($self, $in, $now) = @_;
|
|
||||||
my ($res, $start, $stop, $step);
|
|
||||||
|
|
||||||
($step) = ($in =~ m/\/([0-9]|[0-2][0-9]|3[0,1])$/);
|
|
||||||
($start, $stop) = ($in =~ m/^([*]|[0-9]|[0-2][0-9]|3[0,1])(?:-([0-9]|[0-2][0-9]|3[0,1]))?(?:\/(?:[*]|[0-9]|[0-2][0-9]|3[0,1]))?$/);
|
|
||||||
return if (not defined($start) or ($start eq '*' and defined($stop))); # syntax error
|
|
||||||
|
|
||||||
$stop = (defined($step) or ($start eq '*'))?31:$start if (not defined($stop));
|
|
||||||
$start = 1 if $start eq '*';
|
|
||||||
return if ($start > $stop); # syntax error
|
|
||||||
return $start if ($now < $start); # literal start
|
|
||||||
|
|
||||||
$res = $step //= 1;
|
|
||||||
$res = ($now - $start) % $res;
|
|
||||||
|
|
||||||
return $now if ($res == 0) and ($now <= $stop); # current
|
|
||||||
$res = $now + $step - $res;
|
|
||||||
return $start + 32 if ($res > $stop); # carry over
|
|
||||||
return $res; # regular next
|
|
||||||
};
|
|
||||||
|
|
||||||
sub next {
|
|
||||||
my ($self, $cron, @t) = @_;
|
|
||||||
|
|
||||||
my $inDay = sprintf('%04d%02d%02d', $t[5], $t[4], $t[3]);
|
|
||||||
my ($cronMin, $cronHour, $cronDay, $cronMonth, $cronWeekDay) = split / /, $cron;
|
|
||||||
my ($time, $dst, $weekday);
|
|
||||||
|
|
||||||
# m h d(carry)
|
|
||||||
$time = $self->nextTime($t[1], $t[2], $cronMin, $cronHour);
|
|
||||||
return if (not $time);
|
|
||||||
($t[3], $t[4], $t[5], $dst, $weekday) = normalizeDate($t[3], $t[4], $t[5], $time->[2]);
|
|
||||||
my $calcDay = sprintf('%04d%02d%02d', $t[5], $t[4], $t[3]);
|
|
||||||
|
|
||||||
# date unchanged and known
|
|
||||||
if ($calcDay eq $inDay) {
|
|
||||||
return ($time->[0], $time->[1], $t[3], $t[4], $t[5], $dst);
|
|
||||||
};
|
|
||||||
|
|
||||||
# m h d(carry)
|
|
||||||
$time = $self->nextTime(0, 0, $cronMin, $cronHour);
|
|
||||||
#($t[3], $t[4], $t[5], $dst, $weekday) = normalizeDate($t[3], $t[4], $t[5], $time->[2]);
|
|
||||||
|
|
||||||
# yyyy mm dd
|
|
||||||
my $date = $self->nextDate($t[3], $t[4], $t[5], $cronDay, $cronMonth);
|
|
||||||
return if (not $date);
|
|
||||||
($t[3], $t[4], $t[5], $dst, $weekday) = normalizeDate($date->[2], $date->[1], $date->[0]);
|
|
||||||
|
|
||||||
return ($time->[0], $time->[1], $t[3], $t[4], $t[5], $dst);
|
|
||||||
};
|
|
||||||
|
|
||||||
# test if valid cron expression
|
|
||||||
sub validate {
|
|
||||||
my ($self, $cron) = @_;
|
|
||||||
my ($cronMin, $cronHour, $cronDay, $cronMonth, $cronWeekDay) = split / /, $cron;
|
|
||||||
my $time = $self->nextTime(0, 0, $cronMin, $cronHour);
|
|
||||||
my $date = $self->nextDate(2020, 1, 1, $cronDay, $cronMonth);
|
|
||||||
if (defined($time) and defined($date)) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# min = time: actual minute
|
|
||||||
# hour = time: actual hour
|
|
||||||
sub nextTime {
|
|
||||||
my ($self, $min, $hour, $cronMin, $cronHour) = @_;
|
|
||||||
|
|
||||||
my $calcMin;
|
|
||||||
my $calcHour;
|
|
||||||
my $calcDay = 0;
|
|
||||||
|
|
||||||
foreach my $cronMinEntry (split /,/, $cronMin) {
|
|
||||||
my $e = $self->parseMinuteEntry($cronMinEntry, $min);
|
|
||||||
return if not defined($e); # syntax error
|
|
||||||
if ((not defined($calcMin) and defined($e)) or ($e < $calcMin)) {
|
|
||||||
$calcMin = $e;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
($calcMin, $hour, $calcDay) = normalizeTime($calcMin, $hour, $calcDay);
|
|
||||||
|
|
||||||
foreach my $cronHourEntry (split /,/, $cronHour) {
|
|
||||||
my $e = $self->parseHourEntry($cronHourEntry, $hour);
|
|
||||||
return if not defined($e); # syntax error
|
|
||||||
if ((not defined($calcHour) and defined($e)) or ($e < $calcHour)) {
|
|
||||||
$calcHour = $e;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
my (@time) = normalizeTime($calcMin, $calcHour, $calcDay);
|
|
||||||
return \@time;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
sub nextDate {
|
|
||||||
my ($self, $day, $month, $year, $cronDay, $cronMonth) = @_;
|
|
||||||
|
|
||||||
my $dates = $self->listDates($day, $month, $year, $cronDay, $cronMonth);
|
|
||||||
my $result;
|
|
||||||
foreach (@{$dates}) {
|
|
||||||
if ((not defined($result) and defined($_)) or ($_ and ($_ < $result))) {
|
|
||||||
$result = $_;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return if (not defined($result));
|
|
||||||
my (@date) = ($result =~ m/^(\d{4})(\d{2})(\d{2})$/);
|
|
||||||
return \@date;
|
|
||||||
};
|
|
||||||
|
|
||||||
sub listDates {
|
|
||||||
my ($self, $day, $month, $year, $cronDay, $cronMonth) = @_;
|
|
||||||
my @result;
|
|
||||||
|
|
||||||
#return [] if ($self->{R}++ > 25);
|
|
||||||
|
|
||||||
# my sub daysOfMonth {
|
|
||||||
local *daysOfMonth = sub {
|
|
||||||
my ($m, $y) = @_;
|
|
||||||
my (@d) = (0,31,28,31,30,31,30,31,31,30,31,30,31);
|
|
||||||
# leapyear
|
|
||||||
$d[2] = 29 if (((($y % 4) == 0) and (($y % 100) != 0)) or (($y % 400) == 0));
|
|
||||||
return ($d[$m]);
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach my $cronDayEntry (split /,/, $cronDay) {
|
|
||||||
foreach my $cronMonthEntry (split /,/, $cronMonth) {
|
|
||||||
# impossible cron would recurse forever: [31 2 * * *] / [31 9/2 * * *]
|
|
||||||
my $invalid = 1;
|
|
||||||
if ((my ($fuseDay) = ($cronDayEntry =~ m/^(\d{1,2})/)) and
|
|
||||||
(my ($fuseMonth, $fuseMonthStep) = ($cronMonthEntry =~ m/^(\d{1,2})(?:\/(\d{1,2}))*/))) {
|
|
||||||
#print "FUSE $fuseDay, $fuseMonth, $fuseMonthStep\n";
|
|
||||||
for (my $i = $fuseMonth; $i <= 12 and $invalid; $i += $fuseMonthStep //= 12) {
|
|
||||||
$invalid = 0 if (daysOfMonth($fuseMonth, 2000) >= $fuseDay); # 2000 is leapyear
|
|
||||||
};
|
|
||||||
if ($invalid) {
|
|
||||||
push @result, ();
|
|
||||||
next;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
my $calcDay = $self->parseDateEntry($cronDayEntry, $day);
|
|
||||||
my $calcMonth = $self->parseDateEntry($cronMonthEntry, $month);
|
|
||||||
my $calcYear = $year;
|
|
||||||
#printf "Test: D:%s, M:%s against %s-%s -> %s-%s-%s\n", $cronDayEntry, $cronMonthEntry, $day, $month, $calcDay, $calcMonth, $calcYear;
|
|
||||||
if (defined($calcDay) and defined($calcMonth)) {
|
|
||||||
#$doy = isValid($testM, $testMd);
|
|
||||||
if (($calcDay == $day) and ($calcMonth == $month)) {
|
|
||||||
#printf "RETURN: D:%s, M:%s against %s-%s-%s -> %s-%s-%s\n", $cronDayEntry, $cronMonthEntry, $day, $month, $year, $calcDay, $calcMonth, $calcYear;
|
|
||||||
push @result, sprintf('%04d%02d%02d', $calcYear, $calcMonth, $calcDay);
|
|
||||||
} else {
|
|
||||||
if ($calcMonth > 12) {
|
|
||||||
$calcMonth -= ($calcMonth == 13)?12:32;
|
|
||||||
$calcYear++;
|
|
||||||
};
|
|
||||||
if ($calcDay > daysOfMonth($calcMonth, $calcYear)) {
|
|
||||||
$calcMonth++ if ($calcMonth == $month);
|
|
||||||
$calcDay = 1;
|
|
||||||
};
|
|
||||||
push @result, @{ $self->listDates($calcDay, $calcMonth, $calcYear, $cronDayEntry, $cronMonthEntry) };
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return []; # syntax error
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return \@result;
|
|
||||||
};
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
Loading…
x
Reference in New Issue
Block a user