> 文档中心 > 毕业设计--智能环境监测系统基于harmonyOS的移动端应用开发

毕业设计--智能环境监测系统基于harmonyOS的移动端应用开发

目录

第一章 绪论

1.1 引言

1.2 智能环境监测APP概述

1.3 课题设计思路

第二章 项目开发环境

2.1 鸿蒙介绍

2.1.1 鸿蒙的发展史

2.1.2 鸿蒙应用开发的意义

2.1.3 HUAWEI DevEco Studio 介绍

2.1.4 HUAWEI DevEco Studio 环境安装

2.1.5 HarmonyOS系统技术架构

2.1.6 HUAWEI dececo studio 使用

2.1.7 Harmony编程语言介绍

第三章 传感层功能介绍

3.1硬件平台介绍

3.1.1 新大陆网关

3.1.2 ZigBee介绍

3.2 新大陆云平台介绍

3.2.1 传感器介绍

3.2.2 ADAM-4150介绍 

3.3.3 CC2530单片机介绍

第四章 项目实施

4.1 项目分析

4.2 功能介绍

4.3 软件设计思路

4.4 项目实施准备

4.5 硬件方案实施

4.5.1 新大陆硬件设备配置

4.5.2 新大陆云平台配置

4.5.3 鸿蒙应用权限设置

4.5.4 第三方SDK包导入以及添加依赖关系

4.6 软件代码实现

4.6.1 LoginSlice-登陆界面

4.6.2 MapSlice-地图定位页

4.6.3 DataSlice-数据展示页 

4.6.4 ChartSine-折线图展示页

第五章  软件测试

5.1 软件测试的意义及介绍

5.2 测试计划

5.3 测试执行


第一章 绪论

1.1 引言

在21世纪,但随着科技的发展,物联网的时代也到来了。物联网(Internet of things)及物物联网,物联网运用的技术有智能识别技术(RFID)如公交卡、校园卡;红外识别技术如扫描枪;单片机如stm32;各种传感器如:光照传感器、人体红外传感器、激光雷达传感器等。将上述技术与网络融合在一起,就是物联网了。

随着时代的发展,环境污染的严重,人们开始重视环境质量,为了更简便的获取周围环境的温湿度,光照等参数。则必须要有一套完整的环境监测系统,故需要一套智能环境监测系统来实时获取环境数据,获取传感器数据的硬件技术已成熟,但是软件方面还需发展。智能环境监测APP是智能环境监测系统的重要部分,该软件可以对数据实时查询、控制执行器,还可以对数据制表分析,让数据更加直观便于用户做出相对应的决策。本APP适合居家环境监测、工厂设备状况监测、智慧农业环境监测等。

1.2 智能环境监测APP概述

对于智能环境监测APP来说,为使用者提供环境数据的实时查询是该软件开发的宗旨,也是该软件开发的初心。根据目前的对环境监测的需求情况开发了此软件。智能环境监测APP通过使用新大陆提供的API实现数据的查询,通过高德地图的SDK实现精准定位传感器位置,并且运用chart第三方SDK实现数据的绘图并实时更新给用户观看。

本课题智能环境监测顺应当代事物发展的潮流,通过对环境污染普遍存在的问题进行调查。经过不断的走访调查分析、讨论,最后确认本款APP的功能、UI界面的设计,并确认后方案的可行性从开发本款APP。经过一个月的努力奋斗,智慧环境监测APP初步完成。智慧环境APP不仅方便了用户对环境数据的实时掌握,还可以对远程操控风扇的开关、开启灯光等操作。

1.3 课题设计思路

(1)前期走访、调查需求并分析。前期通过和同组同学讨论和指导老师的建议,进行了项目需求调查采访,根据采访的内容记录,并分析其需求。最后划分项目的模块。

(2)项目模块划分。通过走访,确认了本款软件使用需求,并搭建好功能框架,且设计好UI界面。

(3)软件模块架构设计。通过使用visual画图工具,将软件各模块逻辑设计出来。并分析各个模块之间的功能依赖关系,设计好软件的模块结构,同时考虑好每个模块的完整性。

(4)新大陆云平台配置。通过新大陆云平台新建好设备、传感器和执行器,对设备和传感器的命名规范,便于后期代码编码和调用API。

(5)新大陆网关配置。传感器、执行器的组网,并根据传感器类型进行分组配置序列号,以便后期调试。

(6)单元测试,完善功能。软件编译完后,让项目负责人、指导老师和其他用户对软件进行单元测试,并对其不足的地方进行升级、完善功能。

(7)试运行。集成测试,完善软件。

第二章 项目开发环境

本项目主要用到新大陆套装和基于鸿蒙的环境开发,本章节介绍鸿蒙的开发环境和新大陆的硬件介绍。华为公司为了不让西方在操作系统层面“卡脖子”,推出了基于linux内核的HarmonyOS操作系统。刚推出的HarmonyOS操作系统没有Android成熟的生态圈,为了打造好HarmonyOS生态圈,从爱国方面,本课题选择国产操作系统“HarmonyOS”进行APP编程。

2.1 鸿蒙介绍

2.1.1 鸿蒙的发展史

鸿蒙操作系统的首次提出实在2012年9月,华为总裁任正非在华为“2012诺亚方舟实验室”专家座谈会上提出鸿蒙的概念。这就是鸿蒙的诞生的起点。

2016年,消费者BG软件部门开始研发鸿蒙OS分布式操作系统1.0的版本。

2017年,鸿蒙OS系统内核1.0已经完成了技术的验证,紧接着就马不停蹄的朝着鸿蒙OS系统内核2.0版本的研发。

2018年,鸿蒙OS系统内核2.0版本已经被适用到中端TEE。

2019年,鸿蒙OS 系统内核1.0版本才正式的出现在消费者的眼前。鸿蒙OS系统内核 1.0版本是基于开源的框架,但是鸿蒙的关键技术模块都是自己的科研团队攻坚研发出来的。鸿蒙OS系统内核 1.0版本同时具备分布式架构、方舟编译器、确定时延引擎、TEE微内核形式化验证以及多终端开发IDE(Beta)。

2021年,鸿蒙操作系统才开始正式大规模商用。

2.1.2 鸿蒙应用开发的意义

(1)顺应时代发展

中国网络从4G进入了5G时代。5G具有低延迟、高带宽等优点,将推动物联网时代的到来。毫不夸张地说,5G时代就是物联网的时代。

(2)鸿蒙系统的优点

