资讯详情

可视化的工业互联网 3D 展示

前言

通用电气(GE)、IBM、由英特尔等公司推广的工业互联网正在经历产品-数据分析平台-应用-生态的演变。这主要是由于 Predix 数据分析平台对工业互联网应用的整合能力。Predix 就像工业数据领域一样 iOS 或者像Android系统一样,工程师可以建立自己的模型和应用程序,每天打开前以万计的前传感器和后传感器 5000 一万个数据库。

东方航空公司在实际应用中 Predix 工业互联网应用的收集 500 多台 CFM56 结合远程诊断记录和第三方数据,建立了叶片损伤分析预测模型。过去,航空公司需要定期强制飞机休病假目前,发动机的运行可以根据数据分析平台上的结果进行预测,定制科学的重复检查间隔,提高运行效率。除了航空领域,工厂仓库监管也非常需要互联网干预,不仅可以实时监控仓库当前数据和信息,还可以减少仓库监管数量,更可以预测仓库故障信息,提前通知员工采取相应措施,可以有效避免工厂运营暂停造成的损失。 在这里插入图片描述 http://www.hightopo.com/demo/warehouse/index.html

代码生成

采用这个例子 es6 模块化部署。 index.html 进入 lib/index.js,源码是在 src 我们直接进入文件夹 src/view 下的 index.js 在顶部加载其他模块 export 接口模块:

import sidebar from './sidebar.js'; import header from './header.js'; import BorderLayout from './common/BorderLayout.js'; import shelfPane from './common/shelfPane.js'; import chartPane from './common/chartPane.js'; import graph3dView from './3d/index'; 

场景布局

我们将页面的每个部分放在不同的位置 js 在上面加载的文件中 js export 根层容器 BorderLayout(整体最外层 div),整个图片的部分是基于 borderLayout 的。 最外层容器 BorderLayout 是在 src/view/common 下的 BorderLayout.js 其中,自定义类 ht.Default.def(className, superClass, methods) 是 HT 自定义函数中包装,其中 className 为自定义类名, superClass 要继承的父类,methods 为了方法和变量声明,通过外部定义函数变量,使用该方法 functionName.superClass.constructor.call(this) 方法继承。BorderLayout 自定义类继承 ht.ui.drawable.BorderLayout 布局组件,将自身空间分为上、下、左、右、中五个区域,每个区域可放置一个子组件。为了正常交互,重写 getSplitterAt 函数将 splitterRect 宽度修改为 10、调整左侧 splitterCanvas 为了阻挡子组件而重写的尺寸 layoutSplitterCanvas 两个方法:

let BorderLayout = function() { 
             BorderLayout.superClass.constructor.call(this);     this.setContinuous(true);     this.setSplitterSize(0); };  ht.Default.def(BorderLayout
       
        , ht
        .ui
        .BorderLayout
        , 
        { 
          
        // 自定义类 
        /** * splitter 宽度都为 0,为了能正常交互,重写此函数将 splitterRect 的宽度修改为 10 * @override */ getSplitterAt
        : 
        function 
        (event
        ) 
        { 
          
        // 获取事件对象下分隔条所在的区域 
        var leftRect 
        = 
        this
        ._leftSplitterRect
        , lp
        ; 
        if 
        (leftRect
        ) 
        { 
          leftRect 
        = ht
        .Default
        .
        clone
        (leftRect
        )
        ; leftRect
        .width 
        = 
        10
        ; leftRect
        .x 
        -= 
        5
        ; 
        if 
        (event 
        instanceof 
        Event
        ) lp 
        = 
        this
        .
        lp
        (event
        )
        ; 
        else lp 
        = event
        ; 
        if 
        (ht
        .Default
        .
        containsPoint
        (leftRect
        , lp
        )
        ) 
        return 
        'left'
        ; 
        } 
        return BorderLayout
        .superClass
        .getSplitterAt
        .
        call
        (
        this
        , event
        )
        ; 
        }
        , 
        /** * 调整左侧 splitterCanvas 的尺寸,以便挡住子组件 * @override */ layoutSplitterCanvas
        : 
        function
        (canvas
        , x
        , y
        , width
        , height
        , region
        ) 
        { 
          
        if 
        (region 
        === 
        'left'
        ) 
        { 
          canvas
        .style
        .pointerEvents 
        = 
        ''
        ; canvas
        .style
        .display 
        = 
        'block'
        ; ht
        .Default
        .
        setCanvas
        (canvas
        , 
        10
        , height
        )
        ; canvas
        .style
        .left 
        = 
        this
        .
        getContentLeft
        (
        ) 
        + 
        this
        .
        tx
        (
        ) 
        + x 
        - 
        5 
        + 
        'px'
        ; canvas
        .style
        .top 
        = 
        this
        .
        getContentTop
        (
        ) 
        + 
        this
        .
        ty
        (
        ) 
        + y 
        + 
        'px'
        ; 
        } 
        else 
        { 
          BorderLayout
        .superClass
        .layoutSplitterCanvas
        .
        call
        (
        this
        , canvas
        , x
        , y
        , width
        , height
        , region
        )
        ; 
        } 
        } 
        }
        )
        ; 
        export 
        default BorderLayout
        ; 
       

