> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Openness API创建TIA Portal项目
- URL: https://www.hubwiz.com/blog/create-tia-portal-projects-with-openness-api/
- Published: 2026-07-27T07:23:42.000Z
- Updated: 2026-11-06T04:27:51.000Z
- Description: 本参考展示了如何使用Openness API创建TIA Portal项目、插入SIMATIC S7-1500站点、填充PLC标签表、实例化功能块并编译结果。
- Author: admin
- Tags: PHYSICAL AI, OPENNESS API, #Import 2026-10-14 14:41

"项目基础"（或"项目基准"）是自动化集成商交付给客户的标准化基础：控制器硬件配置、商定的标签命名方案、客户端的可重用功能块（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`实例开始。该实例通过进程附加的工厂获取，整个会话的生命周期取决于该对象。

```csharp
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的连接](https://docs.tia.siemens.cloud/r/en-us/v21/tia-portal-openness-api-for-automation-of-engineering-workflows/tia-portal-openness-api/general-functions/terminating-the-connection-to-the-tia-portal?ref=hubwiz.com)。

**顺序很重要。**始终在`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通道模拟输出         |

```csharp
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`属性暴露它。

```csharp
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映射。

```csharp
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\_*）遵循客户端的合同规范。因为脚本将名称作为常量保存，所以更合同比的命名规则只需要一次查找和替换。

如果目标是"用自然语言把项目基础搭出来"，不必自己写这一整套 Openness 骨架：[TiaLink](https://tia.hubwiz.com/tia-link?utm%5Fsource=blog&utm%5Fmedium=article&utm%5Fcampaign=create-tia-portal-projects-with-openness-api) 提供的是同样粒度的工具（建项目、加 CPU、插模块、建标签表、建 FB、编译），Agent 直接调，你仍然用本文的验收清单核对结果。

## 7\. 添加功能块和程序逻辑

Openness API通过`PlcSoftware.BlockGroup`暴露块容器。创建FB需要指定名称、编程语言和实例DB名称。块体本身在传统意义上不能通过Openness编辑——你通过调用`Composition`服务或导入XML/PLCopen导出来通过API生成代码。

### 7.1 创建空白FB

```csharp
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将导入视为原子操作：

```csharp
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`对象集合。

```csharp
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>`存档。

```csharp
static void SaveProject(Project project)
{
    project.Save();
    // SaveAs(...)也可用于以不同名称写入新位置；
    // 对于将基础分支为每个客户端的副本很有用。
}

```

**保存与释放：**`Save`写入项目文件；`Dispose`释放内存锁。两者是独立的。如果你想让更改持久化，请始终在`Dispose`之前`Save`。

## 10\. 完整的工作骨架

下面的骨架将前面的部分组合成一个可执行文件。这是生产使用的推荐起点。

```csharp
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](https://industrialmonitordirect.com/fi/blogs/knowledgebase/creating-tia-portal-project-bases-with-openness-api-scripts?ref=hubwiz.com)

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

> **这是「AI × 工业 / PLC」专题的一部分。**完整地图见：[AI × 工业 / PLC 完全指南](https://www.hubwiz.com/blog/ai-industrial-stack/)