用Java实现PCT打包解包功能

一、设计概要

1、设计要求

学习PCT通信协议,设计一个打包解包小工具。

2、总体结构

二、设计原理

1、PCT通信协议格式

PCT通信协议规定:

(1)数据包由1字节模块ID+1字节数据头+1字节二级ID+6字节数据+1字节校验和构成,共计10字节。

(2)数据包中有6个数据,每个数据为1字节。

(3)模块ID的最高位bit7固定为0。

(4)模块ID的取值范围为0x00~0x7F,最多有128种类型。

(5)数据头的最高位bit7固定为1,数据头的低7位按照从低位到高位的顺序,依次存放二级ID的最高位bit7、数据1的最高位bit7、数据2的最高位bit7、数据3的最高位bit7、数据4的最高位bit7、数据5的最高位bit7和数据6的最高位bit7。

(6)校验和的低7位为模块ID+数据头+二级ID+数据1+数据2+…+数据6求和的结果(取低7位)。

(7)二级ID、数据1~数据6和校验和的最高位bit7固定为1。注意,并不是说二级ID、数据1~数据6和校验和只有7位,而是在打包后,它们的低7位位置不变,最高位均位于数据头中,因此,依然还是8位。

2、PCT打包过程

PCT通信协议的打包过程分为4步。

第1步,准备原始数据,原始数据由模块ID(0x00~0x7F)、二级ID、数据1~数据6组成,如下图所示。其中,模块ID的取值范围为0x00~0x7F,二级ID和数据的取值范围为0x00~0xFF。

第2步,依次取出二级ID、数据1~数据6的最高位bit7,将其存放于数据头的低7位,按照从低位到高位的顺序依次存放二级ID、数据1~数据6的最高位bit7,如下图所示。

第3步,对模块ID、数据头、二级ID、数据1~数据6的低7位求和,取求和结果的低7位,将其存放于校验和的低7位,如下图所示。

第4步,将数据头、二级ID、数据1~数据6和校验和的最高位置1,如下图所示。

3. PCT解包过程

PCT通信协议的解包过程也分为4步。

第1步,准备解包前的数据包,原始数据包由模块ID、数据头、二级ID、数据1~数据6、校验和组成,如下图所示。其中,模块ID的最高位为0,其余字节的最高位均为1。

第2步,对模块ID、数据头、二级ID、数据1~数据6的低7位求和,如下图所示,取求和结果的低7位与数据包的校验和低7位对比,如果两个值的结果相等,则说明校验正确。

第3步,数据头的最低位bit0与二级ID的低7位拼接之后作为最终的二级ID,数据头的bit1与数据1的低7位拼接之后作为最终的数据1,数据头的bit2与数据2的低7位拼接之后作为最终的数据2,以此类推,如下图所示。

第4步,解包后的结果,由模块ID、二级ID、数据1~数据6组成。其中,模块ID的取值范围为0x00~0x7F,二级ID和数据的取值范围为0x00~0xFF。

三、设计内容

       学习PCT通信协议,设计一个打包解包小工具,在行编辑框中输入模块ID、二级ID及6字节数据后,通过“打包”按钮实现打包操作,并将打包结果显示到打包结果显示区。另外,还可以根据用户输入的10字节待解包数据,通过“解包”按钮实现解包操作,并将解包结果显示到解包结果显示区。

四、使用说明与执行效果

代码结构说明:

PackUnpackTool类:

该java类主要是用于定义图形化界面,该界面包括模块ID、二级ID,裸数据,将要解包的数据的输入框及打包数据的输出框,还有打包和解包按钮。

moduleIDFieldsubIDFielddataField:用于输入模块ID、二级ID和数据的文本框。

packResultAreaunpackDataFieldunpackResultArea:用于显示打包结果、解包的数据和解包结果的文本区域。

mPackUnpackPackUnpack 类的实例,用于打包和解包数据。

PackButtonListener:监听打包按钮的点击事件,获取输入的模块ID、二级ID和数据,调用 mPackUnpack.packData() 进行打包,并将结果显示在相应的文本区域中。

UnpackButtonListener:监听解包按钮的点击事件,获取输入的解包数据,调用 mPackUnpack.unpackData() 进行解包,并将结果显示在相应的文本区域中。

PackUnpack类:

主要用于处理pct打包和解包的数据处理;以下是代码结构的总结:

1.成员变量:

  PackLen:数据包长度。

  GotModID:标志是否获得正确的模块ID。

  RestByte:剩余字节数。

  mPackBuf:用于存储数据包的数组,包括模块ID、数据头、二级ID、数据和校验和。

2.构造函数:

   初始化成员变量,将数据包数组中的元素初始化为0,设置PackLen、GotModID和`RestByte`为默认值。

