mysql连接下位机_与下位机或设备的通信解析优化的一点功能:T4+动态编译

本文介绍了如何通过T4模板动态生成C#类来提高与下位机通信时Modbus协议数据解析的效率。项目中原本采用循环和if判断解析配置信息,但针对大量数据和配置项,这种方法效率较低。改进后,利用T4模板根据配置动态生成源码,避免了循环和类型判断,显著提升了解析速度。解析过程中,区分了源数据和数据库数据类型,并提供了ProtocolExecuteTemplate类的代码示例。
摘要由CSDN通过智能技术生成

去年接触的一个项目中,需要通过TCP与设备进行对接的,传的是Modbus协议的数据,然后后台需要可以动态配置协议解析的方式,即寄存器的解析方式,,配置信息有:Key,数据Index,源数据类型,数据库列类型,数据排列方式

一开始使用的方式是,从数据库读取出协议的配置,然后在接收到数据的时候,循环每个配置项根据配置-----解析数据------转换类型----存临时列表,,后来改进了一下,配置项存缓存,,数据库修改的时候,同时更新缓存。。

但还是慢了一点,因为需一个配置大概是20-30个数据项,每一条数据都要for循环20-30次 ,再加上还有N个根据配置的数据类型去做转换的if判断,这么一套下来,也很耗时间,但待解析的数据量大的情况下,,相对也很耗资源。。。

最后的觉得方案是:利用T4生成C#的class源码+运行时编译成类,数据直接扔class里直接解析出结果,不需要循环,也不需要if判断,因为在t4生成源码的时候,已经根据配置处理完了,因此节省了很多的时间。

不过由于T4模板的IDE支持的很不好,不过好在运行时T4模板在IDE内生成出来的类是partial的,因此,可以把大部分的代码,放在外部的C#文件里。先来看数据项的配置信息:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public classDataItem2 {3 ///

4 ///数据项ID5 ///

6 public ObjectId DataItemID { set; get; }7

8 ///

9 ///偏移量10 ///

11 public int Pos { set; get; }12

13 ///

14 ///大小15 ///

16 public int Size { set; get; }17

18 public int BitIndex { set; get; }19

20 ///

21 ///数据项数据库储存类型22 ///

23 public DbDataTypeEnum DbType { set; get; }24

25 ///

26 ///数据项协议源字节数组中的数据类型27 ///

28 public DataTypeEnum SourceType { set; get; }29

30 ///

31 ///计算因子32 ///

33 public decimal Factor { set; get; }34

35 public string Key { set; get; }36 }37

38 ///

39 ///对应的数据库字段类型40 ///

41 public enumDbDataTypeEnum42 {43 Int32 = 0,44

45 Int64 = 1,46

47 Double = 2,48

49 DateTime = 3,50

51 Decimal = 4,52

53 Boolean = 5

54 }55

56 public enumDataTypeEnum57 {58 Int = 0,59

60 Short = 1,61

62 Datetime = 3,63

64 Long = 5,65

66 Decimal = 6,67

68 UInt = 7,69

70 Byte = 8,71

72 Boolean = 9,73

74 Bit = 10,75

76 UShort = 11,77

78 UByte = 12

79 }

View Code

这里为什么要区分源数据和数据库数据类型呢?主要是因为设备一般是int,short,double,float等类型,但是,对应到数据库,有时候比如说使用mongodb,之类的数据库,不一定有完全匹配的,因此需要区分两种数据项,

再来就是T4的模板  ProtocolExecuteTemplate.tt:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1

2

3

4

5

6

7

8

9

10

11

12

13

14 usingSystem;15 usingSystem.Text;16 usingKugar.Core.BaseStruct;17 usingKugar.Core.ExtMethod;18 usingKugar.Core.Log;19 usingKugar.Device.Service.Data.DTO;20 usingKugar.Device.Service.Data.Enums;21 usingMongoDB.Bson;22

23 namespaceKugar.Device.Service.BLL24 {25

28

29

30 public class :IProtocolExecutor31 {32 private string _version="";33 privateObjectId _protocolID;34 private readonly DateTime _baseDt=TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));35

36 public (ObjectId protocolID, stringversion)37 {38 _version=version;39 _protocolID=protocolID;40 }41

42 public ObjectId ProtocolID {get{return_protocolID;}}43

44 public BsonDocument Execute(byte[] data, intstartIndex)45 {46 BsonDocument bson=newBsonDocument();47

49 bson[""]= ;50

53

54 returnbson;55

56 }57 }58 }

View Code

在直接在循环里输出解析后的语句,并且生成的类名记得后面加多一个随机数。。。。

然后再加一个ProtocolExecuteTemplate.Part.cs的部分类,补全T4模板的功能,因为在T4里IDE支持的不好,,写代码确实难受,,没直接写C#舒服:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public partial classProtocolExecuteTemplate2 {3 private static int _classID = 0;4

5 publicProtocolExecuteTemplate(DTO_ProtocolDataItem protocol)6 {7 Protocol =protocol;8

9 }10

