自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

丸騎行-OpenHarmony騎行助手

系統(tǒng) OpenHarmony
從功能需求,設(shè)計(jì)應(yīng)用交互。APP包含四個(gè)頁面,其中三個(gè)主要交互頁面在一個(gè)Tabs組件中,可點(diǎn)擊底部的導(dǎo)航bar或者左右滑動(dòng)切換展示的內(nèi)容,通過點(diǎn)擊TabContent(0)頁面中定位控件觸發(fā)Navigator導(dǎo)航到屏地圖頁面(WebPage.ets)。

想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:

51CTO 鴻蒙開發(fā)者社區(qū)

https://ost.51cto.com

項(xiàng)目名稱

丸騎行,一款幫你管理電動(dòng)車的輕便APP。

如今電動(dòng)車\自行車保有量巨大,停車點(diǎn)混亂、擁堵現(xiàn)象導(dǎo)致用車找車?yán)щy,為了給人們更好的騎行體驗(yàn),領(lǐng)航員1號(hào)團(tuán)隊(duì)基于OpenHarmony開發(fā)了丸騎行方案。用戶可體驗(yàn)遠(yuǎn)程實(shí)時(shí)查看車輛電量、位置,遠(yuǎn)程開關(guān)鎖、響鈴找車、續(xù)航估算等功能。

  • 作品標(biāo)題:丸騎行
  • 軟件分類:生活類APP
  • 應(yīng)用領(lǐng)域:交通工具-電動(dòng)車
  • 開放源碼許可證:
 Apache License
                      Version 2.0, January 2004
                   http://www.apache.org/licenses/
  • 文件說明
    IotDevice:設(shè)備開發(fā)源碼
    app/: OPenHrmony 應(yīng)用
    bin/ 設(shè)備開發(fā)固件
    hap/ 應(yīng)用Hap包
涉及的OH技術(shù)特性:

ArkUI、服務(wù)卡片、應(yīng)用內(nèi)web、分布式KvStore數(shù)據(jù)持久化、socket網(wǎng)絡(luò)通信。

1、運(yùn)行條件

開發(fā)環(huán)境準(zhǔn)備:

  • 應(yīng)用開發(fā):

DevEco Studio版本:DevEco Studio 3.1.1 Release及以上版本。

OpenHarmony SDK版本:API 9, OpenHarmony 3.2 Release

  • 設(shè)備開發(fā):
  • 主控芯片:RISC-V架構(gòu),Hi3861,適用于上海海思 HiSpark T1、潤(rùn)和 HiHope Pegasus、小熊派 BearPI Nano。
    本文使用Hihope Pegasus、BearPi Nano派驗(yàn)證通過。
  • OpenHarmony 版本: https://gitee.com/HiSpark/hi3861_hdu_iot_application。
  • Windows環(huán)境搭建: hi3861_hdu_iot_application基于Hi3861V100和OpenHarmony. 開發(fā)指南文檔地址: /doc/物聯(lián)網(wǎng)設(shè)計(jì)及應(yīng)用實(shí)驗(yàn)指導(dǎo)手冊(cè).pdf。

2、運(yùn)行說明

  • 操作一:準(zhǔn)備前文所述應(yīng)用開發(fā)環(huán)境,下載hap包到本地,正確編譯后上傳到DAYU800開發(fā)板;
  • 操作二:給DAYU800開發(fā)板連接上可以訪問互聯(lián)網(wǎng)的熱點(diǎn)
  • 操作三:【僅連接硬件需要】使用Hiburn工具下載bin文件到Hi3861開發(fā)板,使用串口工具查看當(dāng)前Hi3861的IP地址
  • 操作四:刷新設(shè)備狀態(tài)
  • 運(yùn)行APP后,右滑進(jìn)入地圖頁面,等待幾秒(看網(wǎng)絡(luò)情況),地圖刷新出來后頁面自動(dòng)刷新,看到頁面獲取到定位數(shù)據(jù)后,已同步保存到數(shù)據(jù)庫。(若GPS未開啟請(qǐng)點(diǎn)擊控件開啟定位功能。)
  • 進(jìn)入首頁頁面,查看對(duì)應(yīng)的電量、位置等數(shù)據(jù),因?yàn)閿?shù)據(jù)寫入是異步的,若未及時(shí)刷新可點(diǎn)擊刷新數(shù)據(jù)控件獲取最新數(shù)據(jù)。
  • 操作五: 桌面服務(wù)卡片
  • 在桌面長(zhǎng)按應(yīng)用圖標(biāo),選擇添加卡片。
  • 在卡片上可查看數(shù)據(jù),當(dāng)前實(shí)現(xiàn)了點(diǎn)擊對(duì)應(yīng)控件或者定時(shí)刷新固定的數(shù)據(jù),暫未與數(shù)據(jù)庫同步。