3.打包数据:

 packData方法负责打包数据,检查模块ID是否在指定范围内以及数据包长度是否正确,然后调用packWithCheckSum方法进行打包。

4.打包带校验和:

  packWithCheckSum方法计算校验和并准备数据进行传输。

5.解包数据:

  unpackData方法负责解包接收到的数据,首先检查模块ID,然后重建数据包。如果接收到完整的数据包,将调用unpackWithCheckSum方法。

6.解包带校验和:

   unpackWithCheckSum方法验证校验和并重建数据包。

效果展示:

Java的swing图形初始化界面:

当模块ID或数据出错时

数据打包解包成功时:

附件(代码)

PackUnpack(实现方法类):

public class PackUnpack {
    /**
     * PackLen 数据包长度
     * GotModID 获得正确的模块ID即为true,否则为false
     * RestByte 剩余字节数
     */
    private static int PackLen;
    private static boolean GotModID;
    private static int RestByte;


    private int[] mPackBuf = new int[10];

    /**
     * @method 类的构造函数,初始化该模块
     */
    public PackUnpack() {
        //模块ID、数据头、二级ID、数据及校验和均清零
        for (int i = 0; i < 10; i++) {
            mPackBuf[i] = 0;
        }
        //数据包的长度默认为0,获取到数据包ID标志默认为false,剩余的字节数默认为0
        PackLen = 0;
        GotModID = false;
        RestByte = 0;
    }

    /**
     * 解包方法返回
     * @return
     */
    public int[] getUnpackResult() {
        return (mPackBuf);
    }

    /**
     * 打包方法封装
     * @param packet
     */
    public void packData(int[] packet) {
        //模块ID必须在0x00-0x7F之间, packDin[0]为模块ID
        if (packet[0] < 0x80) {
            if (packet.length == 10) {
                packWithCheckSum(packet);
            }
        }
    }
    private void packWithCheckSum(int[] packet) {
        int dataHead; //数据头,位于模块ID之后
        int checkSum; //校验和,数据包的最后一个字节

        checkSum = packet[0]; //取出模块ID,赋值给校验和
        dataHead = 0; //数据头清零

        for (int i = 8; i > 1; i--) {
            dataHead <<= 1; //数据头左移
            packet[i] = ((packet[i - 1]) | 0x80); //最高位置为1
            checkSum += packet[i]; //数据加到校验和
            dataHead |= (((packet[i - 1]) & 0x80) >> 7); //取出原始数据的最高位,与dataHead相或
        }
        packet[1] = (dataHead | (0x80)); //数据头的最高位也要置为1
        checkSum += packet[1]; //将数据头加到校验和
        packet[9] = ((checkSum | 0x80) & 0x0ff); //校验和的最高位也要置为1
    }
  //解包数据方法响应
    public boolean unpackData(int data) {
        boolean findPack = false;

        //已经接收到模块ID
        if (GotModID) {
            //非模块ID(数据头、二级ID、数据、校验和)必须大于或等于0X80
            if (data >= 0x80) {
                //存储包括除模块ID之外的9个字节,因为第一个字节是模块ID
                mPackBuf[PackLen] = data;
                PackLen++; //包长递增
                RestByte--; //剩余字节数递减

                //已经接收到完整的数据包
                if (RestByte <= 0 && PackLen == 10) {
                    //接收到完整数据包后尝试解包
                    findPack = unpackWithCheckSum(mPackBuf);
                    //清除获取到模块ID标志,即重新判断下一个数据包
                    GotModID = false;
                }
            } else {
                GotModID = false;
            }
        } else if (data < 0x80) {
            //如果当前的数据小于0x80,将其视为模块ID
            RestByte = 9; //包剩余字节为9
            PackLen = 1; //当前包长为1
            mPackBuf[0] = data;//数据包的模块ID
            GotModID = true; //表示已经接收到模块ID
        }
        return findPack; //如果获取到完整的数据包,并解包成功,findPack为true,否则为false
    }
    //解包方法
    private boolean unpackWithCheckSum(int[] packet) {
        int dataHead; //数据头,位于模块ID之后
        int checkSum; //校验和,数据包的最后一个字节

        checkSum = packet[0]; //取出模块ID,加到校验和
        dataHead = packet[1]; //取出数据头,赋给dataHead
        checkSum += dataHead; //将数据头加到校验和

        for (int i = 1; i < 8; i++) {
            checkSum += packet[i + 1]; //将数据依次加到校验和
            packet[i] = ((packet[i + 1] & 0x7F) | ((dataHead & 0x01) << 7)); //还原二级ID和数据
            dataHead >>= 1; //数据头右移一位
        }

        return (checkSum & 0x7F) == ((packet[9]) & 0x7F);
    }
}

PackUnpackTool(图形界面)

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

