Merge branch 'storageareas' of github.com:ConnorTechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2017-05-26 22:20:50 -04:00
commit 2e673e49c8
22 changed files with 1149 additions and 1273 deletions

View File

@ -1,20 +1,7 @@
ZoneMinder H264 Patch
ZoneMinder
==========
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png?branch=feature-h264-videostorage)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
##Feature-h264-videostorage Branch Details
This branch supports direct recording of h264 cameras into MP4 format uisng the h264 Passthrough option, but only with FFMPEG Monitors currently. It also provides h264 encoding for any other monitor type. If you encounter any issues, please open an issue on GitHub and attach it to the h264 milestone. But do remember this is bleeding edge so it will have problems.
Thanks to @chriswiggins and @mastertheknife for their work, @SteveGilvarry is now maintaining this branch and welcomes any assistance.
**The following SQL changes are required, these will be merged to zmupdate once we are ready to merge this branch to master.**
```
ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing` ,
ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs` ,
ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter` ,
ADD `RecordAudio` TINYINT NOT NULL DEFAULT '0' AFTER `EncoderParameters` ;
ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames` ;
```
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org

View File

@ -354,7 +354,7 @@ CREATE TABLE `Monitors` (
`Deinterlacing` int(10) unsigned NOT NULL default '0',
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
`EncoderParameters` TEXT NOT NULL,
`EncoderParameters` TEXT,
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
`RTSPDescribe` tinyint(1) unsigned,
`Brightness` mediumint(7) NOT NULL default '-1',

View File

@ -71,3 +71,27 @@ SET @s = (SELECT IF(
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- The following alters various columns to allow NULLs
--
ALTER TABLE Monitors MODIFY Host varchar(64);
ALTER TABLE Monitors MODIFY LabelFormat varchar(64);
ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255);
ALTER TABLE Monitors MODIFY Options varchar(255);
ALTER TABLE Monitors MODIFY Protocol varchar(16);
ALTER TABLE Monitors MODIFY User varchar(64);
ALTER TABLE Monitors MODIFY Pass varchar(64);
ALTER TABLE Monitors MODIFY RTSPDescribe tinyint(1) unsigned;
ALTER TABLE Monitors MODIFY ControlId int(10) unsigned;
ALTER TABLE Monitors MODIFY TrackDelay smallint(5) unsigned;
ALTER TABLE Monitors MODIFY ReturnDelay smallint(5) unsigned;
ALTER TABLE Monitors MODIFY EncoderParameters TEXT;
ALTER TABLE Monitors MODIFY Path varchar(255);
ALTER TABLE Monitors MODIFY V4LMultiBuffer tinyint(1) unsigned;
ALTER TABLE Users MODIFY MonitorIds tinytext;
ALTER TABLE Users MODIFY Language varchar(8);
ALTER TABLE Users MODIFY MaxBandwidth varchar(16);

View File

@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9), cmake
, libphp-serialization-perl
, libgnutls28-dev | libgnutls-dev
, libmysqlclient-dev | libmariadbclient-dev
, libjpeg-dev
, libjpeg8-dev | libjpeg-dev
, libpcre3-dev
, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev
, libavdevice-dev

View File

@ -5,10 +5,19 @@ Maintainer: Dmitry Smirnov <onlyjob@debian.org>
Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
<<<<<<< HEAD
,libx264-dev, libmp4v2-dev
,libavcodec-dev, libavformat-dev, libswscale-dev
,libavutil-dev, libavdevice-dev, libavresample-dev
,libboost-dev
=======
,libboost-dev
,libavdevice-dev (>= 6:10~)
,libavcodec-dev (>= 6:10~)
,libavformat-dev (>= 6:10~)
,libavutil-dev (>= 6:10~)
,libswscale-dev (>= 6:10~)
>>>>>>> dragndrop_monitor_sorting
,libbz2-dev
,libgcrypt-dev
,libcurl4-gnutls-dev

View File

@ -1,83 +0,0 @@
AUTOMAKE_OPTIONS = gnu
# Ack! Nasty hack to get modules Makefile regenerated if wiped with make clean
all-local: ZoneMinder/Makefile
ZoneMinder/Makefile: ZoneMinder/Makefile.PL
( cd ZoneMinder; perl Makefile.PL )
bin_SCRIPTS = \
zmdc.pl \
zmaudit.pl \
zmfilter.pl \
zmtrigger.pl \
zmx10.pl \
zmwatch.pl \
zmpkg.pl \
zmupdate.pl \
zmvideo.pl \
zmcontrol.pl \
zmtrack.pl \
zmcamtool.pl \
zmsystemctl.pl \
zmtelemetry.pl
SUBDIRS = \
. \
ZoneMinder
EXTRA_DIST = \
zmdc.pl.in \
zmaudit.pl.in \
zmfilter.pl.in \
zmtrigger.pl.in \
zmx10.pl.in \
zmwatch.pl.in \
zmpkg.pl.in \
zmupdate.pl.in \
zmvideo.pl.in \
zmcontrol.pl.in \
zmtrack.pl.in \
zmcamtool.pl.in \
zmsystemctl.pl.in \
zmtelemtry.pl.in \
ZoneMinder/Makefile.PL \
ZoneMinder/README \
ZoneMinder/Changes \
ZoneMinder/META.yml \
ZoneMinder/MANIFEST \
ZoneMinder/t/ZoneMinder.t \
ZoneMinder/lib/ZoneMinder.pm \
ZoneMinder/lib/ZoneMinder/Base.pm.in \
ZoneMinder/lib/ZoneMinder/Config.pm.in \
ZoneMinder/lib/ZoneMinder/Event.pm \
ZoneMinder/lib/ZoneMinder/Filter.pm \
ZoneMinder/lib/ZoneMinder/Monitor.pm \
ZoneMinder/lib/ZoneMinder/Object.pm \
ZoneMinder/lib/ZoneMinder/Server.pm \
ZoneMinder/lib/ZoneMinder/Storage.pm \
ZoneMinder/lib/ZoneMinder/Logger.pm \
ZoneMinder/lib/ZoneMinder/General.pm \
ZoneMinder/lib/ZoneMinder/Database.pm \
ZoneMinder/lib/ZoneMinder/Memory.pm.in \
ZoneMinder/lib/ZoneMinder/Memory/Shared.pm \
ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm \
ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm \
ZoneMinder/lib/ZoneMinder/ConfigData.pm.in \
ZoneMinder/lib/ZoneMinder/Control.pm \
ZoneMinder/lib/ZoneMinder/Control \
ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm \
ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm \
zm.in \
zmdbbackup.in \
zmdbrestore.in \
zmeventdump.in \
zmlogrotate.conf.in

View File

@ -187,7 +187,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
snprintf( video_file, sizeof(video_file), video_file_format, path, video_name );
/* X264 MP4 video writer */
if(monitor->GetOptVideoWriter() == 1) {
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
#if ZM_HAVE_VIDEOWRITER_X264MP4
videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams());
#else
@ -195,8 +195,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
#endif
}
if(videowriter != NULL) {
if ( videowriter != NULL ) {
/* Open the video stream */
int nRet = videowriter->Open();
if(nRet != 0) {
@ -207,9 +206,10 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
snprintf( timecodes_name, sizeof(timecodes_name), "%d-%s", id, "video.timecodes" );
snprintf( timecodes_file, sizeof(timecodes_file), video_file_format, path, timecodes_name );
/* Create timecodes file */
timecodes_fd = fopen(timecodes_file, "wb");
if(timecodes_fd == NULL) {
if ( timecodes_fd == NULL ) {
Error("Failed creating timecodes file");
}
}
@ -353,12 +353,12 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
noteSet.insert( newNote );
update = true;
}
}
}
}
} // end for
} // end if ( noteSetMap.size() == 0
} // end if newNoteSetupMap.size() > 0
} // end foreach newNoteSetMap
} // end if have old notes
} // end if have new notes
if ( update ) {
std::string notes;

View File

@ -61,7 +61,6 @@
#define MAP_LOCKED 0
#endif
//=============================================================================
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
std::stringstream ss(s);
@ -71,7 +70,6 @@ std::vector<std::string> split(const std::string &s, char delim) {
}
return elems;
}
//=============================================================================
Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) {
strncpy( name, p_name, sizeof(name) );
@ -1228,7 +1226,7 @@ bool Monitor::Analyse() {
}
shared_data->action &= ~RESUME;
}
} // end ifshared_data->action
} // end if shared_data->action
if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) {
Info( "Auto resuming at count %d", image_count );

View File

@ -48,16 +48,14 @@ RemoteCamera::RemoteCamera(
path = '/'+path;
}
RemoteCamera::~RemoteCamera()
{
if(hp != NULL) {
RemoteCamera::~RemoteCamera() {
if ( hp != NULL ) {
freeaddrinfo(hp);
hp = NULL;
}
}
void RemoteCamera::Initialise()
{
void RemoteCamera::Initialise() {
if( protocol.empty() )
Fatal( "No protocol specified for remote camera" );
@ -73,8 +71,7 @@ void RemoteCamera::Initialise()
// Cache as much as we can to speed things up
std::string::size_type authIndex = host.rfind( '@' );
if ( authIndex != std::string::npos )
{
if ( authIndex != std::string::npos ) {
auth = host.substr( 0, authIndex );
host.erase( 0, authIndex+1 );
auth64 = base64Encode( auth );
@ -82,7 +79,6 @@ void RemoteCamera::Initialise()
authIndex = auth.rfind( ':' );
username = auth.substr(0,authIndex);
password = auth.substr( authIndex+1, auth.length() );
}
mNeedAuth = false;
@ -94,9 +90,7 @@ void RemoteCamera::Initialise()
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
if ( ret != 0 )
{
if ( ret != 0 ) {
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
}
}

View File

@ -23,8 +23,28 @@
#include "zm_image.h"
#include "zm_monitor.h"
void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames )
{
void Zone::Setup(
Monitor *p_monitor,
int p_id,
const char *p_label,
ZoneType p_type,
const Polygon &p_polygon,
const Rgb p_alarm_rgb,
CheckMethod p_check_method,
int p_min_pixel_threshold,
int p_max_pixel_threshold,
int p_min_alarm_pixels,
int p_max_alarm_pixels,
const Coord &p_filter_box,
int p_min_filter_pixels,
int p_max_filter_pixels,
int p_min_blob_pixels,
int p_max_blob_pixels,
int p_min_blobs,
int p_max_blobs,
int p_overload_frames,
int p_extend_alarm_frames
) {
monitor = p_monitor;
id = p_id;
@ -48,10 +68,7 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_
overload_frames = p_overload_frames;
extend_alarm_frames = p_extend_alarm_frames;
//Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d, AF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames, extend_alarm_frames );
//Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames );
//Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method );
Debug( 1, "Initialised zone %d/%s - %d - %dx%d", id, label, type, polygon.Width(), polygon.Height() );
Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d, AF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames, extend_alarm_frames );
alarmed = false;
pixel_diff = 0;
@ -67,144 +84,113 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_
overload_count = 0;
extend_alarm_count = 0;
pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE);
pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE );
pg_image->Clear();
pg_image->Fill( 0xff, polygon );
pg_image->Outline( 0xff, polygon );
ranges = new Range[monitor->Height()];
for ( unsigned int y = 0; y < monitor->Height(); y++)
{
for ( unsigned int y = 0; y < monitor->Height(); y++ ) {
ranges[y].lo_x = -1;
ranges[y].hi_x = 0;
ranges[y].off_x = 0;
const uint8_t *ppoly = pg_image->Buffer( 0, y );
for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ )
{
if ( *ppoly )
{
if ( ranges[y].lo_x == -1 )
{
for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ ) {
if ( *ppoly ) {
if ( ranges[y].lo_x == -1 ) {
ranges[y].lo_x = x;
}
if ( (unsigned int)ranges[y].hi_x < x )
{
if ( (unsigned int)ranges[y].hi_x < x ) {
ranges[y].hi_x = x;
}
}
}
}
if ( config.record_diag_images )
{
// FIXME: Is this not a problem? If you had two zones for a monitor.. then these would conflict. You would only get 1 dump file
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-poly.jpg", monitor->getStorage()->Path(), monitor->Id(), id);
if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id);
}
pg_image->WriteJpeg( diag_path );
}
}
} // end Zone::Setup
Zone::~Zone()
{
Zone::~Zone() {
delete[] label;
delete image;
delete pg_image;
delete[] ranges;
}
void Zone::RecordStats( const Event *event )
{
void Zone::RecordStats( const Event *event ) {
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, 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()+1, 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 ) )
{
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event stats: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
} // end void Zone::RecordStats( const Event *event )
//=============================================================================
bool Zone::CheckOverloadCount()
{
bool Zone::CheckOverloadCount() {
Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames);
if ( overload_count )
{
if ( overload_count ) {
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--;
return( false );
}
return true;
}
} // end bool Zone::CheckOverloadCount()
void Zone::SetScore(unsigned int nScore)
{
void Zone::SetScore(unsigned int nScore) {
score = nScore;
}
} // end void Zone::SetScore(unsigned int nScore)
void Zone::SetAlarmImage(const Image* srcImage)
{
void Zone::SetAlarmImage(const Image* srcImage) {
delete image;
image = new Image(*srcImage);
}
} // end void Zone::SetAlarmImage( const Image* srcImage )
int Zone::GetOverloadCount()
{
int Zone::GetOverloadCount() {
return overload_count;
}
} // end int Zone::GetOverloadCount()
void Zone::SetOverloadCount(int nOverCount)
{
void Zone::SetOverloadCount(int nOverCount) {
overload_count = nOverCount;
}
} // end void Zone::SetOverloadCount(int nOverCount )
int Zone::GetOverloadFrames()
{
int Zone::GetOverloadFrames() {
return overload_frames;
}
} // end int Zone::GetOverloadFrames
int Zone::GetExtendAlarmCount()
{
int Zone::GetExtendAlarmCount() {
return extend_alarm_count;
}
} // end int Zone::GetExtendAlarmCount()
void Zone::SetExtendAlarmCount(int nExtendAlarmCount)
{
void Zone::SetExtendAlarmCount(int nExtendAlarmCount) {
extend_alarm_count = nExtendAlarmCount;
}
} // end void Zone::SetExtendAlarmCount( int nExtendAlarmCount )
int Zone::GetExtendAlarmFrames()
{
int Zone::GetExtendAlarmFrames() {
return extend_alarm_frames;
}
} // end int Zone::GetExtendAlarmFrames()
bool Zone::CheckExtendAlarmCount()
{
Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames);
if ( extend_alarm_count )
{
bool Zone::CheckExtendAlarmCount() {
Info( "ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames );
if ( extend_alarm_count ) {
Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames );
extend_alarm_count--;
return( true );
}
return false;
}
} // end bool Zone::CheckExtendAlarmCount
//===========================================================================
bool Zone::CheckAlarms( const Image *delta_image )
{
bool Zone::CheckAlarms( const Image *delta_image ) {
ResetStats();
if ( overload_count )
{
if ( overload_count ) {
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--;
@ -236,9 +222,6 @@ bool Zone::CheckAlarms( const Image *delta_image )
Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y );
Storage *storage = monitor->getStorage();
Debug( 5, "Checking for alarmed pixels" );
/* if(config.cpu_extensions && sseversion >= 20) {
sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
} else {
@ -246,12 +229,10 @@ bool Zone::CheckAlarms( const Image *delta_image )
} */
std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
if ( config.record_diag_images )
{
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", storage->Path(), monitor->Id(), id, 1 );
if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 );
}
diff_image->WriteJpeg( diag_path );
}
@ -275,61 +256,50 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
score = (100*alarm_pixels)/polygon.Area();
if(score < 1)
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score );
if ( check_method >= FILTERED_PIXELS )
{
if ( check_method >= FILTERED_PIXELS ) {
int bx = filter_box.X();
int by = filter_box.Y();
int bx1 = bx-1;
int by1 = by-1;
Debug( 5, "Checking for filtered pixels" );
if ( bx > 1 || by > 1 )
{
if ( bx > 1 || by > 1 ) {
// Now remove any pixels smaller than our filter size
unsigned char *cpdiff;
int ldx, hdx, ldy, hdy;
bool block;
for ( unsigned int y = lo_y; y <= hi_y; y++ )
{
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
int lo_x = ranges[y].lo_x;
int hi_x = ranges[y].hi_x;
pdiff = (uint8_t*)diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{
if ( *pdiff == WHITE )
{
for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) {
if ( *pdiff == WHITE ) {
// Check participation in an X block
ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x;
hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1);
ldy = (y>=(lo_y+by1))?-by1:lo_y-y;
hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1);
block = false;
for ( int dy = ldy; !block && dy <= hdy; dy++ )
{
for ( int dx = ldx; !block && dx <= hdx; dx++ )
{
for ( int dy = ldy; !block && dy <= hdy; dy++ ) {
for ( int dx = ldx; !block && dx <= hdx; dx++ ) {
block = true;
for ( int dy2 = 0; block && dy2 < by; dy2++ )
{
for ( int dx2 = 0; block && dx2 < bx; dx2++ )
{
for ( int dy2 = 0; block && dy2 < by; dy2++ ) {
for ( int dx2 = 0; block && dx2 < bx; dx2++ ) {
cpdiff = diff_buff + (((y+dy+dy2)*diff_width) + (x+dx+dx2));
if ( !*cpdiff )
{
if ( !*cpdiff ) {
block = false;
}
}
}
}
}
if ( !block )
{
if ( !block ) {
*pdiff = BLACK;
continue;
}
@ -337,18 +307,14 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
}
}
}
else
{
} else {
alarm_filter_pixels = alarm_pixels;
}
if ( config.record_diag_images )
{
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", storage->Path(), monitor->Id(), id, 2 );
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 );
}
diff_image->WriteJpeg( diag_path );
}
@ -370,12 +336,11 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
score = (100*alarm_filter_pixels)/(polygon.Area());
if(score < 1)
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score );
if ( check_method >= BLOBS )
{
if ( check_method >= BLOBS ) {
Debug( 5, "Checking for blob pixels" );
typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats;
BlobStats blob_stats[256];
@ -384,16 +349,13 @@ bool Zone::CheckAlarms( const Image *delta_image )
uint8_t last_x, last_y;
BlobStats *bsx, *bsy;
BlobStats *bsm, *bss;
for ( unsigned int y = lo_y; y <= hi_y; y++ )
{
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
int lo_x = ranges[y].lo_x;
int hi_x = ranges[y].hi_x;
pdiff = (uint8_t*)diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{
if ( *pdiff == WHITE )
{
for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) {
if ( *pdiff == WHITE ) {
Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff );
//last_x = (x>lo_x)?*(pdiff-1):0;
//last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0;
@ -412,16 +374,13 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
}
if ( last_x )
{
if ( last_x ) {
Debug( 9, "Left neighbour is %d", last_x );
bsx = &blob_stats[last_x];
if ( last_y )
{
if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y );
bsy = &blob_stats[last_y];
if ( last_x == last_y )
{
if ( last_x == last_y ) {
Debug( 9, "Matching neighbours, setting to %d", last_x );
// Add to the blob from the x side (either side really)
*pdiff = last_x;
@ -429,9 +388,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
bsx->count++;
if ( x > bsx->hi_x ) bsx->hi_x = x;
if ( (int)y > bsx->hi_y ) bsx->hi_y = y;
}
else
{
} else {
// Aggregate blobs
bsm = bsx->count>=bsy->count?bsx:bsy;
bss = bsm==bsx?bsy:bsx;
@ -441,19 +398,16 @@ bool Zone::CheckAlarms( const Image *delta_image )
Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y );
// Now change all those pixels to the other setting
int changed = 0;
for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++)
{
for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) {
int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x;
int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x;
Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx );
Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x );
spdiff = diff_buff + ((diff_width * sy) + lo_sx);
for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ )
{
for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) {
Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff );
if ( *spdiff == bss->tag )
{
if ( *spdiff == bss->tag ) {
Debug( 9, "Setting pixel" );
*spdiff = bsm->tag;
changed++;
@ -462,8 +416,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
*pdiff = bsm->tag;
alarm_blob_pixels++;
if ( !changed )
{
if ( !changed ) {
Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y );
Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y );
Error( "No pixels changed, exiting" );
@ -491,9 +444,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
bss->hi_x = 0;
bss->hi_y = 0;
}
}
else
{
} else {
Debug( 9, "Setting to left neighbour %d", last_x );
// Add to the blob from the x side
*pdiff = last_x;
@ -502,11 +453,8 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( x > bsx->hi_x ) bsx->hi_x = x;
if ( (int)y > bsx->hi_y ) bsx->hi_y = y;
}
}
else
{
if ( last_y )
{
} else {
if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y );
Debug( 9, "Setting to top neighbour %d", last_y );
@ -518,28 +466,19 @@ bool Zone::CheckAlarms( const Image *delta_image )
bsy->count++;
if ( x > bsy->hi_x ) bsy->hi_x = x;
if ( (int)y > bsy->hi_y ) bsy->hi_y = y;
}
else
{
} else {
// Create a new blob
int i;
for ( i = (WHITE-1); i > 0; i-- )
{
for ( i = (WHITE-1); i > 0; i-- ) {
BlobStats *bs = &blob_stats[i];
// See if we can recycle one first, only if it's at least two rows up
if ( bs->count && bs->hi_y < (int)(y-1) )
{
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) )
{
if ( config.create_analysis_images || config.record_diag_images )
{
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ )
{
if ( bs->count && bs->hi_y < (int)(y-1) ) {
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) {
if ( config.create_analysis_images || config.record_diag_images ) {
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ )
{
if ( *spdiff == bs->tag )
{
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) {
if ( *spdiff == bs->tag ) {
*spdiff = BLACK;
}
}
@ -558,8 +497,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
bs->hi_y = 0;
}
}
if ( !bs->count )
{
if ( !bs->count ) {
Debug( 9, "Creating new blob %d", i );
*pdiff = i;
alarm_blob_pixels++;
@ -573,8 +511,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
break;
}
}
if ( i == 0 )
{
if ( i == 0 ) {
Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." );
x = hi_x+1;
y = hi_y+1;
@ -584,40 +521,30 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
}
}
if ( config.record_diag_images )
{
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", storage->Path(), monitor->Id(), id, 3 );
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 );
}
diff_image->WriteJpeg( diag_path );
}
if ( !alarm_blobs )
{
if ( !alarm_blobs ) {
return( false );
}
Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs );
// Now eliminate blobs under the threshold
for ( int i = 1; i < WHITE; i++ )
{
for ( int i = 1; i < WHITE; i++ ) {
BlobStats *bs = &blob_stats[i];
if ( bs->count )
{
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) )
{
if ( config.create_analysis_images || config.record_diag_images )
{
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ )
{
if ( bs->count ) {
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) {
if ( config.create_analysis_images || config.record_diag_images ) {
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ )
{
if ( *spdiff == bs->tag )
{
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) {
if ( *spdiff == bs->tag ) {
*spdiff = BLACK;
}
}
@ -634,21 +561,17 @@ bool Zone::CheckAlarms( const Image *delta_image )
bs->lo_y = 0;
bs->hi_x = 0;
bs->hi_y = 0;
}
else
{
} else {
Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs );
if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count;
if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count;
}
}
}
if ( config.record_diag_images )
{
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", storage->Path(), monitor->Id(), id, 4 );
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 );
}
diff_image->WriteJpeg( diag_path );
}
@ -669,7 +592,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
score = (100*alarm_blob_pixels)/(polygon.Area());
if(score < 1)
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score );
@ -677,25 +600,19 @@ bool Zone::CheckAlarms( const Image *delta_image )
alarm_hi_x = polygon.LoX()-1;
alarm_lo_y = polygon.HiY()+1;
alarm_hi_y = polygon.LoY()-1;
for ( int i = 1; i < WHITE; i++ )
{
for ( int i = 1; i < WHITE; i++ ) {
BlobStats *bs = &blob_stats[i];
if ( bs->count )
{
if ( bs->count == max_blob_size )
{
if ( config.weighted_alarm_centres )
{
if ( bs->count ) {
if ( bs->count == max_blob_size ) {
if ( config.weighted_alarm_centres ) {
unsigned long x_total = 0;
unsigned long y_total = 0;
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ )
{
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ )
{
if ( *spdiff == bs->tag )
{
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) {
if ( *spdiff == bs->tag ) {
x_total += sx;
y_total += sy;
}
@ -703,9 +620,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
alarm_mid_x = int(round(x_total/bs->count));
alarm_mid_y = int(round(y_total/bs->count));
}
else
{
} else {
alarm_mid_x = int((bs->hi_x+bs->lo_x+1)/2);
alarm_mid_y = int((bs->hi_y+bs->lo_y+1)/2);
}
@ -715,94 +630,72 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y;
if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x;
if ( alarm_hi_y < bs->hi_y ) alarm_hi_y = bs->hi_y;
}
}
}
else
{
} // end if bs->count
} // end for i < WHITE
} else {
alarm_mid_x = int((alarm_hi_x+alarm_lo_x+1)/2);
alarm_mid_y = int((alarm_hi_y+alarm_lo_y+1)/2);
}
}
if ( type == INCLUSIVE )
{
if ( type == INCLUSIVE ) {
// score >>= 1;
score /= 2;
}
else if ( type == EXCLUSIVE )
{
} else if ( type == EXCLUSIVE ) {
// score <<= 1;
score *= 2;
}
Debug( 5, "Adjusted score is %d", score );
// Now outline the changed region
if ( score )
{
if ( score ) {
alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) );
//if ( monitor->followMotion() )
if ( true )
{
if ( true ) {
alarm_centre = Coord( alarm_mid_x, alarm_mid_y );
}
else
{
} else {
alarm_centre = alarm_box.Centre();
}
if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images )
{
if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) {
// First mask out anything we don't want
for ( unsigned int y = lo_y; y <= hi_y; y++ )
{
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
pdiff = diff_buff + ((diff_width * y) + lo_x);
int lo_x2 = ranges[y].lo_x;
int hi_x2 = ranges[y].hi_x;
int lo_gap = lo_x2-lo_x;
if ( lo_gap > 0 )
{
if ( lo_gap == 1 )
{
if ( lo_gap > 0 ) {
if ( lo_gap == 1 ) {
*pdiff++ = BLACK;
}
else
{
} else {
memset( pdiff, BLACK, lo_gap );
pdiff += lo_gap;
}
}
ppoly = pg_image->Buffer( lo_x2, y );
for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ )
{
if ( !*ppoly )
{
for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) {
if ( !*ppoly ) {
*pdiff = BLACK;
}
}
int hi_gap = hi_x-hi_x2;
if ( hi_gap > 0 )
{
if ( hi_gap == 1 )
{
if ( hi_gap > 0 ) {
if ( hi_gap == 1 ) {
*pdiff = BLACK;
}
else
{
} else {
memset( pdiff, BLACK, hi_gap );
}
}
}
if( monitor->Colours() == ZM_COLOUR_GRAY8 ) {
if ( monitor->Colours() == ZM_COLOUR_GRAY8 ) {
image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() );
} else {
image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() );
@ -810,20 +703,18 @@ bool Zone::CheckAlarms( const Image *delta_image )
// Only need to delete this when 'image' becomes detached and points somewhere else
delete diff_image;
}
else
{
} else {
delete image;
image = 0;
}
Debug( 3, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score );
Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d",
Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score );
}
return( true );
}
bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon )
{
bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
Debug( 3, "Parsing polygon string '%s'", poly_string );
char *str_ptr = new char[strlen(poly_string)+1];
@ -834,27 +725,21 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon )
int n_coords = 0;
int max_n_coords = strlen(str)/4;
Coord *coords = new Coord[max_n_coords];
while( true )
{
if ( *str == '\0' )
{
while( true ) {
if ( *str == '\0' ) {
break;
}
ws = strchr( str, ' ' );
if ( ws )
{
if ( ws ) {
*ws = '\0';
}
char *cp = strchr( str, ',' );
if ( !cp )
{
if ( !cp ) {
Error( "Bogus coordinate %s found in polygon string", str );
delete[] coords;
delete[] str_ptr;
return( false );
}
else
{
} else {
*cp = '\0';
char *xp = str;
char *yp = cp+1;
@ -892,8 +777,7 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon )
return( true );
}
bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon )
{
bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) {
Debug( 3, "Parsing zone string '%s'", zone_string );
char *str_ptr = new char[strlen(zone_string)+1];
@ -901,14 +785,12 @@ bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour,
strcpy( str, zone_string );
char *ws = strchr( str, ' ' );
if ( !ws )
{
if ( !ws ) {
Debug( 3, "No initial whitespace found in zone string '%s', finishing", str );
}
zone_id = strtol( str, 0, 10 );
Debug( 3, "Got zone %d from zone string", zone_id );
if ( !ws )
{
if ( !ws ) {
delete str_ptr;
return( true );
}
@ -917,14 +799,12 @@ bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour,
str = ws+1;
ws = strchr( str, ' ' );
if ( !ws )
{
if ( !ws ) {
Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string );
}
colour = strtol( str, 0, 16 );
Debug( 3, "Got colour %06x from zone string", colour );
if ( !ws )
{
if ( !ws ) {
delete str_ptr;
return( true );
}
@ -941,8 +821,7 @@ bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour,
return( result );
}
int Zone::Load( Monitor *monitor, Zone **&zones )
{
int Zone::Load( Monitor *monitor, Zone **&zones ) {
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() );
if ( mysql_query( &dbconn, sql ) ) {
@ -960,12 +839,11 @@ int Zone::Load( Monitor *monitor, Zone **&zones )
delete[] zones;
zones = new Zone *[n_zones];
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) {
zones[i] = NULL;
int col = 0;
int Id = atoi(dbrow[col++]);
const char *Name = dbrow[col++];
ZoneType Type = (ZoneType) atoi(dbrow[col++]);
int Type = atoi(dbrow[col++]);
const char *Units = dbrow[col++];
const char *Coords = dbrow[col++];
int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++;
@ -1012,25 +890,22 @@ int Zone::Load( Monitor *monitor, Zone **&zones )
MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100;
}
if ( Type == INACTIVE ) {
if ( atoi(dbrow[2]) == Zone::INACTIVE ) {
zones[i] = new Zone( monitor, Id, Name, polygon );
} else if ( Type == PRIVACY ) {
zones[i] = new Zone( monitor, Id, Name, Type, polygon );
} else {
zones[i] = new Zone( monitor, Id, Name, Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames );
} else if ( atoi(dbrow[2]) == Zone::PRIVACY ) {
zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon );
}
zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames );
}
} // end foreach row in zones table
if ( mysql_errno( &dbconn ) ) {
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
// Yadda yadda
mysql_free_result( result );
return( n_zones );
}
bool Zone::DumpSettings( char *output, bool /*verbose*/ )
{
bool Zone::DumpSettings( char *output, bool /*verbose*/ ) {
output[0] = 0;
sprintf( output+strlen(output), " Id : %d\n", id );
@ -1044,8 +919,7 @@ bool Zone::DumpSettings( char *output, bool /*verbose*/ )
type==PRIVACY?"Privacy":"Unknown"
))))));
sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() );
for ( int i = 0; i < polygon.getNumCoords(); i++ )
{
for ( int i = 0; i < polygon.getNumCoords(); i++ ) {
sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() );
}
sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb );
@ -1084,8 +958,7 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
lo_y = polygon.LoY();
hi_y = polygon.HiY();
for ( unsigned int y = lo_y; y <= hi_y; y++ )
{
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
lo_x = ranges[y].lo_x;
hi_x = ranges[y].hi_x;
@ -1093,16 +966,12 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y );
ppoly = ppoly_image->Buffer( lo_x, y );
for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ )
{
if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) )
{
for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) {
if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) {
pixelsalarmed++;
pixelsdifference += *pdiff;
*pdiff = WHITE;
}
else
{
} else {
*pdiff = BLACK;
}
}

View File

@ -94,7 +94,10 @@ class ImageComponent extends Component {
// Take the StartTime of an Event and return
// the path to its location on the filesystem
public function getEventPath( $event ) {
if ( $config['ZM_USE_DEEP_STORAGE'] == 1 )
return $event['Event']['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['Event']['StartTime']) );
else
return $event['Event']['MonitorId'].'/'.$event['Event']['Id'];
}
}
?>

View File

@ -1,9 +0,0 @@
AUTOMAKE_OPTIONS = gnu
webdir = @WEB_PREFIX@/css
dist_web_DATA = \
bootstrap.min.css \
reset.css \
spinner.css \
overlay.css

View File

@ -61,12 +61,8 @@
text-align: left;
}
#consoleTable .colOrder {
text-align: center;
}
#consoleTable .colMark {
width: 32px;
width: 62px;
text-align: center;
}
@ -85,13 +81,3 @@
#consoleTable .colLeftButtons input {
margin-right: 24px;
}
#consoleTable .colRightButtons {
text-align: right;
padding-right: 8px;
}
#consoleTable .colRightButtons input {
margin: 0 8px;
}

View File

@ -61,12 +61,8 @@
text-align: left;
}
#consoleTable .colOrder {
text-align: center;
}
#consoleTable .colMark {
width: 32px;
width: 62px;
text-align: center;
}
@ -85,13 +81,3 @@
#consoleTable .colLeftButtons input {
margin-right: 24px;
}
#consoleTable .colRightButtons {
text-align: right;
padding-right: 8px;
}
#consoleTable .colRightButtons input {
margin: 0 8px;
}

