700,
'height'=>460,
'image' => array(
'width'=>264,
'height'=>220,
'topOffset'=>20,
),
'imageText' => array(
'width'=>400,
'height'=>30,
'topOffset'=>20,
),
'graph' => array(
'width'=>600,
'height'=>160,
'topOffset'=>30,
),
'title' => array(
'topOffset'=>50
),
'key' => array(
'topOffset'=>50
),
'axes' => array(
'x' => array(
'height' => 20,
),
'y' => array(
'width' => 30,
),
),
'grid' => array(
'x' => array(
'major' => array(
'max' => 12,
'min' => 4,
),
'minor' => array(
'max' => 48,
'min' => 12,
),
),
'y' => array(
'major' => array(
'max' => 8,
'min' => 1,
),
'minor' => array(
'max' => 0,
'min' => 0,
),
),
),
);
$monitors = array();
# The as E, and joining with Monitors is required for the filterSQL filters.
$rangeSql = 'SELECT min(E.StartDateTime) AS MinTime, max(E.EndDateTime) AS MaxTime FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(E.StartDateTime) AND NOT isnull(E.EndDateTime)';
$eventsSql = 'SELECT E.* FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)';
$eventIdsSql = 'SELECT E.Id FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)';
$eventsValues = array();
if ( !empty($user['MonitorIds']) ) {
$monFilterSql = ' AND MonitorId IN ('.$user['MonitorIds'].')';
$rangeSql .= $monFilterSql;
$eventsSql .= $monFilterSql;
$eventIdsSql .= $monFilterSql;
}
$tree = false;
if ( isset($_REQUEST['filter']) ) {
$filter = ZM\Filter::parse($_REQUEST['filter']);
$tree = $filter->tree();
ZM\Debug('Parse tree: ' . print_r($tree,true));
}
if ( isset($_REQUEST['range']) )
$range = validHtmlStr($_REQUEST['range']);
if ( isset($_REQUEST['minTime']) )
$minTime = validHtmlStr($_REQUEST['minTime']);
if ( isset($_REQUEST['midTime']) )
$midTime = validHtmlStr($_REQUEST['midTime']);
if ( isset($_REQUEST['maxTime']) )
$maxTime = validHtmlStr($_REQUEST['maxTime']);
if ( isset($range) and validInt($range) ) {
$halfRange = (int)($range/2);
if ( isset($midTime) ) {
$midTimeT = strtotime($midTime);
$minTimeT = $midTimeT-$halfRange;
$maxTimeT = $midTimeT+$halfRange;
if ( !($range%1) ) {
$maxTimeT--;
}
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
} elseif ( isset($minTime) ) {
$minTimeT = strtotime($minTime);
$maxTimeT = $minTimeT + $range;
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
} elseif ( isset($maxTime) ) {
$maxTimeT = strtotime($maxTime);
$minTimeT = $maxTimeT - $range;
$midTimeT = $minTimeT + $halfRange;
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
}
} elseif ( isset($minTime) && isset($maxTime) ) {
$minTimeT = strtotime($minTime);
$maxTimeT = strtotime($maxTime);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
}
if ( isset($minTime) && isset($maxTime) ) {
$tempMinTime = $tempMaxTime = $tempExpandable = false;
extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable);
$filterSql = parseTreeToSQL($tree);
if ( $filterSql ) {
$filterSql = " AND $filterSql";
$eventsSql .= $filterSql;
$eventIdsSql .= $filterSql;
}
} else {
$filterSql = parseTreeToSQL($tree);
$tempMinTime = $tempMaxTime = $tempExpandable = false;
extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable);
if ( $filterSql ) {
$filterSql = " AND $filterSql";
$rangeSql .= $filterSql;
$eventsSql .= $filterSql;
$eventIdsSql .= $filterSql;
}
if ( !isset($minTime) || !isset($maxTime) ) {
// Dynamically determine range
$row = dbFetchOne($rangeSql);
if ( $row ) {
if ( !isset($minTime) )
$minTime = $row['MinTime'];
if ( !isset($maxTime) )
$maxTime = $row['MaxTime'];
} else {
# Errors will be reported by db functions
}
}
if ( empty($minTime) )
$minTime = $tempMinTime;
if ( empty($maxTime) )
$maxTime = $tempMaxTime;
if ( empty($maxTime) )
$maxTime = 'now';
$minTimeT = strtotime($minTime);
$maxTimeT = strtotime($maxTime);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT );
}
//echo "MnT: $tempMinTime, MxT: $tempMaxTime, ExP: $tempExpandable
";
if ( $tree ) {
appendDatetimeRange($tree, $minTime, $maxTime);
$filterQuery = parseTreeToQuery($tree);
} else {
$filterQuery = false;
}
$scales = array(
array( 'name'=>'year', 'factor'=>60*60*24*365, 'align'=>1, 'zoomout'=>2, 'label'=>STRF_TL_AXIS_LABEL_YEAR ),
array( 'name'=>'month', 'factor'=>60*60*24*30, 'align'=>1, 'zoomout'=>12, 'label'=>STRF_TL_AXIS_LABEL_MONTH ),
array( 'name'=>'week', 'factor'=>60*60*24*7, 'align'=>1, 'zoomout'=>4.25, 'label'=>STRF_TL_AXIS_LABEL_WEEK, 'labelCheck'=>'%W' ),
array( 'name'=>'day', 'factor'=>60*60*24, 'align'=>1, 'zoomout'=>7, 'label'=>STRF_TL_AXIS_LABEL_DAY ),
array( 'name'=>'hour4', 'factor'=>60*60, 'align'=>4, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_4HOUR, 'labelCheck'=>'%H' ),
array( 'name'=>'hour', 'factor'=>60*60, 'align'=>1, 'zoomout'=>4, 'label'=>STRF_TL_AXIS_LABEL_HOUR, 'labelCheck'=>'%H' ),
array( 'name'=>'minute10', 'factor'=>60, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10MINUTE, 'labelCheck'=>'%M' ),
array( 'name'=>'minute', 'factor'=>60, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_MINUTE, 'labelCheck'=>'%M' ),
array( 'name'=>'second10', 'factor'=>1, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10SECOND ),
array( 'name'=>'second', 'factor'=>1, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_SECOND ),
);
$majXScale = getDateScale($scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max']);
// Adjust the range etc for scale
$minTimeT -= $minTimeT%($majXScale['factor']*$majXScale['align']);
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$maxTimeT += (($majXScale['factor']*$majXScale['align'])-$maxTimeT%($majXScale['factor']*$majXScale['align']))-1;
if ( $maxTimeT > time() )
$maxTimeT = time();
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
if ( isset($minTime) && isset($maxTime) ) {
$eventsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'";
$eventIdsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'";
}
if ( 0 ) {
$framesByEventId = array();
$eventsSql .= ' ORDER BY E.Id ASC';
$framesSql = "SELECT EventId,FrameId,Delta,Score FROM Frames WHERE EventId IN($eventIdsSql) AND Score > 0 ORDER BY Score DESC";
$frames_result = dbQuery($framesSql);
while ( $row = $frames_result->fetch(PDO::FETCH_ASSOC) ) {
if ( !isset($framesByEventId[$row['EventId']]) ) {
$framesByEventId[$row['EventId']] = array();
}
$framesByEventId[$row['EventId']][] = $row;
}
}
$chart['data'] = array(
'x' => array(
'lo' => strtotime($minTime),
'hi' => strtotime($maxTime),
),
'y' => array(
'lo' => 0,
'hi' => 0,
)
);
$chart['data']['x']['range'] = ($chart['data']['x']['hi'] - $chart['data']['x']['lo']) + 1;
$chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['width'];
$monEventSlots = array();
$monFrameSlots = array();
$events_result = dbQuery($eventsSql);
if ( !$events_result ) {
ZM\Fatal('SQL-ERR');
return;
}
$max_aspect_ratio = 0;
while( $event = $events_result->fetch(PDO::FETCH_ASSOC) ) {
if ( !isset($monitors[$event['MonitorId']]) ) {
$monitor = $monitors[$event['MonitorId']] = ZM\Monitor::find_one(array('Id'=>$event['MonitorId']));
$monEventSlots[$event['MonitorId']] = array();
$monFrameSlots[$event['MonitorId']] = array();
$aspect_ratio = round($monitor->Width() / $monitor->Height(), 2);
if ( $aspect_ratio > $max_aspect_ratio )
$max_aspect_ratio = $aspect_ratio;
}
$currEventSlots = &$monEventSlots[$event['MonitorId']];
$currFrameSlots = &$monFrameSlots[$event['MonitorId']];
$startTimeT = strtotime($event['StartDateTime']);
$startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $startIndex < 0 )
$startIndex = 0;
if ( isset($event['EndDateTime']) )
$endTimeT = strtotime($event['EndDateTime']);
else
$endTimeT = time();
$endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $endIndex >= $chart['graph']['width'] )
$endIndex = $chart['graph']['width'] - 1;
for ( $i = $startIndex; $i <= $endIndex; $i++ ) {
if ( !isset($currEventSlots[$i]) ) {
if ( $rawStartIndex == $rawEndIndex ) {
$offset = 1;
} else {
$offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0);
}
$currEventSlots[$i] = array( 'count'=>0, 'width'=>1, 'offset'=>$offset, 'event'=>$event );
} else {
$currEventSlots[$i]['count']++;
}
}
if ( $event['MaxScore'] > 0 ) {
if ( $startIndex == $endIndex ) {
# Only fills 1 slot, so just get the max Score
$framesSql = 'SELECT FrameId, Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1';
$frame = dbFetchOne($framesSql, NULL, array($event['Id']));
$i = $startIndex;
if ( !isset($currFrameSlots[$i]) ) {
$currFrameSlots[$i] = array('count'=>1, 'value'=>$event['MaxScore'], 'event'=>$event, 'frame'=>$frame);
} else {
$currFrameSlots[$i]['count']++;
if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) {
$currFrameSlots[$i]['value'] = $event['MaxScore'];
$currFrameSlots[$i]['event'] = $event;
$currFrameSlots[$i]['frame'] = $frame;
}
}
if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $event['MaxScore'];
}
} else {
# Fills multiple Slots, so need multiple scores to generate the graph over multiple slots.
$framesSql = 'SELECT FrameId,Delta,Score FROM Frames WHERE EventId = ? AND Score > 0';
$result = dbQuery($framesSql, array($event['Id']));
while ( $frame = dbFetchNext($result) ) {
#foreach ( $framesByEventId[$event['Id']] as $frame ) {
$frameTimeT = $startTimeT + $frame['Delta'];
$frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $frameIndex < 0 )
continue;
if ( $frameIndex >= $chart['graph']['width'] )
continue;
if ( !isset($currFrameSlots[$frameIndex]) ) {
$currFrameSlots[$frameIndex] = array('count'=>1, 'value'=>$frame['Score'], 'event'=>$event, 'frame'=>$frame);
} else {
$currFrameSlots[$frameIndex]['count']++;
if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) {
$currFrameSlots[$frameIndex]['value'] = $frame['Score'];
$currFrameSlots[$frameIndex]['event'] = $event;
$currFrameSlots[$frameIndex]['frame'] = $frame;
}
}
if ( $frame['Score'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $frame['Score'];
}
} // end foreach frame
}
} // end if MaxScore > 0
} // end foreach event
//ksort( $monitorIds, SORT_NUMERIC );
ksort( $monEventSlots, SORT_NUMERIC );
ksort( $monFrameSlots, SORT_NUMERIC );
// No longer needed?
if ( false ) {
// Add on missing frames
foreach( array_keys($monFrameSlots) as $monitorId ) {
unset( $currFrameSlots );
$currFrameSlots = &$monFrameSlots[$monitorId];
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currFrameSlots[$i]) ) {
if ( !isset($currFrameSlots[$i]['frame']) ) {
$framesSql = 'SELECT FrameId, Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY FrameId LIMIT 1';
$currFrameSlots[$i]['frame'] = dbFetchOne( $framesSql, NULL, array( $currFrameSlots[$i]['event']['Id'] ) );
}
}
}
}
}
$chart['data']['y']['range'] = ($chart['data']['y']['hi'] - $chart['data']['y']['lo']) + 1;
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['height'];
$majYScale = getYScale(
$chart['data']['y']['range'],
$chart['grid']['y']['major']['min'],
$chart['grid']['y']['major']['max']);
// Optimise boxes
foreach( array_keys($monEventSlots) as $monitorId ) {
unset( $currEventSlots );
$currEventSlots = &$monEventSlots[$monitorId];
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currEventSlots[$i]) ) {
if ( isset($currSlot) ) {
if ( $currSlot['event']['Id'] == $currEventSlots[$i]['event']['Id'] ) {
if ( $currSlot['width'] < $maxEventWidth ) {
// Merge slots for the same long event
$currSlot['width']++;
unset( $currEventSlots[$i] );
continue;
} else if ( $currSlot['offset'] < $currEventSlots[$i]['offset'] ) {
// Split very long events
$currEventSlots[$i]['frame'] = array( 'FrameId'=>$currEventSlots[$i]['offset'] );
}
} else if ( $currSlot['width'] < $minEventWidth ) {
// Merge multiple small events
$currSlot['width']++;
unset( $currEventSlots[$i] );
continue;
}
}
$currSlot = &$currEventSlots[$i];
} else {
unset($currSlot);
}
} # end foreach x
unset($currSlot);
} // end foreach Event Monitors
//print_r( $monEventSlots );
// Stack events
$frameSlots = array();
$frameMonitorIds = array_keys($monFrameSlots);
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
foreach ( $frameMonitorIds as $frameMonitorId ) {
$currFrameSlots = &$monFrameSlots[$frameMonitorId];
if ( isset($currFrameSlots[$i]) ) {
if ( !isset($frameSlots[$i]) ) {
$frameSlots[$i] = array();
$frameSlots[$i][] = &$currFrameSlots[$i];
} else {
$slotCount = count($frameSlots[$i]);
for ( $j = 0; $j < $slotCount; $j++ ) {
if ( $currFrameSlots[$i]['value'] > $frameSlots[$i][$j]['value'] ) {
for ( $k = $slotCount; $k > $j; $k-- ) {
$frameSlots[$i][$k] = $frameSlots[$i][$k-1];
}
$frameSlots[$i][$j] = &$currFrameSlots[$i];
break 2;
}
}
$frameSlots[$i][] = &$currFrameSlots[$i];
}
}
unset($currFrameSlots);
} # end foreach MonitorId
} # end foreach x
//ZM\Debug(print_r( $monEventSlots,true ));
//print_r( $monFrameSlots );
//print_r( $chart );
$graphHeight = $chart['graph']['height'];
if ( $mode == 'overlay' ) {
$minEventBarHeight = 10;
$maxEventBarHeight = 40;
if ( count($monitors) ) {
$chart['graph']['eventBarHeight'] = $minEventBarHeight;
while ( ($chart['graph']['eventsHeight'] = (($chart['graph']['eventBarHeight'] * count($monitors)) + (count($monitors)-1))) < $maxEventBarHeight ) {
$chart['graph']['eventBarHeight']++;
}
} else {
$chart['graph']['eventBarHeight'] = $maxEventBarHeight;
$chart['graph']['eventsHeight'] = $maxEventBarHeight;
}
$chart['graph']['activityHeight'] = ($graphHeight - $chart['graph']['eventsHeight']);
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityHeight'];
$chart['eventBars'] = array();
$top = $chart['graph']['activityHeight'];
foreach ( array_keys($monitors) as $monitorId ) {
$chart['eventBars'][$monitorId] = array( 'top' => $top );
$top += $chart['graph']['eventBarHeight']+1;
}
} else if ( $mode == 'split' ) {
$minActivityBarHeight = 30;
$minEventBarHeight = 10;
$maxEventBarHeight = 40;
if ( count($monitors) ) {
$chart['graph']['eventBarHeight'] = $minEventBarHeight;
$chart['graph']['activityBarHeight'] = $minActivityBarHeight;
while ( ((($chart['graph']['eventBarHeight']+$chart['graph']['activityBarHeight']) * count($monitors)) + ((2*count($monitors))-1)) < $graphHeight ) {
$chart['graph']['activityBarHeight']++;
if ( $chart['graph']['eventBarHeight'] < $maxEventBarHeight ) {
$chart['graph']['eventBarHeight']++;
}
}
} else {
$chart['graph']['eventBarHeight'] = $maxEventBarHeight;
$chart['graph']['activityBarHeight'] = $graphHeight - $chart['graph']['eventBarHeight'];
}
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityBarHeight'];
$chart['activityBars'] = array();
$chart['eventBars'] = array();
$top = 0;
$barCount = 1;
foreach ( array_keys($monitors) as $monitorId ) {
$chart['eventBars'][$monitorId] = array( 'top' => $top );
$chart['eventBars'][$monitorId] = array( 'top' => $top+$chart['graph']['activityBarHeight']+1 );
$top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1;
}
} else {
ZM\Warning("No mode $mode");
}
preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches);
preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $maxTime, $endMatches);
if ( $startMatches[1] != $endMatches[1] ) {
// Different years
$title = strftime( STRF_TL_AXIS_RANGE_YEAR1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_YEAR2, $chart['data']['x']['hi'] );
} else if ( $startMatches[2] != $endMatches[2] ) {
// Different months
$title = strftime( STRF_TL_AXIS_RANGE_MONTH1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_MONTH2, $chart['data']['x']['hi'] );
} else if ( $startMatches[3] != $endMatches[3] ) {
// Different dates
$title = strftime( STRF_TL_AXIS_RANGE_DAY1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_DAY2, $chart['data']['x']['hi'] );
} else {
// Different times
$title = strftime( STRF_TL_AXIS_RANGE_TIME1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_TIME2, $chart['data']['x']['hi'] );
}
function drawXGrid( $chart, $scale, $labelClass, $tickClass, $gridClass, $zoomClass=false ) {
$html = '';
ob_start();
$labelCount = 0;
$lastTick = 0;
unset( $lastLabel );
$labelCheck = isset($scale['labelCheck'])?$scale['labelCheck']:$scale['label'];
echo '