基于HarmonyOS控制Hi3861小车之信息通信 原创 精华

软通动力HOS
发布于 2021-8-31 11:37
浏览
27收藏

基于HarmonyOS控制Hi3861小车之信息通信-鸿蒙开发者社区

引言 

      在鸿蒙应用实际开发中,经常会遇到App与IOT设备间的通信,本节主要详细讲述一下通信关键技术,考虑到TCP/UDP协议的特性,两者间通过UDP进行通信是一种必然的选择,UDP一种无连接的协议,具有资源消耗小,处理速度快的优点,了解UDP是怎么通信的,这对于每一个HarmonyOS开发者也是需要了解的重点知识。

核心类

DatagramSocket、DatagramPacket、EventHandler下面分别简单介绍下:

1.DatagramSocket:

构造器DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。主要方法receive(DatagramPacket p):从该DatagramSocket中接收数据报,send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。

2.DatagramPacket:

构造器DatagramPacket(byte[] buf, int length, InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决定了该数据报的目的地。

3.EventHandler:

是HarmonyOS用于处理线程间通信的一种机制,可以通过EventRunner创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用EventHandler创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过EventHandler通知主线程,主线程再更新UI。

功能介绍

 通过App Demo控制小车运动(前进、后退、左转、右转、停止),主要通过UDP数据包发送命令,来说明它们间是怎么通信的,它们间控制命令以json格式发送。

如:

"mode": "CarControl",//控制命令分类
    "cmd": "forward"//具体命令
}。

开发指南

1、创建UDP协议的发送命令对象 

private UdpManager() {
        try {
            mGpsDatagramSocket = new DatagramSocket();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

2、将要发送的数据封装成DatagramPacket对象发送

DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length,
InetAddress.getByName(getIp()), PORT);
// 开始发送
mGpsDatagramSocket.send(sRequest);

3、构造发送的命令

public void sendMessage(String info) {
        Gson gson = new Gson();
        WifiCommand messageInfo = new WifiCommand();
        messageInfo.setCmd(info);
        //控制类型
        messageInfo.setMode();
        //转换成json
        String resultJson = gson.toJson(messageInfo);
        // 创建发送命令SendMessageRunnable对象
        mSendMessageRunnable = new SendMessageRunnable();
        mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8));
        // 启动发送命令线程
        mEventHandler.postTask(mSendMessageRunnable);
        if ("stop".equals(info) || "tripod_on".equals(info) || "tripod_off".equals(info)){
            HiLog.info(label, "info = " + info);
        } else {
            // 启动发送Gps请求线程和接收信息线程
            startReceive();
            startSendGpsMessage();
        }
        HiLog.info(label, "sendMessage = " + resultJson);
    }

实现效果

 基于HarmonyOS控制Hi3861小车之信息通信-鸿蒙开发者社区

附上主要源代码

1. MainAbilitySlice

public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener{
    private Button iTurnUp,iTurnDown,iTurnLeft,iTurnRight,iTurnRun;

    private UdpManager udpManager;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        initComponent();
        // 初始化WiFi控制对象
        udpManager = UdpManager.getInstance(this);
    }

    private void initComponent(){
        iTurnUp = (Button) findComponentById(ResourceTable.Id_i_up);
        iTurnUp.setClickedListener(this);

        iTurnDown = (Button) findComponentById(ResourceTable.Id_i_down);
        iTurnDown.setClickedListener(this);

        iTurnLeft = (Button) findComponentById(ResourceTable.Id_i_left);
        iTurnLeft.setClickedListener(this);

        iTurnRight = (Button) findComponentById(ResourceTable.Id_i_right);
        iTurnRight.setClickedListener(this);

        iTurnRun = (Button) findComponentById(ResourceTable.Id_i_run);
        iTurnRun.setClickedListener(this);

2.  UdpManager

/**
 * UDP连接类
 */
public class UdpManager {
    private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00134, "UdpManager");
    private static final int PORT = 48100;
    private static final int GET_MESSAGE = 1;
    private static UdpManager sUdpManager;
    private static Context sContext;
    private UdpReceiveCallback mReceiveInformationCallback;
    private ReceiveMessageRunnable mReceiveMessageRunnable;
    private SendGpsMessageRunnable mSendGpsMessageRunnable;
    private SendMessageRunnable mSendMessageRunnable;
    private DatagramSocket mGpsDatagramSocket;
    private static  String ip = "192.168.0.1";

    /**
     * 控制是否还需要接收信息控制器
     */
    private boolean flag = false;

    private final EventHandler mEventHandler = new EventHandler(EventRunner.create()) {
        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event.eventId == GET_MESSAGE) {
                if (mReceiveInformationCallback != null) {
                    mReceiveInformationCallback.getMessage(event.object);
                }
            }
        }
    };

    private final EventHandler mReceiveEventHandler = new EventHandler(EventRunner.create()) {
    };

    private final EventHandler mSendGpsEventHandler = new EventHandler(EventRunner.create()) {
    };

    /**
     * UdpManager的单例
     *
     * @return UdpManager单例对象
     */
    public static UdpManager getInstance(Context context) {
        if (sUdpManager == null) {
            sUdpManager = new UdpManager();
            sContext = context;
        }
        return sUdpManager;
    }

    /**
     * 构造函数
     */
    private UdpManager() {
        // 创建UDP协议的发送命令对象
        try {
            mGpsDatagramSocket = new DatagramSocket();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注册接收信息的回调函数
     *
     * @param callback 接收信息回调函数
     */
    public void registerCallback(UdpReceiveCallback callback) {
        mReceiveInformationCallback = callback;
    }

    /**
     * 对外提供的发送命令方法
     *
     * @param info 需要发送的命令
     */
    public void sendMessage(String info) {
        Gson gson = new Gson();
        UdpCommand messageInfo = new UdpCommand();

        // 传进来的控制命令
        messageInfo.setCmd(info);

        //控制类型
        messageInfo.setMode();

        //转换成json
        String resultJson = gson.toJson(messageInfo);

        // 创建发送命令SendMessageRunnable对象
        mSendMessageRunnable = new SendMessageRunnable();
        mSendMessageRunnable.setInfoArray(resultJson.getBytes(StandardCharsets.UTF_8));

        // 启动发送命令线程
        mEventHandler.postTask(mSendMessageRunnable);

        // 启动发送Gps请求线程和接收信息线程
        if ("stop".equals(info)) {
            HiLog.info(label, "info = " + info);
        } else {
            // 启动发送Gps请求线程和接收信息线程
            startReceive();
            startSendGpsMessage();
        }
        HiLog.info(label, "sendMessage = " + resultJson);
    }

    public String getIp() {
        return ip;
    }


    public void setIp(String mIp) {
        this.ip = mIp;
    }

    /**
     * 内部类,用作发送命令
     */
    private class SendMessageRunnable implements Runnable {
        private byte[] mInfoArray;

        void setInfoArray(byte[] infoArray) {
            mInfoArray = infoArray;
        }

        @Override
        public void run() {
            HiLog.info(label, "发送线程 = " + Thread.currentThread().getName());

            // 发送数据
            try {
                // 延时发送50毫秒,因为如果不延时会将小车卡死
                Thread.sleep(50);

                // 将要发送的数据封装成DatagramPacket对象
                DatagramPacket sRequest = new DatagramPacket(mInfoArray, mInfoArray.length,
                        InetAddress.getByName(getIp()), PORT);

                // 开始发送
                mGpsDatagramSocket.send(sRequest);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
                HiLog.info(label, "sendMessage error");
            }
        }
    }

    /**
     * 内部类,用作接收命令
     */
    private class ReceiveMessageRunnable implements Runnable {

        @Override
        public void run() {
            try {
                while (flag) {
                    byte[] buf = new byte[1024];
                    DatagramPacket receiveDatagramPacket = new DatagramPacket(buf, buf.length);
                    if (mGpsDatagramSocket != null && !mGpsDatagramSocket.isClosed()) {
                        HiLog.info(label, "接收线程开始阻塞" + Thread.currentThread().getName());

                        // 接收返回数据,会阻塞线程
                        mGpsDatagramSocket.receive(receiveDatagramPacket);

                        // 将得到的数据转成json
                        String json = new String(receiveDatagramPacket.getData(), StandardCharsets.UTF_8);
                        json = json.substring(json.indexOf("{"), json.lastIndexOf("}")+1);
                        HiLog.info(label, "receiveMessage json = " + json);

                        // 将对象发送给需要接收返回值的地方
                        mEventHandler.sendEvent(InnerEvent.get(GET_MESSAGE, json));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                HiLog.error(label, "receiveMessage error");
            }
        }
    }

    /**
     * 内部类,用作发送请求Gps命令
     */
    private class SendGpsMessageRunnable implements Runnable {

        @Override
        public void run() {
            Gson gson = new Gson();
            UdpCommand messageInfo = new UdpCommand();

            // 传进来的控制命令
            messageInfo.setCmd("getinfo");

            //控制类型
            messageInfo.setMode();

            //转换成json
            String resultJson = gson.toJson(messageInfo);

            byte[] infoArray = resultJson.getBytes(StandardCharsets.UTF_8);

            try {
                // 将要发送的数据封装成DatagramPacket对象
                DatagramPacket sRequest = new DatagramPacket(infoArray, infoArray.length,
                        InetAddress.getByName(getIp()), PORT);

                // 开始发送
                mGpsDatagramSocket.send(sRequest);

                // 启动获取Gps命令线程
                mSendGpsEventHandler.postTask(mSendGpsMessageRunnable, 2000);
                HiLog.info(label, "发送gps");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 启动接收消息
     */
    private void startReceive() {
        if (!flag) {
            flag = true;

            // 创建接收命令ReceiveMessageRunnable对象
            mReceiveMessageRunnable = new ReceiveMessageRunnable();

            // 启动接收命令线程
            mReceiveEventHandler.postTask(mReceiveMessageRunnable);
            HiLog.info(label, "开启接收线程");
        }
    }

    /**
     * 开始获取gps点
     */
    private void startSendGpsMessage() {
        // 创建发送Gps命令SendGpsMessageRunnable对象
        if (mSendGpsMessageRunnable == null) {
            mSendGpsMessageRunnable = new SendGpsMessageRunnable();
        }

        // 启动获取Gps命令线程
        mSendGpsEventHandler.postTask(mSendGpsMessageRunnable);
        HiLog.info(label, "开启发送gps请求线程");
    }

3. UdpCommand

class UdpCommand {
    // 控制命令:forward,back,left,right
    private String cmd;
    // 控制类型
    private String mode;

    public String getCmd() {
        return cmd;
    }

    void setCmd(String cmd) {
        this.cmd = cmd;
    }

    public String getMode() {
        return mode;
    }

    void setMode() {
        this.mode = "CarControl";
    }
}

4. UdpReceiveCallback

/**
 * 接收小车返回数据的回调函数
 */
public interface UdpReceiveCallback {
    void getMessage(Object value);
}

5. xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <DirectionalLayout
        ohos:height="70vp"
        ohos:width="match_parent"
        ohos:orientation="horizontal"
        ohos:layout_alignment="center"
        ohos:top_margin="10vp" >
        <Button
            ohos:id="$+id:i_up"
            ohos:height="50vp"
            ohos:width="120vp"
            ohos:background_element="#FF9F9F9F"
            ohos:left_margin="60vp"
            ohos:text_size="25fp"
            ohos:text="前进"/>

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
已于2021-8-31 11:37:14修改
26
收藏 27
回复
举报
7条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感谢分享,回去试的跑一下。

回复
2021-8-31 11:56:37
软通田可辉
软通田可辉

软通同事文章写的越来越好了

1
回复
2021-8-31 14:05:52
软通小精灵
软通小精灵

感谢软通同事分享

回复
2021-8-31 14:29:52
软通动力HOS
软通动力HOS 回复了 红叶亦知秋
感谢分享,回去试的跑一下。

感谢大家的支持

回复
2021-9-1 09:20:26
芒果爱学习
芒果爱学习

加油加油

回复
2021-9-2 09:22:53
小梁学鸿蒙
小梁学鸿蒙

感谢老师分享

回复
2021-9-2 09:29:48
鸿联
鸿联

前几天收到一个3861小车,试着用手机跑一下

回复
2021-9-25 08:56:22
回复
    相关推荐