左侧栏

左侧栏 sidebar,分为 8 个部分:顶部 logo、货位统计表格、进度条、分割线、货物表格、图表、管理组、问题反馈按钮等。 可以查看 src/view 下的 sidebar.js 文件,这个 js 文件中同样加载了 src/view/common 下的TreeHoverBackgroundDrawable.js 和 ProgressBarSelectBarDrawable.js 中的 TreeHoverBackgroundDrawable 和 ProgressBarSelectBarDrawable 变量,以及 src/controller 下的 sidebar.js 中的 controller 变量:

import TreeHoverBackgroundDrawable from './common/TreeHoverBackgroundDrawable.js';
import ProgressBarSelectBarDrawable from './common/ProgressBarSelectBarDrawable.js';
import controller from '../controller/sidebar.js';

HT 封装了一个 ht.ui.VBoxLayout 函数,用来将子组件放置在同一垂直列中,我们可以将左侧栏要显示的部分都放到这个组件中,这样所有的部分都是以垂直列排布:

let vBoxLayout = new ht.ui.VBoxLayout(); // 此布局器将子组件放置在同一垂直列中;
vBoxLayout.setBackground('#17191a'); 

顶部 logo 是根据在 Label 标签上添加 icon 的方法来实现的,并将这个 topLabel 添加进垂直列 vBoxLayout 中:

let topLabel = new ht.ui.Label(); // 标签组件
topLabel.setText('Demo-logo'); // 设置文字内容
topLabel.setIcon('imgs/logo.json'); // 设置图标,可以是颜色或者图片等
topLabel.setIconWidth(41);
topLabel.setIconHeight(37);
topLabel.setTextFont('18px arial, sans-serif');
topLabel.setTextColor('#fff');
topLabel.setPreferredSize(1, 64); // 组件自身最合适的尺寸
topLabel.setBackground('rgb(49,98,232)');
vBoxLayout.addView(topLabel, { 
         // 将子组件加到容器中
    width: 'match_parent' // 填满父容器 
});

对于“货位统计表格”,我们采用的是 HT 封装的 TreeTableView 组件,以树和表格的组合方式呈现 DataModel 中数据元素属性及父子关系,并将这个“树表”添加进垂直列 vBoxLayout 中:

let shelfTreeTable = new ht.ui.TreeTableView(); // 树表组件,以树和表格的组合方式呈现 DataModel 中数据元素属性及父子关系
shelfTreeTable.setHoverBackgroundDrawable(new TreeHoverBackgroundDrawable('#1ceddf', 2)); // 设置 hover 状态下行选中背景的 Drawable 对象
shelfTreeTable.setSelectBackgroundDrawable(new TreeHoverBackgroundDrawable('#1ceddf', 2)); // 设置行选中背景的 Drawable 对象 参数为“背景
shelfTreeTable.setBackground(null);
shelfTreeTable.setIndent(20); // 设置不同层次的缩进值
shelfTreeTable.setColumnLineVisible(false); // 设置列线是否可见
shelfTreeTable.setRowLineVisible(false);
shelfTreeTable.setExpandIcon('imgs/expand.json'); // 设置展开图标图标,可以是颜色或者图片等
shelfTreeTable.setCollapseIcon('imgs/collapse.json'); // 设置合并图标图标,可以是颜色或者图片等
shelfTreeTable.setPreferredSizeRowCountLimit(); // 设置计算 preferredSize 时要限制的数据行数
shelfTreeTable.setId('shelfTreeTable');
vBoxLayout.addView(shelfTreeTable, { 
        
    width: 'match_parent',
    height: 'wrap_content', // 组件自身首选高度
    marginTop: 24,
    marginLeft: 4, 
    marginRight: 4
});

