尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HarmonyOS APP开发---“智泊“智能停车App,需要用到这个库

HarmonyOS APP开发---“智泊“智能停车App,需要用到这个库 HarmonyOS APP开发—智泊智能停车App需要用到这个库做一个智能停车 App停车场每个车位装一个 CoAP 传感器手机要组播发现所有车位、查询空位状态。ohos/coap的客户端能力 组播支持让车位状态实时可见UDP 通信省电又低延迟。 仓库地址https://gitcode.com/CPF-ApplicationTPC/ohos_coap 安装ohpm install ohos/coap写在前面智泊的核心场景停车场有 200 个车位每个车位装一个 CoAP 传感器地磁或红外检测有无车辆。用户打开 App要看到整个停车场的空位分布点某个车位看详细信息。为什么不用 HTTP传感器是电池供电的低功耗 MCUHTTP 的 TCP 三次握手 几百字节 header 太浪费200 个车位逐个 HTTP 轮询网络和电量都扛不住停车场是局域网环境不需要走公网CoAP基于 UDP报文头只有 4 字节复用 REST 语义还支持组播——一次广播请求所有传感器各自响应。这正是智泊需要的。ohos/coap基于 libcoap v4.3.1通过 NAPI 把 CoAP 客户端和服务端能力暴露给 ArkTS。这篇文章聊什么组播发现车位——一次请求获取全部车位状态单播查询详情——点击某车位查详细信息工程实践——并发实例管理、日志与隐私、内存监控主动 POST打开智泊 AppCoAP 组播请求车位传感器1 响应车位传感器2 响应...200 个传感器汇总空位列表渲染停车场地图点击某车位单播 GET 详情显示: 空闲/占用/预约传感器状态变更App 作为服务端接收第一步安装与权限ohpminstallohos/coaprequestPermissions:[{name:ohos.permission.INTERNET},{name:ohos.permission.GET_NETWORK_INFO}]第二步组播发现全部车位一次组播请求200 个传感器各自响应自己的状态import{CoapClient,CoapRequestMethod,CoapRequestType,CoapResponse,CoapResponseCode,CoapBlockMode,}fromohos/coapinterfaceSpotInfo{id:string// 车位编号如 A-023occupied:boolean// 是否占用section:string// 区域如 A 区sensorIp:string// 传感器 IP用于后续单播查询}EntryComponentstruct ParkingMapPage{Statespots:SpotInfo[][]Statescanning:booleanfalse// 组播发现所有车位asyncdiscoverSpots(){this.scanningtruethis.spots[]// 每个请求创建独立实例不可复用constclientnewCoapClient()client.setBlockMode(CoapBlockMode.COAP_BLOCK_USE_LIBCOAP)try{constdata:CoapResponseawaitclient.request(coap://224.0.1.187:5683/parking/spot,// CoAP 组播地址CoapRequestMethod.GET,CoapRequestType.COAP_MESSAGE_NON,// 组播用 NON无需确认)if(data.codeCoapResponseCode.SUCCESS){constspot:SpotInfoJSON.parse(data.message)this.spots.push(spot)}}catch(err){console.error(组播发现失败: JSON.stringify(err))}finally{this.scanningfalse}}build(){Column(){Button(this.scanning?扫描中...:扫描空车位).onClick(()this.discoverSpots()).enabled(!this.scanning)// 停车场地图按区域分组展示Text(空闲${this.spots.filter((s)!s.occupied).length}/${this.spots.length}).fontSize(18).fontWeight(FontWeight.Bold).margin(12)Grid(){ForEach(this.spots,(spot:SpotInfo){GridItem(){Text(spot.id).fontSize(12).fontColor(spot.occupied?#999:#FFF).backgroundColor(spot.occupied?#EEE:#4ECDC4).width(56).height(56).textAlign(TextAlign.Center).borderRadius(6)}.onClick(()this.querySpotDetail(spot))},(spot:SpotInfo)spot.id)}.columnsTemplate(1fr 1fr 1fr 1fr 1fr).layoutWeight(1)}.width(100%).height(100%)}}为什么用COAP_MESSAGE_NON组播场景下传感器不需要逐个确认否则 200 个确认包会拥塞网络NONNon-confirmable是组播的标准选择。第三步单播查询车位详情用户点击某个车位单播查询详细信息占用时长、是否可预约等// 查询单个车位详情asyncquerySpotDetail(spot:SpotInfo){// ✅ 每次查询创建独立实例避免并发状态冲突constclientnewCoapClient()client.setBlockMode(CoapBlockMode.COAP_BLOCK_USE_LIBCOAP)try{constdataawaitclient.request(coap://${spot.sensorIp}:5683/parking/spot/${spot.id},CoapRequestMethod.GET,CoapRequestType.COAP_MESSAGE_CON,// 单播用 CON需要确认保证可靠)if(data.codeCoapResponseCode.SUCCESS){constdetailJSON.parse(data.message)this.showSpotDialog(detail)}else{promptAction.showToast({message:车位信息获取失败})}}catch(err){console.error(查询失败: JSON.stringify(err))promptAction.showToast({message:传感器无响应})}}并发查询的正确姿势——用户快速点击多个车位时为每个请求创建独立实例// ❌ 危险复用同一实例发并发请求内部状态会冲突constsharedClientnewCoapClient()selectedSpots.forEach((spot){sharedClient.request(coap://${spot.sensorIp}/...,...)// 状态互相干扰})// ✅ 安全每个请求独立实例asyncfunctionqueryBatch(spots:SpotInfo[]):PromiseArraySpotInfo{constresults:ArraySpotInfo[]// 控制并发数避免瞬间创建过多实例constCONCURRENCY5for(leti0;ispots.length;iCONCURRENCY){constbatchspots.slice(i,iCONCURRENCY)constbatchResultsawaitPromise.all(batch.map(async(spot){constclientnewCoapClient()// 独立实例try{constdataawaitclient.request(coap://${spot.sensorIp}:5683/parking/spot/${spot.id},CoapRequestMethod.GET,CoapRequestType.COAP_MESSAGE_CON,)returndata.codeCoapResponseCode.SUCCESS?JSON.parse(data.message):null}catch(e){returnnull}}))results.push(...batchResults.filter((r)r!null))}returnresults}第四步App 作为服务端接收传感器主动推送车位状态变化时有车停入/驶离传感器主动 POST 到手机App 实时更新地图import{CoapServer}fromohos/coapconstservernewCoapServer()// 注册 POST 处理器接收车位状态变更推送server.handle(parking/update,(request){try{constupdateJSON.parse(request.payload)console.info(车位${update.id}状态变更:${update.occupied?占用:空闲})// 更新本地状态UI 自动刷新this.spotsthis.spots.map((s)s.idupdate.id?{...s,occupied:update.occupied}:s)return{code:2.04,payload:OK}// Changed}catch(err){return{code:4.00,payload:Bad Request}}})// 注册 GET 处理器传感器查询 App 侧配置如轮询间隔server.handle(parking/config,(request){return{code:2.05,// Contentpayload:JSON.stringify({pushInterval:5,heartbeat:60}),}})server.start(coap://0.0.0.0:5683)console.info(CoAP 服务端已启动等待传感器推送)// 页面销毁时停止服务端aboutToDisappear(){server.stop()}第五步工程实践要点1. Native 日志的开关管理setNativeLogOpen(true)能打印 libcoap 内部报文调试协议问题时非常有用但日志里会包含设备 IP 等信息// 开发阶段开启方便排查协议问题if(BuildProfile.DEBUG){CoapClient.setNativeLogOpen(true)}// 生产环境必须关闭避免隐私信息进日志// 上线前检查这一行CoapClient.setNativeLogOpen(false)建议把日志开关绑定到构建配置DEBUG/RELEASE避免上线时忘记关。2. 长时间运行的内存监控停车场监控页可能一直开着CoAP 长连接场景要关注内存趋势// 定期记录内存占用观察是否持续增长setInterval((){constmemgetMemoryUsage()console.info(内存占用:${mem}MB车位连接数:${this.spots.length})},60000)如果发现内存持续增长检查是否有未释放的CoapClient实例或未完成清理的服务端 handler。3. 弱网与超时处理停车场地下信号差请求可能超时asyncrequestWithRetry(url:string,maxRetry2):PromiseCoapResponse|null{for(letattempt0;attemptmaxRetry;attempt){constclientnewCoapClient()// 每次重试新实例try{constdataawaitclient.request(url,CoapRequestMethod.GET,CoapRequestType.COAP_MESSAGE_CON)if(data.codeCoapResponseCode.SUCCESS)returndata}catch(err){console.warn(第${attempt1}次请求失败:${JSON.stringify(err)})if(attemptmaxRetry){awaitnewPromise((r)setTimeout(r,500*(attempt1)))// 退避重试}}}returnnull}为什么智泊选了 CoAP维度HTTPMQTTCoAP传输层TCP重TCP✅ UDP轻报文头几百字节2 字节✅ 4 字节组播发现❌ 不支持❌ 不支持✅ 原生支持需要中间服务器是需要 Broker✅ 点对点直连传感器功耗高中✅ 低设备当服务端难难✅ 原生支持关键差异CoAP 的组播能力和设备可直接当服务端让智泊不需要在停车场部署中间服务器——手机和传感器点对点通信即可。总结智泊这个场景里ohos/coap解决了三件事组播发现——一次请求发现 200 个车位传感器比逐个轮询高效百倍双向能力——手机既能当客户端查询也能当服务端接收传感器主动推送低功耗——UDP 4 字节报文头电池供电的传感器能跑几年如果你也在做智能家居、农业监测、工业传感器等 IoT 场景ohos/coap让鸿蒙设备和受限设备之间的通信轻得像呼吸。
返回列表