使用VUE 组件实现HMI 模型

HMI模型化的目的是通过模型能够实现HMI 视图的组态化。

HMI 组态化方法

     HMI 组态要使用一个HMI 设计工具来实现,这个工具类似于图形编辑软件。使用者从UI 组件库中选择合适的模板,放置在页面中,并且配置各种参数。HMI 设计的结果是产生一个页面模型。存储到系统模型库中。当系统运行时,由后台读取HMI 模型,形成一个HMI 模型表,下载到前端,由前端程序形成最终的HMI 页面。

HMI的分层结构

       HMI 界面是一种分层结构,最顶层是Page,然后可以进一步分为面板(Panel),按钮,滑杆,仪表盘以及众多特定行业的HMI 单元,例如电机,阀门,反应釜等等。

       复杂的HMI 是由基本的单元构建而成的。HMI模型化的第一步就是建立HMI基本单元的模型。

NMI 单元的模型

HMI 单元的模型由两个部分组成:

  • 系统中的模型

HMI 单元的信息模型和系统中的其它节点是相同的,可以使用json模板来描述:

一个HMI 模板的实例-标题

 Name:"Title",
        DisplayName:"标题",
        Description:"",
        NodeType:"Object",
        Catalog:["HMI","Data"],
        Attributes:[
            {Name:"Widget",Value:"TextBox"},
            {Name:"Position",Value:[10,20]},
            {Name:"Size",Value:[640,120]},
            {Name:"BackgroundColor",Value:"Green"},
            {Name:"Color",Value:"White"},
            {Name:"Align",Value:"center"},
            {Name:"Text",Value:"HMI Demostration"},
            {Name:"FontSize",Value:"48px"},
         ],
         Childs:[],
         Reference:[]
    }

由此可见,HMI 的信息模型的主要区别可能就是它们的属性比较多。 

  • 前端HMI UI组件

        在HMI 的前端,要有一个UI 组件与信息模型相对应,平台能够根据系统的HMI模型生成和配置前端的UI 组件。

作为HMI单元的VUE组件

        在我前面的博文中,曾经介绍使用Jquery,Bootstrap,HTML5,CSS,Javascript 这些基础技术构建HMI UI组件。尽管这样做能够了解组件的底层逻辑,但是代码工作量比较大。在现代前端架构angular,VUE 中普遍基于了组件(Component)的技术。

        组件是一段可重用的代码,这意味着我们创建一次并在整个 vue 应用程序中重用它。 vue组件是一个单一文件,将HTML,CSS和Javascript包含在一个文件中。

       原始的HTML5 是将完整的页面归结为三个大的文件,它们分别是HTML,CSS和Javascript 文件。组件的思想就是根据以页面中的元素,分解成为一段一段的单元,也就是组件 这样的思想和我们系统模型的概念非常契合。因此,我主张使用VUE 组件来实现HMI 的UI 组件。 

HMI 单元的组件有下面几个特点

  • 绝对定位

HMI页面设计采用了类似图形设计软件的方式,直接将需要的组件放置在页面的合适位置,这就要求组件是绝对位置定位。

  • 动态导入

 HMI 页面是前端运行时根据HMI 节点的模型动态地生成的。因此,需要能够动态地导入组件。

  • 以事件方式返回数据

一个HMI VUE 组件实例

<template>
<object ref='object'> 
   <b-button id="btn" variant="info" @click="clickConfirm()">Click</b-button>
    <b-button id="btn" variant="info" @click="clickCancel()">Canel</b-button>
  <img width="100" src="../assets/images/dout.svg">
  </object>
</template>
<script>

 export default {
    name:"HMI_gauge",  
    data() {
      return {
        Name:"",
        ID:0,
        Options:{
          Size:"400px",
          Position:{
            X:"400px",
            Y:"20px"
          }
        },
                
      }
    },
    components:{
    
    },
   mounted:function(){
    
     this.$refs.object.style.top=this.Options.Position.Y;
     this.$refs.object.style.left=this.Options.Position.X;
 
  },
   methods: {
       clickConfirm(){
         
         this.$emit('HMIEvent', {
             "Event":"Confirm",
             "Param":{
               "ComponentName":this.Name
             }
         });
       },
       clickExit(){
           this.$emit('HMIEvent', {
             "Event":"Cancel",
             "Param":{
               "ComponentName":this.Name
             }
         });
       }
       
    }
  
 }
</script>

<style scoped>
   object {
      position:absolute;
    }
</style>

动态导入VUE 组件

        前面提到,HMI组件不是静态导入。而且导入的组件的数量,类型都是不确定的。所以不能采取预先在Template 中定义组件标签,然后导入组件。要在挂载点Append child 的方式添加组件。