11

12

13 public DTO_ProtocolDataItem Protocol { set; get; }14

15 public string DecodeConfig(DTO_ProtocolDataItem.DataItem item,intstartIndex)16 {17 var str = "";18

19 switch(item.SourceType)20 {21 caseDataTypeEnum.Int:22 str = $"BitConverter.ToInt32(data,startIndex + {startIndex + item.Pos})";23 break;24 caseDataTypeEnum.UInt:25 str = $"BitConverter.ToUInt32(data,startIndex + {startIndex + item.Pos})";26 break;27 caseDataTypeEnum.Short:28 str = $"BitConverter.ToInt16(data,startIndex + {startIndex + item.Pos})";29 break;30 caseDataTypeEnum.Long:31 str= $"BitConverter.ToInt64(data,startIndex + {startIndex + item.Pos})";32 break;33 caseDataTypeEnum.Byte:34 caseDataTypeEnum.UByte:35 caseDataTypeEnum.Bit:36 str = $"data[startIndex + {startIndex + item.Pos}]";37 break;38 caseDataTypeEnum.UShort:39 str = $"BitConverter.ToUInt16(data,startIndex+{startIndex + item.Pos})";40 break;41 default:42 throw newArgumentOutOfRangeException();43 }44

45 if (item.SourceType==DataTypeEnum.Bit)46 {47 returnbyteToBit(str, item.BitIndex);48 }49 else

50 {51 returnvalueTODBType(str, item.Factor, item.DbType);52 }53 }54

55 private string valueTODBType(string sourceValue, decimalfactor, DbDataTypeEnum dbType)56 {57 switch(dbType)58 {59 caseDbDataTypeEnum.Int32:60 return $"new BsonInt32({(factor > 0 ? $"(int)({factor}{getDecimalShortChar(factor)} * {sourceValue})": sourceValue)})";61 caseDbDataTypeEnum.Int64:62 return $"new BsonInt64({(factor > 0 ? $"(long)({factor}{getDecimalShortChar(factor)} * {sourceValue})": sourceValue)})";63 caseDbDataTypeEnum.Double:64 return $"new BsonDouble({(factor > 0 ? $"(double)({factor}{getDecimalShortChar(factor)} * {sourceValue})": $"(double){sourceValue}")})";65 caseDbDataTypeEnum.DateTime:66 return $"new BsonDateTime(_baseDt.AddSeconds({sourceValue}))";67 caseDbDataTypeEnum.Decimal:68 return $"new Decimal128({(factor > 0 ? $"(decimal)({factor}{getDecimalShortChar(factor)} * {sourceValue})": sourceValue)})";69 default:70 throw new ArgumentOutOfRangeException(nameof(dbType), dbType, null);71 }72 }73

74 private string byteToBit(string data, intindex)75 {76 switch(index)77 {78 case 0:79 {80

81 return $"(({data} & 1) ==1)";//(data & 1) == 1;

82 }83 case 1:84 {85 return $"(({data} & 2) ==1)";//(data & 2) == 2;

86 }87 case 2:88 {89 return $"(({data} & 4) ==1)";//(data & 4) == 4;

90 }91 case 3:92 {93 return $"(({data} & 8) ==1)";//(data & 8) == 8;

94 }95 case 4:96 {97 return $"(({data} & 16) ==1)";//(data & 16) == 16;

98 }99 case 5:100 {101 return $"(({data} & 32) ==1)";//(data & 32) == 32;

102 }103 case 6:104 {105 return $"(({data} & 64) ==1)";//(data & 64) == 64;

106 }107 case 7:108 {109 return $"(({data} & 128) ==1)";//(data & 128) == 128;

110 }111 default:112 throw newArgumentOutOfRangeException(nameof(index));113 }114

115 return $"(({data} & {index + 1}) ==1)";116 }117

118 ///

119 ///用于判断传入的fator是否需要使用deciaml进行运算,如果有小数点的,则是否decimal缩写m,,如果没有小数点,则使用普通的int类型120 ///

121 ///

122 ///

123 private string getDecimalShortChar(decimalvalue)124 {125 return (value % 1) == 0 ? "" : "m";126 }127

128 public intGetNextClasID()129 {130 return Interlocked.Increment(ref_classID);131 }132 }

View Code

这样,在运行时,即可直接生成可用于解析的类了,而且也不需要for循环判断,生成出来的类如:

1 public classProtocolExecutor_1_1_0002 {3 public BsonDocument Execute(byte[] data, intstartIndex)4 {5 BsonDocument bson = newBsonDocument();6

7 bson["项目名1"] = new BsonInt32(BitConverter.ToInt32(data, startIndex +偏移量));8 bson["项目名2"] = new BsonInt32(BitConverter.ToInt32(data, startIndex +偏移量));9 。。。。。。。 //其他数据项

10

11 returnbson;12 }13 }

到这一步,就可以根绝配置项生成出对应的C#代码了,剩下的就是动态编译的事情了、将该代码编译出运行时Type,然后传入数据----解析出数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值