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

相关推荐
幻想趾于现实8 小时前
C# Winform 入门(1)之跨线程调用,程序说话
开发语言·c#·winform
幻想趾于现实9 小时前
C# Winform 入门(2)之发送邮件
开发语言·c#
长安——归故李10 小时前
51单片机彩灯控制与数码管结合
汇编·单片机·嵌入式硬件·青少年编程·c#·51单片机·硬件工程
arriettyandray10 小时前
C#/WPF学习系列之问题记录——使用不流畅
c#·wpf
weixin_3077791311 小时前
判断HiveQL语句为建表语句的识别函数
开发语言·数据仓库·hive·c#
我是苏苏11 小时前
C#高级:利用LINQ进行实体列表的集合运算
c#·linq
du fei13 小时前
C# 窗体应用(.FET Framework) 线程操作方法
开发语言·c#
du fei13 小时前
C#文件操作
开发语言·c#
Ljugg13 小时前
把doi直接插入word中,然后直接生成参考文献
开发语言·c#·word
厦门德仔15 小时前
【C#】C#字符串拼接的6种方式及其性能分析对比
服务器·windows·c#