七天学习plc加机器视觉之 openness 接口开发 ,Visual Studio 2022中配置c# 支持openness 开发西门子复杂工程

Openness 开源项目工程代码实现方案 | Openness 源码开发、部署与使用全指南
 

一、七天学会plc 加机器视觉 之如何c# 中配置openness  接口开发plc  复杂工程
以下内容不只示操作流程,更是技术参考文档,属内部vip文档公司,包括提示词工程参考,

 

1.1 关键词布局

 
核心关键词:Openness、Openness 工程代码、Openness 源码、Openness 代码开发、Openness 项目部署
 
长尾关键词:Openness 代码编写规范、Openness 工程搭建、Openness 源码使用教程、Openness 开发实战
 

1.2 项目简介

 
Openness 是一款主打开放架构、高兼容性、可拓展性的通用工程框架 / 工具项目,本文档围绕 Openness 工程代码 完成全流程开发、代码编写、环境配置、运行调试、二次开发等内容梳理,面向开发人员、运维人员、技术学习者提供完整的 Openness 工程代码落地方案。
 
本文档完整记录 Openness 工程代码的设计思路、代码实现、目录结构、运行逻辑,可直接用于项目搭建、二次开发与生产环境部署。
 

二、Openness 工程整体架构(Visual Studio 2022中配置c# 支持openness 开发西门子复杂工程)码、定制模块,可基于本文基础代码进行二次开发。重要部份不只流程,更有提示词参考更有价值 

TIA Portal Openness 工程代码实操全文档(博图自动化开发)

 
关键词:TIA Portal Openness、西门子博图二次开发、C# 操作博图、SCL 代码生成、LAD 梯形图生成、博图自动化编程
 

目录

 
  1. 什么是 Openness(先纠正一个误解)
  2. 环境与前置条件
  3. 创建工程并引用 Openness
  4. 连接博图与读取项目
  5. 生成程序方式一:SCL(推荐入门)
  6. 生成程序方式二:梯形图 LAD(核心,含成功方案)
  7. 全部踩坑根因速查表
  8. 可复用核心代码(实测通过)
  9. 标准工作流程与速查清单
 

 

1. 什么是 Openness(先纠正一个误解)

 
TIA Portal Openness 是西门子官方的 .NET API,可通过代码自动化操作博图软件,支持创建项目、添加设备、生成程序块、编译、导入导出、程序下载等全流程操作。
 
表格
 
开发需求对应技术方案
自动化操作博图(建项目、编写程序块、编译、下载) ✅ Openness
博图程序下载 / 在线连接 S7-1200 / PLCSIM 仿真 ✅ Openness 内置 DownloadProvider / OnlineProvider
直接读写运行中 PLC 的实时变量 ❌ 不使用 Openness,推荐 S7 通信 (Sharp7/S7netPlus) 或 OPC UA
 
核心总结:Openness 操作的是博图软件 / 离线工程数据,并非运行状态下的 PLC;它读取的是项目内组态设备,而非仿真 / 真机中正在运行的 PLC。
 
 

 

2. 环境与前置条件(缺一不可)

 

2.1 基础环境要求

 
表格
 
项目版本 / 配置补充说明
TIA Portal V18(内置 Openness 组件) 博图安装默认附带 Openness,无需单独安装
开发工具 Visual Studio 2022 仅需创建 C# 控制台项目即可
运行框架 .NET Framework 4.8 V18 推荐版本,4.7.2 易出现 DLL 加载失败
核心引用 DLL Siemens.Engineering.dll 路径:C:\Program Files\Siemens\Automation\Portal V18\PublicAPI\V18\
Windows 用户组 加入 Siemens TIA Openness 高频报错根源,未加入会直接提示「拒绝访问」
 

2.2 加入 Openness 用户组(必做)

 
以管理员身份打开 PowerShell,执行以下命令:
 
powershell
 
 
# 将当前用户加入 Openness 用户组
net localgroup "Siemens TIA Openness" "$env:USERNAME" /add
# 验证是否添加成功
net localgroup "Siemens TIA Openness"
 
 
重要:添加完成后必须注销 Windows 并重新登录,配置方可生效。
 
 

 

3. 创建工程并引用 Openness

 

3.1 新建项目

 
在 Visual Studio 2022 中,新建 .NET Framework 4.8 控制台应用。
 

3.2 项目引用配置

 
编辑 .csproj 文件,添加 Siemens.Engineering 引用,必须设置 Private=False(运行时自动从注册表解析路径,不复制 DLL 到 bin 目录):
 
xml
 
 
<Reference Include="Siemens.Engineering">
  <HintPath>C:\Program Files\Siemens\Automation\Portal V18\PublicAPI\V18\Siemens.Engineering.dll</HintPath>
  <SpecificVersion>False</SpecificVersion>
  <Private>False</Private>
</Reference>
 
 

3.3 App.config 框架配置

 
xml
 
 
<startup>
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
 
 

3.4 程序集解析(AssemblyResolve 核心代码)

 
Siemens.Engineering.dll 不会拷贝至运行目录,需要注册程序集解析事件,从系统注册表精准匹配 V18 版本 DLL。
 
坑点:系统可能存在多个博图版本(V15.1/V16/V17/V18),不做版本判断会加载低版本 DLL,触发 TypeLoadException
 
编码规则:必须在调用任何 Openness 类型前注册解析事件。
 
 
csharp
 
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Win32;
using System.Linq;

namespace TiaOpennessDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 第一步:注册程序集解析事件
            AppDomain.CurrentDomain.AssemblyResolve += OpennessAssemblyResolver;
            // 第二步:执行业务代码
            Run();
        }

        private static void Run()
        {
            // 后续所有博图操作代码写在此方法内
        }

        /// <summary>
        /// Openness DLL 注册表解析器(精准匹配版本)
        /// </summary>
        private static Assembly OpennessAssemblyResolver(object sender, ResolveEventArgs args)
        {
            var requested = new AssemblyName(args.Name);
            if (!requested.Name.StartsWith("Siemens.Engineering", StringComparison.OrdinalIgnoreCase))
                return null;

            using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)
                .OpenSubKey(@"SOFTWARE\Siemens\Automation\Openness"))
            {
                if (baseKey == null) return null;
                var candidates = new List<KeyValuePair<Version, string>>();

                foreach (string ver in baseKey.GetSubKeyNames())
                    using (var api = baseKey.OpenSubKey(ver + @"\PublicAPI"))
                    {
                        if (api == null) continue;
                        foreach (string asmVer in api.GetSubKeyNames())
                            using (var k = api.OpenSubKey(asmVer))
                            {
                                string path = k?.GetValue(requested.Name) as string;
                                if (string.IsNullOrEmpty(path) || !File.Exists(path)) continue;
                                Version.TryParse(asmVer, out Version v);
                                candidates.Add(new KeyValuePair<Version, string>(v ?? new Version(0, 0), path));
                            }
                    }

                if (candidates.Count == 0) return null;
                // 优先匹配请求版本,无则使用最高版本
                string chosen = candidates.FirstOrDefault(c => c.Key == requested.Version).Value
                                ?? candidates.OrderByDescending(c => c.Key).First().Value;
                return Assembly.LoadFrom(chosen);
            }
        }
    }
}
 
 

 

