【IoT物联网】IoT小程序在展示中央空调采集数据和实时运行状态上的应用

news2024/11/17 21:30:34

  利用前端语言实现跨平台应用开发似乎是大势所趋,跨平台并不是一个新的概念,“一次编译、到处运行”是老牌服务端跨平台语言Java的一个基本特性。随着时代的发展,无论是后端开发语言还是前端开发语言,一切都在朝着减少工作量,降低工作成本的方向发展。
  和后端开发语言不同,利用前端语言实现跨平台有先天的优势,比如后端语言Java跨平台需要将源代码编译为class字节码文件后,再放进 Java 虚拟机运行;而前端语言JavaScript是直接将源代码放进JavaScript解释器运行。这就使得以JavaScript为跨平台语言开发的应用,可移植性非常强大。
  目前跨平台技术按照解决方案分类,主要分为 Web 跨平台、容器跨平台、小程序跨平台。这里,我们主要以小程序跨端为例,测试对比IoT小程序和其他小程序在开发和应用上的优缺点。说到小程序,大家肯定想到微信小程序,实际在各大互联网公司:支付宝、百度、头条等等都有自己的小程序,小程序跨平台和Web跨平台十分类似,都是基于前端语言实现,小程序跨平台的优势在于可以调用系统底层能力,例如:蓝牙、相机等,性能方面也优于Web跨平台。
  IoT小程序和大多数小程序一样,它是一套跨平台应用显示框架,它利用JS语言低门槛和API标准化大幅度降低了IoT应用的研发难度,其官方框架介绍如下:
框架介绍
  IoT小程序在前端框架能力、应用框架能力、图形框架能力都进行了适配和优化。那么接下来,我们按照其官方步骤搭建开发环境,然后结合中央空调数据采集和状态显示的实际应用场景开发物联网小程序应用。

一、IoT小程序开发环境搭建

  IoT小程序开发环境搭建一共分为四步,对于前端开发来说,安装NodeJS、配置cnpm、安装VSCode都是轻车熟路,不需要细讲,唯一不同的是按照官方说明安装IoT小程序的模拟器和VSCode开发插件HaaS UI,前期开发环境准备完毕,运行Demo查看一下效果,然后就可以进行IoT小程序应用开发了。

搭建开发环境,安装HaaS UI插件和运行新建项目,出现一下界面说明开发环境搭建成功,就可以进行IoT小程序开发了:
项目运行

二、开发展示中央空调采集数据和运行状态的IoT小程序应用

  • 应用场景

  中央空调的维保单位会对中央空调进行定期维护保养,定期的维护保养可排出故障隐患,减少事故发生,降低运行费用,延长设备的使用寿命,同时保障正常的工作时序。除了定期的维护保养外,还需要实时监测中央空调的运行参数(温度、累计排污量、不锈钢_腐蚀率等)和运行状态,及时发现中央空调运行过程中某些参数低于或高于报警值的问题,以便及时定位诊断中央空调存在的问题,然后进行相应的维护保养操作。

  • 架构实现

  中央空调的数据采集和展示是典型的物联网应用架构,在中央空调端部署采集终端,通过Modbus通信协议采集中央空调设备参数,然后再由采集终端通过MQTT消息发送的我们的云端服务器,云端服务器接收到MQTT消息后转发到消息队列Kafka中,由云服务器上的自定义服务应用订阅Kafka主题,再存储到我们时序数据库中。

  下图展示了物联网应用的整体架构和IoT小程序在物联网架构中的位置:

中央空调物联网

  IoT小程序框架作为跨平台应用显示框架,顾名思义,其在物联网应用中主要作为显示框架开发。在传统应用中,我们使用微信小程序实现采集数据和运行状态的展示。而IoT小程序支持部署在AliOS Things、Ubuntu、Linux、MacOS、Window等系统中,这就使得我们可以灵活的将IoT小程序部署到多种设备终端中运行。
  下面将以阿里云ASP-80智显面板为例,把展示中央空调采集数据和运行状态的IoT小程序部署在阿里云ASP-80智显面板中。

  • IoT小程序开发

  我们将从IoT小程序提供的前端框架能力、应用框架能力、图形框架能力来规划相应的功能开发。

  • 前端框架能力
      IoT小程序采用Vue.js(v2.6.12)开源框架,实现了W3C标准的标签和样式子集;定义了四个应用生命周期,分别是:onLaunch,onShow,onHide,onDestroy;定义了十四个前端基础组件,除了基础的CSS样式支持外,还提供了对Less的支持;Net网络请求通过框架内置的JSAPI实现。
      为了快速熟悉IoT小程序框架的开发方式,我们将在VSCode中导入官方公版案例,并以公版案例为基础框架开发我们想要的功能。
    简单实现通过网络请求获取中央空调采集数据并展示:
  1. 在VSCode编辑器中导入从IoT小程序官网下载的公版案例,下载地址。
  2. 因为IoT小程序前端框架使用的是Vue.js框架,所以在新增页面时也是按照Vue.js框架的模式,将页面添加到pages目录。我们是空调项目的IoT小程序,所以这里在pages目录下新增air-conditioning目录用于存放空调IoT小程序相关前端代码。