3、測(cè)試說明

演示視頻鏈接領(lǐng)航員1號(hào)-智騎行

  • 關(guān)于連接硬件:
  • 運(yùn)行APP,點(diǎn)擊首頁的臨時(shí)車輛,輸入Hi3861的IP地址,然后點(diǎn)擊wifi按鈕控件連接設(shè)備,若失敗重啟應(yīng)用或者檢查IP是否正確
  • 點(diǎn)擊開鎖、響鈴找車按鈕,硬件會(huì)做出相應(yīng)動(dòng)作?!拘枰布浜?,具體效果看演示視頻】
  • Hi3861設(shè)備默認(rèn)連接的wifi信息如下
    #define CONFIG_WIFI_SSID “r1” // 要連接的WiFi 熱點(diǎn)賬號(hào)
    #define CONFIG_WIFI_PWD “88888889” // 要連接的WiFi 熱點(diǎn)password
    #define CONFIG_CLIENT_PORT 8888 // 要連接的服務(wù)器端口
  • 關(guān)于獲取定位信息
    定位需要GPS模塊,為了評(píng)委測(cè)試方便,保證評(píng)審期間硬件設(shè)備24h不關(guān)機(jī),每次運(yùn)行APP時(shí),每1s至少可獲取1次上報(bào)數(shù)據(jù)。由于地圖開放平臺(tái)限額地址逆編碼5000次/日,故在H5中默認(rèn)限制逆編碼10次/運(yùn)行,若不想頻繁啟動(dòng)APP,可修改文件src/main/resources/rawfile/index.html如下四段的定義:
<script type="text/javascript">

    var publish_topic="PilotWeb";
    ....
    var getAddressCount = 4990 // 每次逆編碼數(shù)值+1,到5000停止地址逆編碼
    ....
</script>

4、技術(shù)架構(gòu)

(1)APP功能框架

(2)UX/UI設(shè)計(jì)

從功能需求,設(shè)計(jì)應(yīng)用交互。APP包含四個(gè)頁面,其中三個(gè)主要交互頁面在一個(gè)Tabs組件中,可點(diǎn)擊底部的導(dǎo)航bar或者左右滑動(dòng)切換展示的內(nèi)容,通過點(diǎn)擊TabContent(0)頁面中定位控件觸發(fā)Navigator導(dǎo)航到屏地圖頁面(WebPage.ets)。

APP主要頁面布局(文中不展示具體頁面布局代碼,主要講解UI信息與交互):

build() {
    Column(){
      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
        TabContent() {...}
        .tabBar(this.TabBuilder(0,'首頁'))

        TabContent() {...}
        .tabBar(this.TabBuilder(1,'地圖'))

        TabContent() {...}
        .tabBar(this.TabBuilder(2,'我的'))
      }
      .vertical(false)
      .barHeight(100)
      .onChange((index: number) => {
        this.currentIndex = index
      })
      .width('100%')
      .height('100%')

    }
    .height('100%')
    .backgroundImage($r('app.media.background_lite'))
    .backgroundImageSize({ width: '100%', height: '100%' })
  }

為了便于區(qū)分當(dāng)前的Tabs的TabContent,自定義一個(gè)Tab bar,選中時(shí)顯示不同圖標(biāo)與文字效果。

@Builder TabBuilder(index: number ,name:string) {
    Column() {
      Image(this.currentIndex === index ? $r("app.media.bar_on") : $r("app.media.bar_off"))
        .width(50)
        .height(50)
        .margin({ bottom: 8 })
        .objectFit(ImageFit.Contain)
      Text(name)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(this.my_font_size)
        .lineHeight(this.my_font_size+2)
    }.width('100%')
  }

應(yīng)用首頁(Index.ets)

