2012/09/06

An Introduction on HTML5 WebSocket

WebSocket is the new function introduced in html5 and is used to solve the problems in the mutual communications between the browser and the back-end server. With the help of WebSocket technology, the back-end server will be able to send messages to the front-end at any time to ensure the unity of foreground and background, which is out of the question in the tradition stateless HTTP protocol.

The Traditional Server Push
Before the existence of WebSocket, in order to meet the requirement of sending messages to the fore-end from the back-end, some solutions of the existing technologies have been put forward (like ajax, iframe, flashplayer, java applet, etc.) or just make some changes to them to try to satisfy the requirement.

Simple Polling
This is the simplest way. The back-end is asked at intervals to obtain the latest state, which is the easiest to realize, but the effect is awful. It is wasteful and of bad instantaneity, for once there is a renewal in the back-end, the fore-end will know it only after the next polling.

Long Polling
To solve the problems above, long polling came into existence. Polling requests will bock in the back-end, which means that there will be a long connection and returns until a renewal or a time-out appears. In this way, the promptness of the renewals can be ensured. When the fore-end gets the request, it will reestablish the connection to await the notice of renewal from the background.

Other Solutions
Other solutions are about a long connection (like Iframe and htmlfile), which instantly gets information from the back-end or with the aid of the third plug-in (flashPlayer and jre). It uses flash xml socket or java applet socket technology; however, these solutions rely on plug-ins, namely, not pure-html enough. For more traditional server push technology please refer to the article of IBM:
http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

An Introduction on WebSocket
WebSocket is the new technology introduced in html5. It allows the back-end to send texts or binary messages to the fore-end. WebSocket is a new protocol, not belonging to the stateless protocol of http. The protocol name is “ws”, which means that a websocket link address is like this: ws://twaver.com:8080/webSocketServer. “ws”is not http, so the traditional web server does not necessarily support it. Thus WebSocket can run normally only when both the server and the browser support it. But the currently the support is not common and the special web server and the modern browser are needed.

The Support of the Server to WebSocket
Google Chrome browser is the first to support WebSocket, then Safari and Firefox. In addition, Opera and IE of the latest edition (Opera11 and IE10) are also compatible with WebSocket.
The following is the support of the main browsers to WebSocket. Here Opera 11 is not listed, for it has closed the support as default. For more information: http://en.wikipedia.org/wiki/WebSocket and http://caniuse.com/#search=websockets
 


The Main Methods of WebSocket at Client-side
Different browsers have different level of support toward WebSocket. The chapter “Browser support” which is on WebSocket in wiki has introduced that the browser has provided WebSocket class (MozWebSocket class in Firefox), which conforms to the related rule of w3c on WebSocket API. To check whether the browser is compatible with WebSocket or not, you can use the following script code: 

To check whether the browser is compatible with WebSocket or not
window.WebSocket = window.WebSocket || window.MozWebSocket;
if (!window.WebSocket){
    alert("WebSocket not supported by this browser");
    return;
}
Constructor function– WebSocket#constructor(url, optional protocols)
The first parameter is used to ask for the addres;  the second, parameteris optional (the protocol name).
Example:
var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
events – open/message/close/error    
WebSocket#onopen, onmessage, onclose, onerror
When the connection is open, after calling back the method onopen, the event onmessage will be triggered on receiving the messages from the back-end. The method onclose will be called when the back-end is closed. Any error that occur when connecting will be told if calling the method onerror.
Example:
websocket.onmessage = function(evt){
    var data = evt.data;
}
method – send/close
sending mesages – WebSocket#send(data)
closing the connection – WebSocket#close(optional code, optional reason)
Example:
websocket.send(JSON.stringify({action: "node.remove", id: "001"}));
The support of the server to WebSocket
WebSocket is different from http protocol, for the traditional web servers generally don’t support WebSocket, such as the tomcat at the present (tomcat 7.0.26 has supported WebSocket now). Instead, some minority or more active web servers are among the first to support it like jetty and the Node expander of WebSocket which is based on node.js. Generally every programming language has the corresponding server to choose. I have here listed some of them in the following. For more detailed information please refer to:
http://en.wikipedia.org/wiki/Comparison_of_WebSocket_implementations