新增前端代码目录

  1. 在app.json中配置新增的页面,修改pages项,增加"air-conditioning": “pages/air-conditioning/index.vue”。
{
  "pages": {
......
    "air-conditioning": "pages/air-conditioning/index.vue",
......
  },
  "options": {
    "style": {
      "theme": "theme-dark"
    }
  }
}
  1. 在air-conditioning目录下新增index.vue前端页面代码,用于展示空调的采集数据是否正常及历史曲线图。设计需要开发的界面如下,页面的元素有栅格布局、Tabs 标签页、Radio单选框、日期选择框、曲线图表等元素。
    采集数据
    曲线图表
  2. 首先是实现Tabs标签页,IoT小程序没有Tabs组件,只能自己设置多个Text组件自定义样式并添加click事件来实现。
    <div class="tab-list">
      <fl-icon name="back" class="nav-back" @click="onBack" />
      <text
        v-for="(item, index) in scenes"
        :key="index"
        :class="'tab-item' + (index === selectedIndex ? ' tab-item-selected' : '')"
        @click="tabSelected(index)"
        >{{ item }}</text
      >
    </div>
......
  data() {
    return {
      scenes: ["设备概览", "实时数据", "数据统计", "状态统计"],
      selectedIndex: 0
    };
  },
......

Tabs实现效果

6、添加采集数据显示列表,在其他小程序框架中,尤其是以Vue.js为基础框架的小程序框架,这里有成熟的组件,而IoT小程序也是需要自己来实现。

<template>
  <div class="scene-wrapper" v-if="current">
    <div class="label-temperature-wrapper top-title">
      <div class="label-temperature-wrapper left-text">
        <text class="label-temperature">设备编码:</text>
        <text class="label-temperature-unit">97306000000000005{{content}}</text>
      </div>
      <div class="label-temperature-wrapper right-text">
        <text class="label-temperature">数据日期:</text>
        <text class="label-temperature-unit">2023-03-11 23:59:59{{content}}</text>
      </div>
    </div>
    <div class="main-wrapper">
      <div class="section">
        <div class="demo-block icon-block">
          <div class="icons-item" v-for="(value, key, index) in IconTypes" :key="index">
            <div class="label-title-wrapper">
              <text class="label-title left-text">电导率</text>
              <text class="label-title-unit right-text" style="padding-right: 5px;">正常</text>
            </div>
            <div class="label-zhibiao-wrapper">
              <text class="label-zhibiao">当前值:</text>
              <text class="label-zhibiao-unit">56.36{{content}}</text>
            </div>
            <div class="label-zhibiao-wrapper" style="margin-bottom: 10px;">
              <text class="label-zhibiao">目标值:</text>
              <text class="label-zhibiao-unit">63.32{{content}}</text>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

采集数据展示界面

  在开发过程中发现,IoT小程序对样式的支持不是很全面,本来想将 组件放置在同一行,一般情况下,只需要使用标准CSS样式display: inline就可以实现,但这里没有效果只能通过Flexbox进行布局在同一行。在设置字体方面,本来想把采集数据显示的描述字段加粗,用于突出显示,但是使用CSS样式font-weight无效,无论是设置数值还是blod,没有一点加粗效果。
