【博图TIA-Api】通过Excel自动快速导入IO变量
说明
IO变量自动导入其实节省的时间其实并不多,主要是HMI里的IO注释同样可以修改,这里主要指西门子自己的触摸屏,里面的文本列表同样可以自动修改。因为原理差不多,这里先介绍从过Excel导入IO变量。
思路
- 调用TIA的Api接口
- 实现方式为C#的“Windows 窗体应用(.NET Framework),(默认PC已经装了Visual Studio)
- IO变量存在Excel文件中,导入时需要选择文档
- 西门子子接口使用文档(中文的) 点击链接进入
准备
添加引用
Microsoft.Office.Interop.Excel添加在引用中
可以在Visual Studio中【工具】-【Nuget】包管理器-【管理解决方案的Nuget程序包】-【浏览】
因为博图里对于程序文件的保存时xml文件,所以要使用关于操作Xml的引用,不过好像新建项目时会自动引用,最终结果如下即可。
程序的using可直接复制以下代码块,就不怕缺哪项没有了
using Siemens.Engineering;
using Siemens.Engineering.Hmi.Tag;
using Siemens.Engineering.HW;
using Siemens.Engineering.HW.Features;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.SW.Tags;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using Microsoft.Office.Interop.Excel;
using Siemens.Engineering.Hmi;
using Siemens.Engineering.Hmi.TextGraphicList;
using TIA16Api.Properties;
using System.Runtime;
using System.Xml.Linq;
连接项目(返回Device)
C#项目要必须连接当前的项目,然后才能获取项目的的Divice。
注意这里是默认博图软件已经打开,并且打开了项目,如何打开项目可以看这里 【博图TIA-Api】自动填充程序账号密码打开项目
一般设备分为PLC和HMI,所以程序读上来第一个是PLC,第二个是HMI。对应的数组0是PLC,1是HMI。但如果一个项目里有多个PLC和HMI就不一定了。
因此这个函数我增加了一个整型变量输入,想用PLC就输入0,想用HMI就输入1。
注意这里返回的是Device类型
public static Device ReadNowTIADevice(int Number)
{
// 获取正在运行的TIA Portal实例
IEnumerable<TiaPortalProcess> processes = TiaPortal.GetProcesses();
//定义临时项目
Project project;
// 连接到正在运行的TIA Portal实例,目前只能打开一个,暂时未做分配
TiaPortal tiaPortal = processes.First().Attach();
// 获取项目,目前只能获取一个,暂时未做分配
project = tiaPortal.Projects.FirstOrDefault();
//获取设备
Device device = project.Devices[Number];
return device;
}
获取当前Device的PLC(返回PlcSoftware)
从Device获取PlcSoftware,只有获取了PlcSoftware的类型,才能获取程序里的一切内容
#region 获取当前Device的PLC
// Returns PlcSoftware
public static PlcSoftware GetPlcSoftware(Device device)
{
DeviceItemComposition deviceItemComposition = device.DeviceItems;
foreach (DeviceItem deviceItem in deviceItemComposition)
{
SoftwareContainer softwareContainer = deviceItem.GetService<SoftwareContainer>();
if (softwareContainer != null)
{
Software softwareBase = softwareContainer.Software;
PlcSoftware plcSoftware = softwareBase as PlcSoftware;
return plcSoftware;
}
}
return null;
}
#endregion
导入IO变量表
注意导入变量表是Xml文件,具体怎么修改后面介绍,现在只要知道这个函数调用就可以把我们修改后的xml文件导入PLC程序。
#region 导入变量表
public static void ImportTagTable(PlcSoftware plcSoftware, string filepath, bool Enbale)
{
if (!Enbale)
{
return;
}
PlcTagTableUserGroupComposition plcTagTableUserGroupComposition = plcSoftware.TagTableGroup.Groups;
PlcTagTableUserGroup UseTagUserFolder = null;
//找到层级一文件夹
PlcTagTableUserGroup controllerTagUserFolder = plcTagTableUserGroupComposition.Find("层级一");
//在层级一的文件夹内找层级二的文件夹
foreach (PlcTagTableUserGroup plcTagTableUserGroup in controllerTagUserFolder.Groups)
{
if (plcTagTableUserGroup.Name == "层级二")
{
UseTagUserFolder = plcTagTableUserGroup;
}
}
PlcTagTableComposition tagTables = UseTagUserFolder.TagTables;
//导入变量表
tagTables.Import(new FileInfo(filepath), ImportOptions.Override);
// Or, to import into a subfolder:
// plcTagTableSystemGroup.Groups.Find("SubGroup").TagTables.Import(new FileInfo(@"D:\Samples\myTagTable.xml"), ImportOptions.Override);
}
#endregion
注意:因为要修改的PLC变量这里在两个文件夹下,所以必须要表明路径。
获取Excel文档路径
这里可以关联一个按钮的点击事件,同时定义一个全局变量存储路径
string ExcelPath;
#region 选择文档
private void ChoseExcel_Click(object sender, EventArgs e)
{
OpenFileDialog filepath = new OpenFileDialog();
filepath.Title = "请选择项目";
filepath.Filter = "(*.xlsx)|*.xlsx";
filepath.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
//获取选中的文件路劲
if (filepath.ShowDialog() == DialogResult.OK)
{
if (filepath.FileName != null)
{
ProjectPathName.Text = filepath.FileName;
ExcelPath = ProjectPathName.Text;
}
else
{
MessageBox.Show("文档路径获取失败!");
}
}
}
#endregion
读取Excel文件
现在开始处理Excel文件,可以按我的格式默认配置
由于Excel里有多个变量表的变量,所以这个函数后面有一个输入的整型方便多次调用,返回一个存在IO的数组。
注意PLC程序的变量名不能重复,如果重复会报错的,因此填表的时候要注意。
#region 读取Excel文件里的IO数据,第一列名字,第二列地址,第三列注释
public static string[,] ReadExcelFile(string filePath, int TypeNum)
{
//
string[,] result = new string[1000, 4];
// 创建Excel对象
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = excel.Workbooks.Open(filePath);
// 获取第一个工作表
Worksheet worksheet = workbook.Sheets[1];
// 获取单元格值
Range range = worksheet.UsedRange;
int rowCount = range.Rows.Count;
int colCount = range.Columns.Count;
int k;
for (int i = 1; (range.Cells[i + 2, TypeNum] as Range).Value != null; i++)
{
result[i, 1] = (range.Cells[i + 2, TypeNum] as Range).Value.ToString();
result[i, 2] = (range.Cells[i + 2, TypeNum + 1] as Range).Value.ToString();
if ((range.Cells[i + 2, TypeNum + 2] as Range).Value != null)
{
result[i, 3] = (range.Cells[i + 2, TypeNum + 2] as Range).Value.ToString();
}
k = i;
}
// 关闭Excel对象
workbook.Close();
excel.Quit();
return result;
}
#endregion
导出XML文件
导出PLC程序内的xml文件有两种方式
- 版本控制接口导出
- 接口导出(导出方式不如第一个)
版本控制接口导出
首先新建工作区
按顺序选择导出的路径
成功导出后如图
接口导出
XML文件分析(关键部分)
此部分也是最复杂的部分,必须分析西门子对于XML文件的格式
注意以下的所有程序都位于一个函数内,总代码放到最后。
变量表名字的修改
打开XML文件,可以清楚看到,变量表名字位于【Document】-【SW.Tags.PlcTagTable ID=“0”】-【AttributeList】-【Name】中
我们要修改变量表名字就需要修改这个Name节点里的文本
XmlDocument xmlDoc = new XmlDocument();
//加载xml文件,文件路径
xmlDoc.Load(XmlFileName);
//查找要修改的节点
XmlNode Name = xmlDoc.SelectSingleNode("Document/SW.Tags.PlcTagTable/AttributeList");
//取出所有的子节点
XmlNodeList xnl = Name.ChildNodes;
//取出PLC变量表名字
foreach (XmlNode xmlNode in xnl)
{
//将节点转换一下类型
XmlElement xmlElement = (XmlElement)xmlNode;
//判断该子节点是否是要查找的节点
if (xmlElement.Name == "Name")
{
//设置新值,也就是定义的变量表名字,因为我有多个变量表导入,所以我用字符串变量定义了
xmlElement.InnerText = FileName;
break;
}
}
#endregion
变量内容的添加
可以告诉大家,所有的IO变量内容都在这个位置。
因为我们从Excel导入IO,所变量有原来的内容都要清干净。
//查找要修改的节点
XmlNode xns = xmlDoc.SelectSingleNode("Document/SW.Tags.PlcTagTable/ObjectList");
//移出当前所有改节点下子节点
xns.RemoveAll();
【ObjectList】下每一个变量的内容都是如下格式,因为他们之间是有规律的,所以只要按规律修改,再导入PLC程序中就能自动填充变量。
AttributeList
【AttributeList】内具体规律如下
因为是十六进制,所以要转换一下
#region 十进制转十六进制
public static string DecimalToHex(int decimalNum)
{
string hexStr = "";
while (decimalNum > 0)
{
int remainder = decimalNum % 16;
if (remainder >= 10)
{
hexStr = (char)('A' + remainder - 10) + hexStr;
}
else
{
hexStr = remainder + hexStr;
}
decimalNum /= 16;
}
return hexStr;
}
#endregion
具体XML文件修改代码如下
str = DecimalToHex(i);
XmlElement xe1 = xmlDoc.CreateElement("SW.Tags.PlcTag");//创建一个节点
xe1.SetAttribute("ID", str);//设置该节点ID属性
xe1.SetAttribute("CompositionName", "Tags");//设置该节点CompositionName属性
#region AttributeList
XmlElement xe1_1 = xmlDoc.CreateElement("AttributeList");
xe1.AppendChild(xe1_1);
XmlElement xe1_1_1 = xmlDoc.CreateElement("DataTypeName");
xe1_1_1.InnerText = "Bool";
xe1_1.AppendChild(xe1_1_1);
XmlElement xe1_1_2 = xmlDoc.CreateElement("ExternalAccessible");
xe1_1_2.InnerText = "true";
xe1_1.AppendChild(xe1_1_2);
XmlElement xe1_1_3 = xmlDoc.CreateElement("ExternalVisible");
xe1_1_3.InnerText = "true";
xe1_1.AppendChild(xe1_1_3);
XmlElement xe1_1_4 = xmlDoc.CreateElement("ExternalWritable");
xe1_1_4