C# 使用特性的方式封装报文
  RJ2a1qGW1KTQ 2024年08月16日 91 0

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

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


思想:

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

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

场景:

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

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

序号 1 2 3 4 5 6 7 8 9
字节 2 s32 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  特性即可

 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年08月16日 0

暂无评论

推荐阅读
RJ2a1qGW1KTQ
作者其他文章 更多