/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package scouter.client.views;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.csstudio.swt.xygraph.util.XYGraphMediaFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
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.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.part.ViewPart;
import scouter.client.constants.MenuStr;
import scouter.client.context.actions.OpenCxtmenuActiveServiceListAction;
import scouter.client.model.AgentModelThread;
import scouter.client.model.AgentObject;
import scouter.client.model.RefreshThread;
import scouter.client.threads.ObjectSelectManager;
import scouter.client.util.ChartUtil;
import scouter.client.util.ColorUtil;
import scouter.client.util.ExUtil;
import scouter.client.util.TimeUtil;
import scouter.client.util.UIUtil;
import scouter.client.xlog.views.XLogViewPainter;
public abstract class VerticalEQCommonView extends ViewPart implements RefreshThread.Refreshable {
private static final int MARGIN = 10;
private static final int BAR_HEIGHT = 7;
private static final int BAR_PADDING_WIDTH = 2;
private static final int AXIS_PADDING = 16;
private static final int REFRESH_INTERVAL = 200;
private static final int FETCH_INTERVAL = 2000;
public static double CYCLE_INTERVAL = 1000;
private static int MINIMUM_UNIT_WIDTH = 20;
protected RefreshThread thread;
protected Canvas canvas;
private long lastFetchedTime;
protected Set<EqData> valueSet = new TreeSet<EqData>(new EqDataComparator());
private int unitWidth;
private Image ibuffer;
private ScrolledComposite scroll;
int winYSize;
Rectangle area;
public void createPartControl(final Composite parent) {
parent.setBackground(ColorUtil.getInstance().getColor(SWT.COLOR_WHITE));
parent.setBackgroundMode(SWT.INHERIT_FORCE);
parent.setLayout(UIUtil.formLayout(0, 0));
GridLayout layout = new GridLayout(1, true);
layout.marginHeight = MARGIN;
layout.marginWidth = MARGIN;
scroll = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
scroll.setLayoutData(UIUtil.formData(0, 0, 0, 0, 100, 0, 100, 0));
canvas = new Canvas(scroll, SWT.DOUBLE_BUFFERED);
canvas.setLayout(layout);
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
try {
area = canvas.getClientArea();
winYSize = parent.getSize().y;
drawEQImage(e.gc);
} catch (Throwable t) {}
}
});
canvas.addMouseListener(new MouseAdapter() {
public void mouseDoubleClick(MouseEvent e) {
if (unitWidth == 0 || datas == null) {
return;
}
if (e.x <= AXIS_PADDING) {
return;
}
int index = (e.x - AXIS_PADDING) / unitWidth;
if (datas.length < index + 1 || datas[index].isAlive == false) {
return;
}
AgentObject agent = AgentModelThread.getInstance().getAgentObject(datas[index].objHash);
new OpenCxtmenuActiveServiceListAction(getSite().getWorkbenchWindow(), MenuStr.ACTIVE_SERVICE_LIST, datas[index].objHash, agent.getObjType(), agent.getServerId()).run();
}
});
scroll.setContent(canvas);
scroll.setExpandVertical(true);
scroll.setExpandHorizontal(true);
scroll.addListener(SWT.MouseWheel, new Listener() {
public void handleEvent(Event event) {
Point origin = scroll.getOrigin();
origin.y += (event.count * -1) * 10;
scroll.setOrigin(origin);
}
});
//canvas.setToolTipText("Double click to list active services");
thread = new RefreshThread(this, REFRESH_INTERVAL);
thread.start();
}
static Font verdana12Bold = new Font(null, "Verdana", 12, SWT.BOLD);
static Font verdana10Bold = new Font(null, "Verdana", 10, SWT.NORMAL);
static Font verdana10Italic = new Font(null, "Verdana", 10, SWT.ITALIC);
static Font verdana7 = new Font(null, "Verdana", 7, SWT.NORMAL);
long lastDrawTime;
private void drawEQImage(GC gc) {
if (ibuffer != null) {
if (AXIS_PADDING + (MINIMUM_UNIT_WIDTH * size) > winYSize) {
scroll.setMinSize(canvas.computeSize(SWT.DEFAULT, AXIS_PADDING + (MINIMUM_UNIT_WIDTH * size)));
} else {
scroll.setMinSize(canvas.computeSize(SWT.DEFAULT, winYSize));
}
if (ibuffer.isDisposed() == false) {
gc.drawImage(ibuffer, 0, 0);
}
}
}
boolean onGoing = false;
int size;
EqData[] datas;
ObjectSelectManager objSelMgr = ObjectSelectManager.getInstance();
static Color black = ColorUtil.getInstance().getColor(SWT.COLOR_BLACK);
static Color red = ColorUtil.getInstance().getColor(SWT.COLOR_RED);
static Color dark_gary = ColorUtil.getInstance().getColor(SWT.COLOR_GRAY);
private Map<String, Image> objectNameImageMap = new HashMap<String, Image>();
protected void buildBars() {
long now = TimeUtil.getCurrentTime();
if ((now - lastDrawTime) < REFRESH_INTERVAL || area == null) {
return;
}
if(onGoing)
return;
onGoing = true;
int width = area.width > 100 ? area.width : 100;
int height = area.height > 50 ? area.height : 50;
Image img = new Image(null, width, height);
GC gc = new GC(img);
try {
lastDrawTime = now;
double maxValue = 0;
ArrayList<EqData> list = new ArrayList<EqData>();
synchronized (valueSet) {
for (EqData e : valueSet) {
if (objSelMgr.isUnselectedObject(e.objHash)) {
continue;
}
double max = ChartUtil.getEqMaxValue(e.asd.act1 + e.asd.act2 + e.asd.act3);
if (max > maxValue) {
maxValue = max;
}
list.add(e);
}
}
size = list.size();
if (size < 1) {
datas = new EqData[0];
return;
}
datas = list.toArray(new EqData[size]);
unitWidth = (width - AXIS_PADDING) / size;
if (unitWidth < MINIMUM_UNIT_WIDTH) {
unitWidth = MINIMUM_UNIT_WIDTH;
}
// draw horizontal line
gc.setForeground(XLogViewPainter.color_grid_narrow);
gc.setLineStyle(SWT.LINE_DOT);
for (int i = AXIS_PADDING + unitWidth; i <= width - unitWidth; i = i + unitWidth) {
gc.drawLine(i, 0, i, height);
}
// draw axis line
gc.setForeground(black);
gc.setLineStyle(SWT.LINE_SOLID);
int verticalLineX = 6;
int verticalLineY = 6;
gc.drawLine(AXIS_PADDING, verticalLineY, AXIS_PADDING, height - verticalLineY);
gc.drawLine(AXIS_PADDING, height - verticalLineY, width, height - verticalLineY);
int groundWidth = area.width - verticalLineX;
int barSpace = height - verticalLineY - (3 * BAR_HEIGHT);
int imgWidth = unitWidth - (BAR_PADDING_WIDTH * 2);
int mod = (int) (TimeUtil.getCurrentTime() % CYCLE_INTERVAL);
for (int i = 0; i < datas.length; i++) {
// draw objName
String objName = datas[i].displayName;
gc.setForeground(dark_gary);
gc.setFont(verdana10Italic);
int strWidth = gc.stringExtent(objName).x;
while (groundWidth <= (strWidth+5)) {
objName = objName.substring(1);
strWidth = gc.stringExtent(objName).x;
}
int x = (unitWidth * (i + 1)) - 1;
int y = verticalLineY;
if (objectNameImageMap.get(objName) == null) {
//objectNameImageMap.put(objName, SingleSourceHelper.createVerticalTextImage(objName, gc.getFont(), dark_gary.getRGB(), false));
objectNameImageMap.put(objName, createVerticalTextImage(objName, gc.stringExtent(objName).y, gc.stringExtent(objName).x, gc.getFont(), dark_gary.getRGB(), false));
}
gc.drawImage(objectNameImageMap.get(objName), x, y);
if (datas[i].isAlive == false) {
gc.setForeground(dark_gary);
gc.setLineWidth(2);
gc.drawLine(x + (gc.stringExtent(objName).y / 2), y - 1, x + (gc.stringExtent(objName).y / 2), y + gc.stringExtent(objName).x + 1);
}
gc.setLineWidth(1);
ActiveSpeedData asd = datas[i].asd;
long total = asd.act1 + asd.act2 + asd.act3;
double reach = barSpace * (total / maxValue);
int barY = height - verticalLineY - 8;
if (total > 0) {
try {
// distribute bars to 3 types
int noOfBars = (int) reach / BAR_HEIGHT;
int noOfAct1 = (int) (noOfBars * ((double)asd.act1 / total));
int noOfAct2 = (int) (noOfBars * ((double)asd.act2 / total));
int noOfAct3 = (int) (noOfBars * ((double)asd.act3 / total));
int sediments = noOfBars - (noOfAct1 + noOfAct2 + noOfAct3);
while (sediments >= 0) {
if (asd.act3 > 0) {
noOfAct3++;
sediments--;
}
if (sediments >= 0 && asd.act2 > 0) {
noOfAct2++;
sediments--;
}
if (sediments >= 0 && asd.act1 > 0) {
noOfAct1++;
sediments--;
}
}
int barX = AXIS_PADDING + ((unitWidth * i) + BAR_PADDING_WIDTH);
Color lastColor = null;
for (int j = 0; j < noOfAct3; j++) {
// draw red bar
drawNemo(gc, ColorUtil.getInstance().ac3, barX + 1, barY + 1, imgWidth - 2, BAR_HEIGHT - 2);
barY -= BAR_HEIGHT;
lastColor = ColorUtil.getInstance().ac3;
}
for (int j = 0; j < noOfAct2; j++) {
// draw yellow bar
drawNemo(gc, ColorUtil.getInstance().ac2, barX + 1, barY + 1, imgWidth - 2, BAR_HEIGHT - 2);
barY -= BAR_HEIGHT;
lastColor = ColorUtil.getInstance().ac2;
}
for (int j = 0; j < noOfAct1; j++) {
// draw blue bar
drawNemo(gc, ColorUtil.getInstance().ac1, barX + 1, barY + 1, imgWidth - 2, BAR_HEIGHT - 2);
barY -= BAR_HEIGHT;
lastColor = ColorUtil.getInstance().ac1;
}
// draw tong-tong bar
if (lastColor != null) {
drawNemo(gc, lastColor, barX + 1, barY + 1 - ((int) calculateReach(mod, BAR_HEIGHT * 0.7d)), imgWidth - 2, BAR_HEIGHT - 2);
}
} catch (Throwable th) {
th.printStackTrace();
}
}
// draw count text
if (datas[i].isAlive) {
gc.setFont(verdana10Bold);
gc.setForeground(black);
String v = Long.toString(total);
String all = "(" + Long.toString(asd.act3) + " / " + Long.toString(asd.act2) + " / " + Long.toString(asd.act1) + ")";
int xaxis = AXIS_PADDING + (unitWidth * i) + ((unitWidth - gc.stringExtent(v).x) / 2);
int yaxis = barY - (BAR_HEIGHT * 2) - 4;
if (total > 0 && unitWidth >= 52) {
yaxis -= gc.stringExtent(all).y - 2;
}
gc.drawString(v, xaxis, yaxis, true);
if (total > 0 && unitWidth >= 52) {
yaxis += gc.stringExtent(all).y + 2;
gc.setFont(verdana7);
xaxis = AXIS_PADDING + (unitWidth * i) + ((unitWidth - gc.stringExtent(all).x) / 2) - (Long.toString(total).length() * 2);
v = "(";
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(ColorUtil.getInstance().ac3);
v = Long.toString(asd.act3);
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(black);
v = " / ";
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(ColorUtil.getInstance().ac2);
v = Long.toString(asd.act2);
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(black);
v = " / ";
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(ColorUtil.getInstance().ac1);
v = Long.toString(asd.act1);
gc.drawString(v, xaxis, yaxis, true);
xaxis += gc.stringExtent(v).x + 1;
gc.setForeground(black);
v = ")";
gc.drawString(v, xaxis, yaxis, true);
}
}
}
// draw scale text
gc.setForeground(black);
gc.setFont(verdana7);
int max = (int) maxValue;
String v = Integer.toString(max);
String v2 = Integer.toString(max / 2);
gc.drawString(v, 2, 4, true);
gc.drawString(v2, 2, verticalLineY + ((height - verticalLineY) / 2) - gc.stringExtent(v2).y, true);
gc.drawString("0", 2, height - gc.stringExtent(v).y - 4, true);
} catch (Throwable th) { th.printStackTrace(); }
finally {
gc.dispose();
Image old = ibuffer;
ibuffer = img;
if (old != null) {
old.dispose();
}
onGoing = false;
}
}
private void drawNemo(GC gc, Color background, int x, int y, int width, int height) {
gc.setBackground(background);
gc.fillRectangle(x, y, width, height);
gc.setForeground(black);
gc.drawRectangle(x, y, width, height);
}
public void setFocus() {
scroll.setFocus();
}
private double calculateReach(int mod, double weight) {
return (Math.cos(mod * (Math.PI / (CYCLE_INTERVAL / 2.0)) + Math.PI) + 1) / 2 * weight;
}
public void refresh() {
buildBars();
long now = TimeUtil.getCurrentTime();
if (now >= lastFetchedTime + FETCH_INTERVAL) {
lastFetchedTime = now;
valueSet.clear();
fetch();
}
ExUtil.syncExec(canvas, new Runnable() {
public void run() {
canvas.redraw();
}
});
}
public abstract void fetch();
public void dispose() {
super.dispose();
if (ibuffer != null && ibuffer.isDisposed() == false) {
ibuffer.dispose();
}
if (this.thread != null) {
this.thread.shutdown();
}
}
public static class ActiveSpeedData {
public int act1;
public int act2;
public int act3;
}
public static class EqData {
public int objHash;
public boolean isAlive;
public String displayName;
public ActiveSpeedData asd;
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + objHash;
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EqData other = (EqData) obj;
if (objHash != other.objHash)
return false;
return true;
}
public String toString() {
return "EqData [objHash=" + objHash + ", displayName="
+ displayName + "]";
}
}
public class EqDataComparator implements Comparator<EqData> {
public int compare(EqData o1, EqData o2) {
int comp = o1.displayName.compareTo(o2.displayName);
if (comp == 0) {
return o1.objHash - o2.objHash;
}
return comp;
}
}
private Image createVerticalTextImage(String text, int w, int h, Font font, RGB color, boolean upToDown) {
Image image = new Image(Display.getCurrent(), w, h);
final GC gc = new GC(image);
final Color titleColor = new Color(Display.getCurrent(), color);
RGB transparentRGB = new RGB(240, 240, 240);
gc.setBackground(XYGraphMediaFactory.getInstance().getColor(transparentRGB));
gc.fillRectangle(image.getBounds());
gc.setForeground(titleColor);
gc.setFont(font);
final Transform tr = new Transform(Display.getCurrent());
if (!upToDown) {
tr.translate(0, h);
tr.rotate(-90);
gc.setTransform(tr);
} else {
tr.translate(w, 0);
tr.rotate(90);
gc.setTransform(tr);
}
gc.drawText(text, 0, 0);
tr.dispose();
gc.dispose();
final ImageData imageData = image.getImageData();
image.dispose();
titleColor.dispose();
imageData.transparentPixel = imageData.palette.getPixel(transparentRGB);
image = new Image(Display.getCurrent(), imageData);
return image;
}
}