在 HarmonyOS 3.0 和 OpenHarmony 3.2 的支持下,TCP-socket 通信 API 已经稳定可控,今天我们做一个控制应用来控制小车。
效果演示:
设计思路
运行环境:HarmonyOS 3.0,OpenHarmony 3.2
①按键说明
如下:
转向控制:左右滑动摇杆,实现转向,上下滑动摇杆,实现速度控制
动力控制:上下滑动摇杆,实现前进后退
本机 IP 地址展示
对端 IP 地址输入
链接,断开按键,主动进行 TCP 连接请求与断开
②控制指令
本遥控器以状态指令为驱动,每触发一种状态仅发送一次指令,对端在未接收到新指令的情况下一直保持当前指令状态。
前进状态:“1”
后退状态:“2”
左转状态:“3”
右转状态:“4”
停止状态:“0”
页面设计
在摇杆的拖动设计中,主要运用 ontouchmove,ontouchend,ontouchstart 实现。 通过手指坐标来确定摇杆组件的 top 和 left 值,通过设定方向阈值来判断是否开始发送指令,通过打印回调数据来设置参数。
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; }
业务逻辑
①参数调试
我们前面为摇杆组件设置了 ontouch 事件,那么如何设计 Top 或者 left 值来判断什么时候可以开始发送指令呢?
摇杆既不可太过灵敏也不可以太过迟钝,我们可以通过打印触摸事件返回的参数来进行调参。
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}`); } }②触摸控制 根据前文提到的状态控制机制,我们应该在 ontouchmove 中进行判断,当上滑到某一阈值的时候开始发送前进指令,当松手时即 ontouchend 时我们应该立即发送停止指令。 即滑动中判断并发送指令,停止则立马发送停止信息。具体的阈值参数根据个人考虑进行调试设置。
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("开始移动"+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:正在向右转") this.command="4"; this.send_cmd(this.command); } elseif(ny<=90){ this.msg="trun_left" console.info("YZJ:正在向做左转") 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("正在前进") 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("停止前进") this.msg="stop" this.forward_image="Button_normal" this.command="0" this.send_cmd(this.command); }, //创建udpSocket默认端口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'); }) //监听收到的信息打印到屏幕上 this.tcp.on('message',value=>{ letbuffer=value.message; letdataView=newDataView(buffer); letstr=""; for(leti=0;i< dataView.byteLength; ++i) { str += String.fromCharCode(dataView.getUint8(i)) } this.title =str; }); }, sendMessage: async function(cmd){ //发送信息 // let promise1 = this.tcp.connect({ address: {address: this.remote_ip, port: 10006, family: 1} , timeout: 6000}); let promise2 = this.tcp.send({ data:cmd }); promise2.then(() =>{ 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:"断开链接!" }) }, //获取本机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 通过输入框获取对端 IP 地址,点击链接按键时触发 connect 方法请求连接,连接成功弹出对话框"连接成功"。 展示本机 IP 地址。 应用或者页面销毁时应关闭连接,否则会占据对端该端口,导致下次连接失败。 根据状态驱动指令控制,由于 ontouchmove 是一直在触发的,也就是判断是一直在进行的,当我们保持摇杆前进状态的时候,注意要判断指令状态是否更新了?如果指令未变,那么就不再发送指令。只有指令变化的时候才会发送一次指令。 只有连接成功后,才能够发送信息。
tcp 设置参数:
|参数|描述| |-|-| |keepAlive|是否保持连接。默认为false。| |OOBInline|是否为OOB内联。默认为false。| |TCPNoDelay|TCPSocket连接是否无时延。默认为false。| |receiveBufferSize|接收缓冲区大小(单位:Byte)| |sendBufferSize|发送缓冲区大小(单位:Byte)| |reuseAddress|是否重用地址。默认为false| |socketTimeout|套接字超时时间,单位毫秒(ms)|建议开启 HarmonyOS 工程,开发完毕后可同步安装到 OpenHarmony 设备,反之则会变得麻烦一些。
④申请权限
"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" } ]
结语
本次分享的应用需要南北向开发配合食用,同时需要 HarmonyOS 3.0 设备或者 OpenHarmony 3.2 设备。 HarmonyOS 2.0 设备可考虑采用 JS/JAVA 混合开发,JAVA 侧实现 Socket 通信,可参考我往期博客。 下一期,我将会分享如何配置 HarmonyOS 3.0 设备的碰一碰拉起应用配置。
审核编辑:汤梓红
-
遥控器
+关注
关注
18文章
829浏览量
65823 -
HarmonyOS
+关注
关注
79文章
1964浏览量
29942 -
OpenHarmony
+关注
关注
25文章
3628浏览量
16027
原文标题:为鸿蒙小车做一个遥控器
文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论