diff --git a/configure.in b/configure.in index 06cd9dd04..7770ad7ea 100644 --- a/configure.in +++ b/configure.in @@ -132,6 +132,6 @@ AC_SUBST(XLIBS) AC_SUBST(bindir) -AC_OUTPUT(Makefile src/Makefile web/Makefile scripts/Makefile db/Makefile zmconfig.pl) +AC_OUTPUT(Makefile src/Makefile web/Makefile web/graphics/Makefile scripts/Makefile db/Makefile zmconfig.pl) AC_CHECK_FILE(zmconfig.txt,,AC_MSG_WARN([Now run 'perl zmconfig.pl' to create your ZoneMinder configuration])) diff --git a/db/Makefile.am b/db/Makefile.am index defa4161c..5751cd14b 100644 --- a/db/Makefile.am +++ b/db/Makefile.am @@ -22,4 +22,5 @@ EXTRA_DIST = \ zmalter-1.19.3.sql \ zmalter-1.19.4.sql \ zmalter-1.19.5.sql \ - zmalter-1.20.0.sql + zmalter-1.20.0.sql \ + zmalter-1.20.1.sql diff --git a/db/zmalter-1.20.1.sql b/db/zmalter-1.20.1.sql new file mode 100644 index 000000000..15d796485 --- /dev/null +++ b/db/zmalter-1.20.1.sql @@ -0,0 +1,18 @@ +-- +-- This updates a 1.20.0 database to 1.20.1 +-- +-- Make changes to Monitors table +-- +alter table Monitors add column Controllable tinyint(3) unsigned NOT NULL default '0'; +alter table Monitors add column ControlId int(10) unsigned NOT NULL default '0'; +alter table Monitors add column ControlDevice varchar(255) default NULL; +alter table Monitors add column ControlAddress varchar(255) default NULL; +-- +-- These are optional, but we might as well do it now +-- +optimize table Frames; +optimize table Events; +optimize table Filters; +optimize table Zones; +optimize table Monitors; +optimize table Stats; diff --git a/db/zmschema.sql.z b/db/zmschema.sql.z index 892fad349..c7d7adc7b 100644 --- a/db/zmschema.sql.z +++ b/db/zmschema.sql.z @@ -144,6 +144,14 @@ CREATE TABLE Monitors ( MaxFPS decimal(5,2) NOT NULL default '0.00', FPSReportInterval smallint(5) unsigned NOT NULL default '250', RefBlendPerc tinyint(3) unsigned NOT NULL default '10', + Controllable tinyint(3) unsigned NOT NULL default '0', + ControlId int(10) unsigned NOT NULL default '0', + ControlDevice varchar(255) default NULL, + ControlAddress varchar(255) default NULL, + TrackMotion tinyint(3) unsigned NOT NULL default '0', + TrackDelay smallint(5) unsigned NOT NULL default '0', + ReturnLocation tinyint(3) NOT NULL default '-1', + ReturnDelay smallint(5) unsigned NOT NULL default '0', PRIMARY KEY (Id) ) TYPE=MyISAM; @@ -246,6 +254,125 @@ CREATE TABLE Zones ( UNIQUE KEY UC_Id (Id) ) TYPE=MyISAM; +-- +-- Table structure for table `Controls` +-- + +CREATE TABLE Controls ( + Id int(10) unsigned NOT NULL auto_increment, + Name varchar(64) NOT NULL default '', + Type enum('Local','Remote') NOT NULL default 'Local', + Command varchar(255) default NULL, + + CanWake tinyint(3) unsigned NOT NULL default '0', + CanSleep tinyint(3) unsigned NOT NULL default '0', + CanReset tinyint(3) unsigned NOT NULL default '0', + + CanZoom tinyint(3) unsigned NOT NULL default '0', + CanAutoZoom tinyint(3) unsigned NOT NULL default '0', + CanZoomAbs tinyint(3) unsigned NOT NULL default '0', + CanZoomRel tinyint(3) unsigned NOT NULL default '0', + CanZoomCon tinyint(3) unsigned NOT NULL default '0', + MinZoomRange int(10) unsigned default NULL, + MaxZoomRange int(10) unsigned default NULL, + MinZoomStep int(10) unsigned default NULL, + MaxZoomStep int(10) unsigned default NULL, + HasZoomSpeed tinyint(3) unsigned NOT NULL default '0', + MinZoomSpeed int(10) unsigned default NULL, + MaxZoomSpeed int(10) unsigned default NULL, + + CanFocus tinyint(3) unsigned NOT NULL default '0', + CanAutoFocus tinyint(3) unsigned NOT NULL default '0', + CanFocusAbs tinyint(3) unsigned NOT NULL default '0', + CanFocusRel tinyint(3) unsigned NOT NULL default '0', + CanFocusCon tinyint(3) unsigned NOT NULL default '0', + MinFocusRange int(10) unsigned default NULL, + MaxFocusRange int(10) unsigned default NULL, + MinFocusStep int(10) unsigned default NULL, + MaxFocusStep int(10) unsigned default NULL, + HasFocusSpeed tinyint(3) unsigned NOT NULL default '0', + MinFocusSpeed int(10) unsigned default NULL, + MaxFocusSpeed int(10) unsigned default NULL, + + CanIris tinyint(3) unsigned NOT NULL default '0', + CanAutoIris tinyint(3) unsigned NOT NULL default '0', + CanIrisAbs tinyint(3) unsigned NOT NULL default '0', + CanIrisRel tinyint(3) unsigned NOT NULL default '0', + CanIrisCon tinyint(3) unsigned NOT NULL default '0', + MinIrisRange int(10) unsigned default NULL, + MaxIrisRange int(10) unsigned default NULL, + MinIrisStep int(10) unsigned default NULL, + MaxIrisStep int(10) unsigned default NULL, + HasIrisSpeed tinyint(3) unsigned NOT NULL default '0', + MinIrisSpeed int(10) unsigned default NULL, + MaxIrisSpeed int(10) unsigned default NULL, + + CanGain tinyint(3) unsigned NOT NULL default '0', + CanAutoGain tinyint(3) unsigned NOT NULL default '0', + CanGainAbs tinyint(3) unsigned NOT NULL default '0', + CanGainRel tinyint(3) unsigned NOT NULL default '0', + CanGainCon tinyint(3) unsigned NOT NULL default '0', + MinGainRange int(10) unsigned default NULL, + MaxGainRange int(10) unsigned default NULL, + MinGainStep int(10) unsigned default NULL, + MaxGainStep int(10) unsigned default NULL, + HasGainSpeed tinyint(3) unsigned NOT NULL default '0', + MinGainSpeed int(10) unsigned default NULL, + MaxGainSpeed int(10) unsigned default NULL, + + CanWhite tinyint(3) unsigned NOT NULL default '0', + CanAutoWhite tinyint(3) unsigned NOT NULL default '0', + CanWhiteAbs tinyint(3) unsigned NOT NULL default '0', + CanWhiteRel tinyint(3) unsigned NOT NULL default '0', + CanWhiteCon tinyint(3) unsigned NOT NULL default '0', + MinWhiteRange int(10) unsigned default NULL, + MaxWhiteRange int(10) unsigned default NULL, + MinWhiteStep int(10) unsigned default NULL, + MaxWhiteStep int(10) unsigned default NULL, + HasWhiteSpeed tinyint(3) unsigned NOT NULL default '0', + MinWhiteSpeed int(10) unsigned default NULL, + MaxWhiteSpeed int(10) unsigned default NULL, + + HasPresets tinyint(3) unsigned NOT NULL default '0', + NumPresets tinyint(3) unsigned NOT NULL default '0', + HasHomePreset tinyint(3) unsigned NOT NULL default '0', + CanSetPresets tinyint(3) unsigned NOT NULL default '0', + + CanMove tinyint(3) unsigned NOT NULL default '0', + CanMoveDiag tinyint(3) unsigned NOT NULL default '0', + CanMoveMap tinyint(3) unsigned NOT NULL default '0', + CanMoveAbs tinyint(3) unsigned NOT NULL default '0', + CanMoveRel tinyint(3) unsigned NOT NULL default '0', + CanMoveCon tinyint(3) unsigned NOT NULL default '0', + CanPan tinyint(3) unsigned NOT NULL default '0', + MinPanRange int(10) default NULL, + MaxPanRange int(10) default NULL, + MinPanStep int(10) default NULL, + MaxPanStep int(10) default NULL, + HasPanSpeed tinyint(3) unsigned NOT NULL default '0', + MinPanSpeed int(10) default NULL, + MaxPanSpeed int(10) default NULL, + HasTurboPan tinyint(3) unsigned NOT NULL default '0', + TurboPanSpeed int(10) default NULL, + CanTilt tinyint(3) unsigned NOT NULL default '0', + MinTiltRange int(10) default NULL, + MaxTiltRange int(10) default NULL, + MinTiltStep int(10) default NULL, + MaxTiltStep int(10) default NULL, + HasTiltSpeed tinyint(3) unsigned NOT NULL default '0', + MinTiltSpeed int(10) default NULL, + MaxTiltSpeed int(10) default NULL, + HasTurboTilt tinyint(3) unsigned NOT NULL default '0', + TurboTiltSpeed int(10) default NULL, + + CanAutoScan tinyint(3) unsigned NOT NULL default '0', + NumScanPaths tinyint(3) unsigned NOT NULL default '0', + + PRIMARY KEY (Id), + UNIQUE KEY UC_Id (Id) +) TYPE=MyISAM; + + -- -- Create a default admin user. -- @@ -254,3 +381,11 @@ insert into Users values ('','admin',password('admin'),'',1,'View','Edit','Edit' -- Add in a sample filter to purge the oldest 5 events when the disk is 99% full, delete is disabled though -- insert into Filters values ('PurgeWhenFull','trms=2&obr1=&cbr1=&attr1=Archived&op1=&val1=0&cnj2=and&obr2=&cbr2=&attr2=DiskPercent&op2=>=&val2=99&sort_field=Id&sort_asc=1&limit=5',0,0,0,0,0,''); +-- +-- Add in some sample control protocol definitions +-- +insert into Controls values (1,'pelco-d','Local','/usr/local/bin/zmcontrol-pelco-d.pl',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); +insert into Controls values (2,'visca','Local','/usr/local/bin/zmcontrol-visca.pl',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0); +insert into Controls values (3,'KX-HCM10','Remote','/usr/local/bin/zmcontrol-kx-hcm10.pl',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); +insert into Controls values (4,'pelco-d-full','Local','/usr/local/bin/zmcontrol-pelco-d.pl',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); + diff --git a/scripts/Makefile.am b/scripts/Makefile.am index fa336a616..2792b893b 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -9,7 +9,11 @@ bin_SCRIPTS = \ zmwatch.pl \ zmpkg.pl \ zmupdate.pl \ - zmvideo.pl + zmvideo.pl \ + zmcontrol-pelco-d.pl \ + zmcontrol-visca.pl \ + zmcontrol-kx-hcm10.pl \ + zmtrack.pl EXTRA_DIST = \ zmdc.pl.z \ @@ -21,4 +25,8 @@ EXTRA_DIST = \ zmpkg.pl.z \ zmupdate.pl.z \ zmvideo.pl.z \ + zmcontrol-pelco-d.pl \ + zmcontrol-visca.pl \ + zmcontrol-kx-hcm10.pl \ + zmtrack.pl \ zm.z diff --git a/scripts/zmcontrol-kx-hcm10.pl.z b/scripts/zmcontrol-kx-hcm10.pl.z new file mode 100644 index 000000000..47ee96aa7 --- /dev/null +++ b/scripts/zmcontrol-kx-hcm10.pl.z @@ -0,0 +1,346 @@ +#!/usr/bin/perl -wT +# +# ========================================================================== +# +# ZoneMinder Panasonic KX-HCM10 Control Script, $Date$, $Revision$ +# Copyright (C) 2003 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 script continuously monitors the recorded events for the given +# monitor and applies any filters which would delete and/or upload +# matching events +# +use strict; + +# ========================================================================== +# +# These are the elements you need to edit to suit your installation +# +# ========================================================================== +use constant ZM_CONFIG => ""; +use constant ZM_VERSION => ""; +use constant ZM_PATH_BIN => ""; + +# Load the config from the database into the symbol table +BEGIN +{ + no strict 'refs'; + + open( CONFIG, "<".ZM_CONFIG ) or die( "Can't open config file: $!" ); + foreach my $str ( ) + { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*([^=\s]+)\s*$/; + $name =~ tr/a-z/A-Z/; + if (( $name eq 'ZM_DB_SERVER' ) || + ( $name eq 'ZM_DB_NAME' ) || + ( $name eq 'ZM_DB_USER' ) || + ( $name eq 'ZM_DB_PASS' )) + { + *{$name} = sub { $value }; + } + } + close( CONFIG ); + + use DBI; + my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_SERVER, &ZM_DB_USER, &ZM_DB_PASS ); + my $sql = "select * from Config"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); + while( my $config = $sth->fetchrow_hashref() ) + { + *{$config->{Name}} = sub { $config->{Value} }; + } + $sth->finish(); + $dbh->disconnect(); +} + +use Getopt::Long; +use Device::SerialPort; + +use constant LOG_FILE => ZM_PATH_LOGS.'/zmcontrol-kx-hcm10.log'; + +$| = 1; + +$ENV{PATH} = '/bin:/usr/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +sub Usage +{ + print( " +Usage: zmcontrol-kx-hcm10.pl +"); + exit( -1 ); +} + +my $arg_string = join( " ", @ARGV ); + +my $address; +my $command; +my ( $speed, $step ); +my ( $xcoord, $ycoord ); +my ( $width, $height ); +my ( $panspeed, $tiltspeed ); +my ( $panstep, $tiltstep ); +my $preset; + +if ( !GetOptions( + 'address=s'=>\$address, + 'command=s'=>\$command, + 'speed=i'=>\$speed, + 'step=i'=>\$step, + 'xcoord=i'=>\$xcoord, + 'ycoord=i'=>\$ycoord, + 'width=i'=>\$width, + 'height=i'=>\$height, + 'panspeed=i'=>\$panspeed, + 'tiltspeed=i'=>\$tiltspeed, + 'panstep=i'=>\$panstep, + 'tiltstep=i'=>\$tiltstep, + 'preset=i'=>\$preset + ) +) +{ + Usage(); +} + +if ( !$address ) +{ + Usage(); +} + +my $log_file = LOG_FILE; +open( LOG, ">>$log_file" ) or die( "Can't open log file: $!" ); +open( STDOUT, ">&LOG" ) || die( "Can't dup stdout: $!" ); +select( STDOUT ); $| = 1; +open( STDERR, ">&LOG" ) || die( "Can't dup stderr: $!" ); +select( STDERR ); $| = 1; +select( LOG ); $| = 1; + +print( $arg_string."\n" ); + +srand( time() ); + +sub printMsg +{ + my $msg = shift; + my $msg_len = length($msg); + + print( $msg ); + print( "[".$msg_len."]\n" ); +} + +sub sendCmd +{ + my $cmd = shift; + + my $result = undef; + + printMsg( $cmd, "Tx" ); + + use LWP::UserAgent; + my $ua = LWP::UserAgent->new; + $ua->agent( "ZoneMinder Control Agent/".ZM_VERSION ); + + print( "http://$address/$cmd\n" ); + my $req = HTTP::Request->new( GET=>"http://$address/$cmd" ); + my $res = $ua->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + print( "Error check failed: '".$res->status_line()."'\n" ); + } + + return( $result ); +} + +sub cameraReset +{ + print( "Camera Reset\n" ); + my $cmd = "nphRestart?PAGE=Restart&Restart=OK"; + sendCmd( $cmd ); +} + +sub moveUp +{ + print( "Move Up\n" ); + my $cmd = "nphControlCamera?Direction=TiltUp"; + sendCmd( $cmd ); +} + +sub moveDown +{ + print( "Move Down\n" ); + my $cmd = "nphControlCamera?Direction=TiltDown"; + sendCmd( $cmd ); +} + +sub moveLeft +{ + print( "Move Left\n" ); + my $cmd = "nphControlCamera?Direction=PanLeft"; + sendCmd( $cmd ); +} + +sub moveRight +{ + print( "Move Right\n" ); + my $cmd = "nphControlCamera?Direction=PanRight"; + sendCmd( $cmd ); +} + +sub moveMap +{ + my ( $xcoord, $ycoord, $width, $height ) = @_; + print( "Move Map to $xcoord,$ycoord\n" ); + my $cmd = "nphControlCamera?Direction=Direct&NewPosition.x=$xcoord&NewPosition.y=$ycoord&Width=$width&Height=$height"; + sendCmd( $cmd ); +} + +sub zoomTele +{ + print( "Zoom Tele\n" ); + my $cmd = "nphControlCamera?Direction=ZoomTele"; + sendCmd( $cmd ); +} + +sub zoomWide +{ + print( "Zoom Wide\n" ); + my $cmd = "nphControlCamera?Direction=ZoomWide"; + sendCmd( $cmd ); +} + +sub focusNear +{ + print( "Focus Near\n" ); + my $cmd = "nphControlCamera?Direction=FocusNear"; + sendCmd( $cmd ); +} + +sub focusFar +{ + print( "Focus Far\n" ); + my $cmd = "nphControlCamera?Direction=FocusFar"; + sendCmd( $cmd ); +} + +sub focusAuto +{ + print( "Focus Auto\n" ); + my $cmd = "nphControlCamera?Direction=FocusAuto"; + sendCmd( $cmd ); +} + +sub presetClear +{ + my $preset = shift || 1; + print( "Clear Preset $preset\n" ); + my $cmd = "nphPresetNameCheck?Data=$preset"; + sendCmd( $cmd ); +} + +sub presetSet +{ + my $preset = shift || 1; + print( "Set Preset $preset\n" ); + my $cmd = "nphPresetNameCheck?PresetName=$preset&Data=$preset"; + sendCmd( $cmd ); +} + +sub presetGoto +{ + my $preset = shift || 1; + print( "Goto Preset $preset\n" ); + my $cmd = "nphControlCamera?Direction=Preset&PresetOperation=Move&Data=$preset"; + sendCmd( $cmd ); +} + +sub presetHome +{ + print( "Home Preset\n" ); + my $cmd = "nphControlCamera?Direction=HomePosition"; + sendCmd( $cmd ); +} + +if ( $command eq "move_con_up" ) +{ + moveUp(); +} +elsif ( $command eq "move_con_down" ) +{ + moveDown(); +} +elsif ( $command eq "move_con_left" ) +{ + moveLeft(); +} +elsif ( $command eq "move_con_right" ) +{ + moveRight(); +} +elsif ( $command eq "move_map" ) +{ + moveMap( $xcoord, $ycoord, $width, $height ); +} +elsif ( $command eq "zoom_con_tele" ) +{ + zoomTele(); +} +elsif ( $command eq "zoom_con_wide" ) +{ + zoomWide(); +} +elsif ( $command eq "focus_con_near" ) +{ + focusNear(); +} +elsif ( $command eq "focus_con_far" ) +{ + focusFar(); +} +elsif ( $command eq "focus_auto" ) +{ + focusAuto(); +} +elsif ( $command eq "focus_man" ) +{ + #focusMan(); +} +elsif ( $command eq "preset_home" ) +{ + presetHome(); +} +elsif ( $command eq "preset_set" ) +{ + presetSet( $preset ); +} +elsif ( $command eq "preset_goto" ) +{ + presetGoto(); +} +else +{ + print( "Error, can't handle command $command\n" ); +} diff --git a/scripts/zmcontrol-pelco-d.pl.z b/scripts/zmcontrol-pelco-d.pl.z new file mode 100644 index 000000000..5514da670 --- /dev/null +++ b/scripts/zmcontrol-pelco-d.pl.z @@ -0,0 +1,566 @@ +#!/usr/bin/perl -wT +# +# ========================================================================== +# +# ZoneMinder Pelco-D Control Script, $Date$, $Revision$ +# Copyright (C) 2003 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 script continuously monitors the recorded events for the given +# monitor and applies any filters which would delete and/or upload +# matching events +# +use strict; + +# ========================================================================== +# +# These are the elements you need to edit to suit your installation +# +# ========================================================================== +use constant ZM_CONFIG => ""; +use constant ZM_PATH_BIN => ""; + +# Load the config from the database into the symbol table +BEGIN +{ + no strict 'refs'; + + open( CONFIG, "<".ZM_CONFIG ) or die( "Can't open config file: $!" ); + foreach my $str ( ) + { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*([^=\s]+)\s*$/; + $name =~ tr/a-z/A-Z/; + if (( $name eq 'ZM_DB_SERVER' ) || + ( $name eq 'ZM_DB_NAME' ) || + ( $name eq 'ZM_DB_USER' ) || + ( $name eq 'ZM_DB_PASS' )) + { + *{$name} = sub { $value }; + } + } + close( CONFIG ); + + use DBI; + my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_SERVER, &ZM_DB_USER, &ZM_DB_PASS ); + my $sql = "select * from Config"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); + while( my $config = $sth->fetchrow_hashref() ) + { + *{$config->{Name}} = sub { $config->{Value} }; + } + $sth->finish(); + $dbh->disconnect(); +} + +use Getopt::Long; +use Device::SerialPort; + +use constant LOG_FILE => ZM_PATH_LOGS.'/zmcontrol-pelco-d.log'; + +$| = 1; + +$ENV{PATH} = '/bin:/usr/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +sub Usage +{ + print( " +Usage: zmcontrol-pelco-d.pl +"); + exit( -1 ); +} + +my $arg_string = join( " ", @ARGV ); + +my $device = "/dev/ttyS0"; +my $address = 1; +my $command; +my ( $speed, $step ); +my ( $xcoord, $ycoord ); +my ( $panspeed, $tiltspeed ); +my ( $panstep, $tiltstep ); +my $preset; + +if ( !GetOptions( + 'device=s'=>\$device, + 'address=i'=>\$address, + 'command=s'=>\$command, + 'speed=i'=>\$speed, + 'step=i'=>\$step, + 'xcoord=i'=>\$xcoord, + 'ycoord=i'=>\$ycoord, + 'panspeed=i'=>\$panspeed, + 'tiltspeed=i'=>\$tiltspeed, + 'panstep=i'=>\$panstep, + 'tiltstep=i'=>\$tiltstep, + 'preset=i'=>\$preset + ) +) +{ + Usage(); +} + +my $log_file = LOG_FILE; +open( LOG, ">>$log_file" ) or die( "Can't open log file: $!" ); +open( STDOUT, ">&LOG" ) || die( "Can't dup stdout: $!" ); +select( STDOUT ); $| = 1; +open( STDERR, ">&LOG" ) || die( "Can't dup stderr: $!" ); +select( STDERR ); $| = 1; +select( LOG ); $| = 1; + +print( $arg_string."\n" ); + +srand( time() ); + +my $serial_port = new Device::SerialPort( $device ); +$serial_port->baudrate(2400); +$serial_port->databits(8); +$serial_port->parity('none'); +$serial_port->stopbits(1); +$serial_port->handshake('rts'); + +$serial_port->read_const_time(50); +$serial_port->read_char_time(10); + +sub printMsg +{ + my $msg = shift; + my $prefix = shift || ""; + $prefix = $prefix.": " if ( $prefix ); + + my $line_length = 16; + my $msg_len = int(@$msg); + + print( $prefix ); + for ( my $i = 0; $i < $msg_len; $i++ ) + { + if ( ($i > 0) && ($i%$line_length == 0) && ($i != ($msg_len-1)) ) + { + printf( "\n%*s", length($prefix), "" ); + } + printf( "%02x ", $msg->[$i] ); + } + print( "[".$msg_len."]\n" ); +} + +sub sendCmd +{ + my $cmd = shift; + my $ack = shift || 0; + + my $result = undef; + + my $checksum = 0x00; + for ( my $i = 1; $i < int(@$cmd); $i++ ) + { + $checksum += $cmd->[$i]; + $checksum &= 0xff; + } + push( @$cmd, $checksum ); + + printMsg( $cmd, "Tx" ); + my $id = $cmd->[0] & 0xf; + + my $tx_msg = pack( "C*", @$cmd ); + + #print( "Tx: ".length( $tx_msg )." bytes\n" ); + my $n_bytes = $serial_port->write( $tx_msg ); + if ( !$n_bytes ) + { + print( "Error, write failed: $!" ); + } + if ( $n_bytes != length($tx_msg) ) + { + print( "Error, incomplete write, only ".$n_bytes." of ".length($tx_msg)." written: $!" ); + } + + if ( $ack ) + { + print( "Waiting for ack\n" ); + my $max_wait = 3; + my $now = time(); + while( 1 ) + { + my ( $count, $rx_msg ) = $serial_port->read(4); + + if ( $count ) + { + #print( "Rx1: ".$count." bytes\n" ); + my @resp = unpack( "C*", $rx_msg ); + printMsg( \@resp, "Rx" ); + + if ( $resp[0] = 0x80 + ($id<<4) ) + { + if ( ($resp[1] & 0xf0) == 0x40 ) + { + my $socket = $resp[1] & 0x0f; + print( "Got ack for socket $socket\n" ); + $result = !undef; + } + else + { + printf( "Error, got bogus response\n" ); + } + last; + } + else + { + print( "Error, got message for camera ".(($resp[0]-0x80)>>4)."\n" ); + } + } + if ( (time() - $now) > $max_wait ) + { + last; + } + } + } +} + +my $sync = 0xff; + +sub cameraOff +{ + print( "Camera Off\n" ); + my @msg = ( $sync, $address, 0x08, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub cameraOn +{ + print( "Camera On\n" ); + my @msg = ( $sync, $address, 0x88, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub autoScan +{ + print( "Auto Scan\n" ); + my @msg = ( $sync, $address, 0x90, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub manScan +{ + print( "Manual Scan\n" ); + my @msg = ( $sync, $address, 0x10, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub stop +{ + print( "Stop\n" ); + my @msg = ( $sync, $address, 0x00, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub moveUp +{ + print( "Move Up\n" ); + my $speed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x08, 0x00, $speed ); + sendCmd( \@msg ); +} + +sub moveDown +{ + print( "Move Down\n" ); + my $speed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x10, 0x00, $speed ); + sendCmd( \@msg ); +} + +sub moveLeft +{ + print( "Move Left\n" ); + my $speed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x04, $speed, 0x00 ); + sendCmd( \@msg ); +} + +sub moveRight +{ + print( "Move Right\n" ); + my $speed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x02, $speed, 0x00 ); + sendCmd( \@msg ); +} + +sub moveUpLeft +{ + print( "Move Up/Left\n" ); + my $panspeed = shift || 0x3f; + my $tiltspeed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x0c, $panspeed, $tiltspeed ); + sendCmd( \@msg ); +} + +sub moveUpRight +{ + print( "Move Up/Right\n" ); + my $panspeed = shift || 0x3f; + my $tiltspeed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x0a, $panspeed, $tiltspeed ); + sendCmd( \@msg ); +} + +sub moveDownLeft +{ + print( "Move Down/Left\n" ); + my $panspeed = shift || 0x3f; + my $tiltspeed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x14, $panspeed, $tiltspeed ); + sendCmd( \@msg ); +} + +sub moveDownRight +{ + print( "Move Down/Right\n" ); + my $panspeed = shift || 0x3f; + my $tiltspeed = shift || 0x3f; + my @msg = ( $sync, $address, 0x00, 0x12, $panspeed, $tiltspeed ); + sendCmd( \@msg ); +} + +sub flip180 +{ + print( "Flip 180\n" ); + my @msg = ( $sync, $address, 0x00, 0x07, 0x00, 0x21 ); + sendCmd( \@msg ); +} + +sub zeroPan +{ + print( "Zero Pan\n" ); + my @msg = ( $sync, $address, 0x00, 0x07, 0x00, 0x22 ); + sendCmd( \@msg ); +} + +sub setZoomSpeed +{ + my $speed = shift; + my @msg = ( $sync, $address, 0x00, 0x25, 0x00, $speed ); + sendCmd( \@msg, 1 ); +} + +sub zoomTele +{ + print( "Zoom Tele\n" ); + my $speed = shift || 0x01; + setZoomSpeed( $speed ); + my @msg = ( $sync, $address, 0x00, 0x20, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub zoomWide +{ + print( "Zoom Wide\n" ); + my $speed = shift || 0x01; + setZoomSpeed( $speed ); + my @msg = ( $sync, $address, 0x00, 0x40, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub setFocusSpeed +{ + my $speed = shift; + my @msg = ( $sync, $address, 0x00, 0x27, 0x00, $speed ); + sendCmd( \@msg, 1 ); +} + +sub focusNear +{ + print( "Focus Near\n" ); + my $speed = shift || 0x03; + setFocusSpeed( $speed ); + my @msg = ( $sync, $address, 0x01, 0x00, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub focusFar +{ + print( "Focus Far\n" ); + my $speed = shift || 0x03; + setFocusSpeed( $speed ); + my @msg = ( $sync, $address, 0x00, 0x80, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub focusAuto +{ + print( "Focus Auto\n" ); + my @msg = ( $sync, $address, 0x00, 0x2b, 0x00, 0x01 ); + sendCmd( \@msg ); +} + +sub focusMan +{ + print( "Focus Man\n" ); + my @msg = ( $sync, $address, 0x00, 0x2b, 0x00, 0x02 ); + sendCmd( \@msg ); +} + +sub writeScreen +{ + my $string = shift; + print( "Writing '$string' to screen\n" ); + + my @chars = unpack( "C*", $string ); + for ( my $i = 0; $i < length($string); $i++ ) + { + printf( "0x%02x\n", $chars[$i] ); + my @msg = ( $sync, $address, 0x00, 0x15, $i, $chars[$i] ); + sendCmd( \@msg ); + } +} + +sub clearScreen +{ + print( "Clear Screen\n" ); + my @msg = ( $sync, $address, 0x00, 0x17, 0x00, 0x00 ); + sendCmd( \@msg ); +} + +sub clearPreset +{ + my $preset = shift || 1; + print( "Clear Preset $preset\n" ); + my @msg = ( $sync, $address, 0x00, 0x05, 0x00, $preset ); + sendCmd( \@msg ); +} + +sub presetSet +{ + my $preset = shift || 1; + print( "Set Preset $preset\n" ); + my @msg = ( $sync, $address, 0x00, 0x03, 0x00, $preset ); + sendCmd( \@msg ); +} + +sub presetGoto +{ + my $preset = shift || 1; + print( "Goto Preset $preset\n" ); + my @msg = ( $sync, $address, 0x00, 0x07, 0x00, $preset ); + sendCmd( \@msg ); +} + +sub presetHome +{ + print( "Home Preset\n" ); + my @msg = ( $sync, $address, 0x00, 0x07, 0x00, 22 ); + sendCmd( \@msg ); +} + +if ( $command eq "wake" ) +{ + cameraOn(); +} +elsif ( $command eq "sleep" ) +{ + cameraOff(); +} +elsif ( $command eq "move_con_up" ) +{ + moveUp( $tiltspeed ); +} +elsif ( $command eq "move_con_down" ) +{ + moveDown( $tiltspeed ); +} +elsif ( $command eq "move_con_left" ) +{ + moveLeft( $panspeed ); +} +elsif ( $command eq "move_con_right" ) +{ + moveRight( $panspeed ); +} +elsif ( $command eq "move_con_upleft" ) +{ + moveUpLeft( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_upright" ) +{ + moveUpRight( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_downleft" ) +{ + moveDownLeft( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_downright" ) +{ + moveDownRight( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_stop" ) +{ + stop(); +} +elsif ( $command eq "zoom_con_tele" ) +{ + zoomTele( $speed ); +} +elsif ( $command eq "zoom_con_wide" ) +{ + zoomWide( $speed ); +} +elsif ( $command eq "zoom_stop" ) +{ + stop(); +} +elsif ( $command eq "focus_con_near" ) +{ + focusNear(); +} +elsif ( $command eq "focus_con_far" ) +{ + focusFar(); +} +elsif ( $command eq "focus_stop" ) +{ + stop(); +} +elsif ( $command eq "focus_auto" ) +{ + focusAuto(); +} +elsif ( $command eq "focus_man" ) +{ + focusMan(); +} +elsif ( $command eq "preset_home" ) +{ + presetHome(); +} +elsif ( $command eq "preset_set" ) +{ + presetSet( $preset ); +} +elsif ( $command eq "preset_goto" ) +{ + presetGoto( $preset ); +} +else +{ + print( "Error, can't handle command $command\n" ); +} + +$serial_port->close(); diff --git a/scripts/zmcontrol-visca.pl.z b/scripts/zmcontrol-visca.pl.z new file mode 100644 index 000000000..daa8990c3 --- /dev/null +++ b/scripts/zmcontrol-visca.pl.z @@ -0,0 +1,659 @@ +#!/usr/bin/perl -wT +# +# ========================================================================== +# +# ZoneMinder VISCA Control Script, $Date$, $Revision$ +# Copyright (C) 2003 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 script continuously monitors the recorded events for the given +# monitor and applies any filters which would delete and/or upload +# matching events +# +use strict; + +# ========================================================================== +# +# These are the elements you need to edit to suit your installation +# +# ========================================================================== +use constant ZM_CONFIG => ""; +use constant ZM_PATH_BIN => ""; + +# Load the config from the database into the symbol table +BEGIN +{ + no strict 'refs'; + + open( CONFIG, "<".ZM_CONFIG ) or die( "Can't open config file: $!" ); + foreach my $str ( ) + { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*([^=\s]+)\s*$/; + $name =~ tr/a-z/A-Z/; + if (( $name eq 'ZM_DB_SERVER' ) || + ( $name eq 'ZM_DB_NAME' ) || + ( $name eq 'ZM_DB_USER' ) || + ( $name eq 'ZM_DB_PASS' )) + { + *{$name} = sub { $value }; + } + } + close( CONFIG ); + + use DBI; + my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_SERVER, &ZM_DB_USER, &ZM_DB_PASS ); + my $sql = "select * from Config"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); + while( my $config = $sth->fetchrow_hashref() ) + { + *{$config->{Name}} = sub { $config->{Value} }; + } + $sth->finish(); + $dbh->disconnect(); +} + +use Getopt::Long; +use Device::SerialPort; + +use constant LOG_FILE => ZM_PATH_LOGS.'/zmcontrol-visca.log'; + +$| = 1; + +$ENV{PATH} = '/bin:/usr/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +sub Usage +{ + print( " +Usage: zmcontrol-visca.pl +"); + exit( -1 ); +} + +my $arg_string = join( " ", @ARGV ); + +my $device = "/dev/ttyS0"; +my $address = 1; +my $command; +my ( $speed, $step ); +my ( $xcoord, $ycoord ); +my ( $panspeed, $tiltspeed ); +my ( $panstep, $tiltstep ); +my $preset; + +if ( !GetOptions( + 'device=s'=>\$device, + 'address=i'=>\$address, + 'command=s'=>\$command, + 'speed=i'=>\$speed, + 'step=i'=>\$step, + 'xcoord=i'=>\$xcoord, + 'ycoord=i'=>\$ycoord, + 'panspeed=i'=>\$panspeed, + 'tiltspeed=i'=>\$tiltspeed, + 'panstep=i'=>\$panstep, + 'tiltstep=i'=>\$tiltstep, + 'preset=i'=>\$preset + ) +) +{ + Usage(); +} + +my $log_file = LOG_FILE; +open( LOG, ">>$log_file" ) or die( "Can't open log file: $!" ); +open( STDOUT, ">&LOG" ) || die( "Can't dup stdout: $!" ); +select( STDOUT ); $| = 1; +open( STDERR, ">&LOG" ) || die( "Can't dup stderr: $!" ); +select( STDERR ); $| = 1; +select( LOG ); $| = 1; + +print( $arg_string."\n" ); + +srand( time() ); + +my $serial_port = new Device::SerialPort( $device ); +$serial_port->baudrate(9600); +$serial_port->databits(8); +$serial_port->parity('none'); +$serial_port->stopbits(1); +$serial_port->handshake('rts'); +$serial_port->stty_echo(0); + +#$serial_port->read_const_time(250); +$serial_port->read_char_time(2); + +sub printMsg +{ + my $msg = shift; + my $prefix = shift || ""; + $prefix = $prefix.": " if ( $prefix ); + + my $line_length = 16; + my $msg_len = int(@$msg); + + print( $prefix ); + for ( my $i = 0; $i < $msg_len; $i++ ) + { + if ( ($i > 0) && ($i%$line_length == 0) && ($i != ($msg_len-1)) ) + { + printf( "\n%*s", length($prefix), "" ); + } + printf( "%02x ", $msg->[$i] ); + } + print( "[".$msg_len."]\n" ); +} + +sub sendCmd +{ + my $cmd = shift; + my $ack = shift || 0; + my $cmp = shift || 0; + + my $result = undef; + + printMsg( $cmd, "Tx" ); + my $id = $cmd->[0] & 0xf; + + my $tx_msg = pack( "C*", @$cmd ); + + #print( "Tx: ".length( $tx_msg )." bytes\n" ); + my $n_bytes = $serial_port->write( $tx_msg ); + if ( !$n_bytes ) + { + print( "Error, write failed: $!" ); + } + if ( $n_bytes != length($tx_msg) ) + { + print( "Error, incomplete write, only ".$n_bytes." of ".length($tx_msg)." written: $!" ); + } + + if ( $ack ) + { + print( "Waiting for ack\n" ); + my $max_wait = 3; + my $now = time(); + while( 1 ) + { + my ( $count, $rx_msg ) = $serial_port->read(4); + + if ( $count ) + { + #print( "Rx1: ".$count." bytes\n" ); + my @resp = unpack( "C*", $rx_msg ); + printMsg( \@resp, "Rx" ); + + if ( $resp[0] = 0x80 + ($id<<4) ) + { + if ( ($resp[1] & 0xf0) == 0x40 ) + { + my $socket = $resp[1] & 0x0f; + print( "Got ack for socket $socket\n" ); + $result = !undef; + } + else + { + printf( "Error, got bogus response\n" ); + } + last; + } + else + { + print( "Error, got message for camera ".(($resp[0]-0x80)>>4)."\n" ); + } + } + if ( (time() - $now) > $max_wait ) + { + last; + } + } + } + + if ( $cmp ) + { + print( "Waiting for command complete\n" ); + my $max_wait = 10; + my $now = time(); + while( 1 ) + { + #print( "Waiting\n" ); + my ( $count, $rx_msg ) = $serial_port->read(16); + + if ( $count ) + { + #print( "Rx1: ".$count." bytes\n" ); + my @resp = unpack( "C*", $rx_msg ); + printMsg( \@resp, "Rx" ); + + if ( $resp[0] = 0x80 + ($id<<4) ) + { + if ( ($resp[1] & 0xf0) == 0x50 ) + { + printf( "Got command complete\n" ); + $result = !undef; + } + else + { + printf( "Error, got bogus response\n" ); + } + last; + } + else + { + print( "Error, got message for camera ".(($resp[0]-0x80)>>4)."\n" ); + } + } + if ( (time() - $now) > $max_wait ) + { + last; + } + } + } + return( $result ); +} + +my $sync = 0xff; + +sub cameraOff +{ + print( "Camera Off\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x00, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub cameraOn +{ + print( "Camera On\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x00, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub stop +{ + print( "Stop\n" ); + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, 0x00, 0x00, 0x03, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub moveUp +{ + print( "Move Up\n" ); + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, 0x00, $speed, 0x03, 0x01, $sync ); + sendCmd( \@msg ); +} + +sub moveDown +{ + print( "Move Down\n" ); + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, 0x00, $speed, 0x03, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub moveLeft +{ + print( "Move Left\n" ); + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $speed, 0x00, 0x01, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub moveRight +{ + print( "Move Right\n" ); + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $speed, 0x00, 0x02, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub moveUpLeft +{ + print( "Move Up/Left\n" ); + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x01, 0x01, $sync ); + sendCmd( \@msg ); +} + +sub moveUpRight +{ + print( "Move Up/Right\n" ); + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x02, 0x01, $sync ); + sendCmd( \@msg ); +} + +sub moveDownLeft +{ + print( "Move Down/Left\n" ); + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x01, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub moveDownRight +{ + print( "Move Down/Right\n" ); + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x02, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub stepUp +{ + print( "Step Up\n" ); + my $step = shift; + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, 0x00, $speed, 0x00, 0x00, 0x00, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, $sync ); + + sendCmd( \@msg ); +} + +sub stepDown +{ + print( "Step Down\n" ); + my $step = shift; + $step = -$step; + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, 0x00, $speed, 0x00, 0x00, 0x00, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, $sync ); + sendCmd( \@msg ); +} + +sub stepLeft +{ + print( "Step Left\n" ); + my $step = shift; + $step = -$step; + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $speed, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, 0x00, 0x00, 0x00, 0x00, $sync ); + sendCmd( \@msg ); +} + +sub stepRight +{ + print( "Step Right\n" ); + my $step = shift; + my $speed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $speed, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, 0x00, 0x00, 0x00, 0x00, $sync ); + sendCmd( \@msg ); +} + +sub stepUpLeft +{ + print( "Step Up/Left\n" ); + my $panstep = shift; + $panstep = -$panstep; + my $tiltstep = shift; + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, $sync ); + sendCmd( \@msg ); +} + +sub stepUpRight +{ + print( "Step Up/Right\n" ); + my $panstep = shift; + my $tiltstep = shift; + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, $sync ); + sendCmd( \@msg ); +} + +sub stepDownLeft +{ + print( "Step Down/Left\n" ); + my $panstep = shift; + $panstep = -$panstep; + my $tiltstep = shift; + $tiltstep = -$tiltstep; + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, $sync ); + sendCmd( \@msg ); +} + +sub stepDownRight +{ + print( "Step Down/Right\n" ); + my $panstep = shift; + my $tiltstep = shift; + $tiltstep = -$tiltstep; + my $panspeed = shift || 0x40; + my $tiltspeed = shift || 0x40; + my @msg = ( 0x80|$address, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, $sync ); + sendCmd( \@msg ); +} + +sub zoomTele +{ + print( "Zoom Tele\n" ); + my $speed = shift || 0x06; + my @msg = ( 0x80|$address, 0x01, 0x04, 0x07, 0x20|$speed, $sync ); + sendCmd( \@msg ); +} + +sub zoomWide +{ + print( "Zoom Wide\n" ); + my $speed = shift || 0x06; + my @msg = ( 0x80|$address, 0x01, 0x04, 0x07, 0x30|$speed, $sync ); + sendCmd( \@msg ); +} + +sub zoomStop +{ + print( "Zoom Stop\n" ); + my $speed = shift || 0x06; + my @msg = ( 0x80|$address, 0x01, 0x04, 0x07, 0x00, $sync ); + sendCmd( \@msg ); +} + +sub focusNear +{ + print( "Focus Near\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x08, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub focusFar +{ + print( "Focus Far\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x08, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub focusStop +{ + print( "Focus Far\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x08, 0x00, $sync ); + sendCmd( \@msg ); +} + +sub focusAuto +{ + print( "Focus Auto\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x38, 0x02, $sync ); + sendCmd( \@msg ); +} + +sub focusMan +{ + print( "Focus Man\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x38, 0x03, $sync ); + sendCmd( \@msg ); +} + +sub presetClear +{ + my $preset = shift || 1; + print( "Clear Preset $preset\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x3f, 0x00, $preset, $sync ); + sendCmd( \@msg ); +} + +sub presetSet +{ + my $preset = shift || 1; + print( "Set Preset $preset\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x3f, 0x01, $preset, $sync ); + sendCmd( \@msg ); +} + +sub presetGoto +{ + my $preset = shift || 1; + print( "Goto Preset $preset\n" ); + my @msg = ( 0x80|$address, 0x01, 0x04, 0x3f, 0x02, $preset, $sync ); + sendCmd( \@msg ); +} + +sub presetHome +{ + print( "Home Preset\n" ); + my @msg = ( 0x80|$address, 0x01, 0x06, 0x04, $sync ); + sendCmd( \@msg ); +} + +if ( $command eq "move_con_up" ) +{ + moveUp( $tiltspeed ); +} +elsif ( $command eq "move_con_down" ) +{ + moveDown( $tiltspeed ); +} +elsif ( $command eq "move_con_left" ) +{ + moveLeft( $panspeed ); +} +elsif ( $command eq "move_con_right" ) +{ + moveRight( $panspeed ); +} +elsif ( $command eq "move_con_upleft" ) +{ + moveUpLeft( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_upright" ) +{ + moveUpRight( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_downleft" ) +{ + moveDownLeft( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_con_downright" ) +{ + moveDownLeft( $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_stop" ) +{ + stop(); +} +elsif ( $command eq "move_rel_up" ) +{ + stepUp( $tiltstep, $tiltspeed ); +} +elsif ( $command eq "move_rel_down" ) +{ + stepDown( $tiltstep, $tiltspeed ); +} +elsif ( $command eq "move_rel_left" ) +{ + stepLeft( $panstep, $panspeed ); +} +elsif ( $command eq "move_rel_right" ) +{ + stepRight( $panstep, $panspeed ); +} +elsif ( $command eq "move_rel_upleft" ) +{ + stepUpLeft( $panstep, $tiltstep, $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_rel_upright" ) +{ + stepUpRight( $panstep, $tiltstep, $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_rel_downleft" ) +{ + stepDownLeft( $panstep, $tiltstep, $panspeed, $tiltspeed ); +} +elsif ( $command eq "move_rel_downright" ) +{ + stepDownRight( $panstep, $tiltstep, $panspeed, $tiltspeed ); +} +elsif ( $command eq "zoom_con_tele" ) +{ + zoomTele( $speed ); +} +elsif ( $command eq "zoom_con_wide" ) +{ + zoomWide( $speed ); +} +elsif ( $command eq "zoom_stop" ) +{ + zoomStop(); +} +elsif ( $command eq "focus_con_near" ) +{ + focusNear(); +} +elsif ( $command eq "focus_con_far" ) +{ + focusFar(); +} +elsif ( $command eq "focus_stop" ) +{ + focusStop(); +} +elsif ( $command eq "focus_auto" ) +{ + focusAuto(); +} +elsif ( $command eq "focus_man" ) +{ + focusMan(); +} +elsif ( $command eq "preset_home" ) +{ + presetHome(); +} +elsif ( $command eq "preset_set" ) +{ + presetSet( $preset ); +} +elsif ( $command eq "preset_goto" ) +{ + presetGoto(); +} +else +{ + print( "Error, can't handle command $command\n" ); +} + +$serial_port->close(); diff --git a/scripts/zmdc.pl.z b/scripts/zmdc.pl.z index 74c7a31a6..c13f8a09f 100755 --- a/scripts/zmdc.pl.z +++ b/scripts/zmdc.pl.z @@ -90,7 +90,6 @@ use POSIX; use Socket; use IO::Handle; use Data::Dumper; -use Posix qw(sys_wait_h); $| = 1; @@ -98,7 +97,7 @@ $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; -my @daemons = ( 'zmc', 'zma', 'zmf', 'zmfilter.pl', 'zmaudit.pl', 'zmtrigger.pl', 'zmx10.pl', 'zmwatch.pl', 'zmupdate.pl' ); +my @daemons = ( 'zmc', 'zma', 'zmf', 'zmfilter.pl', 'zmaudit.pl', 'zmtrigger.pl', 'zmx10.pl', 'zmwatch.pl', 'zmupdate.pl', 'zmtrack.pl' ); my $command = shift @ARGV; die( "No command given" ) unless( $command ); diff --git a/scripts/zmpkg.pl.z b/scripts/zmpkg.pl.z index 667588999..dcc2f9f95 100755 --- a/scripts/zmpkg.pl.z +++ b/scripts/zmpkg.pl.z @@ -261,6 +261,16 @@ if ( $command =~ /^(?:start|restart)$/ ) } execute( ZM_PATH_BIN."/zmdc.pl start zma -m $monitor->{Id}" ); } + if ( ZM_OPT_CONTROL ) + { + if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) + { + if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) + { + execute( ZM_PATH_BIN."/zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); + } + } + } } } $sth->finish(); diff --git a/scripts/zmtrack.pl.z b/scripts/zmtrack.pl.z new file mode 100755 index 000000000..3ff96af83 --- /dev/null +++ b/scripts/zmtrack.pl.z @@ -0,0 +1,268 @@ +#!/usr/bin/perl -wT +# +# ========================================================================== +# +# ZoneMinder Experimental PTZ Tracking Script, $Date$, $Revision$ +# Copyright (C) 2003 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 script is used to trigger and cancel alarms from external sources +# using an arbitrary text based format +# +use strict; +use bytes; + +# ========================================================================== +# +# User config +# +# ========================================================================== + +use constant ZM_CONFIG => "/usr/local/etc/zm.conf"; # Path to the ZoneMinder config file, autogenerated do not change (from zmconfig) +use constant ZM_VERSION => "1.20.2"; # ZoneMinder version number, autogenerated do not change (from zmconfig) +use constant ZM_PATH_BIN => "/usr/local/bin"; # Path to the ZoneMinder executables, autogenerated do not change (from zmconfig) + +# Load the config from the database into the symbol table +BEGIN +{ + no strict 'refs'; + + open( CONFIG, "<".ZM_CONFIG ) or die( "Can't open config file: $!" ); + foreach my $str ( ) + { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*([^=\s]+)\s*$/; + $name =~ tr/a-z/A-Z/; + if (( $name eq 'ZM_DB_SERVER' ) || + ( $name eq 'ZM_DB_NAME' ) || + ( $name eq 'ZM_DB_USER' ) || + ( $name eq 'ZM_DB_PASS' )) + { + *{$name} = sub { $value }; + } + } + close( CONFIG ); + + use DBI; + my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_SERVER, &ZM_DB_USER, &ZM_DB_PASS ); + my $sql = "select * from Config"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); + while( my $config = $sth->fetchrow_hashref() ) + { + *{$config->{Name}} = sub { $config->{Value} }; + } + $sth->finish(); + $dbh->disconnect(); +} + +use constant LOG_FILE => ZM_PATH_LOGS.'/zmtrack-%s.log'; +use constant SLEEP_TIME => 10000; # In microseconds +use constant VERBOSE => 1; # Whether to output more verbose debug + +# ========================================================================== +# +# Don't change anything from here on down +# +# ========================================================================== + +use DBI; +use POSIX; +use Data::Dumper; +use Getopt::Long; +use Time::HiRes qw( usleep ); + +$| = 1; + +$ENV{PATH} = '/bin:/usr/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +my $mid = 0; + +sub Usage +{ + print( " + Usage: zmtrack.pl -m ,--monitor=] + Parameters are :- + -m, --monitor= - Id of the monitor to track + "); + exit( -1 ); +} + +if ( !GetOptions( 'monitor=s'=>\$mid ) ) +{ + Usage(); +} + +my ( $detaint_mid ) = $mid =~ /^(\d+)$/; +$mid = $detaint_mid; + +my $log_file = sprintf( LOG_FILE, $mid ); +open( LOG, ">>$log_file" ) or die( "Can't open log file: $!" ); +open( STDOUT, ">&LOG" ) || die( "Can't dup stdout: $!" ); +select( STDOUT ); $| = 1; +open( STDERR, ">&LOG" ) || die( "Can't dup stderr: $!" ); +select( STDERR ); $| = 1; +select( LOG ); $| = 1; + +print( "Tracker daemon $mid (experimental) starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + +my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_SERVER, ZM_DB_USER, ZM_DB_PASS ); + +my $sql = "select C.*,M.* from Monitors as M left join Controls as C on M.ControlId = C.Id where M.Id = ?"; +my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + +my $res = $sth->execute( $mid ) or die( "Can't execute '$sql': ".$sth->errstr() ); +my $monitor = $sth->fetchrow_hashref(); + +if ( !$monitor ) +{ + print( "Can't find monitor '$mid'\n" ); + exit( -1 ); +} +if ( !$monitor->{Controllable} ) +{ + print( "Monitor '$mid' is not controllable\n" ); + exit( -1 ); +} +if ( !$monitor->{TrackMotion} ) +{ + print( "Monitor '$mid' is not configured to track motion\n" ); + exit( -1 ); +} + +if ( !$monitor->{CanMoveMap} ) +{ + print( "Monitor '$mid' cannot move in map mode" ); + if ( $monitor->{CanMoveAbs} ) + { + print( ", falling back to pseudo map mode\n" ); + } + else + { + print( "\n" ); + exit( -1 ); + } +} + +print( "Found monitor for id '$monitor'\n" ) if ( VERBOSE ); +my $size = 512; # We only need the first 512 bytes really for the alarm state and forced alarm +$monitor->{ShmKey} = hex(ZM_SHM_KEY)|$monitor->{Id}; +$monitor->{ShmId} = shmget( $monitor->{ShmKey}, $size, 0 ); +if ( !defined($monitor->{ShmId}) ) +{ + printf( "Can't get shared memory id '%x': $!\n", $monitor->{ShmKey}, $! ); + exit( -1 ); +} + +sub Suspend +{ + my $monitor = shift; + my $suspend_cmd = "/usr/local/bin/zmu -m ".$monitor->{Id}." -u -U admin -P pc00zm"; + qx( $suspend_cmd ); +} + +sub Resume +{ + my $monitor = shift; + sleep( $monitor->{TrackDelay} ); + my $resume_cmd = "/usr/local/bin/zmu -m ".$monitor->{Id}." -r -U admin -P pc00zm"; + qx( $resume_cmd ); +} + +sub Track +{ + my $monitor = shift; + my ( $x, $y ) = @_; + my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x; + my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y; + my $move_cmd = $monitor->{Command}; + $move_cmd .= " --device=".$monitor->{ControlDevice} if ( $monitor->{ControlDevice} ); + $move_cmd .= " --address=".$monitor->{ControlAddress} if ( $monitor->{ControlAddress} ); + $move_cmd .= " --command=".($monitor->{CanMoveMap}?"move_map":"move_pseudo_map")." --xcoord=$x --ycoord=$y --width=".$monitor->{Width}." --height=".$monitor->{Height}; + qx( $move_cmd ); +} + +sub Return +{ + my $monitor = shift; + my $move_cmd = $monitor->{Command}; + $move_cmd .= " --device=".$monitor->{ControlDevice} if ( $monitor->{ControlDevice} ); + $move_cmd .= " --address=".$monitor->{ControlAddress} if ( $monitor->{ControlAddress} ); + $move_cmd .= " --command=".($monitor->{ReturnLocation}?"preset1":"preset_home"); + qx( $move_cmd ); +} + +my $last_alarm = 0; +if ( ($monitor->{ReturnLocation} >= 0) ) +{ + Suspend( $monitor ); + Return( $monitor ); + Resume( $monitor ); +} + +my $alarmed = undef; +while( 1 ) +{ + my $state; + if ( !shmread( $monitor->{ShmId}, $state, 8, 4 ) ) + { + print( "Can't read from shared memory: $!\n" ); + exit( -1 ); + } + $state = unpack( "l", $state ); + + if ( $state == 2 ) # Alarmed + { + my $alarm_pos; + if ( !shmread( $monitor->{ShmId}, $alarm_pos, 48, 8 ) ) + { + print( "Can't read from shared memory '$monitor->{ShmKey}/$monitor->{ShmId}': $!\n" ); + next; + } + my ( $alarm_x, $alarm_y ) = unpack( "ll", $alarm_pos ); + if ( $alarm_x > 0 && $alarm_y > 0 ) + { + print( "Got alarm at $alarm_x, $alarm_y\n" ) if ( VERBOSE ); + Suspend( $monitor ); + Track( $monitor, $alarm_x, $alarm_y ); + Resume( $monitor ); + $last_alarm = time(); + $alarmed = !undef; + } + } + else + { + if ( VERBOSE && $alarmed ) + { + print( "Left alarm state\n" ); + $alarmed = undef; + } + if ( ($monitor->{ReturnLocation} >= 0) && ($last_alarm > 0) && ((time()-$last_alarm) > $monitor->{ReturnDelay}) ) + { + print( "Returning to location ".$monitor->{ReturnLocation}."\n" ) if ( VERBOSE ); + Suspend( $monitor ); + Return( $monitor ); + Resume( $monitor ); + $last_alarm = 0; + } + } + usleep( SLEEP_TIME ); +} diff --git a/scripts/zmtrigger.pl.z b/scripts/zmtrigger.pl.z index ea207de7d..415f10c9a 100755 --- a/scripts/zmtrigger.pl.z +++ b/scripts/zmtrigger.pl.z @@ -64,7 +64,7 @@ BEGIN my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_SERVER, &ZM_DB_USER, &ZM_DB_PASS ); my $sql = "select * from Config"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); while( my $config = $sth->fetchrow_hashref() ) { *{$config->{Name}} = sub { $config->{Value} }; @@ -119,7 +119,7 @@ select( LOG ); $| = 1; print( "Trigger daemon starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); -my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_SERVER, ZM_DB_USERA, ZM_DB_PASSA ); +my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_SERVER, ZM_DB_USER, ZM_DB_PASS ); my $sql = "select * from Monitors where Id = ? or Name = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); @@ -304,7 +304,7 @@ sub handleMessage my $trigger = $1; my $delay = $2; my $force_data = pack( "llZ32Z256", $trigger eq "on"?1:2, $trigger eq "on"?$score:0, $cause, $text ); - if ( !shmwrite( $monitor->{ShmId}, $force_data, 52, 4+4+32+256 ) ) + if ( !shmwrite( $monitor->{ShmId}, $force_data, 60, 4+4+32+256 ) ) { print( "Can't write to shared memory: $!\n" ); } @@ -325,7 +325,7 @@ sub handleMessage elsif( $action eq "cancel" ) { my $force_data = pack( "llZ32Z256", 0, 0, "", "" ); - if ( !shmwrite( $monitor->{ShmId}, $force_data, 52, 4+4+32+256 ) ) + if ( !shmwrite( $monitor->{ShmId}, $force_data, 60, 4+4+32+256 ) ) { print( "Can't write to shared memory: $!\n" ); } diff --git a/scripts/zmupdate.pl.z b/scripts/zmupdate.pl.z index ac03bf2e3..3dc166bd2 100755 --- a/scripts/zmupdate.pl.z +++ b/scripts/zmupdate.pl.z @@ -79,11 +79,6 @@ BEGIN } $sth->finish(); $dbh->disconnect(); - if ( !ZM_DB_USER ) - { - *ZM_DB_USER = sub { ZM_DB_USERA }; - *ZM_DB_PASS = sub { ZM_DB_PASSA }; - } } use constant UPDATE_LOG_FILE => ZM_PATH_LOGS.'/zmupdate.log'; diff --git a/src/zm_box.h b/src/zm_box.h index 344455d9f..e8c7cc533 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -23,6 +23,8 @@ #include "zm.h" #include "zm_coord.h" +#include + // // Class used for storing a box, which is defined as a region // defined by two coordinates @@ -53,6 +55,12 @@ public: inline int Height() const { return( size.Y() ); } inline int Area() const { return( size.X()*size.Y() ); } + inline const Coord Centre() const + { + int mid_x = int(round(lo.X()+(size.X()/2.0))); + int mid_y = int(round(lo.Y()+(size.Y()/2.0))); + return( Coord( mid_x, mid_y ) ); + } inline bool Inside( const Coord &coord ) const { return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 239850cf1..5ffa0b2c6 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -64,6 +64,7 @@ Monitor::Monitor( int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, + bool p_track_motion, Purpose p_purpose, int p_n_zones, Zone *p_zones[] @@ -87,6 +88,7 @@ Monitor::Monitor( capture_delay( p_capture_delay ), fps_report_interval( p_fps_report_interval ), ref_blend_perc( p_ref_blend_perc ), + track_motion( p_track_motion ), image( width, height, (p_palette==VIDEO_PALETTE_GREY?1:3) ), ref_image( width, height, (p_palette==VIDEO_PALETTE_GREY?1:3) ), purpose( p_purpose ), @@ -132,6 +134,7 @@ Monitor::Monitor( int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, + bool p_track_motion, Purpose p_purpose, int p_n_zones, Zone *p_zones[] @@ -155,6 +158,7 @@ Monitor::Monitor( capture_delay( p_capture_delay ), fps_report_interval( p_fps_report_interval ), ref_blend_perc( p_ref_blend_perc ), + track_motion( p_track_motion ), image( width, height, (p_palette==VIDEO_PALETTE_GREY?1:3) ), ref_image( width, height, (p_palette==VIDEO_PALETTE_GREY?1:3) ), purpose( p_purpose ), @@ -215,6 +219,8 @@ void Monitor::Setup() fps = 0.0; event_count = 0; image_count = 0; + activity_state = ACTIVE; + resume_image_count = 0; first_alarm_count = 0; last_alarm_count = 0; state = IDLE; @@ -258,6 +264,8 @@ void Monitor::Setup() shared_data->hue = -1; shared_data->colour = -1; shared_data->contrast = -1; + shared_data->alarm_x = -1; + shared_data->alarm_y = -1; trigger_data->size = sizeof(TriggerData); trigger_data->trigger_state = TRIGGER_CANCEL; trigger_data->trigger_score = 0; @@ -290,7 +298,7 @@ void Monitor::Setup() Debug( 1, ( "Monitor %s has function %d", name, function )); Debug( 1, ( "Monitor %s LBF = '%s', LBX = %d, LBY = %d", name, label_format, label_coord.X(), label_coord.Y() )); - Debug( 1, ( "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc )); + Debug( 1, ( "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, track_motion )); if ( purpose == ANALYSIS ) { @@ -444,6 +452,16 @@ void Monitor::CancelForced() trigger_data->trigger_state = TRIGGER_CANCEL; } +void Monitor::Suspend() +{ + shared_data->action |= SUSPEND; +} + +void Monitor::Resume() +{ + shared_data->action |= RESUME; +} + int Monitor::Brightness( int p_brightness ) { if ( purpose != CAPTURE ) @@ -690,6 +708,33 @@ bool Monitor::Analyse() static Image **images; static int last_section_mod = 0; + if ( shared_data->action & SUSPEND ) + { + Info(( "Received suspend indication at count %d", image_count )); + + activity_state = SUSPENDED; + shared_data->action &= ~SUSPEND; + //shared_data->alarm_x = shared_data->alarm_y = -1; + } + if ( shared_data->action & RESUME ) + { + Info(( "Received resume indication at count %d", image_count )); + + activity_state = RESUMING; + //ref_image.Clear(); + ref_image = *snap_image; + resume_image_count = image_count+(warmup_count/2); + shared_data->action &= ~RESUME; + shared_data->alarm_x = shared_data->alarm_y = -1; + } + else if ( activity_state == RESUMING ) + { + if ( resume_image_count && (image_count >= resume_image_count) ) + { + activity_state = ACTIVE; + resume_image_count = 0; + } + } unsigned int score = 0; if ( Ready() ) { @@ -756,7 +801,11 @@ bool Monitor::Analyse() } if ( score ) { - if ( (state == IDLE || state == TAPE || state == PREALARM ) ) + if ( (bool)config.Item( ZM_OPT_CONTROL ) && track_motion && activity_state != ACTIVE ) + { + // Do nothing + } + else if ( (state == IDLE || state == TAPE || state == PREALARM ) ) { if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) { @@ -809,7 +858,11 @@ bool Monitor::Analyse() } else { - if ( state == ALARM ) + if ( (bool)config.Item( ZM_OPT_CONTROL ) && track_motion && activity_state != ACTIVE ) + { + // Do nothing + } + else if ( state == ALARM ) { Info(( "%s: %03d - Gone into alert state", name, image_count )); shared_data->state = state = ALERT; @@ -832,7 +885,7 @@ bool Monitor::Analyse() } } } - else if ( state == PREALARM ) + if ( state == PREALARM ) { if ( function != MOCORD ) { @@ -960,11 +1013,11 @@ int Monitor::Load( int device, Monitor **&monitors, Purpose purpose ) static char sql[BUFSIZ]; if ( device == -1 ) { - strncpy( sql, "select Id, Name, Function+0, Device, Channel, Format, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' and Type = 'Local'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Device, Channel, Format, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Local'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Device, Channel, Format, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' and Type = 'Local' and Device = %d", device ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Device, Channel, Format, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Local' and Device = %d", device ); } if ( mysql_query( &dbconn, sql ) ) { @@ -1012,6 +1065,7 @@ int Monitor::Load( int device, Monitor **&monitors, Purpose purpose ) atof(dbrow[25])>0.0?int(DT_PREC_3/atof(dbrow[25])):0, // MaxFPS atoi(dbrow[26]), // FPSReportInterval atoi(dbrow[27]), // RefBlendPerc + atoi(dbrow[28]), // TrackMotion purpose ); Zone **zones = 0; @@ -1035,11 +1089,11 @@ int Monitor::Load( const char *host, const char*port, const char *path, Monitor static char sql[BUFSIZ]; if ( !host ) { - strncpy( sql, "select Id, Name, Function+0, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' and Type = 'Remote' and Host = '%s' and Port = '%s' and Path = '%s'", host, port, path ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Host = '%s' and Port = '%s' and Path = '%s'", host, port, path ); } if ( mysql_query( &dbconn, sql ) ) { @@ -1087,6 +1141,7 @@ int Monitor::Load( const char *host, const char*port, const char *path, Monitor atof(dbrow[25])>0.0?int(DT_PREC_3/atof(dbrow[25])):0, // MaxFPS atoi(dbrow[26]), // FPSReportInterval atoi(dbrow[27]), // RefBlendPerc + atoi(dbrow[28]), // TrackMotion purpose ); Zone **zones = 0; @@ -1108,7 +1163,7 @@ int Monitor::Load( const char *host, const char*port, const char *path, Monitor Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) { static char sql[BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Device, Channel, Format, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc from Monitors where Id = %d", id ); + snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Device, Channel, Format, Host, Port, Path, Width, Height, Palette, Orientation+0, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, FPSReportInterval, RefBlendPerc, TrackMotion from Monitors where Id = %d", id ); if ( mysql_query( &dbconn, sql ) ) { Error(( "Can't run query: %s", mysql_error( &dbconn ) )); @@ -1156,6 +1211,7 @@ Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) atof(dbrow[29])>0.0?int(DT_PREC_3/atof(dbrow[29])):0, // MaxFPS atoi(dbrow[30]), // FPSReportInterval atoi(dbrow[31]), // RefBlendPerc + atoi(dbrow[32]), // TrackMotion purpose ); } @@ -1189,6 +1245,7 @@ Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) atof(dbrow[29])>0.0?int(DT_PREC_3/atof(dbrow[29])):0, // MaxFPS atoi(dbrow[30]), // FPSReportInterval atoi(dbrow[31]), // RefBlendPerc + atoi(dbrow[32]), // TrackMotion purpose ); } @@ -1441,6 +1498,7 @@ bool Monitor::DumpSettings( char *output, bool verbose ) sprintf( output+strlen(output), "Section Length : %d\n", section_length ); sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); + sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); sprintf( output+strlen(output), "Function: %d - %s\n", function, function==OFF?"None":( function==MONITOR?"Monitor":( @@ -1518,6 +1576,9 @@ unsigned int Monitor::Compare( const Image &comp_image ) } } + Coord alarm_centre; + int top_score = -1; + if ( alarm ) { alarm = false; @@ -1525,6 +1586,7 @@ unsigned int Monitor::Compare( const Image &comp_image ) } else { + // Find all alarm pixels in active zones for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { @@ -1540,6 +1602,14 @@ unsigned int Monitor::Compare( const Image &comp_image ) score += zone->Score(); zone->SetAlarm(); Debug( 3, ( "Zone is alarmed, zone score = %d", zone->Score() )); + if ( (bool)config.Item( ZM_OPT_CONTROL ) && track_motion ) + { + if ( (int)zone->Score() > top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } } } @@ -1559,6 +1629,14 @@ unsigned int Monitor::Compare( const Image &comp_image ) score += zone->Score(); zone->SetAlarm(); Debug( 3, ( "Zone is alarmed, zone score = %d", zone->Score() )); + if ( (bool)config.Item( ZM_OPT_CONTROL ) && track_motion ) + { + if ( zone->Score() > top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } } } } @@ -1584,6 +1662,18 @@ unsigned int Monitor::Compare( const Image &comp_image ) } } + if ( (activity_state == ACTIVE) && (top_score > 0) ) + { + shared_data->alarm_x = alarm_centre.X(); + shared_data->alarm_y = alarm_centre.Y(); + + Info(( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count )); + } + else + { + shared_data->alarm_x = shared_data->alarm_y = -1; + } + delete delta_image; // This is a small and innocent hack to prevent scores of 0 being returned in alarm state return( score?score:alarm ); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 912484f6b..1b8ebc39a 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -63,61 +63,66 @@ public: typedef enum { IDLE, PREALARM, ALARM, ALERT, TAPE } State; + typedef enum { ACTIVE, SUSPENDED, RESUMING } ActivityState; + protected: - static bool initialised; - static bool record_event_stats; - static bool record_diag_images; - static bool opt_adaptive_skip; - static bool create_analysis_images; - static bool blend_alarmed_images; - static bool timestamp_on_capture; - static int bulk_frame_interval; + static bool initialised; + static bool record_event_stats; + static bool record_diag_images; + static bool opt_adaptive_skip; + static bool create_analysis_images; + static bool blend_alarmed_images; + static bool timestamp_on_capture; + static int bulk_frame_interval; protected: // These are read from the DB and thereafter remain unchanged - int id; - char *name; - Function function; // What the monitor is doing - unsigned int width; // Normally the same as the camera, but not if partly rotated - unsigned int height; // Normally the same as the camera, but not if partly rotated - RunMode run_mode; // Whether the monitor is running continuously or is triggered - Orientation orientation; // Whether the image has to be rotated at all - int brightness; // The statically saved brightness of the camera - int contrast; // The statically saved contrast of the camera - int hue; // The statically saved hue of the camera - int colour; // The statically saved colour of the camera - char event_prefix[64]; // The prefix applied to event names as they are created - char label_format[64]; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images - int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count - int warmup_count; // How many images to process before looking for events - int pre_event_count; // How many images to hold and prepend to an alarm event - int post_event_count; // How many unalarmed images must occur before the alarm state is reset - int section_length; // How long events should last in continuous modes - int frame_skip; // How many frames to skip in continuous modes - int capture_delay; // How long we wait between capture frames - int alarm_frame_count; // How many alarm frames are required before an event is triggered - int fps_report_interval;// How many images should be captured/processed between reporting the current FPS - int ref_blend_perc; // Percentage of new image going into reference image. + int id; + char *name; + Function function; // What the monitor is doing + unsigned int width; // Normally the same as the camera, but not if partly rotated + unsigned int height; // Normally the same as the camera, but not if partly rotated + RunMode run_mode; // Whether the monitor is running continuously or is triggered + Orientation orientation; // Whether the image has to be rotated at all + int brightness; // The statically saved brightness of the camera + int contrast; // The statically saved contrast of the camera + int hue; // The statically saved hue of the camera + int colour; // The statically saved colour of the camera + char event_prefix[64]; // The prefix applied to event names as they are created + char label_format[64]; // The format of the timestamp on the images + Coord label_coord; // The coordinates of the timestamp on the images + int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count + int warmup_count; // How many images to process before looking for events + int pre_event_count; // How many images to hold and prepend to an alarm event + int post_event_count; // How many unalarmed images must occur before the alarm state is reset + int section_length; // How long events should last in continuous modes + int frame_skip; // How many frames to skip in continuous modes + int capture_delay; // How long we wait between capture frames + int alarm_frame_count; // How many alarm frames are required before an event is triggered + int fps_report_interval;// How many images should be captured/processed between reporting the current FPS + int ref_blend_perc; // Percentage of new image going into reference image. + bool track_motion; // Whether this monitor tries to track detected motion - double fps; - Image image; - Image ref_image; + double fps; + Image image; + Image ref_image; - Purpose purpose; // What this monitor has been created to do - int event_count; - int image_count; - int first_alarm_count; - int last_alarm_count; - int buffer_count; - int prealarm_count; - State state; - int n_zones; - Zone **zones; - Event *event; - time_t start_time; - time_t last_fps_time; - int shmid; + Purpose purpose; // What this monitor has been created to do + ActivityState activity_state; + int event_count; + int image_count; + int resume_image_count; + int first_alarm_count; + int last_alarm_count; + int buffer_count; + int prealarm_count; + State state; + int n_zones; + Zone **zones; + Event *event; + time_t start_time; + time_t last_fps_time; + int shmid; typedef struct Snapshot { @@ -127,7 +132,7 @@ protected: Snapshot *image_buffer; - typedef enum { GET_SETTINGS=0x0001, SET_SETTINGS=0x0002 } Action; + typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, SUSPEND=0x4, RESUME=0x8 } Action; typedef struct { int size; @@ -142,6 +147,8 @@ protected: int hue; int colour; int contrast; + int alarm_x; + int alarm_y; } SharedData; typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; @@ -174,8 +181,8 @@ protected: } public: - Monitor( int p_id, char *p_name, int p_function, int p_device, int p_channel, int p_format, int p_width, int p_height, int p_palette, int p_orientation, int p_brightness, int p_contrast, int p_hue, int p_colour, char *p_event_prefix, char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, Purpose p_purpose=QUERY, int p_n_zones=0, Zone *p_zones[]=0 ); - Monitor( int p_id, char *p_name, int p_function, const char *p_host, const char *p_port, const char *p_path, int p_width, int p_height, int p_palette, int p_orientation, int p_brightness, int p_contrast, int p_hue, int p_colour, char *p_event_prefix, char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, Purpose p_purpose=QUERY, int p_n_zones=0, Zone *p_zones[]=0 ); + Monitor( int p_id, char *p_name, int p_function, int p_device, int p_channel, int p_format, int p_width, int p_height, int p_palette, int p_orientation, int p_brightness, int p_contrast, int p_hue, int p_colour, char *p_event_prefix, char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, bool p_track_motion, Purpose p_purpose=QUERY, int p_n_zones=0, Zone *p_zones[]=0 ); + Monitor( int p_id, char *p_name, int p_function, const char *p_host, const char *p_port, const char *p_path, int p_width, int p_height, int p_palette, int p_orientation, int p_brightness, int p_contrast, int p_hue, int p_colour, char *p_event_prefix, char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, bool p_track_motion, Purpose p_purpose=QUERY, int p_n_zones=0, Zone *p_zones[]=0 ); ~Monitor(); void Setup(); @@ -210,6 +217,8 @@ public: void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOff(); void CancelForced(); + void Suspend(); + void Resume(); inline void TimestampImage( Image *ts_image, time_t ts_time ) const { @@ -297,7 +306,11 @@ public: inline bool Ready() { - return( function > MONITOR && image_count > warmup_count ); + if ( function <= MONITOR ) + return( false ); + if ( image_count <= warmup_count ) + return( false ); + return( true ); } void DumpImage( Image *dump_image ) const; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index b6fa0d70c..47e2c2be1 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -98,6 +98,9 @@ bool Zone::CheckAlarms( const Image *delta_image ) int alarm_lo_y = 0; int alarm_hi_y = 0; + int alarm_mid_x = -1; + int alarm_mid_y = -1; + int lo_x = limits.Lo().X(); int lo_y = limits.Lo().Y(); int hi_x = limits.Hi().X(); @@ -418,6 +421,32 @@ bool Zone::CheckAlarms( const Image *delta_image ) BlobStats *bs = &blob_stats[i]; if ( bs->count ) { + if ( bs->count == max_blob_size ) + { + //if ( monitor->followMotion() ) + if ( true ) + { + unsigned long x_total = 0; + unsigned long y_total = 0; + + for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) + { + unsigned char *spdiff = diff_image->Buffer( bs->lo_x, sy ); + for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) + { + if ( *spdiff == bs->tag ) + { + x_total += sx; + y_total += sy; + } + } + } + + alarm_mid_x = int(round(x_total/bs->count)); + alarm_mid_y = int(round(y_total/bs->count)); + } + } + if ( alarm_lo_x > bs->lo_x ) alarm_lo_x = bs->lo_x; if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y; if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x; @@ -444,6 +473,16 @@ bool Zone::CheckAlarms( const Image *delta_image ) alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) ); + //if ( monitor->followMotion() ) + if ( true ) + { + alarm_centre = Coord( alarm_mid_x, alarm_mid_y ); + } + else + { + alarm_centre = alarm_box.Centre(); + } + if ( (type < PRECLUSIVE) && check_method >= BLOBS && create_analysis_images ) { image = diff_image->HighlightEdges( alarm_rgb, &limits ); diff --git a/src/zm_zone.h b/src/zm_zone.h index fe88de034..77c70d562 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -78,6 +78,7 @@ protected: int min_blob_size; int max_blob_size; Box alarm_box; + Coord alarm_centre; unsigned int score; Image *image; @@ -120,6 +121,7 @@ public: inline bool Alarmed() const { return( alarmed ); } inline void SetAlarm() { alarmed = true; } inline void ClearAlarm() { alarmed = false; } + inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline unsigned int Score() const { return( score ); } inline void ResetStats() diff --git a/src/zmu.cpp b/src/zmu.cpp index 6fad8b6aa..fce72b5d2 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -48,14 +48,16 @@ void Usage( int status=-1 ) fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); fprintf( stderr, " ring buffer index if given\n" ); - fprintf( stderr, " -r, --read_index : Output ring buffer read index\n" ); - fprintf( stderr, " -w, --write_index : Output ring buffer write index\n" ); + fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); + fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); fprintf( stderr, " -e, --event : Output last event index\n" ); fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); + fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); + fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); fprintf( stderr, " -P, --password : and password combination of the given user\n" ); fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); @@ -64,23 +66,25 @@ void Usage( int status=-1 ) } typedef enum { - BOGUS=0x0000, - STATE=0x0001, - IMAGE=0x0002, - TIME=0x0004, - READ_IDX=0x0008, - WRITE_IDX=0x0010, - EVENT=0x0020, - FPS=0x0040, - ZONES=0x0080, - ALARM=0x0100, - NOALARM=0x0200, - CANCEL=0x0400, - QUERY=0x0800, - BRIGHTNESS=0x1000, - CONTRAST=0x2000, - HUE=0x4000, - COLOUR=0x8000 + BOGUS = 0x00000000, + STATE = 0x00000001, + IMAGE = 0x00000002, + TIME = 0x00000004, + READ_IDX = 0x00000008, + WRITE_IDX = 0x00000010, + EVENT = 0x00000020, + FPS = 0x00000040, + ZONES = 0x00000080, + ALARM = 0x00000100, + NOALARM = 0x00000200, + CANCEL = 0x00000400, + QUERY = 0x00000800, + BRIGHTNESS = 0x00001000, + CONTRAST = 0x00002000, + HUE = 0x00004000, + COLOUR = 0x00008000, + SUSPEND = 0x00010000, + RESUME = 0x00020000, } Function; bool ValidateAccess( User *user, int mon_id, Function function ) @@ -101,7 +105,7 @@ bool ValidateAccess( User *user, int mon_id, Function function ) if ( user->getMonitors() < User::PERM_VIEW ) allowed = false; } - if ( function & (ALARM|NOALARM|CANCEL|BRIGHTNESS|CONTRAST|HUE|COLOUR) ) + if ( function & (ALARM|NOALARM|CANCEL|SUSPEND|RESUME|BRIGHTNESS|CONTRAST|HUE|COLOUR) ) { if ( user->getMonitors() < User::PERM_EDIT ) allowed = false; @@ -136,14 +140,16 @@ int main( int argc, char *argv[] ) {"contrast", 2, 0, 'C'}, {"hue", 2, 0, 'H'}, {"contrast", 2, 0, 'O'}, - {"read_index", 0, 0, 'r'}, - {"write_index", 0, 0, 'w'}, + {"read_index", 0, 0, 'R'}, + {"write_index", 0, 0, 'W'}, {"event", 0, 0, 'e'}, {"fps", 0, 0, 'f'}, {"zones", 0, 0, 'z'}, {"alarm", 0, 0, 'a'}, {"noalarm", 0, 0, 'n'}, {"cancel", 0, 0, 'c'}, + {"suspend", 0, 0, 'u'}, + {"resume", 0, 0, 'r'}, {"query", 0, 0, 'q'}, {"username", 1, 0, 'U'}, {"password", 1, 0, 'P'}, @@ -169,7 +175,7 @@ int main( int argc, char *argv[] ) { int option_index = 0; - int c = getopt_long (argc, argv, "d:m:vsrwei::S:t::fzancqphB::C::H::O::U:P:A:", long_options, &option_index); + int c = getopt_long (argc, argv, "d:m:vsurwei::S:t::fzancqphB::C::H::O::U:P:A:", long_options, &option_index); if (c == -1) { break; @@ -206,10 +212,10 @@ int main( int argc, char *argv[] ) image_idx = atoi( optarg ); } break; - case 'r': + case 'R': function = Function(function | READ_IDX); break; - case 'w': + case 'W': function = Function(function | WRITE_IDX); break; case 'e': @@ -230,6 +236,12 @@ int main( int argc, char *argv[] ) case 'c': function = Function(function | CANCEL); break; + case 'u': + function = Function(function | SUSPEND); + break; + case 'r': + function = Function(function | RESUME); + break; case 'q': function = Function(function | QUERY); break; @@ -468,6 +480,18 @@ int main( int argc, char *argv[] ) printf( "Cancelling forced alarm on/off\n" ); monitor->CancelForced(); } + if ( function & SUSPEND ) + { + if ( verbose ) + printf( "Suspending motion detection\n" ); + monitor->Suspend(); + } + if ( function & RESUME ) + { + if ( verbose ) + printf( "Resuming motion detection\n" ); + monitor->Resume(); + } if ( function & QUERY ) { char mon_string[1024] = ""; diff --git a/web/Makefile.am b/web/Makefile.am index f570644df..f1a0e8279 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -1,22 +1,34 @@ AUTOMAKE_OPTIONS = gnu -# This should be set to your CGI directory +# This should be set to your web directory webdir = @WEB_PREFIX@ # And these to the user and group of your webserver webuser = @WEB_USER@ webgroup = @WEB_GROUP@ +SUBDIRS = \ + graphics + web_DATA = \ favicon.ico \ zm.php \ zm_actions.php \ zm_config.php \ + zm_control_funcs.php \ zm_db.php \ zm_funcs.php \ zm_html.php \ zm_html_styles.css \ zm_html_view_bandwidth.php \ + zm_html_view_blank.php \ zm_html_view_console.php \ + zm_html_view_control.php \ + zm_html_view_controlcaps.php \ + zm_html_view_controlmenu.php \ + zm_html_view_controlpanel.php \ + zm_html_view_controlpreset.php \ + zm_html_view_controlcaps.php \ + zm_html_view_controlcap.php \ zm_html_view_cycle.php \ zm_html_view_error.php \ zm_html_view_event.php \ @@ -92,12 +104,20 @@ EXTRA_DIST = \ zm.php \ zm_actions.php \ zm_config.php.z \ + zm_control_funcs.php \ zm_db.php \ zm_funcs.php \ zm_html.php \ zm_html_styles.css \ zm_html_view_bandwidth.php \ + zm_html_view_blank.php \ zm_html_view_console.php \ + zm_html_view_control.php \ + zm_html_view_controlmenu.php \ + zm_html_view_controlpanel.php \ + zm_html_view_controlpreset.php \ + zm_html_view_controlcaps.php \ + zm_html_view_controlcap.php \ zm_html_view_cycle.php \ zm_html_view_error.php \ zm_html_view_event.php \ @@ -170,6 +190,7 @@ EXTRA_DIST = \ install-data-hook: ( cd $(DESTDIR)$(webdir); chown $(webuser):$(webgroup) $(web_DATA) ) @-( cd $(DESTDIR)$(webdir); if ! test -e events; then mkdir events; fi; chown $(webuser):$(webgroup) events; chmod u+w events ) + @-( cd $(DESTDIR)$(webdir); if ! test -e graphics; then mkdir graphics; fi; chown $(webuser):$(webgroup) graphics; chmod u+w graphics ) @-( cd $(DESTDIR)$(webdir); if ! test -e images; then mkdir images; fi; chown $(webuser):$(webgroup) images; chmod u+w images ) @-( cd $(DESTDIR)$(webdir); if ! test -e sounds; then mkdir sounds; fi; chown $(webuser):$(webgroup) sounds; chmod u+w sounds ) @-( cd $(DESTDIR)$(webdir); if ! test -e temp; then mkdir temp; fi; chown $(webuser):$(webgroup) temp; chmod u+w temp ) diff --git a/web/zm_actions.php b/web/zm_actions.php index 9c85ea4ac..c78786255 100644 --- a/web/zm_actions.php +++ b/web/zm_actions.php @@ -295,6 +295,496 @@ if ( isset($action) ) if ( !$result ) die( mysql_error() ); } + elseif ( $action == "control" && isset( $mid ) ) + { + $result = mysql_query( "select * from Monitors as M inner join Controls as C on (M.ControlId = C.Id ) where M.Id = '$mid'" ); + if ( !$result ) + die( mysql_error() ); + $monitor = mysql_fetch_assoc( $result ); + + $ctrl_command = $monitor['Command']; + if ( $monitor['ControlDevice'] ) + $ctrl_command .= " --device=".$monitor['ControlDevice']; + if ( $monitor['ControlAddress'] ) + $ctrl_command .= " --address=".$monitor['ControlAddress']; + + //$ctrl_command .= " --command=".$control; + + if ( isset($x) && isset($y) ) + { + if ( $control == "move_map" ) + { + $x = deScale( $x, $scale ); + $y = deScale( $y, $scale ); + switch ( $monitor['Orientation'] ) + { + case '0' : + case '180' : + $width = $monitor['Width']; + $height = $monitor['Height']; + break; + case '90' : + case '270' : + $width = $monitor['Height']; + $height = $monitor['Width']; + break; + } + switch ( $monitor['Orientation'] ) + { + case '90' : + $temp_y = $y; + $y = $height - $x; + $x = $temp_y; + break; + case '180' : + $x = $width - $x; + $y = $height - $y; + break; + case '270' : + $temp_x = $x; + $x = $width - $y; + $y = $temp_x; + break; + } + $ctrl_command .= " -xcoord $x -ycoord $y -width $width -height $height"; + } + elseif ( $control == "move_pseudo_map" ) + { + $x = deScale( $x, $scale ); + $y = deScale( $y, $scale ); + + $half_width = $monitor['Width'] / 2; + $half_height = $monitor['Height'] / 2; + $x_factor = ($x - $half_width)/$half_width; + $y_factor = ($y - $half_height)/$half_height; + + switch ( $monitor['Orientation'] ) + { + case '90' : + $temp_y_factor = $y; + $y_factor = -$x_factor; + $x_factor = $temp_y_factor; + break; + case '180' : + $x_factor = -$x_factor; + $y_factor = -$y_factor; + break; + case '270' : + $temp_x_factor = $x; + $x_factor = -$y_factor; + $y_factor = $tenp_x_factor; + break; + } + + $turbo = 0.9; // Threshold for turbo speed + $blind = 0.1; // Threshold for blind spot + + $pan_control = ''; + $tilt_control = ''; + if ( $x_factor > $blind ) + { + $pan_control = 'right'; + } + elseif ( $x_factor < -$blind ) + { + $pan_control = 'left'; + } + if ( $y_factor > $blind ) + { + $tilt_control = 'down'; + } + elseif ( $y_factor < -$blind ) + { + $tilt_control = 'up'; + } + + $dirn = $tilt_control.$pan_control; + if ( !$dirn ) + { + // No command, probably in blind spot in middle + $control = 'null'; + } + else + { + $control = 'move_rel_'.$dirn; + $x_factor = abs($x_factor); + $y_factor = abs($y_factor); + + if ( $monitor['HasPanSpeed'] && $x_factor ) + { + if ( $monitor['HasTurboPan'] ) + { + if ( $x_factor >= $turbo ) + { + $pan_speed = $monitor['TurboPanSpeed']; + } + else + { + $x_factor = $x_factor/$turbo; + $pan_speed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$x_factor))); + } + } + else + { + $pan_speed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$x_factor))); + } + } + if ( $monitor['HasTiltSpeed'] && $y_factor ) + { + if ( $monitor['HasTurboTilt'] ) + { + if ( $y_factor >= $turbo ) + { + $tilt_speed = $monitor['TurboTiltSpeed']; + } + else + { + $y_factor = $y_factor/$turbo; + $tilt_speed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$y_factor))); + } + } + else + { + $tilt_speed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$y_factor))); + } + } + if ( preg_match( '/(left|right)$/', $dirn ) ) + { + $pan_step = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$x_factor))); + $ctrl_command .= " --panstep=".$pan_step." --panspeed=".$pan_speed; + } + if ( preg_match( '/^(up|down)/', $dirn ) ) + { + $tilt_step = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$y_factor))); + $ctrl_command .= " --tiltstep=".$tilt_step." --tiltspeed=".$tilt_speed; + } + } + } + else + { + $turbo = 0.9; // Threshold for turbo speed + $long_y = 48; + $short_x = 32; + $short_y = 32; + + if ( preg_match( '/^([^_]+)_([^_]+)_([^_]+)$/', $control, $matches ) ) + { + $command = $matches[1]; + $mode = $matches[2]; + $dirn = $matches[3]; + + switch( $command ) + { + case 'focus' : + { + switch( $dirn ) + { + case 'near' : + { + $factor = ($long_y-($y+1))/$long_y; + break; + } + case 'far' : + { + $factor = ($y+1)/$long_y; + break; + } + } + if ( $monitor['HasFocusSpeed'] ) + { + $speed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$factor))); + $ctrl_command .= " --speed=".$speed; + } + switch( $mode ) + { + case 'abs' : + case 'rel' : + { + $step = intval(round($monitor['MinFocusStep']+(($monitor['MaxFocusStep']-$monitor['MinFocusStep'])*$x_factor))); + $ctrl_command .= " --step=".$step; + break; + } + } + break; + } + case 'zoom' : + { + switch( $dirn ) + { + case 'tele' : + { + $factor = ($long_y-($y+1))/$long_y; + break; + } + case 'wide' : + { + $factor = ($y+1)/$long_y; + break; + } + } + if ( $monitor['HasZoomSpeed'] ) + { + $speed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$factor))); + $ctrl_command .= " --speed=".$speed; + } + switch( $mode ) + { + case 'abs' : + case 'rel' : + { + $step = intval(round($monitor['MinZoomStep']+(($monitor['MaxZoomStep']-$monitor['MinZoomStep'])*$x_factor))); + $ctrl_command .= " --step=".$step; + break; + } + } + break; + } + case 'iris' : + { + switch( $dirn ) + { + case 'open' : + { + $factor = ($long_y-($y+1))/$long_y; + break; + } + case 'close' : + { + $factor = ($y+1)/$long_y; + break; + } + } + if ( $monitor['HasIrisSpeed'] ) + { + $speed = intval(round($monitor['MinIrisSpeed']+(($monitor['MaxIrisSpeed']-$monitor['MinIrisSpeed'])*$factor))); + $ctrl_command .= " --speed=".$speed; + } + switch( $mode ) + { + case 'abs' : + case 'rel' : + { + $step = intval(round($monitor['MinIrisStep']+(($monitor['MaxIrisStep']-$monitor['MinIrisStep'])*$x_factor))); + $ctrl_command .= " --step=".$step; + break; + } + } + break; + } + case 'white' : + { + switch( $dirn ) + { + case 'in' : + { + $factor = ($long_y-($y+1))/$long_y; + break; + } + case 'out' : + { + $factor = ($y+1)/$long_y; + break; + } + } + if ( $monitor['HasWhiteSpeed'] ) + { + $speed = intval(round($monitor['MinWhiteSpeed']+(($monitor['MaxWhiteSpeed']-$monitor['MinWhiteSpeed'])*$factor))); + $ctrl_command .= " --speed=".$speed; + } + switch( $mode ) + { + case 'abs' : + case 'rel' : + { + $step = intval(round($monitor['MinWhiteStep']+(($monitor['MaxWhiteStep']-$monitor['MinWhiteStep'])*$x_factor))); + $ctrl_command .= " --step=".$step; + break; + } + } + break; + } + case 'gain' : + { + switch( $dirn ) + { + case 'up' : + { + $factor = ($long_y-($y+1))/$long_y; + break; + } + case 'down' : + { + $factor = ($y+1)/$long_y; + break; + } + } + if ( $monitor['HasGainSpeed'] ) + { + $speed = intval(round($monitor['MinGainSpeed']+(($monitor['MaxGainSpeed']-$monitor['MinGainSpeed'])*$factor))); + $ctrl_command .= " --speed=".$speed; + } + switch( $mode ) + { + case 'abs' : + case 'rel' : + { + $step = intval(round($monitor['MinGainStep']+(($monitor['MaxGainStep']-$monitor['MinGainStep'])*$x_factor))); + $ctrl_command .= " --step=".$step; + break; + } + } + break; + } + case 'move' : + { + $x_factor = 0; + $y_factor = 0; + + if ( preg_match( '/^up/', $dirn ) ) + { + $y_factor = ($short_y-($y+1))/$short_y; + } + elseif ( preg_match( '/^down/', $dirn ) ) + { + $y_factor = ($y+1)/$short_y; + } + if ( preg_match( '/left$/', $dirn ) ) + { + $x_factor = ($short_x-($x+1))/$short_x; + } + elseif ( preg_match( '/right$/', $dirn ) ) + { + $x_factor = ($x+1)/$short_x; + } + + if ( $monitor['Orientation'] != '0' ) + { + $conversions = array( + '90' => array( + 'up' => 'left', + 'down' => 'right', + 'left' => 'down', + 'right' => 'up', + 'upleft' => 'downleft', + 'upright' => 'upleft', + 'downleft' => 'downright', + 'downright' => 'upright', + ), + '180' => array( + 'up' => 'down', + 'down' => 'up', + 'left' => 'right', + 'right' => 'left', + 'upleft' => 'downright', + 'upright' => 'downleft', + 'downleft' => 'upright', + 'downright' => 'upleft', + ), + '270' => array( + 'up' => 'right', + 'down' => 'left', + 'left' => 'up', + 'right' => 'down', + 'upleft' => 'upright', + 'upright' => 'downright', + 'downleft' => 'upleft', + 'downright' => 'downleft', + ), + ); + $new_dirn = $conversions[$monitor['Orientation']][$dirn]; + $control = preg_replace( "/_$dirn\$/", "_$new_dirn", $control ); + $dirn = $new_dirn; + } + + if ( $monitor['HasPanSpeed'] && $x_factor ) + { + if ( $monitor['HasTurboPan'] ) + { + if ( $x_factor >= $turbo ) + { + $pan_speed = $monitor['TurboPanSpeed']; + } + else + { + $x_factor = $x_factor/$turbo; + $pan_speed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$x_factor))); + } + } + else + { + $pan_speed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$x_factor))); + } + $ctrl_command .= " --panspeed=".$pan_speed; + } + if ( $monitor['HasTiltSpeed'] && $y_factor ) + { + if ( $monitor['HasTurboTilt'] ) + { + if ( $y_factor >= $turbo ) + { + $tilt_speed = $monitor['TurboTiltSpeed']; + } + else + { + $y_factor = $y_factor/$turbo; + $tilt_speed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$y_factor))); + } + } + else + { + $tilt_speed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$y_factor))); + } + $ctrl_command .= " --tiltspeed=".$tilt_speed; + } + switch( $mode ) + { + case 'rel' : + case 'abs' : + { + if ( preg_match( '/(left|right)$/', $dirn ) ) + { + $pan_step = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$x_factor))); + $ctrl_command .= " --panstep=".$pan_step; + } + if ( preg_match( '/^(up|down)/', $dirn ) ) + + { + $tilt_step = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$y_factor))); + $ctrl_command .= " --tiltstep=".$tilt_step; + } + break; + } + } + } + } + } + } + } + else + { + if ( preg_match( '/^preset_goto_(\d+)$/', $control, $matches ) ) + { + $control = 'preset_goto'; + $ctrl_command .= " --preset ".$matches[1]; + } + elseif ( $control == "move_map" ) + { + $ctrl_command .= " -xcoord $x -ycoord $y"; + } + } + if ( $control != 'null' ) + { + if ( $monitor['Function'] == 'Modect' || $monitor['Function'] == 'Mocord' ) + { + $zmu_command = getZmuCommand( " -m $mid -r" ); + $zmu_output = exec( escapeshellcmd( $zmu_command ) ); + } + $ctrl_command .= " --command=".$control; + //echo $ctrl_command; + $ctrl_output = exec( escapeshellcmd( $ctrl_command ) ); + //echo $ctrl_output; + } + } elseif ( $action == "delete" ) { if ( $mark_zids ) @@ -567,6 +1057,46 @@ if ( isset($action) ) } $refresh_parent = true; } + elseif ( $action == "controlcap" && isset( $cid ) ) + { + if ( $cid > 0 ) + { + $result = mysql_query( "select * from Controls where Id = '$cid'" ); + if ( !$result ) + die( mysql_error() ); + $control = mysql_fetch_assoc( $result ); + } + else + { + $control = array(); + } + + // Define a field type for anything that's not simple text equivalent + $types = array( + // Empty + ); + + $changes = getFormChanges( $control, $new_control, $types ); + + if ( count( $changes ) ) + { + if ( $cid > 0 ) + { + simpleQuery( "update Controls set ".implode( ", ", $changes )." where Id = '$cid'" ); + $refresh_parent = true; + } + else + { + + $sql = "insert into Controls set ".implode( ", ", $changes ); + $result = mysql_query( $sql ); + if ( !$result ) + die( mysql_error() ); + $cid = mysql_insert_id(); + } + $refresh_parent = true; + } + } elseif ( $action == "delete" ) { if ( $run_state ) @@ -597,6 +1127,15 @@ if ( isset($action) ) } } } + if ( $mark_cids ) + { + foreach( $mark_cids as $mark_cid ) + { + simpleQuery( "delete from Controls where Id = '$mark_cid'" ); + simpleQuery( "update Monitors set Controllable = 0, ControlId = 0 where ControlId = '$mark_cid'" ); + $refresh_parent = true; + } + } } } if ( $action == "learn" ) diff --git a/web/zm_config.php.z b/web/zm_config.php.z index f09998c20..812c7739b 100644 --- a/web/zm_config.php.z +++ b/web/zm_config.php.z @@ -152,33 +152,37 @@ function loadConfig() // Javascript window sizes $jws = array( - 'login' => array( 'w'=>720, 'h'=>480 ), + 'bandwidth' => array( 'w'=>200, 'h'=>90 ), 'console' => array( 'w'=>720, 'h'=>312 ), - 'state' => array( 'w'=>300, 'h'=>120 ), - 'version' => array( 'w'=>320, 'h'=>140 ), + 'control' => array( 'w'=>380, 'h'=>480 ), + 'controlcaps' => array( 'w'=>700, 'h'=>320 ), + 'controlcap' => array( 'w'=>360, 'h'=>440 ), 'cycle' => array( 'w'=>16, 'h'=>32 ), - 'montage' => array( 'w'=>20, 'h'=>20 ), - 'monitor' => array( 'w'=>360, 'h'=>300 ), - 'watch' => array( 'w'=>96, 'h'=>372 ), 'device' => array( 'w'=>196, 'h'=>164 ), - 'function' => array( 'w'=>248, 'h'=>92 ), - 'events' => array( 'w'=>720, 'h'=>480 ), 'event' => array( 'w'=>96, 'h'=>168 ), + 'events' => array( 'w'=>720, 'h'=>480 ), 'filter' => array( 'w'=>560, 'h'=>250 ), 'filtersave' => array( 'w'=>560, 'h'=>220 ), - 'zones' => array( 'w'=>72, 'h'=>232 ), - 'zone' => array( 'w'=>360, 'h'=>500 ), - 'video' => array( 'w'=>100, 'h'=>80 ), + 'frames' => array( 'w'=>500, 'h'=>300 ), + 'function' => array( 'w'=>248, 'h'=>92 ), 'groups' => array( 'w'=>400, 'h'=>220 ), 'image' => array( 'w'=>48, 'h'=>80 ), - 'frames' => array( 'w'=>500, 'h'=>300 ), - 'stats' => array( 'w'=>680, 'h'=>200 ), - 'options' => array( 'w'=>760, 'h'=>480 ), - 'optionhelp' => array( 'w'=>320, 'h'=>240 ), - 'restarting' => array( 'w'=>250, 'h'=>150 ), - 'user' => array( 'w'=>230, 'h'=>340 ), - 'settings' => array( 'w'=>200, 'h'=>225 ), + 'login' => array( 'w'=>720, 'h'=>480 ), 'logout' => array( 'w'=>200, 'h'=>100 ), - 'bandwidth' => array( 'w'=>200, 'h'=>90 ), + 'monitor' => array( 'w'=>360, 'h'=>300 ), + 'montage' => array( 'w'=>20, 'h'=>20 ), + 'optionhelp' => array( 'w'=>320, 'h'=>240 ), + 'options' => array( 'w'=>760, 'h'=>480 ), + 'preset' => array( 'w'=>240, 'h'=>90 ), + 'restarting' => array( 'w'=>250, 'h'=>150 ), + 'settings' => array( 'w'=>200, 'h'=>225 ), + 'state' => array( 'w'=>300, 'h'=>120 ), + 'stats' => array( 'w'=>680, 'h'=>200 ), + 'user' => array( 'w'=>280, 'h'=>340 ), + 'version' => array( 'w'=>320, 'h'=>140 ), + 'video' => array( 'w'=>100, 'h'=>80 ), + 'watch' => array( 'w'=>96, 'h'=>384 ), + 'zone' => array( 'w'=>360, 'h'=>500 ), + 'zones' => array( 'w'=>72, 'h'=>232 ), ); ?> diff --git a/web/zm_control_funcs.php b/web/zm_control_funcs.php new file mode 100644 index 000000000..6e913ddf8 --- /dev/null +++ b/web/zm_control_funcs.php @@ -0,0 +1,497 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  
  
+ + + + + + +
+ + + + + + + + + + + +
  + + +
+ +":"  " ?> + +
+ +
 
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + diff --git a/web/zm_html_view_control.php b/web/zm_html_view_control.php new file mode 100644 index 000000000..0a37369c5 --- /dev/null +++ b/web/zm_html_view_control.php @@ -0,0 +1,61 @@ + + + + +ZM - <?= $zmSlangControl ?> + + + + + + + + + + + + + + + diff --git a/web/zm_html_view_controlcap.php b/web/zm_html_view_controlcap.php new file mode 100644 index 000000000..1fce33cc2 --- /dev/null +++ b/web/zm_html_view_controlcap.php @@ -0,0 +1,464 @@ + + + + +ZM - <?= $zmSlangControl ?> - <?= $control['Name'] ?> + + + + + + + + + + + +
-
 
+ + +$value ) +{ + if ( $tab == $name ) + { +?> + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$zmSlangLocal, 'Remote'=>$zmSlangRemote ); +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
checked>
 
disabled>  
+ + diff --git a/web/zm_html_view_controlcaps.php b/web/zm_html_view_controlcaps.php new file mode 100644 index 000000000..3fcc36341 --- /dev/null +++ b/web/zm_html_view_controlcaps.php @@ -0,0 +1,129 @@ + + + + +ZM - <?= $zmSlangControlCaps ?> + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
disabled>
  + + + disabled> + 
+ + diff --git a/web/zm_html_view_controlmenu.php b/web/zm_html_view_controlmenu.php new file mode 100644 index 000000000..e10efce91 --- /dev/null +++ b/web/zm_html_view_controlmenu.php @@ -0,0 +1,79 @@ + + + + + + + + +
+ + + + + + + + +
 
+
+ + diff --git a/web/zm_html_view_controlpanel.php b/web/zm_html_view_controlpanel.php new file mode 100644 index 000000000..95b651c4d --- /dev/null +++ b/web/zm_html_view_controlpanel.php @@ -0,0 +1,125 @@ + + + + +ZM - <?= $monitor['Name'] ?> - <?= $zmSlangEvents ?> + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + + + + + +
  + + + + + + + + + +
+
+ + diff --git a/web/zm_html_view_controlpreset.php b/web/zm_html_view_controlpreset.php new file mode 100644 index 000000000..8dc387fa1 --- /dev/null +++ b/web/zm_html_view_controlpreset.php @@ -0,0 +1,84 @@ + + + + +ZM - <?= $zmSlangSetPreset ?> + + + + + + + + + + + + + + + + +
ZoneMinder -
+ + + + + +
  
+ + diff --git a/web/zm_html_view_monitor.php b/web/zm_html_view_monitor.php index 6ecd71266..c797299cd 100644 --- a/web/zm_html_view_monitor.php +++ b/web/zm_html_view_monitor.php @@ -30,6 +30,10 @@ $tabs["source"] = $zmSlangSource; $tabs["timestamp"] = $zmSlangTimestamp; $tabs["buffers"] = $zmSlangBuffers; $tabs["misc"] = $zmSlangMisc; +if ( ZM_OPT_CONTROL ) +{ + $tabs["control"] = $zmSlangControl; +} if ( ZM_OPT_X10 ) { $tabs["x10"] = $zmSlangX10; @@ -76,6 +80,14 @@ else $monitor['PreEventCount'] = 10; $monitor['PostEventCount'] = 10; $monitor['AlarmFrameCount'] = 1; + $monitor['Controllable'] = 0; + $monitor['ControlType'] = 0; + $monitor['ControlDevice'] = ""; + $monitor['ControlAddress'] = ""; + $monitor['TrackMotion'] = 0; + $monitor['TrackDelay'] = ""; + $monitor['ReturnLocation'] = -1; + $monitor['ReturnDelay'] = ""; $monitor['SectionLength'] = 600; $monitor['FrameSkip'] = 0; $monitor['EventPrefix'] = 'Event-'; @@ -98,7 +110,7 @@ $orientations = array( $zmSlangNormal=>0, $zmSlangRotateRight=>90, $zmSlangInver -ZM - <?= $zmSlangMonitor ?> <?= $monitor['Name'] ?> +ZM - <?= $zmSlangMonitor ?> - <?= $monitor['Name'] ?> - + @@ -230,6 +304,19 @@ if ( $tab != 'buffers' ) + + + + + + + + + @@ -249,7 +336,7 @@ if ( ZM_OPT_X10 && $tab != 'x10' ) } ?> - + + + + + + + + $zmSlangNone, + '0' => $zmSlangHome, + '1' => $zmSlangPreset." 1", + ); +?> + + + - + - + - + + + - diff --git a/web/zm_html_view_montagefeed.php b/web/zm_html_view_montagefeed.php index 598cb2615..4cf4046e9 100644 --- a/web/zm_html_view_montagefeed.php +++ b/web/zm_html_view_montagefeed.php @@ -32,7 +32,15 @@ if ( empty($mode) ) $mode = "still"; } -$result = mysql_query( "select * from Monitors where Id = '$mid'" ); +if ( ZM_OPT_CONTROL ) +{ + $sql = "select M.*,C.CanMoveMap,C.CanMoveRel from Monitors as M left join Controls as C on (M.ControlId = C.Id ) where M.Id = '$mid'"; +} +else +{ + $sql = "select * from Monitors where M.Id = '$mid'"; +} +$result = mysql_query( $sql ); if ( !$result ) die( mysql_error() ); $monitor = mysql_fetch_assoc( $result ); @@ -150,9 +158,38 @@ autostart="true"> $stream_src = getStreamSrc( array( "mode=jpeg", "monitor=".$monitor['Id'], "scale=".$scale, "maxfps=".ZM_WEB_VIDEO_MAXFPS ) ); if ( canStreamNative() ) { + if ( ZM_OPT_CONTROL && ($monitor['CanMoveMap'] || $monitor['CanMoveRel']) ) + { +?> + + + + + + + + + + + + +<?= $monitor['Name'] ?> } else { + if ( ZM_OPT_CONTROL && ($monitor['CanMoveMap'] || $monitor['CanMoveRel']) ) + { +?> + + + + + + + + + + + +<?= $monitor['Name'] ?> diff --git a/web/zm_html_view_montagemenu.php b/web/zm_html_view_montagemenu.php index 9faf4f32b..399ce9382 100644 --- a/web/zm_html_view_montagemenu.php +++ b/web/zm_html_view_montagemenu.php @@ -38,11 +38,45 @@ if ( $mid ) die( mysql_error() ); $monitor = mysql_fetch_assoc( $result ); } +elseif ( ZM_OPT_CONTROL ) +{ + if ( $group ) + { + $sql = "select * from Groups where Id = '$group'"; + $result = mysql_query( $sql ); + if ( !$result ) + die( mysql_error() ); + $row = mysql_fetch_assoc( $result ); + $group_sql = "and find_in_set( Id, '".$row['MonitorIds']."' )"; + } + $sql = "select * from Monitors where Function != 'None' and Controllable = 1 $group_sql order by Id"; + $result = mysql_query( $sql ); + if ( !$result ) + die( mysql_error() ); + $control_mid = 0; + while( $row = mysql_fetch_assoc( $result ) ) + { + if ( !visibleMonitor( $row['Id'] ) ) + { + continue; + } + if ( !$control_mid ) + { + $control_mid = $row['Id']; + } + } +} ?> +
-
 
checked>
 
checked>
@@ -64,15 +98,20 @@ if ( $mid ) else { ?> - - - - - + + + - + - + + + + + + + + diff --git a/web/zm_html_view_watch.php b/web/zm_html_view_watch.php index eb65ff854..a44e7157f 100644 --- a/web/zm_html_view_watch.php +++ b/web/zm_html_view_watch.php @@ -45,9 +45,22 @@ window.resizeTo( - - - - + + + + + + + + diff --git a/web/zm_html_view_watchfeed.php b/web/zm_html_view_watchfeed.php index 9590ff138..77b6153b8 100644 --- a/web/zm_html_view_watchfeed.php +++ b/web/zm_html_view_watchfeed.php @@ -35,7 +35,8 @@ if ( empty($mode) ) if ( !isset( $scale ) ) $scale = ZM_WEB_DEFAULT_SCALE; -$result = mysql_query( "select * from Monitors where Id = '$mid'" ); +$sql = "select M.*,C.CanMoveMap,C.CanMoveRel from Monitors as M left join Controls as C on (M.ControlId = C.Id ) where M.Id = '$mid'"; +$result = mysql_query( $sql ); if ( !$result ) die( mysql_error() ); $monitor = mysql_fetch_assoc( $result ); @@ -52,7 +53,7 @@ header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); // HTTP/1.0 -$image_src = getStreamSrc( array( "mode=single", "monitor=".$monitor['Id'], "scale=".$scale ) ); +$image_src = getStreamSrc( array( "mode=single", "monitor=".$mid, "scale=".$scale ) ); ?> @@ -97,7 +98,7 @@ if ( $mode == "stream" ) { if ( ZM_VIDEO_STREAM_METHOD == 'mpeg' && ZM_VIDEO_LIVE_FORMAT ) { - $stream_src = getStreamSrc( array( "mode=mpeg", "monitor=".$monitor['Id'], "scale=".$scale, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_VIDEO_LIVE_FORMAT ) ); + $stream_src = getStreamSrc( array( "mode=mpeg", "monitor=".$mid, "scale=".$scale, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_VIDEO_LIVE_FORMAT ) ); if ( isWindows() ) { if ( isInternetExplorer() ) @@ -144,12 +145,41 @@ autostart="true"> } else { - $stream_src = getStreamSrc( array( "mode=jpeg", "monitor=".$monitor['Id'], "scale=".$scale, "maxfps=".ZM_WEB_VIDEO_MAXFPS ) ); + $stream_src = getStreamSrc( array( "mode=jpeg", "monitor=".$mid, "scale=".$scale, "maxfps=".ZM_WEB_VIDEO_MAXFPS ) ); if ( canStreamNative() ) { + if ( $control && ($monitor['CanMoveMap'] || $monitor['CanMoveRel']) ) + { +?> + + + + + + + + + + + + +<?= $monitor['Name'] ?> } else { + if ( $control && ($monitor['CanMoveMap'] || $monitor['CanMoveRel']) ) + { +?> + + + + + + + + + + + +<?= $monitor['Name'] ?> diff --git a/web/zm_html_view_watchmenu.php b/web/zm_html_view_watchmenu.php index bf9d75bdc..62566cd0d 100644 --- a/web/zm_html_view_watchmenu.php +++ b/web/zm_html_view_watchmenu.php @@ -63,7 +63,30 @@ function closeWindow() - + + + + + + + + @@ -73,9 +96,9 @@ function closeWindow() - + - + diff --git a/web/zm_lang_en_gb.php b/web/zm_lang_en_gb.php index 7a024f9e4..a73e512f1 100644 --- a/web/zm_lang_en_gb.php +++ b/web/zm_lang_en_gb.php @@ -73,6 +73,7 @@ $zmSlang24BitColour = '24 bit colour'; $zmSlang8BitGrey = '8 bit greyscale'; $zmSlangActual = 'Actual'; +$zmSlangAddNewControl = 'Add New Control'; $zmSlangAddNewMonitor = 'Add New Monitor'; $zmSlangAddNewUser = 'Add New User'; $zmSlangAddNewZone = 'Add New Zone'; @@ -108,21 +109,60 @@ $zmSlangAttrTime = 'Time'; $zmSlangAttrTotalScore = 'Total Score'; $zmSlangAttrWeekday = 'Weekday'; $zmSlangAutoArchiveEvents = 'Automatically archive all matches'; +$zmSlangAuto = 'Auto'; $zmSlangAutoDeleteEvents = 'Automatically delete all matches'; $zmSlangAutoEmailEvents = 'Automatically email details of all matches'; $zmSlangAutoExecuteEvents = 'Automatically execute command on all matches'; $zmSlangAutoMessageEvents = 'Automatically message details of all matches'; $zmSlangAutoUploadEvents = 'Automatically upload all matches'; $zmSlangAvgBrScore = 'Avg.
Score'; -$zmSlangBadMonitorChars = 'Monitor Names may only contain alphanumeric characters plus hyphen and underscore'; +$zmSlangBadNameChars = 'Names may only contain alphanumeric characters plus hyphen and underscore'; $zmSlangBandwidth = 'Bandwidth'; $zmSlangBlobPx = 'Blob Px'; $zmSlangBlobs = 'Blobs'; $zmSlangBlobSizes = 'Blob Sizes'; $zmSlangBrightness = 'Brightness'; $zmSlangBuffers = 'Buffers'; +$zmSlangCanAutoFocus = 'Can Auto Focus'; +$zmSlangCanAutoGain = 'Can Auto Gain'; +$zmSlangCanAutoIris = 'Can Auto Iris'; +$zmSlangCanAutoWhite = 'Can Auto White Bal.'; +$zmSlangCanAutoZoom = 'Can Auto Zoom'; $zmSlangCancel = 'Cancel'; $zmSlangCancelForcedAlarm = 'Cancel Forced Alarm'; +$zmSlangCanFocusAbs = 'Can Absolute Focus'; +$zmSlangCanFocus = 'Can Focus'; +$zmSlangCanFocusCon = 'Can Continuous Focus'; +$zmSlangCanFocusRel = 'Can Relative Focus'; +$zmSlangCanGainAbs = 'Can Absolute Gain'; +$zmSlangCanGain = 'Can Gain '; +$zmSlangCanGainCon = 'Can Continuous Gain'; +$zmSlangCanGainRel = 'Can Relative Gain'; +$zmSlangCanIrisAbs = 'Can Absolute Iris'; +$zmSlangCanIris = 'Can Iris'; +$zmSlangCanIrisCon = 'Can Continuous Iris'; +$zmSlangCanIrisRel = 'Can Relative Iris'; +$zmSlangCanMoveAbs = 'Can Absolute Move'; +$zmSlangCanMove = 'Can Move'; +$zmSlangCanMoveCon = 'Can Continuous Move'; +$zmSlangCanMoveDiag = 'Can Move Diagonally'; +$zmSlangCanMoveMap = 'Can Move Mapped'; +$zmSlangCanMoveRel = 'Can Relative Move'; +$zmSlangCanPan = 'Can Pan' ; +$zmSlangCanReset = 'Can Reset'; +$zmSlangCanSetPresets = 'Can Set Presets'; +$zmSlangCanSleep = 'Can Sleep'; +$zmSlangCanTilt = 'Can Tilt'; +$zmSlangCanWake = 'Can Wake'; +$zmSlangCanWhiteAbs = 'Can Absolute White Bal.'; +$zmSlangCanWhite = 'Can White Balance'; +$zmSlangCanWhiteBal = 'Can White Bal.'; +$zmSlangCanWhiteCon = 'Can Continuous White Bal.'; +$zmSlangCanWhiteRel = 'Can Relative White Bal.'; +$zmSlangCanZoomAbs = 'Can Absolute Zoom'; +$zmSlangCanZoom = 'Can Zoom'; +$zmSlangCanZoomCon = 'Can Continuous Zoom'; +$zmSlangCanZoomRel = 'Can Relative Zoom'; $zmSlangCaptureHeight = 'Capture Height'; $zmSlangCapturePalette = 'Capture Palette'; $zmSlangCaptureWidth = 'Capture Width'; @@ -132,6 +172,7 @@ $zmSlangCheckMethod = 'Alarm Check Method'; $zmSlangChooseFilter = 'Choose Filter'; $zmSlangClose = 'Close'; $zmSlangColour = 'Colour'; +$zmSlangCommand = 'Command'; $zmSlangConfig = 'Config'; $zmSlangConfiguredFor = 'Configured for'; $zmSlangConfirmPassword = 'Confirm Password'; @@ -141,6 +182,13 @@ $zmSlangConsole = 'Console'; $zmSlangContactAdmin = 'Please contact your adminstrator for details.'; $zmSlangContinue = 'Continue'; $zmSlangContrast = 'Contrast'; +$zmSlangControlAddress = 'Control Address'; +$zmSlangControl = 'Control'; +$zmSlangControlDevice = 'Control Device'; +$zmSlangControllable = 'Controllable'; +$zmSlangControlCap = 'Control Capability'; +$zmSlangControlCaps = 'Control Capabilities'; +$zmSlangControlType = 'Control Type'; $zmSlangCycle = 'Cycle'; $zmSlangCycleWatch = 'Cycle Watch'; $zmSlangDay = 'Day'; @@ -170,9 +218,11 @@ $zmSlangEventName = 'Event Name'; $zmSlangEventPrefix = 'Event Prefix'; $zmSlangEvents = 'Events'; $zmSlangExclude = 'Exclude'; +$zmSlangFar = 'Far'; $zmSlangFeed = 'Feed'; $zmSlangFilterPx = 'Filter Px'; $zmSlangFirst = 'First'; +$zmSlangFocus = 'Focus'; $zmSlangForceAlarm = 'Force Alarm'; $zmSlangFPS = 'fps'; $zmSlangFPSReportInterval = 'FPS Report Interval'; @@ -184,13 +234,26 @@ $zmSlangFrameSkip = 'Frame Skip'; $zmSlangFTP = 'FTP'; $zmSlangFunc = 'Func'; $zmSlangFunction = 'Function'; +$zmSlangGain = 'Gain'; $zmSlangGenerateVideo = 'Generate Video'; $zmSlangGeneratingVideo = 'Generating Video'; $zmSlangGoToZoneMinder = 'Go to ZoneMinder.com'; $zmSlangGrey = 'Grey'; $zmSlangGroups = 'Groups'; +$zmSlangHasFocusSpeed = 'Has Focus Speed'; +$zmSlangHasGainSpeed = 'Has Gain Speed'; +$zmSlangHasHomePreset = 'Has Home Preset'; +$zmSlangHasIrisSpeed = 'Has Iris Speed'; +$zmSlangHasPanSpeed = 'Has Pan Speed'; +$zmSlangHasPresets = 'Has Presets'; +$zmSlangHasTiltSpeed = 'Has Tilt Speed'; +$zmSlangHasTurboPan = 'Has Turbo Pan'; +$zmSlangHasTurboTilt = 'Has Turbo Tilt'; +$zmSlangHasWhiteSpeed = 'Has White Bal. Speed'; +$zmSlangHasZoomSpeed = 'Has Zoom Speed'; $zmSlangHighBW = 'High B/W'; $zmSlangHigh = 'High'; +$zmSlangHome = 'Home'; $zmSlangHour = 'Hour'; $zmSlangHue = 'Hue'; $zmSlangId = 'Id'; @@ -199,7 +262,9 @@ $zmSlangIgnore = 'Ignore'; $zmSlangImageBufferSize = 'Image Buffer Size (frames)'; $zmSlangImage = 'Image'; $zmSlangInclude = 'Include'; +$zmSlangIn = 'In'; $zmSlangInverted = 'Inverted'; +$zmSlangIris = 'Iris'; $zmSlangLanguage = 'Language'; $zmSlangLast = 'Last'; $zmSlangLimitResultsPost = 'results only;'; // This is used at the end of the phrase 'Limit to first N results only' @@ -212,10 +277,34 @@ $zmSlangLogin = 'Login'; $zmSlangLogout = 'Logout'; $zmSlangLowBW = 'Low B/W'; $zmSlangLow = 'Low'; +$zmSlangMain = 'Main'; +$zmSlangMan = 'Man'; +$zmSlangManual = 'Manual'; $zmSlangMark = 'Mark'; $zmSlangMaxBrScore = 'Max.
Score'; +$zmSlangMaxFocusRange = 'Max Focus Range'; +$zmSlangMaxFocusSpeed = 'Max Focus Speed'; +$zmSlangMaxFocusStep = 'Max Focus Step'; +$zmSlangMaxGainRange = 'Max Gain Range'; +$zmSlangMaxGainSpeed = 'Max Gain Speed'; +$zmSlangMaxGainStep = 'Max Gain Step'; $zmSlangMaximumFPS = 'Maximum FPS'; +$zmSlangMaxIrisRange = 'Max Iris Range'; +$zmSlangMaxIrisSpeed = 'Max Iris Speed'; +$zmSlangMaxIrisStep = 'Max Iris Step'; $zmSlangMax = 'Max'; +$zmSlangMaxPanRange = 'Max Pan Range'; +$zmSlangMaxPanSpeed = 'Max Pan Speed'; +$zmSlangMaxPanStep = 'Max Pan Step'; +$zmSlangMaxTiltRange = 'Max Tilt Range'; +$zmSlangMaxTiltSpeed = 'Max Tilt Speed'; +$zmSlangMaxTiltStep = 'Max Tilt Step'; +$zmSlangMaxWhiteRange = 'Max White Bal. Range'; +$zmSlangMaxWhiteSpeed = 'Max White Bal. Speed'; +$zmSlangMaxWhiteStep = 'Max White Bal. Step'; +$zmSlangMaxZoomRange = 'Max Zoom Range'; +$zmSlangMaxZoomSpeed = 'Max Zoom Speed'; +$zmSlangMaxZoomStep = 'Max Zoom Step'; $zmSlangMediumBW = 'Medium B/W'; $zmSlangMedium = 'Medium'; $zmSlangMinAlarmGeMinBlob = 'Minimum alarm pixels should be greater than or equal to minimum blob pixels'; @@ -224,34 +313,59 @@ $zmSlangMinAlarmPixelsLtMax = 'Minimum alarm pixels should be less than maximum $zmSlangMinBlobAreaLtMax = 'Minimum blob area should be less than maximum blob area'; $zmSlangMinBlobsLtMax = 'Minimum blobs should be less than maximum blobs'; $zmSlangMinFilterPixelsLtMax = 'Minimum filter pixels should be less than maximum filter pixels'; +$zmSlangMinFocusRange = 'Min Focus Range'; +$zmSlangMinFocusSpeed = 'Min Focus Speed'; +$zmSlangMinFocusStep = 'Min Focus Step'; +$zmSlangMinGainRange = 'Min Gain Range'; +$zmSlangMinGainSpeed = 'Min Gain Speed'; +$zmSlangMinGainStep = 'Min Gain Step'; +$zmSlangMinIrisRange = 'Min Iris Range'; +$zmSlangMinIrisSpeed = 'Min Iris Speed'; +$zmSlangMinIrisStep = 'Min Iris Step'; +$zmSlangMinPanRange = 'Min Pan Range'; +$zmSlangMinPanSpeed = 'Min Pan Speed'; +$zmSlangMinPanStep = 'Min Pan Step'; $zmSlangMinPixelThresLtMax = 'Minimum pixel threshold should be less than maximum pixel threshold'; +$zmSlangMinTiltRange = 'Min Tilt Range'; +$zmSlangMinTiltSpeed = 'Min Tilt Speed'; +$zmSlangMinTiltStep = 'Min Tilt Step'; +$zmSlangMinWhiteRange = 'Min White Bal. Range'; +$zmSlangMinWhiteSpeed = 'Min White Bal. Speed'; +$zmSlangMinWhiteStep = 'Min White Bal. Step'; +$zmSlangMinZoomRange = 'Min Zoom Range'; +$zmSlangMinZoomSpeed = 'Min Zoom Speed'; +$zmSlangMinZoomStep = 'Min Zoom Step'; $zmSlangMisc = 'Misc'; $zmSlangMonitorIds = 'Monitor Ids'; $zmSlangMonitor = 'Monitor'; $zmSlangMonitors = 'Monitors'; $zmSlangMontage = 'Montage'; $zmSlangMonth = 'Month'; +$zmSlangMove = 'Move'; $zmSlangMustBeGe = 'must be greater than or equal to'; $zmSlangMustBeLe = 'must be less than or equal to'; $zmSlangMustConfirmPassword = 'You must confirm the password'; $zmSlangMustSupplyPassword = 'You must supply a password'; $zmSlangMustSupplyUsername = 'You must supply a username'; $zmSlangName = 'Name'; +$zmSlangNear = 'Near'; $zmSlangNetwork = 'Network'; -$zmSlangNew = 'New'; $zmSlangNewGroup = 'New Group'; +$zmSlangNew = 'New'; $zmSlangNewPassword = 'New Password'; $zmSlangNewState = 'New State'; $zmSlangNewUser = 'New User'; $zmSlangNext = 'Next'; $zmSlangNoFramesRecorded = 'There are no frames recorded for this event'; +$zmSlangNoGroups = 'No groups have been defined'; $zmSlangNoneAvailable = 'None available'; $zmSlangNone = 'None'; $zmSlangNo = 'No'; -$zmSlangNoGroups = 'No groups have been defined'; $zmSlangNormal = 'Normal'; $zmSlangNoSavedFilters = 'NoSavedFilters'; $zmSlangNoStatisticsRecorded = 'There are no statistics recorded for this event/frame'; +$zmSlangNumPresets = 'Num Presets'; +$zmSlangOpen = 'Open'; $zmSlangOpEq = 'equal to'; $zmSlangOpGtEq = 'greater than or equal to'; $zmSlangOpGt = 'greater than'; @@ -267,19 +381,24 @@ $zmSlangOptionRestartWarning = 'These changes may not come into effect fully\nwh $zmSlangOptions = 'Options'; $zmSlangOrEnterNewName = 'or enter new name'; $zmSlangOrientation = 'Orientation'; +$zmSlangOut = 'Out'; $zmSlangOverwriteExisting = 'Overwrite Existing'; $zmSlangPaged = 'Paged'; +$zmSlangPan = 'Pan'; +$zmSlangPanTilt = 'Pan/Tilt'; $zmSlangParameter = 'Parameter'; $zmSlangPassword = 'Password'; $zmSlangPasswordsDifferent = 'The new and confirm passwords are different'; $zmSlangPaths = 'Paths'; -$zmSlangPlayAll = 'Play All'; -$zmSlangPhone = 'Phone'; $zmSlangPhoneBW = 'Phone B/W'; +$zmSlangPhone = 'Phone'; $zmSlangPixels = 'pixels'; +$zmSlangPlayAll = 'Play All'; $zmSlangPleaseWait = 'Please Wait'; $zmSlangPostEventImageBuffer = 'Post Event Image Buffer'; $zmSlangPreEventImageBuffer = 'Pre Event Image Buffer'; +$zmSlangPreset = 'Preset'; +$zmSlangPresets = 'Presets'; $zmSlangPrev = 'Prev'; $zmSlangRate = 'Rate'; $zmSlangReal = 'Real'; @@ -293,11 +412,13 @@ $zmSlangRemoteImageColours = 'Remote Image Colours'; $zmSlangRemote = 'Remote'; $zmSlangRename = 'Rename'; $zmSlangReplay = 'Replay'; -$zmSlangReset = 'Reset'; $zmSlangResetEventCounts = 'Reset Event Counts'; +$zmSlangReset = 'Reset'; $zmSlangRestarting = 'Restarting'; $zmSlangRestart = 'Restart'; $zmSlangRestrictedCameraIds = 'Restricted Camera Ids'; +$zmSlangReturnDelay = 'Return Delay'; +$zmSlangReturnLocation = 'Return Location'; $zmSlangRotateLeft = 'Rotate Left'; $zmSlangRotateRight = 'Rotate Right'; $zmSlangRunMode = 'Run Mode'; @@ -309,27 +430,41 @@ $zmSlangSave = 'Save'; $zmSlangScale = 'Scale'; $zmSlangScore = 'Score'; $zmSlangSecs = 'Secs'; -$zmSlangSelect = 'Select'; $zmSlangSectionlength = 'Section length'; +$zmSlangSelect = 'Select'; $zmSlangSetLearnPrefs = 'Set Learn Prefs'; // This can be ignored for now $zmSlangSetNewBandwidth = 'Set New Bandwidth'; +$zmSlangSetPreset = 'Set Preset'; +$zmSlangSet = 'Set'; $zmSlangSettings = 'Settings'; $zmSlangShowFilterWindow = 'ShowFilterWindow'; +$zmSlangSleep = 'Sleep'; $zmSlangSortAsc = 'Asc'; $zmSlangSortBy = 'Sort by'; $zmSlangSortDesc = 'Desc'; $zmSlangSource = 'Source'; $zmSlangSourceType = 'Source Type'; +$zmSlangSpeedHigh = 'High Speed'; +$zmSlangSpeedLow = 'Low Speed'; +$zmSlangSpeedMedium = 'Medium Speed'; +$zmSlangSpeed = 'Speed'; +$zmSlangSpeedTurbo = 'Turbo Speed'; $zmSlangStart = 'Start'; $zmSlangState = 'State'; $zmSlangStats = 'Stats'; $zmSlangStatus = 'Status'; +$zmSlangStepLarge = 'Large Step'; +$zmSlangStepMedium = 'Medium Step'; +$zmSlangStepNone = 'No Step'; +$zmSlangStepSmall = 'Small Step'; +$zmSlangStep = 'Step'; $zmSlangStills = 'Stills'; $zmSlangStopped = 'Stopped'; $zmSlangStop = 'Stop'; $zmSlangStream = 'Stream'; $zmSlangSubmit = 'Submit'; $zmSlangSystem = 'System'; +$zmSlangTele = 'Tele'; $zmSlangThumbnail = 'Thumbnail'; $zmSlangTimeDelta = 'Time Delta'; $zmSlangTimestampLabelFormat = 'Timestamp Label Format'; @@ -338,10 +473,15 @@ $zmSlangTimestampLabelY = 'Timestamp Label Y'; $zmSlangTimestamp = 'Timestamp'; $zmSlangTimeStamp = 'Time Stamp'; $zmSlangTime = 'Time'; +$zmSlangTilt = 'Tilt'; $zmSlangToday = 'Today'; $zmSlangTools = 'Tools'; $zmSlangTotalBrScore = 'Total
Score'; +$zmSlangTrackDelay = 'Track Delay'; +$zmSlangTrackMotion = 'Track Motion'; $zmSlangTriggers = 'Triggers'; +$zmSlangTurboPanSpeed = 'Turbo Pan Speed'; +$zmSlangTurboTiltSpeed = 'Turbo Tilt Speed'; $zmSlangType = 'Type'; $zmSlangUnarchive = 'Unarchive'; $zmSlangUnits = 'Units'; @@ -368,10 +508,14 @@ $zmSlangVideo = 'Video'; $zmSlangViewAll = 'View All'; $zmSlangViewPaged = 'View Paged'; $zmSlangView = 'View'; +$zmSlangWake = 'Wake'; $zmSlangWarmupFrames = 'Warmup Frames'; $zmSlangWatch = 'Watch'; $zmSlangWeb = 'Web'; $zmSlangWeek = 'Week'; +$zmSlangWhiteBalance = 'White Balance'; +$zmSlangWhite = 'White'; +$zmSlangWide = 'Wide'; $zmSlangX10ActivationString = 'X10 Activation String'; $zmSlangX10InputAlarmString = 'X10 Input Alarm String'; $zmSlangX10OutputAlarmString = 'X10 Output Alarm String'; @@ -397,6 +541,7 @@ $zmSlangZoneMinX = 'Minimum X (left)'; $zmSlangZoneMinY = 'Minimum Y (top)'; $zmSlangZones = 'Zones'; $zmSlangZone = 'Zone'; +$zmSlangZoom = 'Zoom'; // Complex replacements with formatting and/or placements, must be passed through sprintf $zmClangCurrentLogin = 'Current login is \'%1$s\''; diff --git a/zmconfig.pl.in b/zmconfig.pl.in index e8b3725e9..2e945fca3 100755 --- a/zmconfig.pl.in +++ b/zmconfig.pl.in @@ -938,6 +938,14 @@ my @options = type => $types{integer}, category => 'system', }, + { + name => "ZM_OPT_CONTROL", + default => "no", + description => "Whether to support controllable (e.g. pan/tilt/zoom) cameras", + help => "ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.", + type => $types{boolean}, + category => 'system', + }, { name => "ZM_CHECK_FOR_UPDATES", default => "yes", @@ -1682,7 +1690,7 @@ if ( $reprocess ) printf( CFG_HDR_FILE "#define ZM_MAX_CFG_ID $last_id\n" ); close( CFG_HDR_FILE ); - my @config_files = qw( zm.conf src/zm_config.h web/zm_config.php scripts/zmdc.pl scripts/zmwatch.pl scripts/zmaudit.pl scripts/zmfilter.pl scripts/zmtrigger.pl scripts/zmx10.pl scripts/zmpkg.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zm db/zmschema.sql ); + my @config_files = qw( zm.conf src/zm_config.h web/zm_config.php scripts/zmdc.pl scripts/zmwatch.pl scripts/zmaudit.pl scripts/zmfilter.pl scripts/zmtrigger.pl scripts/zmx10.pl scripts/zmpkg.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmcontrol-pelco-d.pl scripts/zmcontrol-visca.pl scripts/zmcontrol-kx-hcm10.pl scripts/zmtrack.pl scripts/zm db/zmschema.sql ); foreach my $config_file ( @config_files ) {
    
  :