分享

java拓扑图 twaver

 eeason 2011-07-05

今天心情不错,公司终于签下了一个综合业务监控系统的大单。到底有多大我也不知道,反正连软件带硬件不算小。按照销售的说法,拿下这个项目一个重要的因素就是要提供一个吸引眼球的demo,而我们做的不错!今天和大家分享一下喜悦和经验!

 

这个项目是一个省级电信运营商的综合业务监控系统。公司跟了好长时间了。由于是一个综合的业务监控系统,涉及的管理资源非常多,又要提供大屏幕显 示,所以对系统的呈现效果要求非常高(“政绩工程”么)。前前后后提了很多“无礼”要求,陆续提过的有3D电子地图、3D机房监控、场景仿真、全动画、 Google Earth、全Flash等等....弄的我们晕头转向、焦头烂额。其实很多时候,用户自己也不知道想要什么,反正对厂商的要求就是一个字:“炫”,两个 字“好看”,三个字:“一定好好看!”(不对,好像是四个字哦)。

 

言归正传,项目跟踪过程中,商务始终告诉我们研发一定要做好一件事:如何做好呈现,尤其是综合的业务监控和呈现,这是获得项目的关键,一定要“出 彩”。这个“综合呈现”说起来容易,做起来难。作为省级的电信运营商,内部的各种软硬件系统无数,要监控从上到下、从软到硬,真是烦不胜烦:

  • 基础设施层:主要是网络上的硬件设备,包括交换机、路由器、防火墙、各种主机设备服务器等等;
  • 软件层:这一层主要是主机上面运行的操作系统和各类业务软件系统,例如操作系统(Windows、AIX/HP-UX/LINUX /SOLARIS)、各种中间件(WebLogic、JBoss、IIS、WebSphere等)、数据库(Oracle、Sybase、MySQL) 等;
  • 应用层:这一层是指运行在软件层内部的一些细粒度资源,包括一些关键的软件进程、数据库表空间、业务连接、消息队列等等。这一层数量繁杂、数量众多。不过这些资源的状态直接会影响其上层支撑的业务。
  • 业务层:业务层是最顶层,由以上底层资源所共同支撑起来的面向用户的各种业务。对业务最容易理解的描述就是电信提供给客户的具体“服务”。例如视 频业务、短信业务、WAP业务、专网业务等等。这些业务才是用户最终关心的东西,因为这些业务才是客户的核心资产,是拿来卖钱的最终产品。一旦出问题,将 直接影响money!

此外,还有一大堆机房环境监控的要求,什么配电柜供电、开关状态、UPS、蓄电池、空调、适度温度漏水效仿通风门禁视频.........一大堆。 所以,要对业务进行监控,就必须对业务所支撑的各个底层资源进行监控。另外,还要能够对这些资源的关系进行连接和定义,一旦发生故障和问题,能够从上到下 迅速定位故障起源,在最短时间内发现问题、解决问题。如何呈现这些依赖关系,并对故障和告警进行纵向定位,是软件呈现的一个核心问题,也是用户最关心的一 个问题。

 

用户要求我们先制作一个demo程序,看我们将如何呈现“综合监控”的效果。在充分了解了用户需求之后,经过讨论,我们想做一个完全图形化的分层、跨层的综合监控界面。界面要美观,有动画效果,能够清晰的显示资源依赖关系和告警传播定位。

需要监控和管理的资源

 


 接下来要写代码了。肯定先用我熟悉的TWaver试试。研究了一下,TWaver中有一个平行四边形的Group对象,适合做上图中的“层”的概念。先如下封装并设置属性:

Java代码 复制代码
  1. package demo;   
  2.   
  3. import java.awt.Color;   
  4. import twaver.Group;   
  5. import twaver.TWaverConst;   
  6.   
  7. public class LayerGroup extends Group {   
  8.   
  9.     public LayerGroup() {   
  10.         init();   
  11.     }   
  12.   
  13.     public LayerGroup(Object id) {   
  14.         super(id);   
  15.         init();   
  16.     }   
  17.   
  18.     private void init() {   
  19.         this.setGroupType(TWaverConst.GROUP_TYPE_PARALLELOGRAM);   
  20.         this.putGroupAngle(45);   
  21.   
  22.         this.putGroup3D(true);   
  23.         this.putGroupDeep(10);   
  24.         this.putGroupOutline(false);   
  25.         this.putGroupFillColor(Color.green.darker());   
  26.         this.putGroupGradient(true);   
  27.         this.putGroupGradientFactory(TWaverConst.GRADIENT_LINE_E);   
  28.         this.putGroupHandlerVisible(false);   
  29.         this.putGroupDoubleClickEnabled(false);   
  30.         this.putBorderColor(Color.white);   
  31.         this.putBorderInsets(3);   
  32.         this.putBorderAntialias(true);   
  33.         this.putBorderStroke(TWaverConst.STROKE_SOLID_4);   
  34.         this.putBorderVisible(false);   
  35.         this.putLabelHighlightable(false);   
  36.   
  37.         this.setEnableAlarmPropagationFromChildren(false);   
  38.     }   
  39. }  