啟動(dòng)應(yīng)用后進(jìn)入TabContent(0)設(shè)備主頁,這里展示用戶常用的功能和信息。

  • UI信息:顯示實(shí)時(shí)電量(同步數(shù)據(jù)庫)、開關(guān)鎖、響鈴找車等控件。
  • UX交互:
  • 開關(guān)鎖控件,點(diǎn)擊觸發(fā)this.tcpSend()、this.webController.runJavaScript(‘RingOff()’)或者this.webController.runJavaScript(‘RingOn()’)函數(shù)(具體實(shí)現(xiàn)后文講解),設(shè)備在線時(shí)可實(shí)現(xiàn)消息通信(實(shí)測(cè)見第三章-功能演示);
  • 響鈴找車控件,點(diǎn)擊觸發(fā)this.tcpSend()、this.webController.runJavaScript(‘RingOn()’)、this.webController.runJavaScript(‘RingOff()’)函數(shù)
  • wifi連接控件,顯示設(shè)備近場(chǎng)連接狀態(tài)(socket),點(diǎn)擊觸發(fā)執(zhí)行this.tcpConnect() 或者this.tcpSend()函數(shù),實(shí)現(xiàn)近場(chǎng)通信。
  • 位置信息控件,點(diǎn)擊觸發(fā)Navigator導(dǎo)航到大屏地圖頁面,查看可視化定位數(shù)據(jù)
  • 累計(jì)騎行、預(yù)計(jì)續(xù)航控件展示里程數(shù)據(jù),數(shù)據(jù)根據(jù)數(shù)據(jù)庫中的電量來估算。
  • 點(diǎn)擊刷新數(shù)據(jù)控件,獲取數(shù)據(jù)庫中最新的數(shù)據(jù),并展示到對(duì)應(yīng)控件。
  • 臨時(shí)車輛控件,用于連接臨時(shí)車輛,支持TCP Socket通信的車輛都可以連接。點(diǎn)擊時(shí)觸發(fā)執(zhí)行this.dialogController.open(),打開自定義的對(duì)話框,設(shè)置目標(biāo)IP,隨連隨用,IP不做持久化存儲(chǔ)。

地圖頁面(Index.ets)

點(diǎn)擊底部bar或者再右滑動(dòng)到TabContent(1)可切換到車輛可視化定位頁面,包含帶標(biāo)簽的地圖和定位開關(guān)。

  • UI信息:可視化車輛位置、定位開關(guān)。
  • UX交互:
  • 點(diǎn)擊開啟定位按鈕,觸發(fā)執(zhí)行訂閱位置信息函數(shù)this.webController.runJavaScript(‘subscribeGPS()’)
  • 點(diǎn)擊關(guān)閉定位按鈕,觸發(fā)執(zhí)行訂閱位置信息函數(shù)this.webController.runJavaScript(‘unsubscribeGPS()’)
  • 文本顯示:車輛實(shí)時(shí)地址詳情

用戶個(gè)人頁面(Index.ets)

點(diǎn)擊底部bar或者再右滑動(dòng)到TabContent(2)可切換到用戶設(shè)置頁面,可查看、設(shè)置車輛的基本信息。

  • UI信息:車輛擁有者信息、車輛固定IP、序列號(hào)、固件版本號(hào)。
  • UX交互:IP輸入控件,可輸入車輛IP并支持持久化保存;

大屏地圖頁面(WebPage.ets)

通過點(diǎn)擊TabContent(0)頁面中定位控件觸發(fā)Navigator導(dǎo)航到大屏地圖頁面(WebPage.ets),該頁面會(huì)加載本地web,完成地圖加載、設(shè)備上報(bào)數(shù)據(jù)的獲取。

  • UI信息:車輛的地理位置,每1min自動(dòng)刷新。
  • UX交互:支持縮放地圖;

服務(wù)卡片

  • UI信息:車輛的地理位置,每1min自動(dòng)刷新。
  • UX交互:當(dāng)前只實(shí)現(xiàn)了點(diǎn)擊對(duì)應(yīng)控件或者定時(shí)刷新固定的數(shù)據(jù),暫未與數(shù)據(jù)庫同步。

#星計(jì)劃# 丸騎行-OpenHarmony騎行助手-鴻蒙開發(fā)者社區(qū)#星計(jì)劃# 丸騎行-OpenHarmony騎行助手-鴻蒙開發(fā)者社區(qū)

(3)各功能實(shí)現(xiàn)

數(shù)據(jù)管理與通信連接

數(shù)據(jù)管理

為方便使用和管理數(shù)據(jù),使用KvStore進(jìn)行管理,在src/main/ets/model/KvStoreModel.ts創(chuàng)建了數(shù)據(jù)模板,提供KvStoreModel.createKvStore() KvStoreModel.get() KvStoreModel.put() 接口用于創(chuàng)建獲取數(shù)據(jù)庫數(shù)據(jù)。

