2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

FileLog regexp editor added

git-svn-id: https://svn.fhem.de/fhem/trunk@3128 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2013-04-28 12:40:28 +00:00
parent 0d8819b8d7
commit 433b72340a
9 changed files with 404 additions and 29 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII
- SVN
- feature: eventTypes module added, to help with FileLog details screen
- feature: FB_CALLMONITOR: new reverse search provider dasschnelle.at for
reverse search of austrian telephone numbers
- bugfix: event-on-change-reading in combination with event-change-interval

View File

@ -2205,10 +2205,11 @@ FW_makeEdit($$$)
FW_pO "</tr><tr><td colspan=\"2\">";
FW_pO "<div id=\"edit\" style=\"display:none\"><form>";
FW_pO FW_hidden("detail", $name);
my $cmd = "modify";
my $ncols = $FW_ss ? 30 : 60;
FW_pO "<textarea name=\"val.${cmd}$name\" cols=\"$ncols\" rows=\"10\">".
"$val</textarea>";
FW_pO "<textarea name=\"val.${cmd}$name\" ".
"cols=\"$ncols\" rows=\"10\">$val</textarea>";
FW_pO "<br>" . FW_submit("cmd.${cmd}$name", "$cmd $name");
FW_pO "</form></div>";
FW_pO "</td>";

191
fhem/FHEM/91_eventTypes.pm Executable file
View File

@ -0,0 +1,191 @@
##############################################
# $Id: 91_eventTypes.pm 2982 2013-03-24 17:47:28Z rudolfkoenig $
package main;
use strict;
use warnings;
#####################################
sub
eventTypes_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "eventTypes_Define";
$hash->{NotifyFn} = "eventTypes_Notify";
$hash->{ShutdownFn}="eventTypes_Shutdown";
$hash->{GetFn} = "eventTypes_Get";
$hash->{SetFn} = "eventTypes_Set";
$hash->{AttrFn} = "eventTypes_Attr";
$hash->{AttrList} = "disable:0,1 loglevel:0,1,2,3,4,5,6";
}
#####################################
sub
eventTypes_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "wrong syntax: define <name> eventTypes filename" if(int(@a) != 3);
my @t = localtime;
my $f = ResolveDateWildcards($a[2], @t);
my $fh = new IO::File "$f";
if($fh) {
while(my $l = <$fh>) {
chomp($l);
my @a = split(" ", $l, 3);
$modules{eventTypes}{ldata}{$a[1]}{$a[2]} = $a[0];
}
close($fh);
}
$hash->{STATE} = "active";
return undef;
}
#####################################
sub
eventTypes_Notify($$)
{
my ($me, $eventSrc) = @_;
my $ln = $me->{NAME};
return "" if($attr{$ln} && $attr{$ln}{disable});
return if(!$eventSrc->{CHANGED});
my $t = $eventSrc->{TYPE};
my $n = $eventSrc->{NAME};
my $ll4 = GetLogLevel($ln, 4);
my $ret = "";
foreach my $oe (@{$eventSrc->{CHANGED}}) {
$oe = "" if(!defined($oe));
my $ne = $oe;
$ne =~ s/\b-?\d*\.?\d+\b/.*/g;
$ne =~ s/set_\d+/set_.*/; # HM special :/
Log $ll4, "$ln: $t $n $oe -> $ne";
$modules{eventTypes}{ldata}{$n}{$ne}++;
}
return undef;
}
sub
eventTypes_Attr(@)
{
my @a = @_;
my $do = 0;
if($a[0] eq "set" && $a[2] eq "disable") {
$do = (!defined($a[3]) || $a[3]) ? 1 : 2;
}
$do = 2 if($a[0] eq "del" && (!$a[2] || $a[2] eq "disable"));
return if(!$do);
$defs{$a[1]}{STATE} = ($do == 1 ? "disabled" : "active");
return undef;
}
###################################
sub
eventTypes_Shutdown($$)
{
my ($hash, $name) = @_;
my $fName = $hash->{DEF};
my $fh = new IO::File ">$fName";
return "Can't open $fName: $!" if(!defined($fh));
my $ldata = $modules{eventTypes}{ldata};
foreach my $t (sort keys %{$ldata}) {
foreach my $e (sort keys %{$ldata->{$t}}) {
print $fh "$ldata->{$t}{$e} $t $e\n";
}
}
close($fh);
return undef;
}
###################################
sub
eventTypes_Set($@)
{
my ($hash, @a) = @_;
return "Unknown argument $a[1], choose one of flush" if($a[1] ne "flush");
return eventTypes_Shutdown($hash, $hash->{NAME});
}
###################################
sub
eventTypes_Get($@)
{
my ($hash, @a) = @_;
my $cmd = (defined($a[1]) ? $a[1] : "");
my $arg = $a[2];
return "Unknown argument $cmd, choose one of list" if($cmd ne "list");
my $out = "";
my $ldata = $modules{eventTypes}{ldata};
foreach my $t (sort keys %{$ldata}) {
next if($arg && $t ne $arg);
foreach my $e (sort keys %{$ldata->{$t}}) {
$out .= "$t $e\n";
}
}
return $out;
}
1;
=pod
=begin html
<a name="eventTypes"></a>
<h3>eventTypes</h3>
<ul>
<br>
<a name="eventTypesdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; eventTypes &lt;filename&gt;</code>
<br><br>
Collect event types for all devices. This service is used by frontends.
The filename is used to store the collected events before shutdown.<br>
More than one instance of eventTypes should not be necessary.
Examples:
<ul>
<code>define et eventTypes log/eventTypes.txt</code><br>
</ul>
<br>
</ul>
<br>
<a name="eventTypesset"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="eventTypesget"></a>
<b>Get</b>
<ul>
<li>list [devicename]<br>
return the list of collected event types for all devices or for
devicename if specified.
</li>
</ul>
<br>
<a name="eventTypesattr"></a>
<b>Attributes</b>
<ul>
<li><a href="#disable">disable</a></li>
<li><a href="#loglevel">loglevel</a></li>
</ul>
<br>
</ul>
=end html
=cut

