项目概述
可能有些同学已经接触过了标准系统上的软总线应用开发,但是你玩过轻量系统上的软总线应用吗?现在它来了。我们利用OpenAtom OpenHarmony 3.1 Release(以下简称“OpenHarmony”)版本的轻量系统软总线能力,将智能燃气检测设备和智能窗户通风设备组成一个轻量级分布式网络,实现设备之间的相互控制。
原理图如下:
当家中的燃气告警时,无需任何操作,直接控制窗户通风系统中的电机工作。
开发说明
从上面的视频中可以看到,相关案例设备的应用界面都可以分成外设交互页面和软总线操作页面两大块。在下面的系统框图中可以看到相关页面和其依赖的相关轻量系统能力。例如外设交互界面通过自定义JSI接口与设备硬件打交道,软总线操作界面则通过轻量系统的设备管理能力、软总线能力来实现设备间的发现、认证、传输等软总线操作。
在下面的内容中我们将以智能燃气告警器设备为例,将相关开发过程分成JS应用端开发、自定义JSI实现、开发板端代码三部分来说明:
JS应用开发
燃气告警器JS应用是基于3.1 release版本,并结合方舟开发框架(ArkUI)、分布式组网等特性,使用JS语言开发的一款分布式安全厨房应用。为了体现了 OpenHarmony轻量级分布式特性,不仅需要考虑页面该如何设计、应用怎样同外设交互,还需要考虑两个轻量级设备间如何进行设备认证,设备间如何进行通讯等问题。
所以在相关应用中设计了操作页面,报警页面以及设备认证页面。其中首页是设置燃气浓度阈值,以及显示当前燃气浓度;报警页面是当首页检测到当前燃气浓度达到或高于我们设置的阈值时,会从首页跳转到报警界面;设备认证页面则是对两台设备进行分布式组网。
代码结构如下图:
外设交互页面开发
相关界面如上图所示,我们将首页页面分成三个部分:顶部标签、燃气浓度显示、设置告警阈值。接下来是具体的解析内容:
1)顶部标签解析
顶部标签中除了相关文本界面,主要是顶部两个按钮,分别用来显示当前Wi-Fi连接状态以及跳转到设备认证页面。
GetKey() {
com.get({ // 获取wifi状态
key: 'storage_key',
success: (data)=> {
let res = JSON.parse(data)
if (res.wifi) {
if(res.wifi == 'connected') {
this.isWifi = true;
} else {
this.isWifi = false;
}
}
.....
},
});
},
changePage(operation) {
router.replace({
uri:"pages/dm/dm" // 跳转到设备认证页面
});
}
2)燃气浓度显示
主要是获取当前燃气浓度,并实时刷新到相关界面上。
onInit() {
setTimeout(()=>{
setInterval(()=>this.GetKey(),500) // 每500ms 获取一次
},3000);
},
GetKey() {
com.get({
key: 'storage_key',
success: (data)=> {
let res = JSON.parse(data)
if (res.CurrentGasCONC) {
this.currentValue = res.CurrentGasCONC;
this.progressPercent = ((this.currentValue ) /300) * 100
if(this.currentValue >this.PresetValue && !this.isChange){
this.isChange = true;
router.replace({
uri:"pages/warn/warn" // 燃气数值超标后自动跳转告警页面
});
}
}
},
......
});
},
3)设置报警阈值
首页界面中对于报警阈值的处理,主要包含减小预设阈值和增大预设阈值,都是通过调用相关SetKey操作完成的。
reduceProgress(){ //减小预设阈值
if (this.PresetValue <= 0) {
this.PresetValue = 0
}else{
this.PresetValue = parseInt(this.PresetValue) - 20
}
this.isChange = false;
this.setProgress = ((this.PresetValue ) /300) * 100
this.SetKey( 'GasThreshold', this.PresetValue );
},
addProgress(){ //增大预设阈值
if (this.PresetValue >= 300) {
this.PresetValue = 300
}else{
this.PresetValue = parseInt(this.PresetValue) + 20
}
this.isChange = false;
this.setProgress = ((this.PresetValue ) /300) * 100
this.SetKey( 'GasThreshold', this.PresetValue );
},
SetKey(key1, value1) {
com.set({
key: key1 + '',
value: value1 + '',
// success or failed 状态打印
});
},
设备认证页面开发
相关界面如上图所示,我们将设备认证步骤分成四个步骤:发现设备、发起认证、允许认证、输入PIN码。接下来是具体的解析内容:
1)发现设备解析
设备认证因设备状态不同显示对应的UI,上图显示的UI对应设备状态”status = start“。
startDevice(){
this.subscribeId = Math.floor(Math.random() * 10000 + 1000)
var info = {
"subscribeId": this.subscribeId, // 特定随机的
"mode": 0xAA, // 设置主动发现模式,除此之外还有被动模式DISCOVER_MODE_PASSIVE
"medium": 0, // 自动选择发现介质,目前用的是coap
"freq": 2, // 发送发现消息的频率,目前用的是HIGH 还有LOW/MID/SUPER_HIGH
"isSameAccount": false, // 取消同一账号下才能发现的限制
"isWakeRemote": false, // 目前轻量系统没有睡眠模式,所以不用睡眠唤醒功能
"capability": 0 // 目前使用DDMP
devicemanager.startDeviceDiscovery(info); // 开始设备发现
},
2)发起认证解析
AuthenticateDevice(){ // 发起认证
let extraInfo = {
targetPkgName: 'test',
appName: "Newname",
appDescription: "testAPP",
business: '0',
displayOwner: 0
};
let AuthParam = {
authType: 1, // 以PIN 码方式进行认证校验
appIcon:null,
appThumbnail:null,
extraInfo: extraInfo
};
let _this = this;
devicemanager.authenticateDevice(this.statusInfo, AuthParam, {
// 省略了相关success 和fail 回调处理,完整代码见参考链接
},
3)允许认证解析
当设备状态”status = join-pin“,允许相关认证动作并且显示相关PIN码。
joinAuthOk() {
this.joinPin() //切换显示PIN码界面
this.initStatue() //获取PIN码并显示
devicemanager.setUserOperation(0)
},
initStatue() {
this.log('initStatue')
const data = devicemanager.getAuthenticationParam()
// 参数值转换为 JSON 字符串写入data
this.log('getAuthenticationParam:' + JSON.stringify(data))
// Authentication type, 1 for pin code.
// ode ==1,pin码
if (data && data.authType == 1) {
// 完整代码见参考链接
}
},
4)输入PIN码解析
当设备状态"status = main-pin",进到相关PIN码输入、校验界面。
mainInputPin(s) { // 输入六位数字
if (this.pinNumb == 6) return
if (this.pinNumb < 6) {
this.pin[this.pinNumb] = s
++this.pinNumb
}
if (this.pinNumb == 6) {
console.log("verifyAuthInfo ok")
this.verifyAuthInfo(this.pin.join('')) // PIN码校验
}
},
自定义JSI原理和实现
JSI是OpenHarmony轻量和小型系统的一种JS API实现机制,适合封装IO、CPU密集型、OS底层等能力给到JS应用调用,通过JSI可以实现JS与C/C++代码互相访问。与OpenHarmony 轻量级系统中涉及到的 audio、device、sensor 等需要与硬件打交道的JSI 模块类似,CommunicationKit 和DeviceManager模块同样首先要加入到相关的配置文件中。ace_lite_engine 通过JSI::SetModuleAPI将JS应用中使用的关键字映射成C++函数。具体操作如下:
foundation/ace/ace_engine_lite/frameworks/module_manager/ohos_module_config.h中的OHOS_MODULES新增如下字段:
{"CommunicationKit", InitNativeApiCommunicationKit},
{"devicemanager", InitDeviceManagerModule},
加载自定义模块
如上所示,在JS应用外设控制界面中数据读取和命令下发时,引入了CommunicationKit模块,现在我们就看一下相关具体内容:
InitNativeApiCommunicationKit函数相关内容如下:
vendor/team_x/common/communicationkit/native_utils/src/nativeapi_communication_kit.cpp
void InitNativeApiCommunicationKit(JSIValue exports) {
JSI::SetModuleAPI(exports, "get", NativeapiCommunicationKit::Get); // 与JS应用中的关键字一致
JSI::SetModuleAPI(exports, "set", NativeapiCommunicationKit::Set);
}
相关C++实现NativeapiCommunicationKit::Get 和Set 的方式类似,我们参考轻量系统源码中其他模块的实现,使用ExecuteAsyncWork函数。根据给定的参数创建一个异步工作,并将其分派给主应用程序任务处理程序。其获取燃气浓度逻辑在ExecuteGet实现,设置轻量系统燃气告警阈值由ExecuteSet实现。函数args参数用来接收JS端传过来的参数(JSIValue数组),argsNum表示该数组长度。
ExecuteAsyncWork(thisVal, args, argsNum, ExecuteGet, false);
ExecuteAsyncWork(thisVal, args, argsNum, ExecuteSet, false);
加载设备管理模块
与自定义模块类似,JS应用使用软总线接口时,需要在应用执行前加载设备管理模块。设备管理模块也是OpenHarmony系统中的重要组成部分,我们在标准系统中是通过NAPI的方式来加载相关模块,而在我们轻量系统中,设备管理模块是通过JSI的方式来加载的。首先看到InitDeviceManagerModule函数相关内容如下:
foundation/distributedhardware/devicemanager/interfaces/kits/js_mini/src/native_devicemanager_js.cpp
void InitDeviceManagerModule(JSIValue exports) {
JSI::SetModuleAPI(exports, "createDeviceManager", DeviceManagerModule::CreateDeviceManager);
......
JSI::SetModuleAPI(exports, "startDeviceDiscovery", DeviceManagerModule::StartDeviceDiscoverSync);
JSI::SetModuleAPI(exports, "stopDeviceDiscovery", DeviceManagerModule::StopDeviceDiscoverSync);
JSI::SetModuleAPI(exports, "authenticateDevice", DeviceManagerModule::AuthenticateDevice);
JSI::SetModuleAPI(exports, "verifyAuthInfo", DeviceManagerModule::VerifyAuthInfo);
JSI::SetModuleAPI(exports, "setUserOperation", DeviceManagerModule::SetUserOperationSync);
JSI::SetModuleAPI(exports, "getAuthenticationParam", DeviceManagerModule::GetAuthenticationParamSync);
......
}
开发板端代码开发说明
如下图所示,我们从轻量系统软总线设备的系统启动流程出发,来分析软总线应用执行的相关要点。第一步:首先初始化软总线server;第二步:注册与软总线相关的服务,例如PRC、设备管理DeviceManager服务;第三步:JS engine加载DeviceManager接口声明;第四步:具体的RPC通信操作流程。
初始化软总线服务
轻量系统设备启动的启动过程中,会注册相关初始化软总线服务线程。该线程中的主要内容就是调用InitSoftBusServer函数。该函数会初始化与软总线相关的配置、发现、认证等相关操作。
软总线相关服务注册
服务是OpenHarmony系统中的一个重要概念,不同的功能模块,不同线程/进程之间的调用接口,都统一抽象成了服务。利用服务机制,操作系统、驱动框架等提供的能力都能被包装成服务提供给到应用调用。下面给大家具体介绍软总线相关的服务注册细节和要点。
SAMGR:作为中介者,管理Provider提供的能力,同时帮助Consumer发现Provider的能力。
Provider:服务的提供者,为系统提供能力(对外接口)。
PRC服务注册
RPC:(Remote Procedure Call)用于跨设备跨进程间的通信,在轻量系统软总线应用中,我们利用RPC能力实现了安全厨房项目中两个设备的关联控制,RPC服务注册的具体内容如下:
1.创建相关的静态服务对象;
static MiniService g_miniService = {
.GetName = GetName, // 相关服务名为mini_sa_rpc
.Initialize = Initialize,
.MessageHandle = MessageHandle,
.GetTaskConfig = GetTaskConfig,
SERVER_IPROXY_IMPL_BEGIN,
.Invoke = FeatureInvoke, // 对外提供Invoke方法,供RPC相关client端程序调用
IPROXY_END,
};
2.注册相关的服务和缺省对象;
SAMGR_GetInstance()->RegisterService((Service *)&g_miniService);
SAMGR_GetInstance()->RegisterDefaultFeatureApi(MINI_SERVICE, GET_IUNKNOWN(g_miniService));
设备管理服务注册
如果没注册设备管理服务,那么相关软总线能力也就无从谈起。在轻量系统启动时,执行相关设备管理服务初始化动作,具体内容如下:
1)创建相关的静态服务对象;
static DeviceManagerSamgrService service = {
.GetName = GetName, // 相关服务名为dev_mgr_svc
.Initialize = Initialize,
.MessageHandle = MessageHandle,
.GetTaskConfig = GetTaskConfig,
};
2)注册相关的服务和缺省对象;
SAMGR_GetInstance()->RegisterService((Service *)&service)) ;
SAMGR_GetInstance()->RegisterDefaultFeatureApi(DEVICE_MANAGER_SERVICE_NAME, GET_IUNKNOWN(service));
3)执行相关初始化动作。DeviceManager服务的初始化函数将完成相关状态回调注册和传输通道初始化,然后执行相关Publish动作,做好接收发现消息的准备。
设备间RPC通信
在标准系统应用开发中,我们可以通过分布式数据库和启动远程Ability的方式实现设备之间的通信,而在轻量系统中则通过RPC的方法来实现。在前面的内容中我们讲了服务的概念,下面是拓展的相关原理图:
Consumer:服务的消费者,调用服务提供的功能(对外接口)。
在完成软总线组网后,如果检测到燃气度数超标后,按如下步骤即可实现对智能通风设备的控制:
1.获取软总线网络中的相关节点信息;
GetAllNodeDeviceInfo("com.ohos.devicemanagerui", &nodeInfo, &infoNum);
2.获取远程节点发布的mini_sa_rpc服务中对应的IUnknown方法;
IUnknown *miniDefApi = SAMGR_GetInstance()->GetRemoteDefaultFeatureApi(nodeInfo[0]->networkId, "mini_sa_rpc");
3.查询服务所发布的相关能力,获取指向具体API接口的指针miniInterface;
miniDefApi->QueryInterface(miniDefApi, 0, (void **) &miniInterface);
4.调用相关mini_sa_rpc对外提供的Invoke能力;
miniInterface->Invoke(miniInterface, 1, &reply, NULL, NULL);
操作体验
- 提前准备好安全厨房场景中的智能窗户通风设备和智能燃气告警设备,并完成相关的编译和应用安装动作;
- 提前准备好正常工作的无线路由设备(请保证预设热点名称:test_wifi 密码:12345678;是否能连接互联网均可)
- 将燃气检测设备和窗户通风设备上电,确认两个设备应用启动正常和操作正常;
- 按如下步骤将通风设备、燃气检测设备组成一个软总线网络:
● 分别点击两个设备应用界面右上角的软总线配置图标,进入软总线配置界面;
● 点击智能燃气检测设备应用发现图标,间隔3S后点击发起认证图标;
● 点击智能通风设备软总线配置界面下的允许认证图标,正常情况下会显示一个6位数的PIN码;
● 点击智能燃气检测设备应用输入PIN码按钮,进入数字键盘输入PIN码;
● 分别点击两个应用软总线配置图标左上角的返回按键,进入设备控制界面。 设置燃气检测设备的阈值低于实际读取的燃气数值,燃气检测应用进入警报界面的同时会控制电机工作,自动通风换气,保证家居的安全。待到实际燃气数值低于设置的阈值时,则关闭电机。
参考链接
本项目中涉及到的参考资料和相关文档路径如下:欧智通BES2600WM开发板快速上手学习路径:
https://growing.openharmony.cn/mainPlay/learnPathMaps?id=17
轻量系统应用开发软总线视频课程:
https://www.bilibili.com/video/BV1BS4y1A7ry/?vd_source=fa1330...
设备管理模块文档:
https://gitee.com/openharmony/device_manager/blob/master/READ...
智能燃气检测系统样例:
https://growing.openharmony.cn/mainPlay/detail?sampleId=3935
智能窗户通风系统样例:
https://growing.openharmony.cn/mainPlay/detail?sampleId=3936总结
从本文中可以看到与标准系统一样应用都是调用设备管理模块提供的相关接口来实现的软总线发现、认证等功能,但是不同的地方在于标准系统使用了预制的DeviceManager_UI.hap来显示PIN码、输入PIN码。而轻量系统软总线应用中,相关PIN码显示、PIN码输入需要自己调用相关接口。
与标准系统软总线应用相比,目前轻量系统软总线应用只实现了轻量系统设备之前数据流转功能,轻量系统分布式拉起、分布式数据库等功能待后续更新迭代。下一步还将研究如何利用软总线来连接轻量系统和标准系统,敬请大家期待。
丰富多样的OpenHarmony开发样例离不开广大合作伙伴和开发者的贡献,如果你也想把自己开发的样例分享出来,欢迎提交到OpenHarmony知识体系SIG仓库。
共建开发样例参考https://gitee.com/openharmony-sig/knowledge/blob/master/docs/...