编写上位机与下位机通信的时候,涉及到协议的转换,比较多会使用c#教程到二进制。传统的方法,是将数据整体获取到byte数组中,然后逐字节对数据进行解析。这样操作工作量比较大,对于较长数据python基础教程段更容易计算位置出错。

其实,对于下位机给出通讯的数据结构的情况下,可以直接使用C#的struct将数据直接转换。需要使用到Marshal。

数据结构
假定下位机(C语言编写)给到我们的数据结构是这个,传输方式为小端方式

typedef struct {unsigned long int time;   // 4个字节float tmpr[3];     // 4*3 个字节float forces[6];     // 4*6个字节float distance[6];    // 4*6个字节
} dataItem_t;

方法1

首先需要定义一个struct:

[StructLayout(LayoutKind.Sequential, Size = 64, Pack = 1)]
public struct HardwareData
{//[FieldOffset(0)]public UInt32 Time;   // 4个字节[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]//[FieldOffset(4)]public float[] Tmpr;     // 3* 4个字节//[FieldOffset(16)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]public float[] Forces;     // 6* 4个字节//[FieldOffset(40)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]public float[] Distance;    // 6*4个字节
}

然后使用以下代码进行转换

// code from https://stackoverflow.com/questions/628843/byte-for-byte-serialization-of-a-struct-in-c-sharp/629120#629120
/// <summary>
/// converts byte[] to struct
/// </summary>
public static T RawDeserialize<T>(byte[] rawData, int position)
{int rawsize = Marshal.SizeOf(typeof(T));if (rawsize > rawData.Length - position)throw new ArgumentException("Not enough data to fill struct. Array length from position: " + (rawData.Length - position) + ", Struct length: " + rawsize);IntPtr buffer = Marshal.AllocHGlobal(rawsize);Marshal.Copy(rawData, position, buffer, rawsize);T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));Marshal.FreeHGlobal(buffer);return retobj;
}/// <summary>
/// converts a struct to byte[]
/// </summary>
public static byte[] RawSerialize(object anything)
{int rawSize = Marshal.SizeOf(anything);IntPtr buffer = Marshal.AllocHGlobal(rawSize);Marshal.StructureToPtr(anything, buffer, false);byte[] rawDatas = new byte[rawSize];Marshal.Copy(buffer, rawDatas, 0, rawSize);Marshal.FreeHGlobal(buffer);return rawDatas;
}

注意这里我使用的方式为LayoutKind.Sequential,如果直接使用LayoutKind.Explicit并设置FieldOffset会弹出一个诡异的错误System.TypeLoadException:“Could not load type ‘ConsoleApp3.DataItem’ from assembly ‘ConsoleApp3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.”。

方法2

提示是对齐的错误,这个和编译的时候使用的32bit和64位是相关的,详细数据封送对齐的操作我不就详细说了,贴下代码。

//强制指定x86编译
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 1)]
public struct DataItem
{[MarshalAs(UnmanagedType.U4)][FieldOffset(0)]public UInt32 time;   // 4个字节[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.R4)][FieldOffset(4)]public float[] tmpr;     // 3* 4个字节[FieldOffset(16)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.R4)]public float[] forces;     // 6* 4个字节[FieldOffset(40)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.R4)]public float[] distance;    // 6*4个字节
}

强制指定x64编译没有成功,因为数据对齐后和从下位机上来的数据长度是不符的。

方法3

微软不是很推荐使用LayoutKind.Explicit,如果非要用并且不想指定平台的话,可以使用指针来操作,当然,这个需要unsafe。

var item = RawDeserialize<DataItem>(tail.ToArray(), 0);
unsafe
{float* p = &item.forces;for (int i = 0; i < 6; i++){Console.WriteLine(*p);p++;}
}[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 1)]
public struct DataItem
{[FieldOffset(0)]public UInt32 time;   // 4个字节[FieldOffset(4)]public float tmpr;     // 3* 4个字节[FieldOffset(16)]public float forces;     // 6* 4个字节[FieldOffset(40)]public float distance;    // 6*4个字节
}

方法4

感觉写起来还是很麻烦,既然用上了unsafe,就干脆直接一点。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct DataItem
{public UInt32 time;   // 4个字节public fixed float tmpr[3];     // 3* 4个字节public fixed float forces[6];     // 6* 4个字节public fixed float distance[6];    // 6*4个字节
}

这样,获得数组可以直接正常访问,不再需要unsafe了。

总结
数据解析作为上下位机通讯的常用操作,使用struct直接转换数据可以大大简化工作量。建议还是使用LayoutKind.Sequential来进行封送数据,有关于数据在托管与非托管中的转换,可以详细看看微软有关互操作的内容。

以上代码在.NET 5.0下编译通过并能正常执行。

补充
注意上面的前提要求是字节序为小端字节序(一般计算机都是小端字节序),对于大端字节序发送过来的数据,需要进行字节序转换。我找到一处代码写的很好:

