在编写上位机软件时,需要经常处理命令拼接与其他设备进行通信,通常对不同的命令封装成不同的方法,扩展稍许麻烦。
本次拟以特性方式实现,以兼顾维护性与扩展性。
思想:
一种命令对应一个类,其类中的各个属性对应各个命令段,通过特性的方式,实现其在这包数据命令中的位置、大端或小端及其转换为对应的目标类型;
然后通过反射对其进行拼包,从而得到一包完整数据。
场景:
将一个轴移动到对应的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 特性即可
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容