C# 使用特性的方式封装报文

在编写上位机软件时,需要经常处理命令拼接与其他设备进行通信,通常对不同的命令封装成不同的方法,扩展稍许麻烦。

本次拟以特性方式实现,以兼顾维护性与扩展性。


思想:

一种命令对应一个类,其类中的各个属性对应各个命令段,通过特性的方式,实现其在这包数据命令中的位置、大端或小端及其转换为对应的目标类型;

然后通过反射对其进行拼包,从而得到一包完整数据。

场景:

将一个轴移动到对应的X,Y,Z位置,为了演示,对其共用一个速度

这个移动到指定位置的命令假设按以下顺序构成(为了展示,草率的命令结构):

|--------|----|---------|-----|-----|-----|-----|-----|-----|----|
| 序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 字节 | 2 | u32 | u16 | u16 | u32 | s32 | s32 | s32 | 2 |
| 说明 | 包头 | 步骤号(ID) | 功能码 | 轴 | 速度 | X位置 | Y位置 | Z位置 | 包尾 |


实现:

创建特性 CmdPropertyAttribute

复制代码
 1 [AttributeUsage(AttributeTargets.Property)]
 2 internal class CmdPropertyAttribute : Attribute
 3 {
 4     public Type? TargetType { get; set; }
 5 
 6     public int Number { get; set; }
 7 
 8     public bool IsReverse { get; set; }
 9 
10     public CmdPropertyAttribute(int number)
11     {
12         Number = number;
13     }
14 
15     public CmdPropertyAttribute(int number, Type targetType)
16     {
17         Number = number;
18         TargetType = targetType;
19     }
20 
21     public CmdPropertyAttribute(int number, bool isReverse)
22     {
23         Number = number;
24         IsReverse = isReverse;
25     }
26 
27     public CmdPropertyAttribute(int number, Type targetType, bool isReverse)
28     {
29         Number = number;
30         IsReverse = isReverse;
31         TargetType = targetType;
32     }
33 }

参数类,每一种命令对应一个参数类,它们继承于参数基类

创建参数基类 ParamBase ,每种数据都是步骤号处于第一位,特把其放入到基类中

复制代码
1     public class ParamBase
2     {
3         [CmdProperty(0, true)]
4         public int StepNum { get; set; }
5     }

创建轴枚举 Axis

复制代码
1     public enum Axis : ushort
2     {
3         Axis_1 = 1,
4 
5         Axis_2 = 2,
6     }

创建功能码枚举 FunctionCode

复制代码
1     public enum FunctionCode
2     {
3         Move = 1
4     }

创建移动类 MoveParam ,为了更好展示高低位转换,特对Speed属性进行反转

复制代码
 1     public class MoveParam : ParamBase
 2     {
 3         [CmdProperty(1, typeof(ushort))]
 4         public FunctionCode Function { get; init; }
 5 
 6         [CmdProperty(2, typeof(ushort))]
 7         public Axis Axis { get; set; }
 8 
 9         [CmdProperty(3, true)]
10         public uint Speed { get; set; }
11 
12         [CmdProperty(4)]
13         public int XPoint { get; set; }
14 
15         [CmdProperty(5)]
16         public int YPoint { get; set; }
17 
18         [CmdProperty(6)]
19         public int ZPoint { get; set; }
20 
21         public MoveParam()
22         {
23             
24         }
25 
26         public MoveParam(int stepNum, Axis axis, uint speed, int xPoint, int yPoint, int zPoint)
27         {
28             Function = FunctionCode.Move;
29             StepNum = stepNum;
30             Axis = axis;
31             Speed = speed;
32             XPoint = xPoint;
33             YPoint = yPoint;
34             ZPoint = zPoint;
35         }
36     }

对参数对象进行反射解析,生成对应的数据命令集合

创建扩展类 ParamBaseExtensions