网络上有一些关于VUE 动态载入组件的介绍,但是都似乎有一些小问题,我尝试了几乎一天时间才实现。

 我采用下面的方式导入HMI 组件

<template>
<div class="wrapper">
 <div class="a">
      <div id="mount-point" ref="mount-point"></div>
    </div>
    </div>
</template>
<script>
   import Vue from 'vue';
  export default {
    name: "ExtendMode",  
     data(){
            return {
                instance: null
            }
        },
       created:function(){
          this.switchComponent("./HMI_gauge",{
            Position:{
              X:"100px",
              Y:"100px"
            }
          });
          this.switchComponent("./HMI_gauge",{
            Position:{
              X:"100px",
              Y:"400px"
            }
          });   
        },
        methods: {
            switchComponent: function (name,Options) {
                if(this.instance){
                    this.instance.$destroy();
                    this.$refs['mount-point'].removeChild(this.instance.$el);
                }
                import(`${name}`).then(component => {
                    let VueConstructor = Vue.extend(component.default); //使用Vue.extend创建组件构造器
                    this.instance = new VueConstructor({
                      data:{
                        Options:Options,
                      }
                      });
                    this.instance.$mount();
                    this.$refs['mount-point'].appendChild(this.instance.$el);//挂载到指定dom上
                });
            }
        }

上面的例子中导入了两个组件。 

值得注意的是,这种方式下,如果 HMI 组件中包含了另外的子组件(比如JQWidgets )的话,会产生错误,后来发现这是由于内部的子组件要在HMI 组件导入以后才能够导入。于是,将上面的HMI 组件改为:

<template>
 
<object ref='object'> 
   <b-button id="btn" variant="info" @click="clickConfirm()">Click</b-button>
    <b-button id="btn" variant="info" @click="clickCancel()">Canel</b-button>    
     <components v-bind:is="current"
                  :ranges="ranges" :ticksMinor="ticksMinor"
              :ticksMajor="ticksMajor" :value="0"
              :colorScheme="'scheme05'" :animationDuration="1200">       
    </components>
 </object> 
</template>
<script>
 import JqxGauge  from '../jqwidgets-vue/vue_jqxgauge.vue';
   export default {
    name:"HMI_gauge",  
    data() {
      return {
        Name:"",
        ID:0,
        Options:{
          Size:"400px",
          Position:{
            X:"400px",
            Y:"20px"
          }
        },
           ticksMinor: { interval: 10, size: '5%' },
                ticksMajor: { interval: 20, size: '9%' },
                ranges: [
                    { startValue: 0, endValue: 55, style: { fill: '#4bb648', stroke: '#4bb648' }, endWidth: 5, startWidth: 1 },
                    { startValue: 55, endValue: 110, style: { fill: '#fbd109', stroke: '#fbd109' }, endWidth: 10, startWidth: 5 },
                    { startValue: 110, endValue: 165, style: { fill: '#ff8000', stroke: '#ff8000' }, endWidth: 13, startWidth: 10 },
                    { startValue: 165, endValue: 220, style: { fill: '#e02629', stroke: '#e02629' }, endWidth: 16, startWidth: 13 }
                ],
        current :null      
      }
    },
   components:{
     JqxGauge 
   },

   mounted:function(){
    
     this.$refs.object.style.top=this.Options.Position.Y;
     this.$refs.object.style.left=this.Options.Position.X;
     this.current=JqxGauge ;
  },
   methods: {
     click(){
       console.log("clicked");
     },
       clickConfirm(){
         
         this.$emit('HMIEvent', {
             "Event":"Confirm",
             "Param":{
               "ComponentName":this.Name
             }
         });
       },
       clickExit(){
           this.$emit('HMIEvent', {
             "Event":"Cancel",
             "Param":{
               "ComponentName":this.Name
             }
         });
       }
       
 
    }
  
 }
</script>

<style scoped>
   object {
      position:absolute;
    }
      
</style>

HMI的视图为:

 漂亮吧?费了我好大的劲!

利用d3 导入SVG 图并交互

组件与平台的通信

HMI 节点需要与外界交换信息。

  值得一提的是,在节点中需要调用外部的方法。这可以通过Event  机制来实现:

   this.$emit('HMIEvent', {
             "Event":"CallMethod",
             "Param":{
               "ComponentName":this.Name,
               "Method":"PublishData"
               "Arguments":{....}
             }
         });

进一步地研究

       由于本人对VUE 架构并不熟悉,本文中难免有不准确的地方。下一步要研究如何导入SVG 图形,VUE 组件与SVG 的交互等问题。

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/119618204