在 HarmonyOS 3.0 和 OpenHarmony 3.2 的支持下,TCP-socket 通信 API 已經(jīng)穩(wěn)定可控,今天我們做一個(gè)控制應(yīng)用來控制小車。
效果演示:
設(shè)計(jì)思路
運(yùn)行環(huán)境:HarmonyOS 3.0,OpenHarmony 3.2
①按鍵說明
如下:
轉(zhuǎn)向控制:左右滑動(dòng)搖桿,實(shí)現(xiàn)轉(zhuǎn)向,上下滑動(dòng)搖桿,實(shí)現(xiàn)速度控制
動(dòng)力控制:上下滑動(dòng)搖桿,實(shí)現(xiàn)前進(jìn)后退
本機(jī) IP 地址展示
對(duì)端 IP 地址輸入
鏈接,斷開按鍵,主動(dòng)進(jìn)行 TCP 連接請(qǐng)求與斷開
②控制指令
本遙控器以狀態(tài)指令為驅(qū)動(dòng),每觸發(fā)一種狀態(tài)僅發(fā)送一次指令,對(duì)端在未接收到新指令的情況下一直保持當(dāng)前指令狀態(tài)。
前進(jìn)狀態(tài):“1”
后退狀態(tài):“2”
左轉(zhuǎn)狀態(tài):“3”
右轉(zhuǎn)狀態(tài):“4”
停止?fàn)顟B(tài):“0”
頁面設(shè)計(jì)
在搖桿的拖動(dòng)設(shè)計(jì)中,主要運(yùn)用 ontouchmove,ontouchend,ontouchstart 實(shí)現(xiàn)。 通過手指坐標(biāo)來確定搖桿組件的 top 和 left 值,通過設(shè)定方向閾值來判斷是否開始發(fā)送指令,通過打印回調(diào)數(shù)據(jù)來設(shè)置參數(shù)。
hml:
{{local_ip}}
CSS:
.container{ display:flex; flex-direction:column; justify-content:center; align-items:center; left:0px; top:0px; width:100%; height:100%; } .title{ font-size:40px; text-align:center; width:100%; height:40%; margin:10px; } .yaogan{ position:absolute; top:100px; left:50px; width:200px; height:200px; background-image:url("./common/RadialJoy_Area.png"); background-size:100%; background-repeat:no-repeat; z-index:-1; } .controller{ width:100px; height:100px; top:150px; left:100px; background-image:url("./common/RadialJoy_Area.png"); background-size:100%; background-repeat:no-repeat; position:absolute; z-index:1; } .forward{ position:absolute; left:550px; width:100px; height:100px; background-size:100%; z-index:-1; } .ip_input{ font-size:18px; left:30px; width:200px; height:50px; margin-top:25px; background-color:#ff2c7a87; /*background-imagenone*/ /*background-size:100%;*/ /*background-repeat:no-repeat;*/ } .btn{ width:100px; height:30px; left:30px; margin-top:5px; background-color:#ff93f0fd; /*background-imagenone*/ /*background-size:150%;*/ /*background-repeat:no-repeat;*/ } .ip_local{ font-size:20px; width:200px; height:50px; left:30px; color:#ff3850ef; margin-top:20px; background-image:url("./common/images/bg2.png"); background-size:100%; background-repeat:no-repeat; }
業(yè)務(wù)邏輯
①參數(shù)調(diào)試
我們前面為搖桿組件設(shè)置了 ontouch 事件,那么如何設(shè)計(jì) Top 或者 left 值來判斷什么時(shí)候可以開始發(fā)送指令呢?
搖桿既不可太過靈敏也不可以太過遲鈍,我們可以通過打印觸摸事件返回的參數(shù)來進(jìn)行調(diào)參。
exportdefault{ touchstartfunc(msg){ console.info(`ontouchstart,pointis:${msg.touches[0].globalX}`); console.info(`ontouchstart,pointis:${msg.touches[0].globalY}`); console.info(`ontouchstart,datais:${msg.target.dataSet.a}`); } }②觸摸控制 根據(jù)前文提到的狀態(tài)控制機(jī)制,我們應(yīng)該在 ontouchmove 中進(jìn)行判斷,當(dāng)上滑到某一閾值的時(shí)候開始發(fā)送前進(jìn)指令,當(dāng)松手時(shí)即 ontouchend 時(shí)我們應(yīng)該立即發(fā)送停止指令。 即滑動(dòng)中判斷并發(fā)送指令,停止則立馬發(fā)送停止信息。具體的閾值參數(shù)根據(jù)個(gè)人考慮進(jìn)行調(diào)試設(shè)置。
importpromptfrom'@ohos.prompt'; importwififrom'@ohos.wifi'; importsocketfrom'@ohos.net.socket'; importdisplayfrom'@ohos.display'; varpromise; exportdefault{ data:{ title:"", x:150, y:100, forward_x:150, msg:"forward", forward_image:"Button_normal", TGA:"YZJ", command:"1", local_ip:"", remote_ip:"", speed_mode:1, speed:10, tcp:socket.constructTCPSocketInstance(), pre_cmd:'0', cur_cmd:'0' }, onInit(){ this.title=this.$t('strings.world'); this.getIpAddress(); this.creatScoket(); }, send_cmd(cmd){ if(cmd!=this.cur_cmd){ this.cur_cmd=cmd; this.sendMessage(cmd); } }, onMoveStart(e){ console.info("開始移動(dòng)"+JSON.stringify(e)); }, toSpeed_mode(){ if(this.speed_mode==0) this.speed_mode=1; elseif(this.speed_mode==1) this.speed_mode=0; }, onMove(e){ //圓心是(100,250) if(this.speed_mode==0){ console.info(JSON.stringify(e)) letnx=e.touches[0].globalY-50; this.x=nx; } elseif(this.speed_mode==1){ console.info(JSON.stringify(e)) letny=e.touches[0].globalX-50; this.y=ny; if(ny>=110){ this.msg="trun_right" console.info("YZJ:正在向右轉(zhuǎn)") this.command="4"; this.send_cmd(this.command); } elseif(ny<=90){ ????????????????this.msg="trun_left" ????????????????console.info("YZJ:正在向做左轉(zhuǎn)") ????????????????this.command="3"; ????????????????this.send_cmd(this.command); ????????????} ????????} ????}, ????onMoveEnd(){ ????????this.x=150; ????????this.y=100; ????????this.msg="stop" ????????this.command='0'; ????????this.send_cmd(this.command); ????}, ????onForwardstart(e){ ????????this.forward_image="Button_active"; ????????this.forward_x=e.touches[0].globalY-50 ????}, ????onForward(e){ ????????if(?e.touches[0].globalY-50<=140){ ????????????console.info("正在前進(jìn)") ????????????this.msg="forward" ????????????this.command="1" ????????????this.send_cmd(this.command); ????????????if(e.touches[0].globalY-50<100){ ????????????????this.forward_x=100 ????????????} ????????????else ????????????this.forward_x=e.touches[0].globalY-50 ????????} ????????else?if(e.touches[0].globalY-50>165){ console.info("正在后退") this.msg="backoff" this.command="2" this.send_cmd(this.command); if(e.touches[0].globalY-50>200){ this.forward_x=200 } else this.forward_x=e.touches[0].globalY-50 } }, onForwardend(){ this.forward_x=150; console.info("停止前進(jìn)") this.msg="stop" this.forward_image="Button_normal" this.command="0" this.send_cmd(this.command); }, //創(chuàng)建udpSocket默認(rèn)端口10006 creatScoket:asyncfunction(){ this.tcp.bind({address:this.local_ip,port:8888,family:1},err=>{ if(err){ console.log('YZJ---bindfail'); return; } console.log('YZJ---bindsuccess'); }) //監(jiān)聽收到的信息打印到屏幕上 this.tcp.on('message',value=>{ letbuffer=value.message; letdataView=newDataView(buffer); letstr=""; for(leti=0;i{ console.log('YZJ---sendsuccess'); }).catch(err=>{ console.log('YZJ---sendfail'); }); }, onConnect:asyncfunction(){ promise=this.tcp.connect({address:{address:"192.168.1.1",port:8888,family:1},timeout:6000}); promise.then(()=>{ prompt.showToast({ message:"連接成功!" }) console.log('YZJ---connectsuccess'); this.tcp.setExtraOptions({ keepAlive:true, OOBInline:true, TCPNoDelay:true, socketLinger:{on:true,linger:10}, receiveBufferSize:1000, sendBufferSize:1000, reuseAddress:true, socketTimeout:3000, },err=>{ if(err){ console.log('YZJ---setExtraOptionsfail'); return; } console.log('YZJ---setExtraOptionssuccess'); }); }).catch(err=>{ console.log('YZJ---connectfail'); prompt.showToast({ message:"連接失?。? }) }); }, onDisconnect(){ this.tcp.close() prompt.showToast({ message:"斷開鏈接!" }) }, onDestroy(){ this.tcp.close() prompt.showToast({ message:"斷開鏈接!" }) }, //獲取本機(jī)ip地址 getIpAddress(){ letip=wifi.getIpInfo().ipAddress; this.local_ip=(ip>>24&0xFF)+"."+((ip>>16)&0xFF)+"."+((ip>>8)&0xFF)+"."+(ip&0xFF); }, get_remote_ip(e){ this.remote_ip=e.value } }③TCP 通過輸入框獲取對(duì)端 IP 地址,點(diǎn)擊鏈接按鍵時(shí)觸發(fā) connect 方法請(qǐng)求連接,連接成功彈出對(duì)話框"連接成功"。 展示本機(jī) IP 地址。 應(yīng)用或者頁面銷毀時(shí)應(yīng)關(guān)閉連接,否則會(huì)占據(jù)對(duì)端該端口,導(dǎo)致下次連接失敗。 根據(jù)狀態(tài)驅(qū)動(dòng)指令控制,由于 ontouchmove 是一直在觸發(fā)的,也就是判斷是一直在進(jìn)行的,當(dāng)我們保持搖桿前進(jìn)狀態(tài)的時(shí)候,注意要判斷指令狀態(tài)是否更新了?如果指令未變,那么就不再發(fā)送指令。只有指令變化的時(shí)候才會(huì)發(fā)送一次指令。 只有連接成功后,才能夠發(fā)送信息。
tcp 設(shè)置參數(shù):
|參數(shù)|描述| |-|-| |keepAlive|是否保持連接。默認(rèn)為false。| |OOBInline|是否為OOB內(nèi)聯(lián)。默認(rèn)為false。| |TCPNoDelay|TCPSocket連接是否無時(shí)延。默認(rèn)為false。| |receiveBufferSize|接收緩沖區(qū)大?。▎挝唬築yte)| |sendBufferSize|發(fā)送緩沖區(qū)大小(單位:Byte)| |reuseAddress|是否重用地址。默認(rèn)為false| |socketTimeout|套接字超時(shí)時(shí)間,單位毫秒(ms)|建議開啟 HarmonyOS 工程,開發(fā)完畢后可同步安裝到 OpenHarmony 設(shè)備,反之則會(huì)變得麻煩一些。
④申請(qǐng)權(quán)限
"reqPermissions":[ { "name":"ohos.permission.GET_WIFI_INFO" }, { "name":"ohos.permission.GET_NETWORK_INFO" }, { "name":"ohos.permission.INTERNET" }, { "name":"ohos.permission.SET_NETWORK_INFO" }, { "name":"ohos.permission.ACCELEROMETER" } ]
結(jié)語
本次分享的應(yīng)用需要南北向開發(fā)配合食用,同時(shí)需要 HarmonyOS 3.0 設(shè)備或者 OpenHarmony 3.2 設(shè)備。 HarmonyOS 2.0 設(shè)備可考慮采用 JS/JAVA 混合開發(fā),JAVA 側(cè)實(shí)現(xiàn) Socket 通信,可參考我往期博客。 下一期,我將會(huì)分享如何配置 HarmonyOS 3.0 設(shè)備的碰一碰拉起應(yīng)用配置。
審核編輯:湯梓紅
-
遙控器
+關(guān)注
關(guān)注
18文章
837瀏覽量
66143 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1975瀏覽量
30210 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3722瀏覽量
16323
原文標(biāo)題:為鴻蒙小車做一個(gè)遙控器
文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論