Location: PHPKode > projects > DTGraph - Digitemp/MySQL Graphing Tool > dtgraph-0.4m/www/graph.php
<?php
/*
 * This file is part of an open-source project
 * licensed under the GPL
 *
 */

    //session_start();

    require_once('conf.php');
    require_once('Driver.php');
    require_once('utils.php');

    Utils::myRegisterGlobals();
    Utils::setDefaultValues();

    //JPGraph
    require_once($conf['jp_path']."/jpgraph.php");
    require_once($conf['jp_path']."/jpgraph_line.php");
    require_once($conf['jp_path']."/jpgraph_scatter.php");
    
    //Setup correct driver
    $driver = &DTtemp_Driver::factory($conf['driver'], $conf['sql']);
    $driver->connect();  //must call to connect
    global $colors;
    $colors = $conf['graph']['colors'];

    global $stop_watch;
    $stop_watch['Start'] = microtime();
    
    //echo "Sensor is passed in as $sensor";

    $options = Utils::getGraphOptions();
    $l = $driver->listSensors($sensor, $options['units']);

    $stop_watch['Done list sensors'] = microtime();
    
    $times = Utils::getDisplayDataTimesAbs($startTime, $endTime);

    $stop_watch['Done get Display Data TImes'] = microtime();
    
    if (isset($showRepeats) && $showRepeats == 1) {
        $showRepeats = true;
    } else {
        $showRepeats = false;
    }
    

    $d = $driver->listEvents($sensor, $times['startTime'], $times['endTime'], $showRepeats, !isOptionOn($options,'showAll'), $options['units']); 

    $availableReadingsCount = $driver->countEvents($sensor, $times['startTime'], $times['endTime']);

	if ( $showAlarms ) {
		$alarms = $driver->getAllAlarms($times['startTime'], $times['endTime']); 
	}

    $stop_watch['Done list Events'] = microtime();

    if (PEAR::isError($l)) { //db error
        makeErrorGraph('Error listing sensors from db'. var_export($l, true));
        return;
    } else if (PEAR::isError($d)) { //db error
        makeErrorGraph('Error reading events from db'. var_export($d, true));
        return;
    } else if (PEAR::isError($availableReadingsCount)) {
        makeErrorGraph('Error getting event count from db'. var_export($availableReadingsCount, true));
        return;
    }
    //echo "events starting at ". $times['startTime']. " ending at ".$times['endTime'];
    //echo "we have the following number of readings: " . count($d);
    //print_r($d);
    graph($d,$l, $alarms, $times, $availableReadingsCount, $options);
    $stop_watch['Done Graph'] = microtime();
    //Utils::echo_stopwatch();

        
            /** Functions **/
            /***************/

    /**
     * @param data - the return of listEvents
     * @param list   - the return of listSensors
     * @param times  - the return of getDisplayDataTimes
     * @param options - array of various graph options/params
     */
    function graph($data, $list, $alarms, $times, $availableReadingsCount, $options = array()) {
        global $colors; //for defaults if not set
        global $conf;
        global $showAlarms;

        $xdata = array();
        $ydata = array();
		$xdataAlarmsRaised = array();
		$xdataAlarmsCleared = array();
		$ydataAlarmsRaised = array();
		$ydataAlarmsCleared = array();
        $names = array();

        $negs =  (isset($options['showNegatives']) && $options['showNegatives'] == 1);

        $varPrefix = $negs ? "this" : "requested";
        $varSuffix = 'Hour';
        $varName = $varPrefix.$varSuffix; //flexible graphing...

        //Time used to generate the explanatory text on X scale

        $startTime = $times['startTime'];
        $endTime = $times['endTime'];

        $explanationTime = $negs ? $endTime : $startTime;

        //$relativeStart = ( ($negs == true) ? $times['thisHour'] : $times['requestedHour']);
        //echo "$relativeStart == ? == ".$times['thisHour'] . " OR " . $times['requestedHour'];

        //echo "using negs =" . $negs ? "true " : " false";

        $conversionFactor = 60; // seconds in an X scale unit
        $XUnits = "Minutes since ".date("g A m/d", $explanationTime);
        if ( ($endTime - $startTime) > 3600 * 24 * 28) { //one month?
            $conversionFactor = 3600 * 24 * 30.5; 
            $XUnits = "Approximate Months starting " .date("Y", $explanationTime);
            $varSuffix = 'Year';
        } else if( ($endTime - $startTime) > 3600 * 24 * 1) { //one day :)
            $conversionFactor = 3600 * 24; //days
            $XUnits = "Calendar Days starting ".date("F", $explanationTime)." 1st";
            $varSuffix = 'Month';
        } else if( ($endTime - $startTime) > 3600 * 2) { //2 hours
            $conversionFactor = 3600;
            $XUnits = "Hours since midnight ".date("m/d", $explanationTime);
            $varSuffix = 'Morning';
        }  
        
        //This start point will be the ZERO
        $relativeStart = $times[$varPrefix.$varSuffix];
/**/
        //setup arrays
        while (list($serial,$s) = each($list)) {
            $xdata[$serial] = array();
            $ydata[$serial] = array();
            $names[$serial] = $s['name'];
        }
/**/



		$alarmInfo = array();
		//First of all, pre-process alarms 
		if ( $showAlarms) {
			while(list($serial, $theseAlarms) = each($alarms)) {
				//there are alarms for this sensor, figure out how to show them
				//this is an array
				$alarmInfo[$serial] = array();
				$alarmInfo[$serial]['raised'] = array();
				$alarmInfo[$serial]['cleared'] = array();
				foreach($alarms[$serial] as $oneAlarm) {
					$timeRaised = $oneAlarm['time_raised'];
					$timeRaisedAdjusted = sprintf("%0.3f", ($timeRaised - $relativeStart)/ $conversionFactor);
					array_push($alarmInfo[$serial]['raised'], $timeRaisedAdjusted);
					
					$timeCleared = $oneAlarm['time_cleared'];
					if (isset($timeCleared)) {
						//echo "Adding time cleared: $timeCleared";
						$timeClearedAdjusted = sprintf("%0.3f", ($timeCleared - $relativeStart)/ $conversionFactor);
						array_push($alarmInfo[$serial]['cleared'], $timeClearedAdjusted);
					}

				}
				sort($alarmInfo[$serial]['raised']); //could be out of order
				sort($alarmInfo[$serial]['cleared']); //could be out of order
			}
		}




        //setup xdata and ydata
        while(list($row, $d) = each ($data)) {
            $serial = $d['serialnumber'];
            //safety, should not be needed
            if (!is_array($xdata[$serial])) {
                $xdata[$serial] = array();
                $ydata[$serial] = array();
                $names[$serial] = $serial; //it must not be described
            }

            
            $time = $d['unixtime'];
            $x = sprintf("%0.3f", ($time - $relativeStart)/ $conversionFactor);
            
            //echo "Based on $startTime - $relativeStart div by $conversionFactor, it's $x <BR>";

            //$temp = $d['fahrenheit'];
            $temp = $d[strtolower($options['units'])];
/**/
/**/
            array_push($xdata[$serial], $x);
            array_push($ydata[$serial], $temp);
            //echo "Adding point $x - $temp";

			//check for alarm matches.
			//the items in each array will be in ascending order,
			//so I can check for them one at a time 
			//(because I am going in ascending order too)
			if ($showAlarms && isset($alarmInfo) && is_array($alarmInfo[$serial])) {
				$alarmArray = &$alarmInfo[$serial]['raised'];

				if (count($alarmArray) > 0 && current($alarmArray) <= $x) {
					//echo "$serial: Comparison was true for ".current($alarmArray). " < $x .  Count=".count($alarmArray)."<br>";
					//print_r($alarmArray);
					//then we're on the right element (or just past it)
					array_push($xdataAlarmsRaised, $x);
					array_push($ydataAlarmsRaised, $temp);
					//get rid of that entry
					array_shift($alarmArray);
					//echo "Count is now ".count($alarmArray).'<br>';
				}
				$alarmArray = &$alarmInfo[$serial]['cleared'];
				if (count($alarmArray) > 0 && current($alarmArray) <= $x) {
					//then we're on the right element (or just past it)
					array_push($xdataAlarmsCleared, $x);
					array_push($ydataAlarmsCleared, $temp);
					//get rid of that entry
					array_shift($alarmArray);
				}
			}
        }

        //$graph = setupGraph($mintime,$maxtime,$mintemp, $maxtemp);
        $count = count($data);
        $graph = setupGraph($XUnits, $startTime, $count, $availableReadingsCount, $options);


        reset($list); //already walked this one
        //make plots based on arrays
        
        $graphHasData = false; //if it doesn't have any data, it will blow up

        
        $plots = array();
        while (list($serial,$s) = each($list)) {
            $color = $list[$serial]['color'];
            if (!isset($color) || $color=='') {
                //Take the default just in case
                $color = array_pop($colors);
            }
            $plots[$serial] = makePlot($xdata[$serial], $ydata[$serial], $names[$serial], $color, $options);
            if (count($xdata[$serial]) > 0 ) {
                $graphHasData = true;
            }

        
            //tossing each into its own var as it seems to be 
            //pass by ref and overwrites each time?
            if (isset($plots[$serial])) {
                /*
                echo "Adding a plot: " ;
                print_r($plot);
                echo "<P><HR>";
                */
                $graph->Add($plots[$serial]);
                
                if (isset($options['showBands']) && $options['showBands'] == 1) {
                    if (isset($list[$serial]['max'])) {
                        $max = $list[$serial]['max'];
     //                   $graph->Add(new PlotLine(HORIZONTAL,$max, $color, 1)); 
                    }
                    if (isset($list[$serial]['min'])) {
                        $min = $list[$serial]['min'];
    //                    $graph->Add(new PlotLine(HORIZONTAL,$min, $color, 1)); 
                    }

                    /******** ADD BAND ******** ? ***/

                    if (isset($min) && isset($max)) {
                        $uband=new PlotBand(HORIZONTAL,BAND_SOLID,$min,$max,"white", 1, DEPTH_BACK);
                        $uband->ShowFrame(true);
                        $uband->SetDensity(3); // % line density

                        $graph->AddBand($uband);
                        unset($uband);
                    }
                    /******* DONE ADD BAND *******/
                } //if showBands
                
            }
        }

        if (!$graphHasData) {
            setGraphToError($graph, 'There is no data in the selected timeframe. Consider expanding the range');
		} else { //plot the alarms on it
			if ( $showAlarms) {
				if (count($xdataAlarmsRaised) > 0) {
					//add the alarm plot
					$graph->Add(makeAlarmsPlot($xdataAlarmsRaised, $ydataAlarmsRaised, true));
				}
				if (count($xdataAlarmsCleared) > 0) {
					//add the alarm plot
					$graph->Add(makeAlarmsPlot($xdataAlarmsCleared, $ydataAlarmsCleared, false));
				}
			}
		}
        $graph->Stroke();
        

    }

	/**
	 * Generates a ScatterPlot based on alarm info
	 * @param $isRaised - true if this is the raised alarms plot, 
	 *	false otherwise.  There are two plots total
	 */
	function &makeAlarmsPlot($xdata, $ydata, $isRaised) {
		$plot = new ScatterPlot($ydata, $xdata);
		$plot->mark->SetType($isRaised ? MARK_UTRIANGLE : MARK_DTRIANGLE);
		$plot->mark->SetFillColor($isRaised ? 'yellow' : 'green');
		$plot->mark->SetWidth(5);
		return $plot;
	}

    /**
     * Generates an image that's predetermined to display an error
     * Puts the specified message on the graph
     * Draws the graph
     */
    function makeErrorGraph($error) {
        global $conf;
        echo "$error";
        $graph = new Graph($conf['graph']['width'],$conf['graph']['height'],"auto");
        $xdata = array(1,2);
        $ydata = array(1,2);
        $plot = new LinePlot($xdata, $ydata);
        $graph->Add($plot);
        setGraphToError($graph, $error);
        $graph->Stroke();
    }

    /**
     * Changes the scale and adds the given message
     * to the graph.
     * it is assumed that no data is on the graph
     * @param graph - the graph to add things to
     * @param error - text message to show (in red)
     */
    function setGraphToError(&$graph, $error) {
        $txt =new Text($error);
        $txt->Pos( 0.2,0.5);
        $txt->SetColor( "red");
        $graph->AddText( $txt);

        //override scale so it doesn't complain
        $graph->SetScale("linlin",0, 1, 0, 1);

    }


    function setupGraph($XUnits = "hours", $startTime, $count, $availableReadingsCount, $options = array()) {
        global $conf;
        $graph = new Graph($conf['graph']['width'],$conf['graph']['height'],"auto");
        $graph->SetScale("intlin");
        //$graph->SetScale("linlin",$mintemp,$maxtemp,$mintime, $maxtime);

        //$graph->SetShadow();
        $rightMargin = 20;
        if (isset($options['showMargin']) && $options['showMargin'] == 1) {
            $rightMargin = 110;
        }
        $graph->img->SetMargin(50,$rightMargin,20,40);
        $graph->SetBox(true,'black',2);
        $graph->SetColor($conf['graph']['bgcolor']);
        $graph->SetMarginColor($conf['html']['bgcolor']);


        //$graph->title->Set("Digitemp Activity");
        $graph->title->Set("Digitemp Activity starting ".date("H:i:s m/d/Y", $startTime));
        /**
        $txt =new Text("Starting ".date("H:i:s m/d/Y", $startTime));
        $txt->SetPos( 0.59,0.01);
        $txt->SetColor( "blue");
        $graph->AddText( $txt);
        **/
    //junk:
        $graph->title->SetFont(FF_FONT1,FS_BOLD);

        $graph->xgrid->Show();

        $graph->legend->Pos(0.02,0.02,"right","top");

/*
        $graph->yaxis->SetPos(0);
        $graph->yaxis->SetWeight(2);
        $graph->yaxis->SetFont(FF_FONT1,FS_BOLD);
        $graph->yaxis->SetColor('black','darkblue');
*/
        $graph->xaxis->SetWeight(2);
        $graph->xaxis->SetFont(FF_FONT1,FS_BOLD);
        $graph->xaxis->SetColor('black','darkblue');
        $graph->xaxis->SetPos('min');

        
       // echo "Setting limits to $mintime, $maxtime, $mintemp, $maxtemp";
        //$graph->SetScale("linlin",$mintemp,$maxtemp,$mintime, $maxtime);
        $graph->xaxis->title->Set("Time ($XUnits)");
        $graph->yaxis->title->Set('Temperature ('. ucfirst($options['units']) . ')');

        

        $txt2 =new Text("Measurements shown: $count/$availableReadingsCount");
        $txt2->Pos( 0.02,0.96);
        $txt2->SetColor( "blue");
        $graph->AddText( $txt2);

        return $graph;

    }

    function makePlot($xd, $yd, $plotName, $color, $options = array()) {
//        global $colors;
//        $color = array_pop($colors);
        //echo "xdata is ".sizeof($xd);
        if (sizeof($xd) == 0) {
            return;
        }
        $plot=new LinePlot($yd, $xd);
        //The only way to not have the legend box show
        //is to not set the info at all
        if (isOptionOn($options, 'showLegend')) {
            $plot->SetLegend($plotName);
        }
        $plot->SetColor($color);
        
        if (isOptionOn($options, 'showMarks')) {
            $plot->mark->SetType(MARK_DIAMOND);
            $plot->mark->Show();
            $plot->mark->SetColor($color);
        }
        //echo "<BR> Making plot for $plotName";
        //print_r ($plot);
        return $plot;
    }

    /**
     * Convenience method to check if the option has been 
     * passed in and the value is 1
     */
    function isOptionOn($options = array(), $optionName) {

        if (isset($options[$optionName]) && $options[$optionName] == 1) {
            return true;
        }
        return false;
    }

    $driver->close();

?>
Return current item: DTGraph - Digitemp/MySQL Graphing Tool