我们在设置“行选中”时背景传入了一个 TreeHoverBackgroundDrawable 对象,这个对象是在 src\view\common 下的 TreeHoverBackgroundDrawable.js 文件中定义的,其中 ht.Default.def(className, superClass, methods) 是 HT 中封装的自定义类的函数,其中 className 为自定义类名, superClass 为要继承的父类,methods 为方法和变量声明,要使用这个方法要先在外部定义这个函数变量,通过 functionName.superClass.constructor.call(this) 方法继承。TreeHoverBackgroundDrawable 自定义类继承了 ht.ui.drawable.Drawable 组件用于绘制组件背景、图标等,只重写了 draw 和 getSerializableProperties 两个方法,我们在 draw 方法中重绘了 shelfTreeTable 的行选中背景色,并重载了 getSerializableProperties 序列化组件函数,并将 TreeHoverBackgroundDrawable 传入的参数作为 map 中新添加的属性:

let TreeHoverBackgroundDrawable = function(color, width) { 
        
    TreeHoverBackgroundDrawable.superClass.constructor.call(this);
    this.setColor(color);
    this.setWidth(width);
};
ht.Default.def(TreeHoverBackgroundDrawable, ht.ui.drawable.Drawable, { 
        
    ms_ac: ['color', 'width'],
    draw: function(x, y, width, height, data, view, dom) { 
        
        var self = this,
            g = view.getRootContext(dom),
            color = self.getColor();
       
        g.beginPath();
        g.fillStyle = color;
        g.rect(x, y, self.getWidth(), height);
        g.fill();
    },
    getSerializableProperties: function() { 
        
        var parentProperties = TreeHoverBackgroundDrawable.superClass.getSerializableProperties.call(this);
        return addMethod(parentProperties, { 
        
            color: 1, width: 1
        });
    }
});

记住要导出 TreeHoverBackgroundDrawable :

export default TreeHoverBackgroundDrawable;

HT 还封装了非常好用的 ht.ui.ProgressBar 组件,可直接绘制进度条:

let progressBar = new ht.ui.ProgressBar();
progressBar.setId('progressBar');
progressBar.setBackground('#3b2a00'); // 设置组件的背景,可以是颜色或者图片等
progressBar.setBar('rgba(0,0,0,0)'); // 设置进度条背景,可以是颜色或者图片等
progressBar.setPadding(5);
progressBar.setSelectBarDrawable(new ProgressBarSelectBarDrawable('#c58348', '#ffa866')); // 设置前景(即进度覆盖区域)的 Drawable 对象,可以是颜色或者图片等
progressBar.setValue(40); // 设置当前进度值
progressBar.setBorderRadius(0);
vBoxLayout.addView(progressBar, { 
        
    marginTop: 24,
    width: 'match_parent',
    height: 28,
    marginBottom: 24,
    marginLeft: 14,
    marginRight: 14
});

我们在 设置“前景”的时候传入了一个 ProgressBarSelectBarDrawable 对象,这个对象在 src\view\common 下的 ProgressBarSelectBarDrawable.js 中定义的。具体定义方法跟上面的 TreeHoverBackgroundDrawable 函数对象类似,这里不再赘述。

分割线的制作最为简单,只要将一个矩形的高度设置为 1 即可,我们用 ht.ui.View() 组件来制作:

let separator = new ht.ui.View(); // 所有视图组件的基类,所有可视化组件都必须从此类继承
separator.setBackground('#666');
vBoxLayout.addView(separator, { 
        
    width: 'match_parent',
    height: 1,
    marginLeft: 14, 
    marginRight: 14,
    marginBottom: 24
});

货物表格的操作几乎和货位统计表格相同,这里不再赘述。

我们将一个 json 的图表文件当做图片传给图表的组件容器作为背景,也能很轻松地操作:

let chartView = new ht.ui.View();
chartView.setBackground('imgs/chart.json');
vBoxLayout.addView(chartView, { 
        
    width: 173,
    height: 179,
    align: 'center',
    marginBottom: 10
});

管理组和顶部 logo 的定义方式类似,这里不再赘述。

问题反馈按钮,我们将这个部分用 HT 封装的 ht.ui.Button 组件来制作,并将这个部分添加进垂直列 vBoxLayout 中:

let feedbackButton = new ht.ui.Button(); // 按钮类
feedbackButton.setId('feedbackButton');
feedbackButton.setText('问题反馈:service@hightopo.com');
feedbackButton.setIcon('imgs/em.json');
feedbackButton.setTextColor('#fff');
feedbackButton.setHoverTextColor(shelfTreeTable.getHoverLabelColor()); // 设置 hover 状态下文字颜色
feedbackButton.setActiveTextColor(feedbackButton.getHoverTextColor()); // 设置 active 状态下文字颜色
feedbackButton.setIconWidth(16);
feedbackButton.setIconHeight(16);
feedbackButton.setIconTextGap(10);
feedbackButton.setAlign('left');
feedbackButton.setBackground(null);
feedbackButton.setHoverBackground(null);
feedbackButton.setActiveBackground(null);
vBoxLayout.addView(feedbackButton, { 
        
    width: 'match_parent',
    marginTop: 5,
    marginBottom: 10,
    marginLeft: 20
});

