`
xhload3d
  • 浏览: 209147 次
社区版块
存档分类
最新评论

Web SCADA 电力接线图工控组态编辑器

阅读更多

前言

SVG并非仅仅是一种图像格式, 由于它是一种基于XML的语言,也就意味着它继承了XML的跨平台性和可扩展性,从而在图形可重用性上迈出了一大步。如SVG可以内嵌于其他的XML文档中,而SVG文档中也可以嵌入其他的XML内容,各个不同的SVG图形可以方便地组合, 构成新的SVG图形。这个 Demo 运用的技术基于 HTML5 的技术适应了只能电网调度、配电网运行监控与配电网运维管控,通过移动终端实现 Web SCADA 账上运维的时代需求。由于传统电力行业 CS 桌面监控系统一直到新一代 Web 和移动终端进化中,HT 是实施成本最低,开发和运行效率最高的前端图形技术解决方案。SVG 矢量图形大家都不会陌生了,尤其是在工控电信等领域,但是这篇文章并不是要制作一个新的绘制 SVG 图的编辑器,而是一个可绘制矢量图形并且对这个图形进行数据绑定的更高阶。

效果图



 

http://www.hightopo.com/demo/2deditor/HT-2D-Editor.html 

代码实现

整体框架

根据上图看得出来,整个界面被分为五个部分,分别为 palette 组件面板,toolbar 工具条,graphView 拓扑组件,propertyPane 属性面板以及 treeView 树组件,这五个部分中的组件需要先创建出来,然后才放到对应的位置上去:

dataModel = new ht.DataModel();//数据容器 承载Data数据的模型
palette = new ht.widget.Palette();//组件面板
toolbar = new ht.widget.Toolbar(toolbar_config);//工具条
g2d = new ht.graph.GraphView(dataModel);//拓扑组件  
treeView = new ht.widget.TreeView(dataModel);//树组件
propertyPane = new ht.widget.PropertyPane(dataModel);//属性面板
propertyView = propertyPane.getPropertyView();//属性组件
rulerFrame = new ht.widget.RulerFrame(g2d);//刻度尺

 

这些布局,只需要结合 splitView 和 borderPane 进行布局即可轻松完成~其中 splitView 为 HT 中的 分割组件,参数1为放置在前面的 view 组件(可为左边的,或者上面的);参数2为放置在后面的 view 组件(可为右边的,或者下面的);参数3为可选值,默认为 h,表示左右分割,若设置为 v 则为上下分割;参数4即为分割的比例。borderPane 跟 splitView 的作用有些相似,但是在这个 Demo 中布局,结合这两种组件,代码看起来会更加清爽。

borderPane = new ht.widget.BorderPane();//边框面板
leftSplit = new ht.widget.SplitView(palette, borderPane, 'h', 260);//分割组件,h表示左右分割,v表示上下分割
rightSplit = new ht.widget.SplitView(propertyPane, treeView, 'v', 0.4);
mainSplit = new ht.widget.SplitView(leftSplit, rightSplit, 'h', -260);                                              

borderPane.setTopView(toolbar);//设置边框面板的顶部组件为 toolbar
borderPane.setTopHeight(30);
borderPane.setCenterView(rulerFrame);//设置边框面板的中间组件为 rulerframe
mainSplit.addToDOM();//将 mainSplit 的底层 div 添加进 body 体中

dataModel.deserialize(datamodel_config);//反序列化 datamodel_config 的内容,将json内容转为拓扑图场景内容
g2d.fitContent();

 

布局结束后,就要考虑每一个容器中应该放置哪些内容,我将这些内容分别封装到不同的函数中,通过调用这些函数来进行数据的显示。

Palette 组件面板

左侧的 Palette 组件面板需要向其内部添加 group 作为分组,然后再向组内添加节点。但是我们使用这个组件的最重要的一个原因是它能够拖拽节点,但是因为我们拖拽后需要在 graphView 拓扑组件中生成一个新的节点显示在拓扑图上,所以我将拖拽部分的逻辑写在了 graphView 拓扑组件的初始化函数中,这一小节就不做解释。

虽然说最重要的因素是拖拽,但是不可否认,这个组件在分类上也是非常直观:



 

如上图,我在 Palette 中做了三个分组:电力、食品加工厂以及污水处理。并在这些分组下面填充了很多属于该组类型的节点。我将这些分组的信息存储在 palette_config.js 文件中,由于三组中的信息量太大,这里只将一小部分的信息展示出来,看看是如何通过 json 对象来对分组进行数据显示的:

palette_config = {
    scene: {
        name: '电力',
        items: [
            { name: '文字', image: '__text__', type: ht.Text },
            { name: '箭头', image: 'symbols/arrow.json' },
            { name: '地线', image: 'symbols/earthwire.json' }
        ]
    },
    food: {
        name: '食品加工厂',
        items: [
            { name: '间歇式流化床处理器', image: 'symbols/food/Batch fluid bed processor.json'},
            { name: '啤酒瓶', image: 'symbols/food/Beer bottle.json'},
            { name: '台式均质机', image: 'symbols/food/Batch fluid bed processor.json'}
        ]
    },
    pumps: {
        name: '污水处理',
        items: [
            { name: '3维泵', image: 'symbols/pumps/3-D Pump.json'},
            { name: '18-惠勒卡车', image: 'symbols/pumps/18-wheeler truck 1.json'}
        ]
    }     
};

通过遍历这个对象获取内部数据,显示不同的数据信息。当然,在获取对象的信息的时候,我们需要创建 ht.Group 类的对象,以及分组内部的 ht.Node 类的元素(这些元素都为组的孩子),然后将这些获取来的数据赋值到这两种类型的节点上,并且将这些节点添加到 Palette 的数据容器中:

function initPalette(){//初始化组件面板中的内容
    for(var name in palette_config){//从 palette_config.js 文件中获取数据
        var info = palette_config[name];
        var group = new ht.Group();//组件面板用ht.Group展示分组,ht.Node展示按钮元素
        group.setName(info.name);
        group.setExpanded(false);//设置group默认关闭
        palette.dm().add(group);//将节点添加到 palette 的数据容器中
        
        info.items.forEach(function(item){
            var node = new ht.Node();//新建 ht.Node 类型节点
            node.setName(item.name);//设置名称 用于显示在 palette 面板中节点下方说明文字
            node.setImage(item.image);//设置节点在 palette 面板中的显示图片

            //文本类型
            if (item.type === ht.Text) {//通过 json 对象中设置的 type 信息来获取当前信息为何种类型的节点,不同类型的节点有些属性设置不同
                node.s({
                    'text': 'Text',//文本类型的节点需要设置这个属性显示文本的内容
                    'text.align': 'center',//文本对齐方式
                    'text.vAlign': 'middle',//文本垂直对齐方式
                    'text.font': '32px Arial'//文本字体
                });
            }

            node.item = item;
            node.s({
                'image.stretch': item.stretch || 'centerUniform',//设置节点显示图片为填充的方式,这样不同比例的图片也不会因为拉伸而导致变形
                'draggable': item.draggable === undefined ? true : item.draggable,//设置节点是否可被拖拽

            });                          
            group.addChild(node);//将节点设置为 group 组的孩子
            palette.dm().add(node);//节点同样也得添加到 palette 的数据容器中进行存储
        });
    }             
}

graphView 拓扑组件

前面说到了 Palette 组件中节点拖拽到 graphView 拓扑图形中,来看看这个部分是如何实现的。如果 Palette 中的 Node 的 draggable 属性设置为  true ,那么 Palette 可以自动处理 dragstart ,但是 dragover 和 dragdrop 事件需要我们处理,我们知道 IOS 和 Android 设备上并不支持 dragover 和 dragdrop 这类事件,所以 Palette 插件还提供了模拟的拖拽事件 handleDragAndDrop,可以完美兼容 PC 和手持终端。

function initGraphView(){       
    if(ht.Default.isTouchable){//判断是否为触屏可Touch方式交互
        palette.handleDragAndDrop = function(e, state) {//重写此方法可以禁用HTML5原生的Drag和Drop事件并启用模拟的拖拽事件
            if(ht.Default.containedInView(e, g2d)){//判断交互事件所处位置是否在View组件之上
                if(state === 'between'){
                    e.preventDefault();//取消事件的默认动作。
                }
                else if(state === 'end'){//当state为end时,判断e是否在graphView的范围内,如果是,则创建Node
                    handleDrop(e);
                }
            }
        };
    }
    else{
        g2d.getView().addEventListener("dragover", function(e) {
            e.dataTransfer.dropEffect = "copy";
            e.preventDefault();
        });
        g2d.getView().addEventListener("drop", function(e) {
            handleDrop(e);
        });
    }
}

function handleDrop(e){//被拖拽的元素在目标元素上同时鼠标放开触发的事件
    e.preventDefault();
    var paletteNode = palette.dm().sm().ld();//获取 palette 面板上最后选中的节点                 
    if (paletteNode) {   
        var item = paletteNode.item,
            image = item.image;
            data = g2d.getDataAt(e, null, 5);//获取事件下的节点

        var node = new (item.type || ht.Node)();
        node.setImage(image); //设置节点图片
        node.setName(item.name);  //设置节点名称
        node.p(g2d.lp(e));//设置节点的坐标为拓扑中的逻辑坐标 lp函数为将事件坐标转换为拓扑中的逻辑坐标
        node.s('label', '');//设置节点在 graphView 中底部不显示 setName 中的说明。因为 label 的优先级大于 name 

        if(data instanceof ht.Group){//如果拖拽到“组类型”的节点上,那么直接设置父亲孩子关系
            node.setParent(data);//设置节点的父亲
            data.setExpanded(true);//展开分组
        }else{
            node.setParent(g2d.getCurrentSubGraph());
        }       
        g2d.dm().add(node);
        g2d.sm().ss(node);                                                     
    }                    
}  

我在 graphView 拓扑图的场景中央添加了一个 json 场景,通过 dm.deserialize(datamodel_config) 反序列化 json 场景内容导出的一个电信行业的图纸。HT 独特的矢量引擎功能满足电力行业设备种类繁多、设备图元和线路网络需无极缩放、绑定量测数据实时刷新等需求;三维呈现技术使得电力厂站和变压器等设备 3D 可视化监控成为可能。

treeView 树组件 



至于树组件,树组件和 graphView  拓扑组件共用同一个 dataModl 数据容器,本来只需要创建出一个树组件对象,然后将其添加进布局容器中即可显示当前拓扑图形中的所有的数据节点,一般 HT 会将树组件上的节点分为几种类型进行显示,ht.Edge、ht.Group、ht.Node、ht.SubGraph、ht.Shape 等类型进行显示,但是这样做有一个问题,如果创建的节点非常多的话,那么无法分辨出那个节点是哪一个,也就无法快速地定位和修改该节点,会给绘图人员带来很大的困扰,所以我在 treeView 的 label 和 icon 的显示上做了一些处理:

// 初始化树组件
function initTreeView() {
    // 重载树组件上的文本显示
    treeView.getLabel = function (data) {
        if (data instanceof ht.Text) {
            return data.s('text');
        }
        else if (data instanceof ht.Shape) {
            return data.getName() || '不规则图形'
        }
        return data.getName() || '节点'
    };

    // 重载树组件上的图标显示
    var oldGetIconFunc = treeView.getIcon;
    treeView.getIcon = function (data) {
        if (data instanceof ht.Text) {
            return 'symbols/text.json';
        }
        var img = data.getImage();
        return img ? img : oldGetIconFunc.apply(this, arguments);
    }
}

 

 propertyPane 属性面板


 

属性面板,即为显示属性的一个容器,不同的类型的节点可能在属性的显示上有所不同,所以我在 properties_config.js 文件中将几个比较常见的类型的属性存储到数组中,主要有几种属性: text_properties 用于显示文本类型的节点的属性、data_properties 所有的 data 节点均显示的属性、node_properties 用于显示 ht.Node 类型的节点的属性、group_properties 用于显示 ht.Group 类型的节点的属性以及 edge_properties 用于显示 ht.Edge 类型的节点的属性。通过将这些属性分类,我们可以对在 graphView 中选中的不同的节点类型来对属性进行过滤:

function initPropertyView(){//初始化属性组件               
    dataModel.sm().ms(function(e){//监听选中变化事件
        propertyView.setProperties(null);
        var data = dataModel.sm().ld();
        
        //针对不同类型的节点设置不同的属性内容
        if (data instanceof ht.Text) {//文本类型
            propertyView.addProperties(text_properties);
            return;
        }
        if(data instanceof ht.Data){// data 类型,所有的节点都基于这个类型
            propertyView.addProperties(data_properties);
        }                                        
        if(data instanceof ht.Node){// node 类型
            propertyView.addProperties(node_properties);
        }
        if(data instanceof ht.Group){//组类型
            propertyView.addProperties(group_properties);
        }
        if(data instanceof ht.Edge){//连线类型
            propertyView.addProperties(edge_properties);
        }     
    });                
}

 

数据绑定在属性栏中也有体现,拿 data_properties 中的“标签”和“可编辑”作为演示:

{
    name: 'name',//设置了 name 属性,如果没有设置 accessType 则默认通过 get/setName 来获取和设置 name 值
    displayName: '名称',//用于存取属性名的显示文本值,若为空则显示name属性值
    editable: true//设置该属性是否可编辑                       
}, 
{
    name: '2d.editable',//结合 accessType,则通过 node.s('2d.editable') 获取和设置该属性
    accessType: 'style',//操作存取属性类型
    displayName: '可编辑',//用于存取属性名的显示文本值,若为空则显示name属性值
    valueType: 'boolean',//布尔类型,显示为勾选框
    editable: true//设置该属性是否可编辑  
}

 

这两个属性比较有代表性,一个是直接通过 get/set 来设置 name 属性值,一个是通过结合属性的类型来控制 name 的属性值。只要在属性栏中操作“名称”和“可编辑”两个属性,就可以直接在拓扑图中看到对应的节点的显示情况,这就是数据绑定。当然,还可以对矢量图形进行局部的数据绑定,但是不是本文的重点,有兴趣的可以参考我的这篇文章 WebGL 3D 电信机架实战之数据绑定

toolbar 工具栏

差点忘记说这个部分了,toolbar 上总共有 8 种功能,分别是选中编辑、连线、直角连线、不规则图形、刻度尺显示、场景放大、场景缩小以及场景内容导出 json。这 8 种功能都是存储在 toolbar_config.js 文件中的,通过绘制 toolbar 中的元素给每一个元素都添加上了对应的点击触发的内容,主要讲讲 CreateEdgeInteractor.js 创建连线的内容。

我们通过 ht.Default.def 自定义了 CreateEdgeInteractor 类,然后通过 graphView.setInteractors([ new CreateEdgeInteractor(graphView, 'points')]) 这种方式来添加 graphView 拓扑图中的交互器,可以实现创建连线的交互功能。

在 CreateEdgeInteractor 类中通过监听 touchend 放手后事件向 graphView 拓扑图中添加一个 edge 连线,可以通过在 CreateEdgeInteractor 函数中传参来绘制不同的连线类型,比如 “ortho” 则为折线类型:

var CreateEdgeInteractor = function (graphView, type) {
    CreateEdgeInteractor.superClass.constructor.call(this, graphView);   
    this._type = type;
};
ht.Default.def(CreateEdgeInteractor, DNDInteractor, {//自定义类,继承 DNDInteractor,此交互器有一些基本的交互功能
    handleWindowTouchEnd: function (e) {
        this.redraw();
        var isPoints = false;
        if(this._target){
            var edge = new ht.Edge(this._source, this._target);//创建一条连线,传入起始点和终点
            edge.s({
                'edge.type': this._type//设置连线类型 为传入的参数 type 类型 参考 HT for Web 连线类型
            });
            isPoints = this._type === 'points';//如果没有设置则默认为 points 连线方式
            if(isPoints){
                edge.s({
                    'edge.points': [{//设置连线的点
                         x: (this._source.p().x + this._target.p().x)/2,
                         y: (this._source.p().y + this._target.p().y)/2
                    }]
                });                
            }
            edge.setParent(this._graphView.getCurrentSubGraph());//设置连线的父亲节点为当前子网
            this._graphView.getDataModel().add(edge); //将连线添加到拓扑图的数据容器中
            this._graphView.getSelectionModel().setSelection(edge);//设置选中该节点                        
        }
        this._graphView.removeTopPainter(this);//删除顶层Painter
        if(isPoints){
            resetDefault();//重置toolbar导航栏的状态
        }        
    }            
});

 总结

 一开始想说要做这个编辑器还有点怕怕的,就是感觉任务重,但是不上不行,所以总是在拖,但是后来整体分析下来,发现其实一步一步来就好,不要把步骤想得太复杂,什么事情都是从小堆到大的,以前我们用 svg 绘制的图形都可以在这上面绘制,当然,如果有需要拓展也完全 ok,毕竟别人写的编辑器不一定能够完全满足你的要求。这个编辑器虽说在画图上面跟别家无异,但是最重要的是它能够绘制出矢量图形,结合 HT 的数据绑定和动画,我们就可以对这些矢量图形中的每一个部分进行操作,比如灯的闪烁啊,比如人眨眼睛等等操作,至于这些都是后话了。有了这个编辑器我也能够更加快速地进行开发了~

  • 大小: 324 KB
  • 大小: 197.6 KB
  • 大小: 42 KB
  • 大小: 26.9 KB
  • 大小: 5.5 KB
  • 大小: 5 KB
  • 大小: 1.7 MB
分享到:
评论

相关推荐

    Web SCADA 电力接线系统图

    在电力、油田燃气、供水管网等工业自动化领域 Web SCADA 的概念已经提出了多年,早些年的 Web SCADA 前端技术大部分还是基于 Flex、Silverlight 甚至 Applet 这样的重客户端方案,在 HTML5 流行前 VML 和 SVG 算是...

    电网一次接线图组态

    电网一次接线图组态是电力系统中一个关键的组成部分,它主要用于表示电力系统的实际物理连接方式,包括发电机、变压器、线路、开关设备等主要电气设备的布局和连接关系。在qt平台上进行电网一次接线图组态的开发,...

    JSBOT.rar_SCADA_js 组态_js组态_scada系统_组态软件

    **JSBOT.rar_SCADA_js 组态_js组态_scada系统_组态软件** 这篇文章将深入探讨SCADA(Supervisory Control and Data Acquisition)系统、JS组态以及它们在实际应用中的结合。SCADA系统是一种广泛应用于工业自动化...

    基于vue3实现的svg可视化web组态编辑器。可无需修改代码动态添加svg组件.zip

    包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】...

    scada 工控组态软件

    工控组态软件是构建SCADA系统的关键工具,允许用户通过图形化界面配置、设计和调试控制系统。 SCADA系统的核心功能包括以下几个方面: 1. 数据采集:系统通过各种通信协议与现场设备连接,如PLC(可编程逻辑控制器...

    快速搭建 物联网电力检测,web SCADA 工控组态

    电力检测所需的硬件拓扑图 设备层: 智能仪表 等 检测设备状态 传输层 :通讯网关 协议转换 交互层: 现场检测 大屏展示 远程维护 硬件部署好之后我们就可以在平台上搭建我们需要的系统了 首先我们需要增加协议通道...

    WEBscada系统.doc

    【WEBSCADA系统详解】 WEBSCADA系统,全称Web Supervisory Control and Data Acquisition,是一种融合了互联网技术、面向对象技术、DNA神经元技术等先进科技的SCADA(Supervisory Control And Data Acquisition)...

    工控组态软件的发展与开发设计.rar

    通信技术的进步使得工控组态软件可以无缝连接各种现场设备,如传感器、执行器、控制器等。常见的通信协议包括MODBUS、OPC(OLE for Process Control)、TCP/IP等。开发者需要对这些通信协议有深入理解,以便实现高效...

    工控组态软件中图形组态子系统的设计与实现.rar

    5. **实时通信**:图形组态子系统需要与PLC(可编程逻辑控制器)、SCADA(Supervisory Control And Data Acquisition)系统或其他现场设备进行通信。Delphi提供了多种通信库,如TCP/IP、串口通信等,用于实现实时...

    WEB SCADA 组态画面的制作

    由于其广泛应用于各种行业,如自来水、石油、电力、环保等领域,因此掌握WEB SCADA的组态画面制作和管理技能对于相关专业人员来说至关重要。通过深入学习和实践,用户可以充分利用这一工具来提升运营效率和决策质量...

    常见组态工控软件图片素材

    在工控领域,组态软件是一种广泛使用的工具,它允许工程师通过图形化界面来配置、监控和控制自动化系统。这些软件通常包含丰富的图片素材,帮助用户直观地展示设备、过程和系统的状态。以下是对"常见组态工控软件...

    基于ZPMC第四代组态软件的WEB SCADA的研究与开发.pdf

    本文所介绍的项目便是基于ZPMC(振华港机)第四代组态软件开发的WEB SCADA系统,这个系统不仅能够提供实时监控功能,还能够满足多端访问的需求。 WEB SCADA的关键技术特点和开发流程如下: 1. SVG技术应用:SVG...

    电力项目第一天源码

    在电力项目开发中,源码是实现电力系统自动化、智能化的核心组成部分。"电力项目第一天源码"可能是指一个初阶的或新启动的项目,包含了项目初期的基础框架和功能模块。MyEclipse作为开发工具,它在Java EE项目开发中...

    Web-Editor:Web可视化组态编辑器(Angular8)

    Tempo 基于angular版本开发, 项目截图 ! DEMO数据存储在IndexedDB,不要清除本地缓存:cookie及其他网站数据 ! 商业开发请开发对应后台服务 下载 前端 git clone ...编辑器代码组织 .

    组态软件对比分析,分析目前市场主流的SCADA软件

    ### 组态软件对比分析及SCADA软件市场主流解析 #### 一、组态软件概述 随着我国工业自动化和信息化水平的不断提升,大型企业在工厂级计算机网络、信息管理系统及生产监控系统方面取得了显著的进步。然而,要想实现...

    WEB SCADA物联网平台数据库(Postgre SQL)应用

    【WEB SCADA物联网平台数据库(PostgreSQL)应用】 在物联网(IoT)和工业自动化领域,Web SCADA系统是监控和数据采集系统的一种关键组成部分,它允许远程监控和控制工业设备。Ecava IGX Web SCADA是这样一种平台,专...

    WebAccess 组态软件

    监控组态软件日益成为自动化硬件厂商争夺的重点,然而国外组态软件贵,国内组态软件功能等方面还待有提高。但都是传统的c/s架构,现物联网IOT兴起时,网络,云形式的分布式架构下,以补丁形式推出所谓网络版,毕竞...

    Haiwell(海为)SCADA组态软件.zip

    总的来说,Haiwell(海为)SCADA组态软件是一个功能全面、易于使用的工控解决方案,对于需要实时监控和控制的工业环境,它提供了强大的支持。通过学习和掌握这款软件,工程师们能够提升工作效率,更好地应对复杂的...

    FUXA 1.1.17 WEB SCADA

    【FUXA 1.1.17 WEB SCADA】是一款基于Web的SCADA(Supervisory Control and Data Acquisition)系统,专为工业自动化领域的监控和数据采集设计。这款软件的核心在于提供一个用户友好的界面,使用户能够远程监控和...

    工控组态软件(免费发送)

    这些组件与底层的PLC(可编程逻辑控制器)、SCADA(数据采集与监控系统)或者其他自动化设备通信,实现远程监控和数据交换。 “免费”意味着这个软件可以免费下载和使用,这对于小型企业或个人用户来说是个很大的...

Global site tag (gtag.js) - Google Analytics