ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

西门子Openness实战:用C#脚本批量生成PLC电机控制块

西门子Openness实战:用C#脚本批量生成PLC电机控制块

简介:面向自动化工程师的西门子TIA Portal与Openness集成示例包,演示如何通过开放接口自动生成PLC程序,减少重复性开发,适用于工业控制与智能制造场景。资源共421个文件、1.28MB,以312张PNG截图、72个C#源码、20个XAML界面文件为主,覆盖从TIA Portal启动、状态切换、日志记录的完整工程实现。已有616人学习下载。压缩包结构清晰,包含视图模型、命令绑定、配置管理、版本检测等功能模块,适合正在研究Openness二次开发、需要参考工程结构与调用流程的PLC及上位机开发者。通过示例可掌握TIA Portal自动化操作方式,理解逻辑控制、数据处理与通讯协议设置的实现思路,并借鉴其与ERP/MES系统对接的数据交互方案;对于工厂自动化生产线、复杂物流系统等场景,也能提供快速部署与调试的实用参考。

1. 西门子 Openness:把 TIA Portal 里的手工点击变成可复用的代码

第一次接触西门子 Openness,是在一个需要批量生成 PLC 项目的时候。一个项目里几十个相似的电机控制块,如果靠人工在 TIA Portal 里逐个添加、连线、编译,两天都做不完。后来发现 TIA Portal 内置了 Openness 这个自动化接口,可以用 C# 脚本直接操作 Portal 内部的工程对象:建项目、加 PLC、生成块、编译下载,全都能在外部程序里完成。那一刻的感觉是,西门子终于给工程师开了一扇窗。

这篇文章把这套流程拆开讲清楚。内容包括 Openness 的工作机制、环境装配、用代码生成一个完整的电机启停控制块,以及我实际踩过的几个坑。无论你是刚接触自动化的工程师,还是已经在用脚本批量处理项目的老手,都可以从中找到可以直接复现的思路。新手能按步骤走通,熟手可以重点看参数设置和排查方法。

2. Openness 的底细:它到底是什么,为什么值得用它

2.1 Openness 不是插件,而是 TIA Portal 暴露出来的一层自动化接口

很多第一次听说 Openness 的人会误以为它是一个独立软件或 TIA Portal 的插件。其实它是一组 .NET 程序集,随 TIA Portal 安装到本地。安装目录下能找到西门子公开的一些 DLL 文件,比如Siemens.Engineering.dll,引用它之后,你的 C# 程序就能像操作一个对象模型一样操作 TIA Portal。

核心概念可以这样理解:TIA Portal 的界面操作,本质上是对某个工程对象做“增删改查”;Openness 把这层操作封装成了稳定的 API,允许外部程序在 Portal 进程内执行这些动作。你写代码创建一个新块,和你在界面上右键新建块,最后落到项目文件里的结果是完全一致的。

Openness 的使用场景非常明确:当你有大量重复的工程操作需要执行时,用它远比手工点击可靠。它不会疲劳,不会漏点一个引脚,也不会因为操作顺序错了导致连线错误。

2.2 版本对应关系:这是一切排错的前提

Openness 的版本必须与 TIA Portal 版本严格一致。TIA Portal V15 对应某版本的程序集,V16 又是另一套,V17、V18 同样如此。不同版本的 API 有细微差别,比如创建块时的枚举参数、编译结果的返回类型,都可能不一样。

我在 V16 环境下写的代码,直接拿到 V17 上去编译,会在引用程序集时就报版本冲突。解决办法只能是重新引用对应版本下的程序集文件。建议在项目文档里明确记录你的目标环境版本,否则脚本很可能在换了一台电脑之后就处于无法运行的状态。

2.3 为什么选择 C# 而不是 Python

虽然 Python 在自动化领域很流行,但 Openness 官方只提供了 .NET 接口。你可以通过 IronPython 或 Python.NET 去调用,但这个过程需要处理大量类型转换,而且调试时那些类型转换的报错信息,比 C# 里的编译错误要难定位得多。

我一般会直接用 C# 写一个控制台应用程序。Visual Studio 里新建控制台项目,引用 Openness 程序集,然后写代码。整个过程不需要额外安装其他库,代码结构和 TIA Portal 里的对象层级一一对应,读起来也直观。如果你的环境里只有 Python 基础,先用 C# 写一个最小示例跑通流程,再考虑跨语言调用也不迟。

3. 环境装配与第一个最小示例:从空项目到有代码的块

3.1 装配开发环境的具体步骤

装配过程看起来简单,但有几个细节会卡住不少人。下面是我整理后的完整步骤序列。

