/* * Copyright (c) 2012, 2015, 2016 Eike Stepper (Berlin, Germany) and others. * 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: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.examples.client.offline; import org.eclipse.emf.cdo.examples.client.offline.FailoverMonitorView.OScope.Channel; import org.eclipse.emf.cdo.server.net4j.FailoverMonitor; import org.eclipse.emf.cdo.server.net4j.FailoverMonitor.AgentProtocol; import org.eclipse.net4j.signal.SignalScheduledEvent; import org.eclipse.net4j.util.container.ContainerEventAdapter; import org.eclipse.net4j.util.container.IContainer; import org.eclipse.net4j.util.container.IContainerEvent; import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import java.io.ByteArrayInputStream; import java.util.Random; import java.util.SortedMap; import java.util.TreeMap; /** * @author Eike Stepper */ public class FailoverMonitorView extends AbstractView<FailoverMonitor> { public static final String ID = "org.eclipse.emf.cdo.examples.client.offline.FailoverMonitorView"; //$NON-NLS-1$ public FailoverMonitorView() { super(FailoverMonitor.class); } @Override protected void createPane(Composite parent, FailoverMonitor monitor) { final OScope scope = new OScope(parent, SWT.NONE); monitor.addListener(new ContainerEventAdapter<AgentProtocol>() { @Override protected void notifyContainerEvent(IContainerEvent<AgentProtocol> event) { addEvent(event); super.notifyContainerEvent(event); } @Override protected void onAdded(IContainer<AgentProtocol> monitor, final AgentProtocol agent) { final Channel channel = scope.getChannel(agent.getConnectorDescription()); agent.addListener(new IListener() { public void notifyEvent(IEvent event) { if (event instanceof SignalScheduledEvent) { if (!scope.isDisposed()) { scope.getDisplay().asyncExec(new Runnable() { public void run() { if (!scope.isDisposed()) { channel.peak(); } } }); } } } }); } }); } @Override public void setFocus() { } /** * @author Eike Stepper */ public static class OScope extends Canvas implements Runnable, PaintListener, ControlListener { public static final int[] PEAK = { 10, 95, 80, 90, 50, 25, 10, 5 }; private static final String INITIAL = "#INITIAL#"; private static final Random RANDOM = new Random(); private static final int[] BACKGROUND = { 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, 72, 0, 72, 0, 0, 255, 254, 0, 19, 67, 114, 101, 97, 116, 101, 100, 32, 119, 105, 116, 104, 32, 71, 73, 77, 80, 255, 219, 0, 67, 0, 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12, 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15, 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26, 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30, 255, 219, 0, 67, 1, 5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 255, 192, 0, 17, 8, 0, 20, 0, 20, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, 23, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 7, 255, 196, 0, 34, 16, 0, 2, 2, 0, 5, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 2, 20, 33, 52, 84, 17, 115, 145, 178, 209, 97, 255, 196, 0, 23, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 3, 255, 196, 0, 27, 17, 0, 2, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 17, 33, 49, 81, 18, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 225, 37, 87, 118, 245, 59, 79, 217, 140, 212, 60, 24, 60, 226, 250, 83, 110, 196, 74, 10, 205, 211, 133, 245, 141, 180, 155, 122, 106, 255, 0, 78, 77, 187, 88, 4, 164, 237, 96, 204, 5, 89, 168, 120, 48, 121, 197, 244, 10, 223, 5, 233, 240, 148, 170, 238, 222, 167, 105, 251, 48, 9, 237, 20, 182, 137, 64, 6, 140, 255, 217 }; private SortedMap<String, Channel> channels = new TreeMap<String, Channel>(); private Channel[] channelArray; private int width; private int height; private int channelHeight; private boolean resizing; private Color black; private Color white; public OScope(Composite parent, int style) { super(parent, style | SWT.DOUBLE_BUFFERED); black = getDisplay().getSystemColor(SWT.COLOR_BLACK); white = getDisplay().getSystemColor(SWT.COLOR_WHITE); setBackgroundImage(createBackgroundImage()); addPaintListener(this); addControlListener(this); Channel channel = new Channel(INITIAL); channels.put(INITIAL, channel); channelArray = new Channel[] { channel }; run(); } public int getTimerExecMillis() { return 25; } @Override public void dispose() { super.dispose(); } public Channel getChannel(String name) { synchronized (channels) { Channel channel = channels.get(name); if (channel == null) { boolean resize = false; channel = channels.remove(INITIAL); if (channel == null) { channel = new Channel(name); resize = true; } else { channel.setName(name); } channels.put(name, channel); channelArray = channels.values().toArray(new Channel[channels.size()]); if (resize) { getDisplay().syncExec(new Runnable() { public void run() { controlResized(null); } }); } } return channel; } } public void controlMoved(ControlEvent e) { // Do nothing } public void controlResized(ControlEvent e) { try { resizing = true; Point size = getSize(); width = size.x; height = size.y; int count = channelArray.length; if (count == 0) { return; } int oldChannelHeight = channelHeight; if (oldChannelHeight == 0) { oldChannelHeight = 1; } channelHeight = height / (2 * count); int y = channelHeight; for (Channel channel : channelArray) { channel.resize(y, oldChannelHeight); y += channelHeight + channelHeight; } } finally { resizing = false; } } public void paintControl(PaintEvent e) { if (!resizing) { GC gc = e.gc; gc.setAdvanced(true); gc.setAntialias(SWT.ON); for (Channel channel : channelArray) { channel.paint(e.gc); } } } public void run() { if (isDisposed()) { return; } redraw(); getDisplay().timerExec(getTimerExecMillis(), this); } protected Image createBackgroundImage() { byte[] bytes = new byte[BACKGROUND.length]; for (int i = 0; i < BACKGROUND.length; i++) { bytes[i] = (byte)BACKGROUND[i]; } return new Image(getDisplay(), new ByteArrayInputStream(bytes)); } /** * @author Eike Stepper */ public class Channel { private String name; private int[] values; private int[] head; private int headIndex; private boolean headNegate; private int x; private int y; public Channel(String name) { this.name = name; } public String getName() { return name; } public void setHead(int[] head) { headIndex = 0; headNegate = false; this.head = head; } public void peak() { setHead(PEAK); } @Override public String toString() { return "Channel[" + name + "]"; } public void resize(int y, int oldChannelHeight) { int max = 0; int[] newValues = new int[2 * width]; if (values != null) { System.arraycopy(values, 0, newValues, 0, Math.min(values.length, newValues.length)); max = Math.min(values.length / 2, width); } int x = 0; for (; x < max; x++) { int i = 2 * x; newValues[i] = x; newValues[i + 1] = (newValues[i + 1] - this.y) * channelHeight / oldChannelHeight + y; } for (; x < width; x++) { int i = 2 * x; newValues[i] = x; newValues[i + 1] = y; } values = newValues; x = Math.min(x, width); this.y = y; } public void paint(GC gc) { int i = 2 * x + 1; if (values == null || i >= values.length) { return; } int value = getNextValue() * (90 + RANDOM.nextInt(20)) / 100 + RANDOM.nextInt(4) - 2; if (value > 100) { value = 100; } else if (value < -100) { value = -100; } int fx = y + value * channelHeight / 100; values[i] = fx; gc.setForeground(white); gc.setLineWidth(1); gc.drawPolyline(values); gc.setForeground(black); gc.setLineWidth(2); gc.drawRectangle(x, fx, 2, 2); if (++x >= width) { x = 0; } } void setName(String name) { this.name = name; } private int getNextValue() { if (head == null) { return 0; } int value; if (headNegate) { value = -head[headIndex]; if (++headIndex >= head.length) { setHead(null); headNegate = !headNegate; } } else { value = head[headIndex]; } headNegate = !headNegate; return value; } } } }