首页/文章列表/文章详情

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

编程知识1392024-08-16评论

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

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


思想:

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

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

场景:

将一个轴移动到对应的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 ,每种数据都是步骤号处于第一位,特把其放入到基类中

1publicclassParamBase2{3[CmdProperty(0,true)]4publicint StepNum { get;set; }5}

创建轴枚举  Axis 

1publicenum Axis : ushort2{3 Axis_1 = 1,45 Axis_2 = 2,6}

创建功能码枚举  FunctionCode 

1publicenumFunctionCode2{3 Move = 14}

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

1publicclass MoveParam : ParamBase2{3[CmdProperty(1,typeof(ushort))]4public FunctionCode Function { get; init; }56[CmdProperty(2,typeof(ushort))]7public Axis Axis { get;set; }89[CmdProperty(3,true)]10publicuint Speed { get;set; }1112[CmdProperty(4)]13publicint XPoint { get;set; }1415[CmdProperty(5)]16publicint YPoint { get;set; }1718[CmdProperty(6)]19publicint ZPoint { get;set; }2021publicMoveParam()22{2324}2526publicMoveParam(int stepNum, Axis axis, uintspeed,intxPoint,intyPoint,intzPoint)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 

1publicstaticclassParamBaseExtensions2{3publicstaticbyte[] ToCmd(this ParamBase param)4{5var properties =param.GetType().GetProperties()6 .Where(x => x.IsDefined(typeof(CmdPropertyAttribute),false))7 .OrderBy(x => ((CmdPropertyAttribute)x.GetCustomAttribute(typeof(CmdPropertyAttribute))).Number);89List<byte> result = new();1011foreach(variteminproperties)12{13var cmdAttribute = item.GetCustomAttribute(typeof(CmdPropertyAttribute))asCmdPropertyAttribute;1415var value =item.GetValue(param);1617if(cmdAttribute.TargetTypeisnotnull)18{19 value = Convert.ChangeType(value, cmdAttribute.TargetType);20}2122var propertyBytes =value.ToBytes();2324if(cmdAttribute.IsReverse)25 propertyBytes =propertyBytes.Reverse().ToArray();2627result.AddRange(propertyBytes);28}2930returnresult.ToArray();31}323334privatestaticbyte[] ToBytes(thisobjectobj)35{36returnobjswitch37{38short s =>BitConverter.GetBytes(s),39ushort s =>BitConverter.GetBytes(s),40int s =>BitConverter.GetBytes(s),41uint s =>BitConverter.GetBytes(s),42float s =>BitConverter.GetBytes(s),43double s =>BitConverter.GetBytes(s),44byte s =>[s],45 _ => thrownewNotImplementedException(),46};47}48}

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

创建类  CmdHelper 

1publicclassCmdHelper2{3privatebyte[] GetHeads()4{5return[0x0B,0x0F];6}789privatebyte[] GetTails()10{11return[0x0C,0x0A];12}1314publicbyte[] BuilderCmd(ParamBase param)15{16return17[18 .. GetHeads(),19 .. param.ToCmd(),20 .. GetTails(),21];22}23}

调用:

1var cmdHelper = newCmdHelper();2var param = newMoveParam()3{4 XPoint = 14,5 YPoint = 14,6 ZPoint = 14,7 Axis =Enums.Axis.Axis_1,8 Speed = 20,9 StepNum = 110};11var byteArr =cmdHelper.BuilderCmd(param);1213foreach(variteminbyteArr)14{15Console.Write(item.ToString("X2") + "");16}

最后的打印结果为:

0B 0F 0000000100000100000000140E0000000E0000000E000000 0C 0A

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

 

博客园

这个人很懒...

用户评论 (0)

发表评论

captcha