ChartDirector 7.0 (ASP/COM/VB Edition)

Realtime Chart Demonstration (Windows)

This sample program demonstrates a realtime chart with configurable chart update rate and with custom realtime information displayed on the chart.

In this sample program, new data values are generated by a random number generator, driven by a timer. The values are "shifted" into data arrays, which are used for creating the chart.

The chart is updated by a separate timer. This allows the chart update rate to be configurable independent of the data rate. Also, the chart can be "frozen" for easy reading, while the data can continue to update on the background.

To demonstrate the code structure for update rate control (even though for the update rate in this demo it is not necessary to have any rate control), instead of directly updating the chart, the chart update timer calls ChartViewer.updateViewPort to fire the ViewPortChanged event, and the chart is updated in the event handler.

In ChartDirector's architecture, a realtime chart can show custom information in realtime. In this demonstration, we show the latest data values as part of the legend box, and display an alert message if the latest data values exceed a configurable threshold value.

Source Code Listing

[Windows Version (in Visual Basic)] vbdemo\frmRealTimeDemo.frm
Option Explicit Private cd As New ChartDirector.API ' ' 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 store the last 240 samples. Private Const sampleSize = 240 Private dataSeries1(sampleSize - 1) Private dataSeries2(sampleSize - 1) Private dataSeries3(sampleSize - 1) Private timeStamps(sampleSize - 1) ' This is an internal variable used by the real time random number generator so it ' knows what timestamp should be used for the next data point. Private nextDateTime As Double ' The standard VB date/time functions only has resolution of 1 second. In this realtime ' chart demo, we need millisecond resolution, so we need to use the GetSystemTime API Private Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME) Private Type SYSTEMTIME wYear As Integer wMonth As Integer wDayOfWeek As Integer wDay As Integer wHour As Integer wMinute As Integer wSecond As Integer wMilliseconds As Integer End Type ' ' A utility to get the current time at millisecond resolution ' Private Function getCurrentTime() As Double ' We get the year, month, day, hour from the VB date, as it takes into account ' of time zone conversions Dim currentDate As Date currentDate = Now ' We get the minute and seconds using GetSystemTime for millisecond resolution Dim currentTime As SYSTEMTIME GetSystemTime currentTime ' Return the date/time in chartTime format, which can have millisecond resolution getCurrentTime = cd.chartTime(Year(currentDate), Month(currentDate), _ Day(currentDate), Hour(currentDate), currentTime.wMinute, _ currentTime.wSecond) + currentTime.wMilliseconds / 1000# End Function ' ' Utility to shift a value into an array ' Private Sub shiftData(data, newValue) Dim i For i = LBound(data) + 1 To UBound(data) data(i - 1) = data(i) Next data(UBound(data)) = newValue End Sub ' ' Initialize the Form ' Private Sub Form_Load() SamplePeriod.Text = 1000 nextDateTime = getCurrentTime() Dim i As Long For i = 0 To UBound(timeStamps) timeStamps(i) = cd.NoValue dataSeries1(i) = cd.NoValue dataSeries2(i) = cd.NoValue dataSeries3(i) = cd.NoValue Next End Sub ' ' User clicks on the Run pushbutton ' Private Sub RunPB_Click() ' Enable chart update timer ChartUpdateTimer.Enabled = True End Sub ' ' User clicks on the Freeze pushbutton ' Private Sub FreezePB_Click() ' Disable chart update timer ChartUpdateTimer.Enabled = False End Sub ' ' User changes the chart update period ' Private Sub SamplePeriod_Click() 'Modify timer period ChartUpdateTimer.Interval = CInt(SamplePeriod.Text) End Sub ' ' User presses the alarm threshold spin button ' Private Sub AlarmThreshold_Change() ' Update the chart immediately after threshold changes Call ChartViewer1.UpdateViewPort(True, False) End Sub ' ' The data acquisition routine. In this demo, this is invoked every 250ms. ' Private Sub DataRateTimer_Timer() Dim currentTime As Double currentTime = getCurrentTime() ' This is our formula for the data generator Dim p, dataA, dataB, dataC Do While nextDateTime < currentTime ' Get a data sample p = nextDateTime - Int(nextDateTime / 86400) * 86400 dataA = Cos(p * 8.5) * 10 + 1 / (Cos(p) * Cos(p) + 0.01) + 20 dataB = 100 * Sin(p * 4 / 27.7) * Sin(p * 4 / 10.1) + 150 dataC = 100 * Cos(p * 4 / 6.7) * Cos(p * 4 / 11.9) + 150 ' Shift the values into the arrays shiftData dataSeries1, dataA shiftData dataSeries2, dataB shiftData dataSeries3, dataC shiftData timeStamps, nextDateTime nextDateTime = nextDateTime + DataRateTimer.Interval / 1000# Loop ' We provide some visual feedback to the numbers generated, so you can see the ' data being updated. valueA.Caption = Round(dataSeries1(UBound(dataSeries1)), 2) valueB.Caption = Round(dataSeries2(UBound(dataSeries2)), 2) valueC.Caption = Round(dataSeries3(UBound(dataSeries3)), 2) End Sub ' ' Chart update timer handler ' Private Sub ChartUpdateTimer_Timer() Call ChartViewer1.UpdateViewPort(True, False) End Sub ' ' View port changed event ' Private Sub ChartViewer1_ViewPortChanged(needUpdateChart As Boolean, _ needUpdateImageMap As Boolean) Call drawChart(ChartViewer1) End Sub ' ' Draw the chart and display it in the given viewer ' Private Sub drawChart(viewer As ChartViewer) ' 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. Dim c As XYChart Set c = cd.XYChart(600, 270, &HF4F4F4, &H0, 0) Call 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. Call c.setPlotArea(55, 62, 520, 175, &HFFFFFF, -1, -1, &HCCCCCC, &HCCCCCC) Call c.setClipping ' Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a ' light grey (dddddd) background, black (000000) border, and a glass like raised ' effect. Call c.addTitle("Realtime Chart Demonstration", "timesbi.ttf", 15 _ ).setBackground(&HDDDDDD, &H0, cd.glassEffect()) ' Add a legend box at the top of the plot area with 9pts 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. Dim b As legendBox Set b = c.addLegend2(55, 33, 3, "arialbd.ttf", 9) Call b.setBackground(cd.Transparent, cd.Transparent) Call b.setWidth(520) ' Configure the y-axis with a 10pts Arial Bold axis title Call c.yAxis().setTitle("Price (USD)", "arialbd.ttf", 10) ' Set 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. Call c.xAxis().setTickDensity(75, 15) ' Set the axes width to 2 pixels Call c.xAxis().setWidth(2) Call c.yAxis().setWidth(2) ' Now we add the data to the chart. Dim lastTime As Double lastTime = timeStamps(UBound(timeStamps)) If lastTime <> cd.NoValue Then ' Set up the x-axis to show the time range in the data buffer Call c.xAxis().setDateScale(lastTime - DataRateTimer.Interval * _ (UBound(timeStamps) + 1) / 1000#, lastTime) ' Set the x-axis label format Call c.xAxis().setLabelFormat("{value|hh:nn:ss}") ' Create a line layer to plot the lines Dim layer As lineLayer Set layer = c.addLineLayer2() ' The x-coordinates are the timeStamps. Call 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. Call layer.addDataSet(dataSeries1, &HFF0000, c.formatValue(dataSeries1( _ UBound(dataSeries1)), "Software: <*bgColor=FFCCCC*> {value|2} ")) Call layer.addDataSet(dataSeries2, &HCC00, c.formatValue(dataSeries2( _ UBound(dataSeries2)), "Hardware: <*bgColor=CCFFCC*> {value|2} ")) Call layer.addDataSet(dataSeries3, &HFF, c.formatValue(dataSeries3( _ UBound(dataSeries3)), "Services: <*bgColor=CCCCFF*> {value|2} ")) ' ' To show the capabilities of ChartDirector, we are add a movable threshold ' line to the chart and dynamically print a warning message on the chart if ' a data value exceeds the threshold ' Dim thresholdValue As Double thresholdValue = CDbl(AlarmThreshold.Text) ' Add a red mark line to the chart, with the mark label shown at the left of ' the mark line. Dim m As Mark Set m = c.yAxis().addMark(thresholdValue, &HFF0000, "Alarm = " & thresholdValue) Call m.setAlignment(cd.Left) Call m.setBackground(&HFFCCCC) If dataSeries3(UBound(dataSeries3)) > thresholdValue Or _ dataSeries2(UBound(dataSeries2)) > thresholdValue Then ' Add an alarm message as a custom text box on top-right corner of the ' plot area if the latest data value exceeds threshold. Call c.addText(575, 62, "Alarm - Latest Value Exceeded Threshold", _ "arialbi.ttf", 10, &HFFFFFF, cd.TopRight).setBackground(&HDD0000) End If ' Fill the region above the threshold as semi-transparent red (80ff8888) Call c.addInterLineLayer(layer.getLine(1), m.getLine(), &H80FF8888, cd.Transparent) Call c.addInterLineLayer(layer.getLine(2), m.getLine(), &H80FF8888, cd.Transparent) End If Set viewer.Picture = c.makePicture() End Sub