7. 界面实现之后,需要发送数据请求,来查询采集数据并显示在界面上。IoT小程序通过框架内置JSAPI的Net网络提供网络请求工具。目前从官方文档和代码中来看,官方框架只提供了http请求,没有提供物联网中常用的WebSocket和MQTT工具,估计需要自定义扩展系统JSAPI实现其他网络请求。

  created() {
    const http = $falcon.jsapi.http
    http.request({
      url: 'http://服务域名/device/iot/query/data/point',
      data: {
        'deviceId': '97306000000000005',
        'rangeType': 'mo',
        'lastPoint': '1',
        'beginDateTime': '2023-02-10+16:09:42',
        'endDateTime': '2023-03-12+16:09:42'
      },
      header: {
        'Accept': 'application/json;charset=UTF-8',
        'Accept-Encoding': 'gzip, deflate, br',
        'Content-Type': 'application/json;charset=UTF-8',
        'Authorization': '有效token'
      }
      }, (response) => {
        console.log(response)
        var obj = JSON.parse(response.result)
        console.log(obj.success)
        console.log(JSON.parse(obj.data))
      });
  },

  按照官方要求编写http请求,发现默认未开启https请求:Protocol “https” not supported or disabled in libcurl。切换为http请求,返回数据为乱码,设置Accept-Encoding和Accept为application/json;charset=UTF-8仍然无效,且返回数据为JSON字符串,需要自己手动使用JSON.parse()进行转换,对于习惯于应用成熟框架的人来说,十分不友好。想了解更多关于 $falcon.jsapi.http的相关配置和实现,但是官方文档只有寥寥几句,没有详细的说明如何使用和配置,以及http请求中遇到一些常见问题的解决方式。
8. IoT小程序框架提供画布组件,原则上来讲可以实现常用的曲线图表功能,但是如果使用其基础能力从零开始开发一套图表系统,耗时又耗力,所以这里尝试引入常用的图表组件库ECharts,使用ECharts在IoT小程序上显示曲线图表。

  • 执行cnpm install echarts --save安装echarts组件
cnpm install echarts --save
  • 新建echarts配置文件,按需引入
// 加载echarts,注意引入文件的路径
import echarts from 'echarts/lib/echarts'

// 再引入你需要使用的图表类型,标题,提示信息等
import 'echarts/lib/chart/bar'
import 'echarts/lib/chart/pie'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/title'
import 'echarts/lib/component/tooltip'

export default echarts
  • 新增echarts组件ChartDemo.vue
<template>
    <div ref="chartDemo" style="height:200px;" ></div>
</template>
<script>
  import echarts from '@/utils/echarts-config.js'
  const ChartDemo = {
    name: 'ChartDemo',
    data() {
      return {
        chart: null
      }
    },
    watch: {
      option: {
        handler(newValue, oldValue) {
          this.chart.setOption(newValue)
        },
        deep: true
      }
    },
    mounted() {
      this.chart = echarts.init(this.$refs.chartDemo)
    },
    methods: {
      setOption(option) {
        this.chart && this.chart.setOption(option)
      },
      throttle(func, wait, options) {
        let time, context, args
        let previous = 0
        if (!options) options = {}
  
        const later = function() {
          previous = options.leading === false ? 0 : new Date().getTime()
          time = null
          func.apply(context, args)
          if (!time) context = args = null
        }
  
        const throttled = function() {
          const now = new Date().getTime()
          if (!previous && options.leading === false) previous = now
          const remaining = wait - (now - previous)
          context = this
          args = arguments
          if (remaining <= 0 || remaining > wait) {
            if (time) {
              clearTimeout(time)
              time = null
            }
            previous = now
            func.apply(context, args)
            if (!time) context = args = null
          } else if (!time && options.trailing !== false) {
            time = setTimeout(later, remaining)
          }
        }
        return throttled
      }
    }
  }
  export default ChartDemo
</script>
  
  
  • 在base-page.js中注册全局组件
......
import ChartDemo from './components/ChartDemo.vue';
export class BasePage extends $falcon.Page {
  constructor() {
    super()
  }
  beforeVueInstantiate(Vue) {
    ......
    Vue.component('ChartDemo', ChartDemo);
  }
}
  • 新建空调采集数据展示页history-charts.vue,用于展示Echarts图表
<template>
  <div class="scene-wrapper" v-if="current">
    <div class="brightness-wrap">
      <ChartBlock ref="chart2"></ChartBlock>
    </div>
  </div>
</template>