4. 连接博图与读取项目

 

4.1 附加到已运行的博图进程

 
推荐用法:自动识别本地已打开的博图进程并附加,读取项目、设备信息。
 
csharp
 
 
using Siemens.Engineering;
using Siemens.Engineering.HW;
using System.Collections.Generic;

/// <summary>
/// 附加博图进程,读取项目与设备信息
/// </summary>
private static void AttachTiaAndReadProject()
{
    foreach (var process in TiaPortal.GetProcesses())
    {
        using (TiaPortal tia = process.Attach())
        {
            Project project = tia.Projects.FirstOrDefault();
            if (project == null) continue;

            Console.WriteLine($"当前项目:{project.Name},设备总数:{project.Devices.Count}");

            foreach (Device device in project.Devices)
                foreach (DeviceItem item in EnumerateDeviceItems(device))
                    Console.WriteLine($" - {item.Name} [{item.Classification}] {item.TypeIdentifier}");
        }
    }
}

/// <summary>
/// 递归遍历设备下所有子模块
/// </summary>
private static IEnumerable<DeviceItem> EnumerateDeviceItems(Device device)
{
    foreach (DeviceItem item in device.DeviceItems)
    {
        yield return item;
        foreach (var c in EnumerateDeviceItems(item))
            yield return c;
    }
}
private static IEnumerable<DeviceItem> EnumerateDeviceItems(DeviceItem parent)
{
    foreach (DeviceItem item in parent.DeviceItems)
    {
        yield return item;
        foreach (var c in EnumerateDeviceItems(item))
            yield return c;
    }
}
 
 
注意:首次执行 Attach() 时,博图会弹出访问授权框,需手动点击是,程序才会继续运行。
 
 

