1#!@PERL_EXECUTABLE@ -wT
2#
3# ==========================================================================
4#
5# ZoneMinder Experimental PTZ Tracking Script, $Date: 2009-06-08 10:11:56 +0100 (Mon, 08 Jun 2009) $, $Revision: 2908 $
6# Copyright (C) 2001-2008 Philip Coombes
7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License
10# as published by the Free Software Foundation; either version 2
11# of the License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22# ==========================================================================
23
24=head1 NAME
25
26zmtrack.pl - ZoneMinder Experimental PTZ Tracking Script
27
28=head1 SYNOPSIS
29
30 zmtrack.pl -m <monitor>
31 zmtrack.pl --monitor=<monitor>
32
33=head1 OPTIONS
34
35 -m<monitor>, --monitor=<monitor>   - Id of the monitor to track
36
37=head1 DESCRIPTION
38
39This script is used to trigger and cancel alarms from external sources
40using an arbitrary text based format.
41
42=cut
43use strict;
44use bytes;
45
46# ==========================================================================
47#
48# User config
49#
50# ==========================================================================
51
52use constant SLEEP_TIME => 10000; # In microseconds
53
54# ==========================================================================
55#
56# Don't change anything from here on down
57#
58# ==========================================================================
59
60@EXTRA_PERL_LIB@
61use ZoneMinder;
62use DBI;
63use POSIX;
64use autouse 'Data::Dumper'=>qw(Dumper);
65use Getopt::Long;
66use autouse 'Pod::Usage'=>qw(pod2usage);
67use Time::HiRes qw( usleep );
68
69$| = 1;
70
71$ENV{PATH}  = '/bin:/usr/bin:/usr/local/bin';
72$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
73delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
74
75my $mid = 0;
76
77GetOptions( 'monitor=s'=>\$mid )
78    or pod2usage(-exitstatus => -1);
79
80my ( $detaint_mid ) = $mid =~ /^(\d+)$/;
81$mid = $detaint_mid;
82
83logInit($mid?(id=>'zmtrack_m'.$mid):());
84logSetSignal();
85
86print( "Tracker daemon $mid (experimental) starting at "
87       .strftime( '%y/%m/%d %H:%M:%S', localtime() )
88       ."\n"
89);
90
91my $dbh = zmDbConnect();
92
93my $sql = 'SELECT C.*,M.* FROM Monitors as M
94           LEFT JOIN Controls as C on M.ControlId = C.Id
95           WHERE M.Id = ?'
96;
97my $sth = $dbh->prepare_cached( $sql )
98    or Fatal("Can't prepare '$sql': ".$dbh->errstr());
99
100my $res = $sth->execute( $mid )
101    or Fatal("Can't execute '$sql': ".$sth->errstr());
102my $monitor = $sth->fetchrow_hashref();
103
104if ( !$monitor ) {
105  Fatal("Can't find monitor '$mid'");
106}
107if ( !$monitor->{Controllable} ) {
108  Fatal("Monitor '$mid' is not controllable");
109}
110if ( !$monitor->{TrackMotion} ) {
111  Fatal("Monitor '$mid' is not configured to track motion");
112}
113
114if ( !$monitor->{CanMoveMap} ) {
115  if ( $monitor->{CanMoveRel} ) {
116    Warning("Monitor '$mid' cannot move in map mode, falling back to pseudo map mode");
117  } else {
118    Fatal("Monitor '$mid' cannot move in map mode");
119  }
120}
121
122exit(-1) if !zmMemVerify($monitor);
123
124sub Suspend {
125  my $monitor = shift;
126  zmMonitorSuspend($monitor);
127}
128
129sub Resume {
130  my $monitor = shift;
131  sleep($monitor->{TrackDelay});
132  zmMonitorResume($monitor);
133}
134
135sub Track {
136  my $monitor = shift;
137  my ( $x, $y ) = @_;
138  my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x;
139  my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y;
140
141  my $ctrlCommand = $Config{ZM_PATH_BIN}
142  .'/zmcontrol.pl -i '
143  .$monitor->{Id}
144  ;
145  $ctrlCommand .= ' --command='
146  .( $monitor->{CanMoveMap} ? 'moveMap'
147    : 'movePseudoMap'
148  )
149  ." --xcoord=$x --ycoord=$y"
150  ;
151  executeShellCommand($ctrlCommand);
152}
153
154sub Return {
155  my $monitor = shift;
156
157  my $ctrlCommand = $Config{ZM_PATH_BIN}
158  .'/zmcontrol.pl -i '
159  .$monitor->{Id}
160  ;
161  if ( $monitor->{ReturnLocation} > 0 ) {
162    $ctrlCommand .= ' --command=presetGoto --preset='
163    .$monitor->{ReturnLocation}
164    ;
165  } else {
166    $ctrlCommand .= ' --command=presetHome';
167  }
168  executeShellCommand($ctrlCommand);
169}
170
171my $last_alarm = 0;
172if ( ($monitor->{ReturnLocation} >= 0) ) {
173  Suspend($monitor);
174  Return($monitor);
175  Resume($monitor);
176}
177
178my $alarmed = undef;
179while( 1 ) {
180  if ( zmIsAlarmed($monitor) ) {
181    my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation($monitor);
182    if ( $alarm_x >= 0 && $alarm_y >= 0 ) {
183      Debug("Got alarm at $alarm_x, $alarm_y");
184      Suspend($monitor);
185      Track($monitor, $alarm_x, $alarm_y);
186      Resume($monitor);
187      $last_alarm = time();
188      $alarmed = !undef;
189    }
190  } else {
191    if ( logDebugging() && $alarmed ) {
192      Info('Left alarm state');
193      $alarmed = undef;
194    }
195    if ( ($monitor->{ReturnLocation} >= 0)
196      && ($last_alarm > 0)
197      && ((time()-$last_alarm) > $monitor->{ReturnDelay})
198    ) {
199      Debug("Returning to location ".$monitor->{ReturnLocation});
200      Suspend($monitor);
201      Return($monitor);
202      Resume($monitor);
203      $last_alarm = 0;
204    }
205  }
206  usleep(SLEEP_TIME);
207}
208
2091;
210__END__
211