Java代码  收藏代码
  1. package demo;  
  2.   
  3. import java.awt.Color;  
  4. import twaver.Group;  
  5. import twaver.TWaverConst;  
  6.   
  7. public class LayerGroup extends Group {  
  8.   
  9.     public LayerGroup() {  
  10.         init();  
  11.     }  
  12.   
  13.     public LayerGroup(Object id) {  
  14.         super(id);  
  15.         init();  
  16.     }  
  17.   
  18.     private void init() {  
  19.         this.setGroupType(TWaverConst.GROUP_TYPE_PARALLELOGRAM);  
  20.         this.putGroupAngle(45);  
  21.   
  22.         this.putGroup3D(true);  
  23.         this.putGroupDeep(10);  
  24.         this.putGroupOutline(false);  
  25.         this.putGroupFillColor(Color.green.darker());  
  26.         this.putGroupGradient(true);  
  27.         this.putGroupGradientFactory(TWaverConst.GRADIENT_LINE_E);  
  28.         this.putGroupHandlerVisible(false);  
  29.         this.putGroupDoubleClickEnabled(false);  
  30.         this.putBorderColor(Color.white);  
  31.         this.putBorderInsets(3);  
  32.         this.putBorderAntialias(true);  
  33.         this.putBorderStroke(TWaverConst.STROKE_SOLID_4);  
  34.         this.putBorderVisible(false);  
  35.         this.putLabelHighlightable(false);  
  36.   
  37.         this.setEnableAlarmPropagationFromChildren(false);  
  38.     }  
  39. }  

 通过这个简单的封装,再往Group里头放几个节点和连线,显示效果如下:

 

用Group制作的“层”效果

 

怎么样,有点意思吧?开头不错,继续改进!再依次排列4个Group,用不同颜色,试试效果:

Java代码 复制代码
  1. createLayer(Color.orange, 50010"7.png""<html><center>软件<br>业务层</center></html>");   
  2. createLayer(Color.green.darker(),18020015"8.png""<html><center>技术<br>应用层</center></html>");   
  3. createLayer(Color.magenta.darker(),2803505"5.png""<html><center>技术<br>软件层</center></html>");   
  4. createLayer(Color.cyan.darker(),4005707"1.png""<html><center>基础<br>设施层</center></html>");  
Java代码  收藏代码
  1. createLayer(Color.orange, 50010"7.png""<html><center>软件<br>业务层</center></html>");  
  2. createLayer(Color.green.darker(),18020015"8.png""<html><center>技术<br>应用层</center></html>");  
  3. createLayer(Color.magenta.darker(),2803505"5.png""<html><center>技术<br>软件层</center></html>");  
  4. createLayer(Color.cyan.darker(),4005707"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源代码中学习到的):

Java代码 复制代码
  1. package demo;   
  2.   
  3. import java.awt.Point;   
  4. import java.awt.geom.GeneralPath;   
  5. import twaver.network.TNetwork;   
  6. import twaver.network.ui.LinkUI;   
  7.   
  8. public class InnerLinkUI extends LinkUI {   
  9.   
  10.     public InnerLinkUI(TNetwork network, InnerLink link) {   
  11.         super(network, link);   
  12.     }   
  13.   
  14.     @Override  
  15.     public GeneralPath getPath() {   
  16.         GeneralPath customPath = new GeneralPath();   
  17.         Point p1 = this.getFromPoint();   
  18.         Point p2 = this.getToPoint();   
  19.         customPath.moveTo(p1.x, p1.y);   
  20.         int offset = 20;   
  21.         customPath.quadTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2 + offset, p2.x, p2.y);   
  22.         return customPath;   
  23.     }   
  24. }  
