Openness API创建TIA Portal项目

本参考展示了如何使用Openness API创建TIA Portal项目、插入SIMATIC S7-1500站点、填充PLC标签表、实例化功能块并编译结果。

Openness API创建TIA Portal项目
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

"项目基础"(或"项目基准")是自动化集成商交付给客户的标准化基础:控制器硬件配置、商定的标签命名方案、客户端的可重用功能块(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. 前提条件

  1. TIA Portal V18、V19、V20或V21,已安装Openness选项。在开始→Siemens Automation→TIA Portal Openness下验证。许可证与标准STEP 7 Professional安装捆绑提供。
  2. Visual Studio 2019或2022,使用.NET Framework 4.8(V17/V18)或.NET 6.0/8.0(V19+取决于版本)。Openness程序集针对与TIA Portal主版本匹配的框架版本构建。
  3. 引用Openness程序集:在你的C#项目中添加引用:
  • Siemens.Engineering.dll
  • Siemens.Engineering.Hmi.dll(仅在脚本化HMI时)
  • Siemens.Engineering.AddIn.dll(仅在脚本化Add-In表面时) 位置:C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\
  1. 至少交互式启动TIA Portal一次,以便创建用户配置文件;无头自动化仍然需要本地配置文件目录。
  2. 在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枚举接受WithUserInterfaceWithoutUserInterface。对于无人值守的CI管道使用WithoutUserInterface;当你需要交互式观察结果进行调试时使用WithUserInterface

3.1 正确终止连接

Openness建立在IDisposable契约之上;Dispose方法断开与TIA Portal的连接并释放COM风格的RCW。未能对ProjectTiaPortal调用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_ConveyorTags_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.ErrorWarningInformation0x8000xxxx范围内的错误代码通常表示缺少硬件配置,而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. 验证清单

  1. 在匹配版本的TIA Portal中打开生成的.ap<version>文件。项目应在没有迁移提示的情况下打开。
  2. 确认项目树→PLC_1→设备配置显示S7-1515-2 PN和两个I/O模块分别在插槽1和2中。
  3. 确认PLC标签→Tags_System包含具有预期数据类型和I/O地址的合标签集。
  4. 确认程序块包含导入的FB及其实例DB。
  5. 从UI触发编译→软件(全部重建)。它必须以0个错误和0个警告完成。
  6. 将项目导出为项目主文件,并使用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

汇智网翻译整理,转载请标明出处