92_FileLog.pm 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701
  1. ##############################################
  2. # $Id: 92_FileLog.pm 17181 2018-08-20 17:23:26Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use IO::File;
  7. # This block is only needed when FileLog is loaded bevore FHEMWEB
  8. sub FW_pO(@);
  9. sub FW_pH(@);
  10. sub FW_addContent(;$);
  11. use vars qw($FW_ME); # webname (default is fhem)
  12. use vars qw($FW_RET); # Returned data (html)
  13. use vars qw($FW_RETTYPE);
  14. use vars qw($FW_cmdret); # error msg forwarding from toSVG
  15. use vars qw($FW_detail); # for redirect after toSVG
  16. use vars qw($FW_plotmode);# Global plot mode (WEB attribute), used by weblink
  17. use vars qw($FW_plotsize);# Global plot size (WEB attribute), used by weblink
  18. use vars qw($FW_ss); # is smallscreen
  19. use vars qw($FW_wname); # Web instance
  20. use vars qw(%FW_pos); # scroll position
  21. use vars qw(%FW_webArgs); # all arguments specified in the GET
  22. sub FileLog_seekTo($$$$$);
  23. sub FileLog_dailySwitch($);
  24. #####################################
  25. sub
  26. FileLog_Initialize($)
  27. {
  28. my ($hash) = @_;
  29. $hash->{DefFn} = "FileLog_Define";
  30. $hash->{SetFn} = "FileLog_Set";
  31. $hash->{GetFn} = "FileLog_Get";
  32. $hash->{UndefFn} = "FileLog_Undef";
  33. #$hash->{DeleteFn} = "FileLog_Delete";
  34. $hash->{NotifyFn} = "FileLog_Log";
  35. $hash->{AttrFn} = "FileLog_Attr";
  36. # logtype is used by the frontend
  37. no warnings 'qw';
  38. my @attrList = qw(
  39. addStateEvent:0,1
  40. archiveCompress
  41. archivecmd
  42. archivedir
  43. createGluedFile:0,1
  44. disable:0,1
  45. disabledForIntervals
  46. eventOnThreshold
  47. ignoreRegexp
  48. label
  49. logtype
  50. mseclog:1,0
  51. nrarchive
  52. reformatFn
  53. );
  54. use warnings 'qw';
  55. $hash->{AttrList} = join(" ", @attrList);
  56. $hash->{FW_summaryFn} = "FileLog_fhemwebFn";
  57. $hash->{FW_detailFn} = "FileLog_fhemwebFn";
  58. $hash->{SVG_sampleDataFn} = "FileLog_sampleDataFn";
  59. $hash->{SVG_regexpFn} = "FileLog_regexpFn";
  60. $data{FWEXT}{"/FileLog_toSVG"}{CONTENTFUNC} = "FileLog_toSVG";
  61. $data{FWEXT}{"/FileLog_logWrapper"}{CONTENTFUNC} = "FileLog_logWrapper";
  62. InternalTimer(time()+0.1, sub() { # Forum #39792
  63. map { HandleArchiving($defs{$_},1) } devspec2array("TYPE=FileLog");
  64. FileLog_dailySwitch($hash); # Forum #42415
  65. }, $hash, 0);
  66. }
  67. sub
  68. FileLog_dailySwitch($)
  69. {
  70. my ($hash) = @_;
  71. map { FileLog_Switch($defs{$_}) } devspec2array("TYPE=FileLog");
  72. my $t = time();
  73. my $off = fhemTzOffset($t);
  74. $t = 86400*(int(($t+$off)/86400)+1)+1-$off; # tomorrow, 1s after midnight
  75. InternalTimer($t, "FileLog_dailySwitch", $hash, 0);
  76. }
  77. #####################################
  78. sub
  79. FileLog_Define($@)
  80. {
  81. my ($hash, $def) = @_;
  82. my @a = split("[ \t][ \t]*", $def);
  83. my $fh;
  84. if(@a == 5 && $a[4] eq "readonly") {
  85. $hash->{READONLY} = 1;
  86. pop(@a);
  87. }
  88. return "wrong syntax: define <name> FileLog filename regexp [readonly]"
  89. if(int(@a) != 4);
  90. return "Bad regexp: starting with *" if($a[3] =~ m/^\*/);
  91. eval { "Hallo" =~ m/^$a[3]$/ };
  92. return "Bad regexp: $@" if($@);
  93. my @t = localtime;
  94. my $f = ResolveDateWildcards($a[2], @t);
  95. if(!$hash->{READONLY}) {
  96. $fh = new IO::File ">>$f";
  97. return "Can't open $f: $!" if(!defined($fh));
  98. }
  99. $hash->{FH} = $fh;
  100. $hash->{FD} = $fh->fileno() if($fh);
  101. $hash->{REGEXP} = $a[3];
  102. $hash->{logfile} = $a[2];
  103. $hash->{currentlogfile} = $f;
  104. $hash->{STATE} = "active";
  105. InternalTimer(0, sub(){ notifyRegexpChanged($hash, $a[3]); }, $hash);
  106. return undef;
  107. }
  108. #####################################
  109. sub
  110. FileLog_Undef($$)
  111. {
  112. my ($hash, $name) = @_;
  113. close($hash->{FH}) if($hash->{FH});
  114. return undef;
  115. }
  116. # Unused
  117. sub
  118. FileLog_Delete($$)
  119. {
  120. my ($hash, $name) = @_;
  121. return if(!$hash->{currentlogfile});
  122. unlink($hash->{currentlogfile});
  123. return undef;
  124. }
  125. sub
  126. FileLog_Switch($)
  127. {
  128. my ($log) = @_;
  129. my $fh = $log->{FH};
  130. my @t = localtime;
  131. my $cn = ResolveDateWildcards($log->{logfile}, @t);
  132. if($cn ne $log->{currentlogfile}) { # New logfile
  133. $log->{currentlogfile} = $cn;
  134. return 1 if($log->{READONLY});
  135. $fh->close() if($fh);
  136. HandleArchiving($log);
  137. $fh = new IO::File ">>$cn";
  138. if(!defined($fh)) {
  139. Log3 $log, 0, "Can't open $cn";
  140. return 0;
  141. }
  142. $log->{FH} = $fh;
  143. $log->{FD} = $fh->fileno();
  144. setReadingsVal($log, "linesInTheFile", 0, TimeNow());
  145. return 1;
  146. }
  147. return 0;
  148. }
  149. #####################################
  150. sub
  151. FileLog_Log($$)
  152. {
  153. # Log is my entry, Dev is the entry of the changed device
  154. my ($log, $dev) = @_;
  155. return if($log->{READONLY});
  156. my $ln = $log->{NAME};
  157. return if(IsDisabled($ln));
  158. my $events = deviceEvents($dev, AttrVal($ln, "addStateEvent", 0));
  159. return if(!$events);
  160. my $n = $dev->{NAME};
  161. my $re = $log->{REGEXP};
  162. my $iRe = AttrVal($ln, "ignoreRegexp", undef);
  163. my $max = int(@{$events});
  164. my $tn = $dev->{NTFY_TRIGGERTIME};
  165. if($log->{mseclog}) {
  166. my ($seconds, $microseconds) = gettimeofday();
  167. $tn .= sprintf(".%03d", $microseconds/1000);
  168. }
  169. my $ct = $dev->{CHANGETIME};
  170. my $fh;
  171. my $switched;
  172. my $written = 0;
  173. for (my $i = 0; $i < $max; $i++) {
  174. my $s = $events->[$i];
  175. $s = "" if(!defined($s));
  176. my $t = (($ct && $ct->[$i]) ? $ct->[$i] : $tn);
  177. if($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/ || "$t:$n:$s" =~ m/^$re$/) {
  178. next if($iRe && ($n =~ m/^$iRe$/ || "$n:$s" =~ m/^$iRe$/));
  179. $t =~ s/ /_/; # Makes it easier to parse with gnuplot
  180. if(!$switched) {
  181. FileLog_Switch($log);
  182. $switched = 1;
  183. }
  184. $fh = $log->{FH};
  185. $s =~ s/\n/ /g;
  186. print $fh "$t $n $s\n";
  187. $written++;
  188. }
  189. }
  190. return "" if(!$written);
  191. if($fh) {
  192. $fh->flush;
  193. # Skip sync, it costs too much HD strain, esp. on SSD
  194. # $fh->sync if !($^O eq 'MSWin32'); #not implemented in Windows
  195. }
  196. my $owr = ReadingsVal($ln, "linesInTheFile", 0);
  197. my $eot = AttrVal($ln, "eventOnThreshold", 0);
  198. if($eot && ($owr+$written) % $eot == 0) {
  199. readingsSingleUpdate($log, "linesInTheFile", $owr+$written, 1);
  200. } else {
  201. setReadingsVal($log, "linesInTheFile", $owr+$written, $tn);
  202. }
  203. return "";
  204. }
  205. ###################################
  206. sub
  207. FileLog_Attr(@)
  208. {
  209. my @a = @_;
  210. my $do = 0;
  211. if($a[2] eq "mseclog") {
  212. $defs{$a[1]}{mseclog} = ($a[0] eq "set" && (!defined($a[3]) || $a[3]) );
  213. return;
  214. }
  215. if($a[0] eq "set" && $a[2] eq "ignoreRegexp") {
  216. return "Missing argument for ignoreRegexp" if(!defined($a[3]));
  217. eval { "HALLO" =~ m/$a[3]/ };
  218. return $@;
  219. }
  220. if($a[0] eq "set" && $a[2] eq "disable") {
  221. $do = (!defined($a[3]) || $a[3]) ? 1 : 2;
  222. }
  223. $do = 2 if($a[0] eq "del" && (!$a[2] || $a[2] eq "disable"));
  224. return if(!$do);
  225. $defs{$a[1]}{STATE} = ($do == 1 ? "disabled" : "active");
  226. return undef;
  227. }
  228. ###################################
  229. sub
  230. FileLog_Set($@)
  231. {
  232. my ($hash, @a) = @_;
  233. my $me = $hash->{NAME};
  234. return "no set argument specified" if(int(@a) < 2);
  235. my %sets = (reopen=>0, clear=>0, absorb=>1, addRegexpPart=>2,
  236. removeRegexpPart=>1);
  237. my $cmd = $a[1];
  238. if(!defined($sets{$cmd})) {
  239. my $r = "Unknown argument $cmd, choose one of ".join(" ",sort keys %sets);
  240. my $fllist = join(",", grep { $me ne $_ } devspec2array("TYPE=FileLog"));
  241. $r =~ s/absorb/absorb:$fllist/;
  242. $r =~ s/clear/clear:noArg/;
  243. $r =~ s/reopen/reopen:noArg/;
  244. return $r;
  245. }
  246. return "$cmd needs $sets{$cmd} parameter(s)" if(@a-$sets{$cmd} != 2);
  247. if(($cmd eq "reopen") or ($cmd eq "clear")) {
  248. if(!FileLog_Switch($hash)) { # No rename, reopen anyway
  249. my $fh = $hash->{FH};
  250. my $cn = $hash->{currentlogfile};
  251. $fh->close();
  252. if($cmd eq "clear") {
  253. $fh = new IO::File(">$cn");
  254. setReadingsVal($hash, "linesInTheFile", 0, TimeNow());
  255. } else {
  256. $fh = new IO::File(">>$cn");
  257. }
  258. return "Can't open $cn" if(!defined($fh));
  259. $hash->{FH} = $fh;
  260. $hash->{FD} = $fh->fileno();
  261. }
  262. } elsif($cmd eq "addRegexpPart") {
  263. my %h;
  264. my $re = "$a[2]:$a[3]";
  265. map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP});
  266. $h{$re} = 1;
  267. $re = join("|", sort keys %h);
  268. return "Bad regexp: starting with *" if($re =~ m/^\*/);
  269. eval { "Hallo" =~ m/^$re$/ };
  270. return "Bad regexp: $@" if($@);
  271. $hash->{REGEXP} = $re;
  272. $hash->{DEF} = $hash->{logfile} ." $re";
  273. notifyRegexpChanged($hash, $re);
  274. } elsif($cmd eq "removeRegexpPart") {
  275. my %h;
  276. map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP});
  277. return "Cannot remove regexp part: not found" if(!$h{$a[2]});
  278. return "Cannot remove last regexp part" if(int(keys(%h)) == 1);
  279. delete $h{$a[2]};
  280. my $re = join("|", sort keys %h);
  281. return "Bad regexp: starting with *" if($re =~ m/^\*/);
  282. eval { "Hallo" =~ m/^$re$/ };
  283. return "Bad regexp: $@" if($@);
  284. $hash->{REGEXP} = $re;
  285. $hash->{DEF} = $hash->{logfile} ." $re";
  286. notifyRegexpChanged($hash, $re);
  287. } elsif($cmd eq "absorb") {
  288. my $victim = $a[2];
  289. return "need another FileLog as argument."
  290. if(!$victim ||
  291. !$defs{$victim} ||
  292. $defs{$victim}{TYPE} ne "FileLog" ||
  293. $victim eq $me);
  294. my $vh = $defs{$victim};
  295. my $mylogfile = $hash->{currentlogfile};
  296. return "Cant open the associated files"
  297. if(!open(FH1, $mylogfile) ||
  298. !open(FH2, $vh->{currentlogfile}) ||
  299. !open(FH3, ">$mylogfile.new"));
  300. my $fh = $hash->{FH};
  301. $fh->close();
  302. my $b1 = <FH1>; my $b2 = <FH2>;
  303. while(defined($b1) && defined($b2)) {
  304. if($b1 lt $b2) {
  305. print FH3 $b1; $b1 = <FH1>;
  306. } else {
  307. print FH3 $b2; $b2 = <FH2>;
  308. }
  309. }
  310. while($b1 = <FH1>) { print FH3 $b1; }
  311. while($b2 = <FH2>) { print FH3 $b2; }
  312. close(FH1); close(FH2); close(FH3);
  313. rename("$mylogfile.new", $mylogfile);
  314. $fh = new IO::File(">>$mylogfile");
  315. $hash->{FH} = $fh;
  316. $hash->{FD} = $fh->fileno();
  317. $hash->{REGEXP} .= "|".$vh->{REGEXP};
  318. $hash->{DEF} = $hash->{logfile} . " ". $hash->{REGEXP};
  319. notifyRegexpChanged($hash, $hash->{REGEXP});
  320. CommandDelete(undef, $victim);
  321. }
  322. return undef;
  323. }
  324. sub
  325. FileLog_loadSVG()
  326. {
  327. if(!$modules{SVG}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_SVG.pm") {
  328. my $ret = CommandReload(undef, "98_SVG");
  329. Log3 undef, 1, $ret if($ret);
  330. }
  331. }
  332. #########################
  333. sub
  334. FileLog_fhemwebFn($$$$)
  335. {
  336. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  337. return "<div id=\"$d\" align=\"center\" class=\"FileLog col2\">".
  338. "$defs{$d}{STATE}</div>" if($FW_ss && $pageHash);
  339. my $row = 0;
  340. my $ret = sprintf("<table class=\"FileLog %swide\">",
  341. $pageHash ? "" : "block ");
  342. foreach my $f (FW_fileList($defs{$d}{logfile})) {
  343. my $class = (!$pageHash ? (($row++&1)?"odd":"even") : "");
  344. $ret .= "<tr class=\"$class\">";
  345. $ret .= "<td><div class=\"dname\">$f</div></td>";
  346. my $idx = 0;
  347. foreach my $ln (split(",", AttrVal($d, "logtype", "text"))) {
  348. if($FW_ss && $idx++) {
  349. $ret .= "</tr><tr class=\"".(($row++&1)?"odd":"even")."\"><td>";
  350. }
  351. my ($lt, $name) = split(":", $ln);
  352. $name = $lt if(!$name);
  353. $ret .= FW_pH("$FW_ME/FileLog_logWrapper&dev=$d&type=$lt&file=$f",
  354. "<div class=\"dval\">$name</div>", 1, "dval", 1);
  355. }
  356. $ret .= "</tr>";
  357. }
  358. $ret .= "</table>";
  359. return $ret if($pageHash);
  360. # DETAIL only from here on
  361. my $hash = $defs{$d};
  362. $ret .= "<br>Regexp parts";
  363. $ret .= "<br><table class=\"block wide\">";
  364. my @ra = split(/\|/, $hash->{REGEXP});
  365. if(@ra > 1) {
  366. foreach my $r (@ra) {
  367. $ret .= "<tr class=\"".(($row++&1)?"odd":"even")."\">";
  368. my $cmd = "cmd.X= set $d removeRegexpPart&val.X=$r"; # =.set: avoid JS
  369. $ret .= "<td>$r</td>";
  370. $ret .= FW_pH("$cmd&detail=$d", "removeRegexpPart", 1,undef,1);
  371. $ret .= "</tr>";
  372. }
  373. }
  374. my @et = devspec2array("TYPE=eventTypes");
  375. if(!@et) {
  376. $ret .= FW_pH("$FW_ME/docs/commandref.html#eventTypes",
  377. "To add a regexp an eventTypes definition is needed",
  378. 1, undef, 1);
  379. } else {
  380. my %dh;
  381. my $etList = AnalyzeCommand(undef, "get $et[0] list");
  382. $etList = "" if(!$etList);
  383. foreach my $l (split("\n", $etList)) {
  384. my @a = split(/[ \r\n]/, $l);
  385. $a[1] = "" if(!defined($a[1]));
  386. $a[1] =~ s/\.\*//g;
  387. $a[1] =~ s/,.*//g;
  388. next if(@a < 2);
  389. $dh{$a[0]}{".*"} = 1;
  390. $dh{$a[0]}{$a[1].".*"} = 1;
  391. }
  392. my $list = "";
  393. foreach my $dev (sort keys %dh) {
  394. $list .= " $dev:" . join(",", sort keys %{$dh{$dev}});
  395. }
  396. $list =~ s/(['"])/./g;
  397. $ret .= "<tr class=\"".(($row++&1)?"odd":"even")."\">";
  398. $ret .= '<td colspan="2">';
  399. $ret .= FW_detailSelect($d, "set", $list, "addRegexpPart");
  400. $ret .= "</td></tr>";
  401. }
  402. $ret .= "</table>";
  403. my $newIdx=1;
  404. while($defs{"SVG_${d}_$newIdx"}) {
  405. $newIdx++;
  406. }
  407. my $name = "SVG_${d}_$newIdx";
  408. $ret .= FW_pH("cmd=define $name SVG $d:template:CURRENT;".
  409. "set $name copyGplotFile&detail=$name",
  410. "<div class=\"dval\">Create SVG plot</div>", 0, "dval", 1);
  411. return $ret;
  412. }
  413. ###################################
  414. sub
  415. FileLog_toSVG($)
  416. {
  417. my ($arg) = @_;
  418. FW_digestCgi($arg);
  419. return("text/html;", "bad url: cannot create SVG def")
  420. if(!defined($FW_webArgs{arg}));
  421. my @aa = split(":", $FW_webArgs{arg});
  422. my $max = 0;
  423. for my $d (keys %defs) {
  424. $max = ($1+1) if($d =~ m/^SVG_(\d+)$/ && $1 >= $max);
  425. }
  426. $defs{$aa[0]}{currentlogfile} =~ m,([^/]*)$,;
  427. $aa[2] = "CURRENT" if($1 eq $aa[2]);
  428. $FW_cmdret = FW_fC("define SVG_$max SVG $aa[0]:$aa[1]:$aa[2]");
  429. $FW_detail = "SVG_$max" if(!$FW_cmdret);
  430. return;
  431. }
  432. ######################
  433. # Show the content of the log (plain text), or an image and offer a link
  434. # to convert it to an SVG instance
  435. # If text and no reverse required, try to return the data as a stream;
  436. sub
  437. FileLog_logWrapper($)
  438. {
  439. my ($cmd) = @_;
  440. my $d = $FW_webArgs{dev};
  441. my $type = $FW_webArgs{type};
  442. my $file = $FW_webArgs{file};
  443. my $ret = "";
  444. if(!$d || !$type || !$file) {
  445. FW_addContent(">FileLog_logWrapper: bad arguments</div");
  446. return 0;
  447. }
  448. if(defined($type) && $type eq "text") {
  449. $defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
  450. my $path = "$1/$file";
  451. $path =~ s/%L/$attr{global}{logdir}/g
  452. if($path =~ m/%/ && $attr{global}{logdir});
  453. $path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
  454. FW_addContent();
  455. FW_pO "<div class=\"tiny\">" if($FW_ss);
  456. FW_pO "<pre class=\"log\">";
  457. my $suffix = "</pre>".($FW_ss ? "</div>" : "")."</div>";
  458. my $reverseLogs = AttrVal($FW_wname, "reverseLogs", 0);
  459. if(!$reverseLogs) {
  460. $suffix .= "</body></html>";
  461. return FW_returnFileAsStream($path, $suffix, "text/html", 1, 0);
  462. }
  463. if(!open(FH, $path)) {
  464. FW_addContent(">$path: $!</div></body></html");
  465. return 0;
  466. }
  467. my $cnt = join("", reverse <FH>);
  468. close(FH);
  469. $cnt = FW_htmlEscape($cnt);
  470. FW_pO $cnt;
  471. FW_pO $suffix;
  472. return 1;
  473. } else {
  474. FileLog_loadSVG();
  475. FW_pO "<script type='text/javascript' src='$FW_ME/pgm2/svg.js'></script>";
  476. FW_addContent();
  477. FW_pO "<br>";
  478. if(AttrVal($d,"plotmode",$FW_plotmode) ne "gnuplot") {
  479. FW_pO SVG_zoomLink("$cmd;zoom=-1", "Zoom-in", "zoom in");
  480. FW_pO SVG_zoomLink("$cmd;zoom=1", "Zoom-out","zoom out");
  481. FW_pO SVG_zoomLink("$cmd;off=-1", "Prev", "prev");
  482. FW_pO SVG_zoomLink("$cmd;off=1", "Next", "next");
  483. }
  484. FW_pO "<table><tr><td>";
  485. FW_pO "<td>";
  486. my $logtype = $defs{$d}{NAME};
  487. my $wl = "&amp;pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
  488. my $arg = "$FW_ME/SVG_showLog&dev=$logtype&logdev=$d".
  489. "&gplotfile=$type&logfile=$file$wl";
  490. if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
  491. my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
  492. FW_pO "<embed src=\"$arg\" type=\"image/svg+xml\" " .
  493. "width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
  494. } else {
  495. FW_pO "<img src=\"$arg\"/>";
  496. }
  497. FW_pO "<br>";
  498. FW_pH "$FW_ME/FileLog_toSVG&arg=$d:$type:$file", "Create SVG instance";
  499. FW_pO "</td>";
  500. FW_pO "</td></tr></table>";
  501. FW_pO "</div>";
  502. }
  503. return 0;
  504. }
  505. ###################################
  506. # We use this function to be able to scroll/zoom in the plots created from the
  507. # logfile. When outfile is specified, it is used with gnuplot post-processing,
  508. # when outfile is "-" it is used to create SVG graphics
  509. #
  510. # Up till now following functions are impemented:
  511. # - int (to cut off % from a number, as for the actuator)
  512. # - delta-h / delta-d to get rain/h and rain/d values from continuous data.
  513. #
  514. # It will set the %data values
  515. # mindate<x>, min<x>, maxdate<x>, max<x>, avg<x>, cnt<x>, currdate<x>,
  516. # currval<x>, sum<x>
  517. # for each requested column, beginning with <x> = 1
  518. sub
  519. FileLog_Get($@)
  520. {
  521. my ($hash, @a) = @_;
  522. return "Usage: get $a[0] <infile> <outfile> <from> <to> [<column_spec>...]\n".
  523. " where column_spec is <col>:<regexp>:<default>:<fn>\n" .
  524. " see the FileLogGet entries in the .gplot files\n" .
  525. " <infile> is without directory, - means the current file\n" .
  526. " <outfile> is a prefix, - means stdout\n"
  527. if(int(@a) < 4);
  528. shift @a;
  529. my $inf = shift @a;
  530. my $outf = shift @a;
  531. my $from = shift @a;
  532. my $to = shift @a; # Now @a contains the list of column_specs
  533. my $internal;
  534. my $name = $hash->{NAME};
  535. if($outf eq "INT") {
  536. $outf = "-";
  537. $internal = 1;
  538. }
  539. my $reformatFn = AttrVal($name, "reformatFn", "");
  540. my $tempread;
  541. if($inf eq "-") {
  542. # In case now is after midnight, before the first event is logged.
  543. FileLog_Switch($hash);
  544. $inf = $hash->{currentlogfile};
  545. } else {
  546. my $linf;
  547. if($inf eq "CURRENT") {
  548. # Try to guess
  549. if($from =~ m/^(....)-(..)-(..)/) {
  550. $linf = $hash->{logfile};
  551. my ($Y,$m,$d) = ($1,$2,$3);
  552. sub expandFileWildcards($$$$) {
  553. my ($f,$Y,$m,$d)=@_;
  554. return ResolveDateWildcards($f,
  555. localtime(time_str2num("$Y-$m-$d 00:00:00")));
  556. };
  557. $linf=expandFileWildcards($linf,$Y,$m,$d);
  558. if(AttrVal($name, "createGluedFile", 0)) {
  559. if($to =~ m/^(....)-(..)-(..)/) {
  560. my $linf_to = $hash->{logfile};
  561. my ($Y,$m,$d) = ($1,$2,$3);
  562. $linf_to=expandFileWildcards($linf_to,$Y,$m,$d);
  563. if($linf ne $linf_to){ # use to log files
  564. $tempread=$linf_to.".transit.temp.log";
  565. if(open(my $temp,'>',$tempread)){
  566. if(open(my $i,'<',$linf)){
  567. print $temp join("",<$i>);
  568. close($i);
  569. }
  570. if(open(my $i,'<',$linf_to)){
  571. print $temp join("",<$i>);
  572. close($i);
  573. }
  574. $linf=$tempread;
  575. close($temp);
  576. }
  577. }
  578. }
  579. }
  580. $linf = $hash->{currentlogfile} if($linf =~ m/%/ || ! -f $linf);
  581. } else {
  582. $linf = $hash->{currentlogfile};
  583. }
  584. } else {
  585. $linf = "$1/$inf" if($hash->{currentlogfile} =~ m,^(.*)/[^/]*$,);
  586. $linf = "" if(!$linf); # Missing log directory
  587. }
  588. # Look for the file in the log directory...
  589. if(!-f $linf) {
  590. # ... or in the archivelog
  591. $linf = AttrVal($name, "archivedir",".") ."/". $inf;
  592. }
  593. $inf = $linf;
  594. }
  595. Log3 $name, 4, "$name get: Input file $inf, from:$from to:$to";
  596. my $ifh = new IO::File $inf if($inf);
  597. FileLog_seekTo($inf, $ifh, $hash, $from, $reformatFn) if($ifh);
  598. # Return the the plain file data, $outf is ignored
  599. if(!@a) {
  600. return "" if(!$ifh);
  601. my $out = "";
  602. while(my $l = <$ifh>) {
  603. if($reformatFn) { no strict; $l = &$reformatFn($l); use strict; }
  604. next if($l lt $from);
  605. last if($l gt $to);
  606. $out .= $l;
  607. }
  608. return $out;
  609. }
  610. #############
  611. # Digest the input.
  612. # last1: first delta value after d/h change
  613. # last2: last delta value recorded (for the very last entry)
  614. # last3: last delta timestamp (d or h)
  615. my (@d, @fname);
  616. my (@min, @max, @sum, @cnt, @lastv, @lastd, @mind, @maxd, @firstv, @firstd);
  617. for(my $i = 0; $i < int(@a); $i++) {
  618. my @fld = split(":", $a[$i], 4);
  619. my %h;
  620. if($outf ne "-") {
  621. $fname[$i] = "$outf.$i";
  622. $h{fh} = new IO::File "> $fname[$i]";
  623. }
  624. $h{re} = $fld[1]; # Filter: regexp
  625. $h{df} = defined($fld[2]) ? $fld[2] : ""; # default value
  626. $h{fn} = $fld[3]; # function
  627. $h{didx} = 10 if($fld[3] && $fld[3] eq "delta-d"); # delta idx, substr len
  628. $h{didx} = 13 if($fld[3] && $fld[3] eq "delta-h");
  629. if($fld[0] =~ m/"(.*)"/o) {
  630. $h{col} = $1;
  631. $h{type} = 0;
  632. } else {
  633. $h{col} = $fld[0]-1;
  634. $h{type} = 1;
  635. }
  636. if($h{fn}) {
  637. $h{type} = 4;
  638. $h{type} = 2 if($h{didx});
  639. $h{type} = 3 if($h{fn} eq "int");
  640. }
  641. $h{ret} = "";
  642. $d[$i] = \%h;
  643. $min[$i] = 999999;
  644. $max[$i] = -999999;
  645. $sum[$i] = 0;
  646. $cnt[$i] = 0;
  647. $lastv[$i] = 0;
  648. $lastd[$i] = "undef";
  649. $firstv[$i] = 0;
  650. $firstd[$i] = "undef";
  651. $mind[$i] = "undef";
  652. $maxd[$i] = "undef";
  653. }
  654. my %lastdate;
  655. my $d; # Used by eval functions
  656. my ($rescan, $rescanNum, $rescanIdx, @rescanArr);
  657. $rescan = 0;
  658. RESCAN:
  659. for(;;) {
  660. my $l;
  661. if($rescan) {
  662. last if($rescanIdx<1 || !$rescanNum);
  663. $l = $rescanArr[$rescanIdx--];
  664. } else {
  665. $l = <$ifh> if($ifh);
  666. last if(!$l);
  667. if($reformatFn) { no strict; $l = &$reformatFn($l); use strict; }
  668. }
  669. next if($l lt $from && !$rescan);
  670. last if($l gt $to);
  671. my @fld = split("[ \r\n]+", $l); # 40% CPU
  672. for my $i (0..int(@a)-1) { # Process each req. field
  673. my $h = $d[$i];
  674. next if($rescan && $h->{ret});
  675. my @missingvals;
  676. next if($h->{re} && $l !~ m/$h->{re}/); # 20% CPU
  677. my $col = $h->{col};
  678. my $t = $h->{type};
  679. my $val = undef;
  680. my $dte = $fld[0];
  681. if($t == 0) { # Fixed text
  682. $val = $col;
  683. } elsif($t == 1) { # The column
  684. $val = $fld[$col] if(defined($fld[$col]));
  685. } elsif($t == 2) { # delta-h or delta-d
  686. next if($rescan);
  687. my $hd = $h->{didx}; # TimeStamp-Length
  688. my $ld = substr($fld[0],0,$hd); # TimeStamp-Part (hour or date)
  689. if(!defined($h->{last1}) || $h->{last3} ne $ld) {
  690. if(defined($h->{last1})) {
  691. my @lda = split("[_:]", $lastdate{$hd});
  692. my $ts = "12:00:00"; # middle timestamp
  693. $ts = "$lda[1]:30:00" if($hd == 13);
  694. my $v = $fld[$col]-$h->{last1};
  695. # $v = 0 if($v < 0); # Skip negative delta (why?)
  696. $dte = "$lda[0]_$ts";
  697. $val = sprintf("%g", $v);
  698. if($hd == 13) { # Generate missing 0 values / hour
  699. my @cda = split("[_:]", $ld);
  700. for(my $mi = $lda[1]+1; $mi < $cda[1]; $mi++) {
  701. push @missingvals, sprintf("%s_%02d:30:00 0\n", $lda[0], $mi);
  702. }
  703. }
  704. }
  705. $h->{last1} = $fld[$col];
  706. $h->{last3} = $ld;
  707. }
  708. $h->{last2} = $fld[$col];
  709. $lastdate{$hd} = $fld[0];
  710. } elsif($t == 3) { # int function
  711. $val = $1 if($fld[$col] =~ m/^(\d+).*/o);
  712. } else { # evaluate
  713. $cmdFromAnalyze = $h->{fn};
  714. $val = eval($cmdFromAnalyze);
  715. $cmdFromAnalyze = undef;
  716. }
  717. next if(!defined($val) || $val !~ m/^-?[.\d]+$/o);
  718. if($val < $min[$i]) {
  719. $min[$i] = $val;
  720. $mind[$i] = $dte;
  721. }
  722. if($val > $max[$i]) {
  723. $max[$i] = $val;
  724. $maxd[$i] = $dte;
  725. }
  726. $sum[$i] += $val;
  727. $cnt[$i]++;
  728. if($firstd[$i] eq "undef") {
  729. $firstv[$i] = $val;
  730. $firstd[$i] = $dte;
  731. }
  732. $lastv[$i] = $val;
  733. $lastd[$i] = $dte;
  734. map { $cnt[$i]++; $min[$i] = 0 if(0 < $min[$i]); } @missingvals;
  735. if($outf eq "-") {
  736. $h->{ret} .= "$dte $val\n";
  737. map { $h->{ret} .= $_ } @missingvals;
  738. } else {
  739. my $fh = $h->{fh}; # cannot use $h->{fh} in print directly
  740. print $fh "$dte $val\n";
  741. map { print $fh $_ } @missingvals;
  742. }
  743. $h->{count}++;
  744. $rescanNum--;
  745. last if(!$rescanNum);
  746. }
  747. }
  748. # If no value found for some of the required columns, then look for the last
  749. # matching entry outside of the range. Known as the "window left open
  750. # yesterday" problem
  751. if(!$rescan && $ifh) {
  752. $rescanNum = 0;
  753. map { $rescanNum++ if(!$d[$_]->{count} && $d[$_]->{df} eq "") } (0..$#a);
  754. if($rescanNum) {
  755. $rescan=1;
  756. my $buf;
  757. my $end = $hash->{pos}{"$inf:$from"};
  758. my $start = $end - 1024;
  759. $start = 0 if($start < 0);
  760. $ifh->seek($end, 0);
  761. my $l = <$ifh>;
  762. $end = $ifh->tell if($l && $l lt $from);
  763. $ifh->seek($start, 0);
  764. sysread($ifh, $buf, $end-$start);
  765. @rescanArr = split("\n", $buf);
  766. $rescanIdx = $#rescanArr;
  767. goto RESCAN;
  768. }
  769. }
  770. $ifh->close() if($ifh);
  771. unlink($tempread) if($tempread);
  772. my $ret = "";
  773. for(my $i = 0; $i < int(@a); $i++) {
  774. my $h = $d[$i];
  775. my $hd = $h->{didx};
  776. if($hd && $lastdate{$hd}) {
  777. my $val = defined($h->{last1}) ? $h->{last2}-$h->{last1} : 0;
  778. $min[$i] = $val if($min[$i] == 999999);
  779. $max[$i] = $val if($max[$i] == -999999);
  780. $lastv[$i] = $val if(!$lastv[$i]);
  781. $sum[$i] = ($sum[$i] ? $sum[$i] + $val : $val);
  782. $cnt[$i]++;
  783. my @lda = split("[_:]", $lastdate{$hd});
  784. my $ts = "12:00:00"; # middle timestamp
  785. $ts = "$lda[1]:30:00" if($hd == 13);
  786. my $line = sprintf("%s_%s %0.1f\n", $lda[0],$ts,
  787. defined($h->{last1}) ? $h->{last2}-$h->{last1} : 0);
  788. if($outf eq "-") {
  789. $h->{ret} .= $line;
  790. } else {
  791. my $fh = $h->{fh};
  792. print $fh $line;
  793. $h->{count}++;
  794. }
  795. }
  796. if($outf eq "-") {
  797. $h->{ret} .= "$from $h->{df}\n" if(!$h->{ret} && $h->{df} ne "");
  798. $ret .= $h->{ret} if($h->{ret});
  799. $ret .= "#$a[$i]\n";
  800. } else {
  801. my $fh = $h->{fh};
  802. if(!$h->{count} && $h->{df} ne "") {
  803. print $fh "$from $h->{df}\n";
  804. }
  805. $fh->close();
  806. }
  807. my $j = $i+1;
  808. $data{"min$j"} = $min[$i];
  809. $data{"max$j"} = $max[$i];
  810. $data{"avg$j"} = $cnt[$i] ? sprintf("%0.1f", $sum[$i]/$cnt[$i]) : 0;
  811. $data{"sum$j"} = $sum[$i];
  812. $data{"cnt$j"} = $cnt[$i];
  813. $data{"currval$j"} = $lastv[$i];
  814. $data{"currdate$j"} = $lastd[$i];
  815. $data{"firstval$j"} = $firstv[$i];
  816. $data{"firstdate$j"} = $firstd[$i];
  817. $data{"mindate$j"} = $mind[$i];
  818. $data{"maxdate$j"} = $maxd[$i];
  819. $data{"lastraw$j"} = $h->{last2} if($h->{last2});
  820. Log3 $name, 4,
  821. "$name get: line $j, regexp:".$d[$i]->{re}.", col:".$d[$i]->{col}.
  822. ", output lines:".$data{"cnt$j"};
  823. }
  824. if($internal) {
  825. $internal_data = \$ret;
  826. return undef;
  827. }
  828. return ($outf eq "-") ? $ret : join(" ", @fname);
  829. }
  830. ###############
  831. # this is not elegant. Assume, that current seek pos is after a cr/nl
  832. sub
  833. seekBackOneLine($$)
  834. {
  835. my ($fh, $pos) = @_;
  836. my $buf;
  837. while($pos > 0) { # skip current CR/NL
  838. $fh->seek(--$pos, 0);
  839. $fh->read($buf, 1);
  840. last if($buf ne "\n" && $buf ne "\r");
  841. }
  842. $fh->seek($pos, 0);
  843. while($pos > 0 && $fh->read($buf, 1)) {
  844. return ++$pos if($buf eq "\n" || $buf eq "\r");
  845. $fh->seek(--$pos, 0);
  846. }
  847. return 0;
  848. }
  849. ###################################
  850. #($1-40587)*86400+$2
  851. sub
  852. FileLog_seekTo($$$$$)
  853. {
  854. my ($fname, $fh, $hash, $ts, $reformatFn) = @_;
  855. # If its cached
  856. if($hash->{pos} && $hash->{pos}{"$fname:$ts"}) {
  857. $fh->seek($hash->{pos}{"$fname:$ts"}, 0);
  858. return;
  859. }
  860. $fh->seek(0, 2); # Go to the end
  861. my $upper = $fh->tell;
  862. my ($lower, $next, $last) = (0, $upper/2, -1);
  863. for(my $iter=0; $iter<200; $iter++) { # Binary search
  864. if($next == $last) {
  865. $fh->seek($next, 0);
  866. last;
  867. }
  868. $fh->seek($next, 0);
  869. my $data = <$fh>;
  870. if(!$data) {
  871. $last = $next;
  872. last;
  873. }
  874. if($reformatFn) { no strict; $data = &$reformatFn($data); use strict; }
  875. if($data !~ m/^\d\d\d\d-\d\d-\d\d_\d\d:\d\d:\d\d /o) {
  876. $next = seekBackOneLine($fh, $fh->tell);
  877. next;
  878. }
  879. $last = $next;
  880. if(!$data || $data lt $ts) {
  881. ($lower, $next) = ($next, int(($next+$upper)/2));
  882. } else {
  883. ($upper, $next) = ($next, int(($lower+$next)/2));
  884. }
  885. }
  886. $last = 0 if($last < 0); # Forum #46512
  887. $hash->{pos}{"$fname:$ts"} = $last;
  888. }
  889. sub
  890. FileLog_addTics($$)
  891. {
  892. my ($in, $p) = @_;
  893. return if(!$in || $in !~ m/^\((.*)\)$/);
  894. map { $p->{"\"$2\""}=1 if(m/^ *([^ ]+) ([^ ]+) */); } split(",",$1);
  895. }
  896. sub
  897. FileLog_sampleDataFn($$$$$)
  898. {
  899. my ($flName, $flog, $max, $conf, $wName) = @_;
  900. my $desc = "Input:Column,Regexp,DefaultValue,Function";
  901. my @htmlArr;
  902. my $fName = $defs{$flName}{currentlogfile};
  903. my $reformatFn = AttrVal($flName, "reformatFn", "");
  904. my $fh = new IO::File $fName;
  905. if(!$fh) {
  906. $fName = "<undefined>" if(!defined($fName));
  907. Log3 $wName, 1, "FileLog get sample data: $fName: $!";
  908. return ($desc, \@htmlArr, "");
  909. }
  910. $fh->seek(0, 2); # Go to the end
  911. my $sz = $fh->tell;
  912. $fh->seek($sz > 65536 ? $sz-65536 : 0, 0);
  913. my $data;
  914. $data = <$fh> if($sz > 65536); # discard the first/partial line
  915. my $maxcols = 0;
  916. my %h;
  917. while($data = <$fh>) {
  918. if($reformatFn) { no strict; $data = &$reformatFn($data); use strict; }
  919. my @cols = split(" ", $data);
  920. next if(@cols < 3);
  921. $maxcols = @cols if(@cols > $maxcols);
  922. $cols[2] = "*" if($cols[2] =~ m/^[-\.\d]+$/);
  923. $h{"$cols[1].$cols[2]"} = $data;
  924. $h{"$cols[1].*"} = "" if($cols[2] ne "*");
  925. }
  926. $fh->close();
  927. my $colnums = $maxcols;
  928. my $colregs = join(",", sort keys %h);
  929. my $example = join("<br>", grep /.+/,map { $h{$_} } sort keys %h);
  930. $colnums = join(",", 3..$colnums);
  931. my %tickh;
  932. FileLog_addTics($conf->{ytics}, \%tickh);
  933. FileLog_addTics($conf->{y2tics}, \%tickh);
  934. $colnums = join(",", sort keys %tickh).",$colnums" if(%tickh);
  935. for(my $r=0; $r < $max; $r++) {
  936. my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":::"), 4);
  937. my $ret = "";
  938. $f[1] =~ s/\\x(..)/chr(hex($1))/ge; # Convert \x3a to :
  939. $colregs .= ",$f[1]" if($f[1] && !$h{$f[1]});
  940. $ret .= SVG_sel("par_${r}_0", $colnums, $f[0], undef, "svgColumn");
  941. $ret .= SVG_sel("par_${r}_1", $colregs, $f[1], undef, "svgRegexp");
  942. $ret .= SVG_txt("par_${r}_2", "", $f[2], 2);
  943. $ret .= SVG_txt("par_${r}_3", "", $f[3],10);
  944. push @htmlArr, $ret;
  945. }
  946. return ($desc, \@htmlArr, $example);
  947. }
  948. sub
  949. FileLog_regexpFn($$)
  950. {
  951. my ($name, $filter) = @_;
  952. $filter = " $filter ";
  953. $filter =~ s/ [^: ]*:/ /g;
  954. $filter =~ s/:[^ ]* / /g;
  955. $filter =~ s/(^ | $)//g;
  956. $filter =~ s/ /|/g;
  957. return $filter;
  958. }
  959. 1;
  960. =pod
  961. =item helper
  962. =item summary log events to a file
  963. =item summary_DE schreibt Events in eine Logdatei
  964. =begin html
  965. <a name="FileLog"></a>
  966. <h3>FileLog</h3>
  967. <ul>
  968. <br>
  969. <a name="FileLogdefine"></a>
  970. <b>Define</b>
  971. <ul>
  972. <code>define &lt;name&gt; FileLog &lt;filename&gt; &lt;regexp&gt; [readonly]</code>
  973. <br><br>
  974. Log events to <code>&lt;filename&gt;</code>. The log format is
  975. <ul><code><br>
  976. YYYY-MM-DD_HH:MM:SS &lt;device&gt; &lt;event&gt;<br>
  977. <br></code></ul>
  978. The regexp will be checked against the device name
  979. devicename:event or timestamp:devicename:event combination.
  980. The regexp must match the complete string, not just a part of it.
  981. <br>
  982. <code>&lt;filename&gt;</code> may contain %-wildcards of the
  983. POSIX strftime function of the underlying OS (see your strftime manual).
  984. Common used wildcards are:
  985. <ul>
  986. <li><code>%d</code> day of month (01..31)</li>
  987. <li><code>%m</code> month (01..12)</li>
  988. <li><code>%Y</code> year (1970...)</li>
  989. <li><code>%w</code> day of week (0..6); 0 represents Sunday</li>
  990. <li><code>%j</code> day of year (001..366)</li>
  991. <li><code>%U</code> week number of year with Sunday as first day of week (00..53)</li>
  992. <li><code>%W</code> week number of year with Monday as first day of week (00..53)</li>
  993. </ul>
  994. FHEM also replaces <code>%L</code> by the value of the global logdir attribute.<br>
  995. Before using <code>%V</code> for ISO 8601 week numbers check if it is
  996. correctly supported by your system (%V may not be replaced, replaced by an
  997. empty string or by an incorrect ISO-8601 week number, especially
  998. at the beginning of the year)
  999. If you use <code>%V</code> you will also have to use %G
  1000. instead of %Y for the year!<br>
  1001. If readonly is specified, then the file is used only for visualisation, and
  1002. it is not opened for writing.
  1003. Examples:
  1004. <ul>
  1005. <code>define lamplog FileLog %L/lamp.log lamp</code><br>
  1006. <code>define wzlog FileLog ./log/wz-%Y-%U.log
  1007. wz:(measured-temp|actuator).*</code><br>
  1008. With ISO 8601 week numbers, if supported:<br>
  1009. <code>define wzlog FileLog ./log/wz-%G-%V.log
  1010. wz:(measured-temp|actuator).*</code><br>
  1011. </ul>
  1012. <br>
  1013. </ul>
  1014. <a name="FileLogset"></a>
  1015. <b>Set </b>
  1016. <ul>
  1017. <li>reopen
  1018. <ul>
  1019. Reopen a FileLog after making some manual changes to the
  1020. logfile.
  1021. </ul>
  1022. </li>
  1023. <li>clear
  1024. <ul>
  1025. Clears and reopens the logfile.
  1026. </ul>
  1027. </li>
  1028. <li>addRegexpPart &lt;device&gt; &lt;regexp&gt;
  1029. <ul>
  1030. add a regexp part, which is constructed as device:regexp. The parts
  1031. are separated by |. Note: as the regexp parts are resorted, manually
  1032. constructed regexps may become invalid.
  1033. </ul>
  1034. </li>
  1035. <li>removeRegexpPart &lt;re&gt;
  1036. <ul>
  1037. remove a regexp part. Note: as the regexp parts are resorted, manually
  1038. constructed regexps may become invalid.<br>
  1039. The inconsistency in addRegexpPart/removeRegexPart arguments originates
  1040. from the reusage of javascript functions.
  1041. </ul>
  1042. </li>
  1043. <li>absorb secondFileLog
  1044. <ul>
  1045. merge the current and secondFileLog into one file, add the regexp of the
  1046. secondFileLog to the current one, and delete secondFileLog.<br>
  1047. This command is needed to create combined plots (weblinks).<br>
  1048. <b>Notes:</b>
  1049. <ul>
  1050. <li>secondFileLog will be deleted (i.e. the FHEM definition).</li>
  1051. <li>only the current files will be merged.</li>
  1052. <li>weblinks using secondFilelog will become broken, they have to be
  1053. adopted to the new logfile or deleted.</li>
  1054. </ul>
  1055. </ul>
  1056. </li>
  1057. <br>
  1058. </ul>
  1059. <br>
  1060. <a name="FileLogget"></a>
  1061. <b>Get</b>
  1062. <ul>
  1063. <code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt;
  1064. &lt;to&gt; &lt;column_spec&gt; </code>
  1065. <br><br>
  1066. Read data from the logfile, used by frontends to plot data without direct
  1067. access to the file.<br>
  1068. <ul>
  1069. <li>&lt;infile&gt;<br>
  1070. Name of the logfile to open. Special case: "-" is the currently active
  1071. logfile, "CURRENT" opens the file corresponding to the "from"
  1072. parameter.
  1073. </li>
  1074. <li>&lt;outfile&gt;<br>
  1075. If it is "-", you get the data back on the current connection, else it
  1076. is the prefix for the output file. If more than one file is specified,
  1077. the data is separated by a comment line for "-", else it is written in
  1078. separate files, numerated from 0.
  1079. </li>
  1080. <li>&lt;from&gt; &lt;to&gt;<br>
  1081. Used to grep the data. The elements should correspond to the
  1082. timeformat or be an initial substring of it.</li>
  1083. <li>&lt;column_spec&gt;<br>
  1084. For each column_spec return a set of data in a separate file or
  1085. separated by a comment line on the current connection.<br>
  1086. Syntax: &lt;col&gt;:&lt;regexp&gt;:&lt;default&gt;:&lt;fn&gt;<br>
  1087. <ul>
  1088. <li>&lt;col&gt;
  1089. The column number to return, starting at 1 with the date.
  1090. If the column is enclosed in double quotes, then it is a fix text,
  1091. not a column number.</li>
  1092. <li>&lt;regexp&gt;
  1093. If present, return only lines containing the regexp. Case sensitive.
  1094. </li>
  1095. <li>&lt;default&gt;<br>
  1096. If no values were found and the default value is set, then return
  1097. one line containing the from value and this default. We need this
  1098. feature as gnuplot aborts if a dataset has no value at all.
  1099. </li>
  1100. <li>&lt;fn&gt;
  1101. One of the following:
  1102. <ul>
  1103. <li>int<br>
  1104. Extract the integer at the beginning og the string. Used e.g.
  1105. for constructs like 10%</li>
  1106. <li>delta-h or delta-d<br>
  1107. Return the delta of the values for a given hour or a given day.
  1108. Used if the column contains a counter, as is the case for the
  1109. KS300 rain column.</li>
  1110. <li>everything else<br>
  1111. The string is evaluated as a perl expression. @fld is the
  1112. current line splitted by spaces. Note: The string/perl
  1113. expression cannot contain spaces, as the part after the space
  1114. will be considered as the next column_spec.</li>
  1115. </ul></li>
  1116. </ul></li>
  1117. </ul>
  1118. <br><br>
  1119. Example:
  1120. <ul><code><br>
  1121. get outlog out-2008.log - 2008-01-01 2008-01-08 4:IR:int: 9:IR::
  1122. </code></ul>
  1123. <br>
  1124. </ul>
  1125. <a name="FileLogattr"></a>
  1126. <b>Attributes</b>
  1127. <ul>
  1128. <li><a href="#addStateEvent">addStateEvent</a></li><br><br>
  1129. <a name="archivedir"></a>
  1130. <a name="archivecmd"></a>
  1131. <a name="nrarchive"></a>
  1132. <li>archivecmd / archivedir / nrarchive<br>
  1133. When a new FileLog file is opened, the FileLog archiver wil be called.
  1134. This happens only, if the name of the logfile has changed (due to
  1135. time-specific wildcards, see the <a href="#FileLog">FileLog</a>
  1136. section), and there is a new entry to be written into the file.
  1137. <br>
  1138. If the attribute archivecmd is specified, then it will be started as a
  1139. shell command (no enclosing " is needed), and each % in the command
  1140. will be replaced with the name of the old logfile.<br>
  1141. If this attribute is not set, but nrarchive is set, then nrarchive old
  1142. logfiles are kept along the current one while older ones are moved to
  1143. archivedir (or deleted if archivedir is not set).<br>
  1144. Note: "old" means here the first ones in the alphabetically soreted
  1145. list. <br>
  1146. Note: setting these attributes for the global instance will effect the
  1147. <a href="#logfile">FHEM logfile</a> only.
  1148. </li><br>
  1149. <a name="archiveCompress"></a>
  1150. <li>archiveCompress<br>
  1151. If nrarchive, archivedir and archiveCompress is set, then the files
  1152. in the archivedir will be compressed.
  1153. </li><br>
  1154. <a name="createGluedFile"></a>
  1155. <li>createGluedFile<br>
  1156. If set (to 1), and the SVG-Plot requests a time-range wich is stored
  1157. in two files, a temporary file with the content of both files will be
  1158. created, in order to satisfy the request.
  1159. </li><br>
  1160. <li><a href="#disable">disable</a></li>
  1161. <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
  1162. <br>
  1163. <a name="eventOnThreshold"></a>
  1164. <li>eventOnThreshold<br>
  1165. If set (to a nonzero number), the event linesInTheFile will be
  1166. generated, if the lines in the file is a multiple of the set number.
  1167. Note: the counter is only correct for files created after this
  1168. feature was implemented. A FHEM crash or kill will falsify the counter.
  1169. </li><br>
  1170. <li><a href="#ignoreRegexp">ignoreRegexp</a></li>
  1171. <li><a href="#label">label</a><br></li>
  1172. <a name="logtype"></a>
  1173. <li>logtype<br>
  1174. Used by FHEMWEB to offer gnuplot/SVG images made from the
  1175. logs. The string is made up of tokens separated by comma (,), each
  1176. token specifies a different configuration. The token may contain a
  1177. colon (:), the part before the colon defines the name of the program,
  1178. the part after is the string displayed in the web frontend.<br>
  1179. Example:<br>
  1180. attr ks300log1 logtype
  1181. temp4rain10:Temp/Rain,hum6wind8:Hum/Wind,text:Raw-data
  1182. </li><br>
  1183. <li><a href="#mseclog">mseclog</a></li><br>
  1184. <a name="reformatFn"></a>
  1185. <li>reformatFn<br>
  1186. used to convert "foreign" logfiles for the SVG Module, contains the
  1187. name(!) of a function, which will be called with a "raw" line from the
  1188. original file, and has to return a line in "FileLog" format.<br>
  1189. E.g. to visualize the NTP loopstats, set reformatFn to ntpLoopstats, and
  1190. copy the following into your 99_myUtils.pm:
  1191. <pre><code>
  1192. sub
  1193. ntpLoopstats($)
  1194. {
  1195. my ($d) = @_;
  1196. return $d if($d !~ m/^(\d{5}) (\d+)\.(\d{3}) (.*)$/);
  1197. my ($r, $t) = ($4, FmtDateTime(($1-40587)*86400+$2));
  1198. $t =~ s/ /_/;
  1199. return "$t ntpLoopStats $r";
  1200. }</code></pre>
  1201. </li>
  1202. </ul>
  1203. <br>
  1204. </ul>
  1205. =end html
  1206. =begin html_DE
  1207. <a name="FileLog"></a>
  1208. <h3>FileLog</h3>
  1209. <ul>
  1210. <br>
  1211. <a name="FileLogdefine"></a>
  1212. <b>Define</b>
  1213. <ul>
  1214. <code>define &lt;name&gt; FileLog &lt;filename&gt; &lt;regexp&gt; [readonly]</code>
  1215. <br><br>
  1216. Speichert Ereignisse in einer Log-Datei mit Namen <code>&lt;filename&gt;</code>. Das Log-Format ist
  1217. <ul><code><br>
  1218. YYYY-MM-DD_HH:MM:SS &lt;device&gt; &lt;event&gt;<br>
  1219. <br></code></ul>
  1220. Der Ausdruck unter regexp wird anhand des Ger&auml;tenames &uuml;berpr&uuml;ft und zwar
  1221. devicename:event oder der timestamp:devicename:event-Kombination.
  1222. Der regexp muss mit dem kompletten String &uuml;bereinstimmen und nicht nur teilweise.
  1223. <br>
  1224. <code>&lt;filename&gt;</code> k&ouml;nnen %-wildcards der POSIX
  1225. strftime-Funktion des darunterliegenden OS enthalten (siehe auch strftime
  1226. Beschreibung).
  1227. Allgemein gebr&auml;uchliche Wildcards sind:
  1228. <ul>
  1229. <li><code>%d</code> Tag des Monats (01..31)</li>
  1230. <li><code>%m</code> Monat (01..12)</li>
  1231. <li><code>%Y</code> Jahr (1970...)</li>
  1232. <li><code>%w</code> Wochentag (0..6); beginnend mit Sonntag (0)</li>
  1233. <li><code>%j</code> Tag des Jahres (001..366)</li>
  1234. <li><code>%U</code> Wochennummer des Jahres, wobei Wochenbeginn = Sonntag (00..53)</li>
  1235. <li><code>%W</code> Wochennummer des Jahres, wobei Wochenbeginn = Montag (00..53)</li>
  1236. </ul>
  1237. FHEM ersetzt <code>%L</code> mit dem Wert des global logdir Attributes.<br>
  1238. Bevor <code>%V</code> f&uuml;r ISO 8601 Wochennummern verwendet werden,
  1239. muss &uuml;berpr&uuml;ft werden, ob diese Funktion durch das Brriebssystem
  1240. unterst&uuml;tzt wird (Es kann sein, dass %V nicht umgesetzt wird, durch
  1241. einen Leerstring ersetzt wird oder durch eine falsche ISO-Wochennummer
  1242. dargestellt wird - besonders am Jahresanfang)
  1243. Bei der Verwendung von <code>%V</code> muss gleichzeitig f&uuml;r das Jahr
  1244. ein <code>%G</code> anstelle von <code>%Y</code> benutzt werden.<br>
  1245. Falls man readonly spezifiziert, dann wird die Datei nur zum visualisieren
  1246. verwendet, und nicht zum Schreiben ge&ouml;ffnet.
  1247. <br>
  1248. Beispiele:
  1249. <ul>
  1250. <code>define lamplog FileLog %L/lamp.log lamp</code><br>
  1251. <code>define wzlog FileLog ./log/wz-%Y-%U.log
  1252. wz:(measured-temp|actuator).*</code><br>
  1253. Mit ISO 8601 Wochennummern falls unterst&uuml;tzt:<br>
  1254. <code>define wzlog FileLog ./log/wz-%G-%V.log
  1255. wz:(measured-temp|actuator).*</code><br>
  1256. </ul>
  1257. <br>
  1258. </ul>
  1259. <a name="FileLogset"></a>
  1260. <b>Set </b>
  1261. <ul>
  1262. <li>reopen
  1263. <ul>
  1264. Erneutes &Ouml;ffnen eines FileLogs nach h&auml;ndischen
  1265. &Auml;nderungen in dieser Datei.
  1266. </ul></li>
  1267. <li>clear
  1268. <ul>
  1269. L&ouml;schen und erneutes &Ouml;ffnen eines FileLogs.
  1270. </ul></li>
  1271. <li>addRegexpPart &lt;device&gt; &lt;regexp&gt;
  1272. <ul>
  1273. F&uuml;gt ein regexp Teil hinzu, der als device:regexp aufgebaut ist.
  1274. Die Teile werden nach Regexp-Regeln mit | getrennt. Achtung: durch
  1275. hinzuf&uuml;gen k&ouml;nnen manuell erzeugte Regexps ung&uuml;ltig
  1276. werden.
  1277. </ul></li>
  1278. <li>removeRegexpPart &lt;re&gt;
  1279. <ul>
  1280. Entfernt ein regexp Teil. Die Inkonsistenz von addRegexpPart /
  1281. removeRegexPart-Argumenten hat seinen Ursprung in der Wiederverwendung
  1282. von Javascript-Funktionen.
  1283. </ul></li>
  1284. <li>absorb secondFileLog
  1285. <ul>
  1286. F&uuml;hrt den gegenw&auml;rtigen Log und den secondFileLog zu einer
  1287. gemeinsamen Datei zusammen, f&uuml;gt danach die regexp des
  1288. secondFileLog dem gegenw&auml;rtigen Filelog hinzu und l&ouml;scht dann
  1289. anschlie&szlig;end das secondFileLog.<br>
  1290. Dieses Komanndo wird zur Erzeugung von kombinierten Plots (weblinks)
  1291. ben&ouml;tigt.<br>
  1292. <b>Hinweise:</b>
  1293. <ul>
  1294. <li>secondFileLog wird gel&ouml;scht (d.h. die FHEM-Definition und
  1295. die Datei selbst).</li>
  1296. <li>nur das aktuelle File wird zusammengef&uuml;hrt, keine
  1297. archivierten Versionen.</li>
  1298. <li>Weblinks, die das secondFilelog benutzen werden unbrauchbar, sie
  1299. m&uuml;ssen deshalb auf das neue Logfile angepasst oder
  1300. gel&ouml;scht werden.</li>
  1301. </ul>
  1302. </ul></li>
  1303. <br>
  1304. </ul>
  1305. <br>
  1306. <a name="FileLogget"></a>
  1307. <b>Get</b>
  1308. <ul>
  1309. <code>get &lt;name&gt; &lt;infile&gt; &lt;outfile&gt; &lt;from&gt;
  1310. &lt;to&gt; &lt;column_spec&gt; </code>
  1311. <br><br>
  1312. Liest Daten aus einem Logfile und wird von einem Frontend ben&ouml;tigt, um
  1313. Daten ohne direkten Zugriff aus der Datei zu lesen.<br>
  1314. <ul>
  1315. <li>&lt;infile&gt;<br>
  1316. Name des Logfiles, auf das zugegriffen werden soll. Sonderf&auml;lle:
  1317. "-" steht f&uuml;r das aktuelle Logfile, und "CURRENT" &ouml;ffnet die
  1318. zum "from" passende Datei.</li>
  1319. <li>&lt;outfile&gt;<br>
  1320. Bei einem "-", bekommt man die Daten auf der aktuellen Verbindung
  1321. zur&uuml;ck, anderenfall ist es das Name (eigentlich Prefix, s.u.) des
  1322. Output-Files. Wenn mehr als ein File angesprochen wird, werden die
  1323. einzelnen Dateinamen durch ein "-" getrennt, anderenfalls werden die
  1324. Daten in einzelne Dateien geschrieben, die - beginnend mit 0 -
  1325. durchnummeriert werden.
  1326. </li>
  1327. <li>&lt;from&gt; &lt;to&gt;<br>
  1328. Bezeichnet den gew&uuml;nschten Datenbereich. Die beiden Elemente
  1329. m&uuml;ssen ganz oder mit dem Anfang des Zeitformates
  1330. &uuml;bereinstimmen.</li>
  1331. <li>&lt;column_spec&gt;<br>
  1332. Jede column_spec sendet die gew&uuml;nschten Daten entweder in eine
  1333. gesonderte Datei oder &uuml;ber die gegenw&auml;rtige Verbindung durch
  1334. "-" getrennt.<br>
  1335. Syntax: &lt;col&gt;:&lt;regexp&gt;:&lt;default&gt;:&lt;fn&gt;<br>
  1336. <ul>
  1337. <li>&lt;col&gt;
  1338. gibt die Spaltennummer zur&uuml;ck, beginnend mit 1 beim Datum.
  1339. Wenn die Spaltenmummer in doppelten Anf&uuml;hrungszeichen steht,
  1340. handelt es sich um einen festen Text und nicht um eine
  1341. Spaltennummer.</li>
  1342. <li>&lt;regexp&gt;
  1343. gibt, falls vorhanden, Zeilen mit Inhalten von regexp zur&uuml;ck.
  1344. Gro&szlig;- und Kleinschreibung beachten. </li>
  1345. <li>&lt;default&gt;<br>
  1346. Wenn keine Werte gefunden werden, und der Default-Wert
  1347. (Voreinstellung) wurde gesetzt, wird eine Zeile zur&uuml;ckgegeben,
  1348. die den von-Wert (from) und diesen Default-Wert enth&auml;lt.
  1349. Dieses Leistungsmerkmal ist notwendig, da gnuplot abbricht, wenn
  1350. ein Datensatz keine Daten enth&auml;lt.
  1351. </li>
  1352. <li>&lt;fn&gt;
  1353. Kann folgende Inhalte haben:
  1354. <ul>
  1355. <li>int<br>
  1356. L&ouml;st den Integer-Wert zu Beginn eines Strings heraus. Wird
  1357. z.B. bei 10% gebraucht.</li>
  1358. <li>delta-h oder delta-d<br>
  1359. Gibt nur den Unterschied der Werte-Spalte pro
  1360. Stunde oder pro Tag aus. Wird ben&ouml;tigt, wenn die Spalte
  1361. einen Z&auml;hler enth&auml;lt, wie im Falles des KS300 in der
  1362. Spalte f&uuml;r die Regenmenge.</li>
  1363. <li>alles andere<br>
  1364. Dieser String wird als Perl-Ausdruck ausgewertet. @fld enthaelt
  1365. die aktuelle Zeile getrennt durch Leerzeichen. Achtung:
  1366. Dieser String/Perl-Ausdruck darf keine Leerzeichen enthalten.
  1367. </li>
  1368. </ul></li>
  1369. </ul></li>
  1370. </ul>
  1371. <br><br>
  1372. Beispiel:
  1373. <ul><code><br>
  1374. get outlog out-2008.log - 2008-01-01 2008-01-08 4:IR:int: 9:IR::
  1375. </code></ul>
  1376. <br>
  1377. </ul>
  1378. <a name="FileLogattr"></a>
  1379. <b>Attribute</b>
  1380. <ul>
  1381. <li><a href="#addStateEvent">addStateEvent</a></li><br><br>
  1382. <a name="archivedir"></a>
  1383. <a name="archivecmd"></a>
  1384. <a name="nrarchive"></a>
  1385. <li>archivecmd / archivedir / nrarchive<br>
  1386. Wenn eine neue FileLog-Datei ge&ouml;ffnet wird, wird der FileLog
  1387. archiver aufgerufen. Das geschieht aber nur , wenn der Name der Datei
  1388. sich ge&auml;ndert hat(abh&auml;ngig von den zeitspezifischen
  1389. Wildcards, die weiter oben unter <a href="#FileLogdefine">FileLog
  1390. (define)</a> beschrieben werden) und gleichzeitig ein neuer Datensatz
  1391. in diese Datei geschrieben werden muss. <br>
  1392. Wenn das Attribut archivecmd benutzt wird, startet es als
  1393. shell-Kommando ( eine Einbettung in " ist nicht notwendig), und jedes %
  1394. in diesem Befehl wird durch den Namen des alten Logfiles ersetzt.<br>
  1395. Wenn dieses Attribut nicht gesetzt wird, aber daf&uuml;r nrarchive,
  1396. werden nrarchive viele Logfiles im aktuellen Verzeichnis gelassen, und
  1397. &auml;ltere Dateien in das Archivverzeichnis (archivedir) verschoben
  1398. (oder gel&ouml;scht, falls kein archivedir gesetzt wurde).<br>
  1399. Achtung: "&auml;ltere Dateien" sind die, die in der alphabetisch
  1400. sortierten Liste oben sind.<br>
  1401. Hinweis: Werden diese Attribute als global instance gesetzt, hat das
  1402. auschlie&szlig;lich auf das <a href="#logfile">FHEM logfile</a>
  1403. Auswirkungen. </li><br>
  1404. <a name="archiveCompress"></a>
  1405. <li>archiveCompress<br>
  1406. Falls nrarchive, archivedir und archiveCompress gesetzt ist, dann
  1407. werden die Dateien im archivedir komprimiert abgelegt.
  1408. </li><br>
  1409. <a name="createGluedFile"></a>
  1410. <li>createGluedFile<br>
  1411. Falls gesetzt (1), und im SVG-Plot ein Zeitbereich abgefragt wird, was
  1412. in zwei Logdateien gespeichert ist, dann wird f&uuml;r die Anfrage eine
  1413. tempor&auml;re Datei mit dem Inhalt der beiden Dateien erzeugt.
  1414. </li><br>
  1415. <li><a href="#disable">disable</a></li>
  1416. <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
  1417. <br>
  1418. <a name="eventOnThreshold"></a>
  1419. <li>eventOnThreshold<br>
  1420. Falls es auf eine (nicht Null-) Zahl gesetzt ist, dann wird das
  1421. linesInTheFile Event generiert, falls die Anzahl der Zeilen in der
  1422. Datei ein Mehrfaches der gesetzen Zahl ist. Achtung: der Z&auml;hler ist
  1423. nur f&uuml;r solche Dateien korrekt, die nach dem Impementieren dieses
  1424. Features angelegt wurden. Ein Absturz/Abschu&szlig; von FHEM
  1425. verf&auml;lscht die Z&auml;hlung.
  1426. </li><br>
  1427. <li><a href="#ignoreRegexp">ignoreRegexp</a></li>
  1428. <a name="logtype"></a>
  1429. <li>logtype<br>
  1430. Wird vom SVG Modul ben&ouml;tigt, um daten grafisch aufzubereiten.
  1431. Der String wird aus komma-separierten Tokens
  1432. (,) erzeugt, wobei jeder Token ein eigenes gnuplot-Programm bezeichnet.
  1433. Die Token k&ouml;nnen Doppelpunkte (:) enthalten. Der Teil vor dem
  1434. Doppelpunkt bezeichnet den Namen des Programms; der Teil nach dem
  1435. Doppelpunkt ist der String, der im Web.Frontend dargestellt werden
  1436. soll. Gegenw&auml;rtig sind folgende Typen von gnuplot-Programmen
  1437. implementiert:<br>
  1438. <ul>
  1439. <li>fs20<br>
  1440. Zeichnet on als 1 and off als 0. Die geeignete
  1441. filelog-Definition f&uuml;r das Ger&auml;t fs20dev lautet:<br>
  1442. define fslog FileLog log/fs20dev-%Y-%U.log fs20dev
  1443. </li>
  1444. <li>fht<br>
  1445. Zeichnet die Ist-Temperatur/Soll-temperatur/Aktor Kurven. Die
  1446. passende FileLog-Definition (f&uuml;r das FHT-Ger&auml;t mit
  1447. Namen fht1)sieht wie folgt aus: <br>
  1448. <code>define fhtlog1 FileLog log/fht1-%Y-%U.log
  1449. fht1:.*(temp|actuator).*</code>
  1450. </li>
  1451. <li>temp4rain10<br>
  1452. Zeichnet eine Kurve aus der Temperatur und dem Niederschlag (pro
  1453. Stunde und pro Tag) eines KS300. Die dazu passende
  1454. FileLog-Definition (f&uuml;r das KS300
  1455. Ger&auml;t mit Namen ks300) sieht wie folgt aus:<br>
  1456. define ks300log FileLog log/fht1-%Y-%U.log ks300:.*H:.*
  1457. </li>
  1458. <li>hum6wind8<br>
  1459. Zeichnet eine Kurve aus der Feuchtigkeit und der
  1460. Windgeschwindigkeit eines ks300. Die geeignete
  1461. FileLog-Definition ist identisch mit der vorhergehenden
  1462. Definition. Beide programme erzeugen das gleiche Log.
  1463. </li>
  1464. <li>text<br>
  1465. Zeigt das LogFile in seiner urspr&uuml;nglichen Form (Nur
  1466. Text).Eine gnuplot-Definition ist nicht notwendig.
  1467. </li>
  1468. </ul>
  1469. Beispiel:<br> attr ks300log1 logtype
  1470. temp4rain10:Temp/Rain,hum6wind8:Hum/Wind,text:Raw-data
  1471. </li><br>
  1472. <li><a href="#mseclog">mseclog</a></li><br>
  1473. <a name="reformatFn"></a>
  1474. <li>reformatFn<br>
  1475. wird verwendet, um "fremde" Dateien f&uuml;r die SVG-Anzeige ins
  1476. FileLog-Format zu konvertieren. Es enth&auml;lt nur den Namen einer
  1477. Funktion, der mit der urspr&uuml;nglichen Zeile aufgerufen wird. Z.Bsp.
  1478. um die NTP loopstats Datei zu visualisieren kann man den Wert von
  1479. reformatFn auf ntpLoopstats setzen, und folgende Funktion in
  1480. 99_myUtils.pm definieren:
  1481. <pre><code>
  1482. sub
  1483. ntpLoopstats($)
  1484. {
  1485. my ($d) = @_;
  1486. return $d if($d !~ m/^(\d{5}) (\d+)\.(\d{3}) (.*)$/);
  1487. my ($r, $t) = ($4, FmtDateTime(($1-40587)*86400+$2));
  1488. $t =~ s/ /_/;
  1489. return "$t ntpLoopStats $r";
  1490. }</code></pre>
  1491. </li>
  1492. </ul>
  1493. <br>
  1494. </ul>
  1495. =end html_DE
  1496. =cut