//CODE FROM https://stackoverflow.com/a/15020402
public static class FooTest
{[StructLayout(LayoutKind.Sequential, Pack = 1)]public struct Foo2{public byte b1;public short s;public ushort S;public int i;public uint I;public long l;public ulong L;public float f;public double d;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]public string MyString;}[StructLayout(LayoutKind.Sequential, Pack = 1)]public struct Foo{public byte b1;public short s;public ushort S;public int i;public uint I;public long l;public ulong L;public float f;public double d;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]public string MyString;public Foo2 foo2;}public static void test(){Foo2 sample2 = new Foo2(){b1 = 0x01,s = 0x0203,S = 0x0405,i = 0x06070809,I = 0x0a0b0c0d,l = 0xe0f101112131415,L = 0x161718191a1b1c,f = 1.234f,d = 4.56789,MyString = @"123456789", // null terminated => only 9 characters!};Foo sample = new Foo(){b1 = 0x01,s = 0x0203,S = 0x0405,i = 0x06070809,I = 0x0a0b0c0d,l = 0xe0f101112131415,L = 0x161718191a1b1c,f = 1.234f,d = 4.56789,MyString = @"123456789", // null terminated => only 9 characters!foo2 = sample2,};var bytes_LE = Dummy.StructToBytes(sample, Endianness.LittleEndian);var restoredLEAsLE = Dummy.BytesToStruct<Foo>(bytes_LE, Endianness.LittleEndian);var restoredLEAsBE = Dummy.BytesToStruct<Foo>(bytes_LE, Endianness.BigEndian);var bytes_BE = Dummy.StructToBytes(sample, Endianness.BigEndian);var restoredBEAsLE = Dummy.BytesToStruct<Foo>(bytes_BE, Endianness.LittleEndian);var restoredBEAsBE = Dummy.BytesToStruct<Foo>(bytes_BE, Endianness.BigEndian);Debug.Assert(sample.Equals(restoredLEAsLE));Debug.Assert(sample.Equals(restoredBEAsBE));Debug.Assert(restoredBEAsLE.Equals(restoredLEAsBE));}public enum Endianness{BigEndian,LittleEndian}private static void MaybeAdjustEndianness(Type type, byte[] data, Endianness endianness, int startOffset = 0){if ((BitConverter.IsLittleEndian) == (endianness == Endianness.LittleEndian)){// nothing to change => returnreturn;}foreach (var field in type.GetFields()){var fieldType = field.FieldType;if (field.IsStatic)// don't process static fieldscontinue;if (fieldType == typeof(string)) // don't swap bytes for stringscontinue;var offset = Marshal.OffsetOf(type, field.Name).ToInt32();// handle enumsif (fieldType.IsEnum)fieldType = Enum.GetUnderlyingType(fieldType);// check for sub-fields to recurse if necessaryvar subFields = fieldType.GetFields().Where(subField => subField.IsStatic == false).ToArray();var effectiveOffset = startOffset + offset;if (subFields.Length == 0){Array.Reverse(data, effectiveOffset, Marshal.SizeOf(fieldType));}else{// recurseMaybeAdjustEndianness(fieldType, data, endianness, effectiveOffset);}}}internal static T BytesToStruct<T>(byte[] rawData, Endianness endianness) where T : struct{T result = default(T);MaybeAdjustEndianness(typeof(T), rawData, endianness);GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);try{IntPtr rawDataPtr = handle.AddrOfPinnedObject();result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));}finally{handle.Free();}return result;}internal static byte[] StructToBytes<T>(T data, Endianness endianness) where T : struct{byte[] rawData = new byte[Marshal.SizeOf(data)];GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);try{IntPtr rawDataPtr = handle.AddrOfPinnedObject();Marshal.StructureToPtr(data, rawDataPtr, false);}finally{handle.Free();}MaybeAdjustEndianness(typeof(T), rawData, endianness);return rawData;}}

参考资料

https://www.developerfusion.com/article/84519/mastering-structs-in-c/

https://stackoverflow.com/a/15020402

https://stackoverflow.com/questions/628843/byte-for-byte-serialization-of-a-struct-in-c-sharp/629120

https://stackoverflow.com/questions/2871/reading-a-c-c-data-structure-in-c-sharp-from-a-byte-array/41836532

C#使用struct直接转换下位机数据的示例代码相关推荐

  1. 双y轴设置 颜色_项目实战:Qt多段Y轴折线图框架(双Y轴段折线、支持拽拖、浮动游标显示X值各段Y值、实时下位机数据)...

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/111660400长期持续带来更多项目与技术分享 ...

  2. python3读取excel数据-Python3读取和写入excel表格数据的示例代码

    python操作excel主要用到 xlrd 和 xlwt 这两个库,xlrd读取excel表格数据, 支持 xlsx和xls格式的excel表格 :xlwt写入excel表格数据: 一.python ...

  3. python 定时自动爬取_python实现scrapy爬虫每天定时抓取数据的示例代码

    1. 前言. 1.1. 需求背景. 每天抓取的是同一份商品的数据,用来做趋势分析. 要求每天都需要抓一份,也仅限抓取一份数据. 但是整个爬取数据的过程在时间上并不确定,受本地网络,代理速度,抓取数据量 ...

  4. python定时爬取数据_python实现scrapy爬虫每天定时抓取数据的示例代码

    1. 前言. 1.1. 需求背景. 每天抓取的是同一份商品的数据,用来做趋势分析. 要求每天都需要抓一份,也仅限抓取一份数据. 但是整个爬取数据的过程在时间上并不确定,受本地网络,代理速度,抓取数据量 ...

  5. python自动抓取网管软件的数据_python实现scrapy爬虫每天定时抓取数据的示例代码...

    1. 前言. 1.1. 需求背景. 每天抓取的是同一份商品的数据,用来做趋势分析. 要求每天都需要抓一份,也仅限抓取一份数据. 但是整个爬取数据的过程在时间上并不确定,受本地网络,代理速度,抓取数据量 ...

  6. python爬取抖音用户评论_python实现模拟器爬取抖音评论数据的示例代码

    目标: 由于之前和朋友聊到抖音评论的爬虫,demo做出来之后一直没整理,最近时间充裕后,在这里做个笔记. 提示:大体思路 通过fiddle + app模拟器进行抖音抓包,使用python进行数据整理 ...

  7. Elasticsearch高级搜索排序( 中文+拼音+首字母+简繁转换+特殊符号过滤)(示例代码)

    简介  这篇文章主要介绍了Elasticsearch高级搜索排序( 中文+拼音+首字母+简繁转换+特殊符号过滤)(示例代码)以及相关的经验技巧,文章约21106字,浏览量320,点赞数5,值得参考! ...

  8. 上位机发送与接收下位机数据

    用自带的SeriPort控件或者可以用seriport类,根据上位机与下位机同行寻得协议发送接收数据. 设置buttion控件,点击buttion 发送相应数据: private void send_ ...

  9. 【数据分析学习笔记day09】数据分析实战案例:2016美国大选民意调查统计+2016年美国总统大选民意调查数据统计+示例代码1 +示例代码2:

    文章目录 2016年美国大选民意调查数据统计: 示例代码1 : 示例代码2: 2016年美国大选民意调查数据统计: 项目地址:https://www.kaggle.com/fivethirtyeigh ...

  10. %02 java_Java基础02—变量、运算符和数据类型转换(示例代码)

    Java语言基础 参考资料:<Java从入门到精通>/明日科技编著. 4版. 北京:清华大学出版社,2016 一.变量与常量 常量:在程序执行过程中,其值不能改变的量称为常量. 变量:在程 ...

最新文章

  1. MySQL8索引篇:性能提升了100%!!
  2. nyoj158-省赛来了
  3. 【LeetCode从零单排】No70.ClimbingStairs
  4. matlab中怎么求矩阵的特征值和特征向量
  5. 在程序员的道路上,义无反顾的努力,有思想的人,很多,好的想法,需要学习。(以此共勉)...
  6. 《C++标准程序库》学习笔记5 — 第七章
  7. 入侵检测系统的性能的辨别(3)
  8. 学习笔记 vs19 报错:E1696 C++ 无法打开 源 文件
  9. C#中具有进程间通信的Singleton应用程序
  10. java 确定对象的引用_JVM学习笔记之了解对象存活判断和4种引用【三】
  11. [翻译].NET framework 4.0并行编程:入门
  12. 【转】卡巴斯基2011导入key专用工具+卡巴斯基提取key工具(激活码转换key)
  13. 软考中级,软件设计师考试那些内容,考试大纲什么的?
  14. 简化版WIN7安装打印机时提示“打印机无法安装,打印处理器不存在。”正确处理方法
  15. win7 安装 python3.8.10
  16. GBase 8c基础操作
  17. E6-2019级航类第6次正式上机
  18. 金三银四过后整理出的阿里最新Java程序员面试题目(2018.4月)
  19. 阿西莫夫机器人三原则
  20. 告别2022,重新出发

热门文章

  1. 电脑右下角音量键打不开,调不了音量大小的解决方法
  2. python 对 PDF 的拆分 和合并
  3. 程序员有必要掌握 TDD 吗?
  4. CSS网页布局中文排版的9则技巧
  5. 互联网内卷化下,“大或死”之外的另一路径是什么?
  6. 计算机上求平均分的公式,求平均值的公式
  7. 刷脸支付重磅来袭是业界的口碑代表
  8. RAID磁盘阵列与磁盘阵列卡
  9. 用python制作动态二维码
  10. excel组合汇总_Excel汇总20151102