Merge branch 'storageareas' into valgrind
This commit is contained in:
commit
2fcdee87d9
|
@ -3,6 +3,19 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ "$1" = "configure" ]; then
|
if [ "$1" = "configure" ]; then
|
||||||
|
|
||||||
|
. /etc/zm/zm.conf
|
||||||
|
|
||||||
|
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group.
|
||||||
|
chown www-data:root /var/log/zm
|
||||||
|
chown www-data:www-data /var/lib/zm
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
chown www-data:www-data -R /var/cache/zoneminder
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Do this every time the package is installed or upgraded
|
||||||
|
|
||||||
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
if [ -e "/etc/init.d/mysql" ]; then
|
if [ -e "/etc/init.d/mysql" ]; then
|
||||||
#
|
#
|
||||||
# Get mysql started if it isn't
|
# Get mysql started if it isn't
|
||||||
|
@ -15,39 +28,26 @@ if [ "$1" = "configure" ]; then
|
||||||
# test if database if already present...
|
# test if database if already present...
|
||||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||||
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||||
echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
# This creates the user.
|
||||||
|
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
else
|
||||||
|
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ensure zoneminder is stopped
|
||||||
invoke-rc.d zoneminder stop || true
|
invoke-rc.d zoneminder stop || true
|
||||||
zmupdate.pl --nointeractive
|
zmupdate.pl --nointeractive
|
||||||
|
zmupdate.pl --nointeractive -f
|
||||||
|
invoke-rc.d zoneminder start || true
|
||||||
else
|
else
|
||||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo 'mysql not found, assuming remote server.'
|
echo 'mysql not found, assuming remote server.'
|
||||||
fi
|
fi
|
||||||
chown www-data:www-data /var/log/zm
|
|
||||||
chown www-data:www-data /var/lib/zm/
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
chown www-data:www-data -R /var/cache/zoneminder
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Ensure zoneminder is stopped...
|
|
||||||
if [ -x "/etc/init.d/zoneminder" ]; then
|
|
||||||
if invoke-rc.d zoneminder status ; then
|
|
||||||
invoke-rc.d zoneminder stop || exit $?
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" = "configure" ]; then
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
chown www-data:www-data /var/log/zm
|
|
||||||
chown www-data:www-data /var/lib/zm/
|
|
||||||
chown www-data:www-data -R /var/cache/zoneminder
|
|
||||||
else
|
else
|
||||||
chown www-data:www-data /var/log/zm
|
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||||
zmupdate.pl
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
#DEBHELPER#
|
#DEBHELPER#
|
||||||
|
|
|
@ -3,11 +3,51 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ "$1" = "configure" ]; then
|
if [ "$1" = "configure" ]; then
|
||||||
|
|
||||||
|
. /etc/zm/zm.conf
|
||||||
|
|
||||||
|
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||||
chown www-data:root /var/log/zm
|
chown www-data:root /var/log/zm
|
||||||
chown www-data:www-data /var/lib/zm
|
chown www-data:www-data /var/lib/zm
|
||||||
if [ -z "$2" ]; then
|
if [ -z "$2" ]; then
|
||||||
chown www-data:www-data -R /var/cache/zoneminder
|
chown www-data:www-data -R /var/cache/zoneminder
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Do this every time the package is installed or upgraded
|
||||||
|
|
||||||
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
|
if [ -e "/etc/init.d/mysql" ]; then
|
||||||
|
#
|
||||||
|
# Get mysql started if it isn't
|
||||||
|
#
|
||||||
|
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||||
|
invoke-rc.d mysql start
|
||||||
|
fi
|
||||||
|
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||||
|
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||||
|
# test if database if already present...
|
||||||
|
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||||
|
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||||
|
# This creates the user.
|
||||||
|
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
else
|
||||||
|
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure zoneminder is stopped
|
||||||
|
invoke-rc.d zoneminder stop || true
|
||||||
|
zmupdate.pl --nointeractive
|
||||||
|
zmupdate.pl --nointeractive -f
|
||||||
|
invoke-rc.d zoneminder start || true
|
||||||
|
else
|
||||||
|
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo 'mysql not found, assuming remote server.'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#DEBHELPER#
|
#DEBHELPER#
|
||||||
|
|
|
@ -57,7 +57,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libsys-cpu-perl, libsys-meminfo-perl
|
,libsys-cpu-perl, libsys-meminfo-perl
|
||||||
,mysql-client | virtual-mysql-client
|
,mysql-client | virtual-mysql-client
|
||||||
,perl-modules
|
,perl-modules
|
||||||
,php5-mysql
|
,php5-mysql, php5-gd
|
||||||
,policykit-1
|
,policykit-1
|
||||||
,rsyslog | system-log-daemon
|
,rsyslog | system-log-daemon
|
||||||
,zip
|
,zip
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
. /etc/zm/zm.conf
|
|
||||||
|
|
||||||
if [ "$1" = "configure" ]; then
|
if [ "$1" = "configure" ]; then
|
||||||
|
|
||||||
|
. /etc/zm/zm.conf
|
||||||
|
|
||||||
|
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||||
chown www-data:root /var/log/zm
|
chown www-data:root /var/log/zm
|
||||||
chown www-data:www-data /var/lib/zm
|
chown www-data:www-data /var/lib/zm
|
||||||
if [ -z "$2" ]; then
|
if [ -z "$2" ]; then
|
||||||
|
@ -12,18 +14,45 @@ if [ "$1" = "configure" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Do this every time the package is installed or upgraded
|
# Do this every time the package is installed or upgraded
|
||||||
# Test for database presence to avoid failure of zmupdate.pl
|
|
||||||
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
|
|
||||||
|
if [ -e "/etc/init.d/mysql" ]; then
|
||||||
|
|
||||||
|
#
|
||||||
|
# Get mysql started if it isn't
|
||||||
|
#
|
||||||
|
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||||
|
deb-systemd-invoke start mysql.service || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||||
|
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||||
|
# test if database if already present...
|
||||||
|
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||||
|
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||||
|
# This creates the user.
|
||||||
|
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
else
|
||||||
|
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
fi
|
||||||
|
|
||||||
# Ensure zoneminder is stopped
|
# Ensure zoneminder is stopped
|
||||||
deb-systemd-invoke stop zoneminder.service || exit $?
|
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||||
|
|
||||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
|
||||||
echo 'grant lock tables, create, index, alter on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
|
||||||
# Run the ZoneMinder update tool
|
|
||||||
zmupdate.pl --nointeractive
|
zmupdate.pl --nointeractive
|
||||||
|
zmupdate.pl --nointeractive -f
|
||||||
|
deb-systemd-invoke start zoneminder.service || exit $?
|
||||||
|
|
||||||
|
else
|
||||||
|
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo 'mysql not found, assuming remote server.'
|
||||||
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||||
fi;
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -514,6 +514,8 @@ You do not need to rebuild ZM for X10 support. You will need to install the perl
|
||||||
|
|
||||||
Extending Zoneminder
|
Extending Zoneminder
|
||||||
------------------------
|
------------------------
|
||||||
|
.. _runstate_cron_example:
|
||||||
|
|
||||||
How can I get ZM to do different things at different times of day or week?
|
How can I get ZM to do different things at different times of day or week?
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,14 @@ own empty screen.
|
||||||
* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month.
|
* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month.
|
||||||
* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones.
|
* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones.
|
||||||
* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate
|
* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate
|
||||||
* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an externa entity via zmtrigger tells Zoneminder to (this is advanced usage).
|
* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage).
|
||||||
* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one
|
* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one
|
||||||
|
* **I**: One of the most often missed features is the ability of ZoneMinder to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ <runstate_cron_example>`. More esoteric examples of switching run states based on phone location can be found `here <https://forums.zoneminder.com/viewtopic.php?f=9&t=23026>`__.
|
||||||
|
|
||||||
|
Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes.
|
||||||
|
|
||||||
|
.. image:: images/runstates.png
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Adding Monitors
|
Adding Monitors
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
|
@ -155,10 +155,12 @@ MAIN: while( $loop ) {
|
||||||
Fatal("ZM_AUDIT_MIN_AGE is not set in config.");
|
Fatal("ZM_AUDIT_MIN_AGE is not set in config.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my %Monitors;
|
||||||
my $db_monitors;
|
my $db_monitors;
|
||||||
my $monitorSelectSql = "select Id from Monitors order by Id";
|
my $monitorSelectSql = "select * from Monitors order by Id";
|
||||||
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
|
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
|
||||||
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
|
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
|
||||||
|
|
||||||
my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
|
my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
|
||||||
FROM Events WHERE MonitorId = ? ORDER BY Id";
|
FROM Events WHERE MonitorId = ? ORDER BY Id";
|
||||||
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
|
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
|
||||||
|
@ -169,6 +171,8 @@ MAIN: while( $loop ) {
|
||||||
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
|
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
|
||||||
while( my $monitor = $monitorSelectSth->fetchrow_hashref() )
|
while( my $monitor = $monitorSelectSth->fetchrow_hashref() )
|
||||||
{
|
{
|
||||||
|
$Monitors{$$monitor{Id}} = $monitor;
|
||||||
|
|
||||||
Debug( "Found database monitor '$monitor->{Id}'" );
|
Debug( "Found database monitor '$monitor->{Id}'" );
|
||||||
my $db_events = $db_monitors->{$monitor->{Id}} = {};
|
my $db_events = $db_monitors->{$monitor->{Id}} = {};
|
||||||
my $res = $eventSelectSth->execute( $monitor->{Id} )
|
my $res = $eventSelectSth->execute( $monitor->{Id} )
|
||||||
|
@ -467,20 +471,30 @@ MAIN: while( $loop ) {
|
||||||
|
|
||||||
# New audit to close any events that were left open for longer than MIN_AGE seconds
|
# New audit to close any events that were left open for longer than MIN_AGE seconds
|
||||||
my $selectUnclosedEventsSql =
|
my $selectUnclosedEventsSql =
|
||||||
"SELECT E.Id,
|
#"SELECT E.Id, ANY_VALUE(E.MonitorId),
|
||||||
max(F.TimeStamp) as EndTime,
|
#
|
||||||
unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
|
#max(F.TimeStamp) as EndTime,
|
||||||
max(F.FrameId) as Frames,
|
#unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
|
||||||
|
#max(F.FrameId) as Frames,
|
||||||
|
#count(if(F.Score>0,1,NULL)) as AlarmFrames,
|
||||||
|
#sum(F.Score) as TotScore,
|
||||||
|
#max(F.Score) as MaxScore
|
||||||
|
#FROM Events as E
|
||||||
|
#INNER JOIN Frames as F on E.Id = F.EventId
|
||||||
|
#WHERE isnull(E.Frames) or isnull(E.EndTime)
|
||||||
|
#GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
|
||||||
|
#;
|
||||||
|
"SELECT *, unix_timestamp(StartTime) AS TimeStamp FROM Events WHERE EndTime IS NULL AND StartTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)";
|
||||||
|
|
||||||
|
my $selectFrameDataSql = "SELECT max(TimeStamp) as EndTime, unix_timestamp(max(TimeStamp)) AS EndTimeStamp, max(FrameId) as Frames,
|
||||||
count(if(F.Score>0,1,NULL)) as AlarmFrames,
|
count(if(F.Score>0,1,NULL)) as AlarmFrames,
|
||||||
sum(F.Score) as TotScore,
|
sum(F.Score) as TotScore,
|
||||||
max(F.Score) as MaxScore,
|
max(F.Score) as MaxScore
|
||||||
M.EventPrefix as Prefix
|
FROM Frames WHERE EventId=?";
|
||||||
FROM Events as E
|
my $selectFrameDataSth = $dbh->prepare_cached($selectFrameDataSql)
|
||||||
LEFT JOIN Monitors as M on E.MonitorId = M.Id
|
or Fatal( "Can't prepare '$selectFrameDataSql': ".$dbh->errstr() );
|
||||||
INNER JOIN Frames as F on E.Id = F.EventId
|
|
||||||
WHERE isnull(E.Frames) or isnull(E.EndTime)
|
|
||||||
GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
|
|
||||||
;
|
|
||||||
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
|
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
|
||||||
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
|
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
|
||||||
my $updateUnclosedEventsSql =
|
my $updateUnclosedEventsSql =
|
||||||
|
@ -505,26 +519,32 @@ MAIN: while( $loop ) {
|
||||||
aud_print( "Found open event '$event->{Id}'" );
|
aud_print( "Found open event '$event->{Id}'" );
|
||||||
if ( confirm( 'close', 'closing' ) )
|
if ( confirm( 'close', 'closing' ) )
|
||||||
{
|
{
|
||||||
|
$res = $selectFrameDataSth->execute( $event->{Id} );
|
||||||
|
my $frame = $selectFrameDataSth->fetchrow_hashref();
|
||||||
|
if ( $frame ) {
|
||||||
$res = $updateUnclosedEventsSth->execute
|
$res = $updateUnclosedEventsSth->execute
|
||||||
(
|
(
|
||||||
sprintf("%s%d%s",
|
sprintf("%s%d%s",
|
||||||
$event->{Prefix},
|
$Monitors{$event->{MonitorId}}->{EventPrefix},
|
||||||
$event->{Id},
|
$event->{Id},
|
||||||
RECOVER_TAG
|
RECOVER_TAG
|
||||||
),
|
),
|
||||||
$event->{EndTime},
|
$frame->{EndTime},
|
||||||
$event->{Length},
|
$frame->{EndTimeStamp} - $event->{TimeStamp},
|
||||||
$event->{Frames},
|
$frame->{Frames},
|
||||||
$event->{AlarmFrames},
|
$frame->{AlarmFrames},
|
||||||
$event->{TotScore},
|
$frame->{TotScore},
|
||||||
$event->{AlarmFrames}
|
$frame->{AlarmFrames}
|
||||||
? int($event->{TotScore} / $event->{AlarmFrames})
|
? int($frame->{TotScore} / $frame->{AlarmFrames})
|
||||||
: 0
|
: 0
|
||||||
,
|
,
|
||||||
$event->{MaxScore},
|
$frame->{MaxScore},
|
||||||
RECOVER_TEXT,
|
RECOVER_TEXT,
|
||||||
$event->{Id}
|
$event->{Id}
|
||||||
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
||||||
|
} else {
|
||||||
|
Error("SHOULD DELETE");
|
||||||
|
} # end if has frame data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -862,7 +862,7 @@ sub killAll
|
||||||
my $killall;
|
my $killall;
|
||||||
if ( '@HOST_OS@' eq 'BSD' )
|
if ( '@HOST_OS@' eq 'BSD' )
|
||||||
{
|
{
|
||||||
$killall = 'killall -';
|
$killall = 'killall -q -';
|
||||||
} elsif ( '@HOST_OS@' eq 'solaris' ) {
|
} elsif ( '@HOST_OS@' eq 'solaris' ) {
|
||||||
$killall = 'pkill -';
|
$killall = 'pkill -';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -61,6 +61,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
||||||
mOpenStart = 0;
|
mOpenStart = 0;
|
||||||
mReopenThread = 0;
|
mReopenThread = 0;
|
||||||
wasRecording = false;
|
wasRecording = false;
|
||||||
|
videoStore = NULL;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
|
@ -99,6 +100,7 @@ void FfmpegCamera::Initialise()
|
||||||
av_log_set_level( AV_LOG_QUIET );
|
av_log_set_level( AV_LOG_QUIET );
|
||||||
|
|
||||||
av_register_all();
|
av_register_all();
|
||||||
|
avformat_network_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FfmpegCamera::Terminate()
|
void FfmpegCamera::Terminate()
|
||||||
|
@ -356,7 +358,8 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
if ( mAudioStreamId == -1 )
|
if ( mAudioStreamId == -1 )
|
||||||
Debug( 2, "Unable to locate audio stream in %s", mPath.c_str() );
|
Debug( 2, "Unable to locate audio stream in %s", mPath.c_str() );
|
||||||
|
|
||||||
Debug ( 1, "Found video stream" );
|
Debug ( 3, "Found video stream at index %d", mVideoStreamId );
|
||||||
|
Debug ( 3, "Found audio stream at index %d", mAudioStreamId );
|
||||||
|
|
||||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||||
|
|
||||||
|
@ -408,7 +411,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug ( 1, "Validated imagesize" );
|
Debug ( 1, "Validated imagesize %d", pSize );
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||||
|
@ -590,9 +593,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||||
|
|
||||||
//Keep the last keyframe so we can establish immediate video
|
//Keep the last keyframe so we can establish immediate video
|
||||||
/*if(packet.flags & AV_PKT_FLAG_KEY)
|
if(packet.flags & AV_PKT_FLAG_KEY) {
|
||||||
av_copy_packet(&lastKeyframePkt, &packet);*/
|
Debug(4, "Have keyframe");
|
||||||
|
av_copy_packet(&lastKeyframePkt, &packet);
|
||||||
//TODO I think we need to store the key frame location for seeking as part of the event
|
//TODO I think we need to store the key frame location for seeking as part of the event
|
||||||
|
}
|
||||||
|
|
||||||
//Video recording
|
//Video recording
|
||||||
if ( recording && !wasRecording ) {
|
if ( recording && !wasRecording ) {
|
||||||
|
@ -603,6 +608,9 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
wasRecording = true;
|
wasRecording = true;
|
||||||
strcpy(oldDirectory, event_file);
|
strcpy(oldDirectory, event_file);
|
||||||
|
|
||||||
|
|
||||||
|
// Need to write out all the frames from the last keyframe?
|
||||||
|
|
||||||
} else if ( ( ! recording ) && wasRecording && videoStore ) {
|
} else if ( ( ! recording ) && wasRecording && videoStore ) {
|
||||||
Info("Deleting videoStore instance");
|
Info("Deleting videoStore instance");
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
|
@ -660,9 +668,10 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
}
|
}
|
||||||
if ( videoStore && recording ) {
|
if ( videoStore && recording ) {
|
||||||
if ( record_audio ) {
|
if ( record_audio ) {
|
||||||
Debug(3, "Recording audio packet" );
|
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
|
||||||
//Write the packet to our video store
|
//Write the packet to our video store
|
||||||
int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame
|
//FIXME no relevance of last key frame
|
||||||
|
int ret = videoStore->writeAudioFramePacket( &packet, mFormatContext->streams[packet.stream_index] );
|
||||||
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
||||||
av_free_packet( &packet );
|
av_free_packet( &packet );
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -64,7 +64,9 @@ protected:
|
||||||
bool wasRecording;
|
bool wasRecording;
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
char oldDirectory[4096];
|
char oldDirectory[4096];
|
||||||
//AVPacket lastKeyframePkt;
|
|
||||||
|
// Last Key frame
|
||||||
|
AVPacket lastKeyframePkt;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext;
|
struct SwsContext *mConvertContext;
|
||||||
|
|
|
@ -33,7 +33,8 @@ extern "C"{
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
AVStream *input_st, AVStream *inpaud_st,
|
AVStream *input_st,
|
||||||
|
AVStream *inpaud_st,
|
||||||
int64_t nStartTime) {
|
int64_t nStartTime) {
|
||||||
|
|
||||||
AVDictionary *pmetadata = NULL;
|
AVDictionary *pmetadata = NULL;
|
||||||
|
@ -46,8 +47,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
keyframeMessage = false;
|
keyframeMessage = false;
|
||||||
keyframeSkipNumber = 0;
|
keyframeSkipNumber = 0;
|
||||||
|
|
||||||
|
Info("Opening video storage stream %s format: %d\n", filename, format);
|
||||||
Info("Opening video storage stream %s\n", filename);
|
|
||||||
|
|
||||||
//Init everything we need
|
//Init everything we need
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -96,12 +96,13 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inpaud_st) {
|
if (inpaud_st) {
|
||||||
|
|
||||||
audio_st = avformat_new_stream(oc, inpaud_st->codec->codec);
|
audio_st = avformat_new_stream(oc, inpaud_st->codec->codec);
|
||||||
if (!audio_st) {
|
if (!audio_st) {
|
||||||
Error("Unable to create audio out stream\n");
|
Error("Unable to create audio out stream\n");
|
||||||
audio_st = NULL;
|
audio_st = NULL;
|
||||||
} else {
|
} else {
|
||||||
ret=avcodec_copy_context(audio_st->codec, inpaud_st->codec);
|
ret = avcodec_copy_context(audio_st->codec, inpaud_st->codec);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str());
|
Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str());
|
||||||
}
|
}
|
||||||
|
@ -111,6 +112,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Debug(3, "No Audio output stream");
|
||||||
audio_st = NULL;
|
audio_st = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
|
|
||||||
startTime=av_gettime()-nStartTime;//oc->start_time;
|
startTime=av_gettime()-nStartTime;//oc->start_time;
|
||||||
Info("VideoStore startTime=%d\n",startTime);
|
Info("VideoStore startTime=%d\n",startTime);
|
||||||
}
|
} // VideoStore::VideoStore
|
||||||
|
|
||||||
|
|
||||||
VideoStore::~VideoStore(){
|
VideoStore::~VideoStore(){
|
||||||
|
@ -272,34 +274,47 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt, AVStream *input_st){//, AV
|
||||||
|
|
||||||
int VideoStore::writeAudioFramePacket(AVPacket *ipkt, AVStream *input_st){
|
int VideoStore::writeAudioFramePacket(AVPacket *ipkt, AVStream *input_st){
|
||||||
|
|
||||||
if(!audio_st)
|
if(!audio_st) {
|
||||||
|
Error("Called writeAudioFramePacket when no audio_st");
|
||||||
return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment
|
return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment
|
||||||
|
}
|
||||||
/*if(!keyframeMessage)
|
/*if(!keyframeMessage)
|
||||||
return -1;*/
|
return -1;*/
|
||||||
|
//zm_dump_stream_format( oc, ipkt->stream_index, 0, 1 );
|
||||||
|
|
||||||
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base);
|
// What is this doing? Getting the time of the start of this video chunk? Does that actually make sense?
|
||||||
|
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, audio_st->time_base);
|
||||||
|
|
||||||
AVPacket opkt;
|
AVPacket opkt;
|
||||||
|
|
||||||
av_init_packet(&opkt);
|
av_init_packet(&opkt);
|
||||||
|
Debug(3, "after init packet" );
|
||||||
|
|
||||||
|
|
||||||
//Scale the PTS of the outgoing packet to be the correct time base
|
//Scale the PTS of the outgoing packet to be the correct time base
|
||||||
if (ipkt->pts != AV_NOPTS_VALUE)
|
if (ipkt->pts != AV_NOPTS_VALUE) {
|
||||||
|
Debug(3, "Rescaling output pts");
|
||||||
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time;
|
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time;
|
||||||
else
|
} else {
|
||||||
|
Debug(3, "Setting output pts to AV_NOPTS_VALUE");
|
||||||
opkt.pts = AV_NOPTS_VALUE;
|
opkt.pts = AV_NOPTS_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
//Scale the DTS of the outgoing packet to be the correct time base
|
//Scale the DTS of the outgoing packet to be the correct time base
|
||||||
if(ipkt->dts == AV_NOPTS_VALUE) {
|
if(ipkt->dts == AV_NOPTS_VALUE) {
|
||||||
|
Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||||
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base);
|
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base);
|
||||||
Debug(3, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||||
} else
|
} else {
|
||||||
|
Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||||
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base);
|
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base);
|
||||||
|
Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||||
|
}
|
||||||
opkt.dts -= ost_tb_start_time;
|
opkt.dts -= ost_tb_start_time;
|
||||||
|
|
||||||
|
// Seems like it would be really weird for the codec type to NOT be audiu
|
||||||
if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) {
|
if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) {
|
||||||
Debug( 3, "code is audio, dts != AV_NOPTS_VALUE " );
|
Debug( 4, "code is audio, dts != AV_NOPTS_VALUE " );
|
||||||
int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size);
|
int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size);
|
||||||
if(!duration)
|
if(!duration)
|
||||||
duration = input_st->codec->frame_size;
|
duration = input_st->codec->frame_size;
|
||||||
|
@ -324,7 +339,7 @@ Debug(3, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||||
if(ret!=0){
|
if(ret!=0){
|
||||||
Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str());
|
Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str());
|
||||||
}
|
}
|
||||||
|
Debug(4,"Success writing audio frame" );
|
||||||
av_free_packet(&opkt);
|
av_free_packet(&opkt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -527,6 +527,7 @@ if ( !empty($action) )
|
||||||
'DoNativeMotDet' => 'toggle',
|
'DoNativeMotDet' => 'toggle',
|
||||||
'Exif' => 'toggle',
|
'Exif' => 'toggle',
|
||||||
'RTSPDescribe' => 'toggle',
|
'RTSPDescribe' => 'toggle',
|
||||||
|
'RecordAudio' => 'toggle',
|
||||||
);
|
);
|
||||||
|
|
||||||
$columns = getTableColumns( 'Monitors' );
|
$columns = getTableColumns( 'Monitors' );
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
#menuBar1 {
|
#menuBar1 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1.5em;
|
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
clear: both;
|
clear: both;
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
|
|
||||||
#menuBar2 {
|
#menuBar2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1.2em;
|
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +55,16 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#menuBar1:after,
|
||||||
|
#menuBar2:after {
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
#imageFeed {
|
#imageFeed {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,12 @@
|
||||||
|
|
||||||
#menuBar1 {
|
#menuBar1 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1.5em;
|
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#menuBar1 #nameControl {
|
#menuBar1 #nameControl {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@
|
||||||
|
|
||||||
#menuBar2 {
|
#menuBar2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1.2em;
|
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +55,15 @@
|
||||||
float: right;
|
float: right;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
#menuBar1:after,
|
||||||
|
#menuBar2:after {
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
#imageFeed {
|
#imageFeed {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -12,18 +12,7 @@ function setButtonStates( element )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( form.elements[i].length )
|
|
||||||
{
|
|
||||||
for( var j = 0, j_length = form.elements[i].length; j < j_length; j += 1 ) {
|
|
||||||
if ( form.elements[j].type == "checkbox" ) {
|
|
||||||
if ( form.elements[j].checked ) {
|
|
||||||
if ( checked++ > 1 )
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} // end foreach element in array
|
|
||||||
}
|
|
||||||
} // end foreach element in array
|
|
||||||
$(element).getParent( 'tr' ).toggleClass( 'highlight' );
|
$(element).getParent( 'tr' ).toggleClass( 'highlight' );
|
||||||
form.editBtn.disabled = (checked!=1);
|
form.editBtn.disabled = (checked!=1);
|
||||||
form.deleteBtn.disabled = (checked==0);
|
form.deleteBtn.disabled = (checked==0);
|
||||||
|
|
|
@ -35,6 +35,7 @@ function Monitor( index, id, connKey )
|
||||||
if ( this.streamCmdTimer )
|
if ( this.streamCmdTimer )
|
||||||
this.streamCmdTimer = clearTimeout( this.streamCmdTimer );
|
this.streamCmdTimer = clearTimeout( this.streamCmdTimer );
|
||||||
|
|
||||||
|
var stream = document.getElementById( "liveStream"+this.id );
|
||||||
if ( respObj.result == 'Ok' )
|
if ( respObj.result == 'Ok' )
|
||||||
{
|
{
|
||||||
this.status = respObj.status;
|
this.status = respObj.status;
|
||||||
|
@ -57,7 +58,6 @@ function Monitor( index, id, connKey )
|
||||||
this.setStateClass( $('monitor'+this.index), stateClass );
|
this.setStateClass( $('monitor'+this.index), stateClass );
|
||||||
|
|
||||||
/*Stream could be an applet so can't use moo tools*/
|
/*Stream could be an applet so can't use moo tools*/
|
||||||
var stream = document.getElementById( "liveStream"+this.id );
|
|
||||||
stream.className = stateClass;
|
stream.className = stateClass;
|
||||||
|
|
||||||
var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT );
|
var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT );
|
||||||
|
@ -90,6 +90,10 @@ function Monitor( index, id, connKey )
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
console.error( respObj.message );
|
console.error( respObj.message );
|
||||||
|
// Try to reload the image stream.
|
||||||
|
if ( stream )
|
||||||
|
stream.src = stream.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
|
||||||
|
|
||||||
}
|
}
|
||||||
var streamCmdTimeout = statusRefreshTimeout;
|
var streamCmdTimeout = statusRefreshTimeout;
|
||||||
if ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT )
|
if ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT )
|
||||||
|
|
|
@ -115,7 +115,10 @@ if ( canEdit( 'Monitors' ) )
|
||||||
if ( canEdit( 'Monitors' ) )
|
if ( canEdit( 'Monitors' ) )
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<div id="forceCancelAlarm"><a id="forceAlarmLink" href="#" onclick="cmdForceAlarm()" class="hidden"><?php echo translate('ForceAlarm') ?></a><a id="cancelAlarmLink" href="#" onclick="cmdCancelForcedAlarm()" class="hidden"><?php echo translate('CancelForcedAlarm') ?></a></div>
|
<div id="forceCancelAlarm">
|
||||||
|
<a id="forceAlarmLink" href="#" onclick="cmdForceAlarm();"><?php echo translate('ForceAlarm') ?></a>
|
||||||
|
<a id="cancelAlarmLink" href="#" onclick="cmdCancelForcedAlarm();" class="hidden"><?php echo translate('CancelForcedAlarm') ?></a>
|
||||||
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
Loading…
Reference in New Issue