华为鸿蒙系统英文名:HUAWEI HarmonyOS,是华为2019年8月9日在东莞举办的一场“华为开发者大会”上正式发布的操作系统,又叫鸿蒙OS。基于鸿蒙操作系统可以实现万物互连,符合物联网发展的趋势。

图2-1 HarmonyOS发展历程

(3)新大陆设备支持

新大陆云平台提供了丰富的API接口和SDK供开发者使用。新大陆硬件采集数据的点位非常易于布置,传感器与网关通信代码一键式烧写,采用ZigBee组网技术,实现低消耗,一个ZigBee的电池可供传感器使用一年之久。适合户外、农业、家具等各种地方的使用。收集到的数据通过网关发送到云平台,可以让用户实时查看远程布点的环境数据,并做一系列的执行器操作。

(4)信息的时效性

信息的最重要的价值就是时效性。新大陆数据只能从新大陆官网上查看数据,并且操作繁琐。为了让用户随时随地的掌握实时数据,为此本文针对此问题开展“智能环境系统—基于HarmonyOS的移动端的应用开发”进行探讨。

(5)政治层面

鸿蒙是国产操作系统,支持国产,义不容辞的选择国产系统。鸿蒙系统全权掌握在国人手中,而Android非国产,存在信息技术的安全性的方面的问题。故本文选择基于HarmonyOS的操作系统。

2.1.3 HUAWEI DevEco Studio 介绍

华为deveco Studio是华为为消费者业务开发的集成鸿蒙APP的开发环境(IDE)。它的宗旨在于帮助开发者更高效、更方便、更快速地使用华为的Emui开放功能。

华为deveco Studio不仅具有调试仿真、代码评估、项目管理、编译建设等功能,还开发者提供了网络远程真机调试、应用云测试等多项特色服务。同时汇集emui的开放性,如hIaI、多媒体、互联、安全服务等,并提供迁移控制、MDM、AI模型等转换工具、hicar、并行世界、华为UI控制等开发模板。

2.1.4 HUAWEI DevEco Studio 环境安装

访问官网https://developer.HarmonyOS.com/cn/home 下载安装包,下载完成后安装,选择安装路径,值得注意的一点就是:安装路径上不得有中文,不然软件在运行过程中会报错。

安装完软件内部下载鸿蒙必要的SDK才能运行,安装路径默认在c盘,不要轻易改动。

2.1.5 HarmonyOS系统技术架构

HarmonyOS运用分层设计,从下往上可以在内核层、系统服务层、框架层和应用层之间共享。系统功能是以“系统>子系统>功能/模块”一级一级的展开。如果使用了许多设备,HarmonyOS支持根据实际情况的要求删除一些不必要的子系统或功能。鸿蒙os与Android一样,包括四层。

鸿蒙用的的基于java的编程,Java是一种面向对象的编程语言,java语言不仅接受C++的不同优点,还丢弃了多继承和指针的复杂的概念。Java语言是静态面向对象编程语言的代表,Java语言实现了面向对象理论,让开发者更好编写java语言。

Java语言有着简单、面向对象和分布式等特点。Java语言可以编写桌面应用程序、web应用程序、分布式系统和嵌入式系统应用程序。

2.1.6 HUAWEI dececo studio 使用

因为HUAWEI dececo studio没有用到本地虚拟化技术,华为采用的是申请网络虚拟机远程调试,所以首次调试需要登录华为云平台注册账户实名注册。

(1)选择device manger(设备管理中心)。

(2)点击logoin。然后会跳转浏览器登录华为云,并输入账号和密码。

(3)如果是第一次使用的话,需要实名认证。建议使用银行卡实名认证(便捷、省时)。

(4)最后再重复①~②,然后点同意之后就可以直接运行网络虚拟机。

图2-2 HarmonyOS技术架构图

2.1.7 Harmony编程语言介绍

鸿蒙的编译器是方舟编译器2.0版本。这个版本的编译器可以实现多语言多设备编译,也就是说鸿蒙不仅支持java、JavaScript,也支持其他语言的编译。

图 2-3 方舟编译器编译原理

第三章 传感层功能介绍

3.1硬件平台介绍

本文运用到的硬件主要有新大陆网关、ZigBee传感器、传感器包括(光照传感器、温湿度传感器、人体红外传感器、空气质量传感器和可燃气体传感器)、ADMA4150、继电器、执行器(风扇、灯泡、报警灯)、CC2530单片机。

3.1.1 新大陆网关

新大陆网关是传感器和新大陆物联网云平台之间的通信枢纽。这是决定传感器数据能否进入云端的关键硬件。新大陆网关可检测传感器数据并实时本地控制致动器,或通过485接口向ADMA4150控制致动器发送Modbus命令。配置网关参数、PandID和通道号以及ZigBee传感器内部网络,并配置连接参数以使网关能够访问新大陆物联网云平台。

图3-1 网关实物图

3.1.2 ZigBee介绍

为了创建传感网络,采集到传感器的数据,需要将传感器进行组网,把采集到的数据发送给网关,本项目用到的是Zigbee自组网。

ZigBee技术是一种近距离、低功耗、低速率、低成本的双向无线通讯技术。

主要用于距离短、功耗低且传输速率不高的各种电子设备之间进行数据传输以及典型的有周期性数据、间歇性数据和低反应时间数据传输的应用。

ZigBee技术是一项新技术。它最近出现了。它主要依靠无线网络进行传输。它可以在短距离内进行无线连接。它属于无线网络通信技术。在以数据信息为载体的传输中,ZigBee技术是主要的技术指标。使用起来比较安全,容量也很强。它广泛应用于人类日常通信传输中。

                                         

图3-2 ZigBee实物图

3.2 新大陆云平台介绍

新大陆物联网平台与云是在传感器、多数据处理、ZigBee组网技术、互联网技术等多技术融合在一起的基础上所开发的供教学使用的物联网云平台。

新大陆物联网云平台提供SDK和API等接口访问传感器数据,让物联网专业的同学更容易的学习物联网相关的知识。

3.2.1 传感器介绍

新大陆传感器主要有温湿度传感器、人体传感器、火焰传感器、光照传感器、空气质量传感器、可燃气体传感器等。本文用到了温湿度传感器、人体传感器、光照传感器、空气质量传感器和可燃气体传感器。

                             

图3-3 新大陆物联网云平台介绍

3.2.2 ADAM-4150介绍 

ADAM-4150是输入模拟量,可以通过网关485接口date+和data-对执行器进行控制。

DI输入功能测试:如图3-16

(1)、普通DI功能,以干接点为例,在模块的DIO和D.GND管脚间接一个开关,开关断开时,默认状态下DIO=1,灯亮;开关闭合时,DIO=0,灯灭,支持DI反转功能。如接湿节点,则在管脚间接10~30V电压,DI状态有变化。

