Merge branch 'master' into zma_to_thread
This commit is contained in:
commit
b261fbb397
|
@ -150,6 +150,9 @@ set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
|||
"Location of the web server cache busting files, default: /var/cache/zoneminder")
|
||||
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
|
||||
"Location of dynamic content (events and images), default: /var/lib/zoneminder")
|
||||
set(ZM_FONTDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/fonts" CACHE PATH
|
||||
"Location of the font files used for timestamping, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/fonts")
|
||||
|
||||
set(ZM_DB_HOST "localhost" CACHE STRING
|
||||
"Hostname where ZoneMinder database located, default: localhost")
|
||||
set(ZM_DB_NAME "zm" CACHE STRING
|
||||
|
@ -913,6 +916,7 @@ set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
|
|||
add_subdirectory(src)
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(db)
|
||||
add_subdirectory(fonts)
|
||||
add_subdirectory(web)
|
||||
add_subdirectory(misc)
|
||||
add_subdirectory(onvif)
|
||||
|
|
|
@ -337,6 +337,7 @@ CREATE TABLE `Groups` (
|
|||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`ParentId` int(10) unsigned,
|
||||
FOREIGN KEY (`ParentId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE,
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
|
@ -348,7 +349,9 @@ DROP TABLE IF EXISTS `Groups_Monitors`;
|
|||
CREATE TABLE `Groups_Monitors` (
|
||||
`Id` INT(10) unsigned NOT NULL auto_increment,
|
||||
`GroupId` int(10) unsigned NOT NULL,
|
||||
FOREIGN KEY (`GroupId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE,
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ SET @s = (SELECT IF(
|
|||
AND column_name = 'ParentId'
|
||||
) > 0,
|
||||
"SELECT 'Column GroupId exists in Groups'",
|
||||
"ALTER TABLE Groups ADD `ParentId` int(10) unsigned AFTER `Name`"
|
||||
"ALTER TABLE `Groups` ADD `ParentId` int(10) unsigned AFTER `Name`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
|
|
|
@ -3,7 +3,7 @@ SET @s = (SELECT IF(
|
|||
AND table_name = 'Groups'
|
||||
AND column_name = 'MonitorIds'
|
||||
) > 0,
|
||||
"ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL",
|
||||
"ALTER TABLE `Groups` MODIFY `MonitorIds` text NOT NULL",
|
||||
"SELECT 'Groups no longer has MonitorIds'"
|
||||
));
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Add the EndTime IS NOT NULL term to the Update Disk Space Filter.
|
||||
This will only work if they havn't modified the stock filter
|
||||
*/
|
||||
UPDATE Filters SET Query_json='{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}]}' WHERE Query_json='{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}';
|
||||
|
||||
/*
|
||||
Add the EndTime IS NOT NULL term to the Purge When Full Filter.
|
||||
This will only work if they havn't modified the stock filter .
|
||||
This is important to prevent SQL Errors inserting into Frames table if PurgeWhenFull deletes in-progress events.
|
||||
*/
|
||||
UPDATE Filters SET Query_json='{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}],"limit":100,"sort_asc":1}' WHERE Query_json='{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}';
|
||||
|
||||
/* Add FOREIGN KEYS After deleting lost records */
|
||||
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups_Monitors' and column_name='GroupId' and referenced_table_name='Groups' and referenced_column_name='Id');
|
||||
|
||||
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
|
||||
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY GroupId in Groups_Monitors already exists'", @sqlstmt);
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for GroupId to Groups_Monitors'", @sqlstmt);
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups_Monitors'", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "DELETE FROM `Groups_Monitors` WHERE `GroupId` NOT IN (SELECT `Id` FROM `Groups`)", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups_Monitors` ADD FOREIGN KEY (`GroupId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
||||
/* Add FOREIGN KEYS After deleting lost records */
|
||||
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups_Monitors' and column_name='MonitorId' and referenced_table_name='Monitors' and referenced_column_name='Id');
|
||||
|
||||
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
|
||||
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY MonitorId in Groups_Monitors already exists'", @sqlstmt);
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for MonitorId to Groups_Monitors'", @sqlstmt);
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups_Monitors'", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "DELETE FROM `Groups_Monitors` WHERE `MonitorId` NOT IN (SELECT `Id` FROM `Monitors`)", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups_Monitors` ADD FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
||||
/* Add FOREIGN KEYS After deleting lost records */
|
||||
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups' and column_name='ParentId' and referenced_table_name='Groups' and referenced_column_name='Id');
|
||||
|
||||
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
|
||||
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY ParentId in Groups already exists'", @sqlstmt);
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for ParentId to Groups'", @sqlstmt);
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
||||
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups'", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "UPDATE `Groups` SET `ParentId` = NULL WHERE (ParentId IS NOT NULL) and ParentId NOT IN (SELECT * FROM(SELECT Id FROM `Groups`)tblTmp)", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups` ADD FOREIGN KEY (ParentId) REFERENCES `Groups` (Id) ON DELETE CASCADE", "SELECT '.'");
|
||||
PREPARE stmt FROM @sqlstmt;
|
||||
EXECUTE stmt;
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.35.14
|
||||
Version: 1.35.15
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
Group: System Environment/Daemons
|
||||
|
@ -44,7 +44,7 @@ License: GPLv2+ and LGPLv2+ and MIT
|
|||
URL: http://www.zoneminder.com/
|
||||
|
||||
Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz
|
||||
Source1: https://github.com/FriendOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
|
||||
Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
|
||||
Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz
|
||||
|
||||
%{?rhel:BuildRequires: epel-rpm-macros}
|
||||
|
|
|
@ -5,5 +5,6 @@ var/cache/zoneminder/images
|
|||
var/cache/zoneminder/temp
|
||||
var/cache/zoneminder/cache
|
||||
usr/share/zoneminder/db
|
||||
usr/share/zoneminder/fonts
|
||||
etc/zm/
|
||||
etc/zm/conf.d
|
||||
|
|
|
@ -5,6 +5,7 @@ usr/lib/zoneminder
|
|||
usr/share/polkit-1
|
||||
usr/share/zoneminder/db
|
||||
usr/share/zoneminder/www
|
||||
usr/share/zoneminder/fonts
|
||||
|
||||
# libzoneminder-perl files:
|
||||
usr/share/man/man3
|
||||
|
|
|
@ -5,5 +5,6 @@ var/cache/zoneminder/images
|
|||
var/cache/zoneminder/temp
|
||||
var/cache/zoneminder/cache
|
||||
usr/share/zoneminder/db
|
||||
usr/share/zoneminder/fonts
|
||||
etc/zm/
|
||||
etc/zm/conf.d
|
||||
|
|
|
@ -5,6 +5,7 @@ usr/lib/zoneminder
|
|||
usr/share/polkit-1
|
||||
usr/share/zoneminder/db
|
||||
usr/share/zoneminder/www
|
||||
usr/share/zoneminder/fonts
|
||||
|
||||
# libzoneminder-perl files:
|
||||
usr/share/man/man3
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Glob all database upgrade scripts
|
||||
file(GLOB fontfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.zmfnt")
|
||||
# Install the fonts
|
||||
install(FILES ${fontfileslist} DESTINATION "${ZM_FONTDIR}")
|
||||
|
Binary file not shown.
|
@ -123,14 +123,19 @@ sub get_service_urls {
|
|||
);
|
||||
if ( $result ) {
|
||||
print "Have results from GetServices\n" if $verbose;
|
||||
foreach my $svc ( @{ $result->get_Service() } ) {
|
||||
my $services = $result->get_Service();
|
||||
if ( $services ) {
|
||||
foreach my $svc ( @{ $services } ) {
|
||||
my $short_name = $namespace_map{$svc->get_Namespace()};
|
||||
my $url_svc = $svc->get_XAddr()->get_value();
|
||||
if ( defined $short_name && defined $url_svc ) {
|
||||
print "Got $short_name service $url_svc\n" if $verbose;
|
||||
$self->set_service($short_name, 'url', $url_svc);
|
||||
}
|
||||
}
|
||||
} # end foreach service
|
||||
} else {
|
||||
print "No services from GetServices\n" if $verbose;
|
||||
} # end if services
|
||||
} else {
|
||||
print "No results from GetServices\n" if $verbose;
|
||||
}
|
||||
|
|
|
@ -2748,6 +2748,19 @@ our @options = (
|
|||
requires => [ { name => 'ZM_WEB_LIST_THUMBS', value => 'yes' } ],
|
||||
category => 'web',
|
||||
},
|
||||
{
|
||||
name => 'ZM_WEB_ANIMATE_THUMBS',
|
||||
default => 'yes',
|
||||
description => 'Enlarge and show the live stream when a thumbnail is hovered over',
|
||||
help => q`
|
||||
Enabling this option causes the static thumbnail, shown on certain
|
||||
views, to enlarge and show the live stream, when the thumbnail is
|
||||
hovered over by the mouse.
|
||||
`,
|
||||
type => $types{boolean},
|
||||
requires => [ { name => 'ZM_WEB_LIST_THUMBS', value => 'yes' } ],
|
||||
category => 'web',
|
||||
},
|
||||
{
|
||||
name => 'ZM_WEB_USE_OBJECT_TAGS',
|
||||
default => 'yes',
|
||||
|
@ -3767,6 +3780,14 @@ our @options = (
|
|||
type => $types{boolean},
|
||||
category => 'logging',
|
||||
},
|
||||
{
|
||||
name => 'ZM_FONT_FILE_LOCATION',
|
||||
default => '@ZM_FONTDIR@/default.zmfnt',
|
||||
description => 'Font file location',
|
||||
help => 'This font is used for timestamp labels.',
|
||||
type => $types{string},
|
||||
category => 'config',
|
||||
},
|
||||
);
|
||||
|
||||
our %options_hash = map { ( $_->{name}, $_ ) } @options;
|
||||
|
|
|
@ -41,7 +41,7 @@ require Number::Bytes::Human;
|
|||
require Date::Parse;
|
||||
require POSIX;
|
||||
use Date::Format qw(time2str);
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
use Time::HiRes qw(gettimeofday tv_interval stat);
|
||||
|
||||
#our @ISA = qw(ZoneMinder::Object);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -791,44 +791,50 @@ sub recover_timestamps {
|
|||
return;
|
||||
}
|
||||
my @contents = readdir(DIR);
|
||||
Debug('Have ' . @contents . " files in $path");
|
||||
Debug('Have ' . @contents . ' files in '.$path);
|
||||
closedir(DIR);
|
||||
|
||||
my @mp4_files = grep( /^\d+\-video\.mp4$/, @contents);
|
||||
my @mp4_files = grep(/^\d+\-video\.mp4$/, @contents);
|
||||
if ( @mp4_files ) {
|
||||
$$Event{DefaultVideo} = $mp4_files[0];
|
||||
}
|
||||
|
||||
my @analyse_jpgs = grep( /^\d+\-analyse\.jpg$/, @contents);
|
||||
my @analyse_jpgs = grep(/^\d+\-analyse\.jpg$/, @contents);
|
||||
if ( @analyse_jpgs ) {
|
||||
$$Event{Save_JPEGs} |= 2;
|
||||
$$Event{SaveJPEGs} |= 2;
|
||||
}
|
||||
|
||||
my @capture_jpgs = grep( /^\d+\-capture\.jpg$/, @contents);
|
||||
my @capture_jpgs = grep(/^\d+\-capture\.jpg$/, @contents);
|
||||
if ( @capture_jpgs ) {
|
||||
$$Event{Frames} = scalar @capture_jpgs;
|
||||
$$Event{Save_JPEGs} |= 1;
|
||||
$$Event{SaveJPEGs} |= 1;
|
||||
# can get start and end times from stat'ing first and last jpg
|
||||
@capture_jpgs = sort { $a cmp $b } @capture_jpgs;
|
||||
my $first_file = "$path/$capture_jpgs[0]";
|
||||
( $first_file ) = $first_file =~ /^(.*)$/;
|
||||
my $first_timestamp = (stat($first_file))[9];
|
||||
|
||||
my $last_file = "$path/$capture_jpgs[@capture_jpgs-1]";
|
||||
my $last_file = $path.'/'.$capture_jpgs[@capture_jpgs-1];
|
||||
( $last_file ) = $last_file =~ /^(.*)$/;
|
||||
my $last_timestamp = (stat($last_file))[9];
|
||||
|
||||
my $duration = $last_timestamp - $first_timestamp;
|
||||
$Event->Length($duration);
|
||||
$Event->StartDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) );
|
||||
if ( $Event->Scheme() eq 'Deep' and $Event->RelativePath(undef) and ($path ne $Event->Path(undef)) ) {
|
||||
my ( $year, $month, $day, $hour, $minute, $second ) =
|
||||
($path =~ /(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})$/);
|
||||
Error("Updating starttime to $path $year/$month/$day $hour:$minute:$second");
|
||||
$Event->StartDateTime(sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', 2000+$year, $month, $day, $hour, $minute, $second));
|
||||
}
|
||||
$Event->EndDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) );
|
||||
Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartDateTime} to $$Event{EndDateTime}");
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
foreach my $jpg ( @capture_jpgs ) {
|
||||
my ( $id ) = $jpg =~ /^(\d+)\-capture\.jpg$/;
|
||||
|
||||
if ( ! ZoneMinder::Frame->find_one( EventId=>$$Event{Id}, FrameId=>$id ) ) {
|
||||
my $file = "$path/$jpg";
|
||||
if ( ! ZoneMinder::Frame->find_one(EventId=>$$Event{Id}, FrameId=>$id) ) {
|
||||
my $file = $path.'/'.$jpg;
|
||||
( $file ) = $file =~ /^(.*)$/;
|
||||
my $timestamp = (stat($file))[9];
|
||||
my $Frame = new ZoneMinder::Frame();
|
||||
|
@ -839,11 +845,11 @@ sub recover_timestamps {
|
|||
Type=>'Normal',
|
||||
Score=>0,
|
||||
});
|
||||
}
|
||||
}
|
||||
} # end if Frame not found
|
||||
} # end foreach capture jpg
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
} elsif ( @mp4_files ) {
|
||||
my $file = "$path/$mp4_files[0]";
|
||||
my $file = $path.'/'.$mp4_files[0];
|
||||
( $file ) = $file =~ /^(.*)$/;
|
||||
|
||||
my $first_timestamp = (stat($file))[9];
|
||||
|
|
|
@ -92,11 +92,13 @@ sub Execute {
|
|||
}
|
||||
|
||||
if ( $self->{HasDiskPercent} ) {
|
||||
my $disk_percent = getDiskPercent($$self{Storage} ? $$self{Storage}->Path() : ());
|
||||
$$self{Storage} = ZoneMinder::Storage->find_one() if ! $$self{Storage};
|
||||
my $disk_percent = getDiskPercent($$self{Storage} ? $$self{Storage}->Path() : $Config{ZM_DIR_EVENTS});
|
||||
$sql =~ s/zmDiskPercent/$disk_percent/g;
|
||||
}
|
||||
if ( $self->{HasDiskBlocks} ) {
|
||||
my $disk_blocks = getDiskBlocks();
|
||||
$$self{Storage} = ZoneMinder::Storage->find_one() if ! $$self{Storage};
|
||||
my $disk_blocks = getDiskBlocks($$self{Storage} ? $$self{Storage}->Path() : $Config{ZM_DIR_EVENTS});
|
||||
$sql =~ s/zmDiskBlocks/$disk_blocks/g;
|
||||
}
|
||||
if ( $self->{HasSystemLoad} ) {
|
||||
|
@ -148,15 +150,8 @@ sub Sql {
|
|||
}
|
||||
|
||||
my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query_json});
|
||||
my $sql = 'SELECT E.*,
|
||||
unix_timestamp(E.StartDateTime) as Time,
|
||||
M.Name as MonitorName,
|
||||
M.DefaultRate,
|
||||
M.DefaultScale
|
||||
FROM Events as E
|
||||
INNER JOIN Monitors as M on M.Id = E.MonitorId
|
||||
LEFT JOIN Storage as S on S.Id = E.StorageId
|
||||
';
|
||||
my $sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time
|
||||
FROM Events as E';
|
||||
|
||||
if ( $filter_expr->{terms} ) {
|
||||
foreach my $term ( @{$filter_expr->{terms}} ) {
|
||||
|
@ -174,12 +169,16 @@ sub Sql {
|
|||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= 'S.ServerId';
|
||||
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
|
@ -308,7 +307,7 @@ sub Sql {
|
|||
} elsif ( $value eq 'Even' ) {
|
||||
$self->{Sql} .= ' % 2 = 0';
|
||||
} else {
|
||||
$self->{Sql} .= " IS $value";
|
||||
$self->{Sql} .= ' IS '.$value;
|
||||
}
|
||||
} elsif ( $term->{op} eq 'EXISTS' ) {
|
||||
$self->{Sql} .= ' EXISTS '.$value;
|
||||
|
@ -373,6 +372,8 @@ sub Sql {
|
|||
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
||||
$sort_column = 'E.Id';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$sort_column = 'M.Name';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'Name' ) {
|
||||
$sort_column = 'E.Name';
|
||||
|
@ -422,7 +423,7 @@ sub getDiskPercent {
|
|||
}
|
||||
|
||||
sub getDiskBlocks {
|
||||
my $command = 'df .';
|
||||
my $command = 'df ' . ($_[0] ? $_[0] : '.');
|
||||
my $df = qx( $command );
|
||||
my $space = -1;
|
||||
if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) {
|
||||
|
|
|
@ -32,6 +32,7 @@ require ZoneMinder::Base;
|
|||
require ZoneMinder::Object;
|
||||
require ZoneMinder::Storage;
|
||||
require ZoneMinder::Server;
|
||||
require ZoneMinder::Monitor_Status;
|
||||
|
||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -245,6 +246,14 @@ sub control {
|
|||
}
|
||||
} # end sub control
|
||||
|
||||
sub Status {
|
||||
my $self = shift;
|
||||
$$self{Status} = shift if @_;
|
||||
if ( ! $$self{Status} ) {
|
||||
$$self{Status} = ZoneMinder::Monitor_Status->find_one(MonitorId=>$$self{Id});
|
||||
}
|
||||
return $$self{Status};
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
|
@ -39,6 +39,10 @@ $table = 'Monitor_Status';
|
|||
$serial = $primary_key = 'MonitorId';
|
||||
%fields = map { $_ => $_ } qw(
|
||||
MonitorId
|
||||
Status
|
||||
CaptureFPS
|
||||
AnalysisFPS
|
||||
CaptureBandwidth
|
||||
TotalEvents
|
||||
TotalEventDiskSpace
|
||||
HourEvents
|
||||
|
@ -54,6 +58,10 @@ $serial = $primary_key = 'MonitorId';
|
|||
);
|
||||
|
||||
%defaults = (
|
||||
Status => 'Unknown',
|
||||
CaptureFPS => undef,
|
||||
AnalysisFPS => undef,
|
||||
CaptureBandwidth => undef,
|
||||
TotalEvents => undef,
|
||||
TotalEventDiskSpace => undef,
|
||||
HourEvents => undef,
|
||||
|
|
|
@ -27,7 +27,7 @@ use strict;
|
|||
use ZoneMinder;
|
||||
use Getopt::Long;
|
||||
use autouse 'Pod::Usage'=>qw(pod2usage);
|
||||
use POSIX qw/strftime EPIPE/;
|
||||
use POSIX qw/strftime EPIPE EINTR/;
|
||||
use Socket;
|
||||
use Data::Dumper;
|
||||
use Module::Load::Conditional qw{can_load};
|
||||
|
@ -162,24 +162,24 @@ if ( $options{command} ) {
|
|||
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
|
||||
|
||||
my $rin = '';
|
||||
vec( $rin, fileno(SERVER), 1 ) = 1;
|
||||
vec($rin, fileno(SERVER), 1) = 1;
|
||||
my $win = $rin;
|
||||
my $ein = $win;
|
||||
my $timeout = MAX_COMMAND_WAIT;
|
||||
while( 1 ) {
|
||||
while ( 1 ) {
|
||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||
if ( $nfound > 0 ) {
|
||||
if ( vec( $rout, fileno(SERVER), 1 ) ) {
|
||||
if ( vec($rout, fileno(SERVER), 1) ) {
|
||||
my $paddr = accept(CLIENT, SERVER);
|
||||
my $message = <CLIENT>;
|
||||
close(CLIENT);
|
||||
|
||||
next if !$message;
|
||||
|
||||
my $params = jsonDecode($message);
|
||||
Debug( Dumper( $params ) );
|
||||
Debug(Dumper($params));
|
||||
|
||||
my $command = $params->{command};
|
||||
close(CLIENT);
|
||||
if ( $command eq 'quit' ) {
|
||||
last;
|
||||
} elsif ( $command ) {
|
||||
|
@ -191,23 +191,23 @@ if ( $options{command} ) {
|
|||
Fatal('Bogus descriptor');
|
||||
}
|
||||
} elsif ( $nfound < 0 ) {
|
||||
if ( $! == EPIPE ) {
|
||||
if ( $! == EINTR ) {
|
||||
# Likely just SIGHUP
|
||||
Debug("Can't select: $!");
|
||||
} elsif ( $! == EPIPE ) {
|
||||
Error("Can't select: $!");
|
||||
} else {
|
||||
Fatal("Can't select: $!");
|
||||
}
|
||||
} else {
|
||||
#print( "Select timed out\n" );
|
||||
last;
|
||||
Debug('Select timed out');
|
||||
}
|
||||
} # end while forever
|
||||
Info("Control server $id/$protocol exiting");
|
||||
unlink($sock_file);
|
||||
$control->close();
|
||||
exit(0);
|
||||
} # end if !server up
|
||||
|
||||
|
||||
exit(0);
|
||||
|
||||
1;
|
||||
|
|
|
@ -381,8 +381,10 @@ sub checkFilter {
|
|||
} # end if AutoCopy
|
||||
|
||||
if ( $filter->{UpdateDiskSpace} ) {
|
||||
if ( $$filter{LockRows} ) {
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
$Event->lock_and_load();
|
||||
}
|
||||
|
||||
my $old_diskspace = $$Event{DiskSpace};
|
||||
my $new_diskspace = $Event->DiskSpace(undef);
|
||||
|
@ -394,7 +396,7 @@ sub checkFilter {
|
|||
) {
|
||||
$Event->save();
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
$ZoneMinder::Database::dbh->commit() if !$$filter{LockRows};
|
||||
} # end if UpdateDiskSpace
|
||||
} # end foreach event
|
||||
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};
|
||||
|
@ -405,8 +407,9 @@ sub generateVideo {
|
|||
my $Event = shift;
|
||||
my $phone = shift;
|
||||
|
||||
my $rate = $Event->{DefaultRate}/100;
|
||||
my $scale = $Event->{DefaultScale}/100;
|
||||
my $Monitor = $Event->Monitor();
|
||||
my $rate = $$Monitor{DefaultRate}/100;
|
||||
my $scale = $$Monitor{DefaultScale}/100;
|
||||
my $format;
|
||||
|
||||
my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS});
|
||||
|
@ -655,9 +658,11 @@ sub substituteTags {
|
|||
# First we'd better check what we need to get
|
||||
# We have a filter and an event, do we need any more
|
||||
# monitor information?
|
||||
my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_status = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
|
||||
my $Monitor = $Event->Monitor() if $need_monitor;
|
||||
my $Status = $Monitor->Status() if $need_status;
|
||||
|
||||
# Do we need the image information too?
|
||||
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD)%/;
|
||||
|
@ -687,13 +692,13 @@ sub substituteTags {
|
|||
|
||||
my $url = $Config{ZM_URL};
|
||||
$text =~ s/%ZP%/$url/g;
|
||||
$text =~ s/%MN%/$Event->{MonitorName}/g;
|
||||
$text =~ s/%MET%/$Monitor->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Monitor->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Monitor->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Monitor->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Monitor->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Monitor->{ArchivedEvents}/g;
|
||||
$text =~ s/%MN%/$Monitor->{Name}/g;
|
||||
$text =~ s/%MET%/$Status->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Status->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Status->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Status->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Status->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Status->{ArchivedEvents}/g;
|
||||
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
|
||||
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
|
||||
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
|
||||
|
|
|
@ -350,7 +350,7 @@ sub loadMonitors {
|
|||
$new_monitors{$monitor->{Id}} = $monitor;
|
||||
} # end while fetchrow
|
||||
%monitors = %new_monitors;
|
||||
}
|
||||
} # end sub loadMonitors
|
||||
|
||||
sub handleMessage {
|
||||
my $connection = shift;
|
||||
|
@ -368,10 +368,14 @@ sub handleMessage {
|
|||
$text = '' if !defined($text);
|
||||
|
||||
my $monitor = $monitors{$id};
|
||||
if ( !$monitor ) {
|
||||
loadMonitors();
|
||||
$monitor = $monitors{$id};
|
||||
if ( !$monitor ) {
|
||||
Warning("Can't find monitor '$id' for message '$message'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( !zmMemVerify($monitor) ) {
|
||||
Warning("Can't verify monitor '$id' for message '$message'");
|
||||
return;
|
||||
|
|
|
@ -21,7 +21,8 @@ set(ZM_BIN_SRC_FILES
|
|||
zm_eventstream.cpp
|
||||
zm_exception.cpp
|
||||
zm_fifo.cpp
|
||||
zm_file_camera.cpp zm_ffmpeg_camera.cpp
|
||||
zm_file_camera.cpp
|
||||
zm_font.cpp
|
||||
zm_frame.cpp
|
||||
zm_group.cpp
|
||||
zm_image.cpp
|
||||
|
@ -32,6 +33,7 @@ set(ZM_BIN_SRC_FILES
|
|||
zm_monitor.cpp
|
||||
zm_monitorstream.cpp
|
||||
zm_ffmpeg.cpp
|
||||
zm_ffmpeg_camera.cpp
|
||||
zm_ffmpeg_input.cpp
|
||||
zm_mpeg.cpp
|
||||
zm_packet.cpp
|
||||
|
|
6155
src/zm_bigfont.h
6155
src/zm_bigfont.h
File diff suppressed because it is too large
Load Diff
|
@ -17,62 +17,51 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_buffer.h"
|
||||
|
||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
unsigned int Buffer::assign(const unsigned char *pStorage, unsigned int pSize) {
|
||||
if ( mAllocation < pSize ) {
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
memcpy(mStorage, pStorage, mSize);
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
return mSize;
|
||||
}
|
||||
|
||||
unsigned int Buffer::expand( unsigned int count )
|
||||
{
|
||||
unsigned int Buffer::expand(unsigned int count) {
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
memmove( mStorage, mHead, mSize );
|
||||
if ( spare > static_cast<int>(count) ) {
|
||||
if ( tailSpace < static_cast<int>(count) ) {
|
||||
memmove(mStorage, mHead, mSize);
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
if ( mStorage ) {
|
||||
memcpy(newStorage, mHead, mSize);
|
||||
delete[] mStorage;
|
||||
}
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
return( mSize );
|
||||
return mSize;
|
||||
}
|
||||
|
||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||
int Buffer::read_into(int sd, unsigned int bytes) {
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
int bytes_read = read(sd, mTail, bytes);
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
|
|
740
src/zm_comms.cpp
740
src/zm_comms.cpp
File diff suppressed because it is too large
Load Diff
|
@ -325,10 +325,7 @@ const char *ConfigItem::StringValue() const {
|
|||
return cfg_value.string_value;
|
||||
}
|
||||
|
||||
Config::Config() {
|
||||
n_items = 0;
|
||||
items = 0;
|
||||
}
|
||||
Config::Config() : n_items(0), items(nullptr) { }
|
||||
|
||||
Config::~Config() {
|
||||
if ( items ) {
|
||||
|
@ -342,10 +339,7 @@ Config::~Config() {
|
|||
}
|
||||
|
||||
void Config::Load() {
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
|
||||
strncpy(sql, "SELECT `Name`, `Value`, `Type` FROM `Config` ORDER BY `Id`", sizeof(sql) );
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
if ( mysql_query(&dbconn, "SELECT `Name`, `Value`, `Type` FROM `Config` ORDER BY `Id`") ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
}
|
||||
|
@ -363,10 +357,11 @@ void Config::Load() {
|
|||
}
|
||||
|
||||
items = new ConfigItem *[n_items];
|
||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
items[i] = new ConfigItem(dbrow[0], dbrow[1], dbrow[2]);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
void Config::Assign() {
|
||||
|
|
|
@ -40,38 +40,59 @@ bool zmDbConnect() {
|
|||
Error("Can't initialise database connection: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool reconnect = 1;
|
||||
if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) )
|
||||
Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn));
|
||||
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||
|
||||
if ( !staticConfig.DB_SSL_CA_CERT.empty() ) {
|
||||
mysql_ssl_set(&dbconn,
|
||||
staticConfig.DB_SSL_CLIENT_KEY.c_str(),
|
||||
staticConfig.DB_SSL_CLIENT_CERT.c_str(),
|
||||
staticConfig.DB_SSL_CA_CERT.c_str(),
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find(":");
|
||||
if ( colonIndex == std::string::npos ) {
|
||||
if ( !mysql_real_connect(&dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, 0, nullptr, 0) ) {
|
||||
Error( "Can't connect to server: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||
std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||
if ( dbPortOrSocket[0] == '/' ) {
|
||||
if ( !mysql_real_connect(&dbconn, nullptr, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, 0, dbPortOrSocket.c_str(), 0) ) {
|
||||
if ( !mysql_real_connect(
|
||||
&dbconn,
|
||||
staticConfig.DB_HOST.c_str(),
|
||||
staticConfig.DB_USER.c_str(),
|
||||
staticConfig.DB_PASS.c_str(),
|
||||
nullptr, 0, nullptr, 0) ) {
|
||||
Error("Can't connect to server: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, atoi(dbPortOrSocket.c_str()), nullptr, 0 ) ) {
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
std::string dbHost = staticConfig.DB_HOST.substr(0, colonIndex);
|
||||
std::string dbPortOrSocket = staticConfig.DB_HOST.substr(colonIndex+1);
|
||||
if ( dbPortOrSocket[0] == '/' ) {
|
||||
if ( !mysql_real_connect(
|
||||
&dbconn,
|
||||
nullptr,
|
||||
staticConfig.DB_USER.c_str(),
|
||||
staticConfig.DB_PASS.c_str(),
|
||||
nullptr, 0, dbPortOrSocket.c_str(), 0) ) {
|
||||
Error("Can't connect to server: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( !mysql_real_connect(
|
||||
&dbconn,
|
||||
dbHost.c_str(),
|
||||
staticConfig.DB_USER.c_str(),
|
||||
staticConfig.DB_PASS.c_str(),
|
||||
nullptr,
|
||||
atoi(dbPortOrSocket.c_str()),
|
||||
nullptr, 0) ) {
|
||||
Error("Can't connect to server: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) {
|
||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||
if ( mysql_select_db(&dbconn, staticConfig.DB_NAME.c_str()) ) {
|
||||
Error("Can't select database: %s", mysql_error(&dbconn));
|
||||
return false;
|
||||
}
|
||||
zmDbConnected = true;
|
||||
|
@ -140,7 +161,7 @@ MYSQL_RES *zmDbRow::fetch(const char *query) {
|
|||
}
|
||||
|
||||
row = mysql_fetch_row(result_set);
|
||||
if ( ! row ) {
|
||||
if ( !row ) {
|
||||
mysql_free_result(result_set);
|
||||
result_set = nullptr;
|
||||
Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn));
|
||||
|
@ -155,4 +176,5 @@ zmDbRow::~zmDbRow() {
|
|||
mysql_free_result(result_set);
|
||||
result_set = nullptr;
|
||||
}
|
||||
row = nullptr;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ class zmDbRow {
|
|||
MYSQL_ROW row;
|
||||
public:
|
||||
zmDbRow() : result_set(nullptr), row(nullptr) { };
|
||||
MYSQL_RES *fetch( const char *query );
|
||||
zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
|
||||
MYSQL_RES *fetch(const char *query);
|
||||
zmDbRow(MYSQL_RES *, MYSQL_ROW *row);
|
||||
~zmDbRow();
|
||||
|
||||
MYSQL_ROW mysql_row() const { return row; };
|
||||
|
|
223
src/zm_event.cpp
223
src/zm_event.cpp
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ZoneMinder Event Class Implementation, $Date$, $Revision$
|
||||
// ZoneMinder Event Class Implementation
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
|
@ -52,6 +52,7 @@ Event::Event(
|
|||
const std::string &p_cause,
|
||||
const StringSetMap &p_noteSetMap
|
||||
) :
|
||||
id(0),
|
||||
monitor(p_monitor),
|
||||
start_time(p_start_time),
|
||||
cause(p_cause),
|
||||
|
@ -76,9 +77,6 @@ Event::Event(
|
|||
start_time = now;
|
||||
}
|
||||
|
||||
Storage * storage = monitor->getStorage();
|
||||
scheme = storage->Scheme();
|
||||
|
||||
unsigned int state_id = 0;
|
||||
zmDbRow dbrow;
|
||||
if ( dbrow.fetch("SELECT Id FROM States WHERE IsActive=1") ) {
|
||||
|
@ -87,11 +85,12 @@ Event::Event(
|
|||
|
||||
// Copy it in case opening the mp4 doesn't work we can set it to another value
|
||||
save_jpegs = monitor->GetOptSaveJPEGs();
|
||||
Storage * storage = monitor->getStorage();
|
||||
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO `Events` "
|
||||
"( `MonitorId`, `StorageId`, `Name`, `StartTime`, `Width`, `Height`, `Cause`, `Notes`, `StateId`, `Orientation`, `Videoed`, `DefaultVideo`, `SaveJPEGs`, `Scheme` )"
|
||||
"( `MonitorId`, `StorageId`, `Name`, `StartDateTime`, `Width`, `Height`, `Cause`, `Notes`, `StateId`, `Orientation`, `Videoed`, `DefaultVideo`, `SaveJPEGs`, `Scheme` )"
|
||||
" VALUES "
|
||||
"( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )",
|
||||
monitor->Id(),
|
||||
|
@ -117,9 +116,66 @@ Event::Event(
|
|||
}
|
||||
id = mysql_insert_id(&dbconn);
|
||||
|
||||
if ( !SetPath(storage) ) {
|
||||
// Try another
|
||||
Warning("Failed creating event dir at %s", storage->Path());
|
||||
|
||||
std::string sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
|
||||
if ( monitor->ServerId() )
|
||||
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
|
||||
|
||||
Debug(1, "%s", sql.c_str());
|
||||
storage = nullptr;
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
if ( result ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
if ( SetPath(storage) )
|
||||
break;
|
||||
delete storage;
|
||||
storage = nullptr;
|
||||
} // end foreach row of Storage
|
||||
mysql_free_result(result);
|
||||
result = nullptr;
|
||||
}
|
||||
if ( !storage ) {
|
||||
Info("No valid local storage area found. Trying all other areas.");
|
||||
// Try remote
|
||||
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
|
||||
if ( monitor->ServerId() )
|
||||
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
if ( result ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
if ( SetPath(storage) )
|
||||
break;
|
||||
delete storage;
|
||||
storage = nullptr;
|
||||
} // end foreach row of Storage
|
||||
mysql_free_result(result);
|
||||
result = nullptr;
|
||||
}
|
||||
}
|
||||
if ( !storage ) {
|
||||
storage = new Storage();
|
||||
Warning("Failed to find a storage area to save events.");
|
||||
}
|
||||
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
|
||||
db_mutex.lock();
|
||||
int rc = mysql_query(&dbconn, sql.c_str());
|
||||
db_mutex.unlock();
|
||||
if ( rc ) {
|
||||
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql.c_str());
|
||||
}
|
||||
}
|
||||
Debug(1, "Using storage area at %s", path.c_str());
|
||||
|
||||
db_mutex.unlock();
|
||||
if ( untimedEvent ) {
|
||||
Warning("Event %d has zero time, setting to current", id);
|
||||
Warning("Event %" PRIu64 " has zero time, setting to current", id);
|
||||
}
|
||||
end_time.tv_sec = 0;
|
||||
frames = 0;
|
||||
|
@ -128,79 +184,7 @@ Event::Event(
|
|||
max_score = 0;
|
||||
have_video_keyframe = false;
|
||||
alarm_frame_written = false;
|
||||
|
||||
struct tm *stime = localtime(&start_time.tv_sec);
|
||||
std::string id_file;
|
||||
|
||||
path = stringtf("%s/%d", storage->Path(), monitor->Id());
|
||||
// Try to make the Monitor Dir. Normally this would exist, but in odd cases might not.
|
||||
if ( mkdir(path.c_str(), 0755) ) {
|
||||
if ( errno != EEXIST )
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
if ( storage->Scheme() == Storage::DEEP ) {
|
||||
int dt_parts[6];
|
||||
dt_parts[0] = stime->tm_year-100;
|
||||
dt_parts[1] = stime->tm_mon+1;
|
||||
dt_parts[2] = stime->tm_mday;
|
||||
dt_parts[3] = stime->tm_hour;
|
||||
dt_parts[4] = stime->tm_min;
|
||||
dt_parts[5] = stime->tm_sec;
|
||||
|
||||
std::string date_path;
|
||||
std::string time_path;
|
||||
|
||||
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
|
||||
path += stringtf("/%02d", dt_parts[i]);
|
||||
|
||||
if ( mkdir(path.c_str(), 0755) ) {
|
||||
// FIXME This should not be fatal. Should probably move to a different storage area.
|
||||
if ( errno != EEXIST )
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
}
|
||||
if ( i == 2 )
|
||||
date_path = path;
|
||||
}
|
||||
time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec);
|
||||
|
||||
// Create event id symlink
|
||||
id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
|
||||
if ( symlink(time_path.c_str(), id_file.c_str()) < 0 )
|
||||
Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno));
|
||||
} else if ( storage->Scheme() == Storage::MEDIUM ) {
|
||||
path += stringtf("/%04d-%02d-%02d",
|
||||
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
|
||||
);
|
||||
if ( mkdir(path.c_str(), 0755) ) {
|
||||
if ( errno != EEXIST )
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
}
|
||||
path += stringtf("/%" PRIu64, id);
|
||||
if ( mkdir(path.c_str(), 0755) ) {
|
||||
if ( errno != EEXIST )
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
}
|
||||
} else {
|
||||
path += stringtf("/%" PRIu64, id);
|
||||
if ( mkdir(path.c_str(), 0755) ) {
|
||||
if ( errno != EEXIST )
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
// Create empty id tag file
|
||||
id_file = stringtf("%s/.%" PRIu64, path.c_str(), id);
|
||||
if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) {
|
||||
fclose(id_fp);
|
||||
} else {
|
||||
Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno));
|
||||
}
|
||||
} // deep storage or not
|
||||
|
||||
Debug(2, "Created event %d at %s", id, path.c_str());
|
||||
|
||||
last_db_frame = 0;
|
||||
|
||||
video_name = "";
|
||||
|
||||
snapshot_file = path + "/snapshot.jpg";
|
||||
|
@ -220,11 +204,13 @@ Event::Event(
|
|||
|
||||
video_name = stringtf("%" PRIu64 "-%s.%s", id, "video", container.c_str());
|
||||
snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
|
||||
db_mutex.lock();
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
db_mutex.unlock();
|
||||
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql);
|
||||
return;
|
||||
}
|
||||
db_mutex.unlock();
|
||||
video_file = path + "/" + video_name;
|
||||
Debug(1, "Writing video file to %s", video_file.c_str());
|
||||
Camera * camera = monitor->getCamera();
|
||||
|
@ -681,7 +667,6 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
bool db_frame = ( frame_type != BULK ) || (frames==1) || ((frames%config.bulk_frame_interval)==0) ;
|
||||
if ( db_frame ) {
|
||||
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL(delta_time, timestamp, start_time, DT_PREC_2);
|
||||
Debug(1, "Frame delta is %d.%d - %d.%d = %d.%d",
|
||||
|
@ -692,14 +677,14 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
double fps = monitor->get_capture_fps();
|
||||
if ( write_to_db
|
||||
or
|
||||
(frame_data.size() > MAX_DB_FRAMES)
|
||||
(frame_data.size() >= MAX_DB_FRAMES)
|
||||
or
|
||||
(frame_type==BULK)
|
||||
or
|
||||
( fps and (frame_data.size() > fps) )
|
||||
) {
|
||||
Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK",
|
||||
frame_data.size(), write_to_db, fps);
|
||||
Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)",
|
||||
frame_data.size(), write_to_db, fps, (frame_type==BULK));
|
||||
WriteDbFrames();
|
||||
last_db_frame = frames;
|
||||
|
||||
|
@ -731,3 +716,77 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
|
||||
end_time = timestamp;
|
||||
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)
|
||||
|
||||
bool Event::SetPath(Storage *storage) {
|
||||
scheme = storage->Scheme();
|
||||
|
||||
path = stringtf("%s/%d", storage->Path(), monitor->Id());
|
||||
// Try to make the Monitor Dir. Normally this would exist, but in odd cases might not.
|
||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct tm *stime = localtime(&start_time.tv_sec);
|
||||
if ( scheme == Storage::DEEP ) {
|
||||
|
||||
int dt_parts[6];
|
||||
dt_parts[0] = stime->tm_year-100;
|
||||
dt_parts[1] = stime->tm_mon+1;
|
||||
dt_parts[2] = stime->tm_mday;
|
||||
dt_parts[3] = stime->tm_hour;
|
||||
dt_parts[4] = stime->tm_min;
|
||||
dt_parts[5] = stime->tm_sec;
|
||||
|
||||
std::string date_path;
|
||||
std::string time_path;
|
||||
|
||||
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
|
||||
path += stringtf("/%02d", dt_parts[i]);
|
||||
|
||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if ( i == 2 )
|
||||
date_path = path;
|
||||
}
|
||||
time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec);
|
||||
|
||||
// Create event id symlink
|
||||
std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
|
||||
if ( symlink(time_path.c_str(), id_file.c_str()) < 0 ) {
|
||||
Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else if ( scheme == Storage::MEDIUM ) {
|
||||
path += stringtf("/%04d-%02d-%02d",
|
||||
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
|
||||
);
|
||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
path += stringtf("/%" PRIu64, id);
|
||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
path += stringtf("/%" PRIu64, id);
|
||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create empty id tag file
|
||||
std::string id_file = stringtf("%s/.%" PRIu64, path.c_str(), id);
|
||||
if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) {
|
||||
fclose(id_fp);
|
||||
} else {
|
||||
Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} // deep storage or not
|
||||
return true;
|
||||
} // end bool Event::SetPath
|
||||
|
|
|
@ -47,20 +47,19 @@ class Zone;
|
|||
class Monitor;
|
||||
class EventStream;
|
||||
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
// Maximum number of prealarm frames that can be stored
|
||||
#define MAX_PRE_ALARM_FRAMES 16
|
||||
typedef uint64_t event_id_t;
|
||||
typedef enum { NORMAL=0, BULK, ALARM } FrameType;
|
||||
typedef enum { NORMAL=0, BULK, ALARM } FrameType;
|
||||
|
||||
#include "zm_frame.h"
|
||||
|
||||
//
|
||||
// Class describing events, i.e. captured periods of activity.
|
||||
//
|
||||
class Event {
|
||||
friend class EventStream;
|
||||
|
||||
protected:
|
||||
static int sd;
|
||||
|
||||
public:
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
|
@ -103,13 +102,18 @@ class Event {
|
|||
Storage::Schemes scheme;
|
||||
int save_jpegs;
|
||||
|
||||
void createNotes( std::string ¬es );
|
||||
void createNotes(std::string ¬es);
|
||||
|
||||
public:
|
||||
static bool OpenFrameSocket( int );
|
||||
static bool ValidateFrameSocket( int );
|
||||
static bool OpenFrameSocket(int);
|
||||
static bool ValidateFrameSocket(int);
|
||||
|
||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
|
||||
Event(
|
||||
Monitor *p_monitor,
|
||||
struct timeval p_start_time,
|
||||
const std::string &p_cause,
|
||||
const StringSetMap &p_noteSetMap
|
||||
);
|
||||
~Event();
|
||||
|
||||
uint64_t Id() const { return id; }
|
||||
|
@ -120,37 +124,56 @@ class Event {
|
|||
const struct timeval &StartTime() const { return start_time; }
|
||||
const struct timeval &EndTime() const { return end_time; }
|
||||
|
||||
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
||||
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ) const;
|
||||
bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow );
|
||||
|
||||
void updateNotes( const StringSetMap &stringSetMap );
|
||||
|
||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=nullptr );
|
||||
void AddPacket( ZMPacket *p, int score=0, Image *alarm_frame=nullptr );
|
||||
bool WritePacket( ZMPacket &p );
|
||||
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
||||
bool WriteFrameImage(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
const char *event_file,
|
||||
bool alarm_frame=false
|
||||
) const;
|
||||
bool WriteFrameVideo(
|
||||
const Image *image,
|
||||
const struct timeval timestamp,
|
||||
VideoWriter* videow
|
||||
) const;
|
||||
|
||||
void updateNotes(const StringSetMap &stringSetMap);
|
||||
|
||||
void AddFrames(int n_frames, Image **images, struct timeval **timestamps);
|
||||
void AddFrame(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
int score=0,
|
||||
Image *alarm_image=nullptr);
|
||||
|
||||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
void AddFramesInternal(
|
||||
int n_frames,
|
||||
int start_frame,
|
||||
Image **images,
|
||||
struct timeval **timestamps);
|
||||
void WriteDbFrames();
|
||||
void UpdateFramesDelta(double offset);
|
||||
bool SetPath(Storage *storage);
|
||||
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time ) {
|
||||
static const char *getSubPath(struct tm *time) {
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);
|
||||
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time->tm_year-100, time->tm_mon+1, time->tm_mday,
|
||||
time->tm_hour, time->tm_min, time->tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
static const char *getSubPath( time_t *time ) {
|
||||
return Event::getSubPath( localtime( time ) );
|
||||
static const char *getSubPath(time_t *time) {
|
||||
return Event::getSubPath(localtime(time));
|
||||
}
|
||||
|
||||
const char* getEventFile(void) const {
|
||||
return video_file.c_str();
|
||||
}
|
||||
|
||||
public:
|
||||
static int PreAlarmCount() {
|
||||
return pre_alarm_count;
|
||||
}
|
||||
|
@ -167,7 +190,12 @@ class Event {
|
|||
}
|
||||
pre_alarm_count = 0;
|
||||
}
|
||||
static void AddPreAlarmFrame(Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=nullptr) {
|
||||
static void AddPreAlarmFrame(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
int score=0,
|
||||
Image *alarm_frame=nullptr
|
||||
) {
|
||||
pre_alarm_data[pre_alarm_count].image = new Image(*image);
|
||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||
pre_alarm_data[pre_alarm_count].score = score;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ZoneMinder Event Class Implementation, $Date$, $Revision$
|
||||
// ZoneMinder Event Stream Class Implementation
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
|
@ -16,24 +16,13 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
//
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <getopt.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <glob.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_db.h"
|
||||
#include "zm_time.h"
|
||||
#include "zm_mpeg.h"
|
||||
#include "zm_signal.h"
|
||||
#include "zm_event.h"
|
||||
#include "zm_eventstream.h"
|
||||
#include "zm_storage.h"
|
||||
|
|
|
@ -57,8 +57,8 @@ static bool zmFifoDbgOpen() {
|
|||
|
||||
int zmFifoDbgInit(Monitor *monitor) {
|
||||
zm_fifodbg_inited = true;
|
||||
snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log",
|
||||
monitor->getStorage()->Path(), monitor->Id());
|
||||
snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/dbgpipe-%d.log",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
zmFifoDbgOpen();
|
||||
return 1;
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ bool FifoStream::sendRAWFrames() {
|
|||
return false;
|
||||
}
|
||||
if ( fwrite(buffer, bytes_read, 1, stdout) != 1 ) {
|
||||
if ( !zm_terminate )
|
||||
Error("Problem during writing: %s", strerror(errno));
|
||||
close(fd);
|
||||
return false;
|
||||
|
@ -181,7 +182,7 @@ bool FifoStream::sendMJEGFrames() {
|
|||
return true;
|
||||
|
||||
if ( fprintf(stdout,
|
||||
"--ZoneMinderFrame\r\n"
|
||||
"--" BOUNDARY "\r\n"
|
||||
"Content-Type: image/jpeg\r\n"
|
||||
"Content-Length: %d\r\n\r\n",
|
||||
total_read) < 0 ) {
|
||||
|
@ -206,35 +207,40 @@ void FifoStream::setStreamStart(const char * path) {
|
|||
|
||||
void FifoStream::setStreamStart(int monitor_id, const char * format) {
|
||||
char diag_path[PATH_MAX];
|
||||
const char * filename;
|
||||
Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
|
||||
|
||||
if ( !strcmp(format, "reference") ) {
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%d.jpg",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = MJPEG;
|
||||
filename = "diagpipe-r.jpg";
|
||||
} else if ( !strcmp(format, "delta") ) {
|
||||
filename = "diagpipe-d.jpg";
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%d.jpg",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = MJPEG;
|
||||
} else {
|
||||
if ( strcmp(format, "raw") ) {
|
||||
Warning("Unknown or unspecified format. Defaulting to raw");
|
||||
}
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%d.log",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = RAW;
|
||||
filename = "dbgpipe.log";
|
||||
}
|
||||
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/%d/%s",
|
||||
monitor->getStorage()->Path(), monitor->Id(), filename);
|
||||
setStreamStart(diag_path);
|
||||
}
|
||||
|
||||
void FifoStream::runStream() {
|
||||
if ( stream_type == MJPEG ) {
|
||||
fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n");
|
||||
fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n");
|
||||
} else {
|
||||
fprintf(stdout, "Content-Type: text/html\r\n\r\n");
|
||||
}
|
||||
|
||||
/* only 1 person can read from a fifo at a time, so use a lock */
|
||||
char lock_file[PATH_MAX];
|
||||
snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path);
|
||||
file_create_if_missing(lock_file, false);
|
||||
Debug(1, "Locking %s", lock_file);
|
||||
|
||||
int fd_lock = open(lock_file, O_RDONLY);
|
||||
if ( fd_lock < 0 ) {
|
||||
|
@ -242,8 +248,13 @@ void FifoStream::runStream() {
|
|||
return;
|
||||
}
|
||||
int res = flock(fd_lock, LOCK_EX | LOCK_NB);
|
||||
while ( (res < 0 and errno == EAGAIN) and (! zm_terminate) ) {
|
||||
Warning("Flocking problem on %s: - %s", lock_file, strerror(errno));
|
||||
sleep(1);
|
||||
res = flock(fd_lock, LOCK_EX | LOCK_NB);
|
||||
}
|
||||
if ( res < 0 ) {
|
||||
Error("Flocking problem on %s: - %s", lock_file, strerror(errno));
|
||||
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file, strerror(errno));
|
||||
close(fd_lock);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_font.h"
|
||||
#include "zm_utils.h"
|
||||
|
||||
int ZmFont::ReadFontFile(const std::string &loc) {
|
||||
FILE *f = fopen(loc.c_str(), "rb");
|
||||
if ( !f ) return -1; // FILE NOT FOUND
|
||||
|
||||
struct stat st;
|
||||
stat(loc.c_str(), &st);
|
||||
|
||||
font = new ZMFONT;
|
||||
|
||||
size_t header_size = 8 + (sizeof(ZMFONT_BH) * NUM_FONT_SIZES);
|
||||
|
||||
// MAGIC + pad + BitmapHeaders
|
||||
size_t readsize = fread(&font[0], 1, header_size, f);
|
||||
if ( readsize < header_size ) {
|
||||
delete font;
|
||||
font = nullptr;
|
||||
return -2; // EOF reached, invalid file
|
||||
}
|
||||
|
||||
if ( memcmp(font->MAGIC, "ZMFNT", 5) != 0 ) // Check whether magic is correct
|
||||
return -3;
|
||||
|
||||
for ( int i = 0; i < NUM_FONT_SIZES; i++ ) {
|
||||
/* Character Width cannot be greater than 64 as a row is represented as a uint64_t,
|
||||
height cannot be greater than 200(arbitary number which i have chosen, shouldn't need more than this) and
|
||||
idx should not be more than filesize
|
||||
*/
|
||||
if ( (font->header[i].charWidth > 64 && font->header[i].charWidth == 0) ||
|
||||
(font->header[i].charHeight > 200 && font->header[i].charHeight == 0) ||
|
||||
(font->header[i].idx > st.st_size) ) {
|
||||
delete font;
|
||||
font = nullptr;
|
||||
return -4;
|
||||
}
|
||||
} // end foreach font size
|
||||
|
||||
datasize = st.st_size - header_size;
|
||||
|
||||
font->data = new uint64_t[datasize/sizeof(uint64_t)];
|
||||
readsize = fread(&font->data[0], 1, datasize, f);
|
||||
if ( readsize < datasize ) { // Shouldn't happen
|
||||
delete[] font->data;
|
||||
font->data = nullptr;
|
||||
delete font;
|
||||
font = nullptr;
|
||||
return -2;
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZmFont::~ZmFont() {
|
||||
if ( font && font->data ) {
|
||||
delete[] font->data;
|
||||
font->data = nullptr;
|
||||
}
|
||||
|
||||
if ( font ) {
|
||||
delete font;
|
||||
font = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t *ZmFont::GetBitmapData() {
|
||||
return &font->data[font->header[size].idx];
|
||||
}
|
3369
src/zm_font.h
3369
src/zm_font.h
File diff suppressed because it is too large
Load Diff
200
src/zm_image.cpp
200
src/zm_image.cpp
|
@ -18,7 +18,6 @@
|
|||
//
|
||||
#include "zm.h"
|
||||
#include "zm_font.h"
|
||||
#include "zm_bigfont.h"
|
||||
#include "zm_image.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_rgb.h"
|
||||
|
@ -78,6 +77,9 @@ static deinterlace_4field_fptr_t fptr_deinterlace_4field_gray8;
|
|||
/* Pointer to image buffer memory copy function */
|
||||
imgbufcpy_fptr_t fptr_imgbufcpy;
|
||||
|
||||
/* Font */
|
||||
static ZmFont font;
|
||||
|
||||
void Image::update_function_pointers() {
|
||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
||||
if ( pixels % 16 || pixels % 12 ) {
|
||||
|
@ -116,7 +118,7 @@ Image::Image() {
|
|||
size = 0;
|
||||
allocation = 0;
|
||||
buffer = 0;
|
||||
buffertype = 0;
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
holdbuffer = 0;
|
||||
text[0] = '\0';
|
||||
blend = fptr_blend;
|
||||
|
@ -134,24 +136,26 @@ Image::Image(const char *filename) {
|
|||
size = 0;
|
||||
allocation = 0;
|
||||
buffer = 0;
|
||||
buffertype = 0;
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
holdbuffer = 0;
|
||||
ReadJpeg(filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
||||
text[0] = '\0';
|
||||
update_function_pointers();
|
||||
}
|
||||
|
||||
Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) {
|
||||
Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) :
|
||||
width(p_width),
|
||||
height(p_height),
|
||||
colours(p_colours),
|
||||
padding(p_padding),
|
||||
subpixelorder(p_subpixelorder),
|
||||
buffer(p_buffer) {
|
||||
|
||||
if ( !initialised )
|
||||
Initialise();
|
||||
width = p_width;
|
||||
height = p_height;
|
||||
pixels = width*height;
|
||||
colours = p_colours;
|
||||
pixels = width * height;
|
||||
linesize = p_width * p_colours;
|
||||
padding = p_padding;
|
||||
subpixelorder = p_subpixelorder;
|
||||
size = linesize*height + padding;
|
||||
size = linesize * height + padding;
|
||||
buffer = nullptr;
|
||||
holdbuffer = 0;
|
||||
if ( p_buffer ) {
|
||||
|
@ -166,16 +170,18 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint
|
|||
update_function_pointers();
|
||||
}
|
||||
|
||||
Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) {
|
||||
Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) :
|
||||
width(p_width),
|
||||
linesize(p_linesize),
|
||||
height(p_height),
|
||||
colours(p_colours),
|
||||
padding(p_padding),
|
||||
subpixelorder(p_subpixelorder),
|
||||
buffer(p_buffer)
|
||||
{
|
||||
if ( !initialised )
|
||||
Initialise();
|
||||
width = p_width;
|
||||
linesize = p_linesize;
|
||||
height = p_height;
|
||||
pixels = width*height;
|
||||
colours = p_colours;
|
||||
padding = p_padding;
|
||||
subpixelorder = p_subpixelorder;
|
||||
size = linesize*height + padding;
|
||||
buffer = nullptr;
|
||||
holdbuffer = 0;
|
||||
|
@ -493,6 +499,12 @@ void Image::Initialise() {
|
|||
g_u_table = g_u_table_global;
|
||||
b_u_table = b_u_table_global;
|
||||
|
||||
int res = font.ReadFontFile(config.font_file_location);
|
||||
if ( res == -1 ) {
|
||||
Panic("Invalid font location: %s", config.font_file_location);
|
||||
} else if ( res == -2 || res == -3 || res == -4 ) {
|
||||
Panic("Invalid font file.");
|
||||
}
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
|
@ -659,7 +671,7 @@ void Image::Assign(
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
if ( new_size > allocation || !buffer ) {
|
||||
if ( (new_size > allocation) || !buffer ) {
|
||||
DumpImgBuffer();
|
||||
AllocImgBuffer(new_size);
|
||||
}
|
||||
|
@ -895,8 +907,8 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int
|
|||
|
||||
jpeg_read_header(cinfo, TRUE);
|
||||
|
||||
if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) {
|
||||
Error( "Unexpected colours when reading jpeg image: %d", colours );
|
||||
if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) {
|
||||
Error("Unexpected colours when reading jpeg image: %d", colours);
|
||||
jpeg_abort_decompress(cinfo);
|
||||
fclose(infile);
|
||||
return false;
|
||||
|
@ -911,7 +923,7 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int
|
|||
new_width = cinfo->image_width;
|
||||
new_height = cinfo->image_height;
|
||||
|
||||
if ( width != new_width || height != new_height ) {
|
||||
if ( (width != new_width) || (height != new_height) ) {
|
||||
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height);
|
||||
}
|
||||
|
||||
|
@ -1115,7 +1127,7 @@ cinfo->out_color_space = JCS_EXT_RGB;
|
|||
#else
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
#endif
|
||||
*/
|
||||
*/
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
}
|
||||
break;
|
||||
|
@ -1206,7 +1218,7 @@ bool Image::DecodeJpeg(
|
|||
new_width = cinfo->image_width;
|
||||
new_height = cinfo->image_height;
|
||||
|
||||
if ( width != new_width || height != new_height ) {
|
||||
if ( (width != new_width) || (height != new_height) ) {
|
||||
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u",
|
||||
width, height, new_width, new_height);
|
||||
}
|
||||
|
@ -1878,8 +1890,12 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const {
|
|||
line = text+index;
|
||||
line_no++;
|
||||
}
|
||||
int x = (width - (max_line_len * ZM_CHAR_WIDTH * size) ) / 2;
|
||||
int y = (height - (line_no * LINE_HEIGHT * size) ) / 2;
|
||||
|
||||
font.SetFontSize(size-1);
|
||||
uint16_t char_width = font.GetCharWidth();
|
||||
uint16_t char_height = font.GetCharHeight();
|
||||
int x = (width - (max_line_len * char_width )) / 2;
|
||||
int y = (height - (line_no * char_height) ) / 2;
|
||||
return Coord(x, y);
|
||||
}
|
||||
|
||||
|
@ -1925,13 +1941,15 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour
|
|||
}
|
||||
|
||||
/* RGB32 compatible: complete */
|
||||
/* Bitmap decoding trick has been adopted from here:
|
||||
https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
|
||||
*/
|
||||
void Image::Annotate(
|
||||
const char *p_text,
|
||||
const Coord &coord,
|
||||
const unsigned int size,
|
||||
const Rgb fg_colour,
|
||||
const Rgb bg_colour) {
|
||||
Debug(1, "text %s", p_text);
|
||||
strncpy(text, p_text, sizeof(text)-1);
|
||||
Debug(1, "text %s", text);
|
||||
|
||||
|
@ -1945,31 +1963,32 @@ void Image::Annotate(
|
|||
const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour);
|
||||
const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour);
|
||||
const uint8_t fg_bw_col = fg_colour & 0xff;
|
||||
const Rgb fg_rgb_col = rgb_convert(fg_colour,subpixelorder);
|
||||
const bool fg_trans = (fg_colour == RGB_TRANSPARENT);
|
||||
const Rgb fg_rgb_col = rgb_convert(fg_colour, subpixelorder);
|
||||
|
||||
const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour);
|
||||
const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour);
|
||||
const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour);
|
||||
const uint8_t bg_bw_col = bg_colour & 0xff;
|
||||
const Rgb bg_rgb_col = rgb_convert(bg_colour,subpixelorder);
|
||||
const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder);
|
||||
const bool bg_trans = (bg_colour == RGB_TRANSPARENT);
|
||||
|
||||
int zm_text_bitmask = 0x80;
|
||||
if ( size == 2 )
|
||||
zm_text_bitmask = 0x8000;
|
||||
font.SetFontSize(size-1);
|
||||
const uint16_t char_width = font.GetCharWidth();
|
||||
const uint16_t char_height = font.GetCharHeight();
|
||||
const uint64_t *font_bitmap = font.GetBitmapData();
|
||||
Debug(1, "Font size %d, char_width %d char_height %d", size, char_width, char_height);
|
||||
|
||||
while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) {
|
||||
|
||||
unsigned int line_width = line_len * ZM_CHAR_WIDTH * size;
|
||||
unsigned int line_width = line_len * char_width;
|
||||
|
||||
unsigned int lo_line_x = coord.X();
|
||||
unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT * size);
|
||||
unsigned int lo_line_y = coord.Y() + (line_no * char_height);
|
||||
|
||||
unsigned int min_line_x = 0;
|
||||
// FIXME What if line_width > width?
|
||||
unsigned int max_line_x = width - line_width;
|
||||
unsigned int min_line_y = 0;
|
||||
unsigned int max_line_y = height - (LINE_HEIGHT * size);
|
||||
unsigned int max_line_y = height - char_height;
|
||||
|
||||
if ( lo_line_x > max_line_x )
|
||||
lo_line_x = max_line_x;
|
||||
|
@ -1981,7 +2000,7 @@ void Image::Annotate(
|
|||
lo_line_y = min_line_y;
|
||||
|
||||
unsigned int hi_line_x = lo_line_x + line_width;
|
||||
unsigned int hi_line_y = lo_line_y + (LINE_HEIGHT * size);
|
||||
unsigned int hi_line_y = lo_line_y + char_height;
|
||||
|
||||
// Clip anything that runs off the right of the screen
|
||||
if ( hi_line_x > width )
|
||||
|
@ -1991,99 +2010,78 @@ void Image::Annotate(
|
|||
|
||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||
unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x];
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += width ) {
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) {
|
||||
unsigned char *temp_ptr = ptr;
|
||||
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
|
||||
int f;
|
||||
if ( size == 2 ) {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
|
||||
if ( line[c] > 0xFF ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
|
||||
} else {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
|
||||
}
|
||||
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
|
||||
if ( f & (zm_text_bitmask >> i) ) {
|
||||
if ( !fg_trans )
|
||||
*temp_ptr = fg_bw_col;
|
||||
} else if ( !bg_trans ) {
|
||||
*temp_ptr = bg_bw_col;
|
||||
}
|
||||
uint64_t f = font_bitmap[(line[c] * char_height) + r];
|
||||
if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width);
|
||||
while ( f != 0 ) {
|
||||
uint64_t t = f & -f;
|
||||
int idx = char_width - __builtin_ctzll(f);
|
||||
*(temp_ptr + idx) = fg_bw_col;
|
||||
f ^= t;
|
||||
}
|
||||
temp_ptr += char_width;
|
||||
}
|
||||
}
|
||||
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||
unsigned int wc = width * colours;
|
||||
|
||||
unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours];
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
|
||||
unsigned char *temp_ptr = ptr;
|
||||
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
|
||||
int f;
|
||||
if ( size == 2 ) {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
|
||||
if ( line[c] > 0xFF ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
|
||||
} else {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
|
||||
}
|
||||
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) {
|
||||
if ( f & (zm_text_bitmask >> i) ) {
|
||||
if ( !fg_trans ) {
|
||||
RED_PTR_RGBA(temp_ptr) = fg_r_col;
|
||||
GREEN_PTR_RGBA(temp_ptr) = fg_g_col;
|
||||
BLUE_PTR_RGBA(temp_ptr) = fg_b_col;
|
||||
}
|
||||
} else if ( !bg_trans ) {
|
||||
RED_PTR_RGBA(temp_ptr) = bg_r_col;
|
||||
GREEN_PTR_RGBA(temp_ptr) = bg_g_col;
|
||||
BLUE_PTR_RGBA(temp_ptr) = bg_b_col;
|
||||
uint64_t f = font_bitmap[(line[c] * char_height) + r];
|
||||
if ( !bg_trans ) {
|
||||
for ( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components
|
||||
unsigned char *colour_ptr = temp_ptr + (i*3);
|
||||
RED_PTR_RGBA(colour_ptr) = bg_r_col;
|
||||
GREEN_PTR_RGBA(colour_ptr) = bg_g_col;
|
||||
BLUE_PTR_RGBA(colour_ptr) = bg_b_col;
|
||||
}
|
||||
}
|
||||
while ( f != 0 ) {
|
||||
uint64_t t = f & -f;
|
||||
int idx = char_width - __builtin_ctzll(f);
|
||||
unsigned char *colour_ptr = temp_ptr + (idx*3);
|
||||
RED_PTR_RGBA(colour_ptr) = fg_r_col;
|
||||
GREEN_PTR_RGBA(colour_ptr) = fg_g_col;
|
||||
BLUE_PTR_RGBA(colour_ptr) = fg_b_col;
|
||||
f ^= t;
|
||||
}
|
||||
temp_ptr += char_width * colours;
|
||||
}
|
||||
}
|
||||
} else if ( colours == ZM_COLOUR_RGB32 ) {
|
||||
unsigned int wc = width * colours;
|
||||
|
||||
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x)<<2];
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
|
||||
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x) << 2];
|
||||
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
|
||||
Rgb* temp_ptr = (Rgb*)ptr;
|
||||
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
|
||||
int f;
|
||||
if ( size == 2 ) {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
|
||||
if ( line[c] > 0xFF ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
|
||||
} else {
|
||||
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
|
||||
Warning("Unsupported character %c in %s", line[c], line);
|
||||
continue;
|
||||
}
|
||||
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
|
||||
}
|
||||
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
|
||||
if ( f & (zm_text_bitmask >> i) ) {
|
||||
if ( !fg_trans ) {
|
||||
*temp_ptr = fg_rgb_col;
|
||||
}
|
||||
} else if ( !bg_trans ) {
|
||||
*temp_ptr = bg_rgb_col;
|
||||
uint64_t f = font_bitmap[(line[c] * char_height) + r];
|
||||
if ( !bg_trans ) {
|
||||
for ( int i = 0; i < char_width; i++ )
|
||||
*(temp_ptr + i) = bg_rgb_col;
|
||||
}
|
||||
while ( f != 0 ) {
|
||||
uint64_t t = f & -f;
|
||||
int idx = char_width - __builtin_ctzll(f);
|
||||
*(temp_ptr + idx) = fg_rgb_col;
|
||||
f ^= t;
|
||||
}
|
||||
temp_ptr += char_width;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ inline static uint8_t* AllocBuffer(size_t p_bufsize) {
|
|||
}
|
||||
|
||||
inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
||||
if ( buffer && buffertype != ZM_BUFTYPE_DONTFREE ) {
|
||||
if ( buffer && (buffertype != ZM_BUFTYPE_DONTFREE) ) {
|
||||
if ( buffertype == ZM_BUFTYPE_ZM ) {
|
||||
zm_freealigned(buffer);
|
||||
} else if ( buffertype == ZM_BUFTYPE_MALLOC ) {
|
||||
|
@ -123,6 +123,7 @@ protected:
|
|||
|
||||
inline void DumpImgBuffer() {
|
||||
DumpBuffer(buffer, buffertype);
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
buffer = nullptr;
|
||||
allocation = 0;
|
||||
}
|
||||
|
|
|
@ -40,10 +40,14 @@ void bind_libvnc_symbols() {
|
|||
static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, int h){
|
||||
VncPrivateData *data = (VncPrivateData *)(*rfbClientGetClientData_f)(rfb, &TAG_0);
|
||||
data->buffer = rfb->frameBuffer;
|
||||
Debug(1, "GotFrameBufferUpdateallback x:%d y:%d w%d h:%d width: %d, height: %d",
|
||||
x,y,w,h, rfb->width, rfb->height);
|
||||
}
|
||||
|
||||
static char* GetPasswordCallback(rfbClient* cl){
|
||||
return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1));
|
||||
Debug(1, "Getcredentials: %s", (*rfbClientGetClientData_f)(cl, &TAG_1));
|
||||
return strdup(
|
||||
(const char *)(*rfbClientGetClientData_f)(cl, &TAG_1));
|
||||
}
|
||||
|
||||
static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){
|
||||
|
@ -53,6 +57,7 @@ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Debug(1, "Getcredentials: %s:%s", (*rfbClientGetClientData_f)(cl, &TAG_1), (*rfbClientGetClientData_f)(cl, &TAG_2));
|
||||
c->userCredential.password = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1));
|
||||
c->userCredential.username = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_2));
|
||||
return c;
|
||||
|
@ -87,6 +92,7 @@ VncCamera::VncCamera(
|
|||
p_capture,
|
||||
p_record_audio
|
||||
),
|
||||
mRfb(nullptr),
|
||||
mHost(host),
|
||||
mPort(port),
|
||||
mUser(user),
|
||||
|
@ -110,20 +116,33 @@ VncCamera::VncCamera(
|
|||
Panic("Unexpected colours: %d", colours);
|
||||
}
|
||||
|
||||
if ( capture )
|
||||
Initialise();
|
||||
}
|
||||
if ( capture ) {
|
||||
Debug(3, "Initializing Client");
|
||||
bind_libvnc_symbols();
|
||||
scale.init();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VncCamera::~VncCamera() {
|
||||
if ( capture )
|
||||
Terminate();
|
||||
if ( capture ) {
|
||||
if ( mRfb->frameBuffer )
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
}
|
||||
}
|
||||
|
||||
void VncCamera::Initialise() {
|
||||
Debug(2, "Initializing Client");
|
||||
bind_libvnc_symbols();
|
||||
mRfb = (*rfbGetClient_f)(8, 3, 4);
|
||||
int VncCamera::PrimeCapture() {
|
||||
Debug(1, "Priming capture from %s", mHost.c_str());
|
||||
|
||||
if ( ! mRfb ) {
|
||||
mVncData.buffer = nullptr;
|
||||
mVncData.width = 0;
|
||||
mVncData.height = 0;
|
||||
|
||||
mRfb = (*rfbGetClient_f)(8 /* bits per sample */, 3 /* samples per pixel */, 4 /* bytes Per Pixel */);
|
||||
mRfb->frameBuffer = (uint8_t *)av_malloc(8*4*width*height);
|
||||
mRfb->canHandleNewFBSize = false;
|
||||
|
||||
(*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData);
|
||||
(*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str());
|
||||
|
@ -136,26 +155,22 @@ void VncCamera::Initialise() {
|
|||
mRfb->programName = "Zoneminder VNC Monitor";
|
||||
mRfb->serverHost = strdup(mHost.c_str());
|
||||
mRfb->serverPort = atoi(mPort.c_str());
|
||||
scale.init();
|
||||
}
|
||||
|
||||
void VncCamera::Terminate() {
|
||||
if ( mRfb->frameBuffer )
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
return;
|
||||
}
|
||||
|
||||
int VncCamera::PrimeCapture() {
|
||||
Debug(1, "Priming capture from %s", mHost.c_str());
|
||||
}
|
||||
if ( ! (*rfbInitClient_f)(mRfb, 0, nullptr) ) {
|
||||
/* IF rfbInitClient fails, it calls rdbClientCleanup which will free mRfb */
|
||||
Warning("Failed to Priming capture from %s", mHost.c_str());
|
||||
mRfb = nullptr;
|
||||
return -1;
|
||||
}
|
||||
if ( (mRfb->width != width) or (mRfb->height != height) ) {
|
||||
Warning("Specified dimensions do not match screen size monitor: (%dx%d) != vnc: (%dx%d)",
|
||||
width, height, mRfb->width, mRfb->height);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VncCamera::PreCapture() {
|
||||
Debug(2, "PreCapture");
|
||||
int rc = (*WaitForMessage_f)(mRfb, 500);
|
||||
if ( rc < 0 ) {
|
||||
return -1;
|
||||
|
@ -163,13 +178,16 @@ int VncCamera::PreCapture() {
|
|||
return rc;
|
||||
}
|
||||
rfbBool res = (*HandleRFBServerMessage_f)(mRfb);
|
||||
Debug(3, "PreCapture rc from HandleMessage %d", res == TRUE ? 1 : -1);
|
||||
return res == TRUE ? 1 : -1;
|
||||
}
|
||||
|
||||
int VncCamera::Capture(Image &image) {
|
||||
Debug(2, "Capturing");
|
||||
uint8_t *directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
scale.Convert(
|
||||
int VncCamera::Capture(ZMPacket &zm_packet) {
|
||||
if ( ! mVncData.buffer ) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder);
|
||||
int rc = scale.Convert(
|
||||
mVncData.buffer,
|
||||
mRfb->si.framebufferHeight * mRfb->si.framebufferWidth * 4,
|
||||
directbuffer,
|
||||
|
@ -180,17 +198,13 @@ int VncCamera::Capture(Image &image) {
|
|||
mRfb->si.framebufferHeight,
|
||||
width,
|
||||
height);
|
||||
return 1;
|
||||
return rc == 0 ? 1 : rc;
|
||||
}
|
||||
|
||||
int VncCamera::PostCapture() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_directory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VncCamera::Close() {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
#if HAVE_LIBVNC
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
// Used by vnc callbacks
|
||||
struct VncPrivateData
|
||||
{
|
||||
struct VncPrivateData {
|
||||
uint8_t *buffer;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
|
@ -47,14 +47,10 @@ public:
|
|||
|
||||
~VncCamera();
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
int PreCapture();
|
||||
int PrimeCapture();
|
||||
int Capture( Image &image );
|
||||
int Capture(ZMPacket &packet);
|
||||
int PostCapture();
|
||||
int CaptureAndRecord( Image &image, timeval recording, char* event_directory );
|
||||
int Close();
|
||||
};
|
||||
|
||||
|
|
|
@ -333,11 +333,12 @@ Monitor::Monitor()
|
|||
auto_resume_time(0),
|
||||
last_motion_score(0),
|
||||
camera(0),
|
||||
event(0),
|
||||
n_zones(0),
|
||||
zones(nullptr),
|
||||
timestamps(0),
|
||||
images(0),
|
||||
privacy_bitmask( nullptr ),
|
||||
images(nullptr),
|
||||
privacy_bitmask(nullptr),
|
||||
event_delete_thread(nullptr),
|
||||
n_linked_monitors(0),
|
||||
linked_monitors(nullptr)
|
||||
|
@ -566,8 +567,6 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
else
|
||||
event_close_mode = CLOSE_IDLE;
|
||||
|
||||
Debug(1, "monitor purpose=%d", purpose);
|
||||
|
||||
mem_size = sizeof(SharedData)
|
||||
+ sizeof(TriggerData)
|
||||
+ sizeof(VideoStoreData) //Information to pass back to the capture process
|
||||
|
@ -591,16 +590,47 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
// maybe unneeded
|
||||
// Should maybe store this for later use
|
||||
std::string monitor_dir = stringtf("%s/%d", storage->Path(), id);
|
||||
shared_data = nullptr;
|
||||
|
||||
if ( purpose == CAPTURE ) {
|
||||
if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) {
|
||||
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
|
||||
}
|
||||
} else if ( purpose == ANALYSIS ) {
|
||||
while (
|
||||
( !(this->connect() and shared_data->valid) )
|
||||
or
|
||||
( shared_data->last_write_index == (unsigned int)image_buffer_count )
|
||||
or
|
||||
( shared_data->last_write_time == 0 )
|
||||
) {
|
||||
Debug(1, "Waiting for capture daemon shared_data(%d) last_write_index(%d), last_write_time(%d)",
|
||||
(shared_data ? 1:0),
|
||||
(shared_data ? shared_data->last_write_index : 0),
|
||||
(shared_data ? shared_data->last_write_time : 0));
|
||||
this->disconnect();
|
||||
sleep(1);
|
||||
if ( zm_terminate ) break;
|
||||
}
|
||||
|
||||
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(),
|
||||
image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize());
|
||||
adaptive_skip = true;
|
||||
|
||||
if ( config.record_diag_images ) {
|
||||
diag_path_r = monitor_dir + "/diag-r.jpg";
|
||||
diag_path_d = monitor_dir + "/diag-d.jpg";
|
||||
if ( config.record_diag_images_fifo ) {
|
||||
diag_path_ref = stringtf("%s/diagpipe-r-%d.jpg", staticConfig.PATH_SOCKS.c_str(), id);
|
||||
diag_path_delta = stringtf("%s/diagpipe-d-%d.jpg", staticConfig.PATH_SOCKS.c_str(), id);
|
||||
FifoStream::fifo_create_if_missing(diag_path_ref.c_str());
|
||||
FifoStream::fifo_create_if_missing(diag_path_delta.c_str());
|
||||
} else {
|
||||
diag_path_ref = stringtf("%s/%d/diag-r.jpg", storage->Path(), id);
|
||||
diag_path_delta = stringtf("%s/%d/diag-d.jpg", storage->Path(), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_time = last_fps_time = time( 0 );
|
||||
|
||||
//this->delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
|
||||
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
|
||||
|
@ -769,6 +799,27 @@ Camera * Monitor::getCamera() {
|
|||
#else // HAVE_LIBCURL
|
||||
Error("You must have libcurl installed to use ffmpeg cameras for monitor %d", id);
|
||||
#endif // HAVE_LIBCURL
|
||||
} else if ( type == VNC ) {
|
||||
#if HAVE_LIBVNC
|
||||
camera = new VncCamera(
|
||||
id,
|
||||
host.c_str(),
|
||||
port.c_str(),
|
||||
user.c_str(),
|
||||
pass.c_str(),
|
||||
width,
|
||||
height,
|
||||
colours,
|
||||
brightness,
|
||||
contrast,
|
||||
hue,
|
||||
colour,
|
||||
purpose==CAPTURE,
|
||||
record_audio
|
||||
);
|
||||
#else // HAVE_LIBVNC
|
||||
Fatal("You must have libvnc installed to use VNC cameras for monitor id %d", id);
|
||||
#endif // HAVE_LIBVNC
|
||||
} // end if type
|
||||
|
||||
camera->setMonitor(this);
|
||||
|
@ -815,7 +866,7 @@ bool Monitor::connect() {
|
|||
Debug(3, "Connecting to monitor. Purpose is %d", purpose);
|
||||
#if ZM_MEM_MAPPED
|
||||
snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id);
|
||||
map_fd = open(mem_file, O_RDWR|O_CREAT, (mode_t)0600);
|
||||
map_fd = open(mem_file, O_RDWR|O_CREAT, (mode_t)0660);
|
||||
if ( map_fd < 0 ) {
|
||||
Error("Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno));
|
||||
return false;
|
||||
|
@ -864,12 +915,12 @@ bool Monitor::connect() {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
if ( mem_ptr == MAP_FAILED )
|
||||
Fatal("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno);
|
||||
if ( mem_ptr == nullptr ) {
|
||||
Error("mmap gave a NULL address:");
|
||||
} else {
|
||||
Debug(3, "mmapped to %p", mem_ptr);
|
||||
if ( (mem_ptr == MAP_FAILED) or (mem_ptr == nullptr) ) {
|
||||
Error("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno);
|
||||
close(map_fd);
|
||||
map_fd = -1;
|
||||
mem_ptr = nullptr;
|
||||
return false;
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700);
|
||||
|
@ -960,12 +1011,72 @@ bool Monitor::connect() {
|
|||
timestamps = new struct timeval *[pre_event_count];
|
||||
images = new Image *[pre_event_count];
|
||||
last_signal = shared_data->signal;
|
||||
}
|
||||
|
||||
} // end if purpose == ANALYSIS
|
||||
Debug(3, "Success connecting");
|
||||
return true;
|
||||
} // Monitor::connect
|
||||
|
||||
bool Monitor::disconnect() {
|
||||
if ( !mem_ptr )
|
||||
return true;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
if ( mem_ptr > (void *)0 ) {
|
||||
msync(mem_ptr, mem_size, MS_ASYNC);
|
||||
munmap(mem_ptr, mem_size);
|
||||
}
|
||||
if ( map_fd >= 0 )
|
||||
close(map_fd);
|
||||
|
||||
map_fd = -1;
|
||||
|
||||
if ( purpose == CAPTURE ) {
|
||||
if ( unlink(mem_file) < 0 ) {
|
||||
Warning("Can't unlink '%s': %s", mem_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
struct shmid_ds shm_data;
|
||||
if ( shmctl(shm_id, IPC_STAT, &shm_data) < 0 ) {
|
||||
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
shm_id = 0;
|
||||
|
||||
if ( shm_data.shm_nattch <= 1 ) {
|
||||
if ( shmctl(shm_id, IPC_RMID, 0) < 0 ) {
|
||||
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( shmdt(mem_ptr) < 0 ) {
|
||||
Debug(3, "Can't shmdt: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif // ZM_MEM_MAPPED
|
||||
if ( image_buffer ) {
|
||||
for ( int i = 0; i < image_buffer_count; i++ ) {
|
||||
delete image_buffer[i].image;
|
||||
image_buffer[i].image = nullptr;
|
||||
}
|
||||
delete[] image_buffer;
|
||||
image_buffer = nullptr;
|
||||
}
|
||||
|
||||
if ( purpose == ANALYSIS ) {
|
||||
delete[] timestamps;
|
||||
timestamps = nullptr;
|
||||
delete[] images;
|
||||
images = nullptr;
|
||||
} // end if purpose == ANALYSIS
|
||||
|
||||
mem_ptr = nullptr;
|
||||
shared_data = nullptr;
|
||||
return true;
|
||||
} // end bool Monitor::disconnect()
|
||||
|
||||
Monitor::~Monitor() {
|
||||
if ( mem_ptr ) {
|
||||
if ( event ) {
|
||||
|
@ -985,92 +1096,36 @@ Monitor::~Monitor() {
|
|||
event_delete_thread = nullptr;
|
||||
}
|
||||
|
||||
if ( deinterlacing_value == 4 ) {
|
||||
delete next_buffer.image;
|
||||
}
|
||||
#if 1
|
||||
for ( int i=0; i < image_buffer_count; i++ ) {
|
||||
delete image_buffer[i].image;
|
||||
}
|
||||
#endif
|
||||
delete[] image_buffer;
|
||||
|
||||
if ( purpose == ANALYSIS ) {
|
||||
shared_data->state = state = IDLE;
|
||||
// I think we set it to the count so that it is technically 1 behind capture, which starts at 0
|
||||
shared_data->last_read_index = image_buffer_count;
|
||||
shared_data->last_read_time = 0;
|
||||
|
||||
if ( Event::PreAlarmCount() )
|
||||
Event::EmptyPreAlarmFrames();
|
||||
} else if ( purpose == CAPTURE ) {
|
||||
shared_data->valid = false;
|
||||
memset(mem_ptr, 0, mem_size);
|
||||
}
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
if ( msync(mem_ptr, mem_size, MS_SYNC) < 0 )
|
||||
Error("Can't msync: %s", strerror(errno));
|
||||
if ( munmap(mem_ptr, mem_size) < 0 )
|
||||
Fatal("Can't munmap: %s", strerror(errno));
|
||||
close(map_fd);
|
||||
|
||||
if ( purpose == CAPTURE ) {
|
||||
// How about we store this in the object on instantiation so that we don't have to do this again.
|
||||
char mmap_path[PATH_MAX] = "";
|
||||
snprintf(mmap_path, sizeof(mmap_path), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id);
|
||||
|
||||
if ( unlink(mmap_path) < 0 ) {
|
||||
Warning("Can't unlink '%s': %s", mmap_path, strerror(errno));
|
||||
if ( (deinterlacing & 0xff) == 4 ) {
|
||||
delete next_buffer.image;
|
||||
delete next_buffer.timestamp;
|
||||
}
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
struct shmid_ds shm_data;
|
||||
if ( shmctl(shm_id, IPC_STAT, &shm_data) < 0 ) {
|
||||
Fatal("Can't shmctl: %s", strerror(errno));
|
||||
}
|
||||
if ( shm_data.shm_nattch <= 1 ) {
|
||||
if ( shmctl(shm_id, IPC_RMID, 0) < 0 ) {
|
||||
Fatal("Can't shmctl: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif // ZM_MEM_MAPPED
|
||||
disconnect();
|
||||
} // end if mem_ptr
|
||||
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
|
||||
if ( n_linked_monitors ) {
|
||||
for( int i = 0; i < n_linked_monitors; i++ ) {
|
||||
delete linked_monitors[i];
|
||||
}
|
||||
delete[] linked_monitors;
|
||||
linked_monitors = 0;
|
||||
}
|
||||
|
||||
if ( timestamps ) {
|
||||
delete[] timestamps;
|
||||
timestamps = 0;
|
||||
}
|
||||
if ( images ) {
|
||||
delete[] images;
|
||||
images = 0;
|
||||
}
|
||||
|
||||
delete packetqueue;
|
||||
packetqueue = NULL;
|
||||
packetqueue = nullptr;
|
||||
|
||||
if ( privacy_bitmask ) {
|
||||
delete[] privacy_bitmask;
|
||||
privacy_bitmask = NULL;
|
||||
}
|
||||
for ( int i=0; i < n_zones; i++ ) {
|
||||
for ( int i = 0; i < n_zones; i++ ) {
|
||||
delete zones[i];
|
||||
}
|
||||
delete[] zones;
|
||||
|
||||
delete camera;
|
||||
delete storage;
|
||||
}
|
||||
} // end Monitor::~Monitor()
|
||||
|
||||
void Monitor::AddZones(int p_n_zones, Zone *p_zones[]) {
|
||||
for ( int i=0; i < n_zones; i++ )
|
||||
|
@ -1703,7 +1758,9 @@ bool Monitor::Analyse() {
|
|||
// signal is set by capture
|
||||
bool signal = shared_data->signal;
|
||||
bool signal_change = (signal != last_signal);
|
||||
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d)", signal, signal_change);
|
||||
|
||||
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d) trigger state(%d)",
|
||||
signal, signal_change, trigger_data->trigger_state);
|
||||
|
||||
// if we have been told to be OFF, then we are off and don't do any processing.
|
||||
if ( trigger_data->trigger_state != TRIGGER_OFF ) {
|
||||
|
@ -1795,7 +1852,11 @@ bool Monitor::Analyse() {
|
|||
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
||||
// TODO: Shouldn't we try to connect?
|
||||
if ( linked_monitors[i]->isConnected() ) {
|
||||
Debug(4, "Linked monitor %d %s is connected",
|
||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||
if ( linked_monitors[i]->hasAlarmed() ) {
|
||||
Debug(4, "Linked monitor %d %s is alarmed",
|
||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||
if ( !event ) {
|
||||
if ( first_link ) {
|
||||
if ( cause.length() )
|
||||
|
@ -1806,6 +1867,9 @@ bool Monitor::Analyse() {
|
|||
}
|
||||
noteSet.insert(linked_monitors[i]->Name());
|
||||
score += 50;
|
||||
} else {
|
||||
Debug(4, "Linked monitor %d %s is not alarmed",
|
||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||
}
|
||||
} else {
|
||||
Debug(1, "Linked monitor %d %d is not connected. Connecting.", i, linked_monitors[i]->Id());
|
||||
|
@ -2182,7 +2246,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
|
|||
int n_monitors = mysql_num_rows(result);
|
||||
if ( n_monitors == 1 ) {
|
||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||
Debug(1, "Linking to monitor %d", link_ids[i]);
|
||||
Debug(1, "Linking to monitor %d %s", atoi(dbrow[0]), dbrow[1]);
|
||||
linked_monitors[count++] = new MonitorLink(link_ids[i], dbrow[1]);
|
||||
} else {
|
||||
Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]);
|
||||
|
@ -2214,6 +2278,7 @@ int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose)
|
|||
}
|
||||
if ( mysql_errno(&dbconn) ) {
|
||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||
mysql_free_result(result);
|
||||
return 0;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
@ -2543,8 +2608,8 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo
|
|||
ref_image.Delta(comp_image, &delta_image);
|
||||
|
||||
if ( config.record_diag_images ) {
|
||||
ref_image.WriteJpeg(diag_path_r.c_str(), config.record_diag_images_fifo);
|
||||
delta_image.WriteJpeg(diag_path_d.c_str(), config.record_diag_images_fifo);
|
||||
ref_image.WriteJpeg(diag_path_ref.c_str(), config.record_diag_images_fifo);
|
||||
delta_image.WriteJpeg(diag_path_delta.c_str(), config.record_diag_images_fifo);
|
||||
}
|
||||
|
||||
// Blank out all exclusion zones
|
||||
|
|
|
@ -331,8 +331,8 @@ protected:
|
|||
Image ref_image;
|
||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||
Image write_image; // Used when creating snapshot images
|
||||
std::string diag_path_r;
|
||||
std::string diag_path_d;
|
||||
std::string diag_path_ref;
|
||||
std::string diag_path_delta;
|
||||
|
||||
Purpose purpose; // What this monitor has been created to do
|
||||
int event_count;
|
||||
|
@ -402,6 +402,7 @@ public:
|
|||
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
|
||||
inline int ShmValid() const {
|
||||
return shared_data && shared_data->valid;
|
||||
|
@ -410,23 +411,20 @@ public:
|
|||
|
||||
inline unsigned int Id() const { return id; }
|
||||
inline const char *Name() const { return name; }
|
||||
inline unsigned int ServerId() { return server_id; }
|
||||
inline Storage *getStorage() {
|
||||
if ( ! storage ) {
|
||||
storage = new Storage(storage_id);
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
inline Function GetFunction() const {
|
||||
return function;
|
||||
}
|
||||
inline Function GetFunction() const { return function; }
|
||||
inline bool Enabled() const {
|
||||
if ( function <= MONITOR )
|
||||
return false;
|
||||
return enabled;
|
||||
}
|
||||
inline const char *EventPrefix() const {
|
||||
return event_prefix;
|
||||
}
|
||||
inline const char *EventPrefix() const { return event_prefix; }
|
||||
inline bool Ready() const {
|
||||
if ( function <= MONITOR ) {
|
||||
Error("Should not be calling Ready if the function doesn't include motion detection");
|
||||
|
@ -443,12 +441,8 @@ public:
|
|||
return false;
|
||||
return( enabled && shared_data->active );
|
||||
}
|
||||
inline bool Exif() const {
|
||||
return embed_exif;
|
||||
}
|
||||
inline bool RecordAudio() {
|
||||
return record_audio;
|
||||
}
|
||||
inline bool Exif() const { return embed_exif; }
|
||||
inline bool RecordAudio() { return record_audio; }
|
||||
|
||||
/*
|
||||
inline Purpose Purpose() { return purpose };
|
||||
|
|
|
@ -698,6 +698,7 @@ void MonitorStream::runStream() {
|
|||
if ( !sendFrame(snap->image, snap->timestamp) ) {
|
||||
Debug(2, "sendFrame failed, quiting.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
// Perhaps we should use NOW instead.
|
||||
last_frame_timestamp = *timestamp;
|
||||
|
@ -709,6 +710,7 @@ void MonitorStream::runStream() {
|
|||
if ( !sendFrame(snap->image, snap->timestamp) ) {
|
||||
Debug(2, "sendFrame failed, quiting.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,25 +26,25 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
Storage::Storage() {
|
||||
Storage::Storage() : id(0) {
|
||||
Warning("Instantiating default Storage Object. Should not happen.");
|
||||
id = 0;
|
||||
strcpy(name, "Default");
|
||||
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
|
||||
// not using an absolute path. Make it one by appending ZM_PATH_WEB
|
||||
snprintf( path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str( ), staticConfig.DIR_EVENTS.c_str() );
|
||||
snprintf(path, sizeof(path), "%s/%s",
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
} else {
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1 );
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
|
||||
}
|
||||
scheme = MEDIUM;
|
||||
scheme_str = "Medium";
|
||||
}
|
||||
|
||||
Storage::Storage( MYSQL_ROW &dbrow ) {
|
||||
Storage::Storage(MYSQL_ROW &dbrow) {
|
||||
unsigned int index = 0;
|
||||
id = atoi( dbrow[index++] );
|
||||
strncpy( name, dbrow[index++], sizeof(name)-1 );
|
||||
strncpy( path, dbrow[index++], sizeof(path)-1 );
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name)-1);
|
||||
strncpy(path, dbrow[index++], sizeof(path)-1);
|
||||
type_str = std::string(dbrow[index++]);
|
||||
scheme_str = std::string(dbrow[index++]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
|
@ -57,16 +57,15 @@ Storage::Storage( MYSQL_ROW &dbrow ) {
|
|||
}
|
||||
|
||||
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */
|
||||
Storage::Storage( unsigned int p_id ) {
|
||||
id = 0;
|
||||
Storage::Storage(unsigned int p_id) : id(p_id) {
|
||||
|
||||
if ( p_id ) {
|
||||
if ( id ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%d", p_id);
|
||||
Debug(2,"Loading Storage for %d using %s", p_id, sql );
|
||||
snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
|
||||
Debug(2, "Loading Storage for %u using %s", id, sql);
|
||||
zmDbRow dbrow;
|
||||
if ( !dbrow.fetch(sql) ) {
|
||||
Error("Unable to load storage area for id %d: %s", p_id, mysql_error(&dbconn));
|
||||
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
|
@ -81,17 +80,18 @@ Storage::Storage( unsigned int p_id ) {
|
|||
} else {
|
||||
scheme = SHALLOW;
|
||||
}
|
||||
Debug(1, "Loaded Storage area %d '%s'", id, this->Name());
|
||||
Debug(1, "Loaded Storage area %d '%s'", id, name);
|
||||
}
|
||||
}
|
||||
if ( !id ) {
|
||||
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
|
||||
// not using an absolute path. Make it one by appending ZM_PATH_WEB
|
||||
snprintf(path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
snprintf(path, sizeof(path), "%s/%s",
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
} else {
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
|
||||
}
|
||||
Debug(1,"No id passed to Storage constructor. Using default path %s instead", path);
|
||||
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
|
||||
strcpy(name, "Default");
|
||||
scheme = MEDIUM;
|
||||
scheme_str = "Medium";
|
||||
|
|
|
@ -52,8 +52,9 @@ bool StreamBase::loadMonitor(int p_monitor_id) {
|
|||
Error("Unable to load monitor id %d for streaming", monitor_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( monitor->GetFunction() == Monitor::NONE ) {
|
||||
Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id);
|
||||
Info("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,7 +72,7 @@ bool StreamBase::checkInitialised() {
|
|||
return false;
|
||||
}
|
||||
if ( monitor->GetFunction() == Monitor::NONE ) {
|
||||
Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id);
|
||||
Info("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id);
|
||||
return false;
|
||||
}
|
||||
if ( !monitor->ShmValid() ) {
|
||||
|
@ -124,11 +125,14 @@ bool StreamBase::checkCommandQueue() {
|
|||
processCommand(&msg);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
} else if ( connkey ) {
|
||||
Warning("No sd in checkCommandQueue, comms not open?");
|
||||
} else {
|
||||
// Perfectly valid if only getting a snapshot
|
||||
Debug(1, "No sd in checkCommandQueue, comms not open?");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // end bool StreamBase::checkCommandQueue()
|
||||
|
||||
Image *StreamBase::prepareImage(Image *image) {
|
||||
|
||||
|
|
|
@ -139,8 +139,12 @@ int SWScale::Convert(
|
|||
unsigned int new_height
|
||||
) {
|
||||
/* Parameter checking */
|
||||
if(in_buffer == nullptr || out_buffer == nullptr) {
|
||||
Error("NULL Input or output buffer");
|
||||
if ( in_buffer == nullptr ) {
|
||||
Error("NULL Input buffer");
|
||||
return -1;
|
||||
}
|
||||
if ( out_buffer == nullptr ) {
|
||||
Error("NULL output buffer");
|
||||
return -1;
|
||||
}
|
||||
// if(in_pf == 0 || out_pf == 0) {
|
||||
|
@ -172,11 +176,13 @@ int SWScale::Convert(
|
|||
|
||||
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
||||
/* Warn if the input or output pixelformat is not supported */
|
||||
if(!sws_isSupportedInput(in_pf)) {
|
||||
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
||||
if ( !sws_isSupportedInput(in_pf) ) {
|
||||
Warning("swscale does not support the input format: %c%c%c%c",
|
||||
(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
||||
}
|
||||
if(!sws_isSupportedOutput(out_pf)) {
|
||||
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
||||
if ( !sws_isSupportedOutput(out_pf) ) {
|
||||
Warning("swscale does not support the output format: %c%c%c%c",
|
||||
(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -186,7 +192,7 @@ int SWScale::Convert(
|
|||
#else
|
||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||
#endif
|
||||
if(insize != in_buffer_size) {
|
||||
if ( insize != in_buffer_size ) {
|
||||
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
||||
return -4;
|
||||
}
|
||||
|
|
|
@ -113,11 +113,16 @@ void Zone::Setup(
|
|||
}
|
||||
|
||||
if ( config.record_diag_images ) {
|
||||
if ( config.record_diag_images_fifo ) {
|
||||
snprintf(diag_path, sizeof(diag_path),
|
||||
config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg",
|
||||
monitor->getStorage()->Path(), id);
|
||||
if ( config.record_diag_images_fifo )
|
||||
"%s/diagpipe-%d-poly.jpg",
|
||||
staticConfig.PATH_SOCKS.c_str(), id);
|
||||
|
||||
FifoStream::fifo_create_if_missing(diag_path);
|
||||
} else {
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg",
|
||||
monitor->getStorage()->Path(), id);
|
||||
}
|
||||
pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
|
||||
} else {
|
||||
diag_path[0] = 0;
|
||||
|
@ -139,10 +144,11 @@ void Zone::RecordStats(const Event *event) {
|
|||
"INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d",
|
||||
monitor->Id(), id, event->Id(), event->Frames(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score
|
||||
);
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
int rc = mysql_query(&dbconn, sql);
|
||||
db_mutex.unlock();
|
||||
if ( rc ) {
|
||||
Error("Can't insert event stats: %s", mysql_error(&dbconn));
|
||||
}
|
||||
db_mutex.unlock();
|
||||
} // end void Zone::RecordStats( const Event *event )
|
||||
|
||||
bool Zone::CheckOverloadCount() {
|
||||
|
@ -904,7 +910,7 @@ int Zone::Load(Monitor *monitor, Zone **&zones) {
|
|||
zones[i] = new Zone(monitor, Id, Name, polygon);
|
||||
} else if ( atoi(dbrow[2]) == Zone::PRIVACY ) {
|
||||
zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon);
|
||||
}
|
||||
} else {
|
||||
zones[i] = new Zone(
|
||||
monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB,
|
||||
(Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold,
|
||||
|
@ -912,6 +918,7 @@ int Zone::Load(Monitor *monitor, Zone **&zones) {
|
|||
MinFilterPixels, MaxFilterPixels,
|
||||
MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs,
|
||||
OverloadFrames, ExtendAlarmFrames);
|
||||
}
|
||||
} // end foreach row
|
||||
mysql_free_result(result);
|
||||
return n_zones;
|
||||
|
|
15
src/zma.cpp
15
src/zma.cpp
|
@ -106,9 +106,9 @@ int main( int argc, char *argv[] ) {
|
|||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
if ( optind < argc ) {
|
||||
fprintf(stderr, "Extraneous options, ");
|
||||
while (optind < argc)
|
||||
while ( optind < argc )
|
||||
printf("%s ", argv[optind++]);
|
||||
printf("\n");
|
||||
Usage();
|
||||
|
@ -130,7 +130,7 @@ int main( int argc, char *argv[] ) {
|
|||
hwcaps_detect();
|
||||
|
||||
Monitor *monitor = Monitor::Load(id, true, Monitor::ANALYSIS);
|
||||
zmFifoDbgInit( monitor );
|
||||
zmFifoDbgInit(monitor);
|
||||
|
||||
if ( monitor ) {
|
||||
Info("In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled());
|
||||
|
@ -148,7 +148,14 @@ int main( int argc, char *argv[] ) {
|
|||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time(nullptr);
|
||||
|
||||
while( (!zm_terminate) && monitor->ShmValid() ) {
|
||||
while ( !zm_terminate ) {
|
||||
if ( !monitor->ShmValid() ) {
|
||||
monitor->disconnect();
|
||||
Info("Waiting for shm to become valid");
|
||||
usleep(100000);
|
||||
monitor->connect();
|
||||
continue;
|
||||
}
|
||||
// Process the next image
|
||||
sigprocmask(SIG_BLOCK, &block_set, nullptr);
|
||||
|
||||
|
|
13
src/zms.cpp
13
src/zms.cpp
|
@ -102,9 +102,7 @@ int main(int argc, const char *argv[], char **envp) {
|
|||
|
||||
Debug(1, "Query: %s", query);
|
||||
|
||||
char temp_query[1024];
|
||||
strncpy(temp_query, query, sizeof(temp_query)-1);
|
||||
char *q_ptr = temp_query;
|
||||
char *q_ptr = (char *)query;
|
||||
char *parms[16]; // Shouldn't be more than this
|
||||
int parm_no = 0;
|
||||
while ( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) {
|
||||
|
@ -118,9 +116,13 @@ int main(int argc, const char *argv[], char **envp) {
|
|||
if ( !value )
|
||||
value = "";
|
||||
if ( !strcmp(name, "source") ) {
|
||||
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
||||
if ( !strcmp(value, "fifo") )
|
||||
if ( !strcmp(value, "event") ) {
|
||||
source = ZMS_EVENT;
|
||||
} else if ( !strcmp(value, "fifo") ) {
|
||||
source = ZMS_FIFO;
|
||||
} else {
|
||||
source = ZMS_MONITOR;
|
||||
}
|
||||
} else if ( !strcmp(name, "mode") ) {
|
||||
mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG;
|
||||
mode = !strcmp(value, "raw")?ZMS_RAW:mode;
|
||||
|
@ -329,6 +331,7 @@ int main(int argc, const char *argv[], char **envp) {
|
|||
Error("Neither a monitor or event was specified.");
|
||||
} // end if monitor or event
|
||||
|
||||
Debug(1, "Terminating");
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
$message = '';
|
||||
$data = array();
|
||||
|
||||
|
@ -16,7 +15,7 @@ if ( empty($_REQUEST['task']) ) {
|
|||
}
|
||||
|
||||
if ( empty($_REQUEST['eids']) ) {
|
||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != "query" ) $message = 'No event id(s) supplied';
|
||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No event id(s) supplied';
|
||||
} else {
|
||||
$eids = $_REQUEST['eids'];
|
||||
}
|
||||
|
@ -26,6 +25,7 @@ if ( $message ) {
|
|||
return;
|
||||
}
|
||||
|
||||
require_once('includes/Filter.php');
|
||||
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter();
|
||||
if ( $user['MonitorIds'] ) {
|
||||
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
||||
|
@ -58,7 +58,8 @@ if ( isset($_REQUEST['offset']) ) {
|
|||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||
|
||||
// Limit specifies the number of rows to return
|
||||
$limit = 100;
|
||||
// Set the default to 0 for events view, to prevent an issue with ALL pagination
|
||||
$limit = 0;
|
||||
if ( isset($_REQUEST['limit']) ) {
|
||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||
|
@ -115,6 +116,7 @@ function deleteRequest($eid) {
|
|||
}
|
||||
|
||||
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
|
||||
|
||||
$data = array(
|
||||
'total' => 0,
|
||||
'totalNotFiltered' => 0,
|
||||
|
@ -143,50 +145,15 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
$sort = 'Id';
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$query = array();
|
||||
$query['values'] = array();
|
||||
$values = array();
|
||||
$likes = array();
|
||||
$where = ($filter->sql()?'('.$filter->sql().')' : '');
|
||||
// There are two search bars in the log view, normal and advanced
|
||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||
// Alternatively we could try to do both
|
||||
if ( count($advsearch) ) {
|
||||
|
||||
foreach ( $advsearch as $col=>$text ) {
|
||||
if ( in_array($col, $columns) ) {
|
||||
array_push($likes, 'E.'.$col.' LIKE ?');
|
||||
array_push($query['values'], $text);
|
||||
} else if ( in_array($col, $col_alt) ) {
|
||||
array_push($likes, 'M.'.$col.' LIKE ?');
|
||||
array_push($query['values'], $text);
|
||||
} else {
|
||||
ZM\Error("'$col' is not a sortable column name");
|
||||
continue;
|
||||
}
|
||||
} # end foreach col in advsearch
|
||||
$wherevalues = $query['values'];
|
||||
$where .= ($where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes);
|
||||
|
||||
} else if ( $search != '' ) {
|
||||
|
||||
$search = '%' .$search. '%';
|
||||
foreach ( $columns as $col ) {
|
||||
array_push($likes, 'E.'.$col.' LIKE ?');
|
||||
array_push($query['values'], $search);
|
||||
}
|
||||
$wherevalues = $query['values'];
|
||||
$where .= ( $where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes);
|
||||
}
|
||||
if ( $where )
|
||||
$where = ' WHERE '.$where;
|
||||
$where = $filter->sql()?' WHERE ('.$filter->sql().')' : '';
|
||||
|
||||
$sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort;
|
||||
$col_str = 'E.*, M.Name AS Monitor';
|
||||
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
|
||||
array_push($query['values'], $offset, $limit);
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY '.$sort.' '.$order;
|
||||
|
||||
//ZM\Debug('Calling the following sql query: ' .$query['sql']);
|
||||
//ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
|
||||
$storage_areas = ZM\Storage::find();
|
||||
$StorageById = array();
|
||||
|
@ -194,13 +161,61 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
$StorageById[$S->Id()] = $S;
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
|
||||
$unfiltered_rows = array();
|
||||
$event_ids = array();
|
||||
|
||||
foreach ( dbFetchAll($sql, NULL, $values) as $row ) {
|
||||
$event = new ZM\Event($row);
|
||||
if ( !$filter->test_post_sql_conditions($event) ) {
|
||||
$event->remove_from_cache();
|
||||
continue;
|
||||
}
|
||||
$event_ids[] = $event->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
}
|
||||
|
||||
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
||||
|
||||
$filtered_rows = null;
|
||||
|
||||
if ( count($advsearch) or $search != '' ) {
|
||||
$search_filter = new ZM\Filter();
|
||||
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids));
|
||||
|
||||
// There are two search bars in the log view, normal and advanced
|
||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||
// Alternatively we could try to do both
|
||||
if ( count($advsearch) ) {
|
||||
$terms = array();
|
||||
foreach ( $advsearch as $col=>$text ) {
|
||||
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
|
||||
} # end foreach col in advsearch
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter->addTerms($terms);
|
||||
} else if ( $search != '' ) {
|
||||
$search = '%' .$search. '%';
|
||||
$terms = array();
|
||||
foreach ( $columns as $col ) {
|
||||
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
|
||||
}
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[0]['cnj'] = 'and';
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR'));
|
||||
} # end if search
|
||||
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order;
|
||||
$filtered_rows = dbFetchAll($sql);
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter.');
|
||||
} else {
|
||||
$filtered_rows = $unfiltered_rows;
|
||||
} # end if search_filter->terms() > 1
|
||||
|
||||
$returned_rows = array();
|
||||
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
|
||||
$event = new ZM\Event($row);
|
||||
|
||||
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
|
||||
$imgSrc = $event->getThumbnailSrc(array(),'&');
|
||||
$streamSrc = $event->getStreamSrc(array(
|
||||
|
@ -218,14 +233,15 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
$row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default';
|
||||
$row['Notes'] = nl2br(htmlspecialchars($row['Notes']));
|
||||
$row['DiskSpace'] = human_filesize($event->DiskSpace());
|
||||
$rows[] = $row;
|
||||
}
|
||||
$data['rows'] = $rows;
|
||||
$returned_rows[] = $row;
|
||||
} # end foreach row matching search
|
||||
|
||||
$data['rows'] = $returned_rows;
|
||||
|
||||
# totalNotFiltered must equal total, except when either search bar has been used
|
||||
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'. ($filter->sql() ? ' WHERE '.$filter->sql():''), 'Total');
|
||||
$data['totalNotFiltered'] = count($unfiltered_rows);
|
||||
if ( $search != '' || count($advsearch) ) {
|
||||
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where , 'Total', $wherevalues);
|
||||
$data['total'] = count($filtered_rows);
|
||||
} else {
|
||||
$data['total'] = $data['totalNotFiltered'];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
$data = array();
|
||||
$message = '';
|
||||
|
||||
//
|
||||
// INITIALIZE AND CHECK SANITY
|
||||
//
|
||||
|
||||
if ( !canView('Events') ) $message = 'Insufficient permissions to view frames for user '.$user['Username'];
|
||||
|
||||
// task must be set
|
||||
if ( empty($_REQUEST['task']) ) {
|
||||
$message = 'This request requires a task to be set';
|
||||
// query is the only supported task at the moment
|
||||
} else if ( $_REQUEST['task'] != 'query' ) {
|
||||
$message = 'Unrecognised task '.$_REQUEST['task'];
|
||||
} else {
|
||||
$task = $_REQUEST['task'];
|
||||
}
|
||||
|
||||
if ( empty($_REQUEST['eid']) ) {
|
||||
$message = 'No event id supplied';
|
||||
} else {
|
||||
$eid = validInt($_REQUEST['eid']);
|
||||
}
|
||||
|
||||
if ( $message ) {
|
||||
ajaxError($message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search contains a user entered string to search on
|
||||
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
||||
|
||||
// Advanced search contains an array of "column name" => "search text" pairs
|
||||
// Bootstrap table sends json_ecoded array, which we must decode
|
||||
$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
|
||||
|
||||
// Sort specifies the name of the column to sort on
|
||||
$sort = 'FrameId';
|
||||
if ( isset($_REQUEST['sort']) ) {
|
||||
$sort = $_REQUEST['sort'];
|
||||
}
|
||||
|
||||
// Offset specifies the starting row to return, used for pagination
|
||||
$offset = 0;
|
||||
if ( isset($_REQUEST['offset']) ) {
|
||||
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||
} else {
|
||||
$offset = $_REQUEST['offset'];
|
||||
}
|
||||
}
|
||||
|
||||
// Order specifies the sort direction, either asc or desc
|
||||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||
|
||||
// Limit specifies the number of rows to return
|
||||
$limit = 0;
|
||||
if ( isset($_REQUEST['limit']) ) {
|
||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||
} else {
|
||||
$limit = $_REQUEST['limit'];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MAIN LOOP
|
||||
//
|
||||
|
||||
// Only one supported task at the moment
|
||||
switch ( $task ) {
|
||||
case 'query' :
|
||||
$data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit);
|
||||
break;
|
||||
default :
|
||||
ZM\Fatal("Unrecognised task '$task'");
|
||||
} // end switch task
|
||||
|
||||
ajaxResponse($data);
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS
|
||||
//
|
||||
|
||||
function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) {
|
||||
|
||||
$data = array(
|
||||
'total' => 0,
|
||||
'totalNotFiltered' => 0,
|
||||
'rows' => array(),
|
||||
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)
|
||||
);
|
||||
|
||||
// The names of the dB columns in the events table we are interested in
|
||||
$columns = array('FrameId', 'Type', 'TimeStamp', 'Delta', 'Score');
|
||||
|
||||
if ( !in_array($sort, $columns) ) {
|
||||
ZM\Error('Invalid sort field: ' . $sort);
|
||||
$sort = 'FrameId';
|
||||
}
|
||||
|
||||
$Event = new ZM\Event($eid);
|
||||
$Monitor = $Event->Monitor();
|
||||
$values = array();
|
||||
$likes = array();
|
||||
$where = 'WHERE EventId = '.$eid;
|
||||
|
||||
$sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order;
|
||||
|
||||
//ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
|
||||
$unfiltered_rows = array();
|
||||
$frame_ids = array();
|
||||
require_once('includes/Frame.php');
|
||||
foreach ( dbFetchAll($sql, NULL, $values) as $row ) {
|
||||
$frame = new ZM\Frame($row);
|
||||
$frame_ids[] = $frame->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
}
|
||||
|
||||
ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.');
|
||||
|
||||
$filtered_rows = null;
|
||||
require_once('includes/Filter.php');
|
||||
if ( count($advsearch) or $search != '' ) {
|
||||
$search_filter = new ZM\Filter();
|
||||
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'FrameId', 'op'=>'IN', 'val'=>$frame_ids));
|
||||
|
||||
// There are two search bars in the log view, normal and advanced
|
||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||
// Alternatively we could try to do both
|
||||
if ( count($advsearch) ) {
|
||||
$terms = array();
|
||||
foreach ( $advsearch as $col=>$text ) {
|
||||
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
|
||||
} # end foreach col in advsearch
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter->addTerms($terms);
|
||||
} else if ( $search != '' ) {
|
||||
$search = '%' .$search. '%';
|
||||
$terms = array();
|
||||
foreach ( $columns as $col ) {
|
||||
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
|
||||
}
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[0]['cnj'] = 'and';
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR'));
|
||||
} # end if search
|
||||
|
||||
$sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order;
|
||||
$filtered_rows = dbFetchAll($sql);
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.');
|
||||
} else {
|
||||
$filtered_rows = $unfiltered_rows;
|
||||
} # end if search_filter->terms() > 1
|
||||
|
||||
$returned_rows = array();
|
||||
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
|
||||
if ( ZM_WEB_LIST_THUMBS ) {
|
||||
$base_img_src = '?view=image&fid=' .$row['Id'];
|
||||
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
|
||||
$thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : '';
|
||||
$thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"';
|
||||
$thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg';
|
||||
$img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn)));
|
||||
$full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn)));
|
||||
$frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId'];
|
||||
|
||||
$row['Thumbnail'] = '<img src="' .$img_src. '" '.$thmb_width. ' ' .$thmb_height. 'img_src="' .$img_src. '" full_img_src="' .$full_img_src. '">';
|
||||
}
|
||||
$returned_rows[] = $row;
|
||||
} # end foreach row matching search
|
||||
|
||||
$data['rows'] = $returned_rows;
|
||||
|
||||
# totalNotFiltered must equal total, except when either search bar has been used
|
||||
$data['totalNotFiltered'] = count($unfiltered_rows);
|
||||
if ( $search != '' || count($advsearch) ) {
|
||||
$data['total'] = count($filtered_rows);
|
||||
} else {
|
||||
$data['total'] = $data['totalNotFiltered'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
if ( !canEdit('Events') ) return;
|
||||
|
||||
$eid = isset($_REQUEST['eid']) ? $_REQUEST['eid'] : '';
|
||||
$eid = validInt($eid);
|
||||
$Event = new ZM\Event($eid);
|
||||
|
||||
?>
|
||||
<div class="modal" id="eventRenameModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><?php echo translate('Rename') .' '. translate('Event') ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="text" value="<?php echo validHtmlStr($Event->Name()) ?>"/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="eventRenameBtn"><?php echo translate('Save') ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -31,12 +31,11 @@
|
|||
'Shallow' => translate('Shallow'),
|
||||
);
|
||||
|
||||
$servers = ZM\Server::find( null, array('order'=>'lower(Name)') );
|
||||
global $Servers;
|
||||
$ServersById = array();
|
||||
foreach ( $servers as $S ) {
|
||||
foreach ( $Servers as $S ) {
|
||||
$ServersById[$S->Id()] = $S;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="modal fade" id="storageModal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
|
@ -56,48 +55,54 @@
|
|||
<input type="hidden" name="view" value="storage"/>
|
||||
<input type="hidden" name="object" value="storage"/>
|
||||
<input type="hidden" name="id" value="<?php echo validHtmlStr($sid) ?>"/>
|
||||
<table class="major table-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="major table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Name') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Name') ?></th>
|
||||
<td><input type="text" name="newStorage[Name]" value="<?php echo $newStorage->Name() ?>"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Path') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Path') ?></th>
|
||||
<td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage->Path() ?>"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Url') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Url') ?></th>
|
||||
<td><input type="text" name="newStorage[Url]" value="<?php echo $newStorage->Url() ?>"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Server') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Server') ?></th>
|
||||
<td><?php echo htmlSelect('newStorage[ServerId]', array(''=>'Remote / No Specific Server') + $ServersById, $newStorage->ServerId()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Type') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Type') ?></th>
|
||||
<td><?php echo htmlSelect('newStorage[Type]', $type_options, $newStorage->Type()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('StorageScheme') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('StorageScheme') ?></th>
|
||||
<td><?php echo htmlSelect('newStorage[Scheme]', $scheme_options, $newStorage->Scheme()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('StorageDoDelete') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('StorageDoDelete') ?></th>
|
||||
<td>
|
||||
<input type="radio" name="newStorage[DoDelete]" value="1" <?php echo $newStorage->DoDelete() ? $checked : $null ?>>Yes
|
||||
<input type="radio" name="newStorage[DoDelete]" value="0" <?php echo $newStorage->DoDelete() ? $null : $checked ?>>No
|
||||
<input type="radio" name="newStorage[DoDelete]" id="newStorage[DoDelete]1" value="1" <?php echo $newStorage->DoDelete() ? $checked : $null ?>/>
|
||||
<label class="form-check-label" for="newStorage[DoDelete]1">Yes</label>
|
||||
<input type="radio" name="newStorage[DoDelete]" id="newStorage[DoDelete]0" value="0" <?php echo $newStorage->DoDelete() ? $null : $checked ?>/>
|
||||
<label class="form-check-label" for="newStorage[DoDelete]0">No</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right pr-3" scope="row"><?php echo translate('Enabled') ?></th>
|
||||
<th class="text-right " scope="row"><?php echo translate('Enabled') ?></th>
|
||||
<td>
|
||||
<input type="radio" name="newStorage[Enabled]" value="1" <?php echo $newStorage->Enabled() ? $checked : $null ?>>Yes
|
||||
<input type="radio" name="newStorage[Enabled]" value="0" <?php echo $newStorage->Enabled() ? $null : $checked ?>>No
|
||||
<input type="radio" name="newStorage[Enabled]" id="newStorage[Enabled]1" value="1" <?php echo $newStorage->Enabled() ? $checked : $null ?>/>
|
||||
<label class="form-check-label" for="newStorage[Enabled]1">Yes</label>
|
||||
<input type="radio" name="newStorage[Enabled]" id="newStorage[Enabled]0" value="0" <?php echo $newStorage->Enabled() ? $null : $checked ?>/>
|
||||
<label class="form-check-label" for="newStorage[Enabled]0">No</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button name="action" id="storageSubmitBtn" type="submit" class="btn btn-primary" value="Save"><?php echo translate('Save') ?></button>
|
||||
|
|
|
@ -267,7 +267,6 @@ function collectData() {
|
|||
|
||||
if ( count($fieldSql) ) {
|
||||
$sql = 'SELECT '.join(', ', $fieldSql).' FROM '.$entitySpec['table'];
|
||||
#$sql = 'SELECT '.join(', ', array_map($fieldSql, function($f){return '`'.$f.'`';})).' FROM '.$entitySpec['table'];
|
||||
if ( $joinSql )
|
||||
$sql .= ' '.join(' ', array_unique($joinSql));
|
||||
if ( $id && !empty($entitySpec['selector']) ) {
|
||||
|
@ -314,27 +313,27 @@ function collectData() {
|
|||
$limit = $entitySpec['limit'];
|
||||
elseif ( !empty($_REQUEST['count']) )
|
||||
$limit = validInt($_REQUEST['count']);
|
||||
$limit_offset='';
|
||||
$limit_offset = '';
|
||||
if ( !empty($_REQUEST['offset']) )
|
||||
$limit_offset = validInt($_REQUEST['offset']) . ', ';
|
||||
if ( !empty( $limit ) )
|
||||
if ( !empty($limit) )
|
||||
$sql .= ' limit '.$limit_offset.$limit;
|
||||
if ( isset($limit) && $limit == 1 ) {
|
||||
if ( $sqlData = dbFetchOne($sql, NULL, $values) ) {
|
||||
foreach ( $postFuncs as $element=>$func )
|
||||
$sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' );
|
||||
$data = array_merge( $data, $sqlData );
|
||||
$data = array_merge($data, $sqlData);
|
||||
}
|
||||
} else {
|
||||
$count = 0;
|
||||
foreach( dbFetchAll( $sql, NULL, $values ) as $sqlData ) {
|
||||
foreach ( dbFetchAll($sql, NULL, $values) as $sqlData ) {
|
||||
foreach ( $postFuncs as $element=>$func )
|
||||
$sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' );
|
||||
$sqlData[$element] = eval('return( '.$func.'( $sqlData ) );');
|
||||
$data[] = $sqlData;
|
||||
if ( isset($limi) && ++$count >= $limit )
|
||||
if ( isset($limit) && ++$count >= $limit )
|
||||
break;
|
||||
}
|
||||
}
|
||||
} # end foreach
|
||||
} # end if have limit == 1
|
||||
}
|
||||
}
|
||||
#ZM\Debug(print_r($data, true));
|
||||
|
@ -347,11 +346,11 @@ if ( !isset($_REQUEST['layout']) ) {
|
|||
$_REQUEST['layout'] = 'json';
|
||||
}
|
||||
|
||||
switch( $_REQUEST['layout'] ) {
|
||||
switch ( $_REQUEST['layout'] ) {
|
||||
case 'xml NOT CURRENTLY SUPPORTED' :
|
||||
header('Content-type: application/xml');
|
||||
echo('<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
');
|
||||
');
|
||||
echo '<'.strtolower($_REQUEST['entity']).'>
|
||||
';
|
||||
foreach ( $data as $key=>$value ) {
|
||||
|
@ -365,15 +364,16 @@ switch( $_REQUEST['layout'] ) {
|
|||
$response = array( strtolower(validJsStr($_REQUEST['entity'])) => $data );
|
||||
if ( isset($_REQUEST['loopback']) )
|
||||
$response['loopback'] = validJsStr($_REQUEST['loopback']);
|
||||
#ZM\Warning(print_r($response, true));
|
||||
ajaxResponse($response);
|
||||
break;
|
||||
}
|
||||
case 'text' :
|
||||
header('Content-type: text/plain' );
|
||||
echo join( ' ', array_values( $data ) );
|
||||
header('Content-type: text/plain');
|
||||
echo join(' ', array_values($data));
|
||||
break;
|
||||
default:
|
||||
ZM\Error('Unsupported layout: '. $_REQUEST['layout']);
|
||||
ZM\Error('Unsupported layout: '.$_REQUEST['layout']);
|
||||
}
|
||||
|
||||
function getFrameImage() {
|
||||
|
@ -381,38 +381,38 @@ function getFrameImage() {
|
|||
$frameId = $_REQUEST['id'][1];
|
||||
|
||||
$sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?';
|
||||
if ( !($frame = dbFetchOne( $sql, NULL, array($eventId, $frameId ) )) ) {
|
||||
if ( !($frame = dbFetchOne($sql, NULL, array($eventId, $frameId))) ) {
|
||||
$frame = array();
|
||||
$frame['EventId'] = $eventId;
|
||||
$frame['FrameId'] = $frameId;
|
||||
$frame['Type'] = 'Virtual';
|
||||
}
|
||||
$event = dbFetchOne( 'select * from Events where Id = ?', NULL, array( $frame['EventId'] ) );
|
||||
$frame['Image'] = getImageSrc( $event, $frame, SCALE_BASE );
|
||||
return( $frame );
|
||||
$event = dbFetchOne('SELECT * FROM Events WHERE Id = ?', NULL, array($frame['EventId']));
|
||||
$frame['Image'] = getImageSrc($event, $frame, SCALE_BASE);
|
||||
return $frame;
|
||||
}
|
||||
|
||||
function getNearFrame() {
|
||||
$eventId = $_REQUEST['id'][0];
|
||||
$frameId = $_REQUEST['id'][1];
|
||||
|
||||
$sql = 'select FrameId from Frames where EventId = ? and FrameId <= ? order by FrameId desc limit 1';
|
||||
if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId', array( $eventId, $frameId ) ) ) {
|
||||
$sql = 'select * from Frames where EventId = ? and FrameId > ? order by FrameId asc limit 1';
|
||||
if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId', array( $eventId, $frameId ) ) ) {
|
||||
$sql = 'SELECT FrameId FROM Frames WHERE EventId = ? AND FrameId <= ? ORDER BY FrameId DESC LIMIT 1';
|
||||
if ( !$nearFrameId = dbFetchOne($sql, 'FrameId', array($eventId, $frameId)) ) {
|
||||
$sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId > ? ORDER BY FrameId ASC LIMIT 1';
|
||||
if ( !$nearFrameId = dbFetchOne($sql, 'FrameId', array($eventId, $frameId)) ) {
|
||||
return( array() );
|
||||
}
|
||||
}
|
||||
$_REQUEST['entity'] = 'frame';
|
||||
$_REQUEST['id'][1] = $nearFrameId;
|
||||
return( collectData() );
|
||||
return collectData();
|
||||
}
|
||||
|
||||
function getNearEvents() {
|
||||
global $user, $sortColumn, $sortOrder;
|
||||
|
||||
$eventId = $_REQUEST['id'];
|
||||
$NearEvents = array( 'EventId'=>$eventId );
|
||||
$NearEvents = array('EventId'=>$eventId);
|
||||
|
||||
$event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, array($eventId));
|
||||
if ( !$event ) return $NearEvents;
|
||||
|
@ -423,7 +423,8 @@ function getNearEvents() {
|
|||
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
||||
}
|
||||
|
||||
# When listing, it may make sense to list them in descending order. But when viewing Prev should timewise earlier and Next should be after.
|
||||
# When listing, it may make sense to list them in descending order.
|
||||
# But when viewing Prev should timewise earlier and Next should be after.
|
||||
if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartDateTime' ) {
|
||||
$sortOrder = 'ASC';
|
||||
}
|
||||
|
@ -470,6 +471,6 @@ function getNearEvents() {
|
|||
$NearEvents['NextEventId'] = $NearEvents['NextEventStartTime'] = $NearEvents['NextEventDefVideoPath'] = 0;
|
||||
}
|
||||
return $NearEvents;
|
||||
}
|
||||
} # end function getNearEvents()
|
||||
|
||||
?>
|
||||
|
|
|
@ -116,23 +116,23 @@ class GroupsController extends AppController {
|
|||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||
return;
|
||||
}
|
||||
$this->Group->id = $id;
|
||||
if ( $this->Group->save($this->request->data) ) {
|
||||
return $this->flash(
|
||||
__('The group has been saved.'),
|
||||
array('action' => 'index')
|
||||
);
|
||||
$message = 'Saved';
|
||||
} else {
|
||||
$message = 'Error';
|
||||
// if there is a validation message, use it
|
||||
if ( !$this->group->validates() ) {
|
||||
$message .= ': '.$this->Group->validationErrors;
|
||||
}
|
||||
} else {
|
||||
$options = array('conditions' => array('Group.' . $this->Group->primaryKey => $id));
|
||||
$this->request->data = $this->Group->find('first', $options);
|
||||
}
|
||||
$monitors = $this->Group->Monitor->find('list');
|
||||
} # end if post/put
|
||||
|
||||
$group = $this->Group->findById($id);
|
||||
$this->set(array(
|
||||
'message' => $message,
|
||||
'monitors'=> $monitors,
|
||||
'_serialize' => array('message')
|
||||
'group' => $group,
|
||||
'_serialize' => array('group')
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -237,6 +237,15 @@ class FilterTerm {
|
|||
case 'StartDateTime':
|
||||
$sql .= 'E.StartDateTime';
|
||||
break;
|
||||
case 'FrameId':
|
||||
$sql .= 'Id';
|
||||
break;
|
||||
case 'Type':
|
||||
case 'TimeStamp':
|
||||
case 'Delta':
|
||||
case 'Score':
|
||||
$sql .= $this->attr;
|
||||
break;
|
||||
case 'FramesEventId':
|
||||
$sql .= 'F.EventId';
|
||||
break;
|
||||
|
@ -419,6 +428,12 @@ class FilterTerm {
|
|||
|
||||
public static function is_valid_attr($attr) {
|
||||
$attrs = array(
|
||||
'Score',
|
||||
'Delta',
|
||||
'TimeStamp',
|
||||
'Type',
|
||||
'FrameId',
|
||||
'EventId',
|
||||
'ExistsInFileSystem',
|
||||
'Emailed',
|
||||
'DiskSpace',
|
||||
|
@ -434,6 +449,7 @@ class FilterTerm {
|
|||
'Time',
|
||||
'Weekday',
|
||||
'StartDateTime',
|
||||
'FramesId',
|
||||
'FramesEventId',
|
||||
'StartDate',
|
||||
'StartTime',
|
||||
|
|
|
@ -11,16 +11,8 @@ class ZM_Object {
|
|||
|
||||
$row = NULL;
|
||||
if ( $IdOrRow ) {
|
||||
global $object_cache;
|
||||
if ( ! isset($object_cache[$class]) ) {
|
||||
$object_cache[$class] = array();
|
||||
}
|
||||
$cache = &$object_cache[$class];
|
||||
|
||||
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
|
||||
if ( isset($cache[$IdOrRow]) ) {
|
||||
return $cache[$IdOrRow];
|
||||
}
|
||||
$table = $class::$table;
|
||||
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
|
||||
if ( !$row ) {
|
||||
|
@ -34,6 +26,11 @@ class ZM_Object {
|
|||
foreach ($row as $k => $v) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
global $object_cache;
|
||||
if ( ! isset($object_cache[$class]) ) {
|
||||
$object_cache[$class] = array();
|
||||
}
|
||||
$cache = &$object_cache[$class];
|
||||
$cache[$row['Id']] = $this;
|
||||
}
|
||||
} # end if isset($IdOrRow)
|
||||
|
|
|
@ -71,7 +71,9 @@ if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) {
|
|||
$monitor->sendControlCommand('quit');
|
||||
}
|
||||
} // end if changes
|
||||
$redirect = $_SERVER['HTTP_REFERER'];
|
||||
# HTTP_REFERER will typically be ?view=zone so no good.
|
||||
# if a referer is passed in $_REQUEST then use it otherwise go to ?view=zones
|
||||
$redirect = isset($_REQUEST['REFERER']) ? $_REQUEST['REFERER'] : '?view=zones';
|
||||
} // end if action
|
||||
} // end if $mid and canEdit($mid)
|
||||
?>
|
||||
|
|
|
@ -128,7 +128,7 @@ function dbEscape( $string ) {
|
|||
return $dbConn->quote($string);
|
||||
}
|
||||
|
||||
function dbQuery($sql, $params=NULL) {
|
||||
function dbQuery($sql, $params=NULL, $debug = false) {
|
||||
global $dbConn;
|
||||
if ( dbLog($sql, true) )
|
||||
return;
|
||||
|
@ -145,7 +145,7 @@ function dbQuery($sql, $params=NULL) {
|
|||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if ( defined('ZM_DB_DEBUG') ) {
|
||||
if ( defined('ZM_DB_DEBUG') or $debug ) {
|
||||
ZM\Debug("SQL: $sql values:" . ($params?implode(',',$params):''));
|
||||
}
|
||||
$result = $dbConn->query($sql);
|
||||
|
@ -154,7 +154,7 @@ function dbQuery($sql, $params=NULL) {
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
if ( defined('ZM_DB_DEBUG') ) {
|
||||
if ( defined('ZM_DB_DEBUG') or $debug ) {
|
||||
ZM\Debug('SQL: '.$sql.' '.($params?implode(',',$params):'').' rows: '.$result->rowCount());
|
||||
}
|
||||
} catch(PDOException $e) {
|
||||
|
@ -189,13 +189,13 @@ function dbFetchOne($sql, $col=false, $params=NULL) {
|
|||
}
|
||||
|
||||
function dbFetchAll($sql, $col=false, $params=NULL) {
|
||||
$dbRows = array();
|
||||
$result = dbQuery($sql, $params);
|
||||
if ( ! $result ) {
|
||||
ZM\Error("SQL-ERR dbFetchAll no result, statement was '".$sql."'".($params ? 'params: '.join(',', $params) : ''));
|
||||
return false;
|
||||
return $dbRows;
|
||||
}
|
||||
|
||||
$dbRows = array();
|
||||
while ( $dbRow = $result->fetch(PDO::FETCH_ASSOC) )
|
||||
$dbRows[] = $col ? $dbRow[$col] : $dbRow;
|
||||
return $dbRows;
|
||||
|
|
|
@ -1003,10 +1003,18 @@ function parseSort($saveToSession=false, $querySep='&') {
|
|||
$sortColumn = 'E.DiskSpace';
|
||||
break;
|
||||
case 'StartTime' :
|
||||
# legacy
|
||||
$_REQUEST['sort_field'] = 'StartDateTime';
|
||||
$sortColumn = 'E.StartDateTime';
|
||||
break;
|
||||
case 'StartDateTime' :
|
||||
$sortColumn = 'E.StartDateTime';
|
||||
break;
|
||||
case 'EndTime' :
|
||||
#legacy
|
||||
$_REQUEST['sort_field'] = 'EndDateTime';
|
||||
$sortColumn = 'E.EndDateTime';
|
||||
break;
|
||||
case 'EndDateTime' :
|
||||
$sortColumn = 'E.EndDateTime';
|
||||
break;
|
||||
|
|
|
@ -728,23 +728,11 @@ li.search-choice {
|
|||
}
|
||||
|
||||
.zoom {
|
||||
padding: 50px;
|
||||
transition: transform .2s; /* Animation */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.zoom:hover {
|
||||
transform-origin: 70% 50%;
|
||||
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
|
||||
}
|
||||
|
||||
.zoom-right {
|
||||
padding: 0px;
|
||||
transition: transform .2s; /* Animation */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.zoom-right:hover {
|
||||
.zoom-console {
|
||||
transform-origin: 0% 50%;
|
||||
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
|
||||
}
|
||||
|
|
|
@ -26,8 +26,28 @@ input.large {
|
|||
}
|
||||
|
||||
input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SITEKEY]"],
|
||||
input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"] {
|
||||
width: 100%;
|
||||
input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"],
|
||||
input[name="newConfig[ZM_OPT_GEOLOCATION_TILE_PROVIDER]"],
|
||||
input[name="newConfig[ZM_OPT_GEOLOCATION_ACCESS_TOKEN]"],
|
||||
input[name="newConfig[ZM_AUTH_HASH_SECRET]"],
|
||||
input[name="newConfig[ZM_UPDATE_CHECK_PROXY]"],
|
||||
input[name="newConfig[ZM_WEB_TITLE]"],
|
||||
input[name="newConfig[ZM_WEB_TITLE_PREFIX]"],
|
||||
input[name="newConfig[ZM_HOME_URL]"],
|
||||
input[name="newConfig[ZM_HOME_CONTENT]"],
|
||||
input[name="newConfig[ZM_WEB_CONSOLE_BANNER]"],
|
||||
input[name="newConfig[ZM_LOG_DEBUG_TARGET]"],
|
||||
input[name="newConfig[ZM_LOG_DEBUG_FILE]"],
|
||||
input[name="newConfig[ZM_MESSAGE_ADDRESS]"],
|
||||
input[name="newConfig[ZM_MESSAGE_SUBJECT]"],
|
||||
input[name="newConfig[ZM_FROM_EMAIL]"],
|
||||
input[name="newConfig[ZM_URL]"],
|
||||
input[name="newConfig[ZM_SSMTP_PATH]"],
|
||||
input[name="newConfig[ZM_CSP_REPORT_URI]"],
|
||||
input[name="newStorage[Name]"],
|
||||
input[name="newStorage[Path]"],
|
||||
input[name="newStorage[Url]"] {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#options label {
|
||||
|
|
|
@ -571,3 +571,6 @@ li.search-choice {
|
|||
}
|
||||
/* end chosen override */
|
||||
|
||||
modal-content {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ function exportEventDetail($event, $exportFrames, $exportImages) {
|
|||
<tr><th scope="row"><?php echo translate('Monitor') ?></th><td><?php echo validHtmlStr($event->Monitor()->Name()) ?> (<?php echo $event->MonitorId() ?>)</td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Cause') ?></th><td><?php echo validHtmlStr($event->Cause()) ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Notes') ?></th><td><?php echo validHtmlStr($event->Notes()) ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartDateTime())) ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Duration') ?></th><td><?php echo $event->Length() ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('Frames') ?></th><td><?php echo $event->Frames() ?></td></tr>
|
||||
<tr><th scope="row"><?php echo translate('AttrAlarmFrames') ?></th><td><?php echo $event->AlarmFrames() ?></td></tr>
|
||||
|
@ -999,5 +999,5 @@ function exportEvents(
|
|||
unlink($monitorPath.'/'.$html_eventMaster);
|
||||
}
|
||||
|
||||
return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey;
|
||||
return '?view=archive&type='.$exportFormat.'&connkey='.$connkey;
|
||||
} // end function exportEvents
|
||||
|
|
|
@ -502,11 +502,11 @@ function getBandwidthHTML($bandwidth_options, $user) {
|
|||
$result .= '<div class="dropdown-menu" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
|
||||
if ( count($bandwidth_options) > 1 ) {
|
||||
if ( isset($bandwidth_options['high']) )
|
||||
$result .= '<a data-pdsa-dropdown-val="high" class="dropdown-item" href="#">' .translate('High'). '</a>'.PHP_EOL;
|
||||
$result .= '<a data-pdsa-dropdown-val="high" class="dropdown-item bwselect" href="#">' .translate('High'). '</a>'.PHP_EOL;
|
||||
if ( isset($bandwidth_options['medium']) )
|
||||
$result .= '<a data-pdsa-dropdown-val="medium" class="dropdown-item" href="#">' .translate('Medium'). '</a>'.PHP_EOL;
|
||||
$result .= '<a data-pdsa-dropdown-val="medium" class="dropdown-item bwselect" href="#">' .translate('Medium'). '</a>'.PHP_EOL;
|
||||
# low is theoretically always available
|
||||
$result .= '<a data-pdsa-dropdown-val="low" class="dropdown-item" href="#">' .translate('Low'). '</a>'.PHP_EOL;
|
||||
$result .= '<a data-pdsa-dropdown-val="low" class="dropdown-item bwselect" href="#">' .translate('Low'). '</a>'.PHP_EOL;
|
||||
}
|
||||
$result .= '</div>'.PHP_EOL;
|
||||
|
||||
|
|
|
@ -639,7 +639,7 @@ function delCookie(name) {
|
|||
}
|
||||
|
||||
function bwClickFunction() {
|
||||
$j("#dropdown_bandwidth a").click(function() {
|
||||
$j('.bwselect').click(function() {
|
||||
var bwval = $j(this).data('pdsa-dropdown-val');
|
||||
setCookie("zmBandwidth", bwval, 3600);
|
||||
getNavBar();
|
||||
|
@ -830,10 +830,11 @@ function startDownload( exportFile ) {
|
|||
}
|
||||
|
||||
function exportResponse(data, responseText) {
|
||||
console.log(data);
|
||||
console.log('exportResponse data: ' + JSON.stringify(data));
|
||||
|
||||
var generated = (data.result=='Ok') ? 1 : 0;
|
||||
var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
|
||||
//var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
|
||||
var exportFile = data.exportFile;
|
||||
|
||||
$j('#exportProgress').removeClass( 'text-warning' );
|
||||
if ( generated ) {
|
||||
|
@ -888,3 +889,31 @@ function manageShutdownBtns(element) {
|
|||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
function thumbnail_onmouseover(event) {
|
||||
timeout = setTimeout(function() {
|
||||
var img = event.target;
|
||||
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('stream_src');
|
||||
img.addClass(imgClass);
|
||||
}, 350);
|
||||
}
|
||||
|
||||
function thumbnail_onmouseout(event) {
|
||||
clearTimeout(timeout);
|
||||
var img = event.target;
|
||||
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('still_src');
|
||||
img.removeClass(imgClass);
|
||||
}
|
||||
|
||||
function initThumbAnimation() {
|
||||
if ( ANIMATE_THUMBS ) {
|
||||
$j('.colThumbnail img').each(function() {
|
||||
this.addEventListener('mouseover', thumbnail_onmouseover, false);
|
||||
this.addEventListener('mouseout', thumbnail_onmouseout, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ var canViewMonitors = <?php echo canView('Monitors')?'true':'false' ?>;
|
|||
|
||||
var canEditGroups = <?php echo canEdit('Groups')?'true':'false' ?>;
|
||||
|
||||
var ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;
|
||||
|
||||
var refreshParent = <?php
|
||||
if ( ! empty($refreshParent) ) {
|
||||
if ( $refreshParent == true ) {
|
||||
|
|
|
@ -82,10 +82,10 @@ if ( $groupSql )
|
|||
foreach ( array('ServerId','StorageId','Status','Function') as $filter ) {
|
||||
if ( isset($_SESSION[$filter]) ) {
|
||||
if ( is_array($_SESSION[$filter]) ) {
|
||||
$conditions[] = $filter . ' IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter])). ')';
|
||||
$conditions[] = '`'.$filter . '` IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter])). ')';
|
||||
$values = array_merge($values, $_SESSION[$filter]);
|
||||
} else {
|
||||
$conditions[] = $filter . '=?';
|
||||
$conditions[] = '`'.$filter . '`=?';
|
||||
$values[] = $_SESSION[$filter];
|
||||
}
|
||||
}
|
||||
|
@ -248,9 +248,9 @@ $html .= '</span>
|
|||
'multiple'=>'multiple',
|
||||
'data-placeholder'=>'All',
|
||||
) );
|
||||
# Repurpose this variable to be the list of MonitorIds as a result of all the filtering
|
||||
$selected_monitor_ids = array_map(function($monitor_row){return $monitor_row['Id'];}, $displayMonitors);
|
||||
$html .= '</span>
|
||||
# Repurpose this variable to be the list of MonitorIds as a result of all the filtering
|
||||
$selected_monitor_ids = array_map(function($monitor_row){return $monitor_row['Id'];}, $displayMonitors);
|
||||
$html .= '</span>
|
||||
';
|
||||
echo $html;
|
||||
?>
|
||||
|
|
|
@ -294,6 +294,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
|||
$imgHTML='';
|
||||
if ( ZM_WEB_LIST_THUMBS && ($monitor['Status'] == 'Connected') && $running ) {
|
||||
$options = array();
|
||||
|
||||
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
|
||||
$options['width'] = ZM_WEB_LIST_THUMB_WIDTH;
|
||||
$options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor;
|
||||
|
@ -306,7 +307,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
|||
$thmbWidth = ( $options['width'] ) ? 'width:'.$options['width'].'px;' : '';
|
||||
$thmbHeight = ( $options['height'] ) ? 'height:'.$options['height'].'px;' : '';
|
||||
|
||||
$imgHTML = '<div class="colThumbnail zoom-right"><a';
|
||||
$imgHTML = '<div class="colThumbnail"><a';
|
||||
$imgHTML .= $stream_available ? ' href="?view=watch&mid='.$monitor['Id'].'">' : '>';
|
||||
$imgHTML .= '<img id="thumbnail' .$Monitor->Id(). '" src="' .$stillSrc. '" style="'
|
||||
.$thmbWidth.$thmbHeight. '" stream_src="' .$streamSrc. '" still_src="' .$stillSrc. '"'.
|
||||
|
|
|
@ -42,7 +42,7 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
|
|||
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<div id="content" class="table-responsive-sm">
|
||||
<table
|
||||
id="controlTable"
|
||||
data-locale="<?php echo i18n() ?>"
|
||||
|
@ -54,7 +54,6 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
|
|||
data-remember-order="true"
|
||||
data-click-to-select="true"
|
||||
data-maintain-meta-data="true"
|
||||
data-mobile-responsive="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-toolbar="#toolbar"
|
||||
data-show-columns="true"
|
||||
|
|
|
@ -46,7 +46,7 @@ xhtmlHeaders(__FILE__, translate('Devices') );
|
|||
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div id="content" class="row justify-content-center table-responsive-sm">
|
||||
<table
|
||||
id="devicesTable"
|
||||
data-locale="<?php echo i18n() ?>"
|
||||
|
@ -59,7 +59,6 @@ xhtmlHeaders(__FILE__, translate('Devices') );
|
|||
data-remember-order="true"
|
||||
data-click-to-select="true"
|
||||
data-maintain-meta-data="true"
|
||||
data-mobile-responsive="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-toolbar="#toolbar"
|
||||
data-show-columns="true"
|
||||
|
|
|
@ -124,39 +124,36 @@ $filterQuery = $filter->querystring();
|
|||
|
||||
$connkey = generateConnKey();
|
||||
|
||||
$focusWindow = true;
|
||||
|
||||
$popup = (isset($_REQUEST['popup']) && ($_REQUEST['popup'] == 1));
|
||||
|
||||
xhtmlHeaders(__FILE__, translate('Event'));
|
||||
xhtmlHeaders(__FILE__, translate('Event').' '.$Event->Id());
|
||||
?>
|
||||
<body>
|
||||
<div id="page">
|
||||
<?php if ( !$popup ) echo getNavBarHTML() ?>
|
||||
<div id="header">
|
||||
<?php echo getNavBarHTML() ?>
|
||||
<?php
|
||||
if ( !$Event->Id() ) {
|
||||
echo 'Event was not found.';
|
||||
} else {
|
||||
if ( !file_exists($Event->Path()) ) {
|
||||
if ( !file_exists($Event->Path()) )
|
||||
echo '<div class="error">Event was not found at '.$Event->Path().'. It is unlikely that playback will be possible.</div>';
|
||||
}
|
||||
|
||||
$storage = validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' );
|
||||
?>
|
||||
<div id="dataBar">
|
||||
<span id="dataId" title="<?php echo translate('Id') ?>"><?php echo $Event->Id() ?></span>
|
||||
<span id="dataMonitor" title="<?php echo translate('Monitor') ?>"><?php echo $Monitor->Id().' '.validHtmlStr($Monitor->Name()) ?></span>
|
||||
<span id="dataCause" title="<?php echo $Event->Notes()?validHtmlStr($Event->Notes()):translate('AttrCause') ?>"><?php echo validHtmlStr($Event->Cause()) ?></span>
|
||||
<span id="dataTime" title="<?php echo translate('Time') ?>"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></span>
|
||||
<span id="dataDuration" title="<?php echo translate('Duration') ?>"><?php echo $Event->Length().'s' ?></span>
|
||||
<span id="dataFrames" title="<?php echo translate('AttrFrames').'/'.translate('AttrAlarmFrames') ?>"><?php echo $Event->Frames() ?>/<?php echo $Event->AlarmFrames() ?></span>
|
||||
<span id="dataScore" title="<?php echo translate('AttrTotalScore').'/'.translate('AttrAvgScore').'/'.translate('AttrMaxScore') ?>"><?php echo $Event->TotScore() ?>/<?php echo $Event->AvgScore() ?>/<?php echo $Event->MaxScore() ?></span>
|
||||
<span id="Storage">
|
||||
<?php echo
|
||||
human_filesize($Event->DiskSpace(null)) . ' on ' . validHtmlStr($Event->Storage()->Name()).
|
||||
( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' )
|
||||
?></span>
|
||||
<div id="closeWindow"><a href="#" data-on-click="<?php echo $popup ? 'closeWindow' : 'backWindow' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
|
||||
|
||||
<!-- BEGIN HEADER -->
|
||||
<div class="d-flex flex-row justify-content-between px-3 py-1">
|
||||
<div id="toolbar" >
|
||||
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
|
||||
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
|
||||
<button id="renameBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Rename') ?>" disabled><i class="fa fa-font"></i></button>
|
||||
<button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button>
|
||||
<button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button>
|
||||
<button id="editBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Edit') ?>" disabled><i class="fa fa-pencil"></i></button>
|
||||
<button id="exportBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Export') ?>"><i class="fa fa-external-link"></i></button>
|
||||
<button id="downloadBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DownloadVideo') ?>"><i class="fa fa-download"></i></button>
|
||||
<button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button>
|
||||
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
<<<<<<< HEAD
|
||||
<div id="menuBar1">
|
||||
<div id="nameControl">
|
||||
<input type="text" id="eventName" name="eventName" value="<?php echo validHtmlStr($Event->Name()) ?>" />
|
||||
|
@ -200,10 +197,86 @@ if ( canEdit('Events') ) {
|
|||
<label for="codec"><?php echo translate('Codec') ?></label>
|
||||
<?php echo htmlSelect('codec', $codecs, $codec, array('data-on-change'=>'changeCodec','id'=>'codec')); ?>
|
||||
</div>
|
||||
=======
|
||||
|
||||
<h2><?php echo translate('Event').' '.$Event->Id() ?></h2>
|
||||
|
||||
<div class="d-flex flex-row">
|
||||
<div id="replayControl"><label for="replayMode"><?php echo translate('Replay') ?></label><?php echo htmlSelect('replayMode', $replayModes, $replayMode, array('data-on-change'=>'changeReplayMode','id'=>'replayMode')); ?></div>
|
||||
<div id="scaleControl"><label for="scale"><?php echo translate('Scale') ?></label><?php echo htmlSelect('scale', $scales, $scale, array('data-on-change'=>'changeScale','id'=>'scale')); ?></div>
|
||||
<div id="codecControl"><label for="codec"><?php echo translate('Codec') ?></label><?php echo htmlSelect('codec', $codecs, $codec, array('data-on-change'=>'changeCodec','id'=>'codec')); ?></div>
|
||||
>>>>>>> master
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
|
||||
<!-- BEGIN VIDEO CONTENT ROW -->
|
||||
<div id="content" class="d-flex flex-row justify-content-center">
|
||||
<div class="">
|
||||
<!-- VIDEO STATISTICS TABLE -->
|
||||
<table id="eventStatsTable" class="table-sm table-borderless">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('EventId') ?></th>
|
||||
<td id="dataEventId"><?php echo $Event->Id() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('EventName') ?></th>
|
||||
<td id="dataEventName"><?php echo $Event->Name() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrMonitorId') ?></th>
|
||||
<td id="dataMonitorId"><?php echo $Monitor->Id() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrMonitorName') ?></th>
|
||||
<td id="dataMonitorName"><?php echo validHtmlStr($Monitor->Name()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('Cause') ?></th>
|
||||
<td id="dataCause"><?php echo validHtmlStr($Event->Cause()) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrStartTime') ?></th>
|
||||
<td id="dataStartTime"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('Duration') ?></th>
|
||||
<td id="dataDuration"><?php echo $Event->Length().'s' ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrFrames') ?></th>
|
||||
<td id="dataFrames"><?php echo $Event->Frames() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrAlarmFrames') ?></th>
|
||||
<td id="dataAlarmFrames"><?php echo $Event->AlarmFrames() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrTotalScore') ?></th>
|
||||
<td id="dataTotalScore"><?php echo $Event->TotScore() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrAvgScore') ?></th>
|
||||
<td id="dataAvgScore"><?php echo $Event->AvgScore() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('AttrMaxScore') ?></th>
|
||||
<td id="dataMaxScore"><?php echo $Event->MaxScore() ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('DiskSpace') ?></th>
|
||||
<td id="dataDiskSpace"><?php echo human_filesize($Event->DiskSpace(null)) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right"><?php echo translate('Storage') ?></th>
|
||||
<td id="dataStorage"><?php echo $storage?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="">
|
||||
<div id="eventVideo">
|
||||
<!-- VIDEO CONTENT -->
|
||||
<?php
|
||||
if ( $video_tag ) {
|
||||
?>
|
||||
|
@ -328,6 +401,8 @@ echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
|
|||
<?php
|
||||
} // end if Event exists
|
||||
?>
|
||||
</div>
|
||||
</div><!--content-->
|
||||
|
||||
</div><!--page-->
|
||||
<?php xhtmlFooter() ?>
|
||||
|
|
|
@ -65,7 +65,7 @@ getBodyTopHTML();
|
|||
</div>
|
||||
|
||||
<!-- Table styling handled by bootstrap-tables -->
|
||||
<div class="row justify-content-center">
|
||||
<div class="row justify-content-center table-responsive-sm">
|
||||
<table
|
||||
id="eventTable"
|
||||
data-locale="<?php echo i18n() ?>"
|
||||
|
@ -87,7 +87,6 @@ getBodyTopHTML();
|
|||
data-show-fullscreen="true"
|
||||
data-click-to-select="true"
|
||||
data-maintain-meta-data="true"
|
||||
data-mobile-responsive="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-show-jump-to="true"
|
||||
data-show-refresh="true"
|
||||
|
|
|
@ -28,87 +28,6 @@ require_once('includes/Filter.php');
|
|||
|
||||
$eid = validInt($_REQUEST['eid']);
|
||||
$Event = new ZM\Event($eid);
|
||||
$Monitor = $Event->Monitor();
|
||||
|
||||
$countSql = 'SELECT COUNT(*) AS FrameCount FROM Frames AS F WHERE 1 ';
|
||||
$frameSql = 'SELECT *, unix_timestamp(TimeStamp) AS UnixTimeStamp FROM Frames AS F WHERE 1 ';
|
||||
|
||||
// override the sort_field handling in parseSort for frames
|
||||
if ( empty($_REQUEST['sort_field']) )
|
||||
$_REQUEST['sort_field'] = 'FramesTimeStamp';
|
||||
|
||||
if ( !isset($_REQUEST['sort_asc']) )
|
||||
$_REQUEST['sort_asc'] = true;
|
||||
|
||||
if ( !isset($_REQUEST['filter']) ) {
|
||||
// generate a dummy filter from the eid for pagination
|
||||
$_REQUEST['filter'] = array('Query' => array('terms' => array()));
|
||||
$_REQUEST['filter'] = addFilterTerm(
|
||||
$_REQUEST['filter'],
|
||||
0,
|
||||
array( 'cnj' => 'and', 'attr' => 'FramesEventId', 'op' => '=', 'val' => $eid )
|
||||
);
|
||||
}
|
||||
|
||||
parseSort();
|
||||
$filter = ZM\Filter::parse($_REQUEST['filter']);
|
||||
$filterQuery = $filter->querystring();
|
||||
|
||||
if ( $filter->sql() ) {
|
||||
$countSql .= ' AND ('.$filter->sql().')';
|
||||
$frameSql .= ' AND ('.$filter->sql().')';
|
||||
}
|
||||
|
||||
$frameSql .= " ORDER BY $sortColumn $sortOrder";
|
||||
if ( $sortColumn != 'Id' )
|
||||
$frameSql .= ',Id '.$sortOrder;
|
||||
|
||||
if ( isset($_REQUEST['scale']) ) {
|
||||
$scale = validNum($_REQUEST['scale']);
|
||||
} else if ( isset($_COOKIE['zmWatchScale'.$Monitor->Id()]) ) {
|
||||
$scale = validNum($_COOKIE['zmWatchScale'.$Monitor->Id()]);
|
||||
} else if ( isset($_COOKIE['zmWatchScale']) ) {
|
||||
$scale = validNum($_COOKIE['zmWatchScale']);
|
||||
} else {
|
||||
$scale = max(reScale(SCALE_BASE, $Monitor->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE);
|
||||
}
|
||||
|
||||
$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 1;
|
||||
$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0;
|
||||
|
||||
$nFrames = dbFetchOne($countSql, 'FrameCount');
|
||||
|
||||
if ( !empty($limit) && ($nFrames > $limit) ) {
|
||||
$nFrames = $limit;
|
||||
}
|
||||
|
||||
$pages = (int)ceil($nFrames/ZM_WEB_EVENTS_PER_PAGE);
|
||||
|
||||
if ( !empty($page) ) {
|
||||
if ( $page <= 0 )
|
||||
$page = 1;
|
||||
else if ( $pages and ( $page > $pages ) )
|
||||
$page = $pages;
|
||||
|
||||
$limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE);
|
||||
if ( empty($limit) ) {
|
||||
$limitAmount = ZM_WEB_EVENTS_PER_PAGE;
|
||||
} else {
|
||||
$limitLeft = $limit - $limitStart;
|
||||
$limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft;
|
||||
}
|
||||
$frameSql .= " LIMIT $limitStart, $limitAmount";
|
||||
} else if ( !empty($limit) ) {
|
||||
$frameSql .= ' LIMIT 0, '.$limit;
|
||||
}
|
||||
|
||||
$maxShortcuts = 5;
|
||||
$totalQuery = $sortQuery.'&eid='.$eid.$limitQuery.$filterQuery;
|
||||
$pagination = getPagination($pages, $page, $maxShortcuts, $totalQuery);
|
||||
|
||||
$frames = dbFetchAll($frameSql);
|
||||
|
||||
$focusWindow = true;
|
||||
|
||||
xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
|
||||
?>
|
||||
|
@ -122,10 +41,12 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
|
|||
</div>
|
||||
|
||||
<!-- Table styling handled by bootstrap-tables -->
|
||||
<div class="row justify-content-center">
|
||||
<div class="row justify-content-center table-responsive-sm">
|
||||
<table
|
||||
id="framesTable"
|
||||
data-locale="<?php echo i18n() ?>"
|
||||
data-side-pagination="server"
|
||||
data-ajax="ajaxRequest"
|
||||
data-pagination="true"
|
||||
data-show-pagination-switch="true"
|
||||
data-page-list="[10, 25, 50, 100, 200, All]"
|
||||
|
@ -139,12 +60,12 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
|
|||
data-toolbar="#toolbar"
|
||||
data-show-fullscreen="true"
|
||||
data-maintain-meta-data="true"
|
||||
data-mobile-responsive="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-detail-view="true"
|
||||
data-detail-formatter="detailFormatter"
|
||||
data-show-toggle="true"
|
||||
data-show-jump-to="true"
|
||||
data-show-refresh="true"
|
||||
class="table-sm table-borderless">
|
||||
|
||||
<thead>
|
||||
|
@ -152,57 +73,15 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
|
|||
<tr>
|
||||
<th class="px-3" data-align="center" data-sortable="false" data-field="EventId"><?php echo translate('EventId') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="FrameId"><?php echo translate('FrameId') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="FrameType"><?php echo translate('Type') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="FrameTimeStamp"><?php echo translate('TimeStamp') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="FrameDelta"><?php echo translate('TimeDelta') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="FrameScore"><?php echo translate('Score') ?></th>
|
||||
<?php
|
||||
if ( ZM_WEB_LIST_THUMBS ) {
|
||||
?>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="Type"><?php echo translate('Type') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="TimeStamp"><?php echo translate('TimeStamp') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="Delta"><?php echo translate('TimeDelta') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="true" data-field="Score"><?php echo translate('Score') ?></th>
|
||||
<th class="px-3" data-align="center" data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ( count($frames) ) {
|
||||
foreach ( $frames as $frame ) {
|
||||
$Frame = new ZM\Frame($frame);
|
||||
?>
|
||||
<tr class="<?php echo strtolower($frame['Type']) ?>">
|
||||
<td><?php echo $frame['EventId'] ?></td>
|
||||
<td><?php echo $frame['FrameId'] ?></td>
|
||||
<td><?php echo $frame['Type'] ?></td>
|
||||
<td><?php echo strftime(STRF_FMT_TIME, $frame['UnixTimeStamp']) ?></td>
|
||||
<td><?php echo number_format( $frame['Delta'], 2 ) ?></td>
|
||||
<td><?php echo $frame['Score'] ?></td>
|
||||
<?php
|
||||
if ( ZM_WEB_LIST_THUMBS ) {
|
||||
$base_img_src = '?view=image&fid=' .$Frame->Id();
|
||||
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
|
||||
$thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : '';
|
||||
$thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"';
|
||||
$thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$frame['EventId']. '_' .$frame['FrameId']. '.jpg';
|
||||
$img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn)));
|
||||
$full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn)));
|
||||
$frame_src = '?view=frame&eid=' .$Event->Id(). '&fid=' .$frame['FrameId'];
|
||||
|
||||
echo '<td class="colThumbnail zoom"><img src="' .$img_src. '" '.$thmb_width. ' ' .$thmb_height. 'img_src="' .$img_src. '" full_img_src="' .$full_img_src. '"></td>'.PHP_EOL;
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
<?php
|
||||
} // end foreach frame
|
||||
} else {
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="5"><?php echo translate('NoFramesRecorded') ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<!-- Row data populated via Ajax -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,3 @@
|
|||
function thumbnail_onmouseover(event) {
|
||||
var img = event.target;
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('stream_src');
|
||||
}
|
||||
|
||||
function thumbnail_onmouseout(event) {
|
||||
var img = event.target;
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('still_src');
|
||||
}
|
||||
|
||||
function initThumbAnimation() {
|
||||
$j('.colThumbnail img').each(function() {
|
||||
this.addEventListener('mouseover', thumbnail_onmouseover, false);
|
||||
this.addEventListener('mouseout', thumbnail_onmouseout, false);
|
||||
});
|
||||
}
|
||||
|
||||
function setButtonStates( element ) {
|
||||
var form = element.form;
|
||||
var checked = 0;
|
||||
|
|
|
@ -1,7 +1,40 @@
|
|||
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
|
||||
var table = $j('#eventStatsTable');
|
||||
var backBtn = $j('#backBtn');
|
||||
var renameBtn = $j('#renameBtn');
|
||||
var archiveBtn = $j('#archiveBtn');
|
||||
var unarchiveBtn = $j('#unarchiveBtn');
|
||||
var editBtn = $j('#editBtn');
|
||||
var exportBtn = $j('#exportBtn');
|
||||
var downloadBtn = $j('#downloadBtn');
|
||||
var deleteBtn = $j('#deleteBtn');
|
||||
var prevEventId = 0;
|
||||
var nextEventId = 0;
|
||||
var prevEventStartTime = 0;
|
||||
var nextEventStartTime = 0;
|
||||
var PrevEventDefVideoPath = "";
|
||||
var NextEventDefVideoPath = "";
|
||||
var slider = null;
|
||||
var scroll = null;
|
||||
var currEventId = null;
|
||||
var CurEventDefVideoPath = null;
|
||||
var vid = null;
|
||||
var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame.
|
||||
var intervalRewind;
|
||||
var revSpeed = .5;
|
||||
var cueFrames = null; //make cueFrames available even if we don't send another ajax query
|
||||
var streamCmdTimer = null;
|
||||
var streamStatus = null;
|
||||
var lastEventId = 0;
|
||||
var zmsBroke = false; //Use alternate navigation if zms has crashed
|
||||
var streamParms = "view=request&request=stream&connkey="+connKey;
|
||||
if ( auth_hash ) streamParms += '&auth='+auth_hash;
|
||||
var frameBatch = 40;
|
||||
var currFrameId = null;
|
||||
var eventReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse} );
|
||||
var actReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse} );
|
||||
var frameReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse} );
|
||||
var streamReq = new Request.JSON( {url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse} );
|
||||
|
||||
// Function called when video.js hits the end of the video
|
||||
function vjsReplay() {
|
||||
|
@ -44,10 +77,6 @@ function vjsReplay() {
|
|||
}
|
||||
} // end function vjsReplay
|
||||
|
||||
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
|
||||
|
||||
var cueFrames = null; //make cueFrames available even if we don't send another ajax query
|
||||
|
||||
function initialAlarmCues(eventId) {
|
||||
$j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues) //get frames data for alarmCues and inserts into html
|
||||
.fail(logAjaxFail);
|
||||
|
@ -134,7 +163,6 @@ function renderAlarmCues(containerEl) {
|
|||
return alarmHtml;
|
||||
}
|
||||
|
||||
|
||||
function changeCodec() {
|
||||
location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+$j('#codec').val());
|
||||
}
|
||||
|
@ -217,16 +245,6 @@ function changeRate() {
|
|||
Cookie.write('zmEventRate', rate, {duration: 10*365, samesite: 'strict'});
|
||||
} // end function changeRate
|
||||
|
||||
var streamParms = "view=request&request=stream&connkey="+connKey;
|
||||
if ( auth_hash ) {
|
||||
streamParms += '&auth='+auth_hash;
|
||||
}
|
||||
var streamCmdTimer = null;
|
||||
|
||||
var streamStatus = null;
|
||||
var lastEventId = 0;
|
||||
var zmsBroke = false; //Use alternate navigation if zms has crashed
|
||||
|
||||
function getCmdResponse( respObj, respText ) {
|
||||
if ( checkStreamForErrors('getCmdResponse', respObj) ) {
|
||||
console.log('Got an error from getCmdResponse');
|
||||
|
@ -291,14 +309,6 @@ function getCmdResponse( respObj, respText ) {
|
|||
streamCmdTimer = streamQuery.delay(streamTimeout); //Timeout is refresh rate for progressBox and time display
|
||||
} // end function getCmdResponse( respObj, respText )
|
||||
|
||||
var streamReq = new Request.JSON( {
|
||||
url: monitorUrl,
|
||||
method: 'get',
|
||||
timeout: AJAX_TIMEOUT,
|
||||
link: 'chain',
|
||||
onSuccess: getCmdResponse
|
||||
} );
|
||||
|
||||
function pauseClicked() {
|
||||
if ( vid ) {
|
||||
if ( intervalRewind ) {
|
||||
|
@ -553,11 +563,6 @@ function streamQuery() {
|
|||
streamReq.send( streamParms+"&command="+CMD_QUERY );
|
||||
}
|
||||
|
||||
var slider = null;
|
||||
var scroll = null;
|
||||
var currEventId = null;
|
||||
var CurEventDefVideoPath = null;
|
||||
|
||||
function getEventResponse(respObj, respText) {
|
||||
if ( checkStreamForErrors('getEventResponse', respObj) ) {
|
||||
console.log('getEventResponse: errors');
|
||||
|
@ -572,28 +577,31 @@ function getEventResponse(respObj, respText) {
|
|||
}
|
||||
currEventId = eventData.Id;
|
||||
|
||||
$('dataId').set( 'text', eventData.Id );
|
||||
$j('#dataEventId').text( eventData.Id );
|
||||
$j('#dataEventName').text( eventData.Name );
|
||||
$j('#dataMonitorId').text( eventData.MonitorId );
|
||||
$j('#dataMonitorName').text( eventData.MonitorName );
|
||||
$j('#dataCause').text( eventData.Cause );
|
||||
if ( eventData.Notes ) {
|
||||
$('dataCause').setProperty( 'title', eventData.Notes );
|
||||
$j('#dataCause').prop( 'title', eventData.Notes );
|
||||
} else {
|
||||
$('dataCause').setProperty( 'title', causeString );
|
||||
}
|
||||
$('dataCause').set( 'text', eventData.Cause );
|
||||
$('dataTime').set( 'text', eventData.StartDateTime );
|
||||
$('dataDuration').set( 'text', eventData.Length );
|
||||
$('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames );
|
||||
$('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore );
|
||||
$('eventName').setProperty( 'value', eventData.Name );
|
||||
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery);//if popup removed, check if this allows forward
|
||||
if ( canEditEvents ) {
|
||||
if ( parseInt(eventData.Archived) ) {
|
||||
$('archiveEvent').addClass( 'hidden' );
|
||||
$('unarchiveEvent').removeClass( 'hidden' );
|
||||
} else {
|
||||
$('archiveEvent').removeClass( 'hidden' );
|
||||
$('unarchiveEvent').addClass( 'hidden' );
|
||||
}
|
||||
$j('#dataCause').prop( 'title', causeString );
|
||||
}
|
||||
$j('#dataStartTime').text( eventData.StartDateTime );
|
||||
$j('#dataDuration').text( eventData.Length );
|
||||
$j('#dataFrames').text( eventData.Frames );
|
||||
$j('#dataAlarmFrames').text( eventData.AlarmFrames );
|
||||
$j('dataTotalScore').text( eventData.TotScore );
|
||||
$j('dataAvgScore').text( eventData.AvgScore );
|
||||
$j('dataMaxScore').text( eventData.MaxScore );
|
||||
$j('dataDiskSpace').text( eventData.DiskSpace );
|
||||
$j('dataStorage').text( eventData.Storage );
|
||||
|
||||
// Refresh the status of the archive buttons
|
||||
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
|
||||
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
|
||||
|
||||
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery); //if popup removed, check if this allows forward
|
||||
// Technically, events can be different sizes, so may need to update the size of the image, but it might be better to have it stay scaled...
|
||||
//var eventImg = $('eventImage');
|
||||
//eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } );
|
||||
|
@ -613,8 +621,6 @@ function getEventResponse(respObj, respText) {
|
|||
nearEventsQuery( eventData.Id );
|
||||
} // end function getEventResponse
|
||||
|
||||
var eventReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse} );
|
||||
|
||||
function eventQuery( eventId ) {
|
||||
var eventParms = 'view=request&request=status&entity=event&id='+eventId;
|
||||
if ( auth_hash ) {
|
||||
|
@ -623,13 +629,6 @@ function eventQuery( eventId ) {
|
|||
eventReq.send( eventParms );
|
||||
}
|
||||
|
||||
var prevEventId = 0;
|
||||
var nextEventId = 0;
|
||||
var prevEventStartTime = 0;
|
||||
var nextEventStartTime = 0;
|
||||
var PrevEventDefVideoPath = "";
|
||||
var NextEventDefVideoPath = "";
|
||||
|
||||
function getNearEventsResponse( respObj, respText ) {
|
||||
if ( checkStreamForErrors('getNearEventsResponse', respObj) ) {
|
||||
return;
|
||||
|
@ -656,8 +655,6 @@ function nearEventsQuery( eventId ) {
|
|||
nearEventsReq.send( parms );
|
||||
}
|
||||
|
||||
var frameBatch = 40;
|
||||
|
||||
function loadEventThumb( event, frame, loadImage ) {
|
||||
var thumbImg = $('eventThumb'+frame.FrameId);
|
||||
if ( !thumbImg ) {
|
||||
|
@ -789,15 +786,11 @@ function getFrameResponse(respObj, respText) {
|
|||
loadEventThumb(eventData, frame, respObj.loopback=="true");
|
||||
}
|
||||
|
||||
var frameReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse} );
|
||||
|
||||
function frameQuery( eventId, frameId, loadImage ) {
|
||||
var parms = "view=request&request=status&entity=frameimage&id[0]="+eventId+"&id[1]="+frameId+"&loopback="+loadImage;
|
||||
frameReq.send(parms);
|
||||
}
|
||||
|
||||
var currFrameId = null;
|
||||
|
||||
function checkFrames( eventId, frameId, loadImage ) {
|
||||
if ( !eventData ) {
|
||||
console.error("No event "+eventId+" found");
|
||||
|
@ -917,8 +910,6 @@ function getActResponse( respObj, respText ) {
|
|||
}
|
||||
}
|
||||
|
||||
var actReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse} );
|
||||
|
||||
function actQuery(action, parms) {
|
||||
var actParms = "view=request&request=event&id="+eventData.Id+"&action="+action;
|
||||
if ( auth_hash ) {
|
||||
|
@ -930,55 +921,17 @@ function actQuery(action, parms) {
|
|||
actReq.send(actParms);
|
||||
}
|
||||
|
||||
function deleteEvent() {
|
||||
pauseClicked(); //Provides visual feedback that your click happened.
|
||||
|
||||
var deleteReq = new Request.JSON({
|
||||
url: thisUrl,
|
||||
method: 'post',
|
||||
timeout: AJAX_TIMEOUT,
|
||||
onSuccess: function onDeleteSuccess(respObj, respText) {
|
||||
getActResponse(respObj, respText);
|
||||
// We must wait for the deletion to happen before navigating to the next
|
||||
// event or this request will be cancelled.
|
||||
streamNext(true);
|
||||
},
|
||||
});
|
||||
deleteReq.send("view=request&request=event&id="+eventData.Id+"&action=delete");
|
||||
}
|
||||
|
||||
function renameEvent() {
|
||||
var newName = $('eventName').get('value');
|
||||
var newName = $j('input').val();
|
||||
actQuery('rename', {eventName: newName});
|
||||
}
|
||||
|
||||
// Manage the EDIT button
|
||||
function editEvent() {
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eid='+eventData.Id)
|
||||
.done(function(data) {
|
||||
insertModalHtml('eventDetailModal', data.html);
|
||||
$j('#eventDetailModal').modal('show');
|
||||
// Manage the Save button
|
||||
$j('#eventDetailSaveBtn').click(function(evt) {
|
||||
evt.preventDefault();
|
||||
$j('#eventDetailForm').submit();
|
||||
});
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
//FIXME: update the value of the event name rather than reload the whole page
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
function exportEvent() {
|
||||
window.location.assign('?view=export&eid='+eventData.Id);
|
||||
}
|
||||
|
||||
function archiveEvent() {
|
||||
actQuery('archive');
|
||||
}
|
||||
|
||||
function unarchiveEvent() {
|
||||
actQuery('unarchive');
|
||||
}
|
||||
|
||||
function showEventFrames() {
|
||||
window.location.assign('?view=frames&eid='+eventData.Id);
|
||||
}
|
||||
|
@ -1079,7 +1032,56 @@ function handleClick( event ) {
|
|||
}
|
||||
}
|
||||
|
||||
// Load the Delete Confirmation Modal HTML via Ajax call
|
||||
function getDelConfirmModal() {
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
|
||||
.done(function(data) {
|
||||
insertModalHtml('deleteConfirm', data.html);
|
||||
manageDelConfirmModalBtns();
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
// Manage the DELETE CONFIRMATION modal button
|
||||
function manageDelConfirmModalBtns() {
|
||||
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
|
||||
if ( ! canEditEvents ) {
|
||||
enoperm();
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eventData.Id)
|
||||
.done( function(data) {
|
||||
streamNext( true );
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the CANCEL modal button
|
||||
document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) {
|
||||
$j('#deleteConfirm').modal('hide');
|
||||
});
|
||||
}
|
||||
|
||||
function getEvtStatsCookie() {
|
||||
var cookie = 'zmEventStats';
|
||||
var stats = getCookie(cookie);
|
||||
|
||||
if ( !stats ) {
|
||||
stats = 'on';
|
||||
setCookie(cookie, stats, 10*365);
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
// Load the delete confirmation modal into the DOM
|
||||
getDelConfirmModal();
|
||||
|
||||
var stats = getEvtStatsCookie();
|
||||
if ( stats != 'on' ) table.toggle(false);
|
||||
|
||||
//FIXME prevent blocking...not sure what is happening or best way to unblock
|
||||
if ( $j('#videoobj').length ) {
|
||||
vid = videojs('videoobj');
|
||||
|
@ -1130,7 +1132,136 @@ function initPage() {
|
|||
document.querySelectorAll('select[name="rate"]').forEach(function(el) {
|
||||
el.onchange = window['changeRate'];
|
||||
});
|
||||
|
||||
// enable or disable buttons based on current selection and user rights
|
||||
renameBtn.prop('disabled', !canEditEvents);
|
||||
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
|
||||
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
|
||||
editBtn.prop('disabled', !canEditEvents);
|
||||
exportBtn.prop('disabled', !canViewEvents);
|
||||
downloadBtn.prop('disabled', !canViewEvents);
|
||||
deleteBtn.prop('disabled', !canEditEvents);
|
||||
|
||||
// Don't enable the back button if there is no previous zm page to go back to
|
||||
backBtn.prop('disabled', !document.referrer.length);
|
||||
|
||||
// Manage the BACK button
|
||||
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
|
||||
evt.preventDefault();
|
||||
window.history.back();
|
||||
});
|
||||
|
||||
// Manage the REFRESH Button
|
||||
document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) {
|
||||
evt.preventDefault();
|
||||
window.location.reload(true);
|
||||
});
|
||||
|
||||
// Manage the Event RENAME button
|
||||
document.getElementById("renameBtn").addEventListener("click", function onDownloadClick(evt) {
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=eventrename&eid='+eventData.Id)
|
||||
.done(function(data) {
|
||||
insertModalHtml('eventRenameModal', data.html);
|
||||
$j('#eventRenameModal').modal('show');
|
||||
// Manage the SAVE button
|
||||
$j('#eventRenameBtn').click(renameEvent);
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the ARCHIVE button
|
||||
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+eventData.Id)
|
||||
.done( function(data) {
|
||||
//FIXME: update the status of the archive button reather than reload the whole page
|
||||
window.location.reload(true);
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the UNARCHIVE button
|
||||
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
|
||||
if ( ! canEditEvents ) {
|
||||
enoperm();
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+eventData.Id)
|
||||
.done( function(data) {
|
||||
//FIXME: update the status of the unarchive button reather than reload the whole page
|
||||
window.location.reload(true);
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the EDIT button
|
||||
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
|
||||
if ( ! canEditEvents ) {
|
||||
enoperm();
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]='+eventData.Id)
|
||||
.done(function(data) {
|
||||
insertModalHtml('eventDetailModal', data.html);
|
||||
$j('#eventDetailModal').modal('show');
|
||||
// Manage the Save button
|
||||
$j('#eventDetailSaveBtn').click(function(evt) {
|
||||
evt.preventDefault();
|
||||
$j('#eventDetailForm').submit();
|
||||
});
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the EXPORT button
|
||||
document.getElementById("exportBtn").addEventListener("click", function onExportClick(evt) {
|
||||
evt.preventDefault();
|
||||
window.location.assign('?view=export&eids[]='+eventData.Id);
|
||||
});
|
||||
|
||||
// Manage the DOWNLOAD VIDEO button
|
||||
document.getElementById("downloadBtn").addEventListener("click", function onDownloadClick(evt) {
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=download&eids[]='+eventData.Id)
|
||||
.done(function(data) {
|
||||
insertModalHtml('downloadModal', data.html);
|
||||
$j('#downloadModal').modal('show');
|
||||
// Manage the GENERATE DOWNLOAD button
|
||||
$j('#exportButton').click(exportEvent);
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the Event STATISTICS Button
|
||||
document.getElementById("statsBtn").addEventListener("click", function onStatsClick(evt) {
|
||||
evt.preventDefault();
|
||||
var cookie = 'zmEventStats';
|
||||
|
||||
// Toggle the visiblity of the stats table and write an appropriate cookie
|
||||
if ( table.is(':visible') ) {
|
||||
setCookie(cookie, 'off', 10*365);
|
||||
table.toggle(false);
|
||||
} else {
|
||||
setCookie(cookie, 'on', 10*365);
|
||||
table.toggle(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Manage the DELETE button
|
||||
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
|
||||
if ( ! canEditEvents ) {
|
||||
enoperm();
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
$j('#deleteConfirm').modal('show');
|
||||
});
|
||||
}
|
||||
|
||||
// Kick everything off
|
||||
window.addEventListener('DOMContentLoaded', initPage);
|
||||
$j(document).ready(initPage);
|
||||
|
|
|
@ -40,6 +40,7 @@ var connKey = '<?php echo $connkey ?>';
|
|||
|
||||
var eventData = {
|
||||
Id: '<?php echo $Event->Id() ?>',
|
||||
Name: '<?php echo $Event->Name() ?>',
|
||||
MonitorId: '<?php echo $Event->MonitorId() ?>',
|
||||
Width: '<?php echo $Event->Width() ?>',
|
||||
Height: '<?php echo $Event->Height() ?>',
|
||||
|
@ -47,7 +48,10 @@ var eventData = {
|
|||
StartDateTime: '<?php echo $Event->StartDateTime() ?>',
|
||||
EndDateTime: '<?php echo $Event->EndDateTime() ?>',
|
||||
Frames: '<?php echo $Event->Frames() ?>',
|
||||
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>'
|
||||
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
|
||||
DiskSpace: '<?php echo human_filesize($Event->DiskSpace(null)) ?>',
|
||||
Storage: '<?php validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
|
||||
Archived: <?php echo $Event->Archived?'true':'false' ?>
|
||||
};
|
||||
var monitorUrl = '<?php echo $Event->Storage()->Server()->UrlToIndex(); ?>';
|
||||
|
||||
|
|
|
@ -74,25 +74,6 @@ function processRows(rows) {
|
|||
return rows;
|
||||
}
|
||||
|
||||
function thumbnail_onmouseover(event) {
|
||||
var img = event.target;
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('stream_src');
|
||||
}
|
||||
|
||||
function thumbnail_onmouseout(event) {
|
||||
var img = event.target;
|
||||
img.src = '';
|
||||
img.src = img.getAttribute('still_src');
|
||||
}
|
||||
|
||||
function initThumbAnimation() {
|
||||
$j('.colThumbnail img').each(function() {
|
||||
this.addEventListener('mouseover', thumbnail_onmouseover, false);
|
||||
this.addEventListener('mouseout', thumbnail_onmouseout, false);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the event id's of the selected rows
|
||||
function getIdSelections() {
|
||||
var table = $j('#eventTable');
|
||||
|
@ -332,7 +313,7 @@ function initPage() {
|
|||
var thumb_ndx = $j('#eventTable tr th').filter(function() {
|
||||
return $j(this).text().trim() == 'Thumbnail';
|
||||
}).index();
|
||||
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom');
|
||||
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
|
||||
});
|
||||
|
||||
table.bootstrapTable('resetSearch');
|
||||
|
|
|
@ -31,11 +31,11 @@ function startDownload(file) {
|
|||
|
||||
function exportProgress() {
|
||||
if ( exportTimer ) {
|
||||
var tickerText = $('exportProgressTicker').get('text');
|
||||
var tickerText = $j('#exportProgressTicker').text();
|
||||
if ( tickerText.length < 1 || tickerText.length > 4 ) {
|
||||
$('exportProgressTicker').set('text', '.');
|
||||
$j('#exportProgressTicker').text('.');
|
||||
} else {
|
||||
$('exportProgressTicker').appendText('.');
|
||||
$j('#exportProgressTicker').append('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ function exportProgress() {
|
|||
function exportResponse(respObj, respText) {
|
||||
clearInterval(exportTimer);
|
||||
if ( respObj.result != 'Ok' ) {
|
||||
$('exportProgressTicker').set('text', respObj.message);
|
||||
$j('#exportProgressTicker').text(respObj.message);
|
||||
} else {
|
||||
$('exportProgressTicker').set('text', exportSucceededString);
|
||||
$j('#exportProgressTicker').text(exportSucceededString);
|
||||
startDownload.pass(decodeURIComponent(respObj.exportFile)).delay(1500);
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
var backBtn = $j('#backBtn');
|
||||
var table = $j('#framesTable');
|
||||
|
||||
// Called by bootstrap-table to retrieve zm frame data
|
||||
function ajaxRequest(params) {
|
||||
if ( params.data && params.data.filter ) {
|
||||
params.data.advsearch = params.data.filter;
|
||||
delete params.data.filter;
|
||||
}
|
||||
$j.getJSON(thisUrl + '?view=request&request=frames&task=query&eid='+eid, params.data)
|
||||
.done(function(data) {
|
||||
var rows = processRows(data.rows);
|
||||
// rearrange the result into what bootstrap-table expects
|
||||
console.log('Total: '+data.total);
|
||||
console.log('TotalnotFiltered: '+data.totalNotFiltered);
|
||||
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
function processRows(rows) {
|
||||
$j.each(rows, function(ndx, row) {
|
||||
// WIP: process each row here
|
||||
// VERIFY: Might not need to do anything here for the frames table
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
function thumbnail_onmouseover(event) {
|
||||
var img = event.target;
|
||||
img.src = '';
|
||||
|
@ -11,14 +39,16 @@ function thumbnail_onmouseout(event) {
|
|||
}
|
||||
|
||||
function initThumbAnimation() {
|
||||
if ( WEB_ANIMATE_THUMBS ) {
|
||||
$j('.colThumbnail img').each(function() {
|
||||
this.addEventListener('mouseover', thumbnail_onmouseover, false);
|
||||
this.addEventListener('mouseout', thumbnail_onmouseout, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processClicks(event, field, value, row, $element) {
|
||||
if ( field == 'FrameScore' ) {
|
||||
if ( field == 'Score' ) {
|
||||
window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FrameId);
|
||||
} else {
|
||||
window.location.assign('?view=frame&eid='+row.EventId+'&fid='+row.FrameId);
|
||||
|
@ -34,9 +64,10 @@ function detailFormatter(index, row, $detail) {
|
|||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
var backBtn = $j('#backBtn');
|
||||
var table = $j('#framesTable');
|
||||
// Remove the thumbnail column from the DOM if thumbnails are off globally
|
||||
if ( !WEB_LIST_THUMBS ) $j('th[data-field="Thumbnail"]').remove();
|
||||
|
||||
// Init the bootstrap-table
|
||||
table.bootstrapTable({icons: icons});
|
||||
|
@ -71,6 +102,25 @@ function initPage() {
|
|||
evt.preventDefault();
|
||||
window.location.reload(true);
|
||||
});
|
||||
|
||||
// Update table links each time after new data is loaded
|
||||
table.on('post-body.bs.table', function(data) {
|
||||
var type_ndx = $j('#framesTable tr th').filter(function() {
|
||||
return $j(this).text().trim() == 'Type';
|
||||
}).index();
|
||||
|
||||
$j('#framesTable tr').each(function(ndx, row) {
|
||||
var row = $j(row);
|
||||
var type = row.find('td').eq(type_ndx).text().trim();
|
||||
row.addClass(type.toLowerCase());
|
||||
});
|
||||
|
||||
var thumb_ndx = $j('#framesTable tr th').filter(function() {
|
||||
return $j(this).text().trim() == 'Thumbnail';
|
||||
}).index();
|
||||
var thmbClass = WEB_ANIMATE_THUMBS ? 'colThumbnail zoom' : 'colThumbnail';
|
||||
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass(thmbClass);
|
||||
});
|
||||
}
|
||||
|
||||
$j(document).ready(function() {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
var eid = <?php echo validInt($_REQUEST['eid']) ?>;
|
||||
var WEB_LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
|
||||
var WEB_ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;
|
|
@ -30,29 +30,28 @@ function changeDateTime(e) {
|
|||
window.location = uri;
|
||||
}
|
||||
|
||||
function datetime_change(newDate, oldData) {
|
||||
if (newDate !== oldData.lastVal) {
|
||||
changeDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
$j('#minTime').datetimepicker({
|
||||
timeFormat: "HH:mm:ss",
|
||||
dateFormat: "yy-mm-dd",
|
||||
maxDate: +0,
|
||||
constrainInput: false,
|
||||
onClose: function(newDate, oldData) {
|
||||
if (newDate !== oldData.lastVal) {
|
||||
changeDateTime();
|
||||
}
|
||||
}
|
||||
onClose: datetime_change
|
||||
});
|
||||
|
||||
$j('#maxTime').datetimepicker({
|
||||
timeFormat: "HH:mm:ss",
|
||||
dateFormat: "yy-mm-dd",
|
||||
minDate: $j('#minTime').val(),
|
||||
maxDate: +0,
|
||||
constrainInput: false,
|
||||
onClose: function(newDate, oldData) {
|
||||
if (newDate !== oldData.lastVal) {
|
||||
changeDateTime();
|
||||
}
|
||||
}
|
||||
onClose: datetime_change
|
||||
});
|
||||
}
|
||||
// Kick everything off
|
||||
|
|
|
@ -1,113 +1,13 @@
|
|||
var streamStatus;
|
||||
var auth_hash;
|
||||
var alarmState = STATE_IDLE;
|
||||
var lastAlarmState = STATE_IDLE;
|
||||
var backBtn = $j('#backBtn');
|
||||
var settingsBtn = $j('#settingsBtn');
|
||||
var enableAlmBtn = $j('#enableAlmBtn');
|
||||
var forceAlmBtn = $j('#forceAlmBtn');
|
||||
|
||||
function showEvents() {
|
||||
$('ptzControls').addClass('hidden');
|
||||
$('events').removeClass('hidden');
|
||||
if ( $('eventsControl') ) {
|
||||
$('eventsControl').addClass('hidden');
|
||||
}
|
||||
if ( $('controlControl') ) {
|
||||
$('controlControl').removeClass('hidden');
|
||||
}
|
||||
showMode = 'events';
|
||||
}
|
||||
|
||||
function showPtzControls() {
|
||||
$('events').addClass('hidden');
|
||||
$('ptzControls').removeClass('hidden');
|
||||
if ( $('eventsControl') ) {
|
||||
$('eventsControl').removeClass('hidden');
|
||||
}
|
||||
if ( $('controlControl') ) {
|
||||
$('controlControl').addClass('hidden');
|
||||
}
|
||||
showMode = 'control';
|
||||
}
|
||||
|
||||
function changeScale() {
|
||||
var scale = $('scale').get('value');
|
||||
var newWidth;
|
||||
var newHeight;
|
||||
if ( scale == '0' || scale == 'auto' ) {
|
||||
var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
|
||||
newWidth = newSize.width;
|
||||
newHeight = newSize.height;
|
||||
autoScale = newSize.autoScale;
|
||||
} else {
|
||||
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
|
||||
newWidth = monitorWidth * scale / SCALE_BASE;
|
||||
newHeight = monitorHeight * scale / SCALE_BASE;
|
||||
}
|
||||
|
||||
Cookie.write('zmWatchScale'+monitorId, scale, {duration: 10*365, samesite: 'strict'});
|
||||
|
||||
/*Stream could be an applet so can't use moo tools*/
|
||||
var streamImg = $('liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
streamImg.style.width = newWidth + 'px';
|
||||
streamImg.style.height = newHeight + 'px';
|
||||
|
||||
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
|
||||
} else {
|
||||
console.error('No element found for liveStream'+monitorId);
|
||||
}
|
||||
}
|
||||
|
||||
var alarmState = STATE_IDLE;
|
||||
var lastAlarmState = STATE_IDLE;
|
||||
|
||||
function setAlarmState( currentAlarmState ) {
|
||||
alarmState = currentAlarmState;
|
||||
|
||||
var stateClass = '';
|
||||
if ( alarmState == STATE_ALARM ) {
|
||||
stateClass = 'alarm';
|
||||
} else if ( alarmState == STATE_ALERT ) {
|
||||
stateClass = 'alert';
|
||||
}
|
||||
$('stateValue').set('text', stateStrings[alarmState]);
|
||||
if ( stateClass ) {
|
||||
$('stateValue').setProperty('class', stateClass);
|
||||
} else {
|
||||
$('stateValue').removeProperty('class');
|
||||
}
|
||||
|
||||
var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT );
|
||||
var wasAlarmed = ( lastAlarmState == STATE_ALARM || lastAlarmState == STATE_ALERT );
|
||||
|
||||
var newAlarm = ( isAlarmed && !wasAlarmed );
|
||||
var oldAlarm = ( !isAlarmed && wasAlarmed );
|
||||
|
||||
if ( newAlarm ) {
|
||||
if ( SOUND_ON_ALARM ) {
|
||||
// Enable the alarm sound
|
||||
if ( !canPlayPauseAudio ) {
|
||||
$('alarmSound').removeClass('hidden');
|
||||
} else {
|
||||
$('MediaPlayer').Play();
|
||||
}
|
||||
}
|
||||
if ( POPUP_ON_ALARM ) {
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
if ( oldAlarm ) { // done with an event do a refresh
|
||||
if ( SOUND_ON_ALARM ) {
|
||||
// Disable alarm sound
|
||||
if ( !canPlayPauseAudio ) {
|
||||
$('alarmSound').addClass('hidden');
|
||||
} else {
|
||||
$('MediaPlayer').Stop();
|
||||
}
|
||||
}
|
||||
eventCmdQuery();
|
||||
}
|
||||
|
||||
lastAlarmState = alarmState;
|
||||
} // end function setAlarmState( currentAlarmState )
|
||||
var table = $j('#eventList');
|
||||
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
|
||||
|
||||
if ( monitorType != 'WebSite' ) {
|
||||
var streamCmdParms = 'view=request&request=stream&connkey='+connKey;
|
||||
|
@ -126,16 +26,178 @@ if ( monitorType != 'WebSite' ) {
|
|||
var streamCmdTimer = null;
|
||||
}
|
||||
|
||||
var streamStatus;
|
||||
/*
|
||||
This is the format of the json object sent by bootstrap-table
|
||||
|
||||
var params =
|
||||
{
|
||||
"type":"get",
|
||||
"data":
|
||||
{
|
||||
"search":"some search text",
|
||||
"sort":"StartDateTime",
|
||||
"order":"asc",
|
||||
"offset":0,
|
||||
"limit":25
|
||||
"filter":
|
||||
{
|
||||
"Name":"some advanced search text"
|
||||
"StartDateTime":"some more advanced search text"
|
||||
}
|
||||
},
|
||||
"cache":true,
|
||||
"contentType":"application/json",
|
||||
"dataType":"json"
|
||||
};
|
||||
*/
|
||||
|
||||
// Called by bootstrap-table to retrieve zm event data
|
||||
function ajaxRequest(params) {
|
||||
// Maintain legacy behavior by statically setting these parameters
|
||||
params.data.order = 'desc';
|
||||
params.data.limit = maxDisplayEvents;
|
||||
params.data.sort = 'Id';
|
||||
|
||||
$j.getJSON(thisUrl + '?view=request&request=events&task=query'+filterQuery, params.data)
|
||||
.done(function(data) {
|
||||
var rows = processRows(data.rows);
|
||||
// rearrange the result into what bootstrap-table expects
|
||||
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
function processRows(rows) {
|
||||
$j.each(rows, function(ndx, row) {
|
||||
var eid = row.Id;
|
||||
|
||||
row.Delete = '<i class="fa fa-trash text-danger"></i>';
|
||||
row.Id = '<a href="?view=event&eid=' + eid + filterQuery + '">' + eid + '</a>';
|
||||
row.Name = '<a href="?view=event&eid=' + eid + filterQuery + '">' + row.Name + '</a>';
|
||||
row.Frames = '<a href="?view=frames&eid=' + eid + '">' + row.Frames + '</a>';
|
||||
row.AlarmFrames = '<a href="?view=frames&eid=' + eid + '">' + row.AlarmFrames + '</a>';
|
||||
row.MaxScore = '<a href="?view=frame&eid=' + eid + '&fid=0">' + row.MaxScore + '</a>';
|
||||
if ( LIST_THUMBS ) row.Thumbnail = '<a href="?view=event&eid=' + eid + filterQuery + '&page=1">' + row.imgHtml + '</a>';
|
||||
});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function showEvents() {
|
||||
$j('#ptzControls').addClass('hidden');
|
||||
$j('#events').removeClass('hidden');
|
||||
if ( $j('#eventsControl') ) {
|
||||
$j('#eventsControl').addClass('hidden');
|
||||
}
|
||||
if ( $j('#controlControl') ) {
|
||||
$j('#controlControl').removeClass('hidden');
|
||||
}
|
||||
showMode = 'events';
|
||||
}
|
||||
|
||||
function showPtzControls() {
|
||||
$j('#events').addClass('hidden');
|
||||
$j('#ptzControls').removeClass('hidden');
|
||||
if ( $j('#eventsControl') ) {
|
||||
$j('#eventsControl').removeClass('hidden');
|
||||
}
|
||||
if ( $j('#controlControl') ) {
|
||||
$j('#controlControl').addClass('hidden');
|
||||
}
|
||||
showMode = 'control';
|
||||
}
|
||||
|
||||
function changeScale() {
|
||||
var scale = $j('#scale').val();
|
||||
var newWidth;
|
||||
var newHeight;
|
||||
if ( scale == '0' || scale == 'auto' ) {
|
||||
var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
|
||||
newWidth = newSize.width;
|
||||
newHeight = newSize.height;
|
||||
autoScale = newSize.autoScale;
|
||||
} else {
|
||||
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
|
||||
newWidth = monitorWidth * scale / SCALE_BASE;
|
||||
newHeight = monitorHeight * scale / SCALE_BASE;
|
||||
}
|
||||
|
||||
Cookie.write('zmWatchScale'+monitorId, scale, {duration: 10*365, samesite: 'strict'});
|
||||
|
||||
var streamImg = $j('#liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
var oldSrc = streamImg.attr('src');
|
||||
var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
|
||||
|
||||
streamImg.width( newWidth );
|
||||
streamImg.height( newHeight );
|
||||
streamImg.src = newSrc;
|
||||
} else {
|
||||
console.error('No element found for liveStream'+monitorId);
|
||||
}
|
||||
}
|
||||
|
||||
function setAlarmState( currentAlarmState ) {
|
||||
alarmState = currentAlarmState;
|
||||
|
||||
var stateClass = '';
|
||||
if ( alarmState == STATE_ALARM ) {
|
||||
stateClass = 'alarm';
|
||||
} else if ( alarmState == STATE_ALERT ) {
|
||||
stateClass = 'alert';
|
||||
}
|
||||
$j('#stateValue').text(stateStrings[alarmState]);
|
||||
if ( stateClass ) {
|
||||
$j('#stateValue').addClass(stateClass);
|
||||
} else {
|
||||
$j('#stateValue').removeClass();
|
||||
}
|
||||
|
||||
var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT );
|
||||
var wasAlarmed = ( lastAlarmState == STATE_ALARM || lastAlarmState == STATE_ALERT );
|
||||
|
||||
var newAlarm = ( isAlarmed && !wasAlarmed );
|
||||
var oldAlarm = ( !isAlarmed && wasAlarmed );
|
||||
|
||||
if ( newAlarm ) {
|
||||
table.bootstrapTable('refresh');
|
||||
if ( SOUND_ON_ALARM ) {
|
||||
// Enable the alarm sound
|
||||
if ( !canPlayPauseAudio ) {
|
||||
$j('#alarmSound').removeClass('hidden');
|
||||
} else {
|
||||
$j('#MediaPlayer').trigger('play');
|
||||
}
|
||||
}
|
||||
if ( POPUP_ON_ALARM ) {
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
if ( oldAlarm ) { // done with an event do a refresh
|
||||
table.bootstrapTable('refresh');
|
||||
if ( SOUND_ON_ALARM ) {
|
||||
// Disable alarm sound
|
||||
if ( !canPlayPauseAudio ) {
|
||||
$j('#alarmSound').addClass('hidden');
|
||||
} else {
|
||||
$j('#MediaPlayer').trigger('pause');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastAlarmState = alarmState;
|
||||
} // end function setAlarmState( currentAlarmState )
|
||||
|
||||
function getStreamCmdError(text, error) {
|
||||
console.log(error);
|
||||
// Error are normally due to failed auth. reload the page.
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function getStreamCmdFailure(xhr) {
|
||||
console.log(xhr);
|
||||
}
|
||||
|
||||
function getStreamCmdResponse(respObj, respText) {
|
||||
watchdogOk('stream');
|
||||
if ( streamCmdTimer ) {
|
||||
|
@ -145,35 +207,36 @@ function getStreamCmdResponse(respObj, respText) {
|
|||
// The get status command can get backed up, in which case we won't be able to get the semaphore and will exit.
|
||||
if ( respObj.status ) {
|
||||
streamStatus = respObj.status;
|
||||
$('fpsValue').set('text', streamStatus.fps);
|
||||
$j('#fpsValue').text(streamStatus.fps);
|
||||
|
||||
setAlarmState(streamStatus.state);
|
||||
|
||||
$('levelValue').set('text', streamStatus.level);
|
||||
$j('#levelValue').text(streamStatus.level);
|
||||
var newClass = 'ok';
|
||||
if ( streamStatus.level > 95 ) {
|
||||
$('levelValue').className = 'alarm';
|
||||
newClass = 'alarm';
|
||||
} else if ( streamStatus.level > 80 ) {
|
||||
$('levelValue').className = 'alert';
|
||||
} else {
|
||||
$('levelValue').className = 'ok';
|
||||
newClass = 'alert';
|
||||
}
|
||||
$j('#levelValue').removeClass();
|
||||
$j('#levelValue').addClass(newClass);
|
||||
|
||||
var delayString = secsToTime(streamStatus.delay);
|
||||
|
||||
if ( streamStatus.paused == true ) {
|
||||
$('modeValue').set('text', 'Paused');
|
||||
$('rate').addClass('hidden');
|
||||
$('delayValue').set('text', delayString);
|
||||
$('delay').removeClass('hidden');
|
||||
$('level').removeClass('hidden');
|
||||
$j('#modeValue').text('Paused');
|
||||
$j('#rate').addClass('hidden');
|
||||
$j('#delayValue').text(delayString);
|
||||
$j('#delay').removeClass('hidden');
|
||||
$j('#level').removeClass('hidden');
|
||||
streamCmdPause(false);
|
||||
} else if ( streamStatus.delayed == true ) {
|
||||
$('modeValue').set('text', 'Replay');
|
||||
$('rateValue').set('text', streamStatus.rate);
|
||||
$('rate').removeClass('hidden');
|
||||
$('delayValue').set('text', delayString);
|
||||
$('delay').removeClass('hidden');
|
||||
$('level').removeClass('hidden');
|
||||
$j('#modeValue').text('Replay');
|
||||
$j('#rateValue').text(streamStatus.rate);
|
||||
$j('#rate').removeClass('hidden');
|
||||
$j('#delayValue').text(delayString);
|
||||
$j('#delay').removeClass('hidden');
|
||||
$j('#level').removeClass('hidden');
|
||||
if ( streamStatus.rate == 1 ) {
|
||||
streamCmdPlay(false);
|
||||
} else if ( streamStatus.rate > 0 ) {
|
||||
|
@ -190,14 +253,14 @@ function getStreamCmdResponse(respObj, respText) {
|
|||
}
|
||||
} // rate
|
||||
} else {
|
||||
$('modeValue').set( 'text', 'Live' );
|
||||
$('rate').addClass( 'hidden' );
|
||||
$('delay').addClass( 'hidden' );
|
||||
$('level').addClass( 'hidden' );
|
||||
$j('#modeValue').text( 'Live' );
|
||||
$j('#rate').addClass( 'hidden' );
|
||||
$j('#delay').addClass( 'hidden' );
|
||||
$j('#level').addClass( 'hidden' );
|
||||
streamCmdPlay(false);
|
||||
} // end if paused or delayed
|
||||
|
||||
$('zoomValue').set('text', streamStatus.zoom);
|
||||
$j('zoomValue').text(streamStatus.zoom);
|
||||
if ( streamStatus.zoom == '1.0' ) {
|
||||
setButtonState('zoomOutBtn', 'unavail');
|
||||
} else {
|
||||
|
@ -227,13 +290,15 @@ function getStreamCmdResponse(respObj, respText) {
|
|||
if ( streamStatus.auth ) {
|
||||
auth_hash = streamStatus.auth;
|
||||
// Try to reload the image stream.
|
||||
var streamImg = $('liveStream');
|
||||
var streamImg = $j('#liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
var oldSrc = streamImg.attr('src');
|
||||
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
streamImg.src = newSrc;
|
||||
}
|
||||
streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
eventCmdParms = eventCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
table.bootstrapTable('refresh');
|
||||
controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
|
||||
} // end if have a new auth hash
|
||||
} // end if respObj.status
|
||||
|
@ -243,9 +308,12 @@ function getStreamCmdResponse(respObj, respText) {
|
|||
// If it's an auth error, we should reload the whole page.
|
||||
window.location.reload();
|
||||
if ( 0 ) {
|
||||
var streamImg = $('liveStream'+monitorId);
|
||||
var streamImg = $j('#liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
||||
var oldSrc = streamImg.attr('src');
|
||||
var newSrc = oldSrc.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
||||
|
||||
streamImg.src = newSrc;
|
||||
console.log('Changing livestream src to ' + streamImg.src);
|
||||
} else {
|
||||
console.log('Unable to find streamImg liveStream');
|
||||
|
@ -427,7 +495,7 @@ function getStatusCmdResponse(respObj, respText) {
|
|||
}
|
||||
|
||||
if ( respObj.result == 'Ok' ) {
|
||||
$('fpsValue').set('text', respObj.monitor.FrameRate);
|
||||
$j('#fpsValue').text(respObj.monitor.FrameRate);
|
||||
setAlarmState(respObj.monitor.Status);
|
||||
} else {
|
||||
checkStreamForErrors('getStatusCmdResponse', respObj);
|
||||
|
@ -503,187 +571,6 @@ function cmdForce() {
|
|||
}
|
||||
}
|
||||
|
||||
function getActResponse( respObj, respText ) {
|
||||
if ( respObj.result == 'Ok' ) {
|
||||
if ( respObj.refreshParent && window.opener ) {
|
||||
console.log('refreshing parent');
|
||||
window.opener.location.reload();
|
||||
}
|
||||
}
|
||||
eventCmdQuery();
|
||||
}
|
||||
|
||||
function deleteEvent(event, eventId) {
|
||||
var actParms = 'view=request&request=event&action=delete&id='+eventId;
|
||||
if ( auth_hash ) {
|
||||
actParms += '&auth='+auth_hash;
|
||||
}
|
||||
var actReq = new Request.JSON( {
|
||||
url: thisUrl,
|
||||
method: 'post',
|
||||
timeout: 3000,
|
||||
onSuccess: getActResponse
|
||||
} );
|
||||
actReq.send(actParms);
|
||||
event.stop();
|
||||
}
|
||||
|
||||
if ( monitorType != 'WebSite' ) {
|
||||
var eventCmdParms = "view=request&request=status&entity=events&id="+monitorId+"&count="+maxDisplayEvents+"&sort=Id%20desc";
|
||||
if ( auth_hash ) {
|
||||
eventCmdParms += '&auth='+auth_hash;
|
||||
}
|
||||
var eventCmdReq = new Request.JSON( {
|
||||
url: monitorUrl,
|
||||
method: 'get',
|
||||
timeout: AJAX_TIMEOUT,
|
||||
link: 'cancel',
|
||||
onSuccess: getEventCmdResponse,
|
||||
onTimeout: eventCmdQuery
|
||||
} );
|
||||
var eventCmdTimer = null;
|
||||
var eventCmdFirst = true;
|
||||
}
|
||||
|
||||
function highlightRow( row ) {
|
||||
$(row).toggleClass('highlight');
|
||||
}
|
||||
|
||||
function getEventCmdResponse( respObj, respText ) {
|
||||
watchdogOk('event');
|
||||
if ( eventCmdTimer ) {
|
||||
eventCmdTimer = clearTimeout(eventCmdTimer);
|
||||
}
|
||||
|
||||
if ( respObj.result == 'Ok' ) {
|
||||
var dbEvents = respObj.events.reverse();
|
||||
var eventList = $('eventList');
|
||||
var eventListBody = $(eventList).getElement('tbody');
|
||||
var eventListRows = $(eventListBody).getElements('tr');
|
||||
|
||||
eventListRows.each( function(row) {
|
||||
row.removeClass('updated');
|
||||
} );
|
||||
|
||||
for ( var i = 0; i < dbEvents.length; i++ ) {
|
||||
var zm_event = dbEvents[i];
|
||||
var row = $('event'+zm_event.Id);
|
||||
var newEvent = (row == null ? true : false);
|
||||
if ( newEvent ) {
|
||||
row = new Element('tr', {'id': 'event'+zm_event.Id});
|
||||
new Element('td', {'class': 'colId'}).inject(row);
|
||||
new Element('td', {'class': 'colName'}).inject(row);
|
||||
new Element('td', {'class': 'colTime'}).inject(row);
|
||||
new Element('td', {'class': 'colSecs'}).inject(row);
|
||||
new Element('td', {'class': 'colFrames'}).inject(row);
|
||||
new Element('td', {'class': 'colScore'}).inject(row);
|
||||
new Element('td', {'class': 'colDelete'}).inject(row);
|
||||
|
||||
var link = new Element('a', {
|
||||
'href': '#',
|
||||
'events': {
|
||||
'click': openEvent.pass( [
|
||||
zm_event.Id,
|
||||
'&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1'
|
||||
] )
|
||||
}
|
||||
});
|
||||
link.set('text', zm_event.Id);
|
||||
link.inject(row.getElement('td.colId'));
|
||||
|
||||
link = new Element('a', {
|
||||
'href': '#',
|
||||
'events': {
|
||||
'click': openEvent.pass( [
|
||||
zm_event.Id,
|
||||
'&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1'
|
||||
] )
|
||||
}
|
||||
});
|
||||
link.set('text', zm_event.Name);
|
||||
link.inject(row.getElement('td.colName'));
|
||||
|
||||
row.getElement('td.colTime').set('text', zm_event.StartDateTime);
|
||||
row.getElement('td.colSecs').set('text', zm_event.Length);
|
||||
|
||||
link = new Element('a', {'href': '#', 'events': {'click': openFrames.pass( [zm_event.Id] )}});
|
||||
link.set('text', zm_event.Frames+'/'+zm_event.AlarmFrames);
|
||||
link.inject(row.getElement('td.colFrames'));
|
||||
|
||||
link = new Element('a', {'href': '#', 'events': {'click': openFrame.pass( [zm_event.Id, '0'] )}});
|
||||
link.set('text', zm_event.AvgScore+'/'+zm_event.MaxScore);
|
||||
link.inject(row.getElement('td.colScore'));
|
||||
|
||||
link = new Element('button', {
|
||||
'type': 'button',
|
||||
'title': deleteString,
|
||||
'data-event-id': zm_event.Id,
|
||||
'events': {
|
||||
'click': function(e) {
|
||||
var event_id = e.target.getAttribute('data-event-id');
|
||||
if ( !event_id ) {
|
||||
console.log('No event id in deleteEvent');
|
||||
console.log(e);
|
||||
} else {
|
||||
deleteEvent(e, event_id);
|
||||
}
|
||||
},
|
||||
'mouseover': highlightRow.pass(row),
|
||||
'mouseout': highlightRow.pass(row)
|
||||
}
|
||||
});
|
||||
link.set('text', 'X');
|
||||
link.inject(row.getElement('td.colDelete'));
|
||||
|
||||
if ( i == 0 ) {
|
||||
row.inject($(eventListBody));
|
||||
} else {
|
||||
row.inject($(eventListBody), 'top');
|
||||
if ( !eventCmdFirst ) {
|
||||
row.addClass('recent');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
row.getElement('td.colName a').set('text', zm_event.Name);
|
||||
row.getElement('td.colSecs').set('text', zm_event.Length);
|
||||
row.getElement('td.colFrames a').set('text', zm_event.Frames+'/'+zm_event.AlarmFrames);
|
||||
row.getElement('td.colScore a').set('text', zm_event.AvgScore+'/'+zm_event.MaxScore);
|
||||
row.removeClass('recent');
|
||||
}
|
||||
row.addClass('updated');
|
||||
} // end foreach event
|
||||
|
||||
var rows = $(eventListBody).getElements('tr');
|
||||
for ( var i = 0; i < rows.length; i++ ) {
|
||||
if ( !rows[i].hasClass('updated') ) {
|
||||
rows[i].destroy();
|
||||
rows.splice( i, 1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
while ( rows.length > maxDisplayEvents ) {
|
||||
rows[rows.length-1].destroy();
|
||||
rows.length--;
|
||||
}
|
||||
} else {
|
||||
checkStreamForErrors('getEventCmdResponse', respObj);
|
||||
} // end if objresult == ok
|
||||
|
||||
var eventCmdTimeout = eventsRefreshTimeout;
|
||||
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) {
|
||||
eventCmdTimeout = eventCmdTimeout/5;
|
||||
}
|
||||
eventCmdTimer = eventCmdQuery.delay(eventCmdTimeout);
|
||||
eventCmdFirst = false;
|
||||
}
|
||||
|
||||
function eventCmdQuery() {
|
||||
if ( eventCmdTimer ) { // avoid firing another if we are firing one
|
||||
eventCmdTimer = clearTimeout(eventCmdTimer);
|
||||
}
|
||||
eventCmdReq.send(eventCmdParms);
|
||||
}
|
||||
|
||||
if ( monitorType != 'WebSite' ) {
|
||||
var controlParms = 'view=request&request=control&id='+monitorId;
|
||||
if ( auth_hash ) {
|
||||
|
@ -717,13 +604,15 @@ function controlCmd(event) {
|
|||
var locParms = '';
|
||||
if ( event && (xtell || ytell) ) {
|
||||
var target = event.target;
|
||||
var coords = $(target).getCoordinates();
|
||||
var offset = $j(target).offset();
|
||||
var width = $j(target).width();
|
||||
var height = $j(target).height();
|
||||
|
||||
var x = event.pageX - coords.left;
|
||||
var y = event.pageY - coords.top;
|
||||
var x = event.pageX - offset.left;
|
||||
var y = event.pageY - offset.top;
|
||||
|
||||
if ( xtell ) {
|
||||
var xge = parseInt((x*100)/coords.width);
|
||||
var xge = parseInt((x*100)/width);
|
||||
if ( xtell == -1 ) {
|
||||
xge = 100 - xge;
|
||||
} else if ( xtell == 2 ) {
|
||||
|
@ -732,7 +621,7 @@ function controlCmd(event) {
|
|||
locParms += '&xge='+xge;
|
||||
}
|
||||
if ( ytell ) {
|
||||
var yge = parseInt((y*100)/coords.height);
|
||||
var yge = parseInt((y*100)/height);
|
||||
if ( ytell == -1 ) {
|
||||
yge = 100 - yge;
|
||||
} else if ( ytell == 2 ) {
|
||||
|
@ -763,11 +652,14 @@ function fetchImage( streamImage ) {
|
|||
}
|
||||
|
||||
function handleClick( event ) {
|
||||
var $target = $(event.target);
|
||||
var scaleX = parseInt(monitorWidth / $target.getWidth());
|
||||
var scaleY = parseInt(monitorHeight / $target.getHeight());
|
||||
var x = (event.page.x - $target.getLeft()) * scaleX;
|
||||
var y = (event.page.y - $target.getTop()) * scaleY;
|
||||
var target = event.target;
|
||||
var width = $j(target).width();
|
||||
var height = $j(target).height();
|
||||
|
||||
var scaleX = parseInt(monitorWidth / width);
|
||||
var scaleY = parseInt(monitorHeight / height);
|
||||
var x = (event.page.x - target.getLeft()) * scaleX;
|
||||
var y = (event.page.y - target.getTop()) * scaleY;
|
||||
|
||||
if ( showMode == 'events' || !imageControlMode ) {
|
||||
if ( event.shift ) {
|
||||
|
@ -784,11 +676,11 @@ function handleClick( event ) {
|
|||
|
||||
function appletRefresh() {
|
||||
if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) {
|
||||
var streamImg = $('liveStream'+monitorId);
|
||||
var streamImg = $j('#liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
var parent = streamImg.getParent();
|
||||
streamImg.dispose();
|
||||
streamImg.inject( parent );
|
||||
var parent = streamImg.parent();
|
||||
streamImg.remove();
|
||||
streamImg.append( parent );
|
||||
} else {
|
||||
console.error("Nothing found for liveStream"+monitorId);
|
||||
}
|
||||
|
@ -802,14 +694,12 @@ function appletRefresh() {
|
|||
|
||||
var watchdogInactive = {
|
||||
'stream': false,
|
||||
'status': false,
|
||||
'event': false
|
||||
'status': false
|
||||
};
|
||||
|
||||
var watchdogFunctions = {
|
||||
'stream': streamCmdQuery,
|
||||
'status': statusCmdQuery,
|
||||
'event': eventCmdQuery
|
||||
};
|
||||
|
||||
//Make sure the various refreshes are still taking effect
|
||||
|
@ -832,15 +722,9 @@ function reloadWebSite() {
|
|||
}
|
||||
|
||||
function updatePresetLabels() {
|
||||
var form = $('ctrlPresetForm');
|
||||
var preset_ddm = form.elements['preset'];
|
||||
var lblNdx = $j( '#ctrlPresetForm option:selected' ).val();
|
||||
|
||||
var presetIndex = preset_ddm[preset_ddm.selectedIndex].value;
|
||||
if ( labels[presetIndex] ) {
|
||||
form.newLabel.value = labels[presetIndex];
|
||||
} else {
|
||||
form.newLabel.value = '';
|
||||
}
|
||||
$j('#newLabel').val(labels[lblNdx]);
|
||||
}
|
||||
|
||||
function getCtrlPresetModal() {
|
||||
|
@ -872,6 +756,44 @@ function getSettingsModal() {
|
|||
.fail(logAjaxFail);
|
||||
}
|
||||
|
||||
function processClicks(event, field, value, row, $element) {
|
||||
if ( field == 'Delete' ) {
|
||||
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
|
||||
.done(function(data) {
|
||||
insertModalHtml('deleteConfirm', data.html);
|
||||
manageDelConfirmModalBtns();
|
||||
$j('#deleteConfirm').data('eid', row.Id.replace(/(<([^>]+)>)/gi, ''));
|
||||
$j('#deleteConfirm').modal('show');
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage the DELETE CONFIRMATION modal button
|
||||
function manageDelConfirmModalBtns() {
|
||||
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
|
||||
if ( ! canEditEvents ) {
|
||||
enoperm();
|
||||
return;
|
||||
}
|
||||
|
||||
var eid = $j('#deleteConfirm').data('eid');
|
||||
|
||||
evt.preventDefault();
|
||||
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eid)
|
||||
.done( function(data) {
|
||||
table.bootstrapTable('refresh');
|
||||
$j('#deleteConfirm').modal('hide');
|
||||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
|
||||
// Manage the CANCEL modal button
|
||||
document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) {
|
||||
$j('#deleteConfirm').modal('hide');
|
||||
});
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
if ( canViewControl ) {
|
||||
// Load the PTZ Preset modal into the DOM
|
||||
|
@ -889,9 +811,6 @@ function initPage() {
|
|||
watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2);
|
||||
}
|
||||
|
||||
eventCmdTimer = eventCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
|
||||
watchdogCheck.pass('event').periodical(eventsRefreshTimeout*2);
|
||||
|
||||
if ( canStreamNative || (streamMode == 'single') ) {
|
||||
var streamImg = $('imageFeed').getElement('img');
|
||||
if ( !streamImg ) {
|
||||
|
@ -924,6 +843,7 @@ function initPage() {
|
|||
} else if ( monitorRefresh > 0 ) {
|
||||
setInterval(reloadWebSite, monitorRefresh*1000);
|
||||
}
|
||||
|
||||
// Manage the BACK button
|
||||
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
|
||||
evt.preventDefault();
|
||||
|
@ -948,6 +868,28 @@ function initPage() {
|
|||
// Only enable the settings button for local cameras
|
||||
settingsBtn.prop('disabled', !canViewControl);
|
||||
if ( monitorType != 'Local' ) settingsBtn.hide();
|
||||
|
||||
// Init the bootstrap-table
|
||||
if ( monitorType != 'WebSite' ) table.bootstrapTable({icons: icons});
|
||||
|
||||
// Update table rows each time after new data is loaded
|
||||
table.on('post-body.bs.table', function(data) {
|
||||
$j('#eventList tr:contains("New Event")').addClass('recent');
|
||||
});
|
||||
|
||||
// Take appropriate action when the user clicks on a cell
|
||||
table.on('click-cell.bs.table', processClicks);
|
||||
|
||||
// Some toolbar events break the thumbnail animation, so re-init eventlistener
|
||||
table.on('all.bs.table', initThumbAnimation);
|
||||
|
||||
// Update table links each time after new data is loaded
|
||||
table.on('post-body.bs.table', function(data) {
|
||||
var thumb_ndx = $j('#eventList tr th').filter(function() {
|
||||
return $j(this).text().trim() == 'Thumbnail';
|
||||
}).index();
|
||||
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
|
||||
});
|
||||
} // initPage
|
||||
|
||||
// Kick everything off
|
||||
|
|
|
@ -50,6 +50,7 @@ var SCALE_BASE = <?php echo SCALE_BASE ?>;
|
|||
|
||||
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>;
|
||||
var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>;
|
||||
var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
|
||||
|
||||
var streamMode = "<?php echo $streamMode ?>";
|
||||
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";
|
||||
|
|
|
@ -27,7 +27,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
|
|||
?>
|
||||
<body>
|
||||
<?php echo getNavBarHTML() ?>
|
||||
<div id="page" class="px-3">
|
||||
<div id="page" class="px-3 table-responsive-sm">
|
||||
|
||||
<div id="logSummary" class="text-center">
|
||||
<?php echo translate('State') ?>: <span id="logState"></span> -
|
||||
|
@ -62,7 +62,6 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
|
|||
data-toolbar="#toolbar"
|
||||
data-show-fullscreen="true"
|
||||
data-maintain-meta-data="true"
|
||||
data-mobile-responsive="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-show-jump-to="true"
|
||||
data-auto-refresh="true"
|
||||
|
|
|
@ -369,8 +369,10 @@ $fastblendopts_alarm = array(
|
|||
);
|
||||
|
||||
$label_size = array(
|
||||
1 => translate('Default'),
|
||||
2 => translate('Large'),
|
||||
1 => translate('Small'),
|
||||
2 => translate('Default'),
|
||||
3 => translate('Large'),
|
||||
4 => translate('Extra Large'),
|
||||
);
|
||||
|
||||
$codecs = array(
|
||||
|
@ -1030,7 +1032,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor
|
|||
<td><input type="number" name="newMonitor[AlarmFrameCount]" value="<?php echo validHtmlStr($monitor->AlarmFrameCount()) ?>" min="1"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-right pr-3"><?php echo translate('EstimatedRamUse') ?></td>
|
||||
<td class="text-right pr-3"><?php echo translate('Estimated Ram Use') ?></td>
|
||||
<td id="estimated_ram_use"><?php echo human_filesize($monitor->ImageBufferCount() * $monitor->Width() * $monitor->Height() * $monitor->Colours(), 0) ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
|
|
|
@ -38,30 +38,24 @@ if ( isset($_REQUEST['maxTime']) ) {
|
|||
$maxTime = strftime('%FT%T',time() - 3600);
|
||||
}
|
||||
|
||||
$filter = array(
|
||||
'Query' => array(
|
||||
'terms' => array(
|
||||
array('attr'=>'StartDateTime', 'op'=>'>=', 'val'=>$minTime, 'obr'=>'1'),
|
||||
array('attr'=>'StartDateTime', 'op'=>'<=', 'val'=>$maxTime, 'cnj'=>'and', 'cbr'=>'1'),
|
||||
)
|
||||
),
|
||||
);
|
||||
$filter = new ZM\Filter();
|
||||
$filter->addTerm(array('attr'=>'StartDateTime', 'op'=>'>=', 'val'=>$minTime, 'obr'=>'1'));
|
||||
$filter->addTerm(array('attr'=>'StartDateTime', 'op'=>'<=', 'val'=>$maxTime, 'cnj'=>'and', 'cbr'=>'1'));
|
||||
if ( count($selected_monitor_ids) ) {
|
||||
$filter['Query']['terms'][] = (array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',', $selected_monitor_ids), 'cnj'=>'and'));
|
||||
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',', $selected_monitor_ids), 'cnj'=>'and'));
|
||||
} else if ( ( $group_id != 0 || isset($_SESSION['ServerId']) || isset($_SESSION['StorageId']) || isset($_SESSION['Status']) ) ) {
|
||||
# this should be redundant
|
||||
for ( $i=0; $i < count($displayMonitors); $i++ ) {
|
||||
if ( $i == 0 ) {
|
||||
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'and', 'obr'=>'1');
|
||||
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'and', 'obr'=>'1'));
|
||||
} else if ( $i == count($displayMonitors)-1 ) {
|
||||
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or', 'cbr'=>'1');
|
||||
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or', 'cbr'=>'1'));
|
||||
} else {
|
||||
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or');
|
||||
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or'));
|
||||
}
|
||||
}
|
||||
}
|
||||
parseFilter($filter);
|
||||
$filterQuery = $filter['query'];
|
||||
$filterQuery = $filter->querystring();
|
||||
ZM\Debug($filterQuery);
|
||||
|
||||
$eventsSql = 'SELECT *,
|
||||
|
@ -113,17 +107,16 @@ while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
|
|||
|
||||
?>
|
||||
<body>
|
||||
<?php echo $navbar ?>
|
||||
<form name="monitorForm" method="get" action="?">
|
||||
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
||||
<input type="hidden" name="action" value=""/>
|
||||
|
||||
<?php echo $navbar ?>
|
||||
<div class="filterBar">
|
||||
<?php echo $filterbar ?>
|
||||
<div id="DateTimeDiv">
|
||||
<label>Event Start Time</label>
|
||||
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>" oninput="this.form.submit();"/> to
|
||||
<input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>" oninput="this.form.submit();"/>
|
||||
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>"/> to
|
||||
<input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>"/>
|
||||
</div>
|
||||
</div><!--FilterBar-->
|
||||
|
||||
|
@ -150,12 +143,7 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
|||
$Monitor = new ZM\Monitor($monitor);
|
||||
$montagereview_link = '?view=montagereview&live=0&MonitorId='.$monitor['Id'].'&minTime='.$minTime.'&maxTime='.$maxTime;
|
||||
|
||||
$monitor_filter = addFilterTerm(
|
||||
$filter,
|
||||
count($filter['Query']['terms']),
|
||||
array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'=', 'val'=>$monitor['Id'])
|
||||
);
|
||||
parseFilter($monitor_filter);
|
||||
$monitor_filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'=', 'val'=>$monitor['Id']));
|
||||
|
||||
if ( isset($EventsByMonitor[$Monitor->Id()]) ) {
|
||||
$EventCounts = $EventsByMonitor[$Monitor->Id()];
|
||||
|
@ -175,24 +163,12 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
|||
}
|
||||
|
||||
if ( count($FileMissing) ) {
|
||||
$FileMissing_filter = array(
|
||||
'Query' => array(
|
||||
'terms' => array(
|
||||
array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $FileMissing)))
|
||||
)
|
||||
)
|
||||
);
|
||||
parseFilter($FileMissing_filter);
|
||||
$FileMissing_filter = new ZM\Filter();
|
||||
$FileMissing_filter->addTerm(array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $FileMissing))));
|
||||
}
|
||||
if ( count($ZeroSize) ) {
|
||||
$ZeroSize_filter = array(
|
||||
'Query' => array(
|
||||
'terms' => array(
|
||||
array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $ZeroSize)))
|
||||
)
|
||||
)
|
||||
);
|
||||
parseFilter($ZeroSize_filter);
|
||||
$ZeroSize_filter = new ZM\Filter();
|
||||
$ZeroSize_filter->addTerm(array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $ZeroSize))));
|
||||
}
|
||||
?>
|
||||
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
|
||||
|
@ -202,24 +178,27 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
|||
<div class="small text-nowrap text-muted">
|
||||
<?php echo implode('<br/>',
|
||||
array_map(function($group_id){
|
||||
$Group = new ZM\Group($group_id);
|
||||
$Group = ZM\Group::find_one(array('Id'=>$group_id));
|
||||
if ( $Group ) {
|
||||
$Groups = $Group->Parents();
|
||||
array_push($Groups, $Group);
|
||||
return implode(' > ', array_map(function($Group){ return '<a href="?view=montagereview&GroupId='.$Group->Id().'">'.$Group->Name().'</a>'; }, $Groups ));
|
||||
array_push( $Groups, $Group );
|
||||
}
|
||||
return implode(' > ', array_map(function($Group){ return '<a href="?view=montagereview&GroupId='.$Group->Id().'">'.validHtmlStr($Group->Name()).'</a>'; }, $Groups ));
|
||||
}, $Monitor->GroupIds()));
|
||||
|
||||
?>
|
||||
</div></td>
|
||||
<td class="colServer"><?php echo validHtmlStr($Monitor->Server()->Name())?></td>
|
||||
<td class="colEvents"><a href="?view=<?php echo ZM_WEB_EVENTS_VIEW ?>&page=1<?php echo $monitor_filter['query'] ?>"><?php echo isset($EventsByMonitor[$Monitor->Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?></a></td>
|
||||
<td class="colEvents"><a href="?view=<?php echo ZM_WEB_EVENTS_VIEW ?>&page=1<?php echo $monitor_filter->querystring() ?>"><?php echo isset($EventsByMonitor[$Monitor->Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?></a></td>
|
||||
<td class="colFirstEvent"><?php echo $FirstEvent ? $FirstEvent->link_to($FirstEvent->Id().' at '.$FirstEvent->StartDateTime()) : 'none'?></td>
|
||||
<td class="colLastEvent"><?php echo $LastEvent ? $LastEvent->link_to($LastEvent->Id().' at '.$LastEvent->StartDateTime()) : 'none'?></td>
|
||||
<td class="colMinGap"><?php echo $MinGap ?></td>
|
||||
<td class="colMaxGap"><?php echo $MaxGap ?></td>
|
||||
<td class="colFileMissing<?php echo count($FileMissing) ? ' errorText' : ''?>">
|
||||
<?php echo count($FileMissing) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$FileMissing_filter['query'].'">'.count($FileMissing).'</a>' : '0' ?>
|
||||
<?php echo count($FileMissing) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$FileMissing_filter->querystring().'">'.count($FileMissing).'</a>' : '0' ?>
|
||||
</td>
|
||||
<td class="colZeroSize<?php echo count($ZeroSize) ? ' errorText' : ''?>">
|
||||
<?php echo count($ZeroSize) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$ZeroSize_filter['query'].'">'.count($ZeroSize).'</a>' : '0' ?>
|
||||
<?php echo count($ZeroSize) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$ZeroSize_filter->querystring().'">'.count($ZeroSize).'</a>' : '0' ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
|
|
|
@ -44,7 +44,9 @@ xhtmlHeaders(__FILE__, translate('Stats')." - ".$eid." - ".$fid );
|
|||
<div id="content" class="row justify-content-center">
|
||||
<form name="contentForm" id="contentForm" method="get" action="?">
|
||||
<input type="hidden" name="view" value="none"/>
|
||||
<div class="table-responsive-sm">
|
||||
<?php echo getStatsTableHTML($eid, $fid) ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -145,7 +145,7 @@ $tree = false;
|
|||
if ( isset($_REQUEST['filter']) ) {
|
||||
$filter = ZM\Filter::parse($_REQUEST['filter']);
|
||||
$tree = $filter->tree();
|
||||
ZM\Warning("Parse tree: " . print_r($tree,true));
|
||||
ZM\Debug('Parse tree: ' . print_r($tree,true));
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['range']) )
|
||||
|
|
|
@ -159,21 +159,43 @@ if ( $showPtzControls ) {
|
|||
}
|
||||
if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
|
||||
?>
|
||||
<div id="events">
|
||||
<table id="eventList">
|
||||
<!-- Table styling handled by bootstrap-tables -->
|
||||
<div id="events" class="row justify-content-center table-responsive-sm">
|
||||
<table
|
||||
id="eventList"
|
||||
data-locale="<?php echo i18n() ?>"
|
||||
data-side-pagination="server"
|
||||
data-ajax="ajaxRequest"
|
||||
data-cookie="true"
|
||||
data-cookie-id-table="zmEventListTable"
|
||||
data-cookie-expire="2y"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-uncheckAll="true"
|
||||
data-buttons-class="btn btn-normal"
|
||||
data-show-refresh="true"
|
||||
class="table-sm table-borderless"
|
||||
>
|
||||
<thead>
|
||||
<!-- Row styling is handled by bootstrap-tables -->
|
||||
<tr>
|
||||
<th class="colId"><?php echo translate('Id') ?></th>
|
||||
<th class="colName"><?php echo translate('Name') ?></th>
|
||||
<th class="colTime"><?php echo translate('Time') ?></th>
|
||||
<th class="colSecs"><?php echo translate('Secs') ?></th>
|
||||
<th class="colFrames"><?php echo translate('Frames') ?></th>
|
||||
<th class="colScore"><?php echo translate('Score') ?></th>
|
||||
<th class="colDelete"> </th>
|
||||
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>
|
||||
<th data-sortable="false" data-field="Id"><?php echo translate('Id') ?></th>
|
||||
<th data-sortable="false" data-field="Name"><?php echo translate('Name') ?></th>
|
||||
<th data-sortable="false" data-field="StartDateTime"><?php echo translate('AttrStartTime') ?></th>
|
||||
<th data-sortable="false" data-field="Length"><?php echo translate('Duration') ?></th>
|
||||
<th data-sortable="false" data-field="Frames"><?php echo translate('Frames') ?></th>
|
||||
<th data-sortable="false" data-field="AlarmFrames"><?php echo translate('AlarmBrFrames') ?></th>
|
||||
<th data-sortable="false" data-field="AvgScore"><?php echo translate('AvgBrScore') ?></th>
|
||||
<th data-sortable="false" data-field="MaxScore"><?php echo translate('MaxBrScore') ?></th>
|
||||
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<!-- Row data populated via Ajax -->
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -131,6 +131,7 @@ xhtmlHeaders(__FILE__, translate('Zone'));
|
|||
</div>
|
||||
<div id="content">
|
||||
<form name="zoneForm" id="zoneForm" method="post" action="?">
|
||||
<input type="hidden" name="REFERER" value="<?php echo $_SERVER['HTTP_REFERER'] ?>"/>
|
||||
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
||||
<input type="hidden" name="action" value="zone"/>
|
||||
<input type="hidden" name="mid" value="<?php echo $mid ?>"/>
|
||||
|
|
|
@ -55,7 +55,8 @@ if ( $errorText ) {
|
|||
die();
|
||||
}
|
||||
|
||||
if ( ! ($fh = @fopen($path,'rb') ) ) {
|
||||
if ( ! ($fh = @fopen($path, 'rb') ) ) {
|
||||
ZM\Error('Can\'t open video at '.$path);
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
die();
|
||||
}
|
||||
|
|
|
@ -88,16 +88,19 @@ if [[ -n "$ZM_CONFIG" && ! -f "$ZM_CONFIG" ]]; then
|
|||
fi
|
||||
|
||||
# Load zm.conf
|
||||
if [ -n "$ZM_CONFIG" ]; then
|
||||
echo "Using custom zm.conf $ZM_CONFIG"
|
||||
source "$ZM_CONFIG"
|
||||
elif [ -f "zm.conf" ]; then
|
||||
echo "Using local zm.conf"
|
||||
source "zm.conf"
|
||||
elif [ -f "/etc/zm.conf" ]; then
|
||||
echo "Using system zm.conf"
|
||||
source "/etc/zm.conf"
|
||||
else
|
||||
for zmconf in "$ZM_CONFIG" ./zm.conf /etc/zm.conf /etc/zoneminder/zm.conf; do
|
||||
if [[ -f "$zmconf" ]]; then
|
||||
echo "Using $zmconf"
|
||||
source "$zmconf"
|
||||
# remove filename from path
|
||||
zmconf2="${zmconf%/*}"
|
||||
# source conf.d
|
||||
for i in $(find "${zmconf2}/conf.d" -name \*.conf |sort); do . "$i"; done;
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$zmconf2" ]]; then
|
||||
echo -e "Failed locating zoneminder configuration file (zm.conf)\nUse the -z option to specify the full path to the zoneminder configuration file"
|
||||
exit 45
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue