与下位机或设备的通信解析优化的一点功能: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框架的前两篇,分别讲 ...
随机推荐
- 永续公债(or统一公债)的麦考利久期(Macaulay Duration)的计算
- IDEA中debug启动tomcat报错。Error running t8:Unable to open debugger port(127.0.0.1:49225):java.net.BindException"Address alread in use:JVM_Bind"
解决办法: 1,如下图打开项目配置的tomcat的“Edit Configurations...” 2,打开“Startup/Connection”--------"Debug"- ...
- dsp实验一 常见问题教程
1.选择合适阅读材料:卖油翁.将进酒,等. 2.录音设备三选一:某种电脑声卡+线上convert/楼月mp3录音软件+格式工厂(我选的)/Matlab (注:这是目前已知的方法,我只用了第二个) 3. ...
- tomcat设置为开机自启动
第一步:设置环境变量(在java环境变量配置完成的情况下) 计算机右键——>属性——>高级系统设置——>环境变量——>在用户变量中心新建CATALINA_HOME变量 编辑pa ...
- nginx反向代理配置两个不同服务器
1.什么是反向代理通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中由代理服务器向Inter ...
- Python 数据分析4
本章概要 数据加载.存储与文件格式 数据加载.存储与文件格式 读取文本格式数据 read_csv 默认是按照逗号分割,也可设定其他分割符 df = pd.read_csv('file', sep='| ...
- Python中区分函数和方法
1.简单粗暴型: def func(): ... class Foo: def eat(self): print("吃") f = Foo() print(func) #<f ...
- 十个推荐使用的 Laravel 的辅助函数
壹. array_dot() array_dot () 辅助函数允许你将多维数组转换为使用点符号的一维数组. $array = [ 'user' => ['username' => 'so ...
- vertical-align作用的前提++图片不上下居中
5.3.2 vertical-align作用的前提 很多人,尤其CSS新手,会问这么一个问题:“为什么我设置了vertical-align却没任何作用?” 因为vertical-align起作用是有前 ...
- 高可用Redis(十三):Redis缓存的使用和设计
1.缓存的受益和成本 1.1 受益 1.可以加速读写:Redis是基于内存的数据源,通过缓存加速数据读取速度 2.降低后端负载:后端服务器通过前端缓存降低负载,业务端使用Redis降低后端数据源的负载 ...