TIA Portal Openness实战:用C#脚本批量创建DB块,终结PLC重复劳动
2026/9/20 18:47:02 网站建设 项目流程

简介:西门子Openness自动生成小示例是一份面向自动化工程师的C#参考项目,重点演示如何通过TIA Portal的Openness接口自动生成PLC工程,减少人工重复配置,适合有PLC基础并希望提升开发效率的工程师。资源共421个文件,压缩包仅1.28MB,内含312个PNG图片文件、72个C#源文件、20个XAML界面布局文件,以及配置、图标等,方便查看运行界面与操作效果,可完整还原一个WPF工具从界面到后台的实现结构。已有598人学习/下载,在智能制造和工业4.0背景下,具有较高的二次开发参考价值。通过这个项目可系统学习启动检查、版本验证、界面交互到自动生成项目的完整思路,直接对照源码与图片进行修改或扩展,有助于理解Openness API调用方式并加速实际项目的落地。 干了这么多年自动化项目,我越来越觉得最磨人的不是程序逻辑本身,而是那些纯重复的体力活。尤其是TIA Portal里批量建DB块这件事:一台设备拆出来三四十个工位,每个工位都要一个数据块,变量表明明就在Excel里躺着,你却还得手动在TIA里一个个建、一个个敲类型,敲完还要检查有没有敲错。这种活干一次就够了,第二次我就老老实实把西门子Openness捡起来用。

Openness说白了就是西门子开放给TIA Portal的外部编程接口,用C#写个小程序就能自动创建项目、生成程序块、批量建DB、改设备组态,甚至可以在OB1里自动插入调用网络。今天分享的这个最小示例,就是用C#写一个自动化脚本,从打开TIA项目到自动生成DB块再到保存编译,完整走一遍。不管你是被批量建DB折磨的调试工程师,还是想把PLC标准化交付流程提效的研发人员,这篇都能给你一个能直接改来用的底子。

1. Openness到底能干什么:先弄清楚它解决什么问题

1.1 批量建DB、自动生成代码块,这是Openness的主战场

Openness在官方文档里的定义是"TIA Portal的开放接口",但真正用过之后你会理解,它的核心价值就八个字:把人工重复操作变成脚本调用。

我们做非标设备项目的都懂,程序结构其实高度雷同:每个气缸配一组到位/复位信号,每个阀配一组反馈,每台变频器配一组状态字。几十个工位就代表几十份几乎一样的DB结构。手动建DB,点十几次鼠标建一个,建完还要配置变量类型、默认值,稍微不注意类型写错,PLC一编译就是一片红。而用Openness,我只需要维护一份Excel变量表,脚本读进去,循环创建,几十个DB几分钟全出来,类型、默认值、注释全按模板走,人类手动操作容易犯的错,脚本一个都不会犯。

除了建DB,Openness还能干这些事:自动生成FB/FC/OB代码块的XML骨架、批量修改PLC变量表、导入导出整个项目或单个块、通过编译接口做项目二次编译校验。很多人拿它做程序版本管理,把块导出成XML存到Git里,配合脚本实现PLC程序的自动化差异对比,这套打法在标准化程度高的大厂里已经很成熟了。

1.2 别指望它万能:Openness的适用边界

Openness再强,它也不是万能的。首先,它干不了复杂的业务逻辑,你不可能用C#把一套PID算法直接写进FB的SCL里,虽然理论上XML里能嵌SCL源码,但实际维护成本极高,不如直接手写逻辑块。其次,它做不了在线诊断和实时通讯,比如你想通过Openness读取PLC当前运行状态、强制变量,那是另一个赛道(TIA的OPC UA或西门子S7通讯协议),Openness只负责工程组态层面的事。

我的判断标准很简单:凡是你能在TIA里用"复制粘贴+改名字"完成的操作,就值得用Openness自动化;凡是需要现场调试动态判断的,Openness帮不上忙。把精力放在前一类场景上,收益最明显。

2. 动手前把这些配好:版本、环境和工程设置

2.1 版本匹配是第一道坎,跨版本基本没得商量

