与下位机或设备的通信解析优化的一点功能:T4+动态编译
一开始使用的方式是,从数据库读取出协议的配置,然后在接收到数据的时候,循环每个配置项根据配置-----解析数据------转换类型----存临时列表,,后来改进了一下,配置项存缓存,,数据库修改的时候,同时更新缓存。。
但还是慢了一点,因为需一个配置大概是20-30个数据项,每一条数据都要for循环20-30次 ,再加上还有N个根据配置的数据类型去做转换的if判断,这么一套下来,也很耗时间,但待解析的数据量大的情况下,,相对也很耗资源。。。
最后的觉得方案是:利用T4生成C#的class源码+运行时编译成类,数据直接扔class里直接解析出结果,不需要循环,也不需要if判断,因为在t4生成源码的时候,已经根据配置处理完了,因此节省了很多的时间。
不过由于T4模板的IDE支持的很不好,不过好在运行时T4模板在IDE内生成出来的类是partial的,因此,可以把大部分的代码,放在外部的C#文件里。先来看数据项的配置信息:
public class DataItem
{
/// <summary>
/// 数据项ID
/// </summary>
public ObjectId DataItemID { set; get; } /// <summary>
/// 偏移量
/// </summary>
public int Pos { set; get; } /// <summary>
/// 大小
/// </summary>
public int Size { set; get; } public int BitIndex { set; get; } /// <summary>
/// 数据项数据库储存类型
/// </summary>
public DbDataTypeEnum DbType { set; get; } /// <summary>
/// 数据项协议源字节数组中的数据类型
/// </summary>
public DataTypeEnum SourceType { set; get; } /// <summary>
/// 计算因子
/// </summary>
public decimal Factor { set; get; } public string Key { set; get; }
} /// <summary>
/// 对应的数据库字段类型
/// </summary>
public enum DbDataTypeEnum
{
Int32 = , Int64 = , Double = , DateTime = , Decimal = , Boolean =
} public enum DataTypeEnum
{
Int = , Short = , Datetime = , Long = , Decimal = , UInt = , Byte = , Boolean = , Bit = , UShort = , UByte =
}
这里为什么要区分源数据和数据库数据类型呢?主要是因为设备一般是int,short,double,float等类型,但是,对应到数据库,有时候比如说使用mongodb,之类的数据库,不一定有完全匹配的,因此需要区分两种数据项,
再来就是T4的模板 ProtocolExecuteTemplate.tt:
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="Kugar.Core.NetCore" #>
<#@ assembly name="Kugar.Device.Service.BLL" #>
<#@ assembly name="Kugar.Device.Service.Data" #>
<#@ assembly name="MongoDB.Bson" #> <#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Kugar.Core.BaseStruct" #>
<#@ import namespace="MongoDB.Bson" #> using System;
using System.Text;
using Kugar.Core.BaseStruct;
using Kugar.Core.ExtMethod;
using Kugar.Core.Log;
using Kugar.Device.Service.Data.DTO;
using Kugar.Device.Service.Data.Enums;
using MongoDB.Bson; namespace Kugar.Device.Service.BLL
{
<#
var className="ProtocolExecutor_" + Protocol.Version.Replace('.','_') + "_" + this.GetNextClasID();
#> public class <#=className #>:IProtocolExecutor
{
private string _version="";
private ObjectId _protocolID;
private readonly DateTime _baseDt=TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(, , )); public <#=className #> (ObjectId protocolID, string version)
{
_version=version;
_protocolID=protocolID;
} public ObjectId ProtocolID {get{return _protocolID;}} public BsonDocument Execute(byte[] data, int startIndex)
{
BsonDocument bson=new BsonDocument();
<#
foreach(var item in Protocol.Items){ #>
bson["<#=item.Key #>"]= <#= DecodeConfig(item,) #>;
<#
}
#> return bson; }
}
}
在直接在循环里输出解析后的语句,并且生成的类名记得后面加多一个随机数。。。。
然后再加一个ProtocolExecuteTemplate.Part.cs的部分类,补全T4模板的功能,因为在T4里IDE支持的不好,,写代码确实难受,,没直接写C#舒服:
public partial class ProtocolExecuteTemplate
{
private static int _classID = ; public ProtocolExecuteTemplate(DTO_ProtocolDataItem protocol)
{
Protocol = protocol; } public DTO_ProtocolDataItem Protocol { set; get; } public string DecodeConfig(DTO_ProtocolDataItem.DataItem item,int startIndex)
{
var str = ""; switch (item.SourceType)
{
case DataTypeEnum.Int:
str = $"BitConverter.ToInt32(data,startIndex + {startIndex + item.Pos})";
break;
case DataTypeEnum.UInt:
str = $"BitConverter.ToUInt32(data,startIndex + {startIndex + item.Pos})";
break;
case DataTypeEnum.Short:
str = $"BitConverter.ToInt16(data,startIndex + {startIndex + item.Pos})";
break;
case DataTypeEnum.Long:
str= $"BitConverter.ToInt64(data,startIndex + {startIndex + item.Pos})";
break;
case DataTypeEnum.Byte:
case DataTypeEnum.UByte:
case DataTypeEnum.Bit:
str = $"data[startIndex + {startIndex + item.Pos}]";
break;
case DataTypeEnum.UShort:
str = $"BitConverter.ToUInt16(data,startIndex+{startIndex + item.Pos})";
break;
default:
throw new ArgumentOutOfRangeException();
} if (item.SourceType==DataTypeEnum.Bit)
{
return byteToBit(str, item.BitIndex);
}
else
{
return valueTODBType(str, item.Factor, item.DbType);
}
} private string valueTODBType(string sourceValue, decimal factor, DbDataTypeEnum dbType)
{
switch (dbType)
{
case DbDataTypeEnum.Int32:
return $" new BsonInt32({(factor > 0 ? $"(int)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : sourceValue)})";
case DbDataTypeEnum.Int64:
return $" new BsonInt64({(factor > 0 ? $"(long)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : sourceValue)})";
case DbDataTypeEnum.Double:
return $"new BsonDouble({(factor > 0 ? $"(double)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : $"(double){sourceValue}")})";
case DbDataTypeEnum.DateTime:
return $"new BsonDateTime(_baseDt.AddSeconds({sourceValue}))";
case DbDataTypeEnum.Decimal:
return $"new Decimal128({(factor > 0 ? $"(decimal)({factor}{getDecimalShortChar(factor)} * {sourceValue})" : sourceValue)})";
default:
throw new ArgumentOutOfRangeException(nameof(dbType), dbType, null);
}
} private string byteToBit(string data, int index)
{
switch (index)
{
case :
{ return $"(({data} & 1) ==1)";//(data & 1) == 1;
}
case :
{
return $"(({data} & 2) ==1)";// (data & 2) == 2;
}
case :
{
return $"(({data} & 4) ==1)";//(data & 4) == 4;
}
case :
{
return $"(({data} & 8) ==1)";//(data & 8) == 8;
}
case :
{
return $"(({data} & 16) ==1)";//(data & 16) == 16;
}
case :
{
return $"(({data} & 32) ==1)";//(data & 32) == 32;
}
case :
{
return $"(({data} & 64) ==1)";//(data & 64) == 64;
}
case :
{
return $"(({data} & 128) ==1)";//(data & 128) == 128;
}
default:
throw new ArgumentOutOfRangeException(nameof(index));
} return $"(({data} & {index + 1}) ==1)";
} /// <summary>
/// 用于判断传入的fator是否需要使用deciaml进行运算,如果有小数点的,则是否decimal缩写m,,如果没有小数点,则使用普通的int类型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private string getDecimalShortChar(decimal value)
{
return (value % ) == ? "" : "m";
} public int GetNextClasID()
{
return Interlocked.Increment(ref _classID);
}
}
这样,在运行时,即可直接生成可用于解析的类了,而且也不需要for循环判断,生成出来的类如:
public class ProtocolExecutor_1_1_000
{
public BsonDocument Execute(byte[] data, int startIndex)
{
BsonDocument bson = new BsonDocument(); bson["项目名1"] = new BsonInt32(BitConverter.ToInt32(data, startIndex + 偏移量));
bson["项目名2"] = new BsonInt32(BitConverter.ToInt32(data, startIndex + 偏移量));
。。。。。。。 //其他数据项 return bson;
}
}
到这一步,就可以根绝配置项生成出对应的C#代码了,剩下的就是动态编译的事情了、将该代码编译出运行时Type,然后传入数据----解析出数据
与下位机或设备的通信解析优化的一点功能:T4+动态编译的更多相关文章
- STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发
这里我主要说一下如何做一个USB下位机,这里主要分3部分:1.建立工程:2.添加报文描述符:3.数据的传输.这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了. 建立工程:工程建立参考:h ...
- 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发
STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发 本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...
- C#使用struct直接转换下位机数据
编写上位机与下位机通信的时候,涉及到协议的转换,比较多会使用到二进制.传统的方法,是将数据整体获取到byte数组中,然后逐字节对数据进行解析.这样操作工作量比较大,对于较长数据段更容易计算位置出错. ...
- C#做上位机软件——绘图并传输给下位机
拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...
- 下位机多个".c, .h"文件的相互包含及排版
一.背景: 自从接触单片机编程以来,由于工作上的需要,不可避免的时常会接手别人的代码,但常常由于上一位同事的编码随意性有点大,导致可读性非常的差,有时候不得不完全舍弃原有代码,推倒重来,无形中增加了工 ...
- MSP430G2333下位机乘法运算需要注意的一个问题
背景: 最近负责为主板管理电源的电源管理模块编写软体,使用的MCU为MSP430G2333.功能上很简单,即通过板子上的硬件拨码设定,或者通过IIC与主板通信,由主板的BIOS决定开机及关机的延时供电 ...
- vb配置下位机CAN寄存器小结
2011-12-14 18:44:32 效果图 1,完成设计(由于没有eeprom等存储设备,所以每次上电后需要通过串口配置某些寄存器).在设计中,列出技术评估难度,并进行尝试,参看<我的设计& ...
- C# WPF上位机实现和下位机TCP通讯
下位机使用北京大华程控电源DH1766-1,上位机使用WPF.实现了电压电流实时采集,曲线显示.上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟.昨天写的TCP服务端正好排上用场. 界面如下 ...
- linux设备驱动程序-i2c(2)-adapter和设备树的解析
linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 在本系列linux内核i2c框架的前两篇,分别讲 ...
随机推荐
- Angular+Ionic+RSA实现后端加密前端解密功能
因业务需要,需要给android应用安装证书,通过读取证书文件内容实现某些功能的控制: 流程:后台通过publicKey对指定内容的文件进行加密,生成文件共客户下载,客户下载后选择该证书文件读取到应用 ...
- GIT-windows系统下Gitblit的使用方式
GIT-windows系统下Gitblit的正确打开方式 1. 打开页面. 在配置好Gitblit后,打开可视化界面. 2. 创建用户 点击右上角添加用户,进入明细页面,填写常规信息. 创建用户(账号 ...
- H5_0004:JS设置循环debugger的方法
在HTML页面加上如下代码,则PC打开控制台后,就会循环debugger,防止调试代码. <script>eval(function (p, a, c, k, e, r) { e = fu ...
- Arrays和String单元测试 20175301
要求 在IDEA中以TDD的方式对String类和Arrays类进行学习 一.String类相关方法的单元测试 1.ChatAt的测试 代码: import org.junit.Test; impor ...
- 使用fork的str_cli函数
void str_cli(FILE *fp, int sockfd) { pid_t pid; char sendline[MAXLINE], recvline[MAXLINE]; ) { /* ch ...
- npm knowledge basics
npm inro https://www.npmjs.com/ npm is the package manager for javascript npm 为 nodejs默认的包管理工具, 为nod ...
- 实验一 Java环境的搭建&Eclipse的安装
本次实验为在自己电脑上搭建Java环境,熟悉Java的编译和运行环境并安装Eclipse 一.JAVA环境的搭建 1.从Oracle网站上下载Java的jdk [https://www.oracle. ...
- NPOI读取excel表,如果有公式取出的是公式,想要取数字怎么办?
public static DataTable Import(string strFileName) { DataTable dt = new DataTable(); HSSFWorkbook hs ...
- 关于js dtGrid报错长度的问题
错误js截图 Uncaught TypeError: Cannot read property 'length' of undefined 翻译:Uncaught TypeError:无法读取未定义的 ...
- luogu P5302 [GXOI/GZOI2019]特技飞行
传送门 强行二合一可还行 首先\(c\)的贡献是不会变的,先考虑求出多少交点被矩形覆盖,交点的话可以按左端点纵坐标从下到上顺序枚举一条线段,然后维护右端点纵坐标的set,把之前处理过线段的右端点放进s ...