4.2 查找 PLC 软件容器

 
定位 CPU 对应的 PLC 程序容器(程序块、变量表均存储在此容器下):
 
csharp
 
 
using Siemens.Engineering.HW.Features;
using Siemens.Engineering.SW;

/// <summary>
/// 查找项目中的 PlcSoftware(PLC 程序根容器)
/// </summary>
private static PlcSoftware FindPlcSoftware(Project project)
{
    foreach (Device d in project.Devices)
        foreach (DeviceItem it in EnumerateDeviceItems(d))
        {
            var container = it.GetService<SoftwareContainer>();
            if (container != null && container.Software is PlcSoftware plc)
                return plc;
        }
    return null;
}
 
 

4.3 强制切换离线模式(必做)

 
博图处于在线状态(连接仿真 / 真机)时,无法修改程序,会报错 This function is not supported in online mode。修改程序前强制切离线:
 
csharp
 
 
using Siemens.Engineering.Online;

/// <summary>
/// 将所有设备切换为离线模式
/// </summary>
private static void GoOfflineIfNeeded(Project project)
{
    foreach (Device d in project.Devices)
        foreach (DeviceItem it in EnumerateDeviceItems(d))
        {
            var online = it.GetService<OnlineProvider>();
            if (online != null && online.State != OnlineState.Offline)
                online.GoOffline();
        }
}
 
 

 

5. 生成程序方式一:SCL(推荐入门)

 
采用 外部源 (External Source) 方案:直接编写 .scl 文本代码,由博图自动编译为程序块,无需手动处理复杂 XML,稳定性最高、排错简单。
 
csharp
 
 
using Siemens.Engineering.SW.ExternalSources;
using Siemens.Engineering.SW;
using System.Text;

/// <summary>
/// 生成 SCL 结构化控制语言程序块
/// </summary>
private static void GenerateScl(PlcSoftware plc)
{
    string dir = Environment.CurrentDirectory;
    string sclFile = Path.Combine(dir, "MotorControl_FwdRev.scl");

    // 编写 SCL 源代码
    string sclContent =
"FUNCTION \"MotorControl_FwdRev\" : Void\r\n" +
"{ S7_Optimized_Access := 'TRUE' }\r\n" +
"VERSION : 0.1\r\n\r\nBEGIN\r\n" +
"    %Q0.0 := (%I0.0 OR %Q0.0) AND NOT %I0.2 AND NOT %Q0.1;   // 正转,与反转互锁\r\n" +
"    %Q0.1 := (%I0.1 OR %Q0.1) AND NOT %I0.2 AND NOT %Q0.0;   // 反转,与正转互锁\r\n" +
"END_FUNCTION\r\n";

    // 写入本地文件(UTF-8 编码)
    File.WriteAllText(sclFile, sclContent, new UTF8Encoding(true));

    // 删除历史同名外部源,避免冲突
    var oldSource = plc.ExternalSourceGroup.ExternalSources.Find("MotorControl_FwdRev");
    if (oldSource != null) oldSource.Delete();

    // 从文件创建外部源并生成程序块
    PlcExternalSource source = plc.ExternalSourceGroup.ExternalSources.CreateFromFile("MotorControl_FwdRev", sclFile);
    var resultBlocks = source.GenerateBlocksFromSource(GenerateBlockOption.KeepOnError);

    foreach (var obj in resultBlocks)
    {
        if (obj is PlcBlock blk)
            Console.WriteLine($"✓ 成功生成块: {blk.Name} | 语言: {blk.ProgrammingLanguage}");
    }
}
 
 
适用场景:快速开发、逻辑调试、自动化生成代码;编译报错信息直观,是入门首选方案。
 
 

 

