联调物联网平台enjoy-iot系列之内置TCP协议组件

前言

1、【enjoy-iot: https://gitee.com/open-enjoy/enjoy-iot 】为乐飞智联旗下开源物联网平台,本系列文章旨在记录联调测试的过程。

2、前后端的部署均不在文章谈论范围内。

3、官方参考文档:https://y5yrmmjjns.feishu.cn/wiki/NX0bwqEEZiaU3OkmCZrcJyx7nIc?fromScene=spaceOverview

开启tcp协议组件

1开启tcp协议组件

新增tcp系列产品

2新增tcp系列产品一

3新增tcp系列产品二

新增tcp设备

4新增tcp设备一

5新增tcp设备二

属性上报

1、新增TestTcpClient类

public class TestTcpClient {

    private static byte[] encode(String addr, short code, short mid, byte[] payload) {
        byte[] addrBytes = addr.getBytes(StandardCharsets.UTF_8);
        byte[] body = payload == null ? new byte[0] : payload;
        int length = 2 + addrBytes.length + 2 + 2 + body.length;
        ByteBuffer buf = ByteBuffer.allocate(4 + length).order(ByteOrder.BIG_ENDIAN);
        buf.putInt(length);
        buf.putShort((short) addrBytes.length);
        buf.put(addrBytes);
        buf.putShort(code);
        buf.putShort(mid);
        buf.put(body);
        return buf.array();
    }

    private static byte[] readPacket(InputStream in) throws Exception {
        byte[] header = readFully(in, 4);
        int len = ByteBuffer.wrap(header).order(ByteOrder.BIG_ENDIAN).getInt();
        return readFully(in, len);
    }

    private static short readCode(byte[] body) {
        int idx = 0;
        int addrLen = ByteBuffer.wrap(Arrays.copyOfRange(body, idx, idx + 2)).order(ByteOrder.BIG_ENDIAN).getShort();
        idx += 2 + addrLen;
        return ByteBuffer.wrap(Arrays.copyOfRange(body, idx, idx + 2)).order(ByteOrder.BIG_ENDIAN).getShort();
    }

    private static byte[] readPayload(byte[] body) {
        int idx = 0;
        int addrLen = ByteBuffer.wrap(Arrays.copyOfRange(body, idx, idx + 2)).order(ByteOrder.BIG_ENDIAN).getShort();
        idx += 2 + addrLen + 2 + 2;
        return Arrays.copyOfRange(body, idx, body.length);
    }

    private static int readStatus(byte[] body) {
        byte[] payload = readPayload(body);
        if (payload.length < 4) return -1;
        return ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN).getInt();
    }

    private static byte[] readFully(InputStream in, int len) throws Exception {
        byte[] data = new byte[len];
        int off = 0;
        while (off < len) {
            int r = in.read(data, off, len - off);
            if (r < 0) throw new IllegalStateException("socket closed");
            off += r;
        }
        return data;
    }

    public static void main(String[] args) throws Exception {
        String host = args.length > 0 ? args[0] : "127.0.0.1";
        int port = args.length > 1 ? Integer.parseInt(args[1]) : 6666;
        String addr = args.length > 2 ? args[2] : "tcpdv090";
        String productKey = args.length > 3 ? args[3] : "ZGxatJ7jhJGCM8J5";
        int intervalSec = args.length > 4 ? Integer.parseInt(args[4]) : 5;
        String mode = args.length > 5 ? args[5] : "both";

        try (Socket socket = new Socket(host, port)) {
            socket.setSoTimeout(2000);
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();

            // 模拟注册
            byte[] registerPayload = productKey.getBytes(StandardCharsets.UTF_8);
            byte[] registerPacket = encode(addr, DataPackage.CODE_REGISTER, (short) 1, registerPayload);
            // 二进制转十六进制
            String hexString1 = HexUtil.toHexString(registerPacket);
            out.write(registerPacket);
            out.flush();
            try {
                byte[] regAck = readPacket(in);
                int regStatus = readStatus(regAck);
                System.out.println("register ack status=" + regStatus);
            } catch (Exception e) {
                System.err.println("注册回复读取失败(服务端可能未回包或连接已断开): " + e.getMessage());
            }

            // 模拟心跳上报
            byte[] heartbeatPacket = encode(addr, DataPackage.CODE_HEARTBEAT, (short) 2, new byte[0]);
            out.write(heartbeatPacket);
            out.flush();

            int mid = 3;
            boolean toggle = true;
            while (true) {
                // 模拟属性上报
                if ("prop".equalsIgnoreCase(mode) || ("both".equalsIgnoreCase(mode) && toggle)) {
                    String propJson = "{\"model\":" + (20 + Math.round(Math.random() * 100) / 10.0) + ",\"remain\":" + (50 + (int) (Math.random() * 20)) + "}";
                    byte[] propPacket = encode(addr, DataPackage.CODE_DATA_UP, (short) mid, propJson.getBytes(StandardCharsets.UTF_8));
                    // 二进制转十六进制
                    String hexString2 = HexUtil.toHexString(propPacket);
                    out.write(propPacket);
                    out.flush();
                } else {
                    // 模拟事件上报
                    String evtJson = "{\"event\":\"alarm\",\"level\":" + (1 + (int) (Math.random() * 3)) + "}";
                    byte[] evtPacket = encode(addr, DataPackage.CODE_EVENT_UP, (short) mid, evtJson.getBytes(StandardCharsets.UTF_8));
                    out.write(evtPacket);
                    out.flush();
                }
                mid = (mid + 1) & 0xFFFF;

                try {
                    socket.setSoTimeout(500);
                    byte[] down = readPacket(in);
                    short downCode = readCode(down);
                    // 指令下发
                    if (downCode == DataPackage.CODE_DATA_DOWN) {
                        String downPayload = new String(readPayload(down), StandardCharsets.UTF_8);
                        System.out.println("property down received payload=" + downPayload);
                    }
                } catch (Exception ignore) {
                } finally {
                    socket.setSoTimeout(2000);
                }
                toggle = !toggle;
                Thread.sleep(intervalSec * 1000L);
            }
        }
    }
}

2、Netassistant模拟tcp设备

6属性上报一

7属性上报二

8属性上报三

9属性上报四

3、属性上报成功

10属性上报五

11属性上报六

posted @ 2026-08-17 15:12  南翔技校毕业后  阅读(3)  评论(0)    收藏  举报