TWaver中有一个平行四边形的Group对象,适合做上图中的“层”的概念。先如下封装并设置属性:
- package demo;
- import java.awt.Color;
- import twaver.Group;
- import twaver.TWaverConst;
- public class LayerGroup extends Group {
- public LayerGroup() {
- init();
- }
- public LayerGroup(Object id) {
- super(id);
- init();
- }
- private void init() {
- this.setGroupType(TWaverConst.GROUP_TYPE_PARALLELOGRAM);
- this.putGroupAngle(45);
- this.putGroup3D(true);
- this.putGroupDeep(10);
- this.putGroupOutline(false);
- this.putGroupFillColor(Color.green.darker());
- this.putGroupGradient(true);
- this.putGroupGradientFactory(TWaverConst.GRADIENT_LINE_E);
- this.putGroupHandlerVisible(false);
- this.putGroupDoubleClickEnabled(false);
- this.putBorderColor(Color.white);
- this.putBorderInsets(3);
- this.putBorderAntialias(true);
- this.putBorderStroke(TWaverConst.STROKE_SOLID_4);
- this.putBorderVisible(false);
- this.putLabelHighlightable(false);
- this.setEnableAlarmPropagationFromChildren(false);
- }
- }
通过这个简单的封装,再往Group里头放几个节点和连线,显示效果如下:
用Group制作的“层”效果
怎么样,有点意思吧?开头不错,继续改进!再依次排列4个Group,用不同颜色,试试效果:
- createLayer(Color.orange, 50, 0, 10, "7.png", "<html><center>软件<br>业务层</center></html>");
- createLayer(Color.green.darker(),180, 200, 15, "8.png", "<html><center>技术<br>应用层</center></html>");
- createLayer(Color.magenta.darker(),280, 350, 5, "5.png", "<html><center>技术<br>软件层</center></html>");
- createLayer(Color.cyan.darker(),400, 570, 7, "1.png", "<html><center>基础<br>设施层</center></html>");
以上代码封装了创建一个层的函数,给定颜色、坐标位置、内部节点数量、图标、文字等等。上面代码中的HTML风格字符串是为了在TWaver中(好像Swing中也是这样的)显示换行的标签。每一个层作为容器包含了很多不同类型的资源。显示效果如下图:
四层拓扑图显示效果
注意其中的连线有下垂的弯曲效果。这是我以前在做项目封装过的一个TWaver技巧:通过重写twaver的Link的UI类,重新指定path走向实现的。其实也很简单,首先获得link的from点和to点,取值中间点,再把y纵向增加20,把这个点作为quadTo的控制点画曲线即可。对TWaver熟悉的朋友可以看一下这段代码(其实这个效果也是从TWaver Java的demo源代码中学习到的):
- package demo;
- import java.awt.Point;
- import java.awt.geom.GeneralPath;
- import twaver.network.TNetwork;
- import twaver.network.ui.LinkUI;
- public class InnerLinkUI extends LinkUI {
- public InnerLinkUI(TNetwork network, InnerLink link) {
- super(network, link);
- }
- @Override
- public GeneralPath getPath() {
- GeneralPath customPath = new GeneralPath();
- Point p1 = this.getFromPoint();
- Point p2 = this.getToPoint();
- customPath.moveTo(p1.x, p1.y);
- int offset = 20;
- customPath.quadTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2 + offset, p2.x, p2.y);
- return customPath;
- }
- }
用这种link做出的拓扑图比较生动美观。多加几个节点连线就能看出来了:
四层复杂拓扑图显示效果
不过发现平行四边形Group一个问题:当两个Layer叠加后,下面的节点会被完全覆盖,看不见了。用户说:能不能也能看见?(晕,盖住了也要看见。谁让人家是甲方呢?)于是询问TWaver的人,一个哥们说Group有透明属性。于是试了一下,效果不还错:
- this.putGroupOpaque(false);
层的透明与覆盖
下一步,关键了:要增加层与层之间资源的“依赖关系”。例如一个Oracle跑在一台主机上,而Oracle中的一个关键表空间需要重点监控,它决定了上层一个视频点播业务是否能够正常。为了体现这个依赖关系,在跨层的节点中间建立link。这个link和层内部link显示上应当有所区别:
- package demo;
- import java.awt.Color;
- import twaver.Link;
- import twaver.Node;
- import twaver.TWaverConst;
- import twaver.base.OrthogonalLinkDirectionType;
- public class LayerLink extends Link {
- public LayerLink(Node from, Node to) {
- super(from, to);
- init();
- }
- public LayerLink(Object id, Node from, Node to) {
- super(id, from, to);
- init();
- }
- private void init() {
- this.putLink3D(true);
- this.putLinkWidth(4);
- this.putLinkOutlineWidth(0);
- this.putLinkColor(Color.lightGray);
- this.putLinkAntialias(false);
- this.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);
- }
- }
显示出来后,效果并不理想,有点乱。主要是没有“跨层”的立体感。
跨层连线效果
图中跨层的link没有呈现出“穿透层”的感觉,多了以后反而破坏了整个拓扑图的立体感和生动感,需要再改进。最好能够显示“穿层而过”的效果。需求变态么?不弄点猛药还想拿单子么,程序员就是要与各种“不可能”说“不”嘛!经过反复研究和实验,终于做出了一个更好的效果,如下图:
连线的跨层穿透效果
注意观察其中穿层效果,不知大家是否喜欢?
连线的透明穿透
怎么做到的呢?其实也简单,一点就破,我就不点破了吧,卖个关子先。大家可以先猜猜看,源代码里头也能看到答案。接下来,可以增加一些跨层连线了!看看下图效果:
跨层连线的综合效果图
效果还不错吧?销售看过后非常满意,连说有新意。不过还有最后一个很头大的问题:需要显示告警及其传播路线,也就是告警发生后,要从底层一直沿着依赖关系传播到上层。于是开始研究TWaver的AlarmPropagator告警传播器。通过研究发现,其实告警传播也不复杂,主要原理是当告警发生后,它会根据AlarmPropagator的“指示”和定义的规则,沿着一个特定的“路径”进行告警传播。被传播过的地方,会显示一个有告警颜色的外框,标志其告警状态。
但是问题是,TWaver的告警传播器是按照“父子关系”进行传播的。也就是默认情况下,告警总是从孩子传给父亲,一直到没有parent为止。按照这个规则,这个demo中一个节点发生告警后,会传播给平行四边形这个层对象,这显然是没有意义的,不符合我的要求。我们需要告警沿着层的“依赖关系”进行跨层传播。于是重写AlarmPropagator!也不难,调试了几个小时,用一个递归法总算搞定了。代码如下:
- package demo;
- import java.util.Collection;
- import java.util.Iterator;
- import twaver.AlarmSeverity;
- import twaver.Element;
- import twaver.Node;
- public class DemoPropagator {
- public void propagate(Element element) {
- AlarmSeverity severity = element.getAlarmState().getHighestNativeAlarmSeverity();
- if (element instanceof Node) {
- Node node = (Node) element;
- Collection links = node.getAllLinks();
- if (links != null && !links.isEmpty()) {
- Iterator it = links.iterator();
- while (it.hasNext()) {
- Object o = it.next();
- if (o instanceof LayerLink) {
- LayerLink link = (LayerLink) o;
- if (link.getAlarmState().isEmpty()) {
- link.getAlarmState().addAcknowledgedAlarm(severity);
- Node anotherNode = link.getFrom();
- if (anotherNode.getAlarmState().isEmpty()) {
- anotherNode.getAlarmState().addAcknowledgedAlarm(severity);
- if (anotherNode != node) {
- propagate(anotherNode);//这里递归!
- }
- }
- }
- }
- }
- }
- }
- }
- }
这里代码的逻辑主要是判断是不是跨层link,如果是就沿着它进行传播。噢吼!上面代码好像泄露了上面“穿透Layer”的秘密了,呵呵。最后,再来一个“告警模拟器”来模拟随机、随时发生告警,也就是用一个单独的线程在里面sleep然后生成Alarm并发送到拓扑图的节点上。直接上代码:
- package demo;
- import java.util.Iterator;
- import javax.swing.SwingUtilities;
- import twaver.AlarmSeverity;
- import twaver.Element;
- import twaver.TDataBox;
- import twaver.TWaverUtil;
- public class AlarmMocker extends Thread {
- private TDataBox box = null;
- private DemoPropagator propagator = new DemoPropagator();
- public AlarmMocker(TDataBox box) {
- this.box = box;
- }
- @Override
- public void run() {
- while (true) {
- try {
- Thread.sleep(1 * 1000);
- } catch (InterruptedException ex) {
- ex.printStackTrace();
- }
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- if (TWaverUtil.getRandomInt(5) == 1) {
- //clear all alarm and propagation.
- Iterator it = box.iterator();
- while (it.hasNext()) {
- Element e = (Element) it.next();
- e.getAlarmState().clear();
- }
- }
- Element element = box.getElementByID("4." + TWaverUtil.getRandomInt(10));
- if (element != null) {
- element.getAlarmState().addNewAlarm(AlarmSeverity.getRandomSeverity());
- propagator.propagate(element);
- }
- }
- });
- }
- }
- }
告警模拟器把最底层的里面的节点随机产生告警,再随机的清除,模拟现实网络的监控状态。然后运行demo,观察其告警传播的路线是否符合预期,也就是沿着层进行传播。