翼度科技»论坛 编程开发 .net 查看内容

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

7

主题

7

帖子

21

积分

新手上路

Rank: 1

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

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

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

 

 
实现:

创建特性 CmdPropertyAttribute 
  1. 1 [AttributeUsage(AttributeTargets.Property)]
  2. 2 internal class CmdPropertyAttribute : Attribute
  3. 3 {
  4. 4     public Type? TargetType { get; set; }
  5. 5
  6. 6     public int Number { get; set; }
  7. 7
  8. 8     public bool IsReverse { get; set; }
  9. 9
  10. 10     public CmdPropertyAttribute(int number)
  11. 11     {
  12. 12         Number = number;
  13. 13     }
  14. 14
  15. 15     public CmdPropertyAttribute(int number, Type targetType)
  16. 16     {
  17. 17         Number = number;
  18. 18         TargetType = targetType;
  19. 19     }
  20. 20
  21. 21     public CmdPropertyAttribute(int number, bool isReverse)
  22. 22     {
  23. 23         Number = number;
  24. 24         IsReverse = isReverse;
  25. 25     }
  26. 26
  27. 27     public CmdPropertyAttribute(int number, Type targetType, bool isReverse)
  28. 28     {
  29. 29         Number = number;
  30. 30         IsReverse = isReverse;
  31. 31         TargetType = targetType;
  32. 32     }
  33. 33 }
复制代码
参数类,每一种命令对应一个参数类,它们继承于参数基类
创建参数基类  ParamBase ,每种数据都是步骤号处于第一位,特把其放入到基类中
  1. 1     public class ParamBase
  2. 2     {
  3. 3         [CmdProperty(0, true)]
  4. 4         public int StepNum { get; set; }
  5. 5     }
复制代码
创建轴枚举  Axis 
  1. 1     public enum Axis : ushort
  2. 2     {
  3. 3         Axis_1 = 1,
  4. 4
  5. 5         Axis_2 = 2,
  6. 6     }
复制代码
创建功能码枚举  FunctionCode 
  1. 1     public enum FunctionCode
  2. 2     {
  3. 3         Move = 1
  4. 4     }
复制代码
创建移动类  MoveParam ,为了更好展示高低位转换,特对Speed属性进行反转
  1. 1     public class MoveParam : ParamBase
  2. 2     {
  3. 3         [CmdProperty(1, typeof(ushort))]
  4. 4         public FunctionCode Function { get; init; }
  5. 5
  6. 6         [CmdProperty(2, typeof(ushort))]
  7. 7         public Axis Axis { get; set; }
  8. 8
  9. 9         [CmdProperty(3, true)]
  10. 10         public uint Speed { get; set; }
  11. 11
  12. 12         [CmdProperty(4)]
  13. 13         public int XPoint { get; set; }
  14. 14
  15. 15         [CmdProperty(5)]
  16. 16         public int YPoint { get; set; }
  17. 17
  18. 18         [CmdProperty(6)]
  19. 19         public int ZPoint { get; set; }
  20. 20
  21. 21         public MoveParam()
  22. 22         {
  23. 23             Function = FunctionCode.Move;
  24. 24         }
  25. 25
  26. 26         public MoveParam(int stepNum, Axis axis, uint speed, int xPoint, int yPoint, int zPoint)
  27. 27         {
  28. 28             Function = FunctionCode.Move;
  29. 29             StepNum = stepNum;
  30. 30             Axis = axis;
  31. 31             Speed = speed;
  32. 32             XPoint = xPoint;
  33. 33             YPoint = yPoint;
  34. 34             ZPoint = zPoint;
  35. 35         }
  36. 36     }
复制代码
对参数对象进行反射解析,生成对应的数据命令集合
创建扩展类  ParamBaseExtensions 
  1. 1     public static class ParamBaseExtensions
  2. 2     {
  3. 3         public static byte[] ToCmd(this ParamBase param)
  4. 4         {
  5. 5             var properties = param.GetType().GetProperties()
  6. 6                 .Where(x => x.IsDefined(typeof(CmdPropertyAttribute), false))
  7. 7                 .OrderBy(x => ((CmdPropertyAttribute)x.GetCustomAttribute(typeof(CmdPropertyAttribute))).Number);
  8. 8
  9. 9             List<byte> result = new();
  10. 10
  11. 11             foreach (var item in properties)
  12. 12             {
  13. 13                 var cmdAttribute = item.GetCustomAttribute(typeof(CmdPropertyAttribute)) as CmdPropertyAttribute;
  14. 14
  15. 15                 var value = item.GetValue(param);
  16. 16
  17. 17                 if (cmdAttribute.TargetType is not null)
  18. 18                 {
  19. 19                     value = Convert.ChangeType(value, cmdAttribute.TargetType);
  20. 20                 }
  21. 21
  22. 22                 var propertyBytes = value.ToBytes();
  23. 23
  24. 24                 if (cmdAttribute.IsReverse)
  25. 25                     propertyBytes = propertyBytes.Reverse().ToArray();
  26. 26
  27. 27                 result.AddRange(propertyBytes);
  28. 28             }
  29. 29
  30. 30             return result.ToArray();
  31. 31         }
  32. 32
  33. 33
  34. 34         private static byte[] ToBytes(this object obj)
  35. 35         {
  36. 36             return obj switch
  37. 37             {
  38. 38                 short s => BitConverter.GetBytes(s),
  39. 39                 ushort s => BitConverter.GetBytes(s),
  40. 40                 int s => BitConverter.GetBytes(s),
  41. 41                 uint s => BitConverter.GetBytes(s),
  42. 42                 float s => BitConverter.GetBytes(s),
  43. 43                 double s => BitConverter.GetBytes(s),
  44. 44                 byte s => [s],
  45. 45                 _ => throw new NotImplementedException(),
  46. 46             };
  47. 47         }
  48. 48     }
复制代码
将数据命令与包头,包尾拼接,从而组合成一包完整数据
创建类  CmdHelper 
  1. 1     public class CmdHelper
  2. 2     {
  3. 3         private byte[] GetHeads()
  4. 4         {
  5. 5             return [0x0B, 0x0F];
  6. 6         }
  7. 7
  8. 8
  9. 9         private byte[] GetTails()
  10. 10         {
  11. 11             return [0x0C, 0x0A];
  12. 12         }
  13. 13
  14. 14         public byte[] BuilderCmd(ParamBase param)
  15. 15         {
  16. 16             return
  17. 17                 [
  18. 18                     .. GetHeads(),
  19. 19                     .. param.ToCmd(),
  20. 20                     .. GetTails(),
  21. 21                 ];
  22. 22         }
  23. 23     }
复制代码
调用:
  1. 1 var cmdHelper = new CmdHelper();
  2. 2 var param = new MoveParam()
  3. 3 {
  4. 4     XPoint = 14,
  5. 5     YPoint = 14,
  6. 6     ZPoint = 14,
  7. 7     Axis = Enums.Axis.Axis_1,
  8. 8     Speed = 20,
  9. 9     StepNum = 1
  10. 10 };
  11. 11 var byteArr = cmdHelper.BuilderCmd(param);
  12. 12
  13. 13 foreach (var item in byteArr)
  14. 14 {
  15. 15     Console.Write(item.ToString("X2") + " ");
  16. 16 }
复制代码
最后的打印结果为:
  1. 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】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具