6. 生成程序方式二:梯形图 LAD(核心)

 

6.1 开发原则(避坑核心)

 
禁止手写完整梯形图 XML。TIA V18 的 LAD 块 XML 包含命名空间、接口、多语言文本等复杂结构,手写极易报错。
 
✅ 标准方案:
 
  1. 从博图导出正常可编译的梯形图块 XML(复用博图原生结构);
  2. 仅修改内部 <NetworkSource> 节点,注入梯形图网络 FlgNet
  3. 重新导入 XML 并编译。
 

6.2 FlgNet 基础结构示例

 
示例:M0.0 常闭触点 → Q0.0 线圈
 
xml
 
 
<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
  <Parts>
    <Access Scope="GlobalVariable" UId="21"><Symbol><Component Name="M0_0" /></Symbol></Access>
    <Access Scope="GlobalVariable" UId="22"><Symbol><Component Name="Q0_0" /></Symbol></Access>
    <Part Name="Contact" UId="23"><Negated Name="operand" /></Part>
    <Part Name="Coil" UId="24" />
  </Parts>
  <Wires>
    <Wire UId="25"><Powerrail /><NameCon UId="23" Name="in" /></Wire>
    <Wire UId="26"><IdentCon UId="21" /><NameCon UId="23" Name="operand" /></Wire>
    <Wire UId="27"><NameCon UId="23" Name="out" /><NameCon UId="24" Name="in" /></Wire>
    <Wire UId="28"><IdentCon UId="22" /><NameCon UId="24" Name="operand" /></Wire>
  </Wires>
</FlgNet>
 
 
  • 常开触点:删除 <Negated Name="operand"/>
  • 变量引用异常(显示 ???):原因是变量表未提前创建;
  • V18 固定命名空间:http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4
 

6.3 完整可运行代码(实测通过)

 
csharp
 
 
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.SW.Tags;
using Siemens.Engineering.Compiler;
using System.Xml.Linq;