2、DI做计数器counter功能,最高支持的频率为3KHZ,有掉电保持功能。实验时,在DIO和 D.GND管脚间接信号发生器。

3、DI做频率测量功能,实验时,在DIO和 D.GND管脚间接信号发生器。

4、DO输出功能测试:

1.DO直接输出,实验时,在DO0端口接LED小灯,然后串联开关24V电源(电源最大40V),为保证进入通道的电流值在0.8A以内,接电阻限流,电源负端回到模块GND端口。当控制DO输出时,LED小灯亮。

2.注意:DO输出为集电极开路,可以认为是一个开关功能,故外部接电压使开关导通,截止,但是要注意模块通道所能承受的电流值。

3.DO做脉冲输出,实验时,在DO0端口接LED小灯,然后串联开关24V电源(电源最大40V),为保证进入通道的电流值在0.8A 以内,接电阻限流,电源负端回到模块GND端口。当DO做脉冲输出时,可以看到LED灯按照设定的高低电平持续时间闪烁。

4.DO做延迟输出,接法如普通DO输出。

3.3.3 CC2530单片机介绍

CC2530是一款真正的片上系统(SOC)解决方案,适用于2.4 GHz IEEE 802.15.4、ZigBee和RF4CE应用。它可以以极低的总材料成本构建强大的网络节点。CC2530单片机可根据用户自己的需求来编程个性化的功能。

第四章 项目实施

本章节介绍如何配置传感器、组网、数据上云和APP端开发。项目在HUAWEI Deveco studio开放平台下编写,其主要有四个部分:用户登录,传感器地图定位,数据显示、数据实时绘图功能。用户登录功能让APP更具有安全性,对用户的隐私做了保障,传感器定位信息是采用第三方SDK实现定位,数据显示可以使实时获取传感器信息和远程控制执行器,绘图界面可实时直观展示数据。

4.1 项目分析

根据智能环境监测APP的功能可以分为四个模块,登录模块,map模块,传感器数据与执行器控制模块和chart绘图模块。用户登录是通过zzrhttp网络访问方式与新大陆云平台连接的,map模块是用第三方高德地图SDK,获取本机定位。传感器与执行器模块是zzrhttp中get和post方法与新大陆云平台的服务器进行通信。最后chart是第三方SDK在获取的到传感器历史数据后,在本地进行绘图。

图4-1 智能环境监测APP结构图

4.2 功能介绍

智能环境监测APP主要有四大模块:登录模块、地图模块、数据模块和绘图模块。

1、登录模块:用户打开APP需要输入账号密码来登录,若服务器返回正确的token,则跳转到地图模块。

2、地图模块:可以对高德地图进行放大和缩小,切换地图样式。并显示传感器的位置。设有按钮,点击返回登录界面。点击传感器Mark则跳转到传感器数据界面。

3、数据界面:设置了一键查询按钮,把所有传感器数据都查询显示出来。历史数据查询按钮,点击按钮,则可以看近10条历史数据,并绘制成折线图。设有控制执行器按钮,可以控制执行器开关。

4、绘图界面:查询10条历史数据,并将其绘制成折线图。

4.3 软件设计思路

新大陆云平台和软件调通后,就逐步开始软件部分了。根据用户需求,我设计了4个UI页面。四个UI都使用了经典的线性布局。

(1)用户登录界面

一个大的垂直布局里面放了两个水平布局。用户将用户名和密码填入TextField后将其数居进行post登录请求,从而获取token。

(2)地图显示界面

通过第三方提供的com.dongyu.tinymap.TinyMap控件实现地图展示。

(3)数据采集界面和执行器控制界面

通过text控件将数据绑定显示出来,通过按钮来控制执行器的开关。

(4)数据绘图界面

用第三方提供的chart控件。

4.4 项目实施准备

本次使用的环境是HarmonyOS 2.0 版本。通过deveceo studio编写代码并编译,通过申请华为网络虚拟机运行APP。

4.5 硬件方案实施

本文根据企业要求,在学校布置了4个环境监测点。室外环境监测本文选用传统的ZigBee节点盒用来与网关组网。因为节点盒内置电池适合室外环境,而且ZigBee节能,一个电池可用3~4年。室内则选择5V交流供电的CC2530单片机烧写ZigBee组网代码。

4.5.1 新大陆硬件设备配置

(1)ZigBee组网

用新大陆提供的软件“smartRF”,对传感器代码烧写实现ZigBee组网。新大陆提供了3个.hex文件。分别为继电器.hex、传感器.hex和火焰传感器.hex。顾名思义,则需要对传感器和执行器分别烧写对应的代码。

(2)网关配置

网关的连接参数配置如下图:

图4-3 新大陆网关连接配置图

(3)传感器参数配置

传感器的波特率选择38400,继电器的波特率选择9600。并且传感器的chancel和PAND ID需要和网关一致才能组网。又因网关PAND ID 是10进制,在ZigBee组网参数配置时PAIND ID 是16进制。顾要先将网关的10进制的PAND ID 转换成16进制的PAND ID 。传感器要选择传感器类型,继电器类型的就不用了。

4.5.2 新大陆云平台配置

(1)新建设备

