#!/usr/bin/perl -wT
#
# ==========================================================================
#
# ZoneMinder WatchDog Script, $Date: 2011-05-25 09:27:46 +0100 (Wed, 25 May 2011) $, $Revision: 3360 $
# Copyright (C) 2001-2008 Philip Coombes
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ==========================================================================
#
# This does some basic setup for ZoneMinder to run and then periodically
# checks the fps output of the active daemons to check they haven't 
# locked up. If they have then they are killed and restarted
#
use strict;
use bytes;

# ==========================================================================
#
# These are the elements you can edit to suit your installation
#
# ==========================================================================

use constant DBG_ID => "zmwatch"; # Tag that appears in debug to identify source
use constant DBG_LEVEL => 0; # 0 is errors, warnings and info only, > 0 for debug

use constant START_DELAY => 30; # To give everything else time to start

# ==========================================================================
#
# Don't change anything below here
#
# ==========================================================================

use lib '/usr/local/lib/perl5/site_perl/5.12.4'; # Include custom perl install path
use ZoneMinder;
use POSIX;
use DBI;
use Data::Dumper;

$| = 1;

$ENV{PATH}  = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};

sub Usage
{
    print( "
Usage: zmwatch.pl
");
	exit( -1 );
}

zmDbgInit( DBG_ID, level=>DBG_LEVEL );
zmDbgSetSignal();

Info( "Watchdog starting\n" );
Info( "Watchdog pausing for ".START_DELAY." seconds\n" );
sleep( START_DELAY );

my $dbh = zmDbConnect();

my $sql = "select * from Monitors";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );

while( 1 )
{
	my $now = time();
	my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() );
	while( my $monitor = $sth->fetchrow_hashref() )
	{
		if ( $monitor->{Function} ne 'None' )
		{
            my $restart = 0;
			if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) )
			{
				# Check we have got an image recently
				my $image_time = zmGetLastWriteTime( $monitor );
				next if ( !defined($image_time) ); # Can't read from shared data
				next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.

				my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):ZM_WATCH_MAX_DELAY;
				my $image_delay = $now-$image_time;
				Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" );
				if ( $image_delay > $max_image_delay )
				{
				    Info( "Restarting capture daemon for ".$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n" );
                    $restart = 1;
                }
			}
			else
			{
				#Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" );
                #$restart = 1;
			}

            if ( $restart )
            {
                my $command;
                if ( $monitor->{Type} eq 'Local' )
                {
                    $command = "zmdc.pl restart zmc -d $monitor->{Device}";
                }
                else
                {
                    $command = "zmdc.pl restart zmc -m $monitor->{Id}";
                }
                runCommand( $command );
            }
            elsif ( $monitor->{Function} ne 'Monitor' )
            {
                if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) )
                {
                    # Check we have got an image recently
                    my $image_time = zmGetLastReadTime( $monitor );
                    next if ( !defined($image_time) ); # Can't read from shared data
                    next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.

                    my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):ZM_WATCH_MAX_DELAY;
                    my $image_delay = $now-$image_time;
                    Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
                    if ( $image_delay > $max_image_delay )
                    {
                        Info( "Restarting analysis daemon for ".$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n" );
                        my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
                        runCommand( $command );
                    }
                }
            }
        }
        # Prevent open handles building up if we have connect to shared memory
        zmMemInvalidate( $monitor );
	}
	sleep( ZM_WATCH_CHECK_INTERVAL );
}
Info( "Watchdog exiting\n" );
exit();
