/******************************************************************************* * Copyright (c) 2012 Laurent CARON * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Laurent CARON (laurent.caron at gmail dot com) - initial API and implementation *******************************************************************************/ package org.mihalis.opal.systemMonitor; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; /** * Instances of this class are system monitors. * <dl> * <dt><b>Styles:</b></dt> * <dd>(none)</dd> * <dt><b>Events:</b></dt> * <dd>(none)</dd> * </dl> */ public class SystemMonitor extends Canvas { private final Map<String, SampleWrapper> samples; private boolean captionVisible; private GC gc; private final Color borderColor; private final Color gridColorBackground; private final Color gridColor; private final int gridSize; private final int refreshTime; private boolean keepRunning; /** * Constructs a new instance of this class given its parent and a style * value describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in class * <code>SWT</code> which is applicable to instances of this class, or must * be built by <em>bitwise OR</em>'in together (that is, using the * <code>int</code> "|" operator) two or more of those <code>SWT</code> * style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from super * classes. * </p> * * @param parent a composite control which will be the parent of the new * instance (cannot be null) * @param style the style of control to construct * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the parent</li> * </ul> * */ public SystemMonitor(final Composite parent, final int style) { super(parent, style | SWT.DOUBLE_BUFFERED); this.samples = new LinkedHashMap<String, SampleWrapper>(); this.captionVisible = true; this.borderColor = new Color(getDisplay(), 96, 96, 96); this.gridColor = new Color(getDisplay(), 89, 89, 89); this.gridColorBackground = new Color(getDisplay(), 50, 50, 50); this.gridSize = 12; this.refreshTime = 300; this.keepRunning = true; createListeners(); launchDataCollecting(); } /** * Constructs a new instance of this class given its parent, a style * value describing its behavior and appearance. Also for a given * grid size and refresh interval * <p> * The style value is either one of the style constants defined in class * <code>SWT</code> which is applicable to instances of this class, or must * be built by <em>bitwise OR</em>'in together (that is, using the * <code>int</code> "|" operator) two or more of those <code>SWT</code> * style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from super * classes. * </p> * * @param parent a composite control which will be the parent of the new * instance (cannot be null) * @param style the style of control to construct * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the parent</li> * </ul> * */ public SystemMonitor(final Composite parent, final int style, final int gridSize, final int refeshTime) { super(parent, style | SWT.DOUBLE_BUFFERED); this.samples = new LinkedHashMap<String, SampleWrapper>(); this.captionVisible = true; this.borderColor = new Color(getDisplay(), 96, 96, 96); this.gridColor = new Color(getDisplay(), 89, 89, 89); this.gridColorBackground = new Color(getDisplay(), 50, 50, 50); this.gridSize = gridSize; this.refreshTime = refeshTime; this.keepRunning = true; createListeners(); launchDataCollecting(); } /** * Create the listeners */ private void createListeners() { addPaintListener(new PaintListener() { @Override public void paintControl(final PaintEvent e) { SystemMonitor.this.paintControl(e); } }); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { SystemMonitor.this.borderColor.dispose(); SystemMonitor.this.gridColor.dispose(); SystemMonitor.this.gridColorBackground.dispose(); } }); addControlListener(new ControlAdapter() { /** * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent) */ @Override public void controlResized(final ControlEvent e) { for (final SampleWrapper sample : SystemMonitor.this.samples.values()) { sample.resize(getClientArea().width / SystemMonitor.this.gridSize - 1); } } }); } /** * Draws the widget * * @param e paint event */ private void paintControl(final PaintEvent e) { this.gc = e.gc; e.gc.setAdvanced(true); e.gc.setAntialias(SWT.ON); drawBackground(); drawGrid(); for (final SampleWrapper sample : this.samples.values()) { drawData(sample); } if (this.captionVisible && this.samples.size() == 1) { drawCaption(); } } /** * Draws the background */ private void drawBackground() { final Rectangle clientArea = getClientArea(); this.gc.setForeground(this.borderColor); this.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); this.gc.fillRoundRectangle(clientArea.x, clientArea.y, clientArea.width, clientArea.height, 5, 5); this.gc.drawRoundRectangle(clientArea.x, clientArea.y, clientArea.width, clientArea.height, 5, 5); } /** * Draw the grid */ private void drawGrid() { final Rectangle clientArea = getClientArea(); this.gc.setClipping(clientArea.x + 3, clientArea.y + 3, clientArea.width - 6, clientArea.height - 6); this.gc.setForeground(this.gridColor); this.gc.setBackground(this.gridColorBackground); this.gc.fillRectangle(getClientArea()); for (int x = this.gridSize / 2; x < clientArea.x + clientArea.width; x += this.gridSize) { this.gc.drawLine(x, clientArea.x, x, clientArea.height); } for (int y = this.gridSize / 2; y < clientArea.y + clientArea.height; y += this.gridSize) { this.gc.drawLine(clientArea.x, y, clientArea.width, y); } this.gc.setAlpha(180); this.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); this.gc.fillRoundRectangle(clientArea.x + 10, clientArea.y + 10, clientArea.width + 20, clientArea.width + 20, 5, 5); this.gc.setAlpha(255); this.gc.setClipping(clientArea); } /** * Draw the data * * @param sample sample that contains data */ private void drawData(final SampleWrapper sample) { final List<Double> data = sample.getData(); if (data == null || data.size() < 2) { return; } final Rectangle clientArea = getClientArea(); final double maxValue = sample.getMaxValue(); this.gc.setClipping(clientArea); final Color borderColor = new Color(getDisplay(), sample.getBorderColor()); final Color color = new Color(getDisplay(), sample.getColor()); final int[] pointArray = new int[2 * (data.size() + 2)]; final int availableWidth = clientArea.width - this.gridSize; final int availableHeight = (int) ((clientArea.height - this.gridSize) * 0.98f); int x = this.gridSize / 2 + availableWidth - (data.size() - 1) * this.gridSize; // First point pointArray[0] = x; pointArray[1] = clientArea.y + clientArea.height + this.gridSize / 2 - (this.captionVisible ? 25 : 0); // Following points int index = 2; double maxDisplayedValue = -1d; for (final Double datum : data) { pointArray[index++] = x; pointArray[index++] = clientArea.height - (int) (this.gridSize / 2 + availableHeight * datum / maxValue); x += this.gridSize; maxDisplayedValue = Math.max(maxDisplayedValue, datum); } // Last point pointArray[index++] = x - this.gridSize; pointArray[index++] = clientArea.y + clientArea.height + this.gridSize / 2 - (this.captionVisible ? 25 : 0); // Draw a gradient rectangle this.gc.setAlpha(this.samples.size() == 1 ? 210 : 150); final Region region = new Region(getDisplay()); region.add(pointArray); this.gc.setClipping(region); this.gc.setForeground(borderColor); this.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); this.gc.fillGradientRectangle(this.gridSize / 2, clientArea.height - (int) (this.gridSize / 2 + availableHeight * maxDisplayedValue / maxValue), availableWidth, (int) (availableHeight * maxDisplayedValue / maxValue), true); // Draw the polyline this.gc.setClipping(clientArea); this.gc.setForeground(borderColor); this.gc.drawPolygon(pointArray); region.dispose(); borderColor.dispose(); color.dispose(); this.gc.setAlpha(255); } private void drawCaption() { for (final SampleWrapper sample : this.samples.values()) { if (sample.getCaption() != null && !sample.getCaption().equals("")) { drawCaptionForSample(sample); return; } } } /** * Draws a caption for a given sample * * @param sample sample */ private void drawCaptionForSample(final SampleWrapper sample) { final Rectangle clientArea = getClientArea(); this.gc.setClipping(clientArea); this.gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); this.gc.fillRectangle(clientArea.x, clientArea.y + clientArea.height - 19, clientArea.width, 19); final Color color = new Color(getDisplay(), sample.getBorderColor()); this.gc.setForeground(color); final FontData[] fontData = getFont().getFontData(); for (final FontData f : fontData) { f.setHeight(9); } final Font font = new Font(getDisplay(), fontData); this.gc.setFont(getFont()); final String format = sample.getFormatPattern().replace("{value}", "1$").replace("{maxValue}", "2$").replace("{percentValue}", "3$"); final String formattedCaption = String.format(format, // new Object[] { sample.getLastValue(), // sample.getLastMaxValue(), // Double.valueOf(sample.getLastValue() / sample.getLastMaxValue() * 100.0D) }); this.gc.drawString(sample.getCaption() + " : " + formattedCaption, clientArea.x + this.gridSize, clientArea.y + clientArea.height - 19); font.dispose(); color.dispose(); } /** * Launch the data collecting process */ private void launchDataCollecting() { getDisplay().timerExec(this.refreshTime, new Runnable() { @Override public void run() { collect(); if (!SystemMonitor.this.isDisposed() && SystemMonitor.this.keepRunning && !getDisplay().isDisposed()) { getDisplay().timerExec(SystemMonitor.this.refreshTime, this); } } }); } /** * Collect data */ private void collect() { for (final SampleWrapper sample : this.samples.values()) { sample.collect(); } if (!this.isDisposed() && !getDisplay().isDisposed()) { getDisplay().asyncExec(new Runnable() { @Override public void run() { if (!SystemMonitor.this.isDisposed()) { redraw(); } } }); } } /** * Constructs a new instance of this class given its parent and a style * value describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in class * <code>SWT</code> which is applicable to instances of this class, or must * be built by <em>bitwise OR</em>'in together (that is, using the * <code>int</code> "|" operator) two or more of those <code>SWT</code> * style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from super * classes. * </p> * * @param parent a composite control which will be the parent of the new * instance (cannot be null) * @param style the style of control to construct * @param identier Sample identifier * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the parent</li> * </ul> * */ public SystemMonitor(final Composite parent, final int style, final SampleIdentifier identifier) { this(parent, style); final SampleWrapper wrapper = SampleFactory.getInstance().getSample(identifier); addSample(identifier.name(), wrapper); this.captionVisible = !wrapper.getCaption().trim().equals(""); } /** * Add a sample * * @param id identifier * @param sample sample to add */ public void addSample(final String id, final Sample sample) { this.samples.put(id, new SampleWrapper(sample)); } /** * Add a sample * * @param id identifier * @param sampleWrapper sample wrapper */ private void addSample(final String id, final SampleWrapper sampleWrapper) { this.samples.put(id, sampleWrapper); } /** * Displays all built-in samples */ public void displayAll() { this.samples.clear(); addSample(SampleIdentifier.CPU_USAGE.name(), SampleFactory.getInstance().getSample(SampleIdentifier.CPU_USAGE)); addSample(SampleIdentifier.HEAP_MEMORY.name(), SampleFactory.getInstance().getSample(SampleIdentifier.HEAP_MEMORY)); addSample(SampleIdentifier.PHYSICAL_MEMORY.name(), SampleFactory.getInstance().getSample(SampleIdentifier.PHYSICAL_MEMORY)); addSample(SampleIdentifier.THREADS.name(), SampleFactory.getInstance().getSample(SampleIdentifier.THREADS)); } /** * @return <code>true</code> if the caption is visible, <code>false</code> * otherwise */ public boolean isCaptionVisible() { return this.captionVisible; } /** * Set the caption for a given sample * * @param id sample identifier * @param caption caption to set */ public void setCaption(final String id, final String caption) { if (!this.samples.containsKey(id)) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.samples.get(id).setCaption(caption); } /** * @param captionVisible if true, the caption is visible */ public void setCaptionVisible(final boolean captionVisible) { this.captionVisible = captionVisible; } /** * Set the color for a given sample * * @param id sample identifier * @param color color to set */ public void setColor(final String id, final RGB color) { if (!this.samples.containsKey(id)) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.samples.get(id).setColor(color); } /** * Set the pattern for a given sample * * @param id sample identifier * @param pattern pattern to set */ public void setFormatPattern(final String id, final String pattern) { if (!this.samples.containsKey(id)) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.samples.get(id).setFormatPattern(pattern); } /** * Stop the data collecting process */ public void stop() { this.keepRunning = false; } }