【我的物联网成长记19】物联网智慧路灯应用代码解析(上)
相信不少同学都做过“基于物联网平台构建智慧路灯应用”这个微认证,在这个微认证中,我们构建并部署了一个智慧路灯应用,这个应用拥有一个web界面,可以注册路灯设备,可以获取路灯设备的状态和上报的环境亮度数据并显示到界面上,还可以控制路灯的开关。
在微认证中,这个应用的源码是公开的,但不需要修改就可以直接使用,所以很多同学可能并没有仔细看过这个应用时怎么实现的。所以本文就带各位同学一起看下这个应用具体的设计与实现,以及探讨下如果想要修改这个应用的功能要怎么改,想要开发其他设备的应用要怎么开发。
整体架构
智慧路灯的源码在华为云软件开发平台作为公开示例模板公开,做过微认证的同学已经都已经按照模板创建了自己的代码仓库,接下来就是把代码下到本地用本地的IDE打开,方便查看。
展开代码的目录后我们可以看出来这是一个基于Springboot工程模板创建的web应用.
所以,该应用的主要功能代码都位于main文件夹下,其中java文件夹下是后端代码,resources文件夹下是前端代码和资源。
接下来让我们一边回忆该应用的使用流程,一边查看对应的代码是如何实现的。
Web页面
使用智慧路灯应用的第一步是通过IP和端口访问智慧路灯应用。我们通过浏览器访问应用的时候实际上就是访问了index.html这个文件。
打开index.html,可以看到就是个标准的html页面,定义了页面上显示的各个组件。也就是,如果我们想要修改智慧路灯应用界面上显示的内容,修改这个文件就可以了。
而这个页面的行为和交互则是通过.js文件定义,和业务相关的主要代码都在common.js,也就是想要修改该web页面的交互行为,需要修改common.js。
参数设置
首次访问智慧路灯应用以及点击“参数设置”按钮时,会弹出参数设置窗口,这个窗口的行为由openSetParamsDialog()函数控制。
这个函数的功能是从后台获取数据作为默认值显示在设置窗口,以及在用户点击确认后将设置的数据保存到后台。
获取后台数据的函数是getDeviceParas(),可以看到这里的后台其实就是浏览器的本体缓存,以及若后台没有存储数据时则返回空值。
将设置的数据保存到后台的函数是onSetParamsDialogConfirm(),先读取输入框的值,再写入本地存储,最后调用postDeviceParas()将数据传到后端供后端使用。
通过文件搜索,我们可以找到/set-paras接口对应的后端函数是SetCustomData类的setPara(@RequestBody ParameterConfiguration value)函数,其作用是将参数值写入TempDatabase类中的paras实体,并在用户设置的数据获取方式为订阅推送时调用subscribeDataChange()函数。
通过查看TempDatabase类可以看到该类是个单纯的数据存储类,paras这是ParameterConfiguration类的一个实例,再查看ParameterConfiguration类可以看到它也是个单纯的数据存储类,所以该方法就是将用户设计参数保存到内存中,供本应用运行时调用。
然后让我们继续顺藤摸瓜,去看下subscribeDataChange类的subscribeDataChange()方法。
这个方法是个调用接口的方法,首先通过调用LoginService类的login()方法获取accessToken,然后再调用设备接入的的创建订阅接口。创建订阅接口用于应用向平台订阅设备数据,是物联网应用向物联网平台获取数据的一种方法,本文不进行详述,感兴趣的同学可以查看我们之前的博文。
(这段代码相对较多,截图就只截关键部分了,后面相同的情况不再说明)
通过对比订阅函数和接口文档,我们可以看出来函数中的accessToken对应接口中的X-Auth-Token,所以login()方法对应IAM服务的获取用户Token接口。
除订阅推送外这个应用还支持通过DIS获取数据的方法,不过在当前的代码流程中我们还没看到这个数据获取方式的相关代码,所以就等后面再进行讲解。
注册设备
参数设置成功后,我们需要输入设备标识,然后点击注册设备。
设备标识是设备的唯一标识码,一般用设备的MAC或IMEI,如果是虚拟设备可以随意输入一串数字。
通过查看index页面源码,我们可以看到用户点击注册设备后应用会调用common.js中的regDevice( btn )函数,携带的参数就是用户输入的设备标识。这个方法会先检查你是否设置了参数,然后再调用/register-device接口。
/register-device接口对应RegisterDevice类的registerDevice(@RequestBody DeviceRegisterVerifyCode deviceRegisterVerifyCode)函数,然后这个函数又调用了registerDirectConnectedDevice类的registerDirectDevice(DeviceRegisterVerifyCode deviceInfo)函数,调用了物联网平台的创建设备接口。
然后让我们看回common.js中的regDevice( btn )函数,可以看到注册设备成功后它先移除了toggle-block的hide属性,将页面上剩下的部分(状态查看和路灯控制)显示了出来,然后调用了openDialogRegister(states,title,deviceId,secret)函数,并传入了接口返回的设备Id和密钥,实现了弹窗显示。最后,这个函数调用了initTickTimer()函数,在initTickTimer()函数中,我们终于看到了DIS相关的内容。
这个函数是一个定时任务,循环检查用户设置的数据获取方法,并调用对应函数。
当数据获取方法是DIS时,该函数调用getDis()函数。getDis()函数调用/get-dis接口,并根据返回值设置界面显示值。
/get-dis接口对应GetDis类的getDIsData()函数,这个函数通过集成DIS的SDK,实现了从DIS通道或获取最新数据。这部分的实现可以直接参考DIS的文档,本文不进行详述。设备上报的数据从物联网平台转发至DIS通道则是基于数据转发规则,本文也不进行详述。(眼尖的同学可能会发现这个函数的最后有一行createDeviceCommand.runSetCommand(slMsg);这行代码涉及到这个应用的命令下发机制,此处暂不讲解)
当数据获取方法是订阅推送时,被循环调用的是getSub()函数。getSub()函数调用/get-device-data接口,并根据返回值设置界面显示值。
/get-device-data接口对应GetDevData类的getDeviceData()函数,这个函数直接获取了TempDatabase中的slMsg(存储了环境光强和开关状态)并返回,那这个slMsg的值又是哪来的?
这时候让我们回想一下订阅推送的机制:应用订阅后,物联网平台作为客户端推送通知给应用指定的callbackURL。没错,这个数据是物联网平台主动推送上来了,所以在这个应用中应该会有一个对应callbackURL接口的函数。
从subscribeDataChange()函数中我们可以找到callbackURL的后缀“/v1.0.0/messageReceiver”,通过在代码中搜索我们找到了PushReceiver类中的pushrReceiver(@RequestBody String jsonString)函数。这个函数解析了物联网平台推送的数据,并存到了slMsg中。
至此,我们的应用已经实现了设备的注册和设备上报数据的获取。在下期博客中,我将会为同学们介绍智慧路灯应用三种路灯控制模式的实现。
- 点赞
- 收藏
- 关注作者
评论(0)