交互

视图部分做好了,在模块化开发中,controller 就是做交互的部分,shelfTreeTable 货位统计表格, cargoTreeTable 货物表格, feedbackButton 问题反馈按钮, progressBar 进度条四个部分的交互都是在在 src/controller 下的 sidebar.js 中定义的。通过 findViewById(id, recursive) 根据id查找子组件,recursive 表示是否递归查找。

shelfTreeTable 货位统计表格的数据绑定传输方式与 cargoTreeTable 货物表格类似,这里我们只对 shelfTreeTable 货位统计表格的数据绑定进行解析。shelfTreeTable 一共有三列,其中不同的部分只有“已用”和“剩余”两个部分,所以我们只要将这两个部分进行数据绑定即可,先创建两列:

let column = new ht.ui.Column(); // 列数据,用于定义表格组件的列信息
column.setName('used'); // 设置数据元素名称
column.setAccessType('attr'); // 在这里 name 为 used,采用 getAttr('used') 和 setAttr('used', 98) 的方式存取 set/getAttr 简写为 a
column.setWidth(65);
column.setAlign('center');
columnModel.add(column);

column = new ht.ui.Column();
column.setName('remain');
column.setAccessType('attr');
column.setWidth(65);
column.setAlign('center');
columnModel.add(column);

接着遍历 json 文件,将 json 文件中对应的 used、remain以及 labelColors 通过 set/getAttr 或 简写 a 的方式进行数据绑定:

for (var i = 0; i < json.length; i++) { 
        
    var row = json[i]; // 获取 json 中的属性
    var data = new ht.Data();
    data.setIcon(row.icon); // 将 json 中的 icon 传过来
    data.setName(row.name);
    data.a('used', row.used);
    data.a('remain', row.remain);
    data.a('labelColors', row.colors);
    data.setIcon(row.icon);
    treeTable.dm().add(data); // 在树表组件的数据模型中添加这个 data 节点
    var children = row.children;
    if (children) { 
        
        for (var j = 0; j < children.length; j++) { 
        
            var child = children[j];
            var childData = new ht.Data();
            childData.setName(child.name);
            childData.setIcon(child.icon);
            childData.a('used', child.used);
            childData.a('remain', child.remain);
            childData.a('labelColors', child.colors);
            childData.setParent(data);
            treeTable.dm().add(childData);
        }
    }
}

最后在 controller 函数对象中调用 这个函数:

initTreeTableDatas(shelfTreeTable, json); // json 为 ../model/shelf.json 传入

progressBar 进度条的变化是通过设置定时器改变 progressBar 的 value 值来动态改变的:

setInterval(() => { 
        
    if (progressBar.getValue() >= 100) { 
        
        progressBar.setValue(0);
    }
    progressBar.setValue(progressBar.getValue() + 1);
}, 50);

feedbackButton 问题反馈按钮,通过增加 View 事件监听器来监听按钮的点击事件:

feedbackButton.addViewListener(e => { 
        
    if (e.kind === 'click') { 
         // HT 自定义的事件属性,具体查看 http://hightopo.com/guide/guide/core/beginners/ht-beginners-guide.html
        window.location.href = "mailto:service@www.hightopo.com"; // 当前页面打开URL页面
    }
});

右侧容器splitLayout

直接用的分割组件 ht.ui.SplitLayout 进行分割布局:

let splitLayout = new ht.ui.SplitLayout(); // 此布局器将自身空间划分为上、下两个区域或左、右两个区域,每个区域可以放置一个子组件
splitLayout.setSplitterVisible(false);
splitLayout.setPositionType('absoluteFirst');
splitLayout.setOrientation('v');

右侧头部 header 这个 header 是从 src/view 下的 header.js 中获取的对象,为 ht.ui.RelativeLayout 相对定位布局器,分为 5 个部分:searchField 搜索框、titleLabel 主标题、temperatureLabel1 温度、humidityLabel1 湿度以及 airpressureLabel1 气压。

这里我们没有对“搜索框” searchField 进行数据绑定,以及搜索的功能,这只是一个样例,不涉及业务部分:

let searchField  

标签: e4进口传感器tx91

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台