public class PackUnpackTool extends JFrame {
    private JTextField moduleIDField, subIDField, dataField, unpackDataField;
    private JTextArea packResultArea, unpackResultArea;
    private PackUnpack mPackUnpack;


    public  PackUnpackTool() {
        mPackUnpack = new PackUnpack();
        setTitle("PCT打包解包工具");
        setSize(600, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new GridLayout(2, 2));


        // 创建打包区域
        JPanel packPanel = new JPanel(new GridLayout(4, 2));
        Font font = new Font("宋体", Font.PLAIN, 20);
        JLabel moduleIDLabel = new JLabel("模块ID:");
        moduleIDLabel.setFont(font);
        moduleIDField = new JTextField();
        moduleIDField.setFont(font);
        JLabel subIDLabel = new JLabel("二级ID:");
        subIDLabel.setFont(font);
        subIDField = new JTextField();
        subIDField.setFont(font);
        JLabel dataLabel = new JLabel("原始数据 (6 bytes):");
        dataLabel.setFont(font);
        dataField = new JTextField();
        dataField.setFont(font);
        JButton packButton = new JButton("打包数据");
        packButton.setFont(font);
        packResultArea = new JTextArea();
        packResultArea.setFont(font);
        //不可输出状态
        packResultArea.setEditable(false);

        packPanel.add(moduleIDLabel);
        packPanel.add(moduleIDField);
        packPanel.add(subIDLabel);
        packPanel.add(subIDField);
        packPanel.add(dataLabel);
        packPanel.add(dataField);
        packPanel.add(packButton);
        packPanel.add(packResultArea);

        // 创建解包区域
        JPanel unpackPanel = new JPanel(new GridLayout(2, 2));
        JLabel unpackDataLabel = new JLabel("解包的数据 (10 bytes):");
        unpackDataLabel.setFont(font);
        unpackDataField = new JTextField();
        unpackDataField.setFont(font);
        JButton unpackButton = new JButton("解包");
        unpackButton.setFont(font);
        unpackResultArea = new JTextArea();
        unpackResultArea.setFont(font);
        unpackResultArea.setEditable(false);

        unpackPanel.add(unpackDataLabel);
        unpackPanel.add(unpackDataField);
        unpackPanel.add(unpackButton);
        unpackPanel.add(unpackResultArea);
        // 添加事件监听
        packButton.addActionListener(new PackButtonListener());
        unpackButton.addActionListener(new UnpackButtonListener());
        // 添加到主窗口
        add(packPanel);
        add(unpackPanel);

        setVisible(true);
    }

    class PackButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            int[] buffer = new int[10];
            String moduleIDText = moduleIDField.getText();
            String subIDText = subIDField.getText();
            String dataText = dataField.getText();

            try {
                if (!moduleIDText.isEmpty()) {
                    int moduleID = Integer.parseInt(moduleIDText, 16);
                    if (moduleID > 0x7F) {
                        packResultArea.setText("输入模块ID超出范围");
                        return; // Stop further processing
                    }
                    buffer[0] = moduleID;
                }
                if (!subIDText.isEmpty()) {

                    buffer[1] = Integer.parseInt(subIDText, 16);
                }
                if (!dataText.isEmpty()) {
                    String[] dataBytes = dataText.split(" ");
                    for (int i = 0; i < dataBytes.length; i++) {
                        buffer[i + 2] = Integer.parseInt(dataBytes[i], 16);
                    }
                }
                mPackUnpack.packData(buffer);

                StringBuilder packedResultBuilder = new StringBuilder();
                for (int value : buffer) {
                    packedResultBuilder.append(Integer.toHexString(value)).append(" ");
                }
                packResultArea.setText(packedResultBuilder.toString().toUpperCase());
            } catch (NumberFormatException ex) {
                packResultArea.setText("打包数据格式错误");
            }
        }
    }

    class UnpackButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            int[] buffer = new int[10];
            String unpackDout;
            unpackResultArea.setText("");
            String[] unpackDin = unpackDataField.getText().toString().split(" ");

            if (unpackDin.length != 10) {
                unpackResultArea.setText("解包数据的格式错误");
                return;
            }

            for (int i = 0; i < unpackDin.length; i++) {
                buffer[i] = Integer.parseInt(unpackDin[i], 16);
                if(mPackUnpack.unpackData(buffer[i])) {
                    buffer =  mPackUnpack.getUnpackResult();

                    for(int j = 0; j < 8; j++) {
                        unpackDout = Integer.toHexString(buffer[j]);
                        if ((buffer[j] < 0x10)) {
                            unpackDout = "0" + unpackDout;
                        }
                        unpackResultArea.append(unpackDout.toUpperCase());
                        unpackResultArea.append(" ");
                    }
                }
            }
        }
    }


    public static void main(String[] args) {
        new PackUnpackTool();
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪子回头199

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值