ChartDirector 6.0 (Perl Edition)

Simple Realtime Chart




This sample program demonstrates a realtime chart with configurable chart update rate, in which the chart image is being updated via streaming.

The code consists of two parts - the containing web page and the charting page. The containing web page is in HTML, with an <IMG> tag for displaying the chart. The chart is updated by calling JsChartViewer.streamUpdate on the browser side. This method sends a "stream update request" to the server to update the chart image without refreshing the web page. The charting code in the server always draws the chart using the most up-to-date data. By using a timer to call JsChartViewer.streamUpdate periodically, we can get a real time chart.

Note that the streaming method used in this example can only update the chart image, but not the image map or the Javascript Chart Model, so the chart would not have image map or track cursor features. An alternative is to use AJAX chart update, which has higher overhead but can support image maps and track cursors. Please refer to Realtime Chart with Track Line for an example.

NOTE: This sample script uses "cdjcv.js". When developing your own script using this sample script as a template, please ensure you copy "cdjcv.js" to the proper directory and reference it using the proper path.

Source Code Listing

[CGI Version] perldemo_cgi\realtimedemo.pl
#!/usr/bin/perl

# In the sample code, the ChartDirector for Perl module is assumed to be in "../lib"
use File::Basename;
use lib (dirname($0)."/../lib") =~ /(.*)/;

use perlchartdir;

#
# In this demo, the generated web page needs to load the "cdjcv.js" Javascript file. For ease of
# installation, we put "cdjcv.js" in the same directory as this script. However, if this script is
# installed in a CGI only directory (such as cgi-bin), the web server would not allow the browser to
# access this non-CGI file.
#
# To get around this potential issue, a special load resource script is used to load these files.
# Instead of using:
#
#    <SCRIPT SRC="cdjcv.js">
#
# we now use:
#
#    <SCRIPT SRC="loadresource.pl?file=cdjcv.js">
#
# If this script is not in a CGI only directory, you may replace the following loadResource string
# with an empty string "" to improve performance.
#
my $loadResource = "loadresource.pl?file=";

print "Content-type: text/html\n\n";
print <<EndOfHTML
<!DOCTYPE html>
<html>
<head>
    <title>Simple Realtime Chart</title>
    <script type="text/javascript" src="${loadResource}cdjcv.js"></script>
</head>
<body style="margin:0px">
<table cellspacing="0" cellpadding="0" border="0">
    <tr>
        <td align="right" colspan="2" style="background:#000088; color:#ffff00; padding:0px 4px 2px 0px;">
            <a style="color:#FFFF00; font:italic bold 10pt Arial; text-decoration:none" href="http://www.advsofteng.com/">
                Advanced Software Engineering
            </a>
        </td>
    </tr>
    <tr valign="top">
        <td style="width:130px; background:#c0c0ff; border-right:black 1px solid; border-bottom:black 1px solid;">
            <br />
            <br />
            <div style="font:12px Verdana; padding:10px;">
                <b>Update Period</b><br />
                <select id="UpdatePeriod" style="width:110px">
                    <option value="5">5</option>
                    <option value="10" selected="selected">10</option>
                    <option value="20">20</option>
                    <option value="30">30</option>
                    <option value="60">60</option>
                </select>
                <br /><br /><br />
                <b>Time Remaining</b><br />
                <div style="width:108px; border:#888888 1px inset;">
                    <div style="margin:3px" id="TimeRemaining">0</div>
                </div>
            </div>
        </td>
        <td>
            <div style="font: bold 20pt Arial; margin:5px 0px 0px 5px;">
                Simple Realtime Chart
            </div>
            <hr style="border:solid 1px #000080" />
            <div style="padding:0px 5px 5px 10px">
                <!-- ****** Here is the image tag for the chart image ****** -->
                <img id="ChartImage1" src="realtimechart.pl?chartId=demoChart1">
            </div>
        </td>
    </tr>
</table>
<script type="text/javascript">

//
// Executes once every second to update the countdown display. Updates the chart when the countdown reaches 0.
//
function timerTick()
{
    // Get the update period and the time left
    var updatePeriod = parseInt(document.getElementById("UpdatePeriod").value);
    var timeLeft = Math.min(parseInt(document.getElementById("TimeRemaining").innerHTML), updatePeriod) - 1;

    if (timeLeft == 0)
        // Can update the chart now
        JsChartViewer.get('ChartImage1').streamUpdate();
    else if (timeLeft < 0)
        // Reset the update period
        timeLeft += updatePeriod;

    // Update the countdown display
    document.getElementById("TimeRemaining").innerHTML = timeLeft;
}
window.setInterval("timerTick()", 1000);