Java代码  收藏代码
  1. package demo;  
  2.   
  3. import java.awt.Point;  
  4. import java.awt.geom.GeneralPath;  
  5. import twaver.network.TNetwork;  
  6. import twaver.network.ui.LinkUI;  
  7.   
  8. public class InnerLinkUI extends LinkUI {  
  9.   
  10.     public InnerLinkUI(TNetwork network, InnerLink link) {  
  11.         super(network, link);  
  12.     }  
  13.   
  14.     @Override  
  15.     public GeneralPath getPath() {  
  16.         GeneralPath customPath = new GeneralPath();  
  17.         Point p1 = this.getFromPoint();  
  18.         Point p2 = this.getToPoint();  
  19.         customPath.moveTo(p1.x, p1.y);  
  20.         int offset = 20;  
  21.         customPath.quadTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2 + offset, p2.x, p2.y);  
  22.         return customPath;  
  23.     }  
  24. }  

 用这种link做出的拓扑图比较生动美观。多加几个节点连线就能看出来了:

 

四层复杂拓扑图显示效果

 

不过发现平行四边形Group一个问题:当两个Layer叠加后,下面的节点会被完全覆盖,看不见了。用户说:能不能也能看见?(晕,盖住了也要看见。谁让人家是甲方呢?)于是询问TWaver的人,一个哥们说Group有透明属性。于是试了一下,效果不还错:

Java代码 复制代码
  1. this.putGroupOpaque(false);  
Java代码  收藏代码
  1. this.putGroupOpaque(false);  

 

层的透明与覆盖

 

下一步,关键了:要增加层与层之间资源的“依赖关系”。例如一个Oracle跑在一台主机 上,而Oracle中的一个关键表空间需要重点监控,它决定了上层一个视频点播业务是否能够正常。为了体现这个依赖关系,在跨层的节点中间建立link。 这个link和层内部link显示上应当有所区别:

Java代码 复制代码
  1. package demo;   
  2.   
  3. import java.awt.Color;   
  4. import twaver.Link;   
  5. import twaver.Node;   
  6. import twaver.TWaverConst;   
  7. import twaver.base.OrthogonalLinkDirectionType;   
  8.   
  9. public class LayerLink extends Link {   
  10.     public LayerLink(Node from, Node to) {   
  11.         super(from, to);   
  12.         init();   
  13.     }   
  14.   
  15.     public LayerLink(Object id, Node from, Node to) {   
  16.         super(id, from, to);   
  17.         init();   
  18.     }   
  19.   
  20.     private void init() {   
  21.         this.putLink3D(true);   
  22.         this.putLinkWidth(4);   
  23.         this.putLinkOutlineWidth(0);   
  24.         this.putLinkColor(Color.lightGray);   
  25.         this.putLinkAntialias(false);   
  26.         this.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);   
  27.     }   
  28. }  
Java代码  收藏代码
  1. package demo;  
  2.   
  3. import java.awt.Color;  
  4. import twaver.Link;  
  5. import twaver.Node;  
  6. import twaver.TWaverConst;  
  7. import twaver.base.OrthogonalLinkDirectionType;  
  8.   
  9. public class LayerLink extends Link {  
  10.     public LayerLink(Node from, Node to) {  
  11.         super(from, to);  
  12.         init();  
  13.     }  
  14.   
  15.     public LayerLink(Object id, Node from, Node to) {  
  16.         super(id, from, to);  
  17.         init();  
  18.     }  
  19.   
  20.     private void init() {  
  21.         this.putLink3D(true);  
  22.         this.putLinkWidth(4);  
  23.         this.putLinkOutlineWidth(0);  
  24.         this.putLinkColor(Color.lightGray);  
  25.         this.putLinkAntialias(false);  
  26.         this.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);  
  27.     }  
  28. }  

 显示出来后,效果并不理想,有点乱。主要是没有“跨层”的立体感。

 

跨层连线效果

 

图中跨层的link没有呈现出“穿透层”的感觉,多了以后反而破坏了整个拓扑图的立体感和生 动感,需要再改进。最好能够显示“穿层而过”的效果。需求变态么?不弄点猛药还想拿单子么,程序员就是要与各种“不可能”说“不”嘛!经过反复研究和实 验,终于做出了一个更好的效果,如下图:

连线的跨层穿透效果

注意观察其中穿层效果,不知大家是否喜欢?



 

连线的透明穿透

 

怎么做到的呢?其实也简单,一点就破,我就不点破了吧,卖个关子先。大家可以先猜猜看,源代码里头也能看到答案。接下来,可以增加一些跨层连线了!看看下图效果:

 

跨层连线的综合效果图

效果还不错吧?销售看过后非常满意,连说有新意。不过还有最后一个很头大的问题:需要显示告 警及其传播路线,也就是告警发生后,要从底层一直沿着依赖关系传播到上层。于是开始研究TWaver的AlarmPropagator告警传播器。通过研 究发现,其实告警传播也不复杂,主要原理是当告警发生后,它会根据AlarmPropagator的“指示”和定义的规则,沿着一个特定的“路径”进行告 警传播。被传播过的地方,会显示一个有告警颜色的外框,标志其告警状态。

 