例如在Index.ets中,頁面加載時(shí)先初始化。

import common from '@ohos.app.ability.common'
import { KvStoreModel } from '../model/KvStoreModel'
let kvStoreModel: KvStoreModel = new KvStoreModel() 

aboutToAppear() {
    let context = getContext(this) as common.UIAbilityContext
    // 獲取數(shù)據(jù)庫對(duì)象
    kvStoreModel.createKvStore(globalThis.context,(value)=>{
      console.info('KVStore:kvStoreModel.createKvStore Callback'+value)
    })
    ...
  }

后續(xù)根據(jù)業(yè)務(wù)需求進(jìn)行存取,如在點(diǎn)擊刷數(shù)據(jù)按鈕時(shí),獲取傳入的數(shù)據(jù)。

// 手動(dòng)刷新數(shù)據(jù)(位置+電量+續(xù)航估算)
  Column() {
   ...
    Text("刷新數(shù)據(jù)")
      .fontSize(this.my_font_size)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .fontColor("black")
      .maxLines(this.MAX_LINES)
      .height("40%")
  }.width('48%').height("100%")
  .backgroundColor("#FFFFFF")
  .borderRadius(15)
  .alignItems(HorizontalAlign.Center)
  .justifyContent(FlexAlign.Center)
  .onClick(()=>
  {
    kvStoreModel.get(Const.PILOT_POWER_KEY,(value)=>
    {
      this.bike_power = value
    })
    kvStoreModel.get(Const.PILOT_LOCATION_KEY,(value)=>
    {
      this.bike_location = value
    })
  }

在src/main/ets/common/Constant.ts定義了常用的KEY。

// KvStore存放車輛電量
  static readonly PILOT_POWER_KEY: string = 'PILOT_POWER';
  // KvStore存放車輛位置
  static readonly PILOT_LOCATION_KEY:string = 'PILOT_LOCATION';
  // KvStore存放車輛固定IP
  static readonly PILOT_IP_KEY:string= 'PILOT_IP';
  // KvStore存放車輛滿電續(xù)航,默認(rèn)520+999 = 1519Km,用戶可根據(jù)經(jīng)驗(yàn)設(shè)定
  static readonly PILOT_MAX_DURATION_KEY:string= 'PILOT_DURATION';

通信連接

智騎行APP支持TCP Socket、MQTT通信,用于與硬件交互數(shù)據(jù)。

Socket通信: 近場(chǎng)連接車輛,目前可獲取車輛電量。

具體實(shí)現(xiàn)流程:

創(chuàng)建一個(gè)TCPSocket對(duì)象-->提供連接-發(fā)送-接收的接口-->根據(jù)設(shè)定的IP地址連接目標(biāo)-->發(fā)送/接收數(shù)據(jù)
import socket from '@ohos.net.socket'
// 創(chuàng)建一個(gè)TCPSocket連接,返回一個(gè)TCPSocket對(duì)象。
let tcp = socket.constructTCPSocketInstance();
  tcpInit() {
    // 訂閱TCPSocket相關(guān)的訂閱事件
    tcp.on('message', value => {
      console.log("tcp on message")
      let buffer = value.message
      let dataView = new DataView(buffer)
      let str = ""
      for (let i = 0; i < dataView.byteLength; ++i) {
        str += String.fromCharCode(dataView.getUint8(i))
      }
      //接收到車輛一幀數(shù)據(jù)
      this.recv_rider_msg = str 
      //刷新電量
      this.bike_power = this.recv_rider_msg.substring(19,21).toString()
      console.log("tcp on connect received:" + str)
      // 電量做持久化保存
      kvStoreModel.put(Const.PILOT_POWER_KEY,this.bike_power)
    });
  }

  tcpSend() {
    tcp.getState().then((data) => {
      if (data.isConnected) {
        //發(fā)送消息
        tcp.send(
          { data: this.message_send, }
        ).then(() => {
          promptAction.showToast({message:"send message successful"})
        }).catch((error) => {
          promptAction.showToast({message:"send failed"})
        })
      } else {
        promptAction.showToast({message:"tcp not connect"})
      }
    })
  }

這里需要說明, 用戶可在我的頁面設(shè)定設(shè)備IP地址,可以持久化保存.若需要臨時(shí)連接一臺(tái)公共車輛或者調(diào)試時(shí)可以點(diǎn)擊臨時(shí)用車進(jìn)行連接. 臨時(shí)用車通過自定義的Dialog連接,IP地址通過Link變量獲取到。

@CustomDialog
struct CustomDialogSetIP{
    @State inputValue: string = ''
   @Link InputIP: string // 獲取的IP
  controller: CustomDialogController

  cancel: () => void
  confirm: () => void
  ....
      TextInput({ placeholder: '不做存儲(chǔ),隨連隨用192.168.43.164'}).width('85%').height('70%').fontSize(30)
            .placeholderColor("rgb(0,0,225)")
            .placeholderFont({ size: 16, weight: 100, family: 'cursive', style: FontStyle.Italic })
            .onChange((value: string) => {
              this.inputValue = value
            })
  ....
}

**MQTT通信:**遠(yuǎn)程連接車輛,獲取電量-位置信息。

實(shí)現(xiàn)流程:

①用戶ArkUI頁面,消息通信<-->②APP本地web頁面,發(fā)布或者訂閱消息<-->③云端服務(wù)器<-->④設(shè)備發(fā)布或者訂閱消息; // 數(shù)據(jù)通道是雙向的

用戶ArkUI頁面,消息通信<–>②APP本地web頁面的消息通信。

首先,在啟動(dòng)app時(shí),要在Index.ets的aboutToAppear()中創(chuàng)建一個(gè)和H5頁面通信的消息通道,實(shí)現(xiàn)如下:

// 注冊(cè)與H5通信的通道接口與回調(diào)
    try {
      // 1、創(chuàng)建兩個(gè)消息端口。
      this.ports = this.webController.createWebMessagePorts();
      // 2、在應(yīng)用側(cè)的消息端口(如端口1)上注冊(cè)回調(diào)事件。
      this.ports[1].onMessageEvent((result: web_view.WebMessage) => {
        let msg = 'Got msg from HTML:';
        if (typeof(result) === 'string') {
          console.info(`received string message from html5, string is: ${result}`);
          msg = result;
        } else if (typeof(result) === 'object') {
          if (result instanceof ArrayBuffer) {
            console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
            msg = msg + 'lenght is ' + result.byteLength;
          } else {
            console.info('not support');
          }
        } else {
          console.info('not support');
        }
        this.receivedFromHtml = msg;
        console.info('Callback when the first button is clicked')
        kvStoreModel.put('APP','Pilot')
        kvStoreModel.put(Const.PILOT_POWER_KEY,this.receivedFromHtml.substring(0,2))  //電量
        kvStoreModel.put(Const.PILOT_LOCATION_KEY,this.receivedFromHtml.substring(2,8)) //位置
        this.bike_power = this.receivedFromHtml.substring(0,2)
        this.bike_location = this.receivedFromHtml.substring(2,8)
        kvStoreModel.get(Const.PILOT_POWER_KEY,(value)=>
        {
          this.bike_power = value
        })
        kvStoreModel.get(Const.PILOT_LOCATION_KEY,(value)=>
        {
          this.bike_location = value
        })
      })
      // 3、將另一個(gè)消息端口(如端口0)發(fā)送到HTML側(cè),由HTML側(cè)保存并使用。
      this.webController.postMessage('__init_port__', [this.ports[0]], '*');
    } catch (error) {
      console.error(`ErrorCode: ${error.code},  Message: ${error.message}`);
    }

其次,需要在本地H5 src/main/resources/rawfile/index.html 中創(chuàng)建一個(gè)用于接收的監(jiān)聽端口,具體實(shí)現(xiàn)如下:

// 頁面
var h5Port;
var output = document.querySelector('.output');
window.addEventListener('message', function (event) {
    if (event.data === '__init_port__') {
        if (event.ports[0] !== null) {
            h5Port = event.ports[0]; // 1. 保存從ets側(cè)發(fā)送過來的端口
            h5Port.onmessage = function (event) {
              // 2. 接收ets側(cè)發(fā)送過來的消息.
              var msg = 'Got message from ets:';
              var result = event.data;
              if (typeof(result) === 'string') {
                console.info(`received string message from html5, string is: ${result}`);
                msg = result;
              } else if (typeof(result) === 'object') {
                if (result instanceof ArrayBuffer) {
                  console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
                  msg = msg + 'lenght is ' + result.byteLength;
                } else {
                  console.info('not support');
                }
              } else {
                console.info('not support');
              }
              // this.PositionName  = msg.toString();
              // document.getElementById("getMsg").innerText =  msg;
              send(msg.toString(),"PilotWeb"); //將收到的數(shù)據(jù)通過mqtt發(fā)送到設(shè)備。ets可直接調(diào)用H5函數(shù),該接口備用
            }
        }
    }
})

也可以直接調(diào)用H5的runJavaScript,通過H5中的函數(shù)接口發(fā)送數(shù)據(jù)到MQTT服務(wù)器. 如響鈴找車按鈕,使用this.webController.runJavaScript()即可調(diào)用H5中的RingOff()函數(shù).比消息發(fā)送更便捷。

// 響鈴找車
Column()
{
  if(this.ring_icon_flag)
  {
    Image($r("app.media.ic_ring_on_filled"))
      .onClick(() => {
          ...
        // 調(diào)用H5函數(shù),發(fā)送關(guān)閉響鈴的mqtt數(shù)據(jù)到設(shè)備
          this.webController.runJavaScript('RingOff()');
        })
  }
}

APP本地web頁面,發(fā)布或者訂閱消息<–>③云端服務(wù)器。

在本地H5中直接實(shí)現(xiàn)一個(gè)MQTT實(shí)例,實(shí)現(xiàn)數(shù)據(jù)的交互

const options={
        connectTimeout:4000,
        keepalice:20,
        clean:true,
        clientId:'mqttjsks',
        username:'hellokun',
        password:'123456',
    }
    const client=mqtt.connect('ws://MQTT服務(wù)器的ip地址:8083/mqtt',options)

    client.on('reconnect', (error) => {
     // document.getElementById("status").innerText = '正在重連';
     console.log('正在重連:', error)
     })

    client.on('error',(error)=>{
        // document.getElementById("status").innerText='Faild';
        console.log('connect faild:',error)
    })

發(fā)送數(shù)據(jù)到設(shè)備:

前面提到響鈴找車按鈕觸發(fā) this.webController.runJavaScript(‘RingOff()’);在H5中具體實(shí)現(xiàn)如下:

// 車輛關(guān)鈴
function RingOff()
{
    this.send('ring_off',"PilotWeb"); // 通過MQTT發(fā)送數(shù)據(jù)
}

接收來自設(shè)備端的數(shù)據(jù):

通信方向與發(fā)送時(shí)相反,本地的H5可以通過與ets建立的消息通道,直接發(fā)送數(shù)據(jù)到用戶頁面,這個(gè)通道也可以用來接收H5發(fā)送回來的數(shù)據(jù).

// 使用h5Port往ets側(cè)發(fā)送消息.
function PostMsgToEts(data) {
    console.info('H5 to Ets data:'+data);
    if (h5Port) {
      h5Port.postMessage(data);
    } else {
      console.error('h5Port is null, Please initialize first');
    }
}
// 調(diào)用接口發(fā)送數(shù)據(jù)到ets用戶頁面,便于存儲(chǔ)和展示
this.PostMsgToEts(PilotPower.toString()+PositionName); // 電量+位置

可視化定位

通過MQTT服務(wù)器獲取到車輛的GPS坐標(biāo),接下來使用高德地圖開放平臺(tái)的JS API進(jìn)行地圖標(biāo)點(diǎn)和逆編碼,實(shí)現(xiàn)用戶在地圖上查看車輛的具體位置信息.

只需要使用Web組件,即可加載H5頁面到用戶頁面中,

// Web component loading H5.
 Web({ src: $rawfile('index.html'), controller: this.webController })

在地圖頁面中,開關(guān)/定位功能是直接調(diào)用MQTT的訂閱/取消訂閱接口。

//訂閱話題
 function subscribe() {
     if (client.connected) {
         client.subscribe(this.subscribe_topic);
        // document.getElementById("status").innerText = '開始訂閱';
     }
 }
 //取消訂閱話題
 function unsubscribe() {
     if (client.connected) {
         client.unsubscribe(this.subscribe_topic, (error) => {
             console.log(error || '取消訂閱')
            // document.getElementById("status").innerText = '取消訂閱';
         })
    }
}

開關(guān)鎖/響鈴找車

通信連接一節(jié)講解的通信接口可實(shí)現(xiàn)點(diǎn)擊開關(guān)鎖控件,觸發(fā)x下列函數(shù),設(shè)備在線時(shí)可實(shí)現(xiàn)消息通信(實(shí)測(cè)見演示視頻)。

this.tcpSend()
this.webController.runJavaScript('RingOff()')
this.webController.runJavaScript('RingOn()')
this.webController.runJavaScript('Lock()')
this.webController.runJavaScript('UnLock()')

里程數(shù)據(jù)

里程數(shù)據(jù)根據(jù)電量和用戶設(shè)定的滿電續(xù)航數(shù)據(jù)計(jì)算.獲取到電量數(shù)據(jù)后,自動(dòng)計(jì)算,計(jì)算方式為:

剩余電量/總電量 = 預(yù)計(jì)續(xù)航/用戶設(shè)定最大續(xù)航
this.max_duration = value
// 使用電量預(yù)估續(xù)航,公式: 剩余電量/總電量 = 預(yù)計(jì)續(xù)航/用戶設(shè)定最大續(xù)航 數(shù)值取整
this.bike_duration = (parseInt(this.max_duration)*parseInt(this.bike_power)/100).toFixed(0)
// 累計(jì)騎行 = 最大續(xù)航-預(yù)計(jì)續(xù)航
this.bike_distance = (parseInt(this.max_duration) - parseInt(this.bike_duration)).toFixed(0)

桌面服務(wù)卡片

創(chuàng)建一張2*4尺寸的桌面服務(wù)卡片,目前可展示里程數(shù)據(jù)和電量信息.支持定時(shí)30min自動(dòng)刷新或者用戶觸發(fā)控件刷新數(shù)據(jù). 刷新的數(shù)據(jù)目前還未與數(shù)據(jù)庫同步。

服務(wù)卡片刷新數(shù)據(jù)的方式有如圖所示幾種,智騎行APP中使用了message 和call刷新數(shù)據(jù),使用router拉起應(yīng)用。

卡片使用message與FormExtensionAbility交互介紹: 在服務(wù)卡片的獲取定位控件中,添加點(diǎn)擊事件,發(fā)起postCardAction message。

// 獲取定位
Column() {
  Image($r("app.media.ic_statusbar_gps"))
    ...
  Text(this.location)
    ...
}
.onClick(() => {
  console.info('KVStore postCardAction(this')
  postCardAction(this, {
    'action': 'message',
    'params': {
      'msgTest': 'messageEvent'
    }
  });
})
}

