Openness API创建TIA Portal项目
"项目基础"(或"项目基准")是自动化集成商交付给客户的标准化基础:控制器硬件配置、商定的标签命名方案、客户端的可重用功能块(FB)库,以及下游工程师填充的空程序结构。为每个新合同手动构建这个基础是重复的、容易出错的,而且很慢。TIA Portal Openness API暴露了一个C#对象模型,让你可以从脚本中确定性地生成相同的基础——你交付给客户的内容每次都是字节相同的。
本参考展示了如何使用Openness API创建TIA Portal项目、插入SIMATIC S7-1500站点、填充PLC标签表、实例化功能块并编译结果。它假设你有一个工作的C#开发环境和本地安装的TIA Portal V17或更高版本。
兼容性范围:代码示例针对TIA Portal V18/V19/V20/V21附带的Openness程序集。API表面在这些版本中相似;在V20中更改的方法名(特别是Compile委托)已内联标记。请根据你安装的Siemens.Engineering.dll验证确切的程序集版本,路径为C:\Program Files\Siemens\Automation\Portal V<version>\PublicAPI\<version>\。
1. Openness API的架构概述
Openness API将TIA Portal项目的结构反映为强类型集合的树。相关的根对象包括:
| 命名空间 | 关键类 | 职责 |
|---|---|---|
| Siemens.Engineering | TiaPortal | 入口点;表示运行中的TIA Portal进程 |
| Siemens.Engineering | Project | 在内存中打开的.ap项目 |
| Siemens.Engineering.HW | Device、DeviceItem、Submodule | 硬件树(控制器、I/O、网络) |
| Siemens.Engineering.SW | PlcSoftware | 绑定到CPU的PLC程序容器 |
| Siemens.Engineering.SW.Tags | PlcTagTable、PlcTag | 标签表和单个标签 |
| Siemens.Engineering.SW.Blocks | FB、FC、OB、DB | 程序块(代码容器) |
| Siemens.Engineering.Compiler | ICompilable、CompilerResult | 编译委托和结果对象 |
上述每个对象都是IDisposable,必须按创建的反向顺序释放。Openness运行时托管在进程内(或并排的TIA Portal实例中);泄漏未释放的句柄将锁定项目文件并强制进程终止。
2. 前提条件
- TIA Portal V18、V19、V20或V21,已安装Openness选项。在开始→Siemens Automation→TIA Portal Openness下验证。许可证与标准STEP 7 Professional安装捆绑提供。
- Visual Studio 2019或2022,使用.NET Framework 4.8(V17/V18)或.NET 6.0/8.0(V19+取决于版本)。Openness程序集针对与TIA Portal主版本匹配的框架版本构建。
- 引用Openness程序集:在你的C#项目中添加引用:
Siemens.Engineering.dllSiemens.Engineering.Hmi.dll(仅在脚本化HMI时)Siemens.Engineering.AddIn.dll(仅在脚本化Add-In表面时) 位置:C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\
- 至少交互式启动TIA Portal一次,以便创建用户配置文件;无头自动化仍然需要本地配置文件目录。
- 在TIA Portal之外运行脚本,使用自定义.exe(推荐模式)或从TIA Portal Add-In运行。进程内Add-In在TIA Portal进程内运行;外部可执行文件通过类COM互操作附加。
进程模型:如果TIA Portal尚未运行,Openness API会在特殊的自动化模式下启动它。不要在设计器正在编辑同一项目时运行脚本——Openness对项目文件持有排他写锁。
3. 连接到TIA Portal并打开项目
每个Openness脚本都从获取TiaPortal实例开始。该实例通过进程附加的工厂获取,整个会话的生命周期取决于该对象。
using System;
using Siemens.Engineering;
class Program
{
static int Main(string[] args)
{
TiaPortal tiaPortal = null;
Project project = null;
try
{
// 附加到运行中的TIA Portal实例。如果没有运行,
// Openness会启动一个新的无头进程。
tiaPortal = new TiaPortal(TiaPortalMode.WithoutUserInterface);
// 从磁盘打开现有项目,或创建新项目。
// 对于项目基础生成,我们通常创建新的。
project = tiaPortal.Projects.Create(
new System.IO.DirectoryInfo(@"C:\Projects\ClientBase"),
"ClientBase_Project");
BuildProjectBase(project);
}
catch (EngineeringException ex)
{
Console.Error.WriteLine($"Openness error: {ex.ErrorCode} - {ex.Message}");
return 1;
}
finally
{
// 按获取的反向顺序释放。项目必须在TiaPortal之前释放,
// 否则项目文件会被锁定。
project?.Dispose();
tiaPortal?.Dispose();
}
return 0;
}
}
TiaPortalMode枚举接受WithUserInterface或WithoutUserInterface。对于无人值守的CI管道使用WithoutUserInterface;当你需要交互式观察结果进行调试时使用WithUserInterface。
3.1 正确终止连接
Openness建立在IDisposable契约之上;Dispose方法断开与TIA Portal的连接并释放COM风格的RCW。未能对Project或TiaPortal调用Dispose会使句柄保持活动状态,并在下次尝试打开项目时强制进程终止。
根据西门子官方文档,推荐的模式是使用IDisposable.Dispose()方法分离或关闭TIA Portal的活动实例:参见TIA Portal Openness V21参考中的终止与TIA Portal的连接。
顺序很重要。始终在TiaPortal之前释放Project。C#中的using语句在作用域退出时按源顺序释放,因此请相应地编写嵌套的using块。
4. 添加硬件(SIMATIC S7-1500示例)
硬件通过Project.Devices集合添加。每个设备通过将完全限定的订单号(MLFB/目录号)插入设备树来创建。Openness API不会验证目录号是否存在——如果你输入错误,设备将在下游编译时失败。
| 硬件 | 目录号(MLFB) | 备注 |
|---|---|---|
| S7-1515-2 PN | 6ES7 515-2AM02-0AB0 | 带PROFINET的中端CPU |
| S7-1516-3 PN/DP | 6ES7 516-3AN02-0AB0 | 高性能变体 |
| DI 32x24VDC HF | 6ES7 521-1BL00-0AB0 | 32点数字输入模块 |
| DQ 32x24VDC/0.5A HF | 6ES7 522-1BL00-0AB0 | 32点数字输出模块 |
| AI 8xU/I/RTD/TC ST | 6ES7 531-7KF00-0AB0 | 8通道模拟输入 |
| AQ 4xU/I ST | 6ES7 532-5HD00-0AB0 | 4通道模拟输出 |
using Siemens.Engineering;
using Siemens.Engineering.HW;
static void AddS71500Station(Project project)
{
// 1. 插入CPU(带订单号的设备)。
Device cpu = project.Devices.CreateWithOrder(
new DeviceComposition("Head"),
new System.IO.DirectoryInfo(@"C:\HWLibrary\S7-1500"),
"6ES7 515-2AM02-0AB0", // S7-1515-2 PN, FW 2.9
"PLC_1", // TIA Portal中可见的设备名称
null);
// 2. 定位表示CPU本身的DeviceItem,以便我们可以
// 将I/O模块挂在机架上。
DeviceItem cpuHead = cpu.DeviceItems.First(
di => di.Name == "PLC_1");
// 3. 在插槽1中添加32点数字输入模块。
DeviceItem diModule = cpuHead.CreateAndInsertNew("DI 32x24VDC HF",
1,
new DeviceComposition("Slot"),
"6ES7 521-1BL00-0AB0",
null);
// 4. 在插槽2中添加32点数字输出模块。
DeviceItem dqModule = cpuHead.CreateAndInsertNew("DQ 32x24VDC/0.5A HF",
2,
new DeviceComposition("Slot"),
"6ES7 522-1BL00-0AB0",
null);
// 5. 配置PROFINET接口名称(IO控制器链接和标准端口命名约定所需)。
var pnInterface = cpuHead.GetService<Siemens.Engineering.HW.Features.NetworkInterface>();
if (pnInterface != null)
{
pnInterface.Name = "PN-IE_1";
}
}
CreateWithOrder重载需要一个硬件目录目录。该目录随TIA Portal一起提供,通常位于C:\Program Files\Siemens\Automation\Portal V21\HWLibrary\。将CreateWithOrder指向根目录,API会根据订单号自动解析子目录。
5. 构建PLC软件容器
程序块、标签表和监视表都位于绑定到CPU的PlcSoftware对象下。CPU通过Software属性暴露它。
using Siemens.Engineering;
using Siemens.Engineering.SW;
static PlcSoftware GetOrCreatePlcSoftware(Device cpu)
{
PlcSoftware plc = cpu.GetService<PlcSoftware>();
if (plc == null)
{
// 对于新创建的CPU,软件已存在;此保护
// 存在于打开作为模板的流程中。
throw new InvalidOperationException("CPU has no PlcSoftware container");
}
return plc;
}
6. 创建PLC标签表和标签
标签表是组织单元。典型的项目基础为每个工厂区域定义一个表(例如Tags_Conveyor、Tags_HVAC),加上一个Tags_System表用于集成商的标准I/O映射。
using Siemens.Engineering;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Tags;
static void AddTagTable(PlcSoftware plc, string tableName)
{
PlcTagTable table = plc.TagTableGroup.TagTables.Create(tableName);
table.IsSystemTagTable = false;
return;
}
static void AddTag(PlcTagTable table, string tagName,
string dataTypeName, string ioAddress = null)
{
PlcTag tag = table.Tags.Create(tagName);
tag.DataTypeName = dataTypeName; // 例如:"Bool"、"Int"、"Real"、"Bool"
if (!string.IsNullOrEmpty(ioAddress))
{
tag.LogicalAddress = ioAddress; // 例如:"I0.0"、"Q4.0"
}
}
// 用法
AddTagTable(plc, "Tags_System");
PlcTagTable sysTable = plc.TagTableGroup.TagTables.Find("Tags_System");
AddTag(sysTable, "i_Start", "Bool", "I0.0");
AddTag(sysTable, "i_Stop", "Bool", "I0.1");
AddTag(sysTable, "o_Motor_Run", "Bool", "Q4.0");
AddTag(sysTable, "i_Analog_Level", "Int", "IW6");
AddTag(sysTable, "iw_Process_Value", "Word", "IW8");
AddTag(sysTable, "qw_Valve_Setpoint", "Word", "QW10");
这里使用的命名约定(i_、o_、iw_、qw_)遵循客户端的合同规范。因为脚本将名称作为常量保存,所以更合同比的命名规则只需要一次查找和替换。
7. 添加功能块和程序逻辑
Openness API通过PlcSoftware.BlockGroup暴露块容器。创建FB需要指定名称、编程语言和实例DB名称。块体本身在传统意义上不能通过Openness编辑——你通过调用Composition服务或导入XML/PLCopen导出来通过API生成代码。
7.1 创建空白FB
using Siemens.Engineering;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
static FB CreateMotorFb(PlcSoftware plc, string name)
{
FB fb = plc.BlockGroup.Blocks.CreateFb(
new System.IO.FileInfo(@"C:\Templates\Motor.template.xml"),
name);
// FB的实例DB在第一次实例化时自动创建;
// 这里我们为了清晰预先创建它。
plc.BlockGroup.Blocks.CreateDb(
new System.IO.FileInfo(@"C:\Templates\MotorInstance.template.xml"),
$"DB_{name}");
return fb;
}
上面引用的XML模板文件是标准的TIA Portal块导出。它们可以由工程师在TIA Portal UI中一次性生成(文件→导出块),并被Openness脚本无限期重用。这是标准模式:在IDE中设计逻辑,一次性生成XML,然后从脚本将其放入数千个项目基础中。
7.2 从PLCopen XML导入逻辑
对于你不想在C#中重新编写的复杂逻辑,将预编译的PLCopen XML放在已知位置并导入。Openness API将导入视为原子操作:
using Siemens.Engineering;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.SW.Export;
static void ImportLogic(PlcSoftware plc, string xmlPath, string targetName)
{
// PLCopen XML是TIA Portal在你选择文件→导出块(LAD/FBD/ST)时
// 产生的交换格式。
using (var xml = System.IO.File.OpenRead(xmlPath))
{
// Block组上的Import方法接受XML并根据文件的
// 内部<add>元素创建相应的块类型。
plc.BlockGroup.Blocks.Import(
new System.IO.FileInfo(xmlPath),
ImportOptions.Override);
}
}
8. 编译项目基础
在项目可以下载到真实CPU之前,它必须干净地编译。编译委托是异步的,返回一个CompilerResult,其中包含具有严重性、错误代码和源位置的CompilerMessage对象集合。
using Siemens.Engineering;
using Siemens.Engineering.Compiler;
static CompilerResult CompileBase(Project project)
{
// Project根本身是ICompilable。
ICompilable compilable = (ICompilable)project;
CompilerResult result = compilable.Compile();
foreach (CompilerMessage msg in result.Messages)
{
Console.WriteLine($"[{msg.Severity}] {msg.ErrorCode} " +
$"in {msg.Path}: {msg.Description}");
}
if (result.State != CompilerResultState.Successful)
{
throw new InvalidOperationException(
$"Compile failed with state {result.State} " +
$"and {result.Warnings.Count().ToString()} warnings, " +
$"{result.Errors.Count().ToString()} errors.");
}
return result;
}
Openness V21文档列出了支持的严重性值:CompilerMessageSeverity.Error、Warning和Information。0x8000xxxx范围内的错误代码通常表示缺少硬件配置,而0x8001xxxx指向程序中的类型/实例不匹配。
9. 保存项目
成功编译后,持久化项目以使磁盘上的文件与内存中的树匹配。Project对象暴露了一个Save方法,可以原子地写入.ap<version>存档。
static void SaveProject(Project project)
{
project.Save();
// SaveAs(...)也可用于以不同名称写入新位置;
// 对于将基础分支为每个客户端的副本很有用。
}
保存与释放:Save写入项目文件;Dispose释放内存锁。两者是独立的。如果你想让更改持久化,请始终在Dispose之前Save。
10. 完整的工作骨架
下面的骨架将前面的部分组合成一个可执行文件。这是生产使用的推荐起点。
using System;
using System.Linq;
using Siemens.Engineering;
using Siemens.Engineering.HW;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Tags;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.Compiler;
class ProjectBaseBuilder
{
static int Main(string[] args)
{
TiaPortal tia = null;
Project project = null;
try
{
tia = new TiaPortal(TiaPortalMode.WithoutUserInterface);
string projectPath = args.Length > 0 ? args[0]
: @"C:\Projects\ClientBase\ClientBase.ap21";
string projectName = System.IO.Path.GetFileNameWithoutExtension(projectPath);
string projectDir = System.IO.Path.GetDirectoryName(projectPath);
// 创建新项目。使用Projects.Open打开现有模板。
project = tia.Projects.Create(
new System.IO.DirectoryInfo(projectDir),
projectName);
// 1. 添加CPU + I/O
Device cpu = project.Devices.CreateWithOrder(
new DeviceComposition("Head"),
new System.IO.DirectoryInfo(@"C:\Program Files\Siemens\Automation\Portal V21\HWLibrary"),
"6ES7 515-2AM02-0AB0",
"PLC_1",
null);
DeviceItem cpuHead = cpu.DeviceItems.First(di => di.Name == "PLC_1");
cpuHead.CreateAndInsertNew("DI 32x24VDC HF", 1, new DeviceComposition("Slot"),
"6ES7 521-1BL00-0AB0", null);
cpuHead.CreateAndInsertNew("DQ 32x24VDC/0.5A HF", 2, new DeviceComposition("Slot"),
"6ES7 522-1BL00-0AB0", null);
// 2. PLC软件
PlcSoftware plc = cpu.GetService<PlcSoftware>();
// 3. 标签
PlcTagTable t = plc.TagTableGroup.TagTables.Create("Tags_System");
PlcTag iStart = t.Tags.Create("i_Start");
iStart.DataTypeName = "Bool";
iStart.LogicalAddress = "I0.0";
// 4. 从PLCopen XML导入预构建的FB
plc.BlockGroup.Blocks.Import(
new System.IO.FileInfo(@"C:\Templates\Motor.xml"),
ImportOptions.Override);
// 5. 编译
ICompilable c = (ICompilable)project;
CompilerResult result = c.Compile();
if (result.State != CompilerResultState.Successful)
{
foreach (var m in result.Messages.Where(m => m.Severity == CompilerMessageSeverity.Error))
{
Console.Error.WriteLine($"{m.ErrorCode}: {m.Description}");
}
return 2;
}
// 6. 保存
project.Save();
Console.WriteLine("Project base created at " + project.Path);
}
catch (EngineeringException ex)
{
Console.Error.WriteLine($"Openness error: {ex.ErrorCode}: {ex.Message}");
return 1;
}
finally
{
// 反向顺序:项目,然后是tia。
project?.Dispose();
tia?.Dispose();
}
return 0;
}
}
11. 版本兼容性矩阵
| TIA Portal版本 | Openness程序集 | .NET目标 | 备注 |
|---|---|---|---|
| V17 | Siemens.Engineering.dll 17.0 | .NET Framework 4.8 | 基线;无ImportOptions枚举 |
| V18 | 18.0 | .NET Framework 4.8 | 添加ImportOptions.Override |
| V19 | 19.0 | .NET 6.0 | 添加异步Compile重载 |
| V20 | 20.0 | .NET 6.0 | Compiler委托签名更改;示例代码显示回退 |
| V21 | 21.0 | .NET 8.0 | Add-In API稳定;记录Dispose契约 |
12. 常见故障和现场诊断
| 症状 | 可能的根本原因 | 修复 |
|---|---|---|
| Projects.Create上的EngineeringException: 0x80003001 | 目标目录存在且不为空 | 使用新目录或改用Projects.Open |
| 新标签上的编译错误0x8000FFFF | 数据类型名称拼写错误(例如,bool而不是Bool) | API中的类型名称区分大小写 |
| FB导入上的编译错误0x80010002 | PLCopen XML是从不同TIA Portal版本导出的 | 从匹配的版本重新导出,或设置ImportOptions.None并接受迁移 |
| 脚本崩溃后项目文件被锁定 | 从未调用Dispose(进程被终止) | 终止持有文件的任何S7OI.exe进程;重建锁文件 |
| CreateWithOrder中的ArgumentException | MLFB有拼写错误或错误的固件版本限定符 | 从西门子Industry Mall逐字复制订单号 |
| 标签出现在错误的表中 | TagTableGroup.TagTables.Create返回的表对象与用于添加标签的表不同 | 在迭代Tags之前使用TagTables.Find(name)重新获取 |
13. 验证清单
- 在匹配版本的TIA Portal中打开生成的
.ap<version>文件。项目应在没有迁移提示的情况下打开。 - 确认项目树→PLC_1→设备配置显示S7-1515-2 PN和两个I/O模块分别在插槽1和2中。
- 确认PLC标签→Tags_System包含具有预期数据类型和I/O地址的合标签集。
- 确认程序块包含导入的FB及其实例DB。
- 从UI触发编译→软件(全部重建)。它必须以0个错误和0个警告完成。
- 将项目导出为项目主文件,并使用TIA Portal项目比较器将其与先前合同的参考基础进行比较。只应出现每个客户端的差异。
**源代码控制:**仅在你的VCS能够高效处理大型二进制文件时,才将生成的.ap<version>提交到版本控制系统中。许多集成商将脚本+XML模板存储在Git中,并根据需要重新生成项目文件。第二种模式保持模板可比较。
14. 扩展模式
一旦项目基础可以确定性地生成,相同的架构可以扩展到:生成安全程序(F-CPU F块)、从标签列表CSV填充HMI屏幕、从物料清单配置PROFINET设备名称和IP地址,以及在.ap文件旁边生成客户的命名约定文档。Openness API是脊柱;每个合同的数据是驱动稳定代码路径的JSON或Excel输入。
原文链接:Creating TIA Portal Project Bases with Openness API Scripts
汇智网翻译整理,转载请标明出处