--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.power/src/com/nokia/carbide/cpp/pi/power/PowerTraceGraph.java Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:
+ *
+ */
+
+package com.nokia.carbide.cpp.pi.power;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.DecimalFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.Panel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+import com.nokia.carbide.cpp.internal.pi.actions.SaveSamples;
+import com.nokia.carbide.cpp.internal.pi.analyser.NpiInstanceRepository;
+import com.nokia.carbide.cpp.internal.pi.interfaces.ISaveSamples;
+import com.nokia.carbide.cpp.internal.pi.model.GenericSampledTrace;
+import com.nokia.carbide.cpp.internal.pi.plugin.model.IContextMenu;
+import com.nokia.carbide.cpp.internal.pi.power.actions.PowerSettingsDialog;
+import com.nokia.carbide.cpp.internal.pi.power.actions.PowerStatisticsDialog;
+import com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph;
+import com.nokia.carbide.cpp.internal.pi.visual.GraphComposite;
+import com.nokia.carbide.cpp.internal.pi.visual.PICompositePanel;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEvent;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEventListener;
+import com.nokia.carbide.cpp.pi.address.GppSample;
+import com.nokia.carbide.cpp.pi.address.GppTrace;
+import com.nokia.carbide.cpp.pi.editors.PIPageEditor;
+import com.nokia.carbide.cpp.pi.util.ColorPalette;
+
+
+public class PowerTraceGraph extends GenericTraceGraph implements ActionListener,
+ PIEventListener,
+ MouseMotionListener,
+ IContextMenu
+{
+
+ private int[] DTrace; // used for synchronizing the power and gpptraces.
+ private int[] DPower;
+
+ // 3 tabs can share the same trace, but they need different graphs
+ private PwrTrace trace;
+
+ private FigureCanvas leftFigureCanvas;
+
+ // used to draw the text for the power line in the graph window.
+ private int mPowerLineY = 0;
+
+ public int x = 400;
+ public int y = 800;
+
+ private double averageConsumption = (float)0.0;
+ private int averageComsumptionLastOffset = Integer.MAX_VALUE;
+ private double cumulative = (float)0.0;
+ private double energy = 0.0f;
+ private int[] sampleTimes;
+ private int[] ampValues;
+ private int[] voltValues;
+// private int[] capaValues;
+ private double maxAmps = Integer.MIN_VALUE;
+ private double minAmps = Integer.MAX_VALUE;
+ private double maxPower = 0.0;
+ private boolean mSelecting = false;
+
+ // used when determining when to draw the text over the power bar.
+ private int leftBorder = 0;
+
+ private static int xLegendHeight = 20;
+
+ boolean mShowPowerLine = true;
+
+ private static DecimalFormat powerFormat = new DecimalFormat(Messages.getString("powerFormat")); //$NON-NLS-1$
+ private static DecimalFormat voltageFormat = new DecimalFormat(Messages.getString("voltageFormat")); //$NON-NLS-1$
+
+ private int uid;
+
+ protected static int SAMPLES_AT_ONE_TIME = 1000;
+ protected int stringTime;
+
+
+ // class to pass sample data to the save wizard
+ public class SaveSampleString implements ISaveSamples {
+ int startTime;
+ int endTime;
+
+ public SaveSampleString() {
+ }
+
+ public String getData() {
+ return getSampleString(SAMPLES_AT_ONE_TIME, this.startTime, this.endTime);
+ }
+
+ public int getIndex() {
+ if (stringTime == (int) (getSelectionStart() + 0.0005))
+ return 0;
+
+ return stringTime;
+ }
+
+ public void clear() {
+ this.startTime = (int) (getSelectionStart() + 0.0005);
+ this.endTime = (int) (getSelectionEnd() + 0.0005);
+ stringTime = startTime;
+ }
+ }
+
+ /*
+ * return the power samples selected in the interval
+ */
+ protected String getSampleString(int count, int startTime, int endTime)
+ {
+ Vector sampleVector = ((PwrTrace) this.getTrace()).samples;
+ PwrSample lastSample = (PwrSample) sampleVector.get(sampleVector.size() - 1);
+
+ String returnString = null;
+
+ if (this.stringTime == startTime) {
+ returnString = Messages.getString("PowerTraceGraph.saveSamplesHeading"); //$NON-NLS-1$
+ }
+
+ this.stringTime++;
+
+ // check if we have returned everything
+ if ((this.stringTime > lastSample.sampleSynchTime) || (this.stringTime > endTime)) {
+ this.stringTime = endTime + 1;
+ return returnString;
+ }
+
+ double oldCurrent = -1;
+ double oldVoltage = -1;
+ double oldCapacity = -1;
+ String string = ""; //$NON-NLS-1$
+
+ if (this.trace.isComplete()) {
+ for ( ;
+ (this.stringTime < endTime + 1) && (count > 0) && (this.stringTime < sampleVector.size());
+ this.stringTime++) {
+ PwrSample sample = (PwrSample) sampleVector.get(stringTime);
+ double current = sample.current;
+ double voltage = sample.voltage;
+ double capacity = sample.capacity;
+
+ if ((oldCurrent != current) || (oldVoltage != voltage) || (oldCapacity != capacity)) {
+ string = Messages.getString("PowerTraceGraph.comma") + (int) current + Messages.getString("PowerTraceGraph.comma") + (int) voltage + Messages.getString("PowerTraceGraph.comma") + (int) capacity + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ oldCurrent = current;
+ oldVoltage = voltage;
+ oldCapacity = capacity;
+ }
+
+ returnString += sample.sampleSynchTime + string;
+ count--;
+ }
+ } else {
+ int i = 0;
+
+ //find least sampling time greater than or equal to the start index
+ for ( ; i < sampleVector.size(); i++) {
+ if (((PwrSample) sampleVector.get(i)).sampleSynchTime > this.stringTime)
+ break;
+ }
+
+ if (i != 0)
+ i--;
+
+ for ( ; i < sampleVector.size() && (count > 0); i++) {
+ PwrSample sample = (PwrSample) sampleVector.get(i);
+
+ this.stringTime = (int) sample.sampleSynchTime;
+ if (sample.sampleSynchTime > endTime)
+ break;
+
+ double current = sample.current;
+ double voltage = sample.voltage;
+ double capacity = sample.capacity;
+
+ returnString += sample.sampleSynchTime + Messages.getString("PowerTraceGraph.comma") + (int) current + Messages.getString("PowerTraceGraph.comma") + (int) voltage + Messages.getString("PowerTraceGraph.comma") + (int) capacity + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ count--;
+ }
+ }
+
+ // check if we have returned everything
+ if (this.stringTime >= lastSample.sampleSynchTime) {
+ this.stringTime = endTime + 1;
+ }
+
+ return returnString;
+ }
+
+ protected void actionSaveSamples(ISaveSamples saveSamples)
+ {
+ new SaveSamples(saveSamples);
+ }
+
+ protected MenuItem getSaveSamplesItem(Menu menu, boolean enabled) {
+ MenuItem saveSamplesItem = new MenuItem(menu, SWT.PUSH);
+
+ saveSamplesItem.setText(Messages.getString("PowerTraceGraph.saveSamplesForInterval")); //$NON-NLS-1$
+ saveSamplesItem.setEnabled(enabled);
+
+ if (enabled) {
+ saveSamplesItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ saveEventSamples();
+ }
+ });
+ }
+
+ return saveSamplesItem;
+ }
+
+ public void saveEventSamples() {
+ SaveSampleString saveSampleString = new SaveSampleString();
+ actionSaveSamples(saveSampleString); //$NON-NLS-1$
+ }
+
+ public PowerTraceGraph( int graphIndex, int uid, PwrTrace trace )
+ {
+ super((GenericSampledTrace)trace);
+ this.graphIndex = graphIndex;
+ this.uid = uid;
+
+ if ( trace != null )
+ {
+ this.trace = trace;
+
+ if (this.trace.getSampleTimes() == null)
+ this.trace.initData();
+
+ this.sampleTimes = this.trace.getSampleTimes();
+ this.ampValues = this.trace.getAmpValues();
+ this.voltValues = this.trace.getVoltValues();
+// this.capaValues = this.trace.getCapaValues();
+ this.maxAmps = this.trace.getMaxAmps();
+ this.minAmps = this.trace.getMinAmps();
+ this.maxPower = this.trace.getMaxPower();
+ }
+ }
+
+ public int getUid() {
+ return this.uid;
+ }
+
+ // This method is called when widgets in the PowerInfoPanel are
+ // manipulated.
+ //
+ public void action(String actionString)
+ {
+ if (actionString.equals("show_average")) //$NON-NLS-1$
+ {
+ // show average power as a line
+ mShowPowerLine = true;
+ }
+ else if (actionString.equals("hide_average")) //$NON-NLS-1$
+ {
+ // do not show average power as a line
+ mShowPowerLine = false;
+ }
+ else if (actionString.equals("changeVoltage")) //$NON-NLS-1$
+ {
+ // voltage changed, so compute new maximum power
+ this.trace.scaleMaxPower();
+ this.maxPower = this.trace.getMaxPower();
+ }
+ else if (actionString.equals("changeBatterySize")) //$NON-NLS-1$
+ {
+ // battery size changed, so repaint
+ }
+ else
+ return;
+
+ this.repaint();
+ if (this.leftFigureCanvas != null)
+ this.leftFigureCanvas.redraw();
+ }
+
+ public void piEventReceived(PIEvent be)
+ {
+ switch (be.getType())
+ {
+ case (PIEvent.SELECTION_AREA_CHANGED):
+
+ // send this message to the 2 other graphs
+ PIEvent be2 = new PIEvent(be.getValueObject(),
+ PIEvent.SELECTION_AREA_CHANGED2);
+
+ for (int i = 0; i < 3; i++)
+ {
+ PowerTraceGraph graph = trace.getPowerGraph(i, getUid());
+
+ if (graph != this) {
+ graph.piEventReceived(be2);
+ }
+ }
+
+ // FALL THROUGH
+ case PIEvent.SELECTION_AREA_CHANGED2:
+ double[] values = (double[])be.getValueObject();
+ this.setSelectionStart(values[0]);
+ this.setSelectionEnd(values[1]);
+ this.calcValues();
+ mSelecting = true;
+ break;
+
+ case (PIEvent.MOUSE_PRESSED):
+ this.calcValues();
+ this.parentComponent.getSashForm().redraw();
+ break;
+
+ case PIEvent.SCROLLED:
+ Event event = ((Event)be.getValueObject());
+ this.parentComponent.setScrolledOrigin(event.x, event.y);
+ this.repaint();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ public void refreshDataFromTrace()
+ {
+ }
+
+ public void enablePowerLine( boolean state )
+ {
+ mShowPowerLine = state;
+ }
+
+ public void actionPerformed ( ActionEvent ae )
+ {
+ if( ae.getActionCommand().equals("switch") ) //$NON-NLS-1$
+ {
+ parentComponent.piEventReceived( new PIEvent( "switch_gpp", PIEvent.PLUGIN_STRING_MESSAGE) ); //$NON-NLS-1$
+ }
+ this.repaint();
+ }
+
+ public PICompositePanel getParentComponent() {
+ return this.parentComponent;
+ }
+
+ public float getVoltage()
+ {
+ if ( trace != null )
+ return trace.getVoltage();
+
+ return 0.0f;
+ }
+
+ public void setVoltage( float newVal )
+ {
+ if ( trace != null )
+ trace.setVoltage(newVal);
+ }
+
+ public double getAverageConsumption()
+ {
+ if ( trace != null )
+ {
+ if( averageComsumptionLastOffset != trace.getOffset() )
+ {
+ averageComsumptionLastOffset = trace.getOffset();
+ // not calculated yet
+ calcValues();
+ }
+
+ return this.averageConsumption * trace.getVoltage();
+ }
+
+ return 0.0f;
+ }
+
+ public void setOffset( int newOffset )
+ {
+ if ( trace != null )
+ trace.setOffset( newOffset );
+ }
+
+ public void increaseOffset()
+ {
+ if ( trace != null )
+ {
+ trace.setOffset( trace.getOffset() + 50 );
+ }
+ }
+
+ public void decreaseOffset()
+ {
+ if ( trace != null )
+ {
+ trace.setOffset( trace.getOffset() - 50 );
+ }
+ }
+
+ public void setSize(int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public Dimension getSize()
+ {
+ return new Dimension(x, y);
+ }
+
+ public void paint(Panel panel, Graphics graphics)
+ {
+ this.setSize(this.getSize().width, getVisualSize().height);
+ this.drawDottedLineBackground(graphics, PowerTraceGraph.xLegendHeight);
+ this.drawPowerData(graphics, PowerTraceGraph.xLegendHeight);
+
+ // draws the same selection as the Address/Thread trace
+ this.drawSelectionSection(graphics, PowerTraceGraph.xLegendHeight);
+
+ if (mShowPowerLine)
+ this.drawPowerLine(graphics);
+ }
+
+ public void repaint()
+ {
+ this.parentComponent.repaintComponent();
+ }
+
+ private void showPowerValueAtX(MouseEvent me)
+ {
+ if (me.y > this.getVisualSizeY() - PowerTraceGraph.xLegendHeight) {
+ this.setToolTipText(null);
+ return;
+ }
+
+ // scale the value of x
+ int x = (int) (me.x * this.getScale() + 0.5);
+
+ if (x > (int) (PIPageEditor.currentPageEditor().getMaxEndTime() * 1000 + 0.0005)) {
+ this.setToolTipText(null);
+ return;
+ }
+
+ if (x > trace.getLastSampleNumber())
+ x = trace.getLastSampleNumber();
+
+ String textToShow = Double.toString(x / 1000.0)
+ + Messages.getString("tooltip1"); //$NON-NLS-1$
+
+ // We could instead use the measured voltage: voltValues[x +/- movement]/1000.0 rather than the user-specified voltage?
+ double voltage = trace.getVoltage();
+
+ int movement = trace.getOffset();
+ int ampValue = 0;
+
+ // find the amperage for x
+ int index = -1;
+ if (movement == 0)
+ {
+ index = timeIndex(x);
+ if (index != -1)
+ ampValue = ampValues[index];
+ }
+ else if (movement < 0)
+ {
+ // make it positive
+ movement *= -1;
+
+ // eat the first N=offset values.
+ if (x < sampleTimes.length - movement)
+ {
+ index = timeIndex(x + movement);
+ if (index != -1)
+ ampValue = ampValues[index];
+ }
+ }
+ else // movement > 0
+ {
+ if (x > movement)
+ {
+ index = timeIndex(x - movement);
+ if (index != -1)
+ ampValue = ampValues[index];
+ }
+ }
+
+ // determine the power = amps * voltage
+ textToShow += (int) ((ampValue * voltage) + 0.5);
+
+ this.setToolTipText(textToShow + Messages.getString("tooltip2") //$NON-NLS-1$
+ + PowerTraceGraph.voltageFormat.format(voltage));
+ }
+
+ public int timeIndex(int time) {
+ if (time < sampleTimes[0])
+ return -1;
+
+ if (time >= sampleTimes[sampleTimes.length - 1])
+ return sampleTimes.length - 1;
+
+ int i = 0;
+ for ( ; sampleTimes[i] <= time; i++)
+ ;
+
+ if (sampleTimes[i] == time)
+ return i;
+ else
+ return i - 1;
+ }
+
+ private void drawPowerData(Graphics graphics, int yLegendSpace)
+ {
+ int visY = this.getVisualSize().height - yLegendSpace;
+ if (visY < 0)
+ visY = 0;
+ int sampleCount = this.sampleTimes.length;
+
+ // arrays of values to draw
+ int points[] = new int[sampleCount * 4];
+
+ // the offset changes when the user moves the graph or the traces are synched
+ int movement = trace.getOffset();
+
+ // look for a move to the right ( > 0)
+ // the y value is in thisValue is the milliAmps
+ // it doesn't matter if we render the milliAmps or mW, the graphs have the same
+ // form, it only matters when you show the values associated with the graph.
+ double maxAmps = this.maxPower / this.trace.getVoltage();
+
+ double cachedScale = this.getScale();
+ // or no movement (==0)
+ if( movement == 0 )
+ {
+ for( int i = 0, k = 0; i < sampleTimes.length; i++ )
+ {
+ points[k++] = (int)(sampleTimes[i] / cachedScale);
+ points[k++] = (int)(visY - (ampValues[i] / maxAmps) * visY);
+ if (i < sampleTimes.length - 1)
+ {
+ points[k++] = (int)(sampleTimes[i + 1] / cachedScale);
+ } else {
+ long lastTime = (int) (PIPageEditor.currentPageEditor().getMaxEndTime() * 1000 + 0.0005);
+ points[k++] = (int) (lastTime / cachedScale);
+ }
+ points[k++] = (int)(visY - (ampValues[i] / maxAmps) * visY);
+ }
+ }
+ else if( movement > 0 )
+ {
+ // set the first N=offset values to be 0
+ for (int i = 0, k = movement * 2; i < sampleCount - movement; i++) {
+ points[k++] = sampleTimes[i];
+ points[k++] = ampValues[i];
+ }
+
+ for( int i = 0, k = 0; i < sampleCount; i++ )
+ {
+ if( i < movement )
+ {
+ points[k++] = (int)(i / cachedScale);
+ points[k++] = (int)(visY - ((float)1 / maxAmps) * visY);
+ }
+ else
+ {
+ points[k++] = (int)((points[2 * i] + movement)/cachedScale);
+ points[k++] = (int)(visY - ((float)points[1 + 2 * i] / maxAmps) * visY);
+ }
+ }
+ }
+ else
+ {
+ // make it positive
+ movement *= -1;
+
+ // eat the first N=offset values.
+ int cutoff = sampleTimes.length - movement;
+
+ for (int i = 0, k = movement * 2; i < sampleCount - movement; i++) {
+ points[k++] = sampleTimes[i];
+ points[k++] = ampValues[i];
+ }
+
+ for( int i = 0, k = 0; i < sampleCount; i++ )
+ {
+ if( i >= cutoff )
+ {
+ points[k++] = (int)(i / cachedScale);
+ points[k++] = (int)(visY - ((float)1 / maxAmps) * visY);
+ }
+ else
+ {
+ points[k++] = (int)((points[2 * i] - movement) / cachedScale);
+ points[k++] = (int)(visY - ((float)points[1 + 2 * i] / maxAmps) * visY);
+ }
+ }
+ }
+
+ graphics.setForegroundColor(ColorConstants.red);
+ // draw all the points at once.
+ graphics.drawPolyline(points);
+ points = null;
+ }
+
+ public double calcValueForY( int y )
+ {
+ double visY = this.getVisualSize().height - PowerTraceGraph.xLegendHeight;
+ if (visY <= 0)
+ return 0.0;
+ double maxAmps = this.maxPower / this.trace.getVoltage();
+ double yScalingFactor = maxAmps/(double)visY;
+ // visY-y to compensate for the transpose, * voltage to compenstate for the mA in the samples, we
+ // want to show the mW values here.
+ return ( (double)(visY - (double)y) * yScalingFactor * (double)trace.getVoltage());
+ }
+
+ public int calcYforValue( double value )
+ {
+ double visY = this.getVisualSize().height - PowerTraceGraph.xLegendHeight;
+ if ((visY <= 0) || (this.trace.getVoltage() == 0))
+ return 0;
+ double maxAmps = this.maxPower / this.trace.getVoltage();
+ double yScalingFactor = maxAmps / visY;
+
+ // visY-y to compensate for the transpose, * voltage to compenstate for the mA in the samples, we
+ // want to show the mW values here.
+ if (yScalingFactor == 0)
+ return 0;
+
+ double tmp = value / (double)trace.getVoltage() / yScalingFactor;
+
+ return (int)Math.ceil(visY - tmp);
+ }
+
+ private void drawPowerLine( Graphics graphics )
+ {
+ //mPowerLineY == 0 on startup.
+ boolean startup = (mPowerLineY == 0);
+
+ if( mPowerLineY == 0 || mSelecting )
+ {
+ if( mSelecting )
+ {
+ // still selecting?
+ if( ((super.getSelectionStart() == -1) && (super.getSelectionEnd() == -1)) )
+ {
+ mSelecting = false;
+ // need to turn them back on. mouse drag events for drawing the power line.
+ }
+ else
+ {
+ mPowerLineY = calcYforValue( getAverageConsumption() );
+ }
+ }
+
+ if( mPowerLineY == 0 )
+ {
+ mPowerLineY = calcYforValue( getAverageConsumption() );
+ }
+ }
+
+ double powerValue = 0.0;
+
+ String strValue = null;
+
+ // requirement, if dragging power bar then print true power value. If
+ // selecting then print average of area not the true value of the y coordinate.
+ //
+ if( !mSelecting && !startup )
+ powerValue = calcValueForY(mPowerLineY);
+ else if( mSelecting || startup )
+ powerValue = getAverageConsumption();
+
+ strValue = PowerTraceGraph.powerFormat.format((int)(powerValue + 0.5));
+
+ // this does cause some overhead and could be removed since it only provides some aesthetics.
+ updateVisibleBorders();
+ leftBorder = this.parentComponent.getScrolledOrigin().x;
+// leftBorder = this.getVisibleLeftBorder();
+ if( leftBorder < 0 )
+ leftBorder = 0;
+
+ // draw the average power line with a width of 3
+ int lineWidth = graphics.getLineWidth();
+
+ graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+ graphics.setLineWidth(2);
+ graphics.drawLine(leftBorder, mPowerLineY, this.getSize().width, mPowerLineY );
+
+ graphics.setLineWidth(lineWidth);
+
+ // figure out how big the box behind the text should be.
+ GC gc = new GC(PIPageEditor.currentPageEditor().getSite().getShell());
+ Point point = gc.stringExtent(strValue);
+ gc.dispose();
+
+ // clear the text rectangle
+ graphics.setBackgroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+ graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+ graphics.fillRectangle(leftBorder + 20, mPowerLineY - (point.y / 2), point.x + 6, point.y );
+
+ graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+
+ // make the font bold 16, draw the text, and restore the font
+ int oldStyle = graphics.getFont().getFontData()[0].getStyle();
+ int oldHeight = graphics.getFont().getFontData()[0].getHeight();
+
+ graphics.getFont().getFontData()[0].setStyle((oldStyle & SWT.NORMAL) | (oldStyle & SWT.ITALIC) | SWT.BOLD);
+ graphics.getFont().getFontData()[0].setHeight(16);
+
+ graphics.drawString(strValue, leftBorder + 23, mPowerLineY - (point.y / 2));
+
+ graphics.getFont().getFontData()[0].setStyle(oldStyle);
+ graphics.getFont().getFontData()[0].setHeight(oldHeight);
+
+ if ( startup )
+ mPowerLineY = 0;
+ }
+
+ public void drawPwrGraphSelection(Graphics g)
+ {
+ int selectionStart = (int)super.getSelectionStart();
+ int selectionEnd = (int)super.getSelectionEnd();
+ double scale = super.getScale();
+
+ if(selectionStart != -1 && selectionEnd != -1)
+ {
+ Color selectColor = new Color(Display.getCurrent(),123,156,178);
+
+ g.setForegroundColor(selectColor);
+ g.fillRectangle((int)(selectionStart / scale + 0.5),
+ 0,
+ (int)((selectionEnd - selectionStart) / scale + 0.5),
+ this.getVisualSize().height - PowerTraceGraph.xLegendHeight);
+ selectColor.dispose();
+ }
+ }
+
+ // synchronize the power and software traces
+ // logically:
+ // 1) digitize the GPP and power traces
+ // 2) align the traces so that the tail of the software trace is aligned with
+ // the tail of the power trace.
+ // 3) slowly move software trace to the left ADDing the software and power trace
+ // values together and performing a sum until the highest total is found.
+ // 4) using the index of the highest total, either add dummy values to the power trace or
+ // remove values from the power trace.
+ // 5) finally update the samples and refresh turn off the wait cursor.
+
+ public void doSynchronize( GppTrace gpp, PwrTrace power )
+ {
+// System.err.println( "Starting synchronization process" );
+ digitizeGPPFile(gpp);
+ digitizePowerFile(power);
+
+ int MAX = 0;
+ int MAX_POWER_INDEX = 0;
+ int MAX_TRACE_INDEX = 0;
+ int accum = 0;
+
+ int TOTAL_MOVEMENT = (DPower.length-DTrace.length) + (int)(0.25*DTrace.length) + 1;
+
+ // TI = trace index
+ int TI = 0;
+ // PI = power trace index
+ int PI = DPower.length-DTrace.length;
+
+ for( int j = 0; j < TOTAL_MOVEMENT; j++ )
+ {
+ for( int i = TI; i < DTrace.length; i++ )
+ {
+ if( (DTrace[i] & DPower[PI+i]) == 0 )
+ {
+ accum++;
+ }
+ }
+ if( accum > MAX )
+ {
+ MAX = accum;
+ MAX_POWER_INDEX = PI;
+ MAX_TRACE_INDEX = TI;
+ }
+ accum = 0;
+ PI--;
+ if( PI < 0 )
+ {
+ PI=0;
+ TI++;
+ }
+ }
+ // one more pass through the data to update the graphs.
+ //
+
+ if( MAX_TRACE_INDEX > 0 )
+ {
+ trace.setOffset( trace.getOffset() - MAX_TRACE_INDEX );
+ }
+ else
+ {
+ trace.setOffset( trace.getOffset() + MAX_POWER_INDEX );
+ }
+ parentComponent.getActiveGraph().getCompositePanel().getSashForm()
+ .setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT) );
+ }
+ // digitize the power trace
+ // basically:
+ // 1) find the min and max power values in the trace
+ // 2) for each value in the trace if the value is > Half the average range
+ // then set that value to 1 else 0.
+ //
+ public void digitizePowerFile( PwrTrace power )
+ {
+ double MIN_Value = (double)minAmps/(double)1000;
+ double MAX_Value = (double)maxAmps/(double)1000;
+
+ // find the min and max in the power
+ //
+ // we already have this data so we can simply divide by 1000 for the scale we need.
+
+ float Half_Avg_Range = (float)((MAX_Value - MIN_Value)/2);
+
+ DPower = new int[power.samples.size()];
+ int DPowerIndex = 0;
+
+ // as an aside, this tracks well with the application states.
+ //
+ for( Enumeration e = power.getSamples(); e.hasMoreElements() ; )
+ {
+ PwrSample current = (PwrSample)e.nextElement();
+ if( current.current > Half_Avg_Range )
+ {
+ DPower[DPowerIndex] = 1;
+ }
+ else
+ {
+ DPower[DPowerIndex] = 0;
+ }
+ DPowerIndex++;
+ }
+ }
+
+ // digitize the gpp file
+ // basically:
+ // 1a) divide the run into windows and for each window:
+ // 1b) count the number of transitions i.e. different functions called in the windows by putting the process
+ // name into a hashtable, if the add returns a null value then the insert
+ // did not collide and we transitioned.
+ // 1c) find the min and max number of transistions for all windows
+ //
+ // 2a) for each window if the number of transitions for that window are greater
+ // than the average number of transtitions then set the digitized version to be 1 else 0.
+ //
+ public void digitizeGPPFile( GppTrace thisTrace )
+ {
+ int WINDOW_SIZE = 20;
+
+ int numSamples = thisTrace.samples.size();
+ int count = 1;
+ Hashtable<String,String> histogram = new Hashtable<String,String>();
+ int windowSizes[] = new int[numSamples/WINDOW_SIZE];
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+
+ int numTransitions = 1;
+
+ int total = 0;
+// String lastTrace = null;
+ for( int i = 0; i < numSamples; i++ )
+ {
+ GppSample sample = thisTrace.getGppSample(i);
+ if( count % WINDOW_SIZE == 0 )
+ {
+ windowSizes[i/WINDOW_SIZE] = histogram.size() + numTransitions;
+ if( windowSizes[i/WINDOW_SIZE] < min )
+ min = windowSizes[i/WINDOW_SIZE];
+ else if( windowSizes[i/WINDOW_SIZE] > max )
+ max = windowSizes[i/WINDOW_SIZE];
+
+ total += windowSizes[i/WINDOW_SIZE];
+
+ numTransitions = 1;
+ histogram.clear();
+ count = 0;
+ }
+ count++;
+ if( histogram.put( new String(sample.thread.process.name + Messages.getString("PowerTraceGraph.histogram1") //$NON-NLS-1$
+ + (sample.thread.threadName != null ? sample.thread.threadName
+ : Messages.getString("PowerTraceGraph.11")) + Messages.getString("PowerTraceGraph.12") //$NON-NLS-1$ //$NON-NLS-2$
+ + sample.thread.threadId + Messages.getString("PowerTraceGraph.13") //$NON-NLS-1$
+ + sample.currentFunctionSym.functionName + Messages.getString("PowerTraceGraph.14") //$NON-NLS-1$
+ + Long.toHexString(sample.currentFunctionSym.startAddress.longValue()) ),
+ Messages.getString("PowerTraceGraph.15") ) == null ) //$NON-NLS-1$
+ {
+ numTransitions++;
+ }
+ }
+
+ int average = (max - min) /2 ;
+
+ DTrace = new int[windowSizes.length * WINDOW_SIZE];
+ int DTraceIndex = 0;
+ for( int i = 0; i < windowSizes.length; i++ )
+ {
+ for( int j = 0; j < WINDOW_SIZE; j++ )
+ {
+ // okay, so it seems that when there is more variablity in the trace there is more constant variablity in the power.
+ if( windowSizes[i] <= average /*halfAvgRange */ )
+ {
+ DTrace[DTraceIndex] = 0;
+ }
+ else
+ {
+ DTrace[DTraceIndex] = 1;
+ }
+ DTraceIndex++;
+ }
+ }
+ }
+
+ // calculates the average power numbers of a selection.
+ private void calcValues()
+ {
+ int selStart = (int)super.getSelectionStart();
+ int selEnd = (int)super.getSelectionEnd();
+ int sum = 0;
+ int offset = trace.getOffset();
+ this.cumulative = 0;
+ this.averageConsumption = 0;
+ this.energy = 0;
+
+ if (selStart < offset)
+ selStart = offset;
+
+ if (selEnd < trace.getFirstSampleNumber() || selStart > selEnd)
+ return;
+
+ // find the first sample greater than or equal to selStart
+ int index = timeIndex(selStart);
+
+ // count time before the first sample as a bunch of zeros
+ if (selStart < trace.getFirstSampleNumber()) {
+ sum = trace.getFirstSampleNumber() - selStart;
+ index = 0;
+ }
+
+ for (int j = index; j < sampleTimes.length; j++)
+ {
+ int time = sampleTimes[j] + 1;
+ if (time < selStart)
+ time = selStart;
+
+ int nextTime = j == sampleTimes.length - 1 ? Integer.MAX_VALUE : sampleTimes[j + 1];
+
+ int count = Math.min(nextTime - time + 1, selEnd - time + 1);
+ this.energy += ((double)ampValues[j] * (double)this.voltValues[j])/(float)1000000.0 * count;
+ this.cumulative += ampValues[j] * count;
+ sum += count;
+ time += count;
+
+ if (time > selEnd)
+ break;
+ }
+
+ if (sum > 0)
+ this.averageConsumption = (double)this.cumulative / sum ;
+ }
+
+ public double getCumulativeConsumption()
+ {
+ return this.energy;
+ }
+
+ public float getBatterySize() {
+ if ( trace != null ) {
+ return trace.getBatterySize();
+ } else
+ return 0.0f;
+ }
+
+ public void setBatterySize( float newVal ) {
+ if ( trace != null )
+ trace.setBatterySize( newVal );
+ }
+
+ public void mouseDragged(MouseEvent me)
+ {
+ int tmpY = me.y;
+ int tmpX = me.x;
+
+ if( (tmpY < (this.getVisualSize().height - PowerTraceGraph.xLegendHeight)) && (tmpY > 0) && (!mSelecting))
+ {
+ mPowerLineY = tmpY;
+ }
+
+ this.repaint();
+ }
+
+ public void mouseMoved(MouseEvent me)
+ {
+ showPowerValueAtX( me );
+ }
+
+ public void mouseEntered(MouseEvent arg0) {
+ }
+
+ public void mouseExited(MouseEvent arg0) {
+ }
+
+ public void mouseHover(MouseEvent arg0) {
+ }
+
+ public void addContextMenuItems(Menu menu, org.eclipse.swt.events.MouseEvent me) {
+
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ Boolean showLine = Boolean.TRUE; // by default, show the interval average power as a line
+
+ // if there is a show average power line value associated with the current Analyser tab, then use it
+ Object obj = NpiInstanceRepository.getInstance().getPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine"); //$NON-NLS-1$
+ if ((obj != null) && (obj instanceof Boolean))
+ // retrieve the current value
+ showLine = (Boolean)obj;
+ else
+ // set the initial value
+ NpiInstanceRepository.getInstance().setPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine", showLine); //$NON-NLS-1$
+
+ final Boolean showLineFinal = showLine;
+
+ MenuItem showLineItem = new MenuItem(menu, SWT.CHECK);
+ showLineItem.setText(Messages.getString("PowerTraceGraph.18")); //$NON-NLS-1$
+ showLineItem.setSelection(showLine);
+ showLineItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ String action;
+ NpiInstanceRepository.getInstance().setPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine", !showLineFinal); //$NON-NLS-1$
+ if (!showLineFinal)
+ {
+ action = "show_average"; //$NON-NLS-1$
+ } else {
+ action = "hide_average"; //$NON-NLS-1$
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ PowerTraceGraph graph = trace.getPowerGraph(i, getUid());
+ graph.action(action);
+ }
+ }
+ });
+
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ int startTime = (int) this.getSelectionStart();
+ int endTime = (int) this.getSelectionEnd();
+
+ // save raw samples
+ getSaveSamplesItem(menu, (startTime != -1) && (endTime != -1) && (startTime != endTime));
+
+ new MenuItem(menu, SWT.SEPARATOR);
+
+ MenuItem powerSettingsItem = new MenuItem(menu, SWT.PUSH);
+ powerSettingsItem.setText(Messages.getString("PowerTraceGraph.22")); //$NON-NLS-1$
+ powerSettingsItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ new PowerSettingsDialog(Display.getCurrent());
+ }
+ });
+
+ MenuItem powerStatsItem = new MenuItem(menu, SWT.PUSH);
+ powerStatsItem.setText(Messages.getString("PowerTraceGraph.23")); //$NON-NLS-1$
+ powerStatsItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ new PowerStatisticsDialog(Display.getCurrent());
+ }
+ });
+ }
+
+ public void paintLeftLegend(FigureCanvas figureCanvas, GC gc)
+ {
+ GC localGC = gc;
+
+ if (gc == null)
+ gc = new GC(PIPageEditor.currentPageEditor().getSite().getShell());
+
+ if (this.leftFigureCanvas == null)
+ this.leftFigureCanvas = figureCanvas;
+
+ Rectangle rect = ((GraphComposite) figureCanvas.getParent()).figureCanvas.getClientArea();
+
+ double visY = rect.height - PowerTraceGraph.xLegendHeight;
+ if (visY < 0)
+ visY = 0;
+ double yIncrement = visY / 10;
+
+ // this assumes maxPower is evenly divisible by 10
+ int maxPower = (int) this.maxPower;
+ int powerIncrement = maxPower / 10;
+
+ gc.setForeground(ColorPalette.getColor(new RGB(100, 100, 100)));
+ gc.setBackground(ColorPalette.getColor(new RGB(255, 255, 255)));
+
+ int previousBottom = 0; // bottom of the previous legend drawn
+ String legend;
+
+ // draw 11 value indicators (0..10) to the scale
+ int i = 0;
+ for (double y = 0; i < 11; i++, y += yIncrement, maxPower -= powerIncrement)
+ {
+ // construct the text for each scale
+ legend = (int)maxPower + Messages.getString("PowerTraceGraph.24"); //$NON-NLS-1$
+
+ Point extent = gc.stringExtent(legend);
+
+ gc.drawLine(GenericTraceGraph.yLegendWidth - 3, (int)y + 1, GenericTraceGraph.yLegendWidth, (int)y + 1);
+
+ if (y >= previousBottom)
+ {
+ gc.drawString(legend, GenericTraceGraph.yLegendWidth - extent.x - 4, (int)y);
+ previousBottom = (int)y + extent.y;
+ }
+ }
+
+ if (localGC == null) {
+ gc.dispose();
+ figureCanvas.redraw();
+ }
+ }
+}