在FormExtensionAbility的onFormEvent生命周期中調(diào)用updateForm接口刷新卡片。

onUpdateForm(formId) {
    // 每30min自動(dòng)刷新一次
    let formData = {
      'power': 'power', // 和卡片布局中-電量對(duì)應(yīng)
      'location': 'location', // 和卡片布局中-位置對(duì)應(yīng)
      'distance': 'distance', // 和卡片布局中-里程對(duì)應(yīng)
      'duration': 'duration', // 和卡片布局中-預(yù)計(jì)續(xù)航對(duì)應(yīng)
      'beep': 'beep.', // 和卡片布局中-響鈴找車對(duì)應(yīng)
      'lock': 'lock', // 和卡片布局中-開鎖對(duì)應(yīng)
    };
    let formInfo = formBindingData.createFormBindingData(formData)
    formProvider.updateForm(formId, formInfo).then((data) => {
      console.info('FormAbility updateForm success.' + JSON.stringify(data));
    }).catch((error) => {
      console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
    })

  }

在卡片頁面通過注冊(cè)里程數(shù)據(jù)的onClick點(diǎn)擊事件回調(diào),并在回調(diào)中調(diào)用postCardAction接口觸發(fā)router事件至EntryAbility。

// 里程數(shù)據(jù)
 Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceAround }) {
    Column() .width('47%').height("100%")
   ...
    .onClick(() => {
     ....
      postCardAction(this, {
        'action': 'router',
        'abilityName': 'EntryAbility', // 只能跳轉(zhuǎn)到當(dāng)前應(yīng)用下的UIAbility
        'params': {
          'detail': 'RouterFromCard'
        }
      });
    })

在卡片頁面通過注冊(cè)車輛圖標(biāo)的onClick點(diǎn)擊事件回調(diào),并在回調(diào)中調(diào)用postCardAction接口觸發(fā)call事件至UIAbility。

// 卡片中車輛圖標(biāo)
.onClick(()=>
{
  postCardAction(this, {
    'action': 'call',
    'bundleName': 'com.example.obike',
    'abilityName': 'EntryAbility', // 只能拉起當(dāng)前應(yīng)用下的UIAbility
    'params': {
      'method': 'funA',
      'formId': this.formId,
      'detail': 'CallFromCard'
    }
  });
})

車輛硬件開發(fā)

基于OpenHarmony開發(fā)電動(dòng)車的控制系統(tǒng),主控芯片為Hi3861。近距離時(shí)可通過TCP連接APP,遠(yuǎn)程可通過連接GPS+4G模塊實(shí)現(xiàn)通信。

具體實(shí)現(xiàn):Hi3861通過串口發(fā)送指令到GPS+4G模塊獲取定位信息;通過ADC采集電池電量;通過4G模塊發(fā)送到云服務(wù)器;結(jié)合前文所述通信連接,APP從應(yīng)用內(nèi)web端口獲取數(shù)據(jù)。

Hi3861主要任務(wù)代碼如下:

while (1) {
    memset_s(recvbuf, sizeof(recvbuf), 0, sizeof(recvbuf));
    if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1) {
        printf("recv error \r\n");
    }
    printf("recv :%s\r\n", recvbuf);

    if(!strncmp(recvbuf,UNLOCK,6))
    {
        IoTGpioSetOutputVal(LockCtr_GPIO, 0);
        sleep(TASK_DELAY_1S);
        IoTGpioSetOutputVal(LockCtr_GPIO, 1);
    }
    if(!strncmp(recvbuf,LOCK,5))
    {
        IoTGpioSetOutputVal(LockCtr_GPIO, 1);
        sleep(TASK_DELAY_1S);
        IoTGpioSetOutputVal(LockCtr_GPIO, 0);
    }
    if(!strncmp(recvbuf,RING_ON,7))
    {
        IoTGpioSetOutputVal(LockCtr_GPIO, 1);
    }
    if(!strncmp(recvbuf,RING_OFF,8))
    {
        IoTGpioSetOutputVal(LockCtr_GPIO, 0);
    }
    // sleep(TASK_DELAY_1S);
    osDelay(10); // 100ms
    if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1) {
        perror("send : ");
    }
    // sleep(TASK_DELAY_1S);
}

