diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 9bb8d26e5..974409ff3 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -247,7 +247,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { if ( config.record_diag_images_fifo ) { FifoDebug(5, "{\"zone\":%d,\"type\":\"ALRM\",\"pixels\":%d,\"avg_diff\":%d}", - id,alarm_pixels, pixel_diff); + id, alarm_pixels, pixel_diff); } if ( alarm_pixels ) { @@ -264,11 +264,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - if ( max_alarm_pixels != 0 ) - score = (100*alarm_pixels)/max_alarm_pixels; - else - score = (100*alarm_pixels)/polygon.Area(); - + score = (100*alarm_pixels)/(max_alarm_pixels?max_alarm_pixels:polygon.Area()); 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); @@ -358,8 +354,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { 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]; + // ICON FIXME Would like to get rid of this memset memset(blob_stats, 0, sizeof(BlobStats)*256); uint8_t *spdiff; uint8_t last_x, last_y; @@ -369,22 +364,15 @@ bool Zone::CheckAlarms(const Image *delta_image) { int lo_x = ranges[y].lo_x; int hi_x = ranges[y].hi_x; - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); + pdiff = (uint8_t*)diff_image->Buffer(lo_x, y); 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; - last_x = 0; - if ( x > 0 ) { - if ( (x-1) >= lo_x ) { - last_x = *(pdiff-1); - } - } + last_x = ((x > 0) && ( (x-1) >= lo_x )) ? *(pdiff-1) : 0; last_y = 0; - if (y > 0 ) { + if ( y > 0 ) { if ( (y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x ) { last_y = *(pdiff-diff_width); } @@ -631,7 +619,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { } if ( max_blob_pixels != 0 ) - score = (100*alarm_blob_pixels)/(max_blob_pixels); + score = (100*alarm_blob_pixels)/max_blob_pixels; else score = (100*alarm_blob_pixels)/polygon.Area(); @@ -758,67 +746,42 @@ bool Zone::CheckAlarms(const Image *delta_image) { } 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]; - char *str = str_ptr; - strcpy(str, poly_string); + char *str = (char *)poly_string; char *ws; + char *cp; int n_coords = 0; int max_n_coords = strlen(str)/4; Coord *coords = new Coord[max_n_coords]; - while( true ) { - if ( *str == '\0' ) { - break; - } - ws = strchr(str, ' '); - if ( ws ) { - *ws = '\0'; - } - char *cp = strchr(str, ','); + while ( *str != '\0' ) { + cp = strchr(str, ','); if ( !cp ) { Error("Bogus coordinate %s found in polygon string", str); - delete[] coords; - delete[] str_ptr; - return false; - } else { - *cp = '\0'; - char *xp = str; - char *yp = cp+1; + break; + } + int x = atoi(str); + int y = atoi(cp+1); + Debug(3, "Got coordinate %d,%d from polygon string", x, y); + coords[n_coords++] = Coord(x, y); - int x = atoi(xp); - int y = atoi(yp); - - Debug(3, "Got coordinate %d,%d from polygon string", x, y); -#if 0 - if ( x < 0 ) - x = 0; - else if ( x >= width ) - x = width-1; - if ( y < 0 ) - y = 0; - else if ( y >= height ) - y = height-1; -#endif - coords[n_coords++] = Coord( x, y ); - } + ws = strchr(cp+2, ' '); if ( ws ) str = ws+1; else break; - } - polygon = Polygon(n_coords, coords); + } // end while ! end of string - Debug(3, "Successfully parsed polygon string"); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + if ( n_coords > 2 ) { + Debug(3, "Successfully parsed polygon string %s", str); + polygon = Polygon(n_coords, coords); + } else { + Error("Not enough coordinates to form a polygon!"); + n_coords = 0; + } delete[] coords; - delete[] str_ptr; - - return true; -} + return n_coords ? true : false; +} // end bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) { Debug(3, "Parsing zone string '%s'", zone_string); @@ -827,13 +790,12 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P char *str = str_ptr; strcpy(str, zone_string); + zone_id = strtol(str, 0, 10); + Debug(3, "Got zone %d from zone string", zone_id); + char *ws = strchr(str, ' '); 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 ) { delete[] str_ptr; return true; } @@ -841,13 +803,11 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P *ws = '\0'; str = ws+1; + colour = strtol(str, 0, 16); + Debug(3, "Got colour %06x from zone string", colour); ws = strchr(str, ' '); 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 ) { delete[] str_ptr; return true; } @@ -855,27 +815,28 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P str = ws+1; bool result = ParsePolygonString(str, polygon); - - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] str_ptr; return result; -} +} // end bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) int Zone::Load(Monitor *monitor, Zone **&zones) { static char sql[ZM_SQL_MED_BUFSIZ]; db_mutex.lock(); - 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()); + 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) ) { Error("Can't run query: %s", mysql_error(&dbconn)); db_mutex.unlock(); return 0; } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { Error("Can't use query result: %s", mysql_error(&dbconn)); db_mutex.unlock(); @@ -944,7 +905,13 @@ int Zone::Load(Monitor *monitor, Zone **&zones) { } 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); + 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 mysql_free_result(result); return n_zones; @@ -953,9 +920,9 @@ int Zone::Load(Monitor *monitor, Zone **&zones) { bool Zone::DumpSettings(char *output, bool /*verbose*/) { output[0] = 0; - sprintf( output+strlen(output), " Id : %d\n", id ); - sprintf( output+strlen(output), " Label : %s\n", label ); - sprintf( output+strlen(output), " Type: %d - %s\n", type, + sprintf(output+strlen(output), " Id : %d\n", id ); + sprintf(output+strlen(output), " Label : %s\n", label ); + sprintf(output+strlen(output), " Type: %d - %s\n", type, type==ACTIVE?"Active":( type==INCLUSIVE?"Inclusive":( type==EXCLUSIVE?"Exclusive":( @@ -1020,10 +987,10 @@ void Zone::std_alarmedpixels( *pdiff = BLACK; } } - } + } // end for y = lo_y to hi_y /* Store the results */ *pixel_count = pixelsalarmed; *pixel_sum = pixelsdifference; Debug(7, "STORED pixelsalarmed(%d), pixelsdifference(%d)", pixelsalarmed, pixelsdifference); -} +} // end void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) diff --git a/src/zm_zone.h b/src/zm_zone.h index 0c7b26a57..7d7797725 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -32,15 +32,14 @@ class Monitor; // This describes a 'zone', or an area of an image that has certain // detection characteristics. // -class Zone -{ +class Zone { protected: - struct Range - { + struct Range { int lo_x; int hi_x; int off_x; }; + typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; public: typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType; @@ -52,8 +51,8 @@ protected: int id; char *label; - ZoneType type; - Polygon polygon; + ZoneType type; + Polygon polygon; Rgb alarm_rgb; CheckMethod check_method; @@ -67,17 +66,18 @@ protected: int min_filter_pixels; int max_filter_pixels; + BlobStats blob_stats[256]; int min_blob_pixels; int max_blob_pixels; int min_blobs; int max_blobs; - int overload_frames; + int overload_frames; int extend_alarm_frames; // Outputs/Statistics - bool alarmed; - bool was_alarmed; + bool alarmed; + bool was_alarmed; int pixel_diff; unsigned int alarm_pixels; int alarm_filter_pixels; @@ -139,8 +139,7 @@ public: inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline unsigned int Score() const { return( score ); } - inline void ResetStats() - { + inline void ResetStats() { alarmed = false; was_alarmed = false; pixel_diff = 0; diff --git a/web/ajax/newlog.php b/web/ajax/newlog.php new file mode 100644 index 000000000..f97a6b649 --- /dev/null +++ b/web/ajax/newlog.php @@ -0,0 +1,114 @@ +Id()] = $server; +} + +$rows = array(); +foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) { + $row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey'])); + $row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : ''; + // First strip out any html tags + // Second strip out all characters that are not ASCII 32-126 (yes, 126) + $row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message'])); + $rows[] = $row; +} +$data['rows'] = $rows; +$data['logstate'] = logState(); +$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG); + +ajaxResponse($data); + diff --git a/web/skins/classic/views/js/newlog.js b/web/skins/classic/views/js/newlog.js new file mode 100644 index 000000000..667cd30e3 --- /dev/null +++ b/web/skins/classic/views/js/newlog.js @@ -0,0 +1,116 @@ +var table = $j('#logTable'); + +/* +This is the format of the json object sent by bootstrap-table + +var params = +{ +"type":"get", +"data": + { + "search":"some search text", + "sort":"DateTime", + "order":"asc", + "offset":0, + "limit":25 + }, +"cache":true, +"contentType":"application/json", +"dataType":"json" +}; +*/ + +// Called by bootstrap-table to retrieve zm log data +function ajaxRequest(params) { + $j.getJSON(thisUrl + '?view=request&request=newlog&task=query', params.data) + .done(function(res) { + //console.log('total: ' + res.total); + //console.log('totalNotFiltered: ' + res.totalNotFiltered); + console.log(JSON.stringify(params)); + // rearrange the result into what bootstrap-table expects + var data = {total: res.total, totalNotFiltered: res.totalNotFiltered, rows: res.rows}; + params.success(data); + updateHeaderStats(res); + }) + .fail(logAjaxFail); +} + +function updateHeaderStats(data) { + var pageNum = table.bootstrapTable('getOptions').pageNumber; + var pageSize = table.bootstrapTable('getOptions').pageSize; + var startRow = ( (pageNum - 1 ) * pageSize ) + 1; + var stopRow = pageNum * pageSize; + var newClass = (data.logstate == 'ok') ? 'text-success' : (data.logstate == 'alert' ? 'text-warning' : ((data.logstate == 'alarm' ? 'text-danger' : ''))); + + $j('#logState').text(data.logstate); + $j('#logState').removeClass('text-success'); + $j('#logState').removeClass('text-warning'); + $j('#logState').removeClass('text-danger'); + $j('#logState').addClass(newClass); + + $j('#totalLogs').text(data.total); + $j('#availLogs').text(data.totalNotFiltered); + $j('#lastUpdate').text(data.updated); + $j('#displayLogs').text(startRow + ' to ' + stopRow); +} + +function initPage() { + var backBtn = $j('#backBtn'); + + // Define the icons used in the bootstrap-table top-right toolbar + var icons = { + paginationSwitchDown: 'fa-caret-square-o-down', + paginationSwitchUp: 'fa-caret-square-o-up', + export: 'fa-download', + refresh: 'fa-refresh', + autoRefresh: 'fa-clock-o', + advancedSearchIcon: 'fa-chevron-down', + toggleOff: 'fa-toggle-off', + toggleOn: 'fa-toggle-on', + columns: 'fa-th-list', + fullscreen: 'fa-arrows-alt', + detailOpen: 'fa-plus', + detailClose: 'fa-minus' + }; + + // Init the bootstrap-table with custom icons + table.bootstrapTable({icons: icons}); + + // Assign inf, err, fat, dbg color classes to the rows in the table + table.on('post-body.bs.table', function(data) { + $j('#logTable tr').each(function(ndx, row) { + var row = $j(row) + var level = row.find('td:eq(4)').text(); + + if (( level == 'FAT' ) || ( level == 'PNC' )) { + row.addClass('bg-danger'); + row.addClass('font-weight-bold'); + } else if ( level == 'ERR' ) { + row.addClass('bg-danger'); + } else if ( level == 'WAR' ) { + row.addClass('bg-warning'); + } else if ( level == 'DBG' ) { + row.addClass('bg-info'); + } + }); + }); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/newlog.php b/web/skins/classic/views/newlog.php new file mode 100644 index 000000000..ee3e8a01f --- /dev/null +++ b/web/skins/classic/views/newlog.php @@ -0,0 +1,95 @@ + +
+ +| + | + | + | + | + | + | + | + |
|---|