步骤内容注意点
1安装 TIA Portal V16,确保安装时勾选了 Openness 相关组件默认安装可能不包含全部组件
2打开 Visual Studio,新建一个 .NET Framework 控制台项目.NET Core 也能跑,但建议用 Framework 4.6 以上
3添加引用,指向Siemens.Engineering.dll路径一般在 TIA Portal 安装目录下
4在代码中using Siemens.Engineering;这一步编译通过就说明引用成功
5以管理员身份运行 Visual StudioOpenness 需要以管理员权限调用 Portal

第 5 步很容易忽略。如果 Visual Studio 不是以管理员权限启动,Openness 会报Siemens.Engineering.Hmi.HmiTarget之类的初始化异常,但真正的问题是权限不够。我用管理员权限重启 Visual Studio 后,问题就消失了。

3.2 写一个能打开 TIA Portal 的最小代码

using System; using Siemens.Engineering; using Siemens.Engineering.SW.Blocks; class Program { static void Main(string[] args) { // 创建 TIA Portal 实例 TiaPortal tiaPortal = new TiaPortal(TiaPortalMode.Standalone); Console.WriteLine("TIA Portal 已启动"); // 创建不保存的项目(临时用) Project project = tiaPortal.Projects.Create(); Console.WriteLine("空项目已创建"); // 不做任何事,先验证流程能走通 project.Close(); tiaPortal.Dispose(); Console.WriteLine("TIA Portal 已关闭"); } }

这段代码的逻辑很直白。new TiaPortal(TiaPortalMode.Standalone)会启动一个独立的 Portal 进程,Standalone模式表示不附加到已有项目,而是新启动一个没有项目加载的实例。tiaPortal.Projects.Create()在内存中创建一个空白项目,不会写盘。最后Close()关闭项目,Dispose()释放 Portal 进程。

参数说明:TiaPortalMode.Standalone之外的另一个选择是TiaPortalMode.Project,用于直接打开指定项目文件。在调试初期建议先用Standalone,这样即使代码出了问题,也不会破坏已有的项目文件。

运行这段代码后,会看到控制台逐行打印,同时 TIA Portal 界面会真的弹出。这表示 Openness 已经成功控制 Portal 了。

4. 用 Openness 生成一个电机启停控制块:代码结构与参数详解

4.1 需要生成什么样的控制块

以一个典型的电机控制需求为例:启动按钮按下,电机运行;停止按钮按下,电机停止;同时有运行反馈信号,用于判断电机是否真的在转。需要生成一个 FC(函数)块,它有三个输入参数和两个输出参数。

在实际操作时,手工创建 FC 再写代码需要十分钟。用 Openness 脚本创建,整个过程只需几秒,而且可以循环执行,批量修改参数。

4.2 生成 FC 块的完整代码

using Siemens.Engineering; using Siemens.Engineering.SW.Blocks; using Siemens.Engineering.SW.Tags; // 假设已经获得了打开的 project 对象 PlcBlockGroup blockGroup = project.BlockGroup("PLC_1 [CPU 1214C DC/DC/DC]"); PlcBlock fcMotorControl = blockGroup.Blocks.CreateBlock("FC_MotorControl", PlcBlockType.FC); Console.WriteLine("FC 块已创建");

先解释这里出现的两个核心类。PlcBlockGroup是 PLC 下的块组,它对应 TIA Portal 项目树里 PLC 下的“程序块”目录。PlcBlockType.FC表示创建无背景数据块的函数,它没有独立的实例数据。如果创建 FB,就要用PlcBlockType.FB,同时还要考虑背景 DB 的分配问题。

4.3 给 FC 添加输入输出参数

块创建出来之后,里面还是空的,需要定义接口参数。这一步是最能体现 Openness 对象模型特点的地方。

// 获取 FC 的接口信息 Interface interfaceData = fcMotorControl.Interface; // 添加输入参数 interfaceData.Inputs.Create("StartButton", "Bool"); interfaceData.Inputs.Create("StopButton", "Bool"); interfaceData.Inputs.Create("MotorFeedback", "Bool"); // 添加输出参数 interfaceData.Outputs.Create("MotorRun", "Bool");

创建参数时,第一个参数是接口名称,第二个是数据类型。数据类型的字符串必须是 TIA Portal 中支持的 PLC 数据类型,布尔用Bool,整数用Int或DInt,实数用Real,计时器用TP等。这个字符串拼错一个字母,方法会直接抛异常。

参数创建完成之后,会自动出现在 FC 的接口区,打开 FC 就能在界面里看到这些接口。

4.4 向 FC 体内写入梯形图逻辑

Openness 有个让初学者头疼的地方:它不直接支持在块体中绘制梯形图。你不能用代码“画”一个常开触点出来。但可以用PlcBlock的GetAttribute("Code")获取当前块的代码文本,然后写入 STL(语句表)或 SCL(结构化控制语言)代码。

// 覆盖块体代码为 STL string stlCode = @" A ""StartButton""; O ""MotorRun""; A ""StopButton""; A ""MotorFeedback""; = ""MotorRun""; "; fcMotorControl.SetAttribute("Code", stlCode);

这段 STL 逻辑的含义是:启动按钮接通,或者电机已在运行,同时停止按钮接通(停止按钮用常闭逻辑),同时有电机反馈,则电机运行输出置位。用梯形图术语说,这是启动按钮并联运行自保持,再与停止按钮常闭和反馈信号串联。

这里有个重要的坑:代码字符串必须能用 STL 编译器解析。"StartButton"必须和接口里定义的名称完全一致,包括引号格式和大小写。如果名称匹配不上,编译阶段会报错,提示找不到操作数。

4.5 编译生成的 FC 并检查错误

块内容写入之后,必须编译。Openness 提供了编译接口,编译结果可以从返回值中获取。

using Siemens.Engineering.Compiler; // 编译整个 PLC CompileResult result = plcSoftware.Compile(); Console.WriteLine("编译状态: " + result.State); if (result.State == CompilerResultState.Error) { // 输出错误信息的前 500 个字符 Console.WriteLine(result.Messages[0].Text); }

CompileResult.State有三种常见值:Success、ErrorWithWarning和Error。只要存在 Error,就必须查看Messages列表,里面包含了错误在哪个块、哪一行、什么类型。定位错误时,我一般会先打印所有消息,而不是只看第一条。有时候块 A 的错误其实是块 B 的接口引用导致的。

5. 避坑清单:Openness 生成代码时的五个常见翻车点

5.1 Openness 实例只能启动一个,重复启动会卡死

现象:代码里先后创建了两个TiaPortal对象,结果第二个实例一直卡住无法启动。

原因:Openness 设计为单实例模式。同一台电脑上同时运行两个独立的 Openness 进程,第二个进程会进入等待状态,直到第一个释放。在主流程中如果不小心重复创建实例,程序就挂起了。

解决:严格控制TiaPortal的生命周期。整个程序只创建一个实例,用完立即Dispose()。如果需要切换项目,先关闭旧项目再打开新项目,不要创建第二个实例。

5.2 PLC 名称不同导致块组路径错误

现象:project.BlockGroup("PLC_1")抛出找不到对象的异常。

原因:不同型号、不同固件版本的 CPU,在项目树中的默认名称可能不一样。某些设备固件下 PLC 名称是PLC_1,但更换了设备或从旧项目复制过来后,名称可能已经变成了PLC_2。

解决:不写死名称。启动项目后先遍历 PLC 列表,用代码找到第一个 PLC 的名称,然后基于这个名称拼接块组路径。这样即使项目里 PLC 重命名了,脚本仍然能正确操作。

5.3 块体写入的代码编译失败,找不到操作数

现象:SetAttribute("Code", ...)执行成功,但编译时报“找不到操作数”的错误,定位到某个输入参数名称上。

原因:STL 代码里引用的名称和接口中定义的名称不一致。常见情况包括:名称多了一个空格、大小写不匹配、或者引号写成了半角之外的形式。

解决:设置Code之后调用编译,立即查看未通过的原因。对于名称匹配问题,快速检查代码中的每个字符串字面量是否和接口参数列表完全一致。我一般会写一段脚本,把接口参数全部打印出来,然后和 STL 代码逐行对照。

5.4 编译成功后块没出现在项目树里

现象:代码声明块已创建成功,编译也通过,但打开 TIA Portal 项目树看不到这个 FC。

原因:Openness 在内存中操作,但项目视图没有刷新。另外一种情况是打开了旧版本的项目缓存。

解决:调用project.Store()把项目写盘,然后用project.Open()重新打开。这能强制刷新项目树。如果还看不到,检查块是否被放到了某个特定组而非根目录下。Openness 创建块时的第二个参数PlcBlockGroup指定了块的存放位置,如果传的是子组对象,块就在子组下面。

5.5 下载到 PLC 时报“连接中断”或“固件不匹配”

现象:代码生成和编译都没有问题,但下载步骤失败。

原因:Openness 的下载功能对 PC 的网络设置、在线访问节点名、目标设备 IP 有严格要求。最常见的错误是目标设备 IP 与 PC 不在同一网段,或者 PLC 的访问保护等级拒绝了非授权下载。

解决:先把同一个项目在 TIA Portal 界面上手动下载一次,确认这套设备与连接参数是可行的。然后在 Openness 代码里使用与手动下载完全一致的连接配置。不要在脚本里省掉设备选型这一步,设备类型必须与真实 PLC 固件一致。

6. 进阶:把生成流程变成批量工具,并验证每次生成的正确性

6.1 从单个块到批量生成:用一个外部集合驱动循环

实际项目中,单个 FC 的生成只是热身。真正的价值是在总装调试前,把一批逻辑相似的控制块一次性生成到项目里。这里最常用的方式就是“数据驱动”:把每个块要用的参数放在一个List或DataTable里,循环生成。

// 用集合驱动批量生成 List<MotorConfig> configs = new List<MotorConfig>() { new MotorConfig { Name = "FC_Pump_1", StartAddr = "I0.0", StopAddr = "I0.1" }, new MotorConfig { Name = "FC_Pump_2", StartAddr = "I0.2", StopAddr = "I0.3" } }; foreach (var cfg in configs) { PlcBlock newBlock = blockGroup.Blocks.CreateBlock(cfg.Name, PlcBlockType.FC); Interface iface = newBlock.Interface; iface.Inputs.Create("StartButton", "Bool"); iface.Inputs.Create("StopButton", "Bool"); // 安全性检查:每次生成后判断块是否真的存在 if (!blockGroup.Blocks.Contains(newBlock)) { Console.WriteLine($"块 {cfg.Name} 生成失败"); } }

MotorConfig是一个自定义类,用属性保存每个电机块的不同参数。这样以后想改启动地址,只需要改数据源,不用改生成逻辑。Blocks.Contains(newBlock)是一个简单的自检步骤,可以迅速捕捉生成过程中的异常。

6.2 验证生成结果的正确性:强制走一次完整编译与交叉检查

脚本生成完之后,验证环节不能省。我的做法分三步:

第一步,调用plcSoftware.Compile(),确认编译状态为Success。如果还有 warning,也不能直接放过,要看 warning 内容是否涉及未连接的网络或未初始化的变量。

第二步,遍历生成的所有块,检查每个块的接口数量、名称和数据类型。比如,确认每个 FC 都有两个 Bool 输入和一个 Bool 输出。这个检查可以用反射或简单的GetAttribute获取接口计数。

第三步,用 Openness 自带的导出功能,把所有块导出为 XML 文件,然后与手工创建过的标准块 XML 做差异对比。这个差异对比能发现很多编译不报错但逻辑可能不对的问题,比如某个块的 STL 代码行数和标准块不一致。

导出 XML 的代码片段如下:

// 导出块为 XML 文件 using System.IO; using Siemens.Engineering.Library; // 将单个块导出到指定路径 string exportPath = @"D:\temp\exported_blocks"; Directory.CreateDirectory(exportPath); fcMotorControl.Export(exportPath + "\\FC_MotorControl.xml", ExportOptions.WithDefaults);

导出之后用文本对比工具和标准文件做比对。如果差异集中在地址分配区域,说明逻辑是对的;如果差异出现在代码主体,需要重点检查 STL 行。

6.3 进一步的扩展方向:和版本管理工具集成

Openness 生成的代码本质上是文本,因此可以纳入版本管理。常见做法是,每次批量生成完成后自动导出整个 PLC 的代码到本地目录,然后提交到 Git。这样任何一次自动化操作都可以追溯到具体改动,出问题也能快速回滚。这个思路我已经沿用了很久,它把自动化生成和工程追溯结合了起来。

7. 一点实际心得

Openness 最容易被低估的地方在于:它看起来只是多了一个程序化操作 Portal 的接口,但真正用起来之后,整个项目流程会被重新梳理一遍。以前我拿到一个几十台设备的项目,光创建几十个控制块,手点一天就没了,还容易漏掉连线。现在我在生成脚本里把块的参数、命名规则、互锁逻辑统一约束好,执行一次,半小时不到就全部生成完毕,而且在编译通过之前绝不手动打开 Portal 去碰。

从那以后,我每次写完批量生成脚本,都会强制走一遍导出对比的验证流程,确认生成结果与手工创建的行为完全一致,然后才放它去操作真实项目。希望这份记录能帮你少踩几个坑。

如果你手头正需要批量生成 PLC 项目,不妨直接下载这份资源,对照其中的示例脚本搭起自己的生成环境,然后从最小示例开始跑通,再逐步扩展成你的专用工具。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进