View File

@ -61,12 +61,8 @@
text-align: left;
}
#consoleTable .colOrder {
text-align: center;
}
#consoleTable .colMark {
width: 32px;
width: 62px;
text-align: center;
}
@ -85,12 +81,3 @@
#consoleTable .colLeftButtons input {
margin-right: 24px;
}
#consoleTable .colRightButtons {
text-align: right;
padding-right: 8px;
}
#consoleTable .colRightButtons input {
margin: 0 8px;
}

View File

@ -74,8 +74,6 @@ function xhtmlHeaders( $file, $title ) {
<script type="text/javascript" src="tools/mootools/mootools-core.js"></script>
<script type="text/javascript" src="tools/mootools/mootools-more.js"></script>
<script type="text/javascript" src="js/mootools.ext.js"></script>
<?php if ( !in_array($basename, $bad_views) ) { ?>
<!--<script type="text/javascript" src="js/overlay.js"></script>-->
<script type="text/javascript" src="skins/<?php echo $skin; ?>/js/jquery-1.11.3.js"></script>
<script type="text/javascript" src="skins/<?php echo $skin; ?>/js/jquery-ui-1.11.3.js"></script>
<script type="text/javascript" src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
@ -90,7 +88,6 @@ var $j = jQuery.noConflict();
//]]>
</script>
<script type="text/javascript" src="skins/<?php echo $skin; ?>/views/js/state.js"></script>
<?php } ?>
<?php if ( $title == 'Login' && (defined('ZM_OPT_USE_GOOG_RECAPTCHA') && ZM_OPT_USE_GOOG_RECAPTCHA) ) { ?>
<script src='https://www.google.com/recaptcha/api.js'></script>
<?php } else if ( $title == 'Event' ) {

View File

@ -83,6 +83,7 @@ $eventCounts = array(
$displayMonitors = NULL;
<<<<<<< HEAD
# Also populates displayMonitors
$navbar = getNavBarHTML();
$zoneCount = 0;
@ -107,12 +108,76 @@ for( $i = 0; $i < count($displayMonitors); $i += 1 ) {
$eventCounts[$j]['total'] += $monitor['EventCount'.$j];
}
$zoneCount += $monitor['ZoneCount'];
=======
$group = NULL;
if ( ! empty($_COOKIE['zmGroup']) ) {
if ( $group = dbFetchOne( 'select * from Groups where Id = ?', NULL, array($_COOKIE['zmGroup'])) )
$groupIds = array_flip(explode( ',', $group['MonitorIds'] ));
}
noCacheHeaders();
$maxWidth = 0;
$maxHeight = 0;
$cycleCount = 0;
$minSequence = 0;
$maxSequence = 1;
$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" );
$displayMonitors = array();
for ( $i = 0; $i < count($monitors); $i++ ) {
if ( !visibleMonitor( $monitors[$i]['Id'] ) ) {
continue;
}
if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) {
continue;
}
$monitors[$i]['Show'] = true;
$monitors[$i]['zmc'] = zmcStatus( $monitors[$i] );
$monitors[$i]['zma'] = zmaStatus( $monitors[$i] );
$monitors[$i]['ZoneCount'] = dbFetchOne( 'select count(Id) as ZoneCount from Zones where MonitorId = ?', 'ZoneCount', array($monitors[$i]['Id']) );
$counts = array();
for ( $j = 0; $j < count($eventCounts); $j++ ) {
$filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) );
parseFilter( $filter );
$counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j";
$monitors[$i]['eventCounts'][$j]['filter'] = $filter;
}
$sql = "select ".join($counts,", ")." from Events as E where MonitorId = ?";
$counts = dbFetchOne( $sql, NULL, array($monitors[$i]['Id']) );
if ( $monitors[$i]['Function'] != 'None' ) {
$cycleCount++;
$scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
$scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth;
if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight;
}
if ( $counts ) $monitors[$i] = array_merge( $monitors[$i], $counts );
$displayMonitors[] = $monitors[$i];
>>>>>>> dragndrop_monitor_sorting
}
noCacheHeaders();
$eventsView = ZM_WEB_EVENTS_VIEW;
$eventsWindow = 'zm'.ucfirst(ZM_WEB_EVENTS_VIEW);
<<<<<<< HEAD
=======
$eventCount = 0;
for ( $i = 0; $i < count($eventCounts); $i++ ) {
$eventCounts[$i]['total'] = 0;
}
$zoneCount = 0;
foreach( $displayMonitors as $monitor ) {
for ( $i = 0; $i < count($eventCounts); $i++ ) {
$eventCounts[$i]['total'] += $monitor['EventCount'.$i];
}
$zoneCount += $monitor['ZoneCount'];
}
$versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':'';
>>>>>>> dragndrop_monitor_sorting
$left_columns = 3;
if ( count($servers) ) $left_columns += 1;
if ( ZM_WEB_ID_ON_CONSOLE ) $left_columns += 1;
@ -124,11 +189,56 @@ xhtmlHeaders( __FILE__, translate('Console') );
<form name="monitorForm" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="action" value=""/>
<<<<<<< HEAD
<?php echo $navbar ?>
<div class="container-fluid">
<table class="table table-striped table-hover table-condensed">
=======
<div id="header">
<h3 id="systemTime"><?php echo preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG ) ?></h3>
<h3 id="systemStats"><?php echo translate('Load') ?>: <?php echo getLoad() ?> - <?php echo translate('Disk') ?>: <?php echo getDiskPercent() ?>% - <?php echo ZM_PATH_MAP ?>: <?php echo getDiskPercent(ZM_PATH_MAP) ?>%</h3>
<h2 id="title"><a href="http://www.zoneminder.com" target="ZoneMinder">ZoneMinder</a> <?php echo translate('Console') ?> - <?php echo makePopupLink( '?view=state', 'zmState', 'state', $status, canEdit( 'System' ) ) ?> - <?php echo $run_state ?> <?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?></h2>
<div class="clear"></div>
<h3 id="development"><center><?php echo ZM_WEB_CONSOLE_BANNER ?></center></h3>
<div id="monitorSummary"><?php echo makePopupLink( '?view=groups', 'zmGroups', 'groups', sprintf( $CLANG['MonitorCount'], count($displayMonitors), zmVlang( $VLANG['Monitor'], count($displayMonitors) ) ).($group?' ('.$group['Name'].')':''), canView( 'Groups' ) ); ?></div>
<?php
if ( ZM_OPT_X10 && canView( 'Devices' ) ) {
?>
<div id="devices"><?php echo makePopupLink( '?view=devices', 'zmDevices', 'devices', translate('Devices') ) ?></div>
<?php
}
if ( canView( 'System' ) ) {
?>
<div id="options"><?php echo makePopupLink( '?view=options', 'zmOptions', 'options', translate('Options') ) ?><?php if ( logToDatabase() > Logger::NOLOG ) { ?> / <?php echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' ) ?><?php } ?></div>
<?php
}
if ( canView( 'Stream' ) && $cycleCount > 1 ) {
$cycleGroup = isset($_COOKIE['zmGroup'])?$_COOKIE['zmGroup']:0;
?>
<div id="cycleMontage">
<?php echo makePopupLink( '?view=cycle&amp;group='.$cycleGroup, 'zmCycle'.$cycleGroup, array( 'cycle', $cycleWidth, $cycleHeight ), translate('Cycle'), $running ) ?>&nbsp;/&nbsp;
<?php echo makePopupLink( '?view=montage&amp;group='.$cycleGroup, 'zmMontage'.$cycleGroup, 'montage', translate('Montage'), $running ) ?>&nbsp;/&nbsp;
<?php echo makePopupLink( '?view=montagereview&amp;group='.$cycleGroup, 'zmMontage'.$cycleGroup, 'montagereview', translate('Montage Review'), $running ) ?>
</div>
<?php
} else {
?>
<?php
}
?>
<h3 id="loginBandwidth"><?php
if ( ZM_OPT_USE_AUTH ) {
?><?php echo translate('LoggedInAs') ?> <?php echo makePopupLink( '?view=logout', 'zmLogout', 'logout', $user['Username'], (ZM_AUTH_TYPE == "builtin") ) ?>, <?php echo strtolower( translate('ConfiguredFor') ) ?><?php
} else {
?><?php echo translate('ConfiguredFor') ?><?php
}
?>&nbsp;<?php echo makePopupLink( '?view=bandwidth', 'zmBandwidth', 'bandwidth', $bwArray[$_COOKIE['zmBandwidth']], ($user && $user['MaxBandwidth'] != 'low' ) ) ?> <?php echo translate('BandwidthHead') ?></h3>
</div>
<div id="content">
<table id="consoleTable" cellspacing="0">
>>>>>>> dragndrop_monitor_sorting
<thead>
<tr>
<?php if ( ZM_WEB_ID_ON_CONSOLE ) { ?>
@ -140,6 +250,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
<th class="colServer"><?php echo translate('Server') ?></th>
<?php } ?>
<th class="colSource"><?php echo translate('Source') ?></th>
<<<<<<< HEAD
<?php if ( $show_storage_areas ) { ?>
<th class="colStorage"><?php echo translate('Storage') ?></th>
<?php } ?>
@ -148,6 +259,16 @@ xhtmlHeaders( __FILE__, translate('Console') );
<?php } ?>
<th class="colZones"><a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=zones_overview"><?php echo translate('Zones') ?></a></th>
<?php if ( canEdit('Monitors') ) { ?>
=======
<?php
for ( $i = 0; $i < count($eventCounts); $i++ ) {
?>
<th class="colEvents"><?php echo $eventCounts[$i]['title'] ?></th>
<?php
}
?>
<th class="colZones"><?php echo translate('Zones') ?></th>
>>>>>>> dragndrop_monitor_sorting
<th class="colMark"><?php echo translate('Mark') ?></th>
<?php } ?>
</tr>
@ -155,6 +276,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
<tfoot>
<tr>
<td class="colLeftButtons" colspan="<?php echo $left_columns ?>">
<<<<<<< HEAD
<input type="button" class="btn btn-primary" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/>
<input type="button" class="btn btn-primary" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
<!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> -->
@ -164,28 +286,49 @@ xhtmlHeaders( __FILE__, translate('Console') );
</td>
<?php for ( $i = 0; $i < count($eventCounts); $i++ ) {
parseFilter( $eventCounts[$i]['filter'] );
=======
<input type="button" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/>
<input type="button" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
<!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> -->
<?php echo makePopupButton( '?view=filter&amp;filter[terms][0][attr]=DateTime&amp;filter[terms][0][op]=%3c&amp;filter[terms][0][val]=now', 'zmFilter', 'filter', translate('Filters'), canView( 'Events' ) ) ?>
<input type="button" name="editBtn" value="<?php echo translate('Edit') ?>" onclick="editMonitor( this )" disabled="disabled"/>
<input type="button" name="deleteBtn" value="<?php echo translate('Delete') ?>" onclick="deleteMonitor( this )" disabled="disabled"/>
</td>
<?php
for ( $i = 0; $i < count($eventCounts); $i++ ) {
parseFilter( $eventCounts[$i]['filter'] );
>>>>>>> dragndrop_monitor_sorting
?>
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&amp;page=1'.$eventCounts[$i]['filter']['query'], $eventsWindow, $eventsView, $eventCounts[$i]['total'], canView( 'Events' ) ) ?></td>
<?php
}
?>
<td class="colZones"><?php echo $zoneCount ?></td>
<<<<<<< HEAD
<?php if ( canEdit('Monitors') ) { ?>
<td class="colMark"></td>
<?php } ?>
=======
<td class="colMark"></td>
>>>>>>> dragndrop_monitor_sorting
</tr>
</tfoot>
<tbody id="consoleTableBody">
<?php
<<<<<<< HEAD
for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
$monitor = $displayMonitors[$monitor_i];
?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
=======
foreach( $displayMonitors as $monitor ) {
?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>">
>>>>>>> dragndrop_monitor_sorting
<?php
if ( !$monitor['zmc'] )
if ( !$monitor['zmc'] ) {
$dclass = "errorText";
else
{
} else {
// https://github.com/ZoneMinder/ZoneMinder/issues/1082
if ( !$monitor['zma'] && $monitor['Function']!='Monitor' )
$dclass = "warnText";
@ -262,13 +405,4 @@ if ( canEdit('System') ) {
}
?>
</body>
<script>
$j( function() {
$j( "#consoleTableBody" ).sortable({
handle: ".glyphicon-sort",
update: applySort,
axis:'Y' } );
$j( "#consoleTableBody" ).disableSelection();
} );
</script>
</html>

View File

@ -74,6 +74,15 @@ function initPage() {
createPopup( '?view=version', 'zmVersion', 'version' );
if ( showDonatePopup )
createPopup( '?view=donate', 'zmDonate', 'donate' );
// Makes table sortable
$j( function() {
$j( "#consoleTableBody" ).sortable({
handle: ".glyphicon-sort",
update: applySort,
axis:'Y' } );
$j( "#consoleTableBody" ).disableSelection();
} );
}
function applySort(event, ui) {

View File

@ -19,7 +19,6 @@
//
require_once( 'includes/Server.php');
require_once( 'includes/Storage.php');
if ( !canView( 'Monitors' ) ) {
$view = "error";
@ -29,7 +28,6 @@ if ( !canView( 'Monitors' ) ) {
$tabs = array();
$tabs["general"] = translate('General');
$tabs["source"] = translate('Source');
$tabs["storage"] = translate('Storage');
$tabs["timestamp"] = translate('Timestamp');
$tabs["buffers"] = translate('Buffers');
if ( ZM_OPT_CONTROL && canView( 'Control' ) )
@ -137,7 +135,6 @@ if ( ! empty($_REQUEST['mid']) ) {
'V4LMultiBuffer' => '',
'V4LCapturesPerFrame' => 1,
'ServerId' => $Server['Id'],
'StorageId' => '0',
) );
} # end if $_REQUEST['dupID']
} # end if $_REQUEST['mid']
@ -519,7 +516,6 @@ if ( $tab != 'general' ) {
?>
<input type="hidden" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name) ?>"/>
<input type="hidden" name="newMonitor[ServerId]" value="<?php echo validHtmlStr($monitor->ServerId() ) ?>"/>
<input type="hidden" name="newMonitor[StorageId]" value="<?= validHtmlStr($monitor->StorageId() ) ?>"/>
<input type="hidden" name="newMonitor[Type]" value="<?php echo validHtmlStr($monitor->Type) ?>"/>
<input type="hidden" name="newMonitor[Function]" value="<?php echo validHtmlStr($monitor->Function) ?>"/>
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($monitor->Enabled) ?>"/>
@ -682,17 +678,6 @@ switch ( $tab )
$servers[$server_obj->Id()] = $server_obj->Name();
}
echo htmlSelect( "newMonitor[ServerId]", $servers, $monitor->ServerId() );
?>
</td></tr>
<tr><td><?php echo translate('StorageArea') ?></td><td>
<?php
$storage_areas = array(0=>'Default');
$result = dbQuery( 'SELECT * FROM Storage ORDER BY Name');
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage' );
foreach ( $results as $row => $storage_obj ) {
$storage_areas[$storage_obj->Id] = $storage_obj->Name();
}
echo htmlSelect( "newMonitor[StorageId]", $storage_areas, $monitor->StorageId() );
?>
</td></tr>
<tr><td><?php echo translate('SourceType') ?></td><td><?php echo htmlSelect( "newMonitor[Type]", $sourceTypes, $monitor->Type() ); ?></td></tr>