但是问题是,TWaver的告警传播器是按照“父子关系”进行传播的。也就是默认情况下,告 警总是从孩子传给父亲,一直到没有parent为止。按照这个规则,这个demo中一个节点发生告警后,会传播给平行四边形这个层对象,这显然是没有意义 的,不符合我的要求。我们需要告警沿着层的“依赖关系”进行跨层传播。于是重写AlarmPropagator!也不难,调试了几个小时,用一个递归法总 算搞定了。代码如下:

Java代码 复制代码
  1. package demo;   
  2.   
  3. import java.util.Collection;   
  4. import java.util.Iterator;   
  5. import twaver.AlarmSeverity;   
  6. import twaver.Element;   
  7. import twaver.Node;   
  8.   
  9. public class DemoPropagator {   
  10.   
  11.     public void propagate(Element element) {   
  12.         AlarmSeverity severity = element.getAlarmState().getHighestNativeAlarmSeverity();   
  13.         if (element instanceof Node) {   
  14.             Node node = (Node) element;   
  15.   
  16.             Collection links = node.getAllLinks();   
  17.             if (links != null && !links.isEmpty()) {   
  18.                 Iterator it = links.iterator();   
  19.                 while (it.hasNext()) {   
  20.                     Object o = it.next();   
  21.                     if (o instanceof LayerLink) {   
  22.                         LayerLink link = (LayerLink) o;   
  23.                         if (link.getAlarmState().isEmpty()) {   
  24.                             link.getAlarmState().addAcknowledgedAlarm(severity);   
  25.   
  26.                             Node anotherNode = link.getFrom();   
  27.   
  28.                             if (anotherNode.getAlarmState().isEmpty()) {   
  29.                                 anotherNode.getAlarmState().addAcknowledgedAlarm(severity);   
  30.                                 if (anotherNode != node) {   
  31.                                     propagate(anotherNode);//这里递归!   
  32.                      }   
  33.                             }   
  34.                         }   
  35.                     }   
  36.                 }   
  37.             }   
  38.         }   
  39.     }   
  40. }  