5、展望

**作品商業(yè)價(jià)值:**萬物互聯(lián)時(shí)代,電動(dòng)車智能化是趨勢(shì),團(tuán)隊(duì)基于OpenHarmony開發(fā)的智騎行方案,擁有服務(wù)卡片、定位、找車等功能,成本低易用性強(qiáng)。

作品進(jìn)一步優(yōu)化計(jì)劃:

  • B12版本:實(shí)現(xiàn)服務(wù)卡片數(shù)據(jù)庫同步;實(shí)現(xiàn)BLE通信,無需網(wǎng)絡(luò),近場(chǎng)自動(dòng)連接;實(shí)現(xiàn)"人離車鎖,人來車開"功能;連接真實(shí)電動(dòng)車;
  • B241版本:實(shí)現(xiàn)導(dǎo)航功能、軌跡回放、截圖分享、歷史數(shù)據(jù)查看
  • B242版本: 支持圓屏幕lite設(shè)備

想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:

51CTO 鴻蒙開發(fā)者社區(qū)

https://ost.51cto.com

責(zé)任編輯:jianghua 來源: 51CTO 鴻蒙開發(fā)者社區(qū)
相關(guān)推薦

2018-06-27 16:14:51

華為云

2010-12-01 20:33:56

綠色低碳綠色生活思科

2017-01-06 13:45:45

智能 運(yùn)動(dòng)

2017-07-13 11:19:45

膜拜借貸寶

2017-07-20 14:02:05

互聯(lián)網(wǎng)

2015-08-31 15:24:17

彭于晏魅族騎行

2018-07-12 12:13:55

華為

2017-03-29 15:41:45

互聯(lián)網(wǎng)

2013-11-15 14:14:15

NetApp統(tǒng)一存儲(chǔ)統(tǒng)一存儲(chǔ)

2017-03-30 16:27:46

互聯(lián)網(wǎng)

2023-08-07 12:53:05

開發(fā)服務(wù)

2019-11-05 18:33:35

小米MIUI

2023-08-11 14:00:42

鴻蒙元服務(wù)

2015-06-16 10:32:22

Airwheel電動(dòng)平

2017-07-19 15:45:03

互聯(lián)網(wǎng)

2017-07-19 15:41:46

互聯(lián)網(wǎng)

2017-04-11 16:17:34

共享單車物聯(lián)

2018-10-31 16:31:00

SplunkTrek-Segafr數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)