博途数据类型wstring怎么用_解析博图数据块(昆仑通态触摸屏自动命名)
1,博图数据块的数据排列原则:
数据对齐算法:将当前地址对齐到整数:
numBytes = (int)Math.Ceiling(numBytes);将当前地址对齐到偶整数:
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
2,数据对齐的原则,如果当前的数据类型是:bool,则使用当前地址.
byte,则使用对齐方式1
其他,则使用对齐方式2,即偶数对齐的方法.
3,如何从地址和bool进行设定和取值,假设我们有一个byte[]数组代表整个DB,和一个浮点数代表bool的地址?取值:
int bytePos = (int)Math.Floor(numBytes);
int bitPos = (int)((numBytes - (double)bytePos) / 0.125);
if ((bytes[bytePos] & (int)Math.Pow(2, bitPos)) != 0)
value = true;
else
value = false;
赋值bytePos = (int)Math.Floor(numBytes);
bitPos = (int)((numBytes - (double)bytePos) / 0.125);
if ((bool)obj)
bytes[bytePos] |= (byte)Math.Pow(2, bitPos); // is true
else
bytes[bytePos] &= (byte)(~(byte)Math.Pow(2, bitPos)); // is false
思路:获取当前bit所在的字节地址.然后使用 (注意位是从0开始的.位0..位15..位31.)
t=t | 1<
t=t &(~1<
t=t^(1<
t=t&(1<
2,迭代解析
numbytes: 迭代的plc地址
itemInfos:迭代的信息存储的列表
preName:迭代的名称.
ElementItem:用于解析的元素.
public static void DeserializeItem(ref double numBytes, List itemInfos, string preName, ElementItem item)
{
var PreName = (string.IsNullOrEmpty(preName)) ? "" : preName + "_";
var Info = new ItemInfo() { Name = PreName + item.Name, Type = item.Type };
switch (item.GetTypeInfo())
{
case PlcParamType.BaseType:
switch (item.Type)
{
case "Bool":
Info.Addr = ParseAddr(numBytes, item);
numBytes += 0.125;
break;
case "Char":
case "Byte":
numBytes = Math.Ceiling(numBytes);
Info.Addr = ParseAddr(numBytes, item);
numBytes++;
;
break;
case "Int":
case "UInt":
case "Word":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
Info.Addr = ParseAddr(numBytes, item);
numBytes += 2;
;
break;
case "DInt":
case "UDInt":
case "DWord":
case "Time":
case "Real":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
Info.Addr = ParseAddr(numBytes, item);
numBytes += 4;
;
break;
default:
break;
}
itemInfos.Add(Info);
break;
case PlcParamType.String:
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//----------
Info.Addr = ParseAddr(numBytes, item);
numBytes += item.GetStringLength();
itemInfos.Add(Info);
break;
case PlcParamType.Array:
//------------原程序的可能是个漏洞.
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//-------------
var elementType = item.GetElementType();
for (var i = 0; i < item.GetArrayLength(); i++)
{
var element = new ElementItem() { Name = item.Name + $"[{i}]", Type = elementType };
DeserializeItem(ref numBytes, itemInfos, PreName, element);
}
break;
case PlcParamType.UDT:
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//格式
foreach (var element in item.GetElementItems(Dict))
{
DeserializeItem(ref numBytes, itemInfos, PreName + item.Name, element);
}
break;
default:
throw new ArgumentException("do not find type");
}
}
思路: 如果元素的类型是基本类型:比如 bool,int,time...等等,则直接生成一条ItemInfo记录
如果元素的类型是数组,则获取数组的长度和获取数组的元素类型,然后进行迭代解析.
如果元素是UDT类型,也就是自定义的类型.那么就迭代获取元素,并且迭代解析.
----------------------
所有的这一切,源于一个Dictionary>对象.这个对象存储了自定义的类别的名称和其子元素
比如有以下的一个DB导出文件;
则可以看到其有 "step"类型
元素有element1.....type为 int...
还有 "Recipe"类型
还有 "test1"类型
还有一个DataBlock,就是这个数据库,也将它当作一个类型,其type就是"数据块_1"
,其元素就是
名称 类型
Recipe1 Recipe(这个可以在上面找到)
关键是其内部的Struct结构,对于这个结构,我们自定义生成其类型 为 Name_Struct.
TYPE "step"
VERSION : 0.1
STRUCT
element1 : Int;
element2 : Int;
element3 : Int;
element4 : Int;
element5 : Int;
element6 : Int;
element7 : Real;
element8 : Real;
element9 : Real;
element10 : Real;
element11 : Real;
element12 : Real;
element13 : Real;
element14 : Bool;
element15 : Bool;
element16 : Int;
END_STRUCT;
END_TYPE
TYPE "Recipe"
VERSION : 0.1
STRUCT
"Name" : String[20];
steps : Array[0..29] of "step";
END_STRUCT;
END_TYPE
TYPE "test1"
VERSION : 0.1
STRUCT
b : Bool;
END_STRUCT;
END_TYPE
DATA_BLOCK "数据块_1"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
NON_RETAIN
STRUCT
Recipe1 : "Recipe";
AAA : "Recipe";
aa : String;
adef : Struct
a : Bool;
b : Bool;
c : Int;
bool1 : Bool;
bool2 : Bool;
ffff : Struct
ttt : Bool;
aaa : Bool;
fff : Bool;
eefg : Bool;
END_STRUCT;
afe : Bool;
aaaaaaa : "test1";
x1 : "test1";
x2 : "test1";
x3 : Array[0..1] of Byte;
abcef : Array[0..10] of Struct
aef : Struct
Static_1 : Bool;
Static_2 : Bool;
END_STRUCT;
eef : Bool;
affe : Bool;
END_STRUCT;
END_STRUCT;
END_STRUCT;
BEGIN
Recipe1.steps[29].element14 := FALSE;
END_DATA_BLOCK
3,从db文件中读取类信息.
//从db文件中读取类信息,并且放入到Dict之中,同时填充MainBlock信息,如果有的话.
public static void GetTypeFromFile(Dictionary> Dict, string path, out ElementItem MainBlock)
{
MainBlock = new ElementItem();//生成Block对象.
using (Stream st = new FileStream(path, FileMode.Open))//读取文本文件DB块中的数据.
{
StreamReader reader = new StreamReader(st);
List list;
while (!reader.EndOfStream)
{
string line = reader.ReadLine();//读取一行数据进行判断
switch (GetReaderLineTYPE(line))//通过解析函数解析这一行的数据是什么类型.
{
case "TYPE"://如果是类型 对应 db文件里面 TYPE xxxx 的格式
list = new List();//则创建一个列表准备容纳该TYPE的ELementItem,也就是元素集.
string tn = GetReaderLineName(line);//解析type行的名称.也就是该type的名称.
GetElementsFromReader(reader, list, tn, Dict);//然后调用函数进行将元素放入列表,最后哪个Dictionary参数用于接受内联Struct类型.
Dict[tn] = list;//将该类型在字典中生成名值对,也就是Type名称,List作为值.
break;
case "DATA_BLOCK":
MainBlock = new ElementItem();
string bn = GetReaderLineName(line);
MainBlock.Name = bn;
MainBlock.Type = bn;
list = new List();
GetElementsFromReader(reader, list, bn, Dict);//如果是DB块,则填充Main Block(备用),剩下的根上面一样).
Dict[bn] = list;
break;
default:
break;
}
}
}
}
4, 辅助的读取迭代函数,实现的关键...
public static void GetElementsFromReader(StreamReader reader, List list, string type_name, Dictionary> Dict)
{
ElementItem item;
Tuple tp;
while (!reader.EndOfStream)
{
string line = reader.ReadLine();//首先,其必须是一个解析Type或者DataBlock的过程,因为只有这两处调用它.
switch (GetReaderLineTYPE(line))//解析每行的类别.
{
case "ELEMENT"://当解析到该行是元素的时候.就将该行加入到列表中.
item = new ElementItem();
tp = GetElementInfo(line);
item.Name = tp.Item1;
item.Type = tp.Item2;
list.Add(item);
break;
case "StructELEMENT"://当解析到该行是Struct时,也就是元素类型时Struct的时候,
item = new ElementItem();
tp = GetElementInfo(line);
item.Name = tp.Item1;
item.Type = tp.Item2.Remove(tp.Item2.LastIndexOf("Struct"));//由于Array Struct的存在,将该元素类型从....Struct
//替换为 .... elementName_Struct
item.Type = item.Type + type_name + "_" + item.Name + "_" + "Struct";//
string structType = type_name + "_" + item.Name + "_" + "Struct";//该名称为其新的类别.
list.Add(item);//首先将该元素加入列表.
List sub = new List();//将该子类别(我们自定义的类别,添加到字典中.
GetElementsFromReader(reader, sub, structType, Dict);
Dict[structType] = sub;
break;
case "END_STRUCT"://当接受到这个时,表明一个Type或者一个DataBlock的解析工作结束了,返回上层对象.
return;
default:
break;
}
}
}
5,工具函数1,用于帮助判断DB文件每行的信息.
private static Tuple GetElementInfo(string line)
{
if (!GetReaderLineTYPE(line).Contains("ELEMENT")) throw new Exception("this line is not element" + line);
int pos = line.IndexOf(":");
string Name = line.Remove(pos).Trim(' ', ';');
var t = Name.IndexOf(' ');
if (t > 0)
Name = Name.Remove(t);
string Type = line.Substring(pos + 3).Trim(' ', ';');
if (Type.Contains(":=")) Type = Type.Remove(Type.IndexOf(":="));
return new Tuple(Name, Type);
}
private static string GetReaderLineName(string line)
{
if ((GetReaderLineTYPE(line) != "TYPE") && (GetReaderLineTYPE(line) != "DATA_BLOCK")) throw new Exception("not read name of" + line);
return line.Substring(line.IndexOf(' ')).Trim(' ');
}
private static string GetReaderLineTYPE(string line)
{
//Console.WriteLine(line);
if (line.Contains("TYPE")) return "TYPE";
if (line.Contains("DATA_BLOCK")) return "DATA_BLOCK";
if (line.Contains("END_STRUCT;")) return "END_STRUCT";
if (line.Trim(' ') == "STRUCT") return "STRUCT";
if (line.EndsWith("Struct")) return "StructELEMENT";
if ((line.EndsWith(";"))) return "ELEMENT";
return null;
}
6,从之前生成的字典和MainDataBlock中生成 List也就是展开DB块信息.
public static void DeserializeItem(ref double numBytes, List itemInfos, string preName, ElementItem item)
{
var PreName = (string.IsNullOrEmpty(preName)) ? "" : preName + "_";//如果前导名称不为空,则添加到展开的名称上去.
//这里是个bug,最后将这行放到string生成,或者BaseType生成上,因为会出现多次_
var Info = new ItemInfo() { Name = PreName + item.Name, Type = item.Type };
switch (item.GetTypeInfo())//解析这个元素的类别
{
case PlcParamType.BaseType://基类直接添加.
switch (item.Type)
{
case "Bool":
Info.Addr = ParseAddr(numBytes, item);
numBytes += 0.125;
break;
case "Char":
case "Byte":
numBytes = Math.Ceiling(numBytes);
Info.Addr = ParseAddr(numBytes, item);
numBytes++;
;
break;
case "Int":
case "UInt":
case "Word":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
Info.Addr = ParseAddr(numBytes, item);
numBytes += 2;
;
break;
case "DInt":
case "UDInt":
case "DWord":
case "Time":
case "Real":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
Info.Addr = ParseAddr(numBytes, item);
numBytes += 4;
;
break;
default:
break;
}
itemInfos.Add(Info);
break;
case PlcParamType.String://string类型.直接添加
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//----------
Info.Addr = ParseAddr(numBytes, item);
numBytes += item.GetStringLength();//如果是string,则加256,否则加 xxx+2;
itemInfos.Add(Info);
break;
case PlcParamType.Array://数组则进行分解,再迭代.
//------------原程序的可能是个漏洞.
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//-------------
var elementType = item.GetElementType();
for (var i = 0; i < item.GetArrayLength(); i++)
{
var element = new ElementItem() { Name = item.Name + $"[{i}]", Type = elementType };
DeserializeItem(ref numBytes, itemInfos, PreName, element);
}
break;
case PlcParamType.UDT://PLC类型,进行分解后迭代.
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
//格式
foreach (var element in item.GetElementItems(Dict))
{
DeserializeItem(ref numBytes, itemInfos, PreName + item.Name, element);
}
break;
default:
throw new ArgumentException("do not find type");
}
}
注意,每条信息组成:(s
public class ItemInfo
{
public ItemInfo()
{
}
public string Name { get; set; }//element的名称
public string Type { get; set; }//element的类别(基类别,比如字符串,byte,bool之类.
public object Addr { get; internal set; }//地址;比如dbx0.0之类.//综合就是给出 PLC变量名 PLC变量类型 PLC变量地址 的一个列表.
}
7,功能扩展,支持多个db文件的解析操作
// MainFunction to read message from dbs.
//迭代解析 多个db文件.
public static void GetDBInfosFromFiles(string path, Dictionary> Dict, Dictionary> DataBlocks)
{
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] files = dir.GetFiles("*.db");
List blocks = new List();
foreach (var file in files)//将每个文件的db块加入到 db数组中,再将解析的TYPE都加入到字典中.
{
ElementItem MainBlock;
GetTypeFromFile(Dict, file.FullName, out MainBlock);
if (MainBlock != null)
{
MainBlock.Name = file.Name.Remove(file.Name.IndexOf('.'));
blocks.Add(MainBlock);
}
}
foreach (var block in blocks)//然后迭代解析每个DB块的信息,将求加入到第二个字典中.
{
double numBytes = 0.0;
List itemInfos = new List();
DeserializeItem(ref numBytes, itemInfos, "", block);
DataBlocks[block.Name] = itemInfos;
}
}
8,使用CSVHELPER类从昆仑通态导出的文件中来读取信息
public static MacInfos[] GetCSVInfosFromFile(string path,string[] infos)
{
//用csvhelper类读取数据:注意,必须用这个方法读取,否则是乱码!
using (var reader = new StreamReader(path, Encoding.GetEncoding("GB2312")))
{
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
//读取4行无效信息.............
csv.Read();
infos[0] = csv.GetField(0);
csv.Read();
infos[1] = csv.GetField(0);
csv.Read();
infos[2] = csv.GetField(0);
csv.Read();
infos[3] = csv.GetField(0);
//读取所有的数据放入一个数组中.标准用法.
var records = csv.GetRecords().ToArray();
return records;
}
}
}
9,将CSV文件的变量名进行填充,即将 Dict之中的变量名填充到 CSV的变量名之中.
//用于将填充完的信息数组反写回csv文件.
public static void WriteIntoCSVOfMAC(string path)
{
string csvpath = FindFiles(path, "*.csv").First();//找到csv文件.
string[] strinfos = new string[4];//填充无效信息的4行
MacInfos[] macInfos = GetCSVInfosFromFile(csvpath,strinfos);//获取MacInfos数组和无效信息字符串数组.
foreach(var key in DBInfos.Keys)//轮询每个DB块.
{
var infos = (from info in macInfos
where GetDBFromMacInfo(info) == key.Remove(key.IndexOf('_'))
select info).ToArray();//将找到的Macinfo中再去查找对应的DB块的Macinfos[]数组.
WriteDBToMacInfos(key, infos);//然后将对应的db块的信息,(找到其中的元素的名称,一一写入对应infos的变量名之中.
}
//填充完所有的Macinfos数组后,将这个数组反写回CSV文件.
using (var writer = new StreamWriter(new FileStream(csvpath, FileMode.Open), Encoding.GetEncoding("GB2312")))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
//将原来的无效信息反填充回去.
csv.WriteField(strinfos[0]);
csv.NextRecord();//转移到下一行.去读写数据.
csv.WriteField(strinfos[1]);
csv.NextRecord();
csv.WriteField(strinfos[2]);
csv.NextRecord();
csv.WriteField(strinfos[3]);
csv.NextRecord();
//然后填充数组.
csv.WriteRecords(macInfos);
}
}
10,结论:
1,在使用的时候,首先导出DB块的块生成源,
2,然后将所有的*.db文件们放入c:\macfile文件夹之中.
3,使用昆仑通态软件导入标签功能,导入db块和导入utc块,将变量导入到软件中,这个时候,变量名一栏是空的.
4,使用导出设备信息,将导出一个csv文件.
5,运行小软件.结束.
附上Git地址和我的邮箱地址,
有喜欢工控软件开发的多多沟通交流.
博途数据类型wstring怎么用_解析博图数据块(昆仑通态触摸屏自动命名)相关推荐
- 博途数据类型wstring怎么用_西门子博图数据类型说明
数据类型 数据类型用于指定数据元素的大小以及如何解释数据.每个指令参数至少支持一种数 据类型,而有些参数支持多种数据类型.将光标停在指令的参数域上方,便可看到给定参 数所支持的数据类型. 形参指的是指 ...
- 博途数据类型wstring怎么用_如何在STEP 7 (TIA 博途)中使用“用户定义数据类型” (UDTS)...
说明 创建一个 PLC 数据类型,在项目导航中打开" PLC 数据类型"文件夹并双击"添加新数据类型".新创建的 PLC 数据类型将分配一个默认名称.如果想更改 ...
- 博途数据类型wstring怎么用_在 STEP 7 (TIA 博途) 中,如何使用用户自定义数据类型 (UDT)?...
描述本条目将阐述"用户自定义数据类型(UDT)的应用"及在STEP 7 V5.x 和 STEP 7 (TIA 博途) 不同的处理方法. 用户的目标在许多程序中需要使用连续的数据记录 ...
- 博途数据类型wstring怎么用_博图V14关于自定义数据类型的疑问,大家都是怎么编程写中间标签的那。...
由于单位最近要搞标准化编程.我负责编写程序块.用AB写程序的套路编写西门子博图V14的块. 1,建立自定义数据类型. 2,编写要调用的程序块.如果设备不多建议用FB生成背景数据库,又能保持定时器以及各 ...
- MCGS昆仑通态触摸屏导入博途自定义数据类型和DB块变量的具体方法演示
MCGS昆仑通态触摸屏导入博途自定义数据类型和DB块变量的具体方法演示 如下图所示,在博途中新建项目后,添加自己所需的数据类型,然后选中该数据类型,右击选择"从块生成源"-&quo ...
- 西门子PLC1200伺服库卡机器人12工位博图程序例程,组态采用昆仑通态触摸屏
西门子PLC1200伺服库卡机器人12工位博图程序例程,组态采用昆仑通态触摸屏,详细中文注释,PDF电路图参考,设备操作说明,物料BOM ,PLC和一台库卡机器人profinet通讯 PTO模式控制松 ...
- 博途v15模拟量转换_基于博途V15 西门子S7-1200 + 模拟量SM 1234 正反转变频调速实例...
一.准备工作 所需设备:西门子CPU 1215C AC/DC/Rly6ES7 215-1BG40-0XB0 SEW变频器MDX61B+通信模块DFE32B SM12346ES7 234-4HE32-0 ...
- 博途创建vb脚本实例_基于博途V15 西门子S7-1200 定时器指令应用实例
本篇是<基于博途V15 西门子S7-1200 ...>系列的后续篇,看懂本篇文章之前请大家关注我,然后查找相关文章,学习之后再学习此篇.也请专业领域的大神批评指正. 感谢大家关注与支持! ...
- 昆仑通态触摸屏数据转发上传_嵌_ModbusTcpIp数据转发 昆仑通态屏与屏之间通讯 - 下载 - 搜珍网...
嵌_ModbusTcpIp数据转发/通_通/发送方.MCG 嵌_ModbusTcpIp数据转发/通_通/接收方.MCG 嵌_ModbusTcpIp数据转发/通_通/驱动路径说明.txt 嵌_Modbu ...
最新文章
- Single Shot Multibox Detection (SSD)实战(上)
- [转]数据结构:图的存储结构之邻接多重表
- 网络连接、路由配置等
- Xilinx ISE 开发过程中生成的各种文件(二)
- ewebeditor编辑器ASP/ASPX/PHP/JSP版本漏洞利用总结及解决方法
- python能做游戏吗-没有Python不能做的游戏,这些游戏都可以做
- 防抖debounce和节流throttle
- 前端js获取图片大小 扩展名_前端 JS 获取 Image 图像 宽高 尺寸
- Java中的字符串分割
- recect build 打包发布后访问出现404错误的简易解决方法
- 【获奖公布】元老选手心得分享:请把自己看成一位出色的工程师
- PicoDet的学习笔记
- linux中csh怎么运行,bash csh 设置环境变量 方法例子
- EditPlus4.0汉化+破解注册
- 腾讯校园招聘--一面(技术1对1)面经
- php后台如何添加sitemap,织梦后台的sitemap生成及推送教程
- 什么是股票情绪量化指标?
- SQL server 两表联查及三表联查
- 【爱生活之咖啡】咖啡入坑记--咖啡豆的那些事
- C语言 计算cosx的近似值
热门文章
- 怎样升级android10版本,手机怎么升级win10系统 win10手机版升级教程
- mysql如何撤销上一条指令_mysql命令行,多行命令时如何取消/返回修改前边的命令...
- Nginx 是如何实现高并发?常见的优化手段有哪些?
- Springboot启动扩展点超详细总结,再也不怕面试官问了
- 再涨个姿势,我们常用的撤销和恢复功能,你知道它们使用了什么设计模式实现吗?...
- Java多线程学习四十二:有哪些解决死锁问题的策略和哲学家就餐问题
- SQL Server 2008 R2 安装
- linux 编译 连接失败,编译linux-3.15.5时遇到的几个错误
- Mybatis SQL 语句中 IF函数不支持
- git安装和GitHub使用