<script>
let option2 = {
  title: {
    text: '某站点用户访问来源',
    subtext: '纯属虚构',
    left: 'center'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b} : {c} ({d}%)'
  },
  legend: {
    orient: 'vertical',
    left: 'left',
    data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
  },
  series: [
    {
      name: '访问来源',
      type: 'pie',
      radius: '55%',
      center: ['50%', '60%'],
      data: [
        { value: 335, name: '直接访问' },
        { value: 310, name: '邮件营销' },
        { value: 234, name: '联盟广告' },
        { value: 135, name: '视频广告' },
        { value: 1548, name: '搜索引擎' }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ]
}

export default {
  props:{
    current:{
      type:Boolean,
      default:false
    }
  },
  data() {
    return {
    };
  },
  methods: {
  },
  mounted: function() {
    this.$refs.chart2.setOption(option2)
  }
};
</script>
  • 执行HaaS UI: Build-Debug ,显示打包成功
    打包成功

  • 执行HaaS UI: Simulator ,显示“当前HaaS UI: Simulator任务正在执行,请稍后再试”
    稍后再试

  本来想在模拟器上看一下Echarts显示效果,但是执行HaaS UI: Simulator时一直显示任务正在执行。然后以为是系统进程占用,但是重启、关闭进程等操作一系列操作下来,仍然显示此提示,最后将Echarts代码删除,恢复到没有Echarts的状态,又可以执行了。这里不清楚是否是IoT小程序不支持引入第三方图表组件,从官方文档中没有找到答案。后来又使用echarts的封装组件v-charts进行了尝试,结果依然不能展示。
  如果不能使用第三方组件,那么只能使用IoT官方小程序提供的画布组件来自己实现图表功能,官方提供的画布曲线图示例。
9. 通过IoT小程序提供的组件分别实现显示中央空调采集数据的实时数据、数据统计、状态统计图表。
-实现实时数据折线图

<template>
  <div class="scene-wrapper"  v-show="current">
    <div class="main-wrapper">
        <div class="label-temperature-wrapper top-title">
        <div class="label-temperature-wrapper left-text">
            <text class="label-temperature">设备编码:</text>
            <text class="label-temperature-unit">97306000000000005</text>
        </div>
        <div class="label-temperature-wrapper right-text">
            <text class="label-temperature">数据日期:</text>
            <text class="label-temperature-unit">2023-03-11 23:59:59</text>
        </div>
        </div>
        <canvas ref="c2" class="canvas" width="650" height="300"></canvas>
    </div>
  </div>
</template>
  
  <script>
  export default {
    name: "canvas",
    props: {},
    data() {
      return {};
    },
    mounted() {
      this.c2();
    },
    methods: {
      c2() {
        let ctx = typeof createCanvasContext === "function" ? createCanvasContext(this.$refs.c2) : this.$refs.c1.getContext("2d");
        let arr = [{key:'01:00',value:61.68},{key:'02:00',value:83.68},{key:'03:00',value:56.68},{key:'04:00',value:86.68},{key:'05:00',value:53.68},
        {key:'06:00',value:41.68},{key:'07:00',value:33.68}];
        this.drawStat(ctx, arr);
      },
      //该函数用来绘制折线图
      drawStat(ctx, arr) {
          //画布的款高
          var cw = 700;
          var ch = 300;
          //内间距padding
          var padding = 35;
          //原点,bottomRight:X轴终点,topLeft:Y轴终点
          var origin = {x:padding,y:ch-padding};
          var bottomRight = {x:cw-padding,y:ch-padding};
          var topLeft = {x:padding,y:padding};
  
          ctx.strokeStyle='#FF9500';
          ctx.fillStyle='#FF9500';
  
          //绘制X轴
          ctx.beginPath();
          ctx.moveTo(origin.x,origin.y);
          ctx.lineTo(bottomRight.x,bottomRight.y);
          //绘制X轴箭头
          ctx.lineTo(bottomRight.x-10,bottomRight.y-5);
          ctx.moveTo(bottomRight.x,bottomRight.y);
          ctx.lineTo(bottomRight.x-10,bottomRight.y+5);
          //绘制Y轴
          ctx.moveTo(origin.x,origin.y);
          ctx.lineTo(topLeft.x,topLeft.y);
          //绘制Y轴箭头
          ctx.lineTo(topLeft.x-5,topLeft.y+10);
          ctx.moveTo(topLeft.x,topLeft.y);
          ctx.lineTo(topLeft.x+5,topLeft.y+10);

          //设置字号
          var color = '#FF9500';
          ctx.fillStyle=color;
          ctx.font = "13px scans-serif";//设置字体
  
          //绘制X方向刻度
          //计算刻度可使用的总宽度
          var avgWidth = (cw - 2*padding - 50)/(arr.length-1);
          for(var i=0;i<arr.length;i++){
              //循环绘制所有刻度线
              if(i > 0){
                  //移动刻度起点
                  ctx.moveTo(origin.x+i*avgWidth,origin.y);
                  //绘制到刻度终点
                  ctx.lineTo(origin.x+i*avgWidth,origin.y-10);
              }
              //X轴说明文字:1月,2月...
              var txtWidth = 35;
              ctx.fillText(
                      arr[i].key,
                      origin.x+i*avgWidth-txtWidth/2 + 10,
                      origin.y+20);
          }
          //绘制Y方向刻度
          //最大刻度max
          var max = 0;
          for(var i=0;i<arr.length;i++){
              if(arr[i].value>max){
                  max=arr[i].value;
              }
          }
          console.log(max);
  
          /*var max = Math.max.apply(this,arr);
          console.log(max);*/
          var avgValue=Math.floor(max/5);
          var avgHeight = (ch-padding*2-50)/5;
          for(var i=1;i<arr.length;i++){
              //绘制Y轴刻度
              ctx.moveTo(origin.x,origin.y-i*avgHeight);
              ctx.lineTo(origin.x+10,origin.y-i*avgHeight);
              //绘制Y轴文字
              var txtWidth = 40;
              ctx.fillText(avgValue*i,
                      origin.x-txtWidth-5,
                      origin.y-i*avgHeight+6);
          }
  
          //绘制折线
          for(var i=0;i<arr.length;i++){
              var posY = origin.y - Math.floor(arr[i].value/max*(ch-2*padding-50));
              if(i==0){
                  ctx.moveTo(origin.x+i*avgWidth,posY);
              }else{
                  ctx.lineTo(origin.x+i*avgWidth,posY);
              }
              //具体金额文字
              ctx.fillText(arr[i].value,
                      origin.x+i*avgWidth,
                      posY
              )
          }
          ctx.stroke();
          //绘制折线上的小圆点
          ctx.beginPath();
          for(var i=0;i<arr.length;i++){
              var posY = origin.y - Math.floor(arr[i].value/max*(ch-2*padding-50));
              ctx.arc(origin.x+i*avgWidth,posY,4,0,Math.PI*2);//圆心,半径,画圆
              ctx.closePath();
          }
          ctx.fill();
      }
    }
  };
  </script>

实时数据折线图
-数据统计图表

<template>
  <div class="scene-wrapper"  v-show="current">
    <div class="main-wrapper">
        <div class="label-temperature-wrapper top-title">
        <div class="label-temperature-wrapper left-text">
            <text class="label-temperature">设备编码:</text>
            <text class="label-temperature-unit">97306000000000005</text>
        </div>
        <div class="label-temperature-wrapper right-text">
            <text class="label-temperature">数据日期:</text>
            <text class="label-temperature-unit">2023-03-13 20:29:36</text>
        </div>
        </div>
        <canvas ref="c1" class="canvas" width="650" height="300"></canvas>
    </div>
  </div>
</template>
<script>
  export default {
    name: "canvas",
    props: {},
    data() {
      return {};
    },
    mounted() {
      this.c1();
    },
    methods: {
      c1() {
        let ctx = typeof createCanvasContext === "function" ? createCanvasContext(this.$refs.c1) : this.$refs.c1.getContext("2d");
             this.draw(ctx);
      },
      draw(ctx){
        var x0=30,//x轴0处坐标
              y0=280,//y轴0处坐标
              x1=700,//x轴顶处坐标
              y1=30,//y轴顶处坐标
              dis=30;
                //先绘制X和Y轴
               ctx.beginPath();
               ctx.lineWidth=1; 
               ctx.strokeStyle='#FF9500';
          ctx.fillStyle='#FF9500';
               ctx.moveTo(x0,y1);//笔移动到Y轴的顶部
               ctx.lineTo(x0,y0);//绘制Y轴
               ctx.lineTo(x1,y0);//绘制X轴
               ctx.stroke();
                 
               //绘制虚线和Y轴值  
               var yDis = y0-y1;
               var n=1;
               ctx.fillText(0,x0-20,y0);//x,y轴原点显示0
               while(yDis>dis){
                    ctx.beginPath();
                    //每隔30划一个虚线
                    ctx.setLineDash([2,2]);//实线和空白的比例
                    ctx.moveTo(x1,y0-dis);
                    ctx.lineTo(x0,y0-dis);
                    ctx.fillText(dis,x0-20,y0-dis);
                    //每隔30划一个虚线
                    dis+=30;
                    ctx.stroke();
               }
               
               var xDis=30,//设定柱子之前的间距
               width=40;//设定每个柱子的宽度
                  //绘制柱状和在顶部显示值
               for(var i=0;i<12;i++){//假设有8个月
                   ctx.beginPath();
                   var color = '#' + Math.random().toString(16).substr(2, 6).toUpperCase();//随机颜色
                   ctx.fillStyle=color;
                   ctx.font = "13px scans-serif";//设置字体
                   
                   var height = Math.round(Math.random()*220+20);//在一定范围内随机高度
                   
                   var rectX=x0+(width+xDis)*i,//柱子的x位置
                   rectY=height;//柱子的y位置
                   ctx.color='#FF9500';
                   ctx.fillText((i+1)+'月份',rectX,y0+15);//绘制最下面的月份稳住
                   
                    ctx.fillRect(rectX,y0, width, -height);//绘制一个柱状
                    
                    ctx.fillText(rectY,rectX+10,280-rectY-5);//显示柱子的值
              }
          },
    }
  };
</script>

数据统计图表
-状态统计图表

<template>
    <div class="scene-wrapper"  v-show="current">
      <div class="main-wrapper">
          <div class="label-temperature-wrapper top-title">
          <div class="label-temperature-wrapper left-text">
              <text class="label-temperature">设备编码:</text>
              <text class="label-temperature-unit">97306000000000005</text>
          </div>
          <div class="label-temperature-wrapper right-text">
              <text class="label-temperature">数据日期:</text>
              <text class="label-temperature-unit">2023-03-13 20:29:36</text>
          </div>
          </div>
          <canvas ref="c3" class="canvas" width="600" height="300"></canvas>
      </div>
    </div>
</template>
<script>
    export default {
      name: "canvas",
      props: {},
      data() {
        return {};
      },
      mounted() {
        this.c3();
      },
      methods: {
        c3() {
          let ctx = typeof createCanvasContext === "function" ? createCanvasContext(this.$refs.c3) : this.$refs.c3.getContext("2d");
          this.drawPie(ctx);
        },
        drawPie(pen){
          //假数据
          var deg = Math.PI / 180
          var arr = [
             {
                 name: "开机",
                 time: 8000,
                 color: '#7CFF00'
             },
             {
                 name: "关机",
                 time: 1580,
                 color: '#737F9C'
             },
             {
                 name: "空闲",
                 time: 5790,
                 color: '#0ECC9B'
             },
             {
                 name: "故障",
                 time: 4090,
                 color: '#893FCD'
             },
             {
                 name: "报警",
                 time: 2439,
                 color: '#EF4141'
             },
          ];
          //总价
          pen.translate(30,-120);
          arr.tatol = 0;
          for (let i = 0; i < arr.length; i++) {
              arr.tatol = arr.tatol + arr[i].time
          }
          var stardeg = 0
          arr.forEach(el => {
              pen.beginPath()
              var r1 = 115
              pen.fillStyle = el.color
              pen.strokeStyle='#209AAD';
              pen.font = "15px scans-serif";
              //求出每个time的占比
              var angle = (el.time / arr.tatol) * 360
              //利用占比来画圆弧
              pen.arc(300, 300, r1, stardeg * deg, (stardeg + angle) * deg)
              //将圆弧与圆心相连接,形成扇形
              pen.lineTo(300, 300)
              var r2 = r1+10;
              if(el.name === '关机' || el.name === '空闲')
              {
                  r2 = r1+30
              }
              //给每个扇形添加数组的name
              var y1 = 300 + Math.sin((stardeg + angle) * deg-angle*deg/2 ) *( r2)
              var x1 = 300 + Math.cos((stardeg + angle) * deg-angle*deg/2 ) * (r2)
              pen.fillText(`${el.name}`, x1, y1)
              stardeg = stardeg + angle
              pen.fill()
              pen.stroke()
          });
        },
      }
    };
</script>

状态统计图表

三、将IoT小程序更新到ASP-80智显面板查看运行效果

  将IoT小程序更新到ASP-80智显面板,在硬件设备上查看IoT应用运行效果。如果是使用PC端初次连接,那么需要安装相关驱动和配置,否则无法使用VSCode直接更新IoT小程序到ASP-80智显面板。

  1. 如果使用Win10将IoT小程序包更新到ASP-80智显面板上,必须用到CH340串口驱动,第一次通过TypeC数据线连接设备,PC端设备管理器的端口处不显示端口,这时需要下载Windows版本的CH340串口驱动下载链接 。
    不显示端口
    下载链接
  2. 将下载的驱动文件CH341SER.ZIP解压并安装之后,再次查看PC端设备管理器端口就有了USB Serial CH340端口。

驱动安装成功

USB Serial CH340端口

  1. 使用SourceCRT连接ASP-80智显面板,按照官方文档说明,修改配置文件,连接好WiFi无线网,下一步通过VSCode直接更新IoT小程序到ASP-80智显面板上查看测试。

连接ASP-80智显面板

获取ASP-80智显面板的IP地址
4. 所有准备工作就绪后,点击VSCode的上传按钮HaaS UI: Device,将应用打包并上传至ASP-80智显面板。在选择ip地址框的时候,输入我们上一步获取到的ip地址192.168.1.112,其他参数保持默认即可,上传成功后,VSCode控制台提示安装app成功。
app安装成功提示

  1. IoT小程序安装成功之后就可以在ASP-80智显面板上查看运行效果了。
    设备概览
    实时数据
    数据统计
    状态统计

  综上所述,IoT小程序框架在跨系统平台(AliOS Things、Ubuntu、Linux、MacOS、Window等)方面提供了非常优秀的基础能力,应用的更新升级提供了多种方式,在实际业务开发过程中可以灵活选择。IoT小程序框架通过JSAPI提供了调用系统底层应用的能力,同时提供了自定义JSAPI扩展封装的方法,这样就足够业务开发通过自定义的方式满足特殊的业务需求。
  虽然多家互联网公司都提供了小程序框架,但在128M 128M这样的低资源设备里输出,IoT小程序是比较领先的,它不需要另外下载APP作为小程序的容器,降低了资源的消耗,这一点是其他小程序框架所不能比拟的。
  但是在前端框架方面,实用组件太少。其他小程序已发展多年,基于基础组件封装并开源的前端组件应用场景非常丰富,对于中小企业来说,习惯于使用成熟的开源组件,如果使用IoT小程序开发物联网应用可能需要耗费一定的人力物力。既然是基于Vue.js的框架,却没有提供引入其他优秀组件的文档说明和示例,不利于物联网应用的快速开发,希望官方能够完善文档,详细说明IoT小程序开发框架配置项,将来能够提供更多的实用组件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/727210.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于知识图谱的《红楼梦》人物关系可视化及问答系统(含码源):命名实体识别、关系识别、LTP简单教学

基于知识图谱的《红楼梦》人物关系可视化及问答系统&#xff08;含码源&#xff09;&#xff1a;命名实体识别、关系识别、LTP简单教学 文件树: app.py是整个系统的主入口templates文件夹是HTML的页面 |-index.html 欢迎界面 |-search.html 搜索人物关系页面 |-all_relation.…

JMeter参数化(6)

JMeter参数化 一、JMeter用户参数1、作用2、案例1&#xff1a;百度搜索接口&#xff1a;使用用户参数模拟不同线程操作时&#xff0c;搜索不同的数据 二、用户自定义变量案例1&#xff1a;演示用户定义的变量具有全局性案例2&#xff1a;使用用户定义的变量&#xff0c;分组管理…

基于Java的电影购票系统的设计与实现(源码+文档+数据库)

本系统是一个网上电影售票系统&#xff0c;可以为用户提供方便的在线订票环境。主要实现了用户注册、登录、查询、订购电影票、管理已订购电影票等功能。通过后台管理模块可以实现对用户、播放厅、电影、影片安排、电影票等的管理等功能。本系统以IntelliJ IDEA 作为开发环境&a…

2.3 Web应用 -- 2. HTTP 连接

2.3 Web应用 -- 2. HTTP 连接 HTTP连接的两种类型非持久性连接响应时间分析与建模持久性HTTP HTTP连接的两种类型 非持久性连接(Nonpersistent HTTP) 每个TCP连接最多允许传输一个对象HTTP 1.0版本使用非持久性连接 持久性连接(Persistent HTTP) 每个TCP连接允许传输多个对象H…

vue3项目创建部署

别名联想路径提示&#xff1a; Element-plus默认的颜色是蓝色&#xff0c;以下修改成我们自己的主题色&#xff1a; css预处理语言 “scss是一种css预处理语言,是一个css的扩展,它在css语法的基础上,允许您使用变量,嵌套规则,混合,导入,继承等功能,使得css更加强大和优雅,而…

Jetson Orin Nano Developer Kit

Jetson Orin Nano Developer Kit包括Jetson Orin Nano 8GB模块&#xff0c;该模块具有NVIDIA安培GPU(具有1024个CUDA内核和32个第三代张量内核)和6核ARM CPU&#xff0c;能够运行多个并发AI应用程序管道并提供高推断性能。 开发套件载体板支持所有Jetson Orin Nano和Orin NX模块…

2023年如何通过8个步骤创建社交媒体内容策略

在创建成功的社交媒体内容策略时&#xff0c;感觉有无穷无尽的选项和平台可供选择。但不要因此而不知所措&#xff0c;因为只要稍作规划并发挥大量创造力&#xff0c;您就可以制定有助于提升品牌知名度、提高客户参与度和推动销售的战略。 目录 定义社交媒体内容配方的八个…

基于.Net Core实现的飞书所有文档一键导出服务(支持多系统)

feishu-doc-export 一个支持Windows、Mac、Linux系统的飞书文档一键导出服务&#xff0c;仅需一行命令即可将飞书知识库的全部文档同步到本地电脑。导出速度嘎嘎快&#xff0c;实测700多个文档导出只需25分钟&#xff0c;且程序是后台挂机运行&#xff0c;不影响正常工作。 动…

python爬虫爬取top250中排名、评分、导演等展示可视化界面

源代码4千字报告 需要源代码数据库可视化数据4千字报告加我qq

基于matlab评估单相机校准的准确性(附源码)

一、前言 相机校准是使用特殊校准模式的图像估计相机参数的过程。参数包括相机内在系数、失真系数和相机外在系数。校准相机后&#xff0c;有几种方法可以评估估计参数的准确性&#xff1a; 绘制相机的相对位置和校准模式 计算重投影误差 计算参数估计误差 二、校准相机 …

Spring Boot 数据访问框架介绍及使用

Spring Boot 数据访问框架介绍及使用 Spring Boot 是一个流行的 Java 应用程序框架&#xff0c;它提供了许多工具和库&#xff0c;帮助开发人员快速构建高效的应用程序。其中&#xff0c;Spring Boot 数据访问框架是其中一个重要的组件&#xff0c;它提供了许多不同的选项&…

短视频抖音多账号管理系统源码搭建开发(路径一)

一、开发环境 目录 一、开发环境 二、短视频账号矩阵系统源码功能构建模型 短视频抖音多账号管理系统源码搭建开发&#xff0c;视频抖音多账号管理系统源码的开发环境配置非常重要。 1、首先&#xff0c;确保您的计算机已经安装了适当的开发工具&#xff0c;如Java SDK、An…

(css)文字与底部对齐

(css)文字与底部对齐 修改前&#xff1a; 修改后&#xff1a; 代码&#xff1a; .AITip {height: 11%;color: #01ffff;font-size: 24px;//主要属性display: flex;justify-content: center;align-items: flex-end;line-height: 1; }解决参考&#xff1a;https://blog.csdn.n…

多家快递如何跟踪物流信息,并快速掌握了每条物流信息时间差呢

对于很电商商家朋友们他平时一般都是发多家快递&#xff0c;多家快递公司的物流信息处理和管理是一个挑战。很多公司在处理这些信息时需要耗费大量人力和时间&#xff0c;为了提高工作效率和降低成本&#xff0c;需要采用一些高效的管理和利用方法。例如使用第三方的工具来&…

路由的介绍

目录 路由器的转发原理&#xff1a;路由表 路由——指示路由器去往未知网段的方法 路由器的转发原理&#xff1a;路由表 当一个数据包来到路由器&#xff0c;路由器将基于数据包中的目标IP地址查询自身的路由表&#xff0c;如果路由表中有相应的记录&#xff0c;则无条件根据…

Windows系统显示语言的修改及“我们无法获取此Windows显示语言”报错的处理

本文介绍在Windows 10操作系统中&#xff0c;修改系统的显示语言的方法&#xff1b;并解决在这一过程中&#xff0c;出现的“很抱歉&#xff0c;我们无法获取此Windows显示语言”报错问题。 本文就以将原本系统语言的中文修改为英文为例&#xff0c;介绍具体的方法。首先&#…

java 8 stream流之大数据篇

如果你会任意一门语言的stream流&#xff0c;没道理不会大数据开发。 俗话说男追女隔座山&#xff0c;女追男隔层纱。 如果说零基础学大数据&#xff0c;感觉前面是一座山&#xff0c;那么只要你会java或者任意一门语言的stream流&#xff0c;那大数据就只隔了一层纱。 本文以…

gazebo软件创造.world文件。

Gazebo可以创造数据。在Gazebo中创建一个机器人世界&#xff0c;不仅可以仿真机器人的运动功能&#xff0c;还可以仿真机器人的传感器数据。 背景&#xff1a;我是个新接触ros2的小白&#xff0c;变学习试用成功后分享狭窄的片面知识&#xff0c;必然不全&#xff0c;请多包含。…

高精度算法

&#xff08;一&#xff09;算法笔记 &#xff08;二&#xff09;算法的代码 2.1 大数加法的运算 #include<iostream> #include<vector> using namespace std;vector<int> add(vector<int> &a,vector<int> &b){if(a.size()<b.size(…

vue3+elementui-plus实现一个接口上传多个文件

首先&#xff0c;先使用element-plus写好上传组件&#xff0c;变量的定义我在这里就省略了都 <el-form-item prop"file" label"附件"><el-uploadstyle"width:100%"class"upload-demo"dragref"upload-demo"action&…