View File

@ -20,13 +20,14 @@ FileLog_Initialize($)
$hash->{SetFn} = "FileLog_Set";
$hash->{GetFn} = "FileLog_Get";
$hash->{UndefFn} = "FileLog_Undef";
$hash->{DeleteFn} = "FileLog_Delete";
$hash->{NotifyFn} = "FileLog_Log";
$hash->{AttrFn} = "FileLog_Attr";
# logtype is used by the frontend
$hash->{AttrList} = "disable:0,1 logtype nrarchive archivedir archivecmd";
$hash->{FW_summaryFn} = "FW_dumpFileLog";
$hash->{FW_detailFn} = "FW_dumpFileLog";
$hash->{FW_summaryFn} = "FileLog_fhemwebFn";
$hash->{FW_detailFn} = "FileLog_fhemwebFn";
}
@ -66,6 +67,16 @@ FileLog_Undef($$)
return undef;
}
sub
FileLog_Delete($$)
{
my ($hash, $name) = @_;
return if(!$hash->{currentlogfile});
unlink($hash->{currentlogfile});
return undef;
}
sub
FileLog_Switch($)
{
@ -148,27 +159,100 @@ FileLog_Attr(@)
}
###################################
sub
FileLog_Set($@)
{
my ($hash, @a) = @_;
return "no set argument specified" if(int(@a) != 2);
return "Unknown argument $a[1], choose one of reopen"
if($a[1] ne "reopen");
my $me = $hash->{NAME};
my $fh = $hash->{FH};
my $cn = $hash->{currentlogfile};
$fh->close();
$fh = new IO::File ">>$cn";
return "Can't open $cn" if(!defined($fh));
$hash->{FH} = $fh;
return "no set argument specified" if(int(@a) < 2);
my %sets = (reopen=>0, absorb=>1, addRegexpPart=>2, removeRegexpPart=>1);
my $cmd = $a[1];
if(!defined($sets{$cmd})) {
my $r = "Unknown argument $cmd, choose one of ".join(" ",sort keys %sets);
my $fllist = join(",", grep { $me ne $_ } devspec2array("TYPE=FileLog"));
$r =~ s/absorb/absorb:$fllist/;
return $r;
}
return "$cmd needs $sets{$cmd} parameter(s)" if(@a-$sets{$cmd} != 2);
if($cmd eq "reopen") {
my $fh = $hash->{FH};
my $cn = $hash->{currentlogfile};
$fh->close();
$fh = new IO::File(">>$cn");
return "Can't open $cn" if(!defined($fh));
$hash->{FH} = $fh;
} elsif($cmd eq "addRegexpPart") {
my %h;
my $re = "$a[2]:$a[3]";
map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP});
$h{$re} = 1;
$re = join("|", sort keys %h);
eval { "Hallo" =~ m/^$re$/ };
return "Bad regexp: $@" if($@);
$hash->{REGEXP} = $re;
$hash->{DEF} = $hash->{logfile} ." $re";
} elsif($cmd eq "removeRegexpPart") {
my %h;
map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP});
return "Cannot remove regexp part: not found" if(!$h{$a[2]});
return "Cannot remove last regexp part" if(int(keys(%h)) == 1);
delete $h{$a[2]};
my $re = join("|", sort keys %h);
eval { "Hallo" =~ m/^$re$/ };
return "Bad regexp: $@" if($@);
$hash->{REGEXP} = $re;
$hash->{DEF} = $hash->{logfile} ." $re";
} elsif($cmd eq "absorb") {
my $victim = $a[2];
return "need another FileLog as argument."
if(!$victim ||
!$defs{$victim} ||
$defs{$victim}{TYPE} ne "FileLog" ||
$victim eq $me);
my $vh = $defs{$victim};
my $mylogfile = $hash->{currentlogfile};
return "Cant open the associated files"
if(!open(FH1, $mylogfile) ||
!open(FH2, $vh->{currentlogfile}) ||
!open(FH3, ">$mylogfile.new"));
my $fh = $hash->{FH};
$fh->close();
my $b1 = <FH1>; my $b2 = <FH2>;
while(defined($b1) && defined($b2)) {
if($b1 lt $b2) {
print FH3 $b1; $b1 = <FH1>;
} else {
print FH3 $b2; $b2 = <FH2>;
}
}
while($b1 = <FH1>) { print FH3 $b1; }
while($b2 = <FH2>) { print FH3 $b2; }
close(FH1); close(FH2); close(FH3);
rename("$mylogfile.new", $mylogfile);
$fh = new IO::File(">>$mylogfile");
$hash->{FH} = $fh;
$hash->{REGEXP} .= "|".$vh->{REGEXP};
$hash->{DEF} = $hash->{logfile} . " ". $hash->{REGEXP};
CommandDelete(undef, $victim);
}
return undef;
}
#########################
sub
FW_dumpFileLog($$$$)
FileLog_fhemwebFn($$$$)
{
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
@ -193,6 +277,58 @@ FW_dumpFileLog($$$$)
}
$ret .= "</tr>";
}
$ret .= "</table>";
return $ret if($pageHash);
# DETAIL only from here on
my $hash = $defs{$d};
$ret .= "<br>Regexp parts";
$ret .= "<br><table class=\"block wide\">";
my @ra = split(/\|/, $hash->{REGEXP});
if(@ra > 1) {
foreach my $r (@ra) {
$ret .= "<tr class=\"".(($row++&1)?"odd":"even")."\">";
my $cmd = "cmd.X=set $d removeRegexpPart&val.X=$r";
$ret .= "<td>$r</td>";
$ret .= FW_pH("$cmd&detail=$d", "removeRegexpPart", 1,undef,1);
$ret .= "</tr>";
}
}
my @et = devspec2array("TYPE=eventTypes");
if(!@et) {
$ret .= FW_pH("$FW_ME/docs/commandref.html#eventTypes",
"To add a regexp an eventTypes definition is needed",
1, undef, 1);
} else {
my %dh;
foreach my $l (split("\n", AnalyzeCommand(undef, "get $et[0] list"))) {
my @a = split(/[ \r\n]/, $l);
$a[1] = "" if(!defined($a[1]));
$a[1] =~ s/\.\*//g;
$a[1] =~ s/,.*//g;
$dh{$a[0]}{".*"} = 1;
$dh{$a[0]}{$a[1].".*"} = 1;
}
my $list = ""; my @al;
foreach my $dev (sort keys %dh) {
$list .= " $dev:" . join(",", sort keys %{$dh{$dev}});
push @al, $dev;
}
$ret .= "<tr><td colspan=\"2\"><form autocomplete=\"off\">";
$ret .= FW_hidden("detail", $d);
$ret .= FW_hidden("dev.$d", "$d addRegexpPart");
$ret .= FW_submit("cmd.$d", "set", "set");
$ret .= "<div class=\"set downText\">&nbsp;$d addRegexpPart&nbsp;</div>";
$ret .= FW_select("","arg.$d",\@al, undef, "set",
"FW_selChange(this.options[selectedIndex].text,'$list','val.$d')");
$ret .= FW_textfield("val.$d", 30, "set");
$ret .= "<script type=\"text/javascript\">" .
"FW_selChange('$al[0]','$list','val.$d')</script>";
$ret .= "</form></td></tr>";
}
$ret .= "</table>";
return $ret;
}
@ -585,12 +721,45 @@ seekTo($$$$)
<a name="FileLogset"></a>
<b>Set </b>
<ul>
<code>set &lt;name&gt; reopen</code><br>
Used to reopen a FileLog after making some manual changes to the logfile.
<li>reopen
<ul>
Reopen a FileLog after making some manual changes to the
logfile.
</ul>
</li>
<li>addRegexpPart &lt;device&gt; &lt;regexp&gt;
<ul>
add a regexp part, which is constructed as device:regexp. The parts
are separated by |. Note: as the regexp parts are resorted, manually
constructed regexps may become invalid.
</ul>
</li>
<li>removeRegexpPart &lt;re&gt;
<ul>
remove a regexp part. Note: as the regexp parts are resorted, manually
constructed regexps may become invalid.<br>
The inconsistency in addRegexpPart/removeRegexPart arguments originates
from the reusage of javascript functions.
</ul>
</li>
<li>absorb secondFileLog
<ul>
merge the current and secondFileLog into one file, add the regexp of the
secondFileLog to the current one, and delete secondFileLog.<br>
This command is needed to create combined plots (weblinks).<br>
<b>Notes:</b>
<ul>
<li>secondFileLog will be deleted (i.e. the FHEM definition and
the file itself).</li>
<li>only the current files will be merged.</li>
<li>weblinks using secondFilelog will become broken, they have to be
adopted to the new logfile or deleted.</li>
</ul>
</ul>
</li>
<br>
</ul>
<br>
</ul>
<br>
<a name="FileLogget"></a>
@ -665,7 +834,7 @@ seekTo($$$$)
<a name="archivecmd"></a>
<a name="nrarchive"></a>
<li>archivecmd / archivedir / nrarchive<br>
When a new FileLog file is opened, the FileLog archiver wil be called.
When a new FileLog file is opened, the FileLog archiver wil be called.
This happens only, if the name of the logfile has changed (due to
time-specific wildcards, see the <a href="#FileLog">FileLog</a>
section), and there is a new entry to be written into the file.