Openness不是独立软件,它必须和TIA Portal主程序完全同版本使用。比如你电脑装的是TIA V17,下载Openness安装包时就只能认准V17对应的版本,装完DLL路径通常在安装目录下的PublicAPI子文件夹里,里面会按接口版本再分一层目录。

这个版本对应关系是社区里问得最多的问题,因为一旦版本对不上,最常见的报错就是"无法加载Siemens.Engineering.dll"或者各种类型加载异常。我踩过一次跨版本调用:拿V15的DLL去连V16的项目,程序启动直接抛异常,折腾了一下午才反应过来是版本问题。所以装之前务必去TIA Portal的安装目录下确认PublicAPI的版本子目录和你的TIA一致。

开发环境方面,我用的方案是Visual Studio + .NET Framework 4.8,TIA V17对应Openness v6,V15/V16对应v5相关版本。C#控制台应用就够用,不需要上WPF,除非你想做带界面的自动化工具。注意项目平台的Target框架别选错了,选成.NET Core是引不了Siemens.Engineering.dll的,这DLL只兼容.NET Framework。

2.2 工程项目的正确打开方式

Openness调用TIA项目,项目路径本身有讲究。开发阶段我强烈建议用项目的副本练手,别直接拿现场在用的项目测试。因为Openness操作过程中一旦脚本异常退出,项目文件可能会停留在被占用状态,严重时甚至会损坏项目结构。我一般会在D盘准备一个专门的测试目录,里面放一个精简的测试项目,所有调试都在这个环境里跑。

还有一个容易忽视的点:TIA Portal项目本身有版本概念,V17的软件打开V15的项目也需要做升级兼容操作。Openness同样不例外,它打开的低版本项目会在内存里做一次自动升级,处理时间明显变长,而且如果项目结构过于复杂,升级过程可能报错。所以测试项目最好用和当前Openness版本完全一致的TIA版本创建的。

另外,运行Openness脚本的Windows账号最好有管理员权限。虽然平时操作不需要管理员,但Openness的COM组件注册、防火墙放行这几步都很容易受权限影响,直接用管理员身份跑控制台程序,能省掉一批莫名其妙的权限报错。

2.3 有界面还是无界面:两种运行模式怎么选

Openness提供两种启动模式:带界面的WithUserInterface和后台运行的WithoutUserInterface。

带界面模式启动后你能看到一个TIA Portal窗口自己打开、自己操作,非常适合调试阶段。我建议新人第一次跑脚本一定用这个模式,你肉眼能看到脚本执行到哪一步,卡住了也知道卡在什么地方。无界面模式则完全是后台静默运行,适合正式交付给同事使用的场景,界面不弹出来,也避免有人误点窗口导致操作中断。

两种模式在代码里只差一个枚举参数,但有个细节要留意:无界面模式不是真的没有任何窗口,它还是会起一个TIA的进程,只是界面层被隐藏了。而且这个进程很占内存,我见过一个中型项目跑下来,TIA进程吃掉2GB内存都很正常,所以脚本跑完记得要释放资源、关闭进程。

3. 最小闭环:自动创建一个DB块

3.1 连接TIA Portal并打开项目

先放一段最基础的C#代码,完成"启动TIA+打开项目"这步。这段代码是后面所有操作的地基,建议直接新建控制台项目跑一遍。

