diff -urN zabbix-2.4.5/frontends/php/chart3.php zabbix-2.4.5_mod/frontends/php/chart3.php --- zabbix-2.4.5/frontends/php/chart3.php 2015-04-22 10:56:03.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/chart3.php 2015-07-31 14:44:04.921824393 +0300 @@ -45,7 +45,7 @@ 'legend' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), 'showworkperiod' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), 'showtriggers' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), - 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), + 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,10,11'), null), 'yaxismin' => array(T_ZBX_DBL, O_OPT, null, null, null), 'yaxismax' => array(T_ZBX_DBL, O_OPT, null, null, null), 'percent_left' => array(T_ZBX_DBL, O_OPT, null, BETWEEN(0, 100), null), diff -urN zabbix-2.4.5/frontends/php/graphs.php zabbix-2.4.5_mod/frontends/php/graphs.php --- zabbix-2.4.5/frontends/php/graphs.php 2015-04-22 10:56:19.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/graphs.php 2015-07-31 14:44:53.858292386 +0300 @@ -41,7 +41,7 @@ 'name' => array(T_ZBX_STR, O_OPT, null, NOT_EMPTY, 'isset({add}) || isset({update})', _('Name')), 'width' => array(T_ZBX_INT, O_OPT, null, BETWEEN(20, 65535), 'isset({add}) || isset({update})', _('Width')), 'height' => array(T_ZBX_INT, O_OPT, null, BETWEEN(20, 65535), 'isset({add}) || isset({update})', _('Height')), - 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2,3'), 'isset({add}) || isset({update})'), + 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2,3,10,11'), 'isset({add}) || isset({update})'), 'show_3d' => array(T_ZBX_INT, O_OPT, P_NZERO, IN('0,1'), null), 'show_legend' => array(T_ZBX_INT, O_OPT, P_NZERO, IN('0,1'), null), 'ymin_type' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2'), null), diff -urN zabbix-2.4.5/frontends/php/include/classes/api/services/CGraphGeneral.php zabbix-2.4.5_mod/frontends/php/include/classes/api/services/CGraphGeneral.php --- zabbix-2.4.5/frontends/php/include/classes/api/services/CGraphGeneral.php 2015-04-22 10:56:03.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/classes/api/services/CGraphGeneral.php 2015-07-31 14:36:39.145666647 +0300 @@ -349,7 +349,8 @@ } // Y axis MIN value < Y axis MAX value - if (($graph['graphtype'] == GRAPH_TYPE_NORMAL || $graph['graphtype'] == GRAPH_TYPE_STACKED) + if (($graph['graphtype'] == GRAPH_TYPE_NORMAL || $graph['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $graph['graphtype'] == GRAPH_TYPE_STACKED || $graph['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) && $graph['ymin_type'] == GRAPH_YAXIS_TYPE_FIXED && $graph['ymax_type'] == GRAPH_YAXIS_TYPE_FIXED && $graph['yaxismin'] >= $graph['yaxismax']) { diff -urN zabbix-2.4.5/frontends/php/include/classes/graphdraw/CLineGraphDraw.php zabbix-2.4.5_mod/frontends/php/include/classes/graphdraw/CLineGraphDraw.php --- zabbix-2.4.5/frontends/php/include/classes/graphdraw/CLineGraphDraw.php 2015-04-22 10:56:02.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/classes/graphdraw/CLineGraphDraw.php 2015-08-03 16:13:23.176634622 +0300 @@ -18,6 +18,7 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +define(LOGARITHMIC_MIN, 0.0001); class CLineGraphDraw extends CGraphDraw { @@ -48,15 +49,25 @@ $this->m_showWorkPeriod = 1; $this->m_showTriggers = 1; $this->zero = array(); - $this->graphOrientation = array( - GRAPH_YAXIS_SIDE_LEFT => '', - GRAPH_YAXIS_SIDE_RIGHT => '' + $this->haveNegativeValues = array( + GRAPH_YAXIS_SIDE_LEFT => false, + GRAPH_YAXIS_SIDE_RIGHT => false ); $this->grid = array(); // vertical & horizontal grids params $this->gridLinesCount = array(); // How many grids to draw $this->gridStep = array(); // grid step $this->gridPixels = 25; // optimal grid size $this->gridPixelsVert = 40; + + $this->isLogarithmic = false; + if ($this->type == GRAPH_TYPE_NORMAL_LOGARITHMIC) { + $this->type = GRAPH_TYPE_NORMAL; + $this->isLogarithmic = true; + } + if ($this->type == GRAPH_TYPE_STACKED_LOGARITHMIC) { + $this->type = GRAPH_TYPE_STACKED; + $this->isLogarithmic = true; + } } /********************************************************************************************************/ @@ -138,16 +149,6 @@ $this->num++; } - public function setGraphOrientation($value, $axisside) { - if ($value < 0) { - $this->graphOrientation[$axisside] = '-'; - } - elseif (zbx_empty($this->graphOrientation[$axisside]) && $value > 0) { - $this->graphOrientation[$axisside] = '+'; - } - return $this->graphOrientation[$axisside]; - } - public function setYMinAxisType($yaxistype) { $this->ymin_type = $yaxistype; } @@ -322,9 +323,10 @@ $curr_data['shift_max'][$idx] = 0; $curr_data['shift_avg'][$idx] = 0; } - $loc_min = is_array($curr_data['min']) ? min($curr_data['min']) : null; - $this->setGraphOrientation($loc_min, $this->items[$i]['axisside']); + if ($loc_min < 0) { + $this->haveNegativeValues[$this->items[$i]['axisside']] = true; + } unset($row); } $curr_data['avg_orig'] = is_array($curr_data['avg']) ? zbx_avg($curr_data['avg']) : null; @@ -395,46 +397,48 @@ } } } + } + + /********************************************************************************************************/ + // CALCULATIONS + /********************************************************************************************************/ - // calculate shift for stacked graphs + protected function calcStackedShifts() { if ($this->type == GRAPH_TYPE_STACKED) { - for ($i = 1; $i < $this->num; $i++) { + $prev_datas = array(); + for ($i = 0; $i < $this->num; $i++) { + //find cur_data and prev_data + if (isset($curr_data)) { + $prev_datas[$curr_side] = &$curr_data; + } $curr_data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; + $curr_side = $this->items[$i]['axisside']; + $prev_data = &$prev_datas[$curr_side]; - if (!isset($curr_data)) { + if (!isset($curr_data) || !isset($prev_data)) { continue; } - for ($j = $i - 1; $j >= 0; $j--) { - if ($this->items[$j]['axisside'] != $this->items[$i]['axisside']) { - continue; - } - - $prev_data = &$this->data[$this->items[$j]['itemid']][$this->items[$j]['calc_type']]; - - if (!isset($prev_data)) { - continue; - } - - for ($ci = 0; $ci < $this->sizeX; $ci++) { - foreach (array('min', 'max', 'avg') as $var_name) { - $shift_var_name = 'shift_'.$var_name; - $curr_shift = &$curr_data[$shift_var_name]; - $curr_var = &$curr_data[$var_name]; - $prev_shift = &$prev_data[$shift_var_name]; - $prev_var = &$prev_data[$var_name]; - $curr_shift[$ci] = $prev_var[$ci] + $prev_shift[$ci]; - } - } - break; + for ($ci = 0; $ci < $this->sizeX; $ci++) { + // min + $curr_shift = &$curr_data['shift_min']; + $prev_shift = &$prev_data['shift_min']; + $curr_shift[$ci] = $prev_data['min'][$ci] + $prev_shift[$ci]; + + // max + $curr_shift = &$curr_data['shift_max']; + $prev_shift = &$prev_data['shift_max']; + $curr_shift[$ci] = $prev_data['max'][$ci] + $prev_shift[$ci]; + + // avg + $curr_shift = &$curr_data['shift_avg']; + $prev_shift = &$prev_data['shift_avg']; + $curr_shift[$ci] = $prev_data['avg'][$ci] + $prev_shift[$ci]; } } } } - /********************************************************************************************************/ - // CALCULATIONS - /********************************************************************************************************/ protected function calcTriggers() { $this->triggers = array(); if ($this->m_showTriggers != 1) { @@ -1862,7 +1866,7 @@ $fncRealName = _('avg'); } - $data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; + $curr_data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; // draw color square if (function_exists('imagecolorexactalpha') && function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) { @@ -1882,7 +1886,7 @@ : $this->items[$i]['hostname'].NAME_DELIMITER.$this->items[$i]['name_expanded']; // draw legend of an item with data - if (isset($data) && isset($data['min'])) { + if (isset($curr_data) && isset($curr_data['min'])) { if ($this->items[$i]['axisside'] == GRAPH_YAXIS_SIDE_LEFT) { $units['left'] = $this->items[$i]['units']; } @@ -1903,7 +1907,7 @@ )); $legend->addCell($rowNum, array( 'text' => convert_units(array( - 'value' => min($data['min']), + 'value' => min($curr_data['min']), 'units' => $this->items[$i]['units'], 'convert' => ITEM_CONVERT_NO_UNITS )), @@ -1911,7 +1915,7 @@ )); $legend->addCell($rowNum, array( 'text' => convert_units(array( - 'value' => $data['avg_orig'], + 'value' => $curr_data['avg_orig'], 'units' => $this->items[$i]['units'], 'convert' => ITEM_CONVERT_NO_UNITS )), @@ -1919,7 +1923,7 @@ )); $legend->addCell($rowNum, array( 'text' => convert_units(array( - 'value' => max($data['max']), + 'value' => max($curr_data['max']), 'units' => $this->items[$i]['units'], 'convert' => ITEM_CONVERT_NO_UNITS )), @@ -2297,94 +2301,576 @@ } } - public function draw() { - $start_time = microtime(true); + protected function calcLogZero() { + $sides = array(GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT); - set_image_header(); + foreach ($sides as $num => $side) { + $this->unit2px[$side] = (log10($this->m_maxY[$side]) - log10($this->m_minY[$side])) / $this->sizeY; + if ($this->unit2px[$side] == 0) { + $this->unit2px[$side] = 1; + } - $this->selectData(); + $this->zero[$side] = $this->sizeY + $this->shiftY; + $this->oxy[$side] = log10($this->m_minY[$side]); + } + } + + protected function calcLogMinMaxInterval() { + $sides = array(GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT); + foreach ($sides as $side) { + //floor(log10($this->minY[$side])); + $bottom = LOGARITHMIC_MIN * 10; + while ($this->m_minY[$side] > $bottom) { + $bottom *= 10; + } + $bottom /= 10; + $this->m_minY[$side] = $bottom; - if (isset($this->axis_valuetype[GRAPH_YAXIS_SIDE_RIGHT])) { - $sides[] = GRAPH_YAXIS_SIDE_RIGHT; + //ceil(log10($this->maxY[$side])); + $top = $bottom; + while ($this->m_maxY[$side] > $top) { + $top *= 10; + } + + if ($top / $bottom + 0.0001 < 100) { + $top = $bottom * 100; + } + $this->m_maxY[$side] = $top; + + $this->gridLinesCount[$side] = round(log10($top) - log10($bottom)); + $this->gridStepX[$side] = floor($this->sizeY / $this->gridLinesCount[$side]); } + } + + private function drawLogVerticalGrid() { + $mainSide = $this->yaxisleft ? GRAPH_YAXIS_SIDE_LEFT : GRAPH_YAXIS_SIDE_RIGHT; - if (isset($this->axis_valuetype[GRAPH_YAXIS_SIDE_LEFT]) || !isset($sides)) { - $sides[] = GRAPH_YAXIS_SIDE_LEFT; + for ($i = 1; $i < $this->gridLinesCount[$mainSide]; $i++) { + $y = $this->shiftY + $i * $this->gridStepX[$mainSide]; + + dashedLine( + $this->im, + $this->shiftXleft, + $y, + $this->sizeX + $this->shiftXleft, + $y, + $this->getColor($this->graphtheme['maingridcolor'], 0) + ); } + } - foreach ($sides as $graphSide) { - $this->m_minY[$graphSide] = $this->calculateMinY($graphSide); - $this->m_maxY[$graphSide] = $this->calculateMaxY($graphSide); + private function drawLogLeftSide() { + if ($this->yaxisleft == 0 || $this->skipLeftScale == 1) { + return; + } - if ($this->m_minY[$graphSide] === null) { - $this->m_minY[$graphSide] = 0; + $minY = $this->m_minY[GRAPH_YAXIS_SIDE_LEFT]; + $maxY = $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]; + + $units = null; + $unitsLong = null; + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_LEFT) { + if (is_null($units)) { + $units = $this->items[$item]['units']; + } + elseif ($this->items[$item]['units'] != $units) { + $units = false; + } } - if ($this->m_maxY[$graphSide] === null) { - $this->m_maxY[$graphSide] = 1; + } + + if (is_null($units) || $units === false) { + $units = ''; + } + else { + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_LEFT && !empty($this->items[$item]['unitsLong'])) { + $unitsLong = $this->items[$item]['unitsLong']; + break; + } } + } + + if (!empty($unitsLong)) { + $dims = imageTextSize(9, 90, $unitsLong); + + $tmpY = $this->sizeY / 2 + $this->shiftY+$dims['height'] / 2; + if ($tmpY < $dims['height']) { + $tmpY = $dims['height'] + 6; + } + + imageText( + $this->im, + 9, + 90, + $dims['width'] + 8, + $tmpY, + $this->getColor($this->graphtheme['textcolor'], 0), + $unitsLong + ); + } + + $hstr_count = $this->gridLinesCount[GRAPH_YAXIS_SIDE_LEFT]; + $val = $this->m_minY[GRAPH_YAXIS_SIDE_LEFT]; + for ($i = 0; $i <= $hstr_count; $i++) { + // using bc library, incase of large numbers + $str = convert_units(array('value' => $val, 'units' => $units[$side], 'convert' => ITEM_CONVERT_NO_UNITS)); + $dims = imageTextSize(8, 0, $str); + $posX = $this->shiftXleft - $dims['width'] - 9; + $posY = $this->getYStepMarkerPosY(GRAPH_YAXIS_SIDE_LEFT, $i); + + // only draw the marker if it doesn't overlay the previous one + imageText( + $this->im, + 8, + 0, + $posX, + $posY, + $this->getColor($this->graphtheme['textcolor'], 0), + $str + ); + + $val = bcmul($val, 10); + } + } - if ($this->m_minY[$graphSide] == $this->m_maxY[$graphSide]) { - if ($this->graphOrientation[$graphSide] == '-') { - $this->m_maxY[$graphSide] = 0; + private function drawLogRightSide() { + if ($this->yaxisright == 0 || $this->skipRightScale == 1) { + return; + } + + $minY = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]; + $maxY = $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]; + + $units = null; + $unitsLong = null; + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_RIGHT) { + if (is_null($units)) { + $units = $this->items[$item]['units']; } - elseif ($this->m_minY[$graphSide] == 0) { - $this->m_maxY[$graphSide] = 1; + elseif ($this->items[$item]['units'] != $units) { + $units = false; } - else { - $this->m_minY[$graphSide] = 0; + } + } + + if (is_null($units) || $units === false) { + $units = ''; + } + else { + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_RIGHT && !empty($this->items[$item]['unitsLong'])) { + $unitsLong = $this->items[$item]['unitsLong']; + break; } } - elseif ($this->m_minY[$graphSide] > $this->m_maxY[$graphSide]) { - if ($this->graphOrientation[$graphSide] == '-') { - $this->m_minY[$graphSide] = bcmul($this->m_maxY[$graphSide], 0.2); + } + + if (!empty($unitsLong)) { + $dims = imageTextSize(9, 90, $unitsLong); + + $tmpY = $this->sizeY / 2 + $this->shiftY + $dims['height'] / 2; + if ($tmpY < $dims['height']) { + $tmpY = $dims['height'] + 6; + } + + imageText( + $this->im, + 9, + 90, + $this->fullSizeX - $dims['width'], + $tmpY, + $this->getColor($this->graphtheme['textcolor'], 0), + $unitsLong + ); + } + + $hstr_count = $this->gridLinesCount[GRAPH_YAXIS_SIDE_RIGHT]; + $val = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]; + for ($i = 0; $i <= $hstr_count; $i++) { + // using bc library, incase of large numbers + $str = convert_units(array('value' => $val, 'units' => $units[$side], 'convert' => ITEM_CONVERT_NO_UNITS)); + $dims = imageTextSize(8, 0, $str); + $posX = $this->sizeX + $this->shiftXleft + 12; + $posY = $this->getYStepMarkerPosY(GRAPH_YAXIS_SIDE_RIGHT, $i); + + // only draw the marker if it doesn't overlay the previous one + imageText( + $this->im, + 8, + 0, + $posX, + $posY, + $this->getColor($this->graphtheme['textcolor'], 0), + $str + ); + + $val = bcmul($val, 10); + } + } + + /** monkey patched from 2.0 to support logarithmic graps */ + protected function getYStepMarkerPosY($yAxis, $stepNumber) { + $posY = $this->sizeY - $this->gridStepX[$yAxis] * $stepNumber + $this->shiftY + 4; + $posY += $this->getYStepMarkerPosOffset($yAxis, $stepNumber); + + return $posY; + } + + /** monkey patched from 2.0 to support logarithmic graps */ + protected function getYStepMarkerPosOffset($yAxis, $stepNumber) { + $offset = bcdiv($this->gridStepX[$yAxis], $this->gridStep[$yAxis]); + $offset = bcmul($offset, $this->getYStepMarkerValueOffset($yAxis, $stepNumber)); + + return $offset; + } + + /** monkey patched from 2.0 to support logarithmic graps */ + protected function getYStepMarkerValueOffset($yAxis, $stepNumber) { + $step = $this->gridStep[$yAxis]; + $minY = abs($this->m_minY[$yAxis]); + + $offset = 0; + if ($stepNumber > 0 && $minY) { + $offset = ($minY > $step) ? bcmod($minY, $step) : $minY; + } + return $offset; + } + + protected function drawLogPercentile() { + if ($this->type != GRAPH_TYPE_NORMAL) { + return ; + } + + foreach ($this->percentile as $side => $percentile) { + if ($percentile['percent'] > 0 && $percentile['value']) { + if ($side == 'left') { + $axisside = GRAPH_YAXIS_SIDE_LEFT; + $color = $this->graphtheme['leftpercentilecolor']; } else { - $this->m_minY[$graphSide] = 0; + $axisside = GRAPH_YAXIS_SIDE_RIGHT; + $color = $this->graphtheme['rightpercentilecolor']; } + + $y = $this->zero[$axisside] - (log10($percentile['value']) - $this->oxy[$axisside]) / $this->unit2px[$axisside]; + imageline( + $this->im, + $this->shiftXleft, + $y, + $this->sizeX + $this->shiftXleft, + $y, + $this->getColor($color) + ); } + } + } + + protected function limitToBoundsLog(&$value, $min, $max) { + if ($value > $max) + $value = $max; + if ($value < $min) + $value = $min; + } + + protected function drawLogElement(&$data, $from, $to, $minX, $maxX, $minY, $maxY, $drawtype, $max_color, $avg_color, $min_color, $minmax_color, $calc_fnc, $axisside) { + if (!isset($data['max'][$from]) || !isset($data['max'][$to])) { + return; + } + + $oxy = $this->oxy[$axisside]; + $zero = $this->zero[$axisside]; + $unit2px = $this->unit2px[$axisside]; + + $shift_min_from = $shift_min_to = 0; + $shift_max_from = $shift_max_to = 0; + $shift_avg_from = $shift_avg_to = 0; + + if (isset($data['shift_min'][$from])) { + $shift_min_from = log10($data['shift_min'][$from]); + } + if (isset($data['shift_min'][$to])) { + $shift_min_to = log10($data['shift_min'][$to]); + } + + if (isset($data['shift_max'][$from])) { + $shift_max_from = log10($data['shift_max'][$from]); + } + if (isset($data['shift_max'][$to])) { + $shift_max_to = log10($data['shift_max'][$to]); + } + + if (isset($data['shift_avg'][$from])) { + $shift_avg_from = log10($data['shift_avg'][$from]); + } + if (isset($data['shift_avg'][$to])) { + $shift_avg_to = log10($data['shift_avg'][$to]); + } + + $min_from = log10($data['min'][$from] + $data['shift_min'][$from]); + $min_to = log10($data['min'][$to] + $data['shift_min'][$to]); + + $max_from = log10($data['max'][$from] + $data['shift_max'][$from]); + $max_to = log10($data['max'][$to] + $data['shift_max'][$to]); + + $avg_from = log10($data['avg'][$from] + $data['shift_avg'][$from]); + $avg_to = log10($data['avg'][$to] + $data['shift_avg'][$to]); + + $x1 = $from + $this->shiftXleft - 1; + $x2 = $to + $this->shiftXleft; + + $y1min = $zero - ($min_from - $oxy) / $unit2px; + $y2min = $zero - ($min_to - $oxy) / $unit2px; + + $y1max = $zero - ($max_from - $oxy) / $unit2px; + $y2max = $zero - ($max_to - $oxy) / $unit2px; + + $y1avg = $zero - ($avg_from - $oxy) / $unit2px; + $y2avg = $zero - ($avg_to - $oxy) / $unit2px; + + switch ($calc_fnc) { + case CALC_FNC_MAX: + $y1 = $y1max; + $y2 = $y2max; + $shift_from = $shift_max_from; + $shift_to = $shift_max_to; + break; + case CALC_FNC_MIN: + $y1 = $y1min; + $y2 = $y2min; + $shift_from = $shift_min_from; + $shift_to = $shift_min_to; + break; + case CALC_FNC_ALL: + // max + $y1x = (($y1max > ($this->sizeY + $this->shiftY)) || $y1max < $this->shiftY); + $y2x = (($y2max > ($this->sizeY + $this->shiftY)) || $y2max < $this->shiftY); - // If max Y-scale bigger min Y-scale only for 10% or less, then we don't allow Y-scale duplicate - if ($this->m_maxY[$graphSide] && $this->m_minY[$graphSide]) { - if ($this->m_minY[$graphSide] < 0) { - $absMinY = bcmul($this->m_minY[$graphSide], '-1'); + if ($y1x) { + $y1max = ($y1max > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; } - else { - $absMinY = $this->m_minY[$graphSide]; + if ($y2x) { + $y2max = ($y2max > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; } - if ($this->m_maxY[$graphSide] < 0) { - $absMaxY = bcmul($this->m_maxY[$graphSide], '-1'); + + // min + $y1n = (($y1min > ($this->sizeY + $this->shiftY)) || $y1min < $this->shiftY); + $y2n = (($y2min > ($this->sizeY + $this->shiftY)) || $y2min < $this->shiftY); + + if ($y1n) { + $y1min = ($y1min > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; } - else { - $absMaxY = $this->m_maxY[$graphSide]; + if ($y2n) { + $y2min = ($y2min > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; } - if ($absMaxY < $absMinY) { - $oldAbMaxY = $absMaxY; - $absMaxY = $absMinY; - $absMinY = $oldAbMaxY; - } + $a[0] = $x1; + $a[1] = $y1max; + $a[2] = $x1; + $a[3] = $y1min; + $a[4] = $x2; + $a[5] = $y2min; + $a[6] = $x2; + $a[7] = $y2max; + + // don't use break, avg must be drawed in this statement + case CALC_FNC_AVG: - if (bcdiv((bcsub($absMaxY, $absMinY)), $absMaxY) <= 0.1) { - if ($this->m_minY[$graphSide] > 0) { - $this->m_minY[$graphSide] = bcmul($this->m_minY[$graphSide], 0.95); + // don't use break, avg must be drawed in this statement + default: + $y1 = $y1avg; + $y2 = $y2avg; + $shift_from = $shift_avg_from; + $shift_to = $shift_avg_to; + } + + $y1_shift = $zero - ($shift_from - $oxy) / $unit2px; + $y2_shift = $zero - ($shift_to - $oxy) / $unit2px; + + // так как координаты графика вверх ногами, минимум и максимум перекручены + $graphMax = $zero - (log10($this->m_minY[$axisside]) - $oxy) / $unit2px; + $graphMin = $zero - (log10($this->m_maxY[$axisside]) - $oxy) / $unit2px; + + $this->limitToBoundsLog($y1, $graphMin, $graphMax); + $this->limitToBoundsLog($y2, $graphMin, $graphMax); + $this->limitToBoundsLog($y1_shift, $graphMin, $graphMax); + $this->limitToBoundsLog($y2_shift, $graphMin, $graphMax); + + // draw main line + switch ($drawtype) { + case GRAPH_ITEM_DRAWTYPE_BOLD_LINE: + if ($calc_fnc == CALC_FNC_ALL) { + imagefilledpolygon($this->im, $a, 4, $minmax_color); + if (!$y1x || !$y2x) { + zbx_imagealine($this->im, $x1, $y1, $x2, $y2, $avg_color, LINE_TYPE_BOLD); } - else { - $this->m_minY[$graphSide] = bcmul($this->m_minY[$graphSide], 1.05); + + if (!$y1n || !$y2n) { + zbx_imagealine($this->im, $x1, $y1, $x2, $y2, $avg_color, LINE_TYPE_BOLD); } - if ($this->m_maxY[$graphSide] > 0) { - $this->m_maxY[$graphSide] = bcmul($this->m_maxY[$graphSide], 1.05); + } + + zbx_imagealine($this->im, $x1, $y1, $x2, $y2, $avg_color, LINE_TYPE_BOLD); + break; + case GRAPH_ITEM_DRAWTYPE_LINE: + if ($calc_fnc == CALC_FNC_ALL) { + imagefilledpolygon($this->im, $a, 4, $minmax_color); + if (!$y1x || !$y2x) { + zbx_imagealine($this->im, $x1, $y1max, $x2, $y2max, $max_color); } - else { - $this->m_maxY[$graphSide] = bcmul($this->m_maxY[$graphSide], 0.95); + if (!$y1n || !$y2n) { + zbx_imagealine($this->im, $x1, $y1min, $x2, $y2min, $min_color); } } - } + + zbx_imagealine($this->im, $x1, $y1, $x2, $y2, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_FILLED_REGION: + $a[0] = $x1; + $a[1] = $y1; + $a[2] = $x1; + $a[3] = $y1_shift; + $a[4] = $x2; + $a[5] = $y2_shift; + $a[6] = $x2; + $a[7] = $y2; + + imagefilledpolygon($this->im, $a, 4, $avg_color); + + break; + case GRAPH_ITEM_DRAWTYPE_DOT: + imagefilledrectangle($this->im, $x1 - 1, $y1 - 1, $x1, $y1, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_BOLD_DOT: + imagefilledrectangle($this->im, $x2 - 1, $y2 - 1, $x2 + 1, $y2 + 1, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_DASHED_LINE: + if (function_exists('imagesetstyle')) { + // use imagesetstyle+imageline instead of bugged imagedashedline + $style = array($avg_color, $avg_color, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT); + imagesetstyle($this->im, $style); + zbx_imageline($this->im, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); + } + else { + imagedashedline($this->im, $x1, $y1, $x2, $y2, $avg_color); + } + break; + case GRAPH_ITEM_DRAWTYPE_GRADIENT_LINE: + imageLine($this->im, $x1, $y1, $x2, $y2, $avg_color); // draw the initial line + imageLine($this->im, $x1, $y1 - 1, $x2, $y2 - 1, $avg_color); + + $bitmask = 255; + $blue = $avg_color & $bitmask; + + // $blue_diff = 255 - $blue; + $bitmask = $bitmask << 8; + $green = ($avg_color & $bitmask) >> 8; + + // $green_diff = 255 - $green; + $bitmask = $bitmask << 8; + $red = ($avg_color & $bitmask) >> 16; + // $red_diff = 255 - $red; + + // note: though gradients on the chart looks ok, the formula used is completely incorrect + // if you plan to fix something here, it would be better to start from scratch + $maxAlpha = 110; + $startAlpha = 50; + $alphaRatio = $maxAlpha / ($this->sizeY - $startAlpha); + + $diffX = $x1 - $x2; + for ($i = 0; $i <= $diffX; $i++) { + $Yincr = ($diffX > 0) ? (abs($y2 - $y1) / $diffX) : 0; + + $gy = ($y1 > $y2) ? ($y2 + $Yincr * $i) : ($y2 - $Yincr * $i); + $steps = $this->sizeY + $this->shiftY - $gy + 1; + + for ($j = 0; $j < $steps; $j++) { + if (($gy + $j) < ($this->shiftY + $startAlpha)) { + $alpha = 0; + } + else { + $alpha = 127 - abs(127 - ($alphaRatio * ($gy + $j - $this->shiftY - $startAlpha))); + } + + $color = imagecolorexactalpha($this->im, $red, $green, $blue, $alpha); + imagesetpixel($this->im, $x2 + $i, $gy + $j, $color); + } + } + break; } + } - $this->calcMinMaxInterval(); + public function draw() { + $start_time = microtime(true); + + set_image_header(); + + $this->selectData(); + $this->calcStackedShifts(); + + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_LEFT); + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_RIGHT); + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_LEFT); + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_RIGHT); + if ($this->isLogarithmic) { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = min($this->m_minY[GRAPH_YAXIS_SIDE_LEFT], $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]); + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = max($this->m_maxY[GRAPH_YAXIS_SIDE_LEFT], $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]); + } + + if (!$this->isLogarithmic) { + if ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_LEFT] == '-') { + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == 0) { + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 1; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] > $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_LEFT] == '-') { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + } + + if ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_RIGHT] == '-') { + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == 0) { + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 1; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] > $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_RIGHT] == '-') { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + } + + $this->calcMinMaxInterval(); + } else { + $this->calcLogMinMaxInterval(); + } $this->updateShifts(); $this->calcTriggers(); - $this->calcZero(); + if (!$this->isLogarithmic) { + $this->calcZero(); + } else { + $this->calcLogZero(); + } $this->calcPercentile(); $this->fullSizeX = $this->sizeX + $this->shiftXleft + $this->shiftXright + 1; @@ -2415,7 +2901,11 @@ $this->drawHeader(); $this->drawWorkPeriod(); $this->drawTimeGrid(); - $this->drawHorizontalGrid(); + if (!$this->isLogarithmic) { + $this->drawHorizontalGrid(); + } else { + $this->drawLogVerticalGrid(); + } $this->drawXYAxisScale($this->graphtheme['gridbordercolor']); $maxX = $this->sizeX; @@ -2483,33 +2973,60 @@ } if ($draw) { - $this->drawElement( - $data, - $i, - $j, - 0, - $this->sizeX, - $minY, - $maxY, - $valueDrawType, - $max_color, - $avg_color, - $min_color, - $minmax_color, - $calc_fnc, - $this->items[$item]['axisside'] - ); + if (!$this->isLogarithmic) { + $this->drawElement( + $data, + $i, + $j, + 0, + $this->sizeX, + $minY, + $maxY, + $valueDrawType, + $max_color, + $avg_color, + $min_color, + $minmax_color, + $calc_fnc, + $this->items[$item]['axisside'] + ); + } else { + $this->drawLogElement( + $data, + $i, + $j, + 0, + $this->sizeX, + $minY, + $maxY, + $valueDrawType, + $max_color, + $avg_color, + $min_color, + $minmax_color, + $calc_fnc, + $this->items[$item]['axisside'] + ); + } } $j = $i; } } - $this->drawSides(); + if (!$this->isLogarithmic) { + $this->drawSides(); + } else { + $this->drawLogLeftSide(); + $this->drawLogRightSide(); + } if ($this->drawLegend) { $this->drawTriggers(); - $this->drawPercentile(); + if (!$this->isLogarithmic) + $this->drawPercentile(); + else + $this->drawLogPercentile(); $this->drawLegend(); } diff -urN zabbix-2.4.5/frontends/php/include/defines.inc.php zabbix-2.4.5_mod/frontends/php/include/defines.inc.php --- zabbix-2.4.5/frontends/php/include/defines.inc.php 2015-04-22 10:56:02.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/defines.inc.php 2015-08-03 15:36:11.541255589 +0300 @@ -694,6 +694,8 @@ define('GRAPH_TYPE_COLUMN', 7); define('GRAPH_TYPE_BAR_STACKED', 8); define('GRAPH_TYPE_COLUMN_STACKED', 9); +define('GRAPH_TYPE_NORMAL_LOGARITHMIC', 10); +define('GRAPH_TYPE_STACKED_LOGARITHMIC', 11); define('BR_DISTRIBUTION_MULTIPLE_PERIODS', 1); define('BR_DISTRIBUTION_MULTIPLE_ITEMS', 2); diff -urN zabbix-2.4.5/frontends/php/include/graphs.inc.php zabbix-2.4.5_mod/frontends/php/include/graphs.inc.php --- zabbix-2.4.5/frontends/php/include/graphs.inc.php 2015-04-22 10:56:02.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/graphs.inc.php 2015-08-03 15:37:32.814251766 +0300 @@ -24,7 +24,9 @@ GRAPH_TYPE_NORMAL => _('Normal'), GRAPH_TYPE_STACKED => _('Stacked'), GRAPH_TYPE_PIE => _('Pie'), - GRAPH_TYPE_EXPLODED => _('Exploded') + GRAPH_TYPE_EXPLODED => _('Exploded'), + GRAPH_TYPE_NORMAL_LOGARITHMIC => _('Logarithmic'), + GRAPH_TYPE_STACKED_LOGARITHMIC => _('Stacked logarithmic') ); if (is_null($type)) { diff -urN zabbix-2.4.5/frontends/php/include/views/configuration.graph.edit.php zabbix-2.4.5_mod/frontends/php/include/views/configuration.graph.edit.php --- zabbix-2.4.5/frontends/php/include/views/configuration.graph.edit.php 2015-04-22 10:56:02.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/views/configuration.graph.edit.php 2015-08-03 15:39:59.099441057 +0300 @@ -63,11 +63,12 @@ $graphFormList->addRow(_('Show legend'), new CCheckBox('show_legend', $this->data['show_legend'], null, 1)); // append graph types to form list -if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_STACKED) { +if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $this->data['graphtype'] == GRAPH_TYPE_STACKED || $this->data['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) { $graphFormList->addRow(_('Show working time'), new CCheckBox('show_work_period', $this->data['show_work_period'], null, 1)); $graphFormList->addRow(_('Show triggers'), new CCheckBox('show_triggers', $this->data['show_triggers'], null, 1)); - if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL) { + if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC) { // percent left $percentLeftTextBox = new CTextBox('percent_left', $this->data['percent_left'], 6, false, 7); $percentLeftCheckbox = new CCheckBox('visible[percent_left]', 1, 'javascript: showHideVisible("percent_left");', 1); @@ -151,6 +152,7 @@ '&numeric=1", 0, 0, "zbx_popup_item");', 'formlist' ); + $graphFormList->addRow(_('Y axis MAX value'), $yaxisMaxData); } } else { @@ -229,11 +231,16 @@ new CCol(SPACE, null, null, 15), new CCol(SPACE, null, null, 15), new CCol(_('Name'), null, null, ($this->data['graphtype'] == GRAPH_TYPE_NORMAL) ? 280 : 360), + new CCol(_('Name'), null, null, + ($this->data['graphtype'] != GRAPH_TYPE_NORMAL && $this->data['graphtype'] != GRAPH_TYPE_NORMAL_LOGARITHMIC) ? 360 : 280), ($this->data['graphtype'] == GRAPH_TYPE_PIE || $this->data['graphtype'] == GRAPH_TYPE_EXPLODED) ? new CCol(_('Type'), null, null, 80) : null, new CCol(_('Function'), null, null, 80), - ($this->data['graphtype'] == GRAPH_TYPE_NORMAL) ? new CCol(_('Draw style'), 'nowrap', null, 80) : null, - ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_STACKED) + ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC) + ? new CCol(_('Draw style'), 'nowrap', null, 80) : null, + ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $this->data['graphtype'] == GRAPH_TYPE_STACKED || $this->data['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) + ? new CCol(_('Y axis side'), 'nowrap', null, 80) : null, new CCol(_('Colour'), null, null, 100), new CCol(_('Action'), null, null, 50) diff -urN zabbix-2.4.5/frontends/php/include/views/js/configuration.graph.edit.js.php zabbix-2.4.5_mod/frontends/php/include/views/js/configuration.graph.edit.js.php --- zabbix-2.4.5/frontends/php/include/views/js/configuration.graph.edit.js.php 2015-04-22 10:56:02.000000000 +0300 +++ zabbix-2.4.5_mod/frontends/php/include/views/js/configuration.graph.edit.js.php 2015-08-03 15:42:02.233913127 +0300 @@ -42,7 +42,7 @@ - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> @@ -53,7 +53,7 @@ - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> @@ -328,7 +330,7 @@ src += '&graph3d=' + ($('#show_3d').is(':checked') ? 1 : 0); - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> src += '&percent_left=' + $('#percent_left').val() + '&percent_right=' + $('#percent_right').val();