Send wind even if it hasn't changed
[dweather.git] / loop.pl
diff --git a/loop.pl b/loop.pl
index 2768a009e92bbf74401deccf70db33d688b05da0..e627dde79127b8a86f3f66a2cec460a97e6e46dc 100755 (executable)
--- a/loop.pl
+++ b/loop.pl
@@ -19,6 +19,7 @@ use IO::File;
 
 use constant pi => 3.14159265358979; 
 
+my $randomfn = '/dev/urandom';
 my $devname = "/dev/davis";
 my $datafn = ".loop_data";
 my $dataf;
@@ -32,7 +33,8 @@ my $nlcount;
 my $state = "ready";
 my $buf;
 my $dbg;
-my $ser;                                                       # the serial port Mojo::IOLoop::Stream
+our $ser;                                                      # the serial port Mojo::IOLoop::Stream
+our $ob;                                                       # the Serial Port filehandle
 my $last_min_h;
 my $last_hour_h;
 
@@ -42,6 +44,9 @@ our $WS = {};                                 # websocket connections
 our $ld = {};
 our @last10minsr = ();
 our @last5daysh = ();
+our $windmins = 2;                             # no of minutes of wind data for the windrose
+our $histdays = 5;                             # no of days of (half)hour data to search for main graph
+our $updatepermin = 60 / 2.5;  # no of updates per minute
 
 our $loop_count;                               # how many LOOPs we have done, used as start indicator
 
@@ -92,7 +97,7 @@ $bar_trend{60} = "Rising Rapidly";
 
 our $ending = 0;
 
-$SIG{TERM} = $SIG{INT} = sub {++$ending; Mojo::IOLoop->stop;};
+$SIG{TERM} = $SIG{INT} = sub {$ending = 1; Mojo::IOLoop->stop;};
 $SIG{HUP} = 'IGNORE';
 
 
@@ -111,11 +116,6 @@ websocket '/weather' => sub {
   $c->send($ld->{lasthour_h}) if exists $ld->{lasthour_h};
   $c->send($ld->{lastmin_h}) if exists $ld->{lastmin_h};
 
-  # send the 5 days worth of data to the graph
-  say "last10min = " . scalar @last10minsr . " last5day = " . scalar  @last5daysh;
-  $c->send($_) for @last10minsr;
-  $c->send($_) for @last5daysh;
-  
   # disable timeout
   $c->inactivity_timeout(3615);
  
@@ -157,16 +157,30 @@ read_ld();
 
 my $tnow = time;
 my $dayno = int ($tnow/86400);
-@last5daysh = grab_history(SMGLog->new("day"), "h", $tnow-(86400*5), $_) for ($dayno-4, $dayno-3, $dayno-2, $dayno-1, $dayno);  
-@last10minsr = map {my ($t, $js) = split(/\s/, $_, 2); $js} grab_history(SMGLog->new("debug"), "r", $tnow-(60*3), $dayno);
+for (my $i = 0-$histdays; $i < 0; ++$i ) {
+       push @last5daysh, grab_history(SMGLog->new("day"), "h", $tnow-(86400*$histdays), $dayno+$i+1); 
+}
+@last10minsr = map {my ($t, $js) = split(/\s/, $_, 2); $js} grab_history(SMGLog->new("debug"), "r", $tnow-(60*$windmins), $dayno);
+dbg sprintf("last5days = %d last10mins = %d", scalar @last5daysh, scalar @last10minsr);
+
+sysopen(R, $randomfn, 0) or die "cannot open $randomfn $!\n";
+my $rs;
+sysread(R, $rs, 8) or die "not enough randomness available\n";
+close R;
+
+app->secrets([qw(Here's something that's really seakrett), $rs]);
 
 our $dlog = SMGLog->new("day");
+$did = Mojo::IOLoop->recurring(1 => sub {$dlog->flushall});
+
 dbg "before next tick";
 Mojo::IOLoop->next_tick(sub { loop() });       
 dbg "before app start";
 app->start;
 dbg "after app start";
 
+doclose();
+
 write_ld();
 $dataf->close if $dataf;
 undef $dataf;
@@ -176,7 +190,7 @@ undef $dataf;
 cycle_loop_data_files();
 
 dbg '***';
-dbg "*** ending $0";
+dbg "*** ending $0 (\$ending = $ending)";
 dbg '***';
 
 exit 0;
@@ -188,9 +202,8 @@ sub loop
        dbg "last_min: " . scalar gmtime($ld->{last_min});
        dbg "last_hour: " . scalar gmtime($ld->{last_hour});
        
-       $did = Mojo::IOLoop->recurring(1 => sub {$dlog->flushall});
-       
-       do_reopen($devname);
+       $ser = doopen($devname);
+       start_loop() if $ser;
 }
 
 