The programming languages for different servers are different, hence the difference in their concrete implementations. Even if the same language, the implementation class and the interface function are also distinct. For example, though jetty and netty are all based on java language, there is nearly no two implementation classes with the same name. Now taking jetty (http://www.eclipse.org/jetty) as an example, I will introduce the classes and methods related to WebSocket.



WebSocket in jetty
Based on servlet standard, the method doWebSocketConnect(…) has been added into WebSocketServlet to ask to create a corresponding WebSocket instance of the back-end for the client-side.
org.eclipse.jetty.websocket.WebSocketServlet
{
WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
}
The WebSocket class at the back-end is corresponding to that at the client side. It is able to listen to the state-changed events like “open”, “message” and “close”. What’s more, it also involves a “Connection” property, which is used to send messages to the client-side.
org.eclipse.jetty.websocket.WebSocket
org.eclipse.jetty.websocket.WebSocket.OnTextMessage
{
void onOpen(Connection connect);
void onClose(int code, String message);
void onMessage(String message);
}
WebSocket.Connection back-end-connection class which is involved in WebSocket object, is used to send messages to the client-side.
org.eclipse.jetty.websocket.WebSocket.Connection
{
void sendMessage(String message);
}
This article is just an introduction. Later on we will introduce an instance in which WebSocket and TWaver components are combined together.



How to Remind Users to Save Data in TWaver Flex When the Browser Is to Be Closed

Quite a number of people have asked that how can Flex receive the message that reminds users to save their data before the close of the browser. In fact, this is a common question. Here is the show-how.
Though Flex is unable to know when the browser closes, javascript is (window.onbeforeunload). Hence our ideas:
  1. The method is provided in Flex for javascript to call in order to judge whether there is data needed to save when closing the browser. If there is, then the message pops out; otherwise nothing will be done.
  2. <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
                    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex"
                    creationComplete="init();" paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0">
        <mx:Script>
            <![CDATA[
                import mx.controls.Alert;

                import twaver.*;
                import twaver.network.interaction.InteractionEvent;

                private var box:ElementBox = new ElementBox();

                private function init():void {
                    this.initBox();
                    // Add callback, javascript use it to check whether popup message when browser is closing
                    ExternalInterface.addCallback("needSave", needSave);
                    // Enable save location button once location changed
                    network.addInteractionListener(function(e:InteractionEvent):void {
                        if(InteractionEvent.LAZY_MOVE_END == e.kind
                                || InteractionEvent.LIVE_MOVE_END == e.kind) {
                            if(!btnSave.enabled){
                                btnSave.enabled = true;
                            }
                        }
                    });
                }

                private function initBox():void {
                    var from:Node = new Node();
                    from.name = "From";
                    from.location = new Point(100, 200);
                    box.add(from);

                    var to:Node = new Node();
                    to.name = "To";
                    to.location = new Point(400, 500);
                    box.add(to);

                    box.add(new Link(from, to));
                    this.network.elementBox = box;
                }

                private function save():void {
                    saveData();
                    btnSave.enabled = false;
                }

                private function saveData():void {
                    Alert.show("Data saved");
                }

                public function needSave():Boolean {
                    return btnSave.enabled;
                }
            ]]>
        </mx:Script>
        <mx:VBox width="100%" height="100%">
            <mx:HBox width="100%">
                <mx:Button id="btnSave" label="Save Location" click="save()" enabled="false"/>
            </mx:HBox>
            <tw:Network id="network" width="100%" height="100%"/>
        </mx:VBox>
    </mx:Application>
  3. The event window.onbeforeunload is added into the html page to judge whether to pop up the massage according to whether there is data needed to be saved in Flex.
<script>
    window.onbeforeunload = function (evt) {
        var demo = document.demo || window.demo;
        if (demo.needSave()) {
            var message = 'You did not save your data. Do you really want to quit?';
            if (!evt) {
                evt = window.event;
            }
            if (evt) {
                evt.returnValue = message;
            }
            return message;
        }
    }
</script>
 

2012/08/31

The Application of TWaver to the NMS of FTTX Devices

In recent years, with the increasingly high requirements on the bandwidth, optic network equipment has gradually replaced those old transmission mediums and become the mainstream one. Now we will give an introduction on how to apply TWaver to the NMS(Network Management System) of FTTX devices through a demo.

This demo is divided into 3 layers:

  1. The layer MAP
  2. What way this layer applies is the combination of Map and elements, which can present an overview of the current system on the whole.
    The map, in fact, is formed by splicing together a large number of elements, each of which can be managed and selected to operate. In addition, the color of the elements can also be changed to show different states.
    In this demo, we can double-click the blocks (regions) or the elements on them to enter their inner layers. (Here interactions can be customized according to the detailed requirements.)
  3. The layer GIS
  4. What way this method applies is TWaver’s GIS component whose map-accessed function can help you access the maps from outer net quickly and conveniently and the server of the map of self-driving tour from intranet. We use Map World in this example. With the help of it, your device can be exactly positioned on the map with only the longitude and latitude of the device. Surely, detailed information on the device can also be shown on the map through ways like Attachment.
  5. The layer Test
  6. Broken lines are used in most FTTX systems to show the linking relationship. These requirements can be well met by TWaver’s varieties of LinkType. In order to create a good effect, we need to make a little adjustment to the position of the Label of the Link.

2012/08/30

To Make Your Own Title Bar of Flex AIR

Recently, some people have made some requirements on how to use codes to make unable the max and min buttons on the title bar in Flex AIR program. Though the two buttons can be both enabled or made unable in app.xml files, they cannot be switched dynamically because WindowedApplication.maximizable and WindowedApplication.minimizable are read-only. Next we will introduce how to customize our own title bar of AIR program. Let’s first see the final effects:
  1. First hide the original title bar: edit xxx-app.xml, then find the row systemChrome, cancel the explanation, and set the value as none:
  2. <!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
    <systemChrome>none</systemChrome>
  3. Construct the title bar: first fill the background with gradient color; then add “close”, ”minimize”, ”maximize” and the title bar respectively. Fortunately, the method “exit”,”minimize”,”maximize” and “restore” have been provided in WindowedApplication of Flex, hence the simplicity to close, minimize, maximize and restore the window.
  4.   <s:Group width="100%" height="20">
        <s:Rect id="background" left="0" top="0" right="0" bottom="0">
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:entries>
                        <s:GradientEntry color="0xCCCCCC" ratio="0" alpha="1"/>
                        <s:GradientEntry color="0x999999" ratio=".66" alpha="1"/>
                    </s:entries>
                </s:LinearGradient>
            </s:fill>
        </s:Rect>
        <s:Image source="close.png" click="exit()" width="20" y="2"/>
        <s:Image source="min.png" click="minimize()" width="20" x="20" y="2"/>
        <s:Image source="max.png" click="handleMax()" width="20" x="40" y="2"/>
        <s:Label id="_titlebar" text="TestAir" left="60" top="0" right="0" bottom="0" textAlign="center" verticalAlign="middle"/>
    </s:Group>
  5. Add listener to the title so as to move the window when dragging the title. Here we have taken a roundabout course. (The listener is used to listen to the mouse-move events to calculate the distance that the mouse has moved to change x and y position. The window is kept shaking when the mouse is moving.) Since there is a method startMove in nativeWindow of WindowedApplication, the window can be moved as long as this method is called in mouse-down event.
this.addEventListener(Event.ADDED_TO_STAGE, function(e:*):void {
    _titlebar.addEventListener(MouseEvent.MOUSE_DOWN, function (e:MouseEvent):void {
        nativeWindow.startMove();
    });
});
 
 

2012/08/24

The Application of Modularized TWaver Flex Application

The more widely-used Flex is; the more problems will thus rise:
  1. The size of SWF files will become larger;
  2. It will cost longer time to download SWF files;
  3. If there are several Flex applications, then how to multiplex the same codes (including Flex Framework, customized component library and third-party packages, such as TWaver Flex );
  4. Users need to download the new SWF file again after every update;
  5. How to add the new functions without revising and editing the old SWF files.
How to solve these problems?
Firstly, Runtime Shared Libraries can be used. The Flex Framework, the customized component libraries and the third-party packages are all separate swf files. The codes for them will not be included in Flex application programs, so:
  1. Just update the codes for new users’ programs, instead of updating Flex Framework, customized component libraries and third-party packages after the program is revised;
  2. RSL is buffered by the browser: it is not necessary to download RSLagain after updating the user program;
  3. There is no need to download repeatedly since a RSL can be shared by several Flex applications.
Secondly,modularity can be used.Function modules are divided into different SWF files, so:
  1. Able to update a specified function module separately;
  2. Able to dynamically add modules.
However, the problems will not be solved if just use RSL or Modularity:
  1. The function modules cannot be divided and they also cannot be loaded dynamically if only RSL is used.
  2. The shared Flex Framework, customized component libraries and the third-party package used in all modules are not able to be deleted if only using Modularity, hence the oversize of the sub-modular files.
In this article we will introduce the combination of RSL and Modularity in detail and help you to make modularized, extensible and powerful TWaver Flex application.
To begin with,in order to avoid the high coupling between the main program and the sub-module, we need to create a Flex Library project which has defined all public classes used in sub-modules and the interface of the communication between the sub-modules and the main program.
  1. IModule interface: the sub-module’s realization of this interface is used to show the name of it in the main program (get title), and call back it after the loading of it is finished (ready).
  2. package demo {
        public interface IModule {
            function get title():String;
            function ready(app:IApplication):void;
        }
    }
     
  3. IApplication interface is realized by the main program, used in the interaction between the sub-module and the main program. Now there is no method in this interface. You can add your own methods according to your requirement.
  4. package demo {
        public interface IApplication {

        }
    }
     
In addition, Library project has also customized the Network components and Node to the use of sub-module. But you need to pay attention to the fact that the link type of framework linkage and twaver.swc must be changed to external in the project selections in order to reduce the size of the sec files generated by Library project.
Next, create the main program--Flex project which uses the Library project above and twaver.swc. Its function is to show Tree on the left side and after clicking the tree node, the corresponding sub-module will be loaded on the right side.
  1. Add the component’s label and initialize the interface. First is HDivededBox component. Left: FastTree, right: VBox. (In VBox) up: Label, showing the name of the sub-module, bottom: sub-module container.
  2. <mx:HDividedBox width="100%" height="100%">
        <tw:FastTree id="tree" width="300" height="100%"/>
        <mx:VBox width="100%" height="100%">
            <mx:Label id="title" width="100%" textAlign="center"/>
            <mx:Canvas id="content" width="100%" height="100%"/>
        </mx:VBox>
    </mx:HDividedBox>
     
  3. Initialize the tree node: in order to succeed to dynamically add modules, here we read the information on modules from xml files. If you need to add new modules, you can change xml files directly instead of changing the main program.
  4. Xml file includes the name and the url of the module: 
      <modules>
          <module name="PSTN" url="ModulePSTN.swf"/>
          <module name="Alarm" url="ModuleAlarm.swf"/>
      </modules>
    Load codes for xml files as follows to save url information into the “client” property and to use this url to load the sub-module when clicking this node. 
    private function initTreeBox():void {
        var httpService:HTTPService = new HTTPService();
        httpService.resultFormat = "e4x";
        httpService.addEventListener(ResultEvent.RESULT, this.addModules);
        httpService.url = "modules.xml";
        httpService.send();
    }

    private function addModules(e:ResultEvent):void {
        for each(var module:XML in e.result.module){
            this.addModule(module.@name, module.@url);
        }
    }

    private function addModule(name:String, url:String):void {
        var data:Data = new Data();
        data.name = name;
        data.setClient("url", url);
        this.tree.dataBox.add(data);
    }

  5. Add the listener which listens to the selection of the nodes in Tree: When a node in Tree is selected, we first judge whether the corresponding sub-module has been loaded or not. If not, it will be dynamically loaded, and then saved into the “client” property; if it has already been loaded, the sub-module previously saved in the “client” property will be directly added into the container at the right side:
  6. this.tree.selectionModel.addSelectionChangeListener(this.handleSelectionChangeEvent);

    private function handleSelectionChangeEvent(e:SelectionChangeEvent):void {
        var selectedData:IData = this.tree.selectionModel.lastData;
        if(selectedData){
            var moduleLoader:ModuleLoader = selectedData.getClient("module");
            if(moduleLoader){
                this.content.removeAllChildren();
                this.content.addChild(moduleLoader);
                this.title.text = (moduleLoader.child as IModule).title;
            }else{
                moduleLoader = new ModuleLoader();
                moduleLoader.percentWidth = 100;
                moduleLoader.percentHeight = 100;
                moduleLoader.addEventListener(ModuleEvent.READY, this.moduleReady);
                moduleLoader.loadModule(selectedData.getClient("url"));
                selectedData.setClient("module", moduleLoader);
            }
        }
    }

    private function moduleReady(event:ModuleEvent):void {
        var moduleLoader:ModuleLoader = event.target as ModuleLoader;
        var module:IModule = moduleLoader.child as IModule;
        content.removeAllChildren();
        content.addChild(moduleLoader);
        this.title.text = module.title;
        module.ready(this);
    }
     
Notice that when importing twaver.swc and Library project, twaver.swc must be above the Library project, or it will complains: couldn’t find the class twaver.network::Network. In addition, the link type of framework linkage, twaver.swc and the Library project just mentioned above must be Runtime Shared Library(RSL). Please see the third step in the following for how to set in detail:

At last, please notice that when importing twaver.swc and Library project, twaver.swc must be above the Library project, or it will complains: couldn’t find the class twaver.network::Network. In addition, the link type of framework linkage, twaver.swc and the Library project just mentioned above must be Runtime Shared Library(RSL). Please see the third step in the following for how to set in detail:
Moreover, the compiling path of the sub-module can be changed to the catalogue of bin-debug of the main-program project. In this way, you don’t need to replicate the sub-module swf to the bin-debut of the main program after changing the sub-module every time.

Furthermore, it is unnecessary to generate HTML Wrapper, for the sub-module cannot be run independently. It can only be loaded from the main program. So there is no need to generate the html file which wraps sub-modules:
Finally, please notice that when changing link type to RSL, the OK button cannot be clicked only after you add RSL path, or you can just click “Add” button.
The codes for the sub-module is quite simple, but you need to pay attention that the root tab of mxml files must be changed to Module and the IModule interface must be realized.
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:demo="http://www.demo.com/demo" implements="demo.IModule"
    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex"
    creationComplete="init()" width="100%" height="100%">
<mx:Script>
<![CDATA[
    import demo.*;

    private var _app:IApplication = null;

    public function get title():String {
        return "Alarm Demo";
    }

    public function ready(app:IApplication):void {
        this._app = app;
    }
]]>
</mx:Script>
</mx:Module>
We can last test the program after the procedures above:
Now let’s see the size of the package including the sub-modules, Library project and the main program. (It is really small-sized for the sub-module only is only less than 50K.)
Please refer to the official document of Adobe for more information on Module and RSL:
Creating Modular Applications
Using Runtime Shared Libraries

2012/08/23

To Draw Lines on the Nodes of Tree in Twaver

Recently, someone has asked how to add the guide lines to the nodes of Tree in TWaver on TWaver’s forum. This will be added into the new edition of TWaver Flex. Let’s first see the effects:
The properties in the following have been added in the new FastTree:
  1. lineStyle. Default: Consts.TREE_LINE_STYLE_NONE. Options: Consts.TREE_LINE_STYLE_NONE, Consts.TREE_LINE_STYLE_DOTTED and Consts.TREE_LINE_STYLE_SOLID.
  2. lineThickness. Default: 1
  3. lineColor. Default: 0×808080
  4. lineAlpha. Default: 1.
The following example can adjust these properties:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:tw="http://www.servasoftware.com/2009/twaver/flex"
                applicationComplete="init()" backgroundColor="#FFFFFF">
    <mx:Script>
        <![CDATA[
            import twaver.*;

            private var box:DataBox = new DataBox();

            private function init():void {
                // Init data
                addDatas(null, 2, 2);

                // Set icon function
                tree.iconFunction = function(data:IData):String{
                    if (showIcon.selected == true){
                        return data.icon;
                    }
                    return null;
                };
                tree.dataBox = box;
                tree.expandAll();
            }

            // Init data
            private function addDatas(parent:IData, level:int, count:int):void {
                for(var i:int = 0; i < count; i++) {
                    var data:IData = new Data();
                    data.name = "Data" + level + "-" + count;
                    data.parent = parent;
                    box.add(data);
                    if(level > 0) {
                        addDatas(data, level-1, count);
                    }
                }
            }

            // Change line style
            private function handleLineStyleChanged():void {
                tree.lineStyle = String(lineStyle.selectedItem);
            }

            // Change line thickness
            private function handleLineThicknessChanged():void {
                tree.lineThickness = lineThickness.value;
            }

            // Change line color
            private function handleLineColorChanged():void {
                tree.lineColor = lineColor.selectedColor;
            }

            // Change line alpha
            private function handleLineAlphaChanged():void {
                tree.lineAlpha = lineAlpha.value;
            }

            // Change check mode
            private function handleCheckModeChanged():void {
                tree.checkMode = showCheckbox.selected ?
                    Consts.CHECK_DESCENDANT_ANCESTOR : null;
            }
        ]]>
    </mx:Script>
    <mx:HBox width="100%" height="100%">
        <tw:FastTree id="tree" width="200" height="100%"
                     lineStyle="{Consts.TREE_LINE_STYLE_DOTTED}"/>
        <mx:Form width="100%" height="100%">
            <mx:FormItem label="Line Style">
                <mx:ComboBox id="lineStyle" selectedIndex="1"
                             change="handleLineStyleChanged()">
                    <mx:dataProvider>
                        <mx:String>{Consts.TREE_LINE_STYLE_NONE}</mx:String>
                        <mx:String>{Consts.TREE_LINE_STYLE_DOTTED}</mx:String>
                        <mx:String>{Consts.TREE_LINE_STYLE_SOLID}</mx:String>
                    </mx:dataProvider>
                </mx:ComboBox>
            </mx:FormItem>
            <mx:FormItem label="Line Thickness">
                <mx:NumericStepper id="lineThickness"
                                   minimum="0" maximum="5" stepSize="1" value="1"
                                   change="handleLineThicknessChanged()"/>
            </mx:FormItem>
            <mx:FormItem label="Line Color">
                <mx:ColorPicker id="lineColor" selectedColor="#808080"
                                change="handleLineColorChanged()"/>
            </mx:FormItem>
            <mx:FormItem label="Line Alpha">
                <mx:NumericStepper id="lineAlpha" minimum="0"
                                   maximum="1" stepSize="0.1" value="1"
                                   change="handleLineAlphaChanged()"/>
            </mx:FormItem>
            <mx:FormItem label="Show Checkbox">
                <mx:CheckBox id="showCheckbox"
                             label="Show Checkbox" selected="false"
                             change="handleCheckModeChanged()"/>
            </mx:FormItem>
            <mx:FormItem label="Show Icon">
                <mx:CheckBox id="showIcon" label="Show Icon" selected="true"
                             change="tree.invalidateModel()"/>
            </mx:FormItem>
        </mx:Form>
    </mx:HBox>
</mx:Application>

2012/08/17

TWaver’s Application in Power-system

As a professional graphical interface component, TWaver can be used in all walks of life. In this article we are going to give an introduction on how to draw an interface of power system with the help of TWaver. Now let's first see several images on power system:
the 1st wiring layout of a specified substation:

the 2nd wiring layout of a specified substation:

Of course the images above are what I have found on the Net. Then is TWaver able to realize it? The answer is absolutely. Let’s first analyze it: in a power system these interfaces are composed of many electric elements, such as transformer, disconnecting link, switch, earthed switch, capacitor and generator, etc.

Then let’s see how to extend these electric elements: first comes a simple disconnecting link which has two statuses: open and closed. So we can define a class for it which inherits from ResizableNode and adding turnOn to control its switch-status.

public class Switch extends ResizableNode{
    private boolean turnOn = true;

    public Switch() {
        super();
        init();
    }

    public Switch(Object id){
        super(id);
        init();
    }

    private void init(){
        this.setSize(5,30);
        this.putBorderColor(Color.black);
        this.putBorderInsets(12);
    }

     public String getUIClassID() {
        return SwitchUI.class.getName();
    }

     public boolean isTurnOn() {
        return turnOn;
    }

     public void setTurnOn(boolean turnOn) {
        if(this.turnOn != turnOn){
            boolean oldValue = this.turnOn;
            this.turnOn = turnOn;
            this.firePropertyChange("turnOn", oldValue, this.turnOn);
        }
    }
}
Next, we need to draw the shape of the swich according to the property of turnOn on UI. Here is the major paint method:

public void paintBody(Graphics2D g) {
    g.setStroke(TWaverConst.DOUBLE_WIDTH_STROKE);

    Switch switzh = (Switch)this.getElement();

    //get position
    final Point location = switzh.getLocation();
    final Dimension size = switzh.getSize();
    boolean trunOn = switzh.isTurnOn();
    final int x = location.x;
    final int y = location.y;
    final int width = size.width;
    final int height = size.height;

    //draw color frame
    g.setColor(new Color(170,0,225));
    g.drawOval(x, y, width, width);
    g.drawOval(x, y+height-width, width, width);

    if(trunOn){
        g.drawLine(x+width/2, y+height-width, x+height/2,y+width);
    }else{
        g.drawLine(x+width/2, y+height-width, x+width/2, y+width);
    }
}
Let’s see the extended effect through main

This is a simple disconnecting-link element, so it is quite easy to extend it. The next one is a little more complicated- disconnecting link. Same as disconnecting link, it also has two statuses.
Since in the example above we have set several different directions to the earthed switches, we can also do the same to disconnecting link. Let’s see how to draw the earthed switch:

public void paintBody(Graphics2D g) {
    g.setStroke(TWaverConst.BASIC_STROKE);
    g.setColor(Color.black)

    EarthingSwitch earthingSwitch = (EarthingSwitch)this.getElement();
    final Point location = earthingSwitch.getLocation();

    boolean turnOn = earthingSwitch.isTurnOn();
    int position = earthingSwitch.getSwitchPosition();
    final int x = location.x;
    final int y = location.y;
    int width = earthingSwitch.getWidth();
    int height = earthingSwitch.getHeight();
    //draw body
    if(turnOn){
        if(position == Utils.SWITCH_POSITION_TOP){
            g.drawLine(x+width/4, y, x+width/4*3, y);
            g.drawLine(x+width/6, y+2, x+width/6*5, y+2);
            g.drawLine(x, y+4, x+width, y+4);
            g.drawLine(x+width/2, y+4, x+width/2, y+7);
            g.drawLine(x+width/2, y+7, x+width, y+height-10);
            g.drawLine(x+width/2, y+height-3, x+width/2, y+height);
        }else if(position == Utils.SWITCH_POSITION_LEFT){
            //transfer width and height
            int middle = width;
            width = height;
            height = middle;

            g.drawLine(x,y+height/4, x, y+height/4*3);
            g.drawLine(x+2, y+height/6, x+2, y+height/6*5);
            g.drawLine(x+4, y, x+4, y+height);
            g.drawLine(x+4, y+height/2, x+7, y+height/2);
            g.drawLine(x+7, y+height/2, x+width-10, y);
            g.drawLine(x+width-3, y+height/2, x+width, y+height/2);
        }else if(position == Utils.SWITCH_POSITION_BOTTOM){
            g.drawLine(x+width/4, y+height, x+width/4*3, y+height);
            g.drawLine(x+width/6, y+height-2, x+width/6*5, y+height-2);
            g.drawLine(x, y+height-4, x+width, y+height-4);
            g.drawLine(x+width/2, y+height-4, x+width/2, y+height-7);
            g.drawLine(x+width/2, y+height-7, x, y+10);
            g.drawLine(x+width/2, y+3, x+width/2, y);
        }else if(position == Utils.SWITCH_POSITION_RIGHT){
            //transfer width and height
            int middle = width;
            width = height;
            height = middle;

            g.drawLine(x+width,y+height/4, x+width, y+height/4*3);
            g.drawLine(x+width-2, y+height/6, x+width-2, y+height/6*5);
            g.drawLine(x+width-4, y, x+width-4, y+height);
            g.drawLine(x+width-4, y+height/2, x+width-7, y+height/2);
            g.drawLine(x+width-7, y+height/2, x+10, y);
            g.drawLine(x+3, y+height/2, x, y+height/2);
        }
    }else{
        if(position == Utils.SWITCH_POSITION_TOP){
            g.drawLine(x+width/4, y, x+width/4*3, y);
            g.drawLine(x+width/6, y+2, x+width/6*5, y+2);
            g.drawLine(x, y+4, x+width, y+4);
            g.drawLine(x+width/2, y+4, x+width/2, y+7);
            g.drawLine(x+width/2, y+7, x+width/2, y+height-3);
            g.drawLine(x+width/2, y+height-3, x+width/2, y+height);
        }else if(position == Utils.SWITCH_POSITION_LEFT){
            //transfer width and height
            int middle = width;
            width = height;
            height = middle;

            g.drawLine(x,y+height/4, x, y+height/4*3);
            g.drawLine(x+2, y+height/6, x+2, y+height/6*5);
            g.drawLine(x+4, y, x+4, y+height);
            g.drawLine(x+4, y+height/2, x+7, y+height/2);
            g.drawLine(x+7, y+height/2, x+width-3, y+height/2);
            g.drawLine(x+width-3, y+height/2, x+width, y+height/2);
        }else if(position == Utils.SWITCH_POSITION_BOTTOM){
            g.drawLine(x+width/4, y+height, x+width/4*3, y+height);
            g.drawLine(x+width/6, y+height-2, x+width/6*5, y+height-2);
            g.drawLine(x, y+height-4, x+width, y+height-4);
            g.drawLine(x+width/2, y+height-4, x+width/2, y+height-7);
            g.drawLine(x+width/2, y+height-7, x+width/2, y+3);
            g.drawLine(x+width/2, y+3, x+width/2, y);
        }else if(position == Utils.SWITCH_POSITION_RIGHT){
            //transfer width and height
            int middle = width;
            width = height;
            height = middle;

            g.drawLine(x+width,y+height/4, x+width, y+height/4*3);
            g.drawLine(x+width-2, y+height/6, x+width-2, y+height/6*5);
            g.drawLine(x+width-4, y, x+width-4, y+height);
            g.drawLine(x+width-4, y+height/2, x+width-7, y+height/2);
            g.drawLine(x+width-7, y+height/2, x+3, y+height/2);
            g.drawLine(x+3, y+height/2, x, y+height/2);
        }
    }
}
There are some other elements, such as switch, generator and transformer. These can be easily configured by customDraw provided by TWaver. These are very easy so here the codes are omitted. See the image directly:
With the basic elements, it seems not so complicated to draw a power-system interface. We have drawn a power-system image with tools in TWaver through the extended electric elements above.
At last, interactions can be added on the interface, for example: you can double-click to open or close the switch of disconnecting link and then add your own business. A simple power-system is thus finished.

network.addElementDoubleClickedActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        Element ele = network.getSelectionModel().lastElement();
        if(ele instanceof Switch){
            boolean turnOn = ((Switch)ele).isTurnOn();
            ((Switch)ele).setTurnOn(!turnOn);
        }
    }
});
In this way, the first two images of a substation can also be easily made. The following is another power-interface image made by TWaver.