首先访问新大陆物联网云平台(http://www.nlecloud.com/),并登录注册。然后点击右上角用户,选择开发设置。生成自己的API秘钥,之后的所有步骤都需要用到它。再次点击开发者中心,点击新增项目,在输入自己项目名称后点击下一步,并绑定新大陆

网关。这个步骤是将网关与本账号绑定,网关的上传的数据就会上传到这个账号。绑定账号需要写入设备标识,若设备已被绑定,则点击解绑设备,并上传新大陆设备序列号的图片,申请解绑后,可直接绑定。   

(2)添加传感器

按部就班的配置传感器,传感器采取的数据非0和1的(温度传感器)选择ZigBee,若传感器采集的数据是0和1,如人体传感器,则选择数字量

(3)添加执行器

本文中风扇是通过ZigBee与网关通信的,故执行器添加如上述步选择ZigBee,并配置好序列号。而报警灯是通过网关连接ADMA4150有线连接。则添加执行器时选择数字量,配置好通道号。

4.5.3 鸿蒙应用权限设置

本项目需要有网络访问权限和获取本地位置权限。

第一步:找到config.json文件。

第二步:找到modue节点。

复制粘贴如下代码:

获取网络权限:

"reqPermissions": [{"name": "ohos.permission.GET_NETWORK_INFO"},{"name": "ohos.permission.SET_NETWORK_INFO"},{"name": "ohos.permission.INTERNET"}],

获取地理位置权限:

"reqPermissions": [{"name": "ohos.permission.LOCATION","reason": "$string:reason_description","usedScene": {"ability": ["com.myAPPlication.LocationAbility"],"when": "inuse"},

第三步:点击sync now。

4.5.4 第三方SDK包导入以及添加依赖关系

根据需求分析,导入好第三方SDK。我在gitee.com找第三方项目地图项目和绘图项目。将其项目包解压到本项目的文件夹下,本文中用到了’tinymap’和’chart’包。在settings.gradle 修改一下代码,这两个项目文件编译到本项目中。代码为:include ':entry',':tinymap',':ChartLib'。与此同时在entry/build.gradle里面的dependencies添加如下代码

{ implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])implementation project(':tinymap')implementation project(':ChartLib')}

添加zzrhttp依赖,

implementation 'com.zzrv5.zzrhttp:ZZRHttp:1.0.1'

Zzrhttp是实现post,get等一些网络请求操作的操作。同时用到gson依赖,

implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.7',

gson是用来对zzrhttp请求所返回的结果做解析用的

然后点一下sync now ,等出现Gradle sync finished 说明添加依赖成功。

图4-9 第三方依赖包导入成功样图

4.6 软件代码实现

鸿蒙与安卓不同,安卓只有activity,鸿蒙不仅有Mainability而且还有MainabilitySlice。因为代码涉及到页面的跳转,用到鸿蒙Slice自带的present函数进行页面跳转。

4.6.1 LoginSlice-登陆界面

第一个页面就是登录页,这个页面主要功能就是通过新大陆提供的RESTfulAPI获取token令牌。实现代码如下:

1  zhanghao.getText().equals("请输入账号")|mima.getText().equals("请输入密码")

2  Map A=new HashMap();

3  A.put("Account",zhanghao.getText());

4  A.put("Password",mima.getText());

5  ZZRHttp.post(url, A, new ZZRCallBack.CallBackString() {

6  public void onFailure(int i, java.lang.String s) {}

7  public void onResponse(java.lang.String s) {

8  JSONObject object =JSONObject.parseObject(s) ;

9  getResult.sendEvent(InnerEvent.get(1001,object));

首先第1行:设置按钮的监听事件对输入框输入的合法性进行一个判断,账号密码不能为空,若为空,系统会toast出来提示“请输入账号和密码!”这样可以有效地减少服务器的负担。当符合条件了就调用登录函数发送请求。在2-4行:定义一个MAP的String类型的键值对的对象,把用户输入的账号和密码以键值对的形式放入A中。在5-7行:根据新大陆提供的登录APIhttps://API.nlecloud.com/Users/Login通过ZZRHttp提供post的方法将账号密码发出。onResponse方法里是请求发出后成功接收到的结果。8-9行:在回调函数onResponse方法的里面定义一个JSONObject存放返回的结果,并把这object放到event.object里面。并定义一个EventHandler去处理这个返回结果。

根据返回结果,我在getResult做了json解析处理。下面是代码介绍:

10  JSONObject A=(JSONObject)event.object ;

11  JSONObject token1 = A.getJSONObject("ResultObj");

12  restoken=token1.getString("AccessToken");

13  Intent intent1 = new Intent();

14  intent1.setParam("token",token1.getString("AccessToken"));

15  present(new MainAbility3Slice(),intent1);

首先在第10行定义一个JSONObject存放在event.object的数据,然后根据返回结果再次定义一个JSONObject对象放ResultObj这个json对象。然后为了防止结果取出空值导致程序闪退,则判断ResultObj是否为空,若为空则告诉用户账号密码错误。11-12行:否则则定义一个字符串获取ResultObj中的AccessToken的值。成功获取AccessToken之后,就可以跳转到MapSlice了。13-14行:这里是初始化一个Intent,运用鸿蒙提供的intent的setParam方法,将AccessToken伴随着intent跳转到第二个页面。第15行:通过present的方法进行跳转。

 

图4-10 post成功请求的返回结果

本页完整代码:

package com.example.myapplication.slice;import com.alibaba.fastjson.JSONObject;import com.example.myapplication.ResourceTable;import com.zzrv5.mylibrary.ZZRCallBack;import com.zzrv5.mylibrary.ZZRHttp;import javax.xml.soap.Text;import java.awt.*;import java.util.HashMap;import java.util.Map;//import okhttp3.OkHttpClient;//import okhttp3.Request;public class MainAbilitySlice extends AbilitySlice {    //private static final HiLogLabel label1 = new HiLogLabel(HiLog.LOG_APP, 0xD001100, "MainAbility");    private Button buttonGet;    private TextField zhanghao,mima;    private Text token,wendu;    private Image img;    private String restoken;    private String weathercode;    private String weathertemperature;    private String url;    private Component p;    @Override    public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); zhanghao = (TextField) findComponentById(ResourceTable.Id_zhanghao); buttonGet = (Button) findComponentById(ResourceTable.Id_bt); mima=(TextField)findComponentById(ResourceTable.Id_mima); try {     String appId = getBundleManager().getBundleInfo(getBundleName(), 0).getAppId();     HiLog.info(label1,"qweasdzxc+"+appId); } catch (RemoteException e) {     e.printStackTrace(); } button_onclick();    }    private void button_onclick()    { buttonGet.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  if(zhanghao.getText().equals("请输入账号")|mima.getText().equals("请输入密码"))  {      new ToastDialog(getContext()).setText("请输入账号和密码!").show();  }  else  {      post();  }     } });    }    EventHandler getResult = new EventHandler(EventRunner.current()){ @Override protected void processEvent(InnerEvent event) {     super.processEvent(event);     if(event==null)     { return; }     if(event.eventId==1001)     {  JSONObject A=(JSONObject)event.object ;  JSONObject token1 = A.getJSONObject("ResultObj");  if(token1==null)  {      new ToastDialog(getContext()).setText("您的帐号不存在,请查证后再次尝试登录!").show();  }  else  {      restoken=token1.getString("AccessToken");      Intent intent1 = new Intent();  intent1.setParam("token",token1.getString("AccessToken"));  present(new MainAbility3Slice(),intent1);     } HiLog.info(label1,"qweasdzxc"+restoken+"SEC");     } }    };    private void post(){ new Thread(new Runnable() {     @Override     public void run() {  String url="http://api.nlecloud.com/Users/Login";  Map A=new HashMap();  A.put("Account",zhanghao.getText());  A.put("Password",mima.getText());      ZZRHttp.post(url, A, new ZZRCallBack.CallBackString() {   @Override   public void onFailure(int i, java.lang.String s) {HiLog.info(label1,"qweasdzxc:"+s+"=FALL"+"SUESS");   }   @Override   public void onResponse(java.lang.String s) {JSONObject object =JSONObject.parseObject(s) ;getResult.sendEvent(InnerEvent.get(1001,object));      HiLog.info(label1,"qweasdzxc:"+s+""+"SUESS");   }      });     } }).start();    }    @Override    public void onActive() { super.onActive();    }    @Override    public void onForeground(Intent intent) { super.onForeground(intent);    }}

4.6.2 MapSlice-地图定位页

图实现是导入第三方包,通过Locator 和MyLocatorCallback回调函数查询到定位,location.startLocating(requestParam,locatorCallback);实现查询本机定位功能。然后将其得到的经纬度通过坐标换算成火星坐标。其公式封装成函数为如下:

16  public static double[] toMercator(double lon,double lat){

17  double[] xy = new double[2];

18  double x = lon * 20037508.342789/180;

19  double y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);

20  y = y * 20037508.34789 / 180;

21  xy[0] = x; xy[1] = y;

22  return xy;}

首先16行:定义一个返回值为双精度字符数组类型函数名为toMercator的函数。函数两个双精度的lon(经度Longitude)和lat(纬度latitude)参数就是获取的用户位置的地理坐标。17-20行:分别把经纬度换成火星坐标。21行:把转换后的坐标当道xy[]数组里。第22行:返回换算后的结果。

换算后的坐标传给TinyMap.java初始化坐标,绘出地图。

Mark点设置:

23  double[] coord = toMercator(120.244055,31.873696);

24  tinyMap.addElement((float)coord[0],(float)coord[1],  ResourceTable.Media_dot)

第23行:定义double数组,通过坐标转换函数转换坐标。第24行:运用第三方包提供的SDK把Mark资源图片添加到地图上。

切换地图功能实现:

25  ListDialog listDialog = new ListDialog(MainAbility3Slice.this,  ListDialog.SINGLE);

26  listDialog.setItems(new String[]{ "高德地图 - 矢量","高德地图 - 栅格  "});

27  listDialog.setOnSingleSelectListener(new IDialog.ClickedListener() {

28  public void onClick(IDialog iDialog, int i) {

29  if (i == 0)

30  tinyMap.setMapSource(TinyMap.MapSource.GAODE_VECTOR);

31  if (i == 1)

32  tinyMap.setMapSource(TinyMap.MapSource.GAODE_SATELLITE);

33  tinyMap.refreshMap();

34  tinyMap.zoomIn();

35  tinyMap.zoomOut();

首先第25-26行:使用ListDialog ,并定义了一个字符串数组在对话框显示。第27行设置了点击监听事件,根据点击素组的index做出判断,第30行和第32行调用第三方SDK提供的setmapsource实现更换地图资源。第33行刷新地图。

地图放大缩小按键设置:

地图放大缩小是调用SDK提供的tinymap.out函数,实现地图放大,tinymap.zoon 地图缩小 ,实现地图缩小。

本页完整代码:

package com.example.myapplication.slice;import com.amap.api.maps.MapView;import com.amap.api.maps.MapsInitializer;import com.dongyu.tinymap.TinyMap;import com.example.myapplication.ResourceTable;import ohos.aafwk.ability.AbilitySlice;import ohos.aafwk.content.Intent;import ohos.agp.components.Button;import ohos.agp.components.Component;import ohos.agp.components.DirectionalLayout;import ohos.agp.utils.Point;import ohos.agp.window.dialog.IDialog;import ohos.agp.window.dialog.ListDialog;import ohos.bundle.IBundleManager;import ohos.hiviewdfx.HiLog;import ohos.hiviewdfx.HiLogLabel;import ohos.location.Location;import ohos.location.Locator;import ohos.location.LocatorCallback;import ohos.location.RequestParam;import ohos.multimodalinput.event.TouchEvent;public class MainAbility3Slice extends AbilitySlice implements Component.DoubleClickedListener {    private static final HiLogLabel label1 = new HiLogLabel(HiLog.LOG_APP, 0xD001100, "MainAbility");    private MapView mapView;    final  int MY_PERMISSIONS_REQUEST_LOCATION=12;    private  TinyMap tinyMap;    Locator location;    private String tk;    private  RequestParam requestParam;    private MyLocatorCallback locatorCallback;    private Point mCenterPoint;  //  RequestParam requestParam = new RequestParam(RequestParam.PRIORITY_ACCURACY,0,0);    MapsInitializer mapsInitializer =new MapsInitializer();    @Override    public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main3); tk=intent.getStringParam("token"); Button tiaoxhuan =(Button)findComponentById(ResourceTable.Id_shujv); Button fh =(Button)findComponentById(ResourceTable.Id_fanhui1); DirectionalLayout dl=(DirectionalLayout)findComponentById(ResourceTable.Id_bujv); dl.setDoubleClickedListener( this); location=new Locator(this);  requestParam =new RequestParam(RequestParam.SCENE_NAVIGATION); locatorCallback = new MyLocatorCallback();// location.requestOnce(requestParam,locatorCallback); if(verifySelfPermission("ohos.permisson.Location")!= IBundleManager.PERMISSION_DENIED) {     if(canRequestPermission("ohos.permisson.Location"))     {     requestPermissionsFromUser(      new String[]{"ohos.permisson.Location"},MY_PERMISSIONS_REQUEST_LOCATION     );     } }else {     location.startLocating(requestParam,locatorCallback); } dl.setLongClickedListener(new Component.LongClickedListener() {     @Override     public void onLongClicked(Component component) {     } }); fh.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  present(new MainAbilitySlice(),intent);     } }); tiaoxhuan.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  intent.getParam("token");  present(new MainAbilitySlice(),intent);     } });  tinyMap=(TinyMap)findComponentById(ResourceTable.Id_map); Button btnZoomIn = (Button) findComponentById(ResourceTable.Id_btn_zoomin);btnZoomIn.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  tinyMap.zoomIn();  double[] coord = toMercator(120.244055,31.873696);  tinyMap.addElement((float) coord[0], (float)coord[1], ResourceTable.Media_dot);  double[] coord1 = toMercator(120.245015,31.874656);  tinyMap.addElement((float) coord1[0], (float)coord1[1], ResourceTable.Media_dot);  double[] coord2 = toMercator(120.245955,31.873616);  tinyMap.addElement((float) coord2[0], (float)coord2[1], ResourceTable.Media_dot);  double[] coord3 = toMercator(120.244005,31.874586);  tinyMap.addElement((float) coord3[0], (float)coord3[1], ResourceTable.Media_dot);     } }); Button btnZoomOut = (Button) findComponentById(ResourceTable.Id_btn_zoomout); btnZoomOut.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  tinyMap.zoomOut();  tinyMap.refreshMap();  double[] coord = toMercator(120.244055,31.873696);  tinyMap.addElement((float) coord[0], (float)coord[1], ResourceTable.Media_dot);  double[] coord1 = toMercator(120.245015,31.874656);  tinyMap.addElement((float) coord1[0], (float)coord1[1], ResourceTable.Media_dot);  double[] coord2 = toMercator(120.245955,31.873616);  tinyMap.addElement((float) coord2[0], (float)coord2[1], ResourceTable.Media_dot);  double[] coord3 = toMercator(120.244005,31.874586);  tinyMap.addElement((float) coord3[0], (float)coord3[1], ResourceTable.Media_dot);     } }); Button btnChangeBaseMapType = (Button) findComponentById(ResourceTable.Id_btn_changebasemaptype); btnChangeBaseMapType.setClickedListener(new Component.ClickedListener() {     @Override     public void onClick(Component component) {  ListDialog listDialog = new ListDialog(MainAbility3Slice.this, ListDialog.SINGLE);  listDialog.setItems(new String[]{   "高德地图 - 矢量",   "高德地图 - 栅格"});  listDialog.setOnSingleSelectListener(new IDialog.ClickedListener() {      @Override      public void onClick(IDialog iDialog, int i) {  // if (i == 0)      // tinyMap.setMapSource(TinyMap.MapSource.GAODE_ROAD);   if (i == 0)tinyMap.setMapSource(TinyMap.MapSource.GAODE_VECTOR);   if (i == 1)tinyMap.setMapSource(TinyMap.MapSource.GAODE_SATELLITE);   tinyMap.refreshMap();   double[] coord = toMercator(120.244055,31.873696);   tinyMap.addElement((float) coord[0], (float)coord[1], ResourceTable.Media_dot);   double[] coord1 = toMercator(120.245015,31.874656);   tinyMap.addElement((float) coord1[0], (float)coord1[1], ResourceTable.Media_dot);   double[] coord2 = toMercator(120.245955,31.873616);   tinyMap.addElement((float) coord2[0], (float)coord2[1], ResourceTable.Media_dot);   double[] coord3 = toMercator(120.244005,31.874586);   tinyMap.addElement((float) coord3[0], (float)coord3[1], ResourceTable.Media_dot);   listDialog.hide();      }  });  listDialog.setButton(0, "取消", new IDialog.ClickedListener() {      @Override      public void onClick(IDialog iDialog, int i) {   listDialog.hide();      }  });  listDialog.setSize(600, 600);  listDialog.show();  tinyMap.refreshMap();  double[] coord = toMercator(120.244055,31.873696);  tinyMap.addElement((float) coord[0], (float)coord[1], ResourceTable.Media_dot);  double[] coord1 = toMercator(120.245015,31.874656);  tinyMap.addElement((float) coord1[0], (float)coord1[1], ResourceTable.Media_dot);  double[] coord2 = toMercator(120.245955,31.873616);  tinyMap.addElement((float) coord2[0], (float)coord2[1], ResourceTable.Media_dot);  double[] coord3 = toMercator(120.244005,31.874586);  tinyMap.addElement((float) coord3[0], (float)coord3[1], ResourceTable.Media_dot);     } });   }    @Override    public void requestPermissionsFromUser(String[] permissions, int requestCode) { super.requestPermissionsFromUser(permissions, requestCode); switch (requestCode) {     case MY_PERMISSIONS_REQUEST_LOCATION:{  location.startLocating(requestParam,locatorCallback);  mCenterPoint = new Point();     } }    }    public class MyLocatorCallback implements LocatorCallback { @Override public void onLocationReport(Location location) {     HiLog.info(label1,"qweasdzxc"+location.getLatitude()+"经度");     HiLog.info(label1,"qweasdzxc"+location.getLongitude()+"纬度");      mCenterPoint = new Point((float) location.getLatitude(), (float) location.getLongitude()); } @Override public void onStatusChanged(int type) { } @Override public void onErrorReport(int type) { }    }    public static double[] toMercator(double lon,double lat)    { double[] xy = new double[2]; double x = lon * 20037508.342789/180; double y = Math.log(Math.tan((90 + lat) * Math.PI / 360))  / (Math.PI / 180); y = y * 20037508.34789 / 180; xy[0] = x; xy[1] = y; HiLog.info(label1,"qweasdzxc:y="+y); return xy;    }   public static double[] toLonLat(double mercatorX,double mercatorY)   {double[] xy = new double[2];double x = mercatorX / 20037508.34 * 180;double y = mercatorY / 20037508.34 * 180;y= 180 / Math.PI * (2 * Math.atan( Math.exp ( y * Math.PI / 180)) - Math.PI / 2);xy[0] = x;xy[1] = y;return xy;   }    @Override    protected void onStop() { super.onStop();/* if (mapView != null) {     mapView.onDestroy(); }*/ location.stopLocating(locatorCallback);    }    @Override    public void onActive() { super.onActive();    }    @Override    public void onForeground(Intent intent) { super.onForeground(intent);    }    @Override    public void onDoubleClick(Component component) { Intent A =new Intent(); A.setParam("token",tk); present(new  MainAbility2Slice(),A);    }}

4.6.3 DataSlice-数据展示页 

数据查询页面根据新大陆提供的RESTfulAPI接口,查询传感器用get方法,控制执行器则用的是post方法。

定义个全局变量的String类型的字符串,用tk=intent.getStringParam("token");接收从Map传过来的token。设置一个按钮监听器,因为是实时查询,所以我加了一个定时器timer,timer再循环调用封装函数。

36  if(aaa){

37  aaa=false;

38  timer = new Timer();

39  timer.schedule(new TimerTask() {

40  setChaxunguangz(token);wendu(token);shidu(token);krqt(token)

41  time.setText("已查询"+count+"次");

42  chaxun.setText("停止查询");

43  },0,5000); }

44  else if(!aaa)

45  chaxun.setText("一键查询");

46  time.setText("———实时数据查询———");

47  aaa=true;

首先第36行定义一个flag,若flag为true,运行查询数据函数。41-42行:并改变按钮文本,为false则停止查询并将按钮恢复初始化文本。第40行:定时器里不断调用查询函数并更新数据。第43行:设置定时器参数5000代表5秒运行一次。第47行把标志位归正。

查询传感器数据代码如下:

48  A.put("AccessToken",intent);

49  ZZRHttp.get(url, A,new ZZRCallBack.CallBackString() {

50  public void onResponse(String s) {

51  JSONObject object =JSONObject.parseObject(s) ;

52  getResult.sendEvent(InnerEvent.get(1002,object)              

首先第48行:将上一界面传来的token放入MAP A里。第49行:运行zzrhttp.get方法。根据新大陆RESTfulAPI的官方文档,对返回结果做解析。对照官方文档,要把get的URL改成"https://API.nlecloud.com/devices/333593/Sensors/z_light"。51-52行:回调函数里对返回结果做处理。

图4-11 新大陆官方API调用参数及返回结果

取数据的操作和取token操作一样。取到数据后并把数据显示到界面上。

返回结果处理与显示:

51  JSONObject A=(JSONObject)event.object ;

52  JSONObject res =A.getJSONObject("ResultObj");

53  String guangzhao =res.getString("Value");

54  guanghzao.setText("光照:"+guangzhao+"lx");

首先在第51行定义一个JSONObject存放在event.object的数据,然后根据返回结果再次定义一个JSONObject对象放ResultObj这个json对象。然后为了防止结果取出空值导致程序闪退,则判断ResultObj是否为空,若为空则告诉用数据错误。53-54行:否则则定义一个字符串获取ResultObj中的Value的值。成功获取value之后,在第54行更新ul界面。

历史数据绘图:

调用新大陆历史数据API:https://API.nlecloud.com/devices/333593/Datas/?与实时查询有稍微的差别。查询历史数据要求提供APItag和查询时间和结束时间,所以要在请求API的body里面要放置AccessToken和查询的起止时间。

图4-12 历史数据获取参数文档

取得数据后,解析json包。历史数据是多个json数组组合的json包。故在json解析时定义一个for循环将其数据取出。

并定义一个String素组,利用for循环将数据一个一个放入数据中,然后把数据放入intent1.setStringArrayListParam里面,伴随着intent跳转到chartSline并绘制出折线图。

有6个传感器,1个绘图界面,考虑到这点,我又加了一个int类型tag,伴随着intent传到ChartSine 分类初始化y轴。

本页部分代码:

 EventHandler getResult = new EventHandler(EventRunner.current()){ @Override protected void processEvent(InnerEvent event) {     super.processEvent(event);     if(event==null)     {  return;     }     if(event.eventId==1000)     {  JSONObject A=(JSONObject)event.object ;  JSONObject res =A.getJSONObject("ResultObj");  if(res==null)  {      new ToastDialog(getContext()).setText("数据错误!").show();  }  else  {      String guangzhao =res.getString("Value");      guanghzao.setText("光照:"+guangzhao+"lx");      HiLog.info(label1,"qweasdzxc"+guangzhao+"1111");  }     }     else if(event.eventId==1001)     {  JSONObject A=(JSONObject)event.object ;  JSONObject res =A.getJSONObject("ResultObj");  if(res==null)  {      new ToastDialog(getContext()).setText("数据错误!").show();  }  else  {      String wendu =res.getString("Value");      tokee_wd.setText("温度:"+wendu+"°C");      HiLog.info(label1,"qweasdzxc"+wendu+"1111");  }     } public void setChaxunguangz(String intent) { A.put("AccessToken",intent); new Thread(new Runnable() {     @Override     public void run() {  ZZRHttp.get("http://api.nlecloud.com/devices/408567/Sensors/z_light", A,new ZZRCallBack.CallBackString() {      @Override      public void onFailure(int i, String s) {   return;      }      @Override      public void onResponse(String s) {   JSONObject object =JSONObject.parseObject(s) ;   getResult.sendEvent(InnerEvent.get(1000,object));      }  });     } }).start();    }

4.6.4 ChartSine-折线图展示页

首先取出上一个界面穿过来的传感器数据、tag、token,并初始化LineChart。再定义定时器,不断实时调用查询数据的API,将数据绘图,实现数据实时绘图的功能。

                           

图4-13 历史数据折线图

定时器5秒更新一次,根据tag,调用查询历史数据的函数。initdata(max)初始化图像。设置lable显示的文字、设置X轴和Y轴的最大值、最小值等属性值。因为传感器数据不同,故根据传感器数据设置与其对应的表格值。

第五章  软件测试

软件测试是程序功能实现后需要进行的不可缺失的步骤,一个软件只有通过软件测试才算的上是一个合格的软件。

5.1 软件测试的意义及介绍

软件测试的目的是通过人工或自动的手段来完成对指定程序的测试,已检验此程序的实际功能是否达到设计的预期效果,以及与预期效果之间的差距。目前软件测试的方法主要分为黑盒测试和白盒测试两种:黑盒测试简单来说就是测试人员运行需要测试的程序,按照开发的功能一步一步去测试即可。黑盒测试不需要测试人员关注代码的结构及逻辑性。只需要通过肉眼观察程序的实现是否与设计的效果一直即可以及白盒测试则需要测试人员对对整个程序的内部结构有一定了解,测试程序是否存在bug或逻辑上的错误。

5.2 测试计划

(1)虚拟机及真机测试测试:

(2)登录界面测试:不输入输入框中内容测试;账号密码输入错误测试;正常登录测试。

(3)地图界面测试:传感器及自身位置显示测试,Mark点点击测试。

(4)传感器数据界面测试:数据是否获取到并实时刷新;折线图及控制执行器点击事件能否正确跳转。

(5)执行器控制界面测试:执行器是否可以正常控制。

(6)折线图界面测试:折线图能否正常展现;折线图是否每嗝五秒实现数据刷新。

(7)对整个程序进行整体测试,是否符合预期效果。

5.3 测试执行

第一步:按照测试计划先对登录界面进行测试。如表5-1和5-2。

第一步测试结果:在分别对虚拟机和真机进行三次测试后,登录log界面可以打印数据,但是页面不跳转。经过一番检查过后发现,EventHandler数据处理条件判断错误。

第二步:按照测试计划对地图界面进行测试。如表5-3和表5-4。

表 5-1 登录界面虚拟机测试表

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

续前表5-1 登录界面虚拟机测试表

1

输入框不输入进行登录点击测试

直接点击登录按钮

是否弹出“用户名或密码不能为空”提示

2021.11.25

2

账号或密码输入错误,点击登录按钮测试

在输入框中输入随机内容,点击登录按钮测试

是否弹出“账号或密码错误”提示

2021.11.25

3

输入正确的账号密码,点击登录按钮进行测试

在输入框输入正确的账号及密码,点击登录按钮

是否成功登录并跳转至地图界面

2021.11.25

表 5-2 登录界面真机测试表

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

输入框不输入进行登录点击测试

直接点击登录按钮

是否弹出“用户名或密码不能为空”提示

2021.11.25

2

账号或密码输入错误,点击登录按钮测试

在输入框中输入随机内容,点击登录按钮测试

是否弹出“账号或密码错误”提示

2021.11.25

3

输入正确的账号密码,点击登录按钮进行测试

在输入框输入正确的账号及密码,点击登录按钮

是否成功登录并跳转至地图界面

2021.11.25

表 5-3 地图页面虚拟机测试表

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

续前表 5-3 地图页面虚拟机测试表

1

地图界面的传感器及自身Mark点位置显示

进入地图界面后放大地图观察自身及传感器Mark点位置

传感器及自身Mark点位置是否正确

2021.11.28

2

Mark点点击跳转测试

随机点击Mark点

点击Mark点是否跳转至数据界面

2021.11.28

3

地图放大、缩小测试

点击放大按钮和缩小按钮

地图是否能放大、缩小

2021.11.28

4

地图样式的切换测试

点击切换样式按钮,是否可以切换地图

高德地图矢量图和高德地图卫星图是否能正确展示

2021.11.28

5

返回按钮测试

点击返回按钮

能否跳到登录界面

2021.11.28

第二步测试结果:对于此次测试的结果虚拟机会在某种情况闪退问题而真机不会,检查了鸿蒙调试报告发现没有任何问题,推断此问题属于鸿蒙运行环境的问题。

第三步:按照测试计划对传感器数据界面的各项功能进行测试。如表5-5和表5-6。

第三步测试结果:在分别对虚拟机和真机进行两次测试后,传感器数据界面功能基

表 5-4 地图页面真机测试表

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

地图界面的传感器及自身Mark点位置显示

进入地图界面后放大地图观察自身及传感器Mark点位置

传感器及自身Mark点位置是否正确

2021.11.28

2

Mark点点击跳转测试

随机点击Mark点

点击Mark点是否跳转至数据界面

2021.11.28

续前表 5-4 地图页面真机测试表

3

地图放大、缩小测试

点击放大按钮和缩小按钮

地图是否能放大、缩小

2021.11.28

4

地图样式的切换测试

点击切换样式按钮,是否可以切换地图

高德地图矢量图和高德地图卫星图是否能正确展示

2021.11.28

5

返回按钮测试

点击返回按钮

能否跳转到登录界面

2021.11.28

本实现并符合设计预期。

第四步:按照测试计划对执行器控制界面进行测试。如表5-7和表5-8。

第四步测试结果:在分别对虚拟机和真机进行对应的测试后,执行器控制界面基本功能全部实现并符合设计预期。

第五步:按照测试计划对折线图界面进行测试。如表5-9和表5-10。

第五步测试结果:折线图不能实时刷新。经代码排查发现,没有实时实例化ul对象,故把实例化对象代码放进定时器里,这可以实时刷新界面

第六步:按照测试计划对整个程序进行测试。表5-11和5-12。

表 5-6 传感器控制虚拟机调试

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

传感器界面数据是否获取到并实时刷新测试

进入传感器界面后观察数据的显示,并让一位同学从人体传感器前经过并离开

在进入传感器界面后观察数据是否显示且随着同学的经过人体传感器数据是否发生变化

2021.12.2

2

执行器控制按钮点击跳转测试

点击对应的折线图或执行器控制按钮

点击button后是否发生对应的跳转事件

2021.12.2

表 5-6 传感器控制真机调试

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

传感器界面数据是否获取到并实时刷新测试

进入传感器界面后观察数据的显示,并让一位同学从人体传感器前经过并离开

在进入传感器界面后观察数据是否显示且随着同学的经过人体传感器数据是否发生变化

2021.12.2

2

执行器控制按钮点击跳转测试

点击对应的折线图或执行器控制按钮

点击button后是否发生对应的跳转事件

2021.12.2

表 5-7 执行器控制虚拟机测试

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

执行器控制界面能否正常控制开关

进入执行器控制界面后点击需要控制开关的设备进行“开”与“关”的点击

观测随着“开”与“关”的点击对应的执行器是否发生变化

2021.12.2

表 5-8 执行器控制真机测试

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

执行器控制界面能否正常控制开关

进入执行器控制界面点击需要控制开关的设备进行“开”与“关”的点击

观测随着“开”与“关”的点击对应的执行器是否发生变化

2021.12.2

表 5-9 折线图绘制虚拟机调试

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

折线图界面能否正常显示

点击对应传感器跳转至折线图并进行观察

观察进入折线图后是否正常显示对应传感器数据的折线图

2021.12.3

2

折线图是否每五秒更新

观察折线图五秒的变化

折线图是否发生变化

2021.12.3

表 5-10 折线图绘制真机调试

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

折线图界面能否正常显示

点击对应传感器跳转至折线图并进行观察

观察进入折线图后是否正常显示对应传感器数据的折线图

2021.12.3

2

折线图是否每五秒更新

观察折线图五秒的变化

折线图是否发生变化

2021.12.3

序号(虚拟机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

完整运行程序体验程序是否符合预期设计

从登陆开始演示一遍整个程序

体验在程序使用过程中各项功能是否符合预期设计

2021.12.4

表 5-11 软件总调试虚拟机

表 5-12 软件总调试真机

序号(真机)

测试项目

测试操作

检查事项

测试结果

测试时间

1

完整运行程序体验程序是否符合预期设计

从登陆开始演示一遍整个程序

体验在程序使用过程中各项功能是否符合预期设计

2021.12.4

第六步测试结果:在结束对虚拟机和真机的全流程测试后。测试阶段就此完成了。从上表的测试结果中可以发现两次测试结果全部符合作者的预期。

参考文献

[1] https://www.sohu.com/a/469132123_468595

[2] http://www.nlecloud.com/about

[3] 新大陆.云平台.应用开发.RESTfluAPI

[4] https://www.cnblogs.com/HarmonyOS/p/14667327.html

[5] https://www.sohu.com/a/444074764_463994

[6] https://baike.baidu.com/item/Java/85979?fr=aladdin

[7] 2020环境监测现状及前景分析.全国能源信息平台.2020.07

[8] https://blog.csdn.net/rl529014/article/details/51556707/

[9] 软件测试中黑盒测试和白盒测试详细解释.白领爱开车.2019.09

[10] https://www.php.cn/faq/464195.html