@@ -234,8 +247,7 @@ sub start_loop
        undef $tid;
        $tid = Mojo::IOLoop->recurring(0.6 => sub {
                                                                           if (++$nlcount > 10) {
-                                                                                  dbg "\\n count > 10, closing connection" if isdbg 'chan';
-                                                                                  do_reopen($devname);
+                                                                                  doclose();
                                                                                   return;
                                                                           }
                                                                           dbg "writing $nlcount \\n" if isdbg 'state'; 
@@ -250,47 +262,68 @@ sub chgstate
        $state = $_[0];
 }
 
-sub do_reopen
-{
-       my $name = shift;
-       dbg "do reopen on '$name' ending $ending";
-       unless ($ending) {
-               $ser = do_open($name);
-               start_loop();
-               chgstate('');
-               $nlcount = 0;
-               Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
-       }
-}
+my $closing;
 
-sub do_open
+sub doopen
 {
        my $name = shift;
 
-       $ser->close if $ser;
-       undef $ser;
-
-       my $ob = Serial->new($name, 19200) || die "$name $!\n";
+    $ob = Serial->new($name, 19200) || die "$name $!\n";
        dbg "streaming $name fileno(" . fileno($ob) . ")" if isdbg 'chan';
        
        my $ser = Mojo::IOLoop::Stream->new($ob);
-       $ser->on(error=>sub {dbg "serial $_[1]"; do_reopen($name) unless $ending});
-       $ser->on(close=>sub {dbg "serial closing"; do_reopen($name) unless $ending});
-       $ser->on(timeout=>sub {dbg "serial timeout";});
+       $ser->on(error=>sub {dbg "error serial $_[1]"; doclose();});
+       $ser->on(close=>sub {dbg "event close";  doclose();});
+       $ser->on(timeout=>sub {dbg "event serial timeout"; doclose();});
        $ser->on(read=>sub {on_read(@_)});
        $ser->start;
 
+    $rid = Mojo::IOLoop->recurring($poll_interval => sub {
+                                       start_loop() if !$state;
+                                   });
+
+       chgstate('');
+       $nlcount = 0;
+       
+       return $ser;
+}
+
+sub doclose
+{
+       return if $closing++;
+       
+       dbg "serial port closing" if $ser || $ob;
+       if ($ser) {
+               $ser->stop;
+               $ser->close;
+               undef $ser;
+       }
+       if ($ob) {
+               $ob->close();
+               undef $ob;
+       }
        Mojo::IOLoop->remove($tid) if $tid;
        undef $tid;
        Mojo::IOLoop->remove($rid) if $rid;
        undef $rid;
-       $rid = Mojo::IOLoop->recurring($poll_interval => sub {
-                                                                          start_loop() if !$state;
-                                                                  });
+
+    if (Mojo::IOLoop->is_running && $ending == 0) {
+               Mojo::IOLoop->delay(
+                                                       sub {
+                                                               my $delay = shift;
+                                                               Mojo::IOLoop->timer(5 => $delay->begin);
+                                                               dbg "Waiting 5 seconds before opening serial port";
+                                                        },
+
+                                                       sub {
+                                                               dbg "Opening Serial port";
+                                                               $ser = doopen($devname);
+                                                               $closing = 0;
+                                                       }
+                                                  )->wait;
+       }
        chgstate('');
        $nlcount = 0;
-       
-       return $ser;
 }
 
 my @min;
@@ -315,14 +348,14 @@ sub process
 
                # Common ones
                $tmp = unpack("s", substr $blk,7,2) / 1000;
-               $h{Pressure} = nearest(1, in2mb($tmp));
+               $h{Pressure} = nearest(0.1, in2mb($tmp));
 
                $tmp = unpack("s", substr $blk,9,2) / 10;
                $h{Temp_In} = nearest(0.1, f2c($tmp));
 
                $temp  = nearest(0.1, f2c(unpack("s", substr $blk,12,2) / 10));
                $h{Temp_Out}  = $temp;
-               if ($temp > 75 || $temp < -75) {
+               if ($temp > 60 || $temp < -60) {
                        dbg "LOOP Temperature out of range ($temp), record ignored";
                        return;
                }
@@ -388,24 +421,35 @@ sub process
                my $ts = time;
                my $s;
                my $dayno = int($ts/86400);
+               my $writeld;
+               my $cycledata;
+               
                if ($dayno > $ld->{last_day}) {
+                       $ld->{Wind_Max} = $wind->{w};
                        $ld->{Temp_Out_Max} = $ld->{Temp_Out_Min} = $temp;
-                       $ld->{Temp_Out_Max_T} = $ld->{Temp_Out_Min_T} = clocktime($ts, 0);
+                       $ld->{Temp_Out_Max_T} = $ld->{Temp_Out_Min_T} = $ld->{Wind_Max_T} = clocktime($ts, 0);
                        $ld->{last_day} = $dayno;
-                       write_ld();
-                       cycle_loop_data_files();
+                       ++$writeld;
+                       ++$cycledata;
                }
                if ($temp > $ld->{Temp_Out_Max}) {
-                       $ld->{Temp_Out_Max} = $temp;
-                       $ld->{Temp_Out_Max_T} = clocktime($ts, 0);
-                       write_ld();
+                       $h{Temp_Out_Max} = $ld->{Temp_Out_Max} = $temp;
+                       $h{Temp_Out_Max_T} = $ld->{Temp_Out_Max_T} = clocktime($ts, 0);
+                       ++$writeld;
                }
                if ($temp < $ld->{Temp_Out_Min}) {
-                       $ld->{Temp_Out_Min} = $temp;
-                       $ld->{Temp_Out_Min_T} = clocktime($ts, 0);
-                       write_ld();
+                       $h{Temp_Out_Min} = $ld->{Temp_Out_Min} = $temp;
+                       $h{Temp_Out_Min_T} = $ld->{Temp_Out_Min_T} = clocktime($ts, 0);
+                       ++$writeld;
+               }
+
+               if ($wind->{w} > $ld->{Wind_Max}) {
+                       $h{Wind_Max} = $ld->{Wind_Max} = $wind->{w};
+                       $h{Wind_Max_T} = $ld->{Wind_Max_T} = clocktime($ts, 0);
+                       ++$writeld;
                }
 
+
                if ($ts >= $ld->{last_hour} + 1800) {
                        $h{Pressure_Trend}    = unpack("C", substr $blk,3,1);
                        $h{Pressure_Trend_txt} = $bar_trend{$h{Pressure_Trend}};
@@ -417,11 +461,6 @@ sub process
                        $h{Sunrise}  =~ s/(\d{2})(\d{2})/$1:$2/;
                        $h{Sunset}   = sprintf( "%04d", unpack("S", substr $blk,93,2) );
                        $h{Sunset}  =~ s/(\d{2})(\d{2})/$1:$2/;
-                       $h{Temp_Out_Max} = $ld->{Temp_Out_Max};
-                       $h{Temp_Out_Min} = $ld->{Temp_Out_Min};
-                       $h{Temp_Out_Max_T} = $ld->{Temp_Out_Max_T};
-                       $h{Temp_Out_Min_T} = $ld->{Temp_Out_Min_T};
-                       
 
                        if ($loop_count) {      # i.e not the first
                                my $a = wind_average(scalar @{$ld->{wind_hour}} ? @{$ld->{wind_hour}} : {w => $h{Wind}, d => $h{Dir}});
@@ -434,8 +473,15 @@ sub process
                                $h{Dir_1m} = nearest(1, $a->{d});
 
                                ($h{Rain_1m}, $h{Rain_1h}, $h{Rain_24h}) = calc_rain($rain);
+                               
                        }
                        $ld->{last_rain_min} = $ld->{last_rain_hour} = $rain;
+                       $h{Temp_Out_Max} = $ld->{Temp_Out_Max};
+                       $h{Temp_Out_Max_T} = $ld->{Temp_Out_Max_T};
+                       $h{Temp_Out_Min} = $ld->{Temp_Out_Min};
+                       $h{Temp_Out_Min_T} = $ld->{Temp_Out_Min_T};
+                       $h{Wind_Max} = $ld->{Wind_Max};
+                       $h{Wind_Max_T} = $ld->{Wind_Max_T};
 
                        $last_hour_h = {%h};
                        $s = genstr($ts, 'h', \%h);
@@ -451,7 +497,7 @@ sub process
                                push @last5daysh, $s;
                                shift @last5daysh if @last5daysh > 5*24;
                        }
-                       write_ld();
+                       ++$writeld;
                        
                } elsif ($ts >= $ld->{last_min} + 60) {
                        my $a = wind_average(@{$ld->{wind_min}});
@@ -465,13 +511,17 @@ sub process
                                $h{Wind_1m} = nearest(0.1, $a->{w});
                                $h{Dir_1m} = nearest(1, $a->{d});
                                ($h{Rain_1m}, $h{Rain_1h}, $h{Rain_24h}) = calc_rain($rain);
+
+                               my $wkph = $a->{w} * 3.6;
+                               $h{WindChill} = nearest(0.1, $a->{w} >= 1.2 ? 13.12 + 0.6215 * $temp - 11.37 * $wkph ** 0.16 + 0.3965 * $temp * $wkph ** 0.16 : $temp); 
                        }
                        $ld->{last_rain_min} = $rain;
-
                        $h{Temp_Out_Max} = $ld->{Temp_Out_Max};
-                       $h{Temp_Out_Min} = $ld->{Temp_Out_Min};
                        $h{Temp_Out_Max_T} = $ld->{Temp_Out_Max_T};
+                       $h{Temp_Out_Min} = $ld->{Temp_Out_Min};
                        $h{Temp_Out_Min_T} = $ld->{Temp_Out_Min_T};
+                       $h{Wind_Max} = $ld->{Wind_Max};
+                       $h{Wind_Max_T} = $ld->{Wind_Max_T};
 
                        $last_min_h = {%h};
                        $s = genstr($ts, 'm', \%h);
@@ -481,21 +531,22 @@ sub process
                        @{$ld->{wind_min}} = ();
                        
                        output_str($s, 1) if $s;
-                       write_ld();
+                       ++$writeld;
 
                } else {
                        my $o = gen_hash_diff($ld->{last_h}, \%h);
-                       if ($o) {
-                               $s = genstr($ts, 'r', $o);
-                               push @last10minsr, $s;
-                               shift @last10minsr if @last10minsr > 240;
-                       }
-                       else {
-                               dbg "loop rec not changed" if isdbg 'chan';
-                       }
+                       $o ||= {};
+                       # we always send wind even if it hasn't changed in order to update the wind rose.
+                       $o->{Dir} ||= ($h{Dir} + 0);
+                       $o->{Wind} ||= ($h{Wind} + 0);
+                       $s = genstr($ts, 'r', $o);
+                       push @last10minsr, $s;
+                       shift @last10minsr while @last10minsr > ($windmins * $updatepermin);
                        output_str($s, 0) if $s;
                }
                $ld->{last_h} = \%h;
+               write_ld() if $writeld;
+               cycle_loop_data_files() if $cycledata;
                ++$loop_count;
        } else {
                dbg "CRC check failed for LOOP data!";