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 特性即可

相关推荐
IT规划师16 分钟前
C#|.net core 基础 - 扩展数组添加删除性能最好的方法
c#·.netcore·数组
时光追逐者1 小时前
分享6个.NET开源的AI和LLM相关项目框架
人工智能·microsoft·ai·c#·.net·.netcore
friklogff1 小时前
【C#生态园】提升C#开发效率:深入了解自然语言处理库与工具
开发语言·c#·区块链
__water10 小时前
『功能项目』回调函数处理死亡【54】
c#·回调函数·unity引擎
__water10 小时前
『功能项目』眩晕图标显示【52】
c#·unity引擎·动画事件
__water11 小时前
『功能项目』第二职业法师的平A【57】
c#·unity引擎·魔法球伤害传递
__water13 小时前
『功能项目』战士的伤害型技能【45】
c#·unity引擎·战士职业伤害型技能
君莫愁。14 小时前
【Unity】检测鼠标点击位置是否有2D对象
unity·c#·游戏引擎
Lingbug15 小时前
.Net日志组件之NLog的使用和配置
后端·c#·.net·.netcore
咩咩觉主15 小时前
Unity实战案例全解析:PVZ 植物卡片状态分析
unity·c#·游戏引擎