复制代码
 1     public static class ParamBaseExtensions
 2     {
 3         public static byte[] ToCmd(this ParamBase param)
 4         {
 5             var properties = param.GetType().GetProperties()
 6                 .Where(x => x.IsDefined(typeof(CmdPropertyAttribute), false))
 7                 .OrderBy(x => ((CmdPropertyAttribute)x.GetCustomAttribute(typeof(CmdPropertyAttribute))).Number);
 8 
 9             List<byte> result = new();
10 
11             foreach (var item in properties)
12             {
13                 var cmdAttribute = item.GetCustomAttribute(typeof(CmdPropertyAttribute)) as CmdPropertyAttribute;
14 
15                 var value = item.GetValue(param);
16 
17                 if (cmdAttribute.TargetType is not null)
18                 {
19                     value = Convert.ChangeType(value, cmdAttribute.TargetType);
20                 }
21 
22                 var propertyBytes = value.ToBytes();
23 
24                 if (cmdAttribute.IsReverse)
25                     propertyBytes = propertyBytes.Reverse().ToArray();
26 
27                 result.AddRange(propertyBytes);
28             }
29 
30             return result.ToArray();
31         }
32 
33 
34         private static byte[] ToBytes(this object obj)
35         {
36             return obj switch
37             {
38                 short s => BitConverter.GetBytes(s),
39                 ushort s => BitConverter.GetBytes(s),
40                 int s => BitConverter.GetBytes(s),
41                 uint s => BitConverter.GetBytes(s),
42                 float s => BitConverter.GetBytes(s),
43                 double s => BitConverter.GetBytes(s),
44                 byte s => [s],
45                 _ => throw new NotImplementedException(),
46             };
47         }
48     }

将数据命令与包头,包尾拼接,从而组合成一包完整数据

创建类 CmdHelper

复制代码
 1     public class CmdHelper
 2     {
 3         private byte[] GetHeads()
 4         {
 5             return [0x0B, 0x0F];
 6         }
 7 
 8 
 9         private byte[] GetTails()
10         {
11             return [0x0C, 0x0A];
12         }
13 
14         public byte[] BuilderCmd(ParamBase param)
15         {
16             return
17                 [
18                     .. GetHeads(),
19                     .. param.ToCmd(),
20                     .. GetTails(),
21                 ];
22         }
23     }

调用:

复制代码
 1 var cmdHelper = new CmdHelper();
 2 var param = new MoveParam()
 3 {
 4     XPoint = 14,
 5     YPoint = 14,
 6     ZPoint = 14,
 7     Axis = Enums.Axis.Axis_1,
 8     Speed = 20,
 9     StepNum = 1
10 };
11 var byteArr = cmdHelper.BuilderCmd(param);
12 
13 foreach (var item in byteArr) 
14 {
15     Console.Write(item.ToString("X2") + " ");
16 }

最后的打印结果为:

复制代码
0B 0F 00 00 00 01 00 00 01 00 00 00 00 14 0E 00 00 00 0E 00 00 00 0E 00 00 00 0C 0A

如果后续在写其他命令,只需继承于 ParamBase 类,在对应的属性上使用 CmdProperty 特性即可

相关推荐
CodeCraft Studio13 分钟前
Excel处理控件Aspose.Cells教程:使用 C# 在 Excel 中创建股票高低收盘图
信息可视化·c#·excel·aspose·股票收盘图·c# excel库·收盘图
秋月的私语23 分钟前
c#字符串Split与CSV解析中的引号处理
服务器·开发语言·c#
小猪快跑爱摄影28 分钟前
【AutoCad 2025】【C#】零基础教程(一)——Rider 构建 HELLO 插件
开发语言·c#
mangge081 小时前
c#自动刷新已经打开的网址
c#
唐青枫2 小时前
一次看懂 C# TimeSpan:时间差操作的完整指南
c#·.net
未来之窗软件服务13 小时前
幽冥大陆(四十一)美萍V10酒店门锁SDK C#语言仙盟插件——东方仙盟筑基期
开发语言·c#·仙盟创梦ide·东方仙盟·东方仙盟sdk·酒店智能门锁·东方仙盟 vos 智能浏览器
用户83562907805117 小时前
Word 图表自动化:基于 C# 的高效数据可视化方案
后端·c#
wangnaisheng19 小时前
【C#】MQTT的使用
c#
玩泥巴的1 天前
一分钟实现.NET与飞书长连接的WebSocket架构
c#·.net·二次开发·飞书
mudtools1 天前
一分钟实现.NET与飞书长连接的WebSocket架构
后端·c#·.net