ChartDirector 7.0 (ASP/COM/VB Edition)
Realtime Chart Demonstration (Windows)
Source Code Listing
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