Can the X-Trace marking on the Easy Chart be set to a specific date and time?
I've been digging around in the API and I can't seem to find a way to specify the X-Trace via scripting.
I've got a customer who wants to show an alarm journal underneath the historian screen, and if an alarm is clicked on, then show/move the X-Trace marking to the date and time of the alarm.
This sort of thing is exactly what my NoteChart module was designed to support, including the scriptable XTrace timestamp. (I'd show the alarm events as notes, though, to save screen real estate.)
I honestly would have expected changing the plot's domainCrosshairValue to move the xTrace, but I couldn't get it to work event with a repaint. What I did get to work was calculating the x-coordinate from the domain location and simulating a mouse click using robot. Here is the custom method I developed that simply requires the specified date in millis to be passed in:
def setXTrace(self, timeStampInMillis):
from java.awt import Robot
from java.awt.event import MouseEvent
# Get the internal EasyChart Class, the JFreeChart, and the plot
chartField = self.getClass().getDeclaredField('chart')
chartField.setAccessible(True)
easyChartPanel = chartField.get(self)
chart = easyChartPanel.chart
plot = chart.plot
# Calculate the x-coordinate based on the time in millis
xCoordinate = int((timeStampInMillis - plot.domainAxis.lowerBound) / (plot.domainAxis.upperBound - plot.domainAxis.lowerBound) * easyChartPanel.width)
# Create a robot for simulating mouse events
robot = Robot()
# Simulate a mouse click event at the calculated x-coordinate within the easyChart
robot.mouseMove(easyChartPanel.locationOnScreen.x + xCoordinate, easyChartPanel.locationOnScreen.y)
robot.mousePress(MouseEvent.BUTTON1_MASK)
robot.mouseRelease(MouseEvent.BUTTON1_MASK)
Alternatively, you could simply draw a value marker on the chart to mark the location of the alarm event. Here is an example I developed for the easy chart that you could possibly adapt to your requirement:
In case you are still curious, I figured out that it's the chart panel's setAnchor method that can be used to programmatically set the xTrace. However, it's not exposed, so reflection has to be used to invoke it, and it uses a Point2D.Double to position the range and domain crosshairs. Therefore, just like the robot method I developed, any timestamp has to be converted to a x coordinate to correctly position the domain crosshair on the chart. Also, in my testing, I found that a repaint is needed to make the effect immediately observable.
I found some time this morning to install the note chart module in my test environment, and that is by far the simplest approach. All you have to do to move the xTrace is change the traceTS parameter, and it takes a normal and natural date without any convert to millis or x-coordinate hocus pocus:
newDate = # Alarm event active time
event.source.parent.getComponent('Easy Note Chart').traceTS = newDate
It came to my attention that the xCoordinate calculation for the robot method listed above could be a little off due to the variable plotData position within the chartPanel. This can be corrected easily with the screenDataArea, but messing around with it, the approach didn't feel complete because of these reasons:
• The mouse cursor jumping to the chart and not staying at the click point was a bit jarring
• If the alarm date wasn't displayed on the chart the mouse cursor would move off screen
• If the chart wasn't already in xTrace mode, nothing would happen.
The following updated approach corrects all of those issues, centers the alarm date on the chart, and puts the xTrace there without the need for any complicated calculations. It also takes a normal unformatted date:
#def setXTrace(self, date):
'''
This is designed to be placed in a custom method on the chart itself
'''
from java.awt import Robot, MouseInfo
from java.awt.event import MouseEvent
# Get the mouse's current location, so it can be put back after the chart has been clicked
eventLocation = MouseInfo.getPointerInfo().location
# Ensure that the chart is in xTrace mode
self.setMode(4)
# Reposition the plot, so the selected date and time is centered
# ...with exactly one day's worth of data visible
startDate = system.date.addHours(date, -12)
endDate = system.date.addHours(date, 12)
self.startDate = startDate
self.outerRangeStart = startDate
self.endDate = endDate
self.outerRangeEnd = endDate
# Get the internal EasyChart class
easyChartPanel = self.getComponent(0).getComponent(0)
# Get the data area and calculate the coodinates for the exact midpoint of the plotted data
# ...using the data area bounds and the chart's location on the screen
dataArea = easyChartPanel.screenDataArea
xCoordinate = int(easyChartPanel.locationOnScreen.x + dataArea.x + (0.5 * dataArea.width))
yCoordinate = int(easyChartPanel.locationOnScreen.y + dataArea.y + (0.5 * dataArea.height))
# Create a robot instance
robot = Robot()
# Ensure that the mouse is not still being pressed by the user before moving the cursor
# ...This line was added to correct a behavior that occurred during testing
robot.mouseRelease(MouseEvent.BUTTON1_MASK)
# Move the mouse to the midpoint of the plot
robot.mouseMove(xCoordinate, yCoordinate)
# Click the mouse button to move the xTrace
robot.mousePress(MouseEvent.BUTTON1_MASK)
robot.mouseRelease(MouseEvent.BUTTON1_MASK)
# Move the mouse back to its starting location
robot.mouseMove(eventLocation.x, eventLocation.y)