热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

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

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

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

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

 1  public class DataItem
 2         {
 3             /// 
 4             ///  数据项ID
 5             /// 
 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 enum DbDataTypeEnum
42     {
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 enum DataTypeEnum
57     {
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:

 1 <#@ template language="C#" #>
 2 <#@ assembly name="System.Core" #>
 3 <#@ assembly name="Kugar.Core.NetCore" #>
 4 <#@ assembly name="Kugar.Device.Service.BLL" #>
 5 <#@ assembly name="Kugar.Device.Service.Data" #>
 6 <#@ assembly name="MongoDB.Bson" #>
 7 
 8 <#@ import namespace="System.Linq" #>
 9 <#@ import namespace="System.Text" #>
10 <#@ import namespace="System.Collections.Generic" #>
11 <#@ import namespace="Kugar.Core.BaseStruct" #>
12 <#@ import namespace="MongoDB.Bson" #>
13 
14 using System;
15 using System.Text;
16 using Kugar.Core.BaseStruct;
17 using Kugar.Core.ExtMethod;
18 using Kugar.Core.Log;
19 using Kugar.Device.Service.Data.DTO;
20 using Kugar.Device.Service.Data.Enums;
21 using MongoDB.Bson;
22 
23 namespace Kugar.Device.Service.BLL
24 {
25     <#
26         var className="ProtocolExecutor_" + Protocol.Version.Replace('.','_') + "_" + this.GetNextClasID();
27  #>
28 
29 
30     public class <#=className #>:IProtocolExecutor
31     {
32         private string _version="";
33         private ObjectId _protocolID;
34         private readonly DateTime _baseDt=TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
35 
36         public <#=className #> (ObjectId protocolID, string version)
37         {
38             _version=version;
39             _protocolID=protocolID;
40         }
41 
42         public ObjectId ProtocolID {get{return _protocolID;}}
43 
44         public BsonDocument Execute(byte[] data, int startIndex)
45         {
46             BsonDocument bson=new BsonDocument();
47             <#
48                 foreach(var item in Protocol.Items){ #>
49             bson["<#=item.Key #>"]= <#= DecodeConfig(item,0) #>;
50 <#
51                 }
52  #>
53 
54             return bson;
55             
56         }    
57     }
58 }
View Code

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

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

  1 public partial class ProtocolExecuteTemplate
  2     {
  3         private static int _classID = 0;
  4 
  5         public ProtocolExecuteTemplate(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,int startIndex)
 16         {
 17             var str = "";
 18 
 19             switch (item.SourceType)
 20             {
 21                 case DataTypeEnum.Int:
 22                     str = $"BitConverter.ToInt32(data,startIndex + {startIndex + item.Pos})";
 23                     break;
 24                 case DataTypeEnum.UInt:
 25                     str = $"BitConverter.ToUInt32(data,startIndex + {startIndex + item.Pos})";
 26                     break;
 27                 case DataTypeEnum.Short:
 28                     str = $"BitConverter.ToInt16(data,startIndex + {startIndex + item.Pos})";
 29                     break;
 30                 case DataTypeEnum.Long:
 31                     str= $"BitConverter.ToInt64(data,startIndex + {startIndex + item.Pos})";
 32                     break;
 33                 case DataTypeEnum.Byte:
 34                 case DataTypeEnum.UByte:
 35                 case DataTypeEnum.Bit:
 36                     str = $"data[startIndex + {startIndex + item.Pos}]";
 37                     break;
 38                 case DataTypeEnum.UShort:
 39                     str = $"BitConverter.ToUInt16(data,startIndex+{startIndex + item.Pos})";
 40                     break;
 41                 default:
 42                     throw new ArgumentOutOfRangeException();
 43             }
 44 
 45             if (item.SourceType==DataTypeEnum.Bit)
 46             {
 47                 return byteToBit(str, item.BitIndex);
 48             }
 49             else
 50             {
 51                 return valueTODBType(str, item.Factor, item.DbType);
 52             }
 53         }
 54 
 55         private string valueTODBType(string sourceValue, decimal factor, DbDataTypeEnum dbType)
 56         {
 57             switch (dbType)
 58             {
 59                 case DbDataTypeEnum.Int32:
 60                     return $" new BsonInt32({(factor > 0 ? $"(int)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : sourceValue)})";
 61                 case DbDataTypeEnum.Int64:
 62                     return $" new BsonInt64({(factor > 0 ? $"(long)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : sourceValue)})";
 63                 case DbDataTypeEnum.Double:
 64                     return $"new BsonDouble({(factor > 0 ? $"(double)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : $"(double){sourceValue}")})";
 65                 case DbDataTypeEnum.DateTime:
 66                     return $"new BsonDateTime(_baseDt.AddSeconds({sourceValue}))";
 67                 case DbDataTypeEnum.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, int index)
 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 new ArgumentOutOfRangeException(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(decimal value)
124         {
125             return (value % 1) == 0 ? "" : "m";
126         }
127 
128         public int GetNextClasID()
129         {
130             return Interlocked.Increment(ref _classID);
131         }
132     }
View Code

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

 1     public class ProtocolExecutor_1_1_000
 2     {
 3         public BsonDocument Execute(byte[] data, int startIndex)
 4         {
 5             BsonDocument bson = new BsonDocument();
 6 
 7             bson["项目名1"] = new BsonInt32(BitConverter.ToInt32(data, startIndex + 偏移量));
 8             bson["项目名2"] = new BsonInt32(BitConverter.ToInt32(data, startIndex + 偏移量));
 9               。。。。。。。 //其他数据项
10 
11             return bson;
12         }
13     }

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


推荐阅读
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
author-avatar
浮尘所惑的猫猫
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有