/// <summary>
/// 生成 LAD 梯形图程序(实测可用)
/// </summary>
private static void GenerateLadder(PlcSoftware plc)
{
    string dir = Environment.CurrentDirectory;

    // 1. 提前创建变量表与变量(解决梯形图 ??? 问题)
    PlcTagTable tagTable = plc.TagTableGroup.TagTables.Find("OpennessTags") as PlcTagTable
                           ?? plc.TagTableGroup.TagTables.Create("OpennessTags");
    if (tagTable.Tags.Find("M0_0") == null) tagTable.Tags.Create("M0_0", "Bool", "%M0.0");
    if (tagTable.Tags.Find("Q0_0") == null) tagTable.Tags.Create("Q0_0", "Bool", "%Q0.0");

    // 2. 定位 Main 主程序块
    PlcBlock mainBlock = plc.BlockGroup.Blocks.Find("Main") as PlcBlock
                         ?? plc.BlockGroup.Blocks.OfType<PlcBlock>().FirstOrDefault();
    if (mainBlock == null)
    {
        Console.WriteLine("未找到 Main 块");
        return;
    }

    // 3. 块未编译则先编译(未编译块无法导出 XML)
    if (!mainBlock.IsConsistent)
        mainBlock.GetService<ICompilable>().Compile();

    // 4. 导出原生 XML
    string exportPath = Path.Combine(dir, "Main_export.xml");
    string modifyPath = Path.Combine(dir, "Main_modified.xml");
    mainBlock.Export(new FileInfo(exportPath), ExportOptions.WithDefaults);

    // 5. 编辑 XML,注入梯形图 FlgNet 网络
    XDocument xmlDoc = XDocument.Load(exportPath);
    XElement netSource = xmlDoc.Descendants().First(e => e.Name.LocalName == "NetworkSource");
    XNamespace flgNs = "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4";
    netSource.RemoveNodes();

    // 拼接 FlgNet 内容
    netSource.Add(new XElement(flgNs + "FlgNet",
        new XElement(flgNs + "Parts",
            new XElement(flgNs + "Access", new XAttribute("Scope", "GlobalVariable"), new XAttribute("UId", "21"),
                new XElement(flgNs + "Symbol", new XElement(flgNs + "Component", new XAttribute("Name", "M0_0")))),
            new XElement(flgNs + "Access", new XAttribute("Scope", "GlobalVariable"), new XAttribute("UId", "22"),
                new XElement(flgNs + "Symbol", new XElement(flgNs + "Component", new XAttribute("Name", "Q0_0")))),
            new XElement(flgNs + "Part", new XAttribute("Name", "Contact"), new XAttribute("UId", "23"),
                new XElement(flgNs + "Negated", new XAttribute("Name", "operand"))),
            new XElement(flgNs + "Part", new XAttribute("Name", "Coil"), new XAttribute("UId", "24"))
        ),
        new XElement(flgNs + "Wires",
            new XElement(flgNs + "Wire", new XAttribute("UId", "25"),
                new XElement(flgNs + "Powerrail"),
                new XElement(flgNs + "NameCon", new XAttribute("UId", "23"), new XAttribute("Name", "in"))),
            new XElement(flgNs + "Wire", new XAttribute("UId", "26"),
                new XElement(flgNs + "IdentCon", new XAttribute("UId", "21")),
                new XElement(flgNs + "NameCon", new XAttribute("UId", "23"), new XAttribute("Name", "operand"))),
            new XElement(flgNs + "Wire", new XAttribute("UId", "27"),
                new XElement(flgNs + "NameCon", new XAttribute("UId", "23"), new XAttribute("Name", "out")),
                new XElement(flgNs + "NameCon", new XAttribute("UId", "24"), new XAttribute("Name", "in"))),
            new XElement(flgNs + "Wire", new XAttribute("UId", "28"),
                new XElement(flgNs + "IdentCon", new XAttribute("UId", "22")),
                new XElement(flgNs + "NameCon", new XAttribute("UId", "24"), new XAttribute("Name", "operand")))
        )
    ));

    xmlDoc.Save(modifyPath);

    // 6. 导入修改后的 XML 并编译验证
    var importResult = plc.BlockGroup.Blocks.Import(new FileInfo(modifyPath), ImportOptions.Override);
    foreach (var item in importResult)
    {
        if (item is PlcBlock blk)
        {
            Console.WriteLine($"导入块: {blk.Name} | 语言: {blk.ProgrammingLanguage}");
            CompileBlock(blk);
        }
    }
}

/// <summary>
/// 编译程序块并输出编译日志
/// </summary>
private static void CompileBlock(PlcBlock block)
{
    var compileResult = block.GetService<ICompilable>().Compile();
    Console.WriteLine($"编译结果: {compileResult.State} | 错误数: {compileResult.ErrorCount} | 警告数: {compileResult.WarningCount}");
    foreach (var msg in compileResult.Messages)
        Console.WriteLine($"[{msg.State}] {msg.Path} : {msg.Description}");
}
 
 

 

