HTML5-based power wiring diagram SCADA application

The concept of Web SCADA has been proposed for many years in the fields of industrial automation such as electric power, oil field gas, water supply network, etc. In the early years, most of the front-end technologies of Web SCADA were based on client-heavy solutions such as Flex, Silverlight and even Applet. Before HTML5 became popular VML and SVG are truly pure web solutions and have many applications. In recent years, with the popularity of HTML5 and the popularization of HTML5 support in mobile terminals and browsers, more and more new projects have begun to adopt truly pure HTML5 solutions. More specifically, it should be said that the Canvas solution with large data volume application performance higher than SVG has gradually become the preferred standard solution for today's Web SCADA front-end technology.

Sample code download: http://download.csdn.net/download/u013161495/10206301

Example image (the "glowing" part of the picture will flash):

I still use  HT for Web  to develop this example, and I encapsulate the repeated parts as an "icon". The "icon" here refers to a vector icon. Vector is the abbreviation of vector graphics in HT for Web. Common raster bitmaps such as png and jpg describe the graphics by storing the color information of each pixel. The pictures in this way will be stretched, enlarged or reduced. There are problems such as blurred graphics, thick lines and jagged lines. Vector images, on the other hand, describe graphics through points, lines, and polygons, so you can zoom in and out infinitely with consistent accuracy.

In HT for Web, all places where raster bitmaps can be used can be replaced by vector graphics, such as primitive pictures on GraphView components, icons on TreeView and TableView, etc. Even the system interface made by the entire HT framework can achieve full vector graphics In this way, the scaling of primitives on the GraphView component will not be distorted, and it is no longer necessary to provide pictures of different sizes for the Retina display. In  the mobile era of devicePixelRatio  diverse, to achieve perfect cross-platform, vector may be the lowest cost s solution.

In HT, the vector is described in JSON format, and the usage method is the same as the ordinary raster bitmap. It is registered through ht.Default.setImage('hightopo', jsonObject), and the registration name of the corresponding image can be set to the data model. , such as node.setImage('hightopo') and node.setIcon('hightopo'), etc.

The vector json description must contain the width, height and comps parameter information:

  • width The width of the vector graphic
  • height The height of the vector shape
  • Array of components of comps vector graphics, each array object is an independent component type , and the order of the array is the order in which the components are drawn

At the same time, the following optional parameter information can be set:

  • visible is visible, the default is true
  • opacity transparency, the default is 1, the value range is 0~1
  • color Dyeing color, after setting the color, the content drawn inside the vector will be fused with the dyeing value
  • clip is used to clip the drawing area, two types can be set: boolean
    • boolean type, control whether the content beyond the width and height area is clipped when drawing, the default is false, no clipping
    • The function type can be drawn with the canvas brush to achieve the effect of custom cutting any shape

So let's take a look at how this icon is drawn with HT:

As can be seen from the picture, this icon consists of a straight line, a rectangle and an arrow. We name this icon arrow:

ht.Default.setImage('arrow', { // register image arrow 
    "width": 60, // width of vector graphics 
    "height": 30, // height of vector graphics 
    "comps": [ // vector graphics Component Array array, each array object is an independent component type, and the order of the array is the drawing order of the components 
        { // Drawing the straight part 
            "type": "shape", // Polygon 
            "borderWidth": 1, // Border width 
            "borderColor": "rgb(255,0,0)", // border color 
            "points": [ // Point points information [x1, y1, x2, y2, x3, y3, ...] is stored in the way of point coordinates 
                1.4262 ,
                 14.93626,
                51.46768,
                14.93626
            ]
        },
        { // Draw the tip of the arrow 
            "type": "shape" ,
             "borderWidth": 1 ,
             "borderColor": "rgb(255,0,0)" ,
             "rotation": 4.71239, // rotate 
            "points" : [
                 45.50336 ,
                 9.63425 ,
                 52.88591 ,
                 13.92955 ,
                 60.26846 ,
                 9.63425 ,
                 52.88591 ,
                 20.23827 ,
                 45.50336 ,
                9.63425
            ]
        },
        { // Draw rectangle part 
            "type": "rect", // rectangle 
            "background": { // background color 
                "func": "attr@lightBg" ,
                 "value": "rgb(255,0,0)"
            },
            "borderColor": "#979797",
            "shadow": {//阴影
                "func": "attr@shadow",
                "value": false
            },
            "shadowColor": {//阴影颜色
                "func": "attr@shadowColor",
                "value": "rgba(255,0,0,0.7)"
            },
            "shadowBlur": 32, // Shadow blur 
            "shadowOffsetX": 0, // Shadow horizontal offset 
            "shadowOffsetY": 0, // Shadow vertical offset 
            "rect": [ // Specify the area of ​​the rectangle[x, y, width, height] The coordinates of the starting position and the size of the rectangle are 
                11.44694 ,
                 10.43626 ,
                 30 ,
                 9
            ]
        }
    ]
});