View File

@ -61,12 +61,13 @@ FW_showWeblink($$$$)
##################
sub
weblink_FwDetail($)
weblink_FwDetail($@)
{
my ($d)= @_;
my ($d, $text)= @_;
my $alias= AttrVal($d, "alias", $d);
my $ret = "<br>";
$ret .= "$text " if($text);
$ret .= FW_pHPlain("detail=$d", $alias) if(!$FW_subdir);
$ret .= "<br>";
return $ret;
@ -115,10 +116,10 @@ weblink_FwFn($$$$)
my @va = split(":", $link, 3);
if($wltype eq "fileplot" &&
(@va != 3 || !$defs{$va[0]} || !$defs{$va[0]}{currentlogfile})) {
$ret .= "Broken definition for fileplot $d: $link<br>";
$ret .= weblink_FwDetail($d, "Broken definition ");
} elsif ($wltype eq "dbplot" && (@va != 2 || !$defs{$va[0]})) {
$ret .= "Broken definition for dbplot $d: $link<br>";
$ret .= weblink_FwDetail($d, "Broken definition ");
} else {
if(defined($va[2]) && $va[2] eq "CURRENT") {

View File

@ -82,6 +82,7 @@
<a href="#DbLog">DbLog</a> &nbsp;
<a href="#dewpoint">dewpoint</a> &nbsp;
<a href="#dummy">dummy</a> &nbsp;
<a href="#eventTypes">eventTypes</a> &nbsp;
<a href="#FHEM2FHEM">FHEM2FHEM</a> &nbsp;
<a href="#FHEMWEB">FHEMWEB</a> &nbsp;
<a href="#FB_CALLMONITOR">FB_CALLMONITOR</a> &nbsp;

View File

@ -82,6 +82,7 @@
<a href="#DbLog">DbLog</a> &nbsp;
<a href="#dewpoint">dewpoint</a> &nbsp;
<a href="#dummy">dummy</a> &nbsp;
<a href="#eventTypes">eventTypes</a> &nbsp;
<a href="#FHEM2FHEM">FHEM2FHEM</a> &nbsp;
<a href="#FHEMWEB">FHEMWEB</a> &nbsp;
<a href="#FB_CALLMONITOR">FB_CALLMONITOR</a> &nbsp;

View File

@ -126,7 +126,8 @@ sub CommandTrigger($$);
#Special values in %modules (used if set):
# DefFn - define a "device" of this type
# UndefFn - clean up at delete
# UndefFn - clean up (delete timer, close fd), called by delete and rereadcfg
# DeleteFn - clean up (delete logfile), called by delete after UndefFn
# ParseFn - Interpret a raw message
# ListFn - details for this "device"
# SetFn - set/activate this device
@ -1412,6 +1413,11 @@ CommandDelete($$)
push @rets, $ret;
next;
}
$ret = CallFn($sdev, "DeleteFn", $defs{$sdev}, $sdev);
if($ret) {
push @rets, $ret;
next;
}
# Delete releated hashes
foreach my $p (keys %selectlist) {

View File

@ -235,9 +235,13 @@ FW_selChange(sel, list, elName)
var value;
var l = list.split(" ");
for(var i=0; i < l.length; i++) {
var nv = l[i].split(":",2);
if(nv[0] == sel) {
value = nv[1]; break;
cmd = l[i];
var off = l[i].indexOf(":");
if(off >= 0)
cmd = l[i].substring(0, off);
if(cmd == sel) {
if(off >= 0)
value = l[i].substring(off+1);
}
}
var el = document.getElementsByName(elName)[0];