Java代码  收藏代码
  1. package demo;  
  2.   
  3. import java.util.Collection;  
  4. import java.util.Iterator;  
  5. import twaver.AlarmSeverity;  
  6. import twaver.Element;  
  7. import twaver.Node;  
  8.   
  9. public class DemoPropagator {  
  10.   
  11.     public void propagate(Element element) {  
  12.         AlarmSeverity severity = element.getAlarmState().getHighestNativeAlarmSeverity();  
  13.         if (element instanceof Node) {  
  14.             Node node = (Node) element;  
  15.   
  16.             Collection links = node.getAllLinks();  
  17.             if (links != null && !links.isEmpty()) {  
  18.                 Iterator it = links.iterator();  
  19.                 while (it.hasNext()) {  
  20.                     Object o = it.next();  
  21.                     if (o instanceof LayerLink) {  
  22.                         LayerLink link = (LayerLink) o;  
  23.                         if (link.getAlarmState().isEmpty()) {  
  24.                             link.getAlarmState().addAcknowledgedAlarm(severity);  
  25.   
  26.                             Node anotherNode = link.getFrom();  
  27.   
  28.                             if (anotherNode.getAlarmState().isEmpty()) {  
  29.                                 anotherNode.getAlarmState().addAcknowledgedAlarm(severity);  
  30.                                 if (anotherNode != node) {  
  31.                                     propagate(anotherNode);//这里递归!  
  32.                      }  
  33.                             }  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.         }  
  39.     }  
  40. }  

 

这里代码的逻辑主要是判断是不是跨层link,如果是就沿着它进行传播。噢吼!上面代码好像泄露了上面“穿透Layer”的秘密了,呵呵。最后,再 来一个“告警模拟器”来模拟随机、随时发生告警,也就是用一个单独的线程在里面sleep然后生成Alarm并发送到拓扑图的节点上。直接上代码:

Java代码 复制代码
  1. package demo;   
  2.   
  3. import java.util.Iterator;   
  4. import javax.swing.SwingUtilities;   
  5. import twaver.AlarmSeverity;   
  6. import twaver.Element;   
  7. import twaver.TDataBox;   
  8. import twaver.TWaverUtil;   
  9.   
  10. public class AlarmMocker extends Thread {   
  11.   
  12.     private TDataBox box = null;   
  13.     private DemoPropagator propagator = new DemoPropagator();   
  14.   
  15.     public AlarmMocker(TDataBox box) {   
  16.         this.box = box;   
  17.     }   
  18.   
  19.     @Override  
  20.     public void run() {   
  21.         while (true) {   
  22.             try {   
  23.                 Thread.sleep(1 * 1000);   
  24.             } catch (InterruptedException ex) {   
  25.                 ex.printStackTrace();   
  26.             }   
  27.   
  28.             SwingUtilities.invokeLater(new Runnable() {   
  29.   
  30.                 public void run() {   
  31.   
  32.                     if (TWaverUtil.getRandomInt(5) == 1) {   
  33.                         //clear all alarm and propagation.   
  34.                         Iterator it = box.iterator();   
  35.                         while (it.hasNext()) {   
  36.                             Element e = (Element) it.next();   
  37.                             e.getAlarmState().clear();   
  38.                         }   
  39.                     }   
  40.   
  41.                     Element element = box.getElementByID("4." + TWaverUtil.getRandomInt(10));   
  42.                     if (element != null) {   
  43.                         element.getAlarmState().addNewAlarm(AlarmSeverity.getRandomSeverity());   
  44.                         propagator.propagate(element);   
  45.                     }   
  46.                 }   
  47.             });   
  48.         }   
  49.     }   
  50. }  
Java代码  收藏代码
  1. package demo;  
  2.   
  3. import java.util.Iterator;  
  4. import javax.swing.SwingUtilities;  
  5. import twaver.AlarmSeverity;  
  6. import twaver.Element;  
  7. import twaver.TDataBox;  
  8. import twaver.TWaverUtil;  
  9.   
  10. public class AlarmMocker extends Thread {  
  11.   
  12.     private TDataBox box = null;  
  13.     private DemoPropagator propagator = new DemoPropagator();  
  14.   
  15.     public AlarmMocker(TDataBox box) {  
  16.         this.box = box;  
  17.     }  
  18.   
  19.     @Override  
  20.     public void run() {  
  21.         while (true) {  
  22.             try {  
  23.                 Thread.sleep(1 * 1000);  
  24.             } catch (InterruptedException ex) {  
  25.                 ex.printStackTrace();  
  26.             }  
  27.   
  28.             SwingUtilities.invokeLater(new Runnable() {  
  29.   
  30.                 public void run() {  
  31.   
  32.                     if (TWaverUtil.getRandomInt(5) == 1) {  
  33.                         //clear all alarm and propagation.  
  34.                         Iterator it = box.iterator();  
  35.                         while (it.hasNext()) {  
  36.                             Element e = (Element) it.next();  
  37.                             e.getAlarmState().clear();  
  38.                         }  
  39.                     }  
  40.   
  41.                     Element element = box.getElementByID("4." + TWaverUtil.getRandomInt(10));  
  42.                     if (element != null) {  
  43.                         element.getAlarmState().addNewAlarm(AlarmSeverity.getRandomSeverity());  
  44.                         propagator.propagate(element);  
  45.                     }  
  46.                 }  
  47.             });  
  48.         }  
  49.     }  
  50. }  

 

告警模拟器把最底层的里面的节点随机产生告警,再随机的清除,模拟现实网络的监控状态。然后运行demo,观察其告警传播的路线是否符合预期,也就是沿着层进行传播。

 

注意一个细节:由于上面告警模拟器在一个单独的Thread线程中运行,在产生告警并更改界面时候,需要用 SwingUtilities.invokeLater进行代码封装调用,保证它在Swing线程中执行,避免屏幕和界面“花”或不可预知的显示结果。 唉,谁让Swing是单线程而且是线程不安全呢?这是个古老话题,就不罗嗦了。

 

废话不多说,直接上最终效果图:

 

 

demo最终效果图

 

双击层动画旋转并放大

 

看到告警跨层传播的效果了吗?最后,根据客户的要求,又增加了一些动画效果:双击平行四边形后,平行四边形会动画变成矩形、动画飞到屏幕中间,然后 动画放大内部拓扑图,供用户查看细节;再次双击,平行四边形快速旋转缩小并回到原来位置。demo程序交付并演示后,获得客户高度评价。用我们商务人员的 话来说就是:“我们的demo最出彩!”作为程序员,自己做的东西能为公司创造价值和利润就是最大的肯定和成就感!

 

由于demo掺杂了不少公司的代码,我会花一点时间整理一下,弄出一个干净的demo代码贴出来,请感兴趣的朋友留意。我这里也有可以直接运行的jar包,感兴趣的朋友可以留下邮箱。对了,以上信息仅供技术交流之用,请勿用于其它商业用途。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多