Location>code7788 >text

C# Encapsulating messages with features

Popularity:876 ℃/2024-08-16 20:04:36

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