using System; using Siemens.Engineering; using Siemens.Engineering.SW; using Siemens.Engineering.SW.Blocks; class OpennessDemo { static void Main(string[] args) { // 创建TIA Portal实例,WithUserInterface方便观察运行过程 TiaPortal portal = new TiaPortal(TiaPortalMode.WithUserInterface); // 打开测试项目 Project project = portal.Projects.Open(@"D:\OpennessTest\DemoProject\Demo.ap17"); // 找到PLC设备,注意设备名称要和项目树里的完全一致 Device device = project.Devices.Find("PLC_1"); DeviceItem cpuItem = device.DeviceItems.Find("PLC_1"); // 获取PLC软件对象,后面建块全靠它 PlcSoftware plcSoftware = cpuItem.GetService<PlcSoftware>(); Console.WriteLine("项目打开成功,PLC名称: " + plcSoftware.Name); // 先不释放资源,方便观察TIA窗口 Console.ReadKey(); } }

注意几个关键点:

  • project.Devices.Find()里传的设备名必须和TIA项目树里的完全一致,大小写都不能错,否则会抛找不到设备的异常。不清楚名字的话,可以先在TIA里手动打开项目看一眼。
  • GetService ()是获取PLC编程软件服务的通用入口,DB、FB、FC、OB都在这个服务的对象模型下。
  • 引用Siemens.Engineering.dll时,如果Visual Studio提示找不到命名空间,多半是版本引用问题,检查一下项目框架是否.NET Framework,以及DLL路径是否选对。

3.2 用XML模板拼DB内容并写入

Openness创建DB块的标准方式是用CreateBlockFromString方法,把DB块的XML内容以字符串方式传进去。这个XML结构有固定格式,但手写容易出错,我的经验是:第一次用TIA手动建一个目标结构的DB,然后导出成XML,用它做模板改。

下面是一个最小的全局DB XML模板,包含两个静态变量,一个Int类型,一个Real类型:

<?xml version="1.0" encoding="utf-8"?> <SW.Blocks.DB ID="0" CompositionName="Blocks"> <ObjectList> <SW.Blocks.CompileUnit ID="1" CompositionName="CompileUnit"> <AttributeList> <ProgrammingLanguage>DB</ProgrammingLanguage> <Header> <Title>MyTestDB</Title> </Header> </AttributeList> </SW.Blocks.CompileUnit> </ObjectList> <AttributeList> <Interface> <Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v5"> <Section Name="Static"> <Member Name="Temperature" Datatype="Int"> <AttributeList><StartValue>0</StartValue></AttributeList> </Member> <Member Name="Pressure" Datatype="Real"> <AttributeList><StartValue>0.0</StartValue></AttributeList> </Member> </Section> </Sections> </Interface> </AttributeList> </SW.Blocks.DB>

C#里用字符串拼接的方式传入:

string dbXml = @"<?xml version=""1.0"" encoding=""utf-8""?> <SW.Blocks.DB ID=""0"" CompositionName=""Blocks""> <ObjectList> <SW.Blocks.CompileUnit ID=""1"" CompositionName=""CompileUnit""> <AttributeList> <ProgrammingLanguage>DB</ProgrammingLanguage> <Header><Title>MyTestDB</Title></Header> </AttributeList> </SW.Blocks.CompileUnit> </ObjectList> <AttributeList> <Interface> <Sections xmlns=""http://www.siemens.com/automation/Openness/SW/Interface/v5""> <Section Name=""Static""> <Member Name=""Temperature"" Datatype=""Int""> <AttributeList><StartValue>0</StartValue></AttributeList> </Member> <Member Name=""Pressure"" Datatype=""Real""> <AttributeList><StartValue>0.0</StartValue></AttributeList> </Member> </Section> </Sections> </Interface> </AttributeList> </SW.Blocks.DB>"; BlockGroup blockGroup = plcSoftware.BlockGroup; Block newBlock = blockGroup.Blocks.CreateBlockFromString("DB", "MyTestDB", dbXml); Console.WriteLine("DB创建成功: " + newBlock.Name);

这里有两个容易踩的坑。第一,CreateBlockFromString前面那个字符串参数传块类型,DB就传"DB",FB就传"FB",FC传"FC",OB传"OB",大小写敏感,传错直接抛异常。第二,第二个字符串参数是块的逻辑名,它会覆盖XML里Header/Title里的名字,所以最好让两处保持一致,避免打开TIA后看到块名不一致的怪现象。

3.3 保存项目、释放资源的正确姿势

块创建完成后,一定要显式保存项目。不保存就直接关进程,刚才建的块全部白干。保存用一行代码:

project.Save();

完整跑完一块后,资源释放的顺序也值得注意。正确顺序是先释放Project对象,再释放TiaPortal对象,最后进程才会正常退出。反过来先关TiaPortal再操作Project,会直接报对象已释放的错。

project.Dispose(); portal.Dispose(); Console.WriteLine("资源释放完成");

我在调试早期犯过一个低级错误:脚本结束时没有Dispose,TIA进程一直挂在后台,第二次运行脚本时就报"另一个实例已占用项目",无论重启VS还是重启电脑都没救,最后只能去任务管理器手动结束Siemens.Automation.Portal进程。后来养成习惯,凡是打开过TiaPortal的代码,finally块里务必把这两个Dispose放进去,任何情况下都不能跳过。

4. 批量实战:从Excel变量表直接生成几十个DB

4.1 整体思路:模板循环,核心是数据驱动

单个DB能建了,批量就只是个循环问题。实践中我的做法是:Excel里维护一张变量总表,每行一个变量,带列:DB名称、变量名、数据类型、初始值、注释。C#程序通过NPOI或Excel COM读取这张表,按DB名称分组组装成XML,循环调用CreateBlockFromString。

这个思路的核心是"数据驱动",不是写死每个DB结构,而是让Excel表成为唯一的真源。以后现场要加一个工位,改Excel表里加几行,重新跑一次脚本,几十个DB的增删改一分钟内完成。做过的朋友都懂,这比在TIA里手动同步变量表不知道高到哪里去了。

我这里用NPOI读Excel,因为它是开源库,不需要在部署电脑上装Office。NuGet搜NPOI直接安装即可。读取逻辑很简单:

using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; // 假设Excel第一行是表头,从第二行开始读 var workbook = new XSSFWorkbook(@"D:\OpennessTest\Variables.xlsx"); var sheet = workbook.GetSheetAt(0); var dbMap = new Dictionary<string, List<VariableItem>>(); for (int rowIdx = 1; rowIdx <= sheet.LastRowNum; rowIdx++) { IRow row = sheet.GetRow(rowIdx); if (row == null) continue; string dbName = row.GetCell(0)?.ToString()?.Trim(); string varName = row.GetCell(1)?.ToString()?.Trim(); string dataType = row.GetCell(2)?.ToString()?.Trim(); string startValue = row.GetCell(3)?.ToString()?.Trim(); string comment = row.GetCell(4)?.ToString()?.Trim(); if (string.IsNullOrEmpty(dbName)) continue; if (!dbMap.ContainsKey(dbName)) dbMap[dbName] = new List<VariableItem>(); dbMap[dbName].Add(new VariableItem { Name = varName, DataType = dataType, StartValue = startValue, Comment = comment }); } foreach (var pair in dbMap) { string dbXml = BuildDbXml(pair.Key, pair.Value); blockGroup.Blocks.CreateBlockFromString("DB", pair.Key, dbXml); Console.WriteLine("已创建: " + pair.Key); }

注意Excel中数据类型列建议用规范名称,比如Bool、Int、DInt、Real、LReal、String,代码里做一层映射校验,遇到不认识的就打日志跳过去,别让一行的错误搞崩整个批处理。

4.2 DB的XML结构长什么样,照着改就行

很多新手卡在buildDbXml这个函数上,觉得XML复杂。其实你不需要理解每一层标签的含义,只需要掌握两个可变位置:Sections节点下的Member列表,以及Header里的Title。Member节点就是变量,加一个变量就加一个Member节点,属性只有三个:Name、Datatype、StartValue。有注释的可以加一个Comment子节点,但Openness对注释的支持没有想象中强,实测下来中文注释偶尔会编码异常,我一般不在XML里写注释,而是通过后续的变量导入流程补。

还有一个必须提醒的点:数据类型里String有点特殊,它的Datatype不是简单的String,而是String[长度],比如字符串长度20的变量要写成:

<Member Name="ProductCode" Datatype="String[20]"> <AttributeList><StartValue>' '</StartValue></AttributeList> </Member>

这里的StartValue字符串要用单引号包起来,忘记引号的话TIA编译会报字符串格式错误。初始值这一项如果Excel里留空,就不要生成StartValue节点,否则空字符串在某些版本里也会导致编译告警。

4.3 顺手在OB1里插入一段调用:进阶玩法

搞定DB批量创建之后,还能再进一步:让Openness自动在OB1里插入一个调用某个FC的网络。这个操作的核心是先导出OB1的XML,在XML的NetworkSource节点下追加一段Network结构,再导回项目。听起来复杂,但做一次就明白套路了。

先导出OB1的XML:

var ob1 = (OB)blockGroup.Blocks.Find("OB1"); string tempXmlPath = @"D:\OpennessTest\ob1_export.xml"; ob1.Export(tempXmlPath, ExportOptions.WithDefaults);

打开导出的XML,你会看到NetworkSource节点下是一排Network节点。你要做的就是在这些Network最后面增加一个新Network,内容无非是调用指令的SCL或LAD表示。比如调用一个名为"FC_Control"的FC块:

<Network Title="AutoInserted"> <Statement> <SCL> <Text>"FC_Control"();</Text> </SCL> </Statement> </Network>

改完XML后调用Import导回:

blockGroup.Blocks.Import(tempXmlPath, ImportOptions.Override);

这里有个必须注意的坑:OB1导出再导入时,如果原项目OB1里已经有大量程序,导入过程会把原有内容整体覆盖为修改后XML的内容。所以必须在导出XML里增补内容,不能只留新Network再导回去。我一直用"导出-修改-导回"三步走,导回前多做一次XML备份,出问题还能恢复。

5. 常见问题与排查技巧实录

5.1 我踩过的坑汇总:一行一行注释版

以下几个问题是社区里出现频率最高,也是我自己实测踩过的,整理成表格方便查阅:

现象根本原因解决办法
启动报"无法加载DLL或依赖项"TIA版本和Openness DLL版本不匹配确认PublicAPI子目录版本,重新引用对应DLL
打开项目时卡住,CPU跑满项目较大,Openness在内存中做版本升级耐心等待,或改用无界面模式减少UI开销
CreateBlockFromString报"命名冲突"目标DB名称已存在创建前调用Blocks.Find()检查,或用重命名策略
创建后的DB变量类型是空的XML里Datatype属性拼写错误严格用Bool/Int/Real/String[长度]等标准类型名
导回OB1后原程序消失导入时用了不完整的XML必须在完整导出XML基础上改,别重写
脚本第二次运行报项目被占用上次TiaPortal未Dispose任务管理器结束Siemens.Automation.Portal,修正释放逻辑

此外还有一个很容易被忽略的坑:Openness对项目路径里的中文和空格支持不太好,我建议所有测试工程路径都改成英文小写加下划线,避免很多隐蔽的编码问题。这个建议放之四海皆准,很多看似玄学的Openness报错,最终都出在路径上。

5.2 每次跑脚本前必做的三件事

现在是经验部分,分享我自己跑Openness脚本前固定检查的三件事。

第一,确认TIA Portal软件本体没在运行。Openness启动时会创建TIA实例,如果用户手动打开了TIA,再跑Openness脚本大概率冲突,报"无法获取许可证"或者"实例正在使用中"。所以脚本第一行就强制检查并提示手动关闭TIA,或者干脆在脚本里加个进程预检。

第二,项目文件做好备份。Openness的写操作不算安全事务,脚本中间崩了,项目文件可能处于不一致状态。我习惯在批量操作前把整个项目文件夹复制一份到backup目录,脚本跑完确认无误再删。成本极低,但救过我好几次。

第三,变量表先做数据校验再跑。用Excel驱动批量生成时,先单独跑一个只读校验模式,把Excel里所有DB名、变量类型、重复项先检查一遍,有错误打日志但不生成任何块。等校验通过再跑真正的写入模式。这样的两段式流程,能避免跑到一半才发现Excel中间一行类型写错了,已经建了二十多个废DB还要手动清理的尴尬。

Openness这套东西,上手第一周可能觉得文档少、API绕,但一旦把第一个模板趟通,后面就是指数级的回报。我见过身边有同行把整套标准化PLC程序生成工具做出来后,项目交付周期从三周压到一周,用的就是类似今天这个示例的底层逻辑。自动化这件事,关键不在于写多复杂的代码,而在于把重复劳动抽象成模板和数据,剩下的,就是让脚本替你熬夜了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询