When writing software for the host computer, you need to deal with command splicing to communicate with other devices frequently, and usually encapsulate different commands into different methods, which is slightly troublesome to extend.
This is proposed to be implemented as a feature to balance maintainability and extensibility.
Thoughts:
One kind of command corresponds to a class, and each attribute in its class corresponds to each command segment, and its position in this packet of data commands, its big end or small end and its conversion to the corresponding target type are realized by means of characteristics;
It is then packetized by reflection to get a complete packet of data.
Scene:
Move an axis to the corresponding X, Y, and Z positions and, for demonstration purposes, share a velocity for its
This command to move to a specified position is assumed to be composed in the following order (for the sake of presentation, a hasty command structure):
serial number | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
nibbles | 2 | u32 | u16 | u16 | u32 | s32 | s32 | s32 | 2 |
clarification | Baotou prefecture level city in Inner * | Step number (ID) | function code | axle | tempo | X position | Y position | Z position | finish off |
Realization:
Creating FeaturesCmdPropertyAttribute
1 [AttributeUsage()] 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 }
Parameter classes, each command corresponds to a parameter class that inherits from the parameter base class
Creating a parameter base classParamBase Each of the data is the first step number and is placed in the base class.
1 public class ParamBase 2 { 3 [CmdProperty(0, true)] 4 public int StepNum { get; set; } 5 }
Creating an Axis EnumerationAxis
1 public enum Axis : ushort 2 { 3 Axis_1 = 1, 4 5 Axis_2 = 2, 6 }
Creating a function code enumerationFunctionCode
1 public enum FunctionCode 2 { 3 Move = 1 4 }
Creating Mobile ClassesMoveParam In order to better demonstrate the high and low level transitions, a special pair ofSpeedProperties are inverted
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 = ; 29 StepNum = stepNum; 30 Axis = axis; 31 Speed = speed; 32 XPoint = xPoint; 33 YPoint = yPoint; 34 ZPoint = zPoint; 35 } 36 }
Reflective parsing of parameter objects to generate a collection of corresponding data commands
Creating Extended ClassesParamBaseExtensions
1 public static class ParamBaseExtensions 2 { 3 public static byte[] ToCmd(this ParamBase param) 4 { 5 var properties = ().GetProperties() 6 .Where(x => (typeof(CmdPropertyAttribute), false)) 7 .OrderBy(x => ((CmdPropertyAttribute)(typeof(CmdPropertyAttribute))).Number); 8 9 List<byte> result = new(); 10 11 foreach (var item in properties) 12 { 13 var cmdAttribute = (typeof(CmdPropertyAttribute)) as CmdPropertyAttribute; 14 15 var value = (param); 16 17 if ( is not null) 18 { 19 value = (value, ); 20 } 21 22 var propertyBytes = (); 23 24 if () 25 propertyBytes = ().ToArray(); 26 27 (propertyBytes); 28 } 29 30 return (); 31 } 32 33 34 private static byte[] ToBytes(this object obj) 35 { 36 return obj switch 37 { 38 short s => (s), 39 ushort s => (s), 40 int s => (s), 41 uint s => (s), 42 float s => (s), 43 double s => (s), 44 byte s => [s], 45 _ => throw new NotImplementedException(), 46 }; 47 } 48 }
Splice data commands with packet header and footer to form a complete packet.
Creating ClassesCmdHelper
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 .. (), 20 .. GetTails(), 21 ]; 22 } 23 }
Call:
1 var cmdHelper = new CmdHelper(); 2 var param = new MoveParam() 3 { 4 XPoint = 14, 5 YPoint = 14, 6 ZPoint = 14, 7 Axis = .Axis_1, 8 Speed = 20, 9 StepNum = 1 10 }; 11 var byteArr = (param); 12 13 foreach (var item in byteArr) 14 { 15 (("X2") + " "); 16 }
The final printout is:
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
If you are subsequently writing other commands, just inherit from theParamBase class on the corresponding property using theCmdProperty Characterization is sufficient