7. 全部踩坑根因速查表

 
表格
 
序号报错 / 现象根本原因解决方案
1 TypeLoadException 找不到 ProjectBase 加载了低版本 DLL(V15.1) 启用 AssemblyResolve 按版本精准匹配 V18 DLL
2 not supported in online mode 博图处于在线连接状态 调用 OnlineProvider.GoOffline() 切换离线
3 梯形图触点显示 ??? 变量未提前创建 / XML 引用异常 优先创建 PLC 变量表与变量;使用完整导出 XML
4 Inconsistent blocks cannot be exported 程序块从未编译,状态不一致 导出 XML 前先执行 Compile() 编译块
5 Missing 'Namespace' identifier attribute 手写 XML 缺失 V18 命名空间 不手写整块 XML,基于博图导出的原生 XML 修改
6 导出 XML 无 <FlgNet> 节点 原块网络为空 手动向 <NetworkSource> 注入 FlgNet 内容
- 执行 Attach() 后程序阻塞 博图未授权访问 切换到博图,点击授权弹窗「是」
- 控制台中文乱码 系统代码页不匹配 设置控制台编码为 UTF-8(不影响功能运行)
 

 

8. 可复用核心代码汇总

 

8.1 项目引用与命名空间

 
程序集引用
 
  1. Siemens.Engineering.dll(V18,Private=False
  2. System.Xml.Linq
 
通用 using 引用
 
csharp
 
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Linq;
using Microsoft.Win32;
using System.Xml.Linq;

using Siemens.Engineering;
using Siemens.Engineering.HW;
using Siemens.Engineering.HW.Features;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.SW.Tags;
using Siemens.Engineering.SW.ExternalSources;
using Siemens.Engineering.Online;
using Siemens.Engineering.Compiler;
 
 

8.2 通用工具方法

 
文档内所有通用方法:
 
  1. OpennessAssemblyResolver:DLL 注册表解析
  2. EnumerateDeviceItems:递归遍历设备子项
  3. FindPlcSoftware:查找 PLC 程序容器
  4. GoOfflineIfNeeded:批量切离线
  5. CompileBlock:编译块并输出日志
  6. GenerateScl:生成 SCL 代码
  7. GenerateLadder:生成 LAD 梯形图
 

 

9. 标准工作流程与速查清单

 

9.1 一次性环境准备(仅首次配置)

 
  1. 安装 TIA Portal V18 + Visual Studio 2022
  2. 安装 .NET Framework 4.8
  3. 将当前 Windows 用户加入 Siemens TIA Openness 用户组,注销重登
  4. 项目引用 Siemens.Engineering.dll,配置 Private=False
 

9.2 代码运行标准流程

 
  1. 注册 AssemblyResolve(必须最先执行)
  2. 调用 TiaPortal.GetProcesses() + Attach() 附加博图进程,手动授权
  3. 读取项目,调用 FindPlcSoftware 定位 PLC 容器
  4. 执行 GoOfflineIfNeeded,强制切换离线模式
  5. 提前创建 PLC 变量表与变量(规避梯形图 ???
  6. 目标程序块若未编译,先执行 Compile()
  7. 导出原生块 XML,修改 <NetworkSource> 注入梯形图网络
  8. 导入 XML 覆盖原块,再次编译验证
  9. 保存项目,流程结束
 

 

文档使用说明

 
  1. 格式:Markdown 格式,可直接导入 Typora、GitBook、语雀、CSDN 等平台;全选复制可粘贴至 Word。
  2. SEO 优化:全文自然植入核心关键词,层级清晰、目录索引完善,适合知识库 / 技术博客发布。
  3. 代码说明:所有 C# 代码基于 TIA Portal V18 + .NET Framework 4.8 实测通过,可直接复用。
posted @ 2026-06-07 21:24  鬼门元歌  阅读(34)  评论(0)    收藏  举报