采用Union实现上位机交互中的数据解析
在计算机科学中,联合体(英语:union)又名共用体,是一种具有多个类型或格式的值,或者把它定义为一种由具有这样的值的变量形成的数据结构。一些编程语言(如C语言)可以支持被称为“联合体”的特殊类型,来表示上述的变量。与枚举和结构体不同的是,一个联合体的长度等于其内部长度最大的成员的长度,并且它们都共享着同一段内存。
结构体与联合体变量的内存分配方式如下图所示:
上位机软件开发采用的C#语言不支持联合体,但可以通过手动控制结构体每个元素的位置来实现类似的功能。这需要结合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它们的时候必须引用System.Runtime.InteropServices。具体实现方式如下:
using DuiHelpers; using System; using System.Runtime.InteropServices; namespace ConsoleApp { /// <summary> /// C#上位机软件中采用结构体方式进行数据解析实现 /// Added by wzl 2021/01/09 /// 方法优势: /// 1、相比以前的字节拆分的方式,省去数据解析时的大量字节对比与赋值操作; /// 2、解析结果可靠,降低了编程的出错概率; /// </summary> internal unsafe class Program { static void Main(string[] args) { //模拟下位机返回的数据 byte[] dataBuffer = new byte[100]; for (int i = 0; i < 100; i++) dataBuffer[i] = (byte)(i + 100); //将接收到的数据(字节数组)转换成结构体 net_type obj = (net_type)DuiHelperByte.BytesToStuct(dataBuffer, typeof(net_type)); //从结构体对象中直接取值(不用再进行逐个字节对比和赋值) byte b1 = obj.get_param.ack_ip[0]; } /// <summary> /// byte数组转结构体 /// </summary> /// <param name="bytes">byte数组</param> /// <param name="type">结构体类型</param> /// <returns>转换后的结构体</returns> public static object BytesToStuct(byte[] bytes, Type type) { //得到结构体的大小 int size = Marshal.SizeOf(type); //byte数组长度小于结构体的大小 if (size > bytes.Length) { //返回空 return null; } //分配结构体大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(size); //将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, size); //将内存空间转换为目标结构体 object obj = Marshal.PtrToStructure(structPtr, type); //释放内存空间 Marshal.FreeHGlobal(structPtr); //返回结构体 return obj; } } public enum version_type : ushort { VER_BROADCAST = 0x5FAF, // 广播帧标识 VER_NET = 0x01AF, // 网络帧头 } public enum cmd_type : ushort { CMD_USER_APP_COM_START = 0x8000, // 用户应用命令起始 /*********自定义命令*********************************/ CMD_GET_PARAM = 0x8001, // 获取参数 CMD_ACK_GET_PARAM, // 返回获取参数 CMD_SET_PARAM, // 设置参数 CMD_ACK_SET_PARAM, // 返回设置参数成功 /**************************************************/ } [StructLayout(LayoutKind.Explicit)] public struct net_type { [FieldOffset(0)] public version_type ver; // 消息头 [FieldOffset(2)] public cmd_type cmd; //命令 [FieldOffset(4)] public get_param_type get_param; // 广播获取参数 [FieldOffset(4)] public set_prarm_type set_prarm; // 广播设置IP地址 } public enum sys_run_state_type : byte { SYS_BOOTLOADER = 0x01, // Bootloader 运行状态 SYS_APP, // APP 运行状态 } // 获取系统参数, [StructLayout(LayoutKind.Sequential)] public unsafe struct get_param_type // size=6 { public fixed byte ack_ip[4]; // 返回ip地址 public ushort ack_port; // 返回端口号 } // 设置系统参数 [StructLayout(LayoutKind.Sequential)] public unsafe struct set_prarm_type //size=50+2 { public fixed byte ack_ip[4]; // 返回ip地址 public ushort ack_port; // 返回端口号(*注意:struct按uint的长度进行了字节对齐,所以长度增加了2个字节) public fixed uint sn_id[3]; // MCU唯一序号(广播帧时作为设备ID,必须与本机相同才进行设置) public uint ip_cfg_mask; // 结构体对应元素数据掩码: 0,无效数据,不做处理;1,有效数据; } }
网上参考资料:
https://zhuanlan.zhihu.com/p/589867601
https://www.cnblogs.com/chihirosan/archive/2016/01/28/5166057.html
posted on 2024-01-09 13:57 wangzhiliang 阅读(16) 评论(0) 编辑 收藏 举报