</script>
</body>
</html>
EndOfHTML
;

[CGI Version] perldemo_cgi\realtimechart.pl
#!/usr/bin/perl

# In the sample code, the ChartDirector for Perl module is assumed to be in "../lib"
use File::Basename;
use lib (dirname($0)."/../lib") =~ /(.*)/;

use perlchartdir;

#
# Data to draw the chart. In this demo, the data buffer will be filled by a random data generator.
# In real life, the data is probably stored in a buffer (eg. a database table, a text file, or some
# global memory) and updated by other means.
#

# We use a data buffer to emulate the last 240 samples.
my $sampleSize = 240;
my $dataSeries1 = [(0) x $sampleSize];
my $dataSeries2 = [(0) x $sampleSize];
my $dataSeries3 = [(0) x $sampleSize];
my $timeStamps = [(0) x $sampleSize];

# Our pseudo random number generator
my $firstDate = perlchartdir::chartTime2(time()) - scalar(@$timeStamps);
for(my $i = 0; $i < scalar(@$timeStamps); ++$i) {
    my $p = $firstDate + $i;
    $timeStamps->[$i] = $p;
    $dataSeries1->[$i] = cos($p * 2.1) * 10 + 1 / (cos($p) * cos($p) + 0.01) + 20;
    $dataSeries2->[$i] = 100 * sin($p / 27.7) * sin($p / 10.1) + 150;
    $dataSeries3->[$i] = 100 * cos($p / 6.7) * cos($p / 11.9) + 150;
}

# Create an XYChart object 600 x 270 pixels in size, with light grey (f4f4f4) background, black
# (000000) border, 1 pixel raised effect, and with a rounded frame.
my $c = new XYChart(600, 270, 0xf4f4f4, 0x000000, 0);
$c->setRoundedFrame();

# Set the plotarea at (55, 62) and of size 520 x 175 pixels. Use white (ffffff) background. Enable
# both horizontal and vertical grids by setting their colors to grey (cccccc). Set clipping mode to
# clip the data lines to the plot area.
$c->setPlotArea(55, 62, 520, 175, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
$c->setClipping();

# Add a title to the chart using 15pt Times New Roman Bold Italic font, with a light grey (dddddd)
# background, black (000000) border, and a glass like raised effect.
$c->addTitle("Field Intensity at Observation Satellite", "timesbi.ttf", 15)->setBackground(0xdddddd,
    0x000000, perlchartdir::glassEffect());

# Add a legend box at the top of the plot area with 9pt Arial Bold font. We set the legend box to
# the same width as the plot area and use grid layout (as opposed to flow or top/down layout). This
# distributes the 3 legend icons evenly on top of the plot area.
my $b = $c->addLegend2(55, 33, 3, "arialbd.ttf", 9);
$b->setBackground($perlchartdir::Transparent, $perlchartdir::Transparent);
$b->setWidth(520);

# Configure the y-axis with a 10pt Arial Bold axis title
$c->yAxis()->setTitle("Intensity (V/m)", "arialbd.ttf", 10);

# Configure the x-axis to auto-scale with at least 75 pixels between major tick and 15 pixels
# between minor ticks. This shows more minor grid lines on the chart.
$c->xAxis()->setTickDensity(75, 15);

# Set the axes width to 2 pixels
$c->xAxis()->setWidth(2);
$c->yAxis()->setWidth(2);

# Set the x-axis label format
$c->xAxis()->setLabelFormat("{value|hh:nn:ss}");

# Create a line layer to plot the lines
my $layer = $c->addLineLayer2();

# The x-coordinates are the timeStamps.
$layer->setXData($timeStamps);

# The 3 data series are used to draw 3 lines. Here we put the latest data values as part of the data
# set name, so you can see them updated in the legend box.
$layer->addDataSet($dataSeries1, 0xff0000, $c->formatValue($dataSeries1->[scalar(@$dataSeries1) - 1
    ], "Alpha: <*bgColor=FFCCCC*> {value|2} "));
$layer->addDataSet($dataSeries2, 0x00cc00, $c->formatValue($dataSeries2->[scalar(@$dataSeries2) - 1
    ], "Beta: <*bgColor=CCFFCC*> {value|2} "));
$layer->addDataSet($dataSeries3, 0x0000ff, $c->formatValue($dataSeries3->[scalar(@$dataSeries3) - 1
    ], "Gamma: <*bgColor=CCCCFF*> {value|2} "));

# Output the chart
binmode(STDOUT);
print "Content-type: image/png\n\n";
print $c->makeChart2($perlchartdir::PNG);