每一个数组对象中的基本类型与 style 的 shape 参数是完全一一对应, 只是将 style 中的名称改成骆驼式命名法去掉了.分隔符,查找对应的 style 属性请参考 HT for Web 风格手册,有些后期添加的属性可能在风格手册中还没有添加,大家只要知道这么一个属性就行了,一般看属性名就知道这个属性的功能了。

上面代码中有一段可能让大家疑惑的点我没有在代码中解释,接下来我们着重来讲一下这个部分的内容:数据绑定。从文章一开始的图片我们知道,这个图标中的矩形部分是会变颜色的。

数据绑定意味将 Data 图元的数据模型信息,与界面图形的颜色、大小和角度等可视化参数进行自动同步, HT 的预定义图形组件默认就已与 DataModel 中的 Data 数据绑定,例如用户修改 Node 的 position 位置值, 则 GraphView 和 Graph3dView 上的相应图元位置会自动同步变化。

传统的数据绑定有单向绑定和双向绑定的概念,但 HT 系统的设计模式使得绑定更加简化易于理解,HT 只有一个 DataModel 数据模型, 绑定 DataModel 的图形组件并没有组件内部的其他数据模型,所以组件都是如实根据 DataModel 来呈现界面效果,因此当用户拖拽图元移动时, 本质也是修改了数据模型中 Node 的 position 位置值,而该属性变化触发的事件通过模型再次派发到图形组件,引发图形组件根据新的模型信息刷新界面。

绑定的格式很简单,只需将以前的参数值用一个带 func 属性的对象替换即可,func 的内容有以下几种类型:

  • function 类型,直接调用该函数,并传入相关 Data 和 view 对象,由函数返回值决定参数值,即 func(data, view) 调用。
  • string 类型:
    • func@*** 开头,则返回 data.getStyle(***) 值,其中 *** 代表 style 的属性名
    • attr@*** 开头,则返回 data.getAttr(***) 值,其中 *** 代表 attr 的属性名
    • field@*** 开头,则返回 data.*** 值,其中 *** 代表 data 的属性名
    • 如果不匹配以上情况,则直接将 string 类型作为 data 对象的函数名调用 data.***(view),返回值作为参数值

除了 func 属性外,还可设置 value 属性作为默认值,如果对应的 func 取得的值为 undefined 或 null 时,则会采用 value 属性定义的默认值。 例如以下代码,如果对应的 Data 对象的 attr 属性 lightBg 为 undefined 或 null 时,则会采用 rgb(255, 0, 0) 颜色:

"background": {//背景颜色
    "func": "attr@lightBg",// 返回的是 getAttr('lightBg')的值
    "value": "rgb(255,0,0)"//设置默认值
}

同理,上面代码中的 shadow 和 shadowColor 也都是以这种方式来进行数据绑定的,绑定的数据只与这个数组对象部分有关,所以就算这个图标是一张图片,我们还是能单独控制局部改变颜色等等。想了解所有的 func 的使用可以参考这个例子http://www.hightopo.com/guide/guide/core/databinding/examples/example_piebinding.html,所有的类型都用上了,非常实用。

我在代码中就是通过控制这几个绑定的属性来改变这个数组对象的颜色的,灯要闪烁,肯定会有“发光”的感觉才更真实,那么这里还需要解释一个内容,shadow 这个属性,解释为阴影,什么是阴影?比较好的一种解释就是,在一个矩形框中,由矩形中心点触发,由内至外颜色逐渐变浅,有一点像虚化,下面这张图片就是阴影的展示:

接着是搭建场景,大家可以直接使用 lightBling/displays/电力 下的 大厦.json 文件,在这个文件中,我设置了部分的“箭头”图标的 tag 标签。在 HT 中,一般建议 id 属性由 HT 自动分配,用户业务意义的唯一标示可存在 tag 属性上,通过 Data#setTag(tag) 函数允许任意动态改变 tag 值, 通过 DataModel#getDataByTag(tag) 可查找到对应的 Data 对象,并支持通过 DataModel#removeDataByTag(tag) 删除 Data 对象。

不过我是直接在 json 中添加 “tag” 属性,具体的 json 拓扑结构说明如下:

我们用到的 大厦.json,我拿一部分出来解析一下:

{
    "c": "ht.Node",//类名,用来反序列化
    "i": 274997,//id 值
    "p": {//get/set 类型属性 这里面的所有属性都可通过 get/set获取和设置
        "displayName": "灯-红",//显示名称
        "tag": "alarm",//标签 可通过 getTag 和 setTag 来获取和设置
        "image": "symbols/隧道用图标/交通灯/灯/灯-红.json",//图片 引用的路径为相对路径 这边调用的“红灯”图标的 json 文件
        "position": {//坐标
          "x": 70.9971,
          "y": 47.78651
        }
    },
    "s": {//对应 setStyle 属性
        "2d.movable": false,//2d 下不可以动 若要开启,直接设置 setStyle('2d.movable', true) 即可,下面同理
        "2d.editable": false//2d 下不可编辑
    }
}

其实整个不需要动画的部分都是 json 文件中的内容,大家可以根据上面的 json 拓扑结构来解析图纸的 json。那么问题来了,如何在 GraphView 中载入图纸的 json 文件?HT 封装了 ht.Defautl.xhrLoad 函数用来将对应的图纸 json 载入到 GraphView 上,参数为 text 文本,需要通过 ht.Default.parse 转义成 json:

ht.Default.xhrLoad('displays/电力/大厦.json', function(text){
    var json = ht.Default.parse(text);
    window.gv.dm().deserialize(json);//反序列化,并将反序列化的对象加入 DataModel
});

此时,DataModel 中的内容就是这个 json 文件反序列化出来的所有图元了,所以我们可以通过 DataModel 任意获取和改变 json 中的图元的样式和属性。其中,setAttr/getAttr 中的属性我们必须先定义一下,不然 HT 又不知道这个节点是否有这个用户自定义的属性:

for(var i = 0; i < gv.dm().size(); i++){
    var data = gv.dm().getDatas().get(i);//获取 datamodel 中的对应 i 的节点
    if(data.getTag()){//如果这个节点有设置 tag 值
        data.a('shadow', false);//设置自定义属性,并且给一个值
        data.a('shadowColor', 'rgba(255,0,0,0.9)');
        data.a('lightBg', 'rgb(255, 0, 0)');
        data.a('alarmColor', 'red');
    }
}

这下我们就可以任意操作上面已经声明过的属性了,当然,HT 默认的属性我们也能任意操作!我在 json 文件中设置了几个 tag 标签,light1~light15 以及 alarm 标签,我们可以通过 data.getTag() 来获取这个节点对应的标签名,或者通过 dataModel.getDataByTag(tagName) 已知标签名来获取对应节点。

动画的部分 HT 有三种动画的方式,针对的点不同,这里我用到的是 schedule 主要用于在指定的时间间隔进行函数回调处理。HT 中调度进行的流程是,先通过 DataModel 添加调度任务,DataModel 会在调度任务指定的时间间隔到达时, 遍历 DataModel 所有图元回调调度任务的 action 函数,可在该函数中对传入的 Data 图元做相应的属性修改以达到动画效果。

以下是我例子中的动画相关代码:

var blingTask = {
    interval: 1000,//间隔毫秒数
    action: function(data){//间隔动作函数
        if(data.getTag() === 'light1' || data.getTag() === 'light13' || data.getTag() === 'light5' || data.getTag() === 'light6' || data.getTag() === 'light10' || data.getTag() === 'light11' || data.getTag() === 'light12' || data.getTag() === 'light14' || data.getTag() === 'light15'){
            if(data.a('lightBg') === 'rgb(255, 0, 0)'){//如果属性lightBg值为这个,就做以下一系列的动作 
                data.a('lightBg', 'rgb(0, 255, 0)');
                data.a('shadow', true);
                data.a('shadowColor', 'rgba(0, 255, 0, 0.9)');
            }else if(data.a('lightBg') === 'rgb(0, 255, 0)'){
                data.a('lightBg', 'rgb(255, 255, 0)');
                data.a('shadow', true);
                data.a('shadowColor', 'rgba(255, 255, 0, 0.9)');
            }else{
                data.a('lightBg', 'rgb(255, 0, 0)');
                data.a('shadow', true);
                data.a('shadowColor', 'rgba(255, 0, 0, 0.9)');
            }
        }else if(data.getTag() === 'alarm'){
            if(data.a('alarmColor') === 'red'){
                data.a('alarmColor', 'rgb(0, 255, 0)');
            }else{
                data.a('alarmColor', 'red');
            }
        }
    }
};
window.gv.dm().addScheduleTask(blingTask);//添加动画进 DataModel 中

其他部分我相信大家都能看得懂,实在不就去官网(或者这个链接也行,里面还有常见问题)查查对应的文档,写得很清楚哦~

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326170627&siteId=291194637