腾进电子 发表于 2024-8-16 22:50:14

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

在编写上位机软件时,需要经常处理命令拼接与其他设备进行通信,通常对不同的命令封装成不同的方法,扩展稍许麻烦。
本次拟以特性方式实现,以兼顾维护性与扩展性。
思想:

一种命令对应一个类,其类中的各个属性对应各个命令段,通过特性的方式,实现其在这包数据命令中的位置、大端或小端及其转换为对应的目标类型;
然后通过反射对其进行拼包,从而得到一包完整数据。
场景:

将一个轴移动到对应的X,Y,Z位置,为了演示,对其共用一个速度
这个移动到指定位置的命令假设按以下顺序构成(为了展示,草率的命令结构):
序号123456789字节2s32u16u16u32s32s32s322说明包头步骤号(ID)功能码轴速度X位置Y位置Z位置包尾 
 

 

 
实现:

创建特性 CmdPropertyAttribute 
1
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         
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         
4         public FunctionCode Function { get; init; }
5
6         
7         public Axis Axis { get; set; }
8
9         
10         public uint Speed { get; set; }
11
12         
13         public int XPoint { get; set; }
14
15         
16         public int YPoint { get; set; }
17
18         
19         public int ZPoint { get; set; }
20
21         public MoveParam()
22         {
23             Function = FunctionCode.Move;
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 => ,
45               _ => throw new NotImplementedException(),
46             };
47         }
48   }将数据命令与包头,包尾拼接,从而组合成一包完整数据
创建类  CmdHelper 
1   public class CmdHelper
2   {
3         private byte[] GetHeads()
4         {
5             return ;
6         }
7
8
9         private byte[] GetTails()
10         {
11             return ;
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  特性即可
 

来源:https://www.cnblogs.com/fengxinyuan/p/18363546
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: C# 使用特性的方式封装报文