|
在编写上位机软件时,需要经常处理命令拼接与其他设备进行通信,通常对不同的命令封装成不同的方法,扩展稍许麻烦。
本次拟以特性方式实现,以兼顾维护性与扩展性。
思想:
一种命令对应一个类,其类中的各个属性对应各个命令段,通过特性的方式,实现其在这包数据命令中的位置、大端或小端及其转换为对应的目标类型;
然后通过反射对其进行拼包,从而得到一包完整数据。
场景:
将一个轴移动到对应的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 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 => [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 特性即可
来源:https://www.cnblogs.com/fengxinyuan/p/18363546
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|