七天学习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 梯形图生成、博图自动化编程
目录
- 什么是 Openness(先纠正一个误解)
- 环境与前置条件
- 创建工程并引用 Openness
- 连接博图与读取项目
- 生成程序方式一:SCL(推荐入门)
- 生成程序方式二:梯形图 LAD(核心,含成功方案)
- 全部踩坑根因速查表
- 可复用核心代码(实测通过)
- 标准工作流程与速查清单
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 包含命名空间、接口、多语言文本等复杂结构,手写极易报错。
✅ 标准方案:
- 从博图导出正常可编译的梯形图块 XML(复用博图原生结构);
- 仅修改内部
<NetworkSource>节点,注入梯形图网络FlgNet; - 重新导入 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 项目引用与命名空间
程序集引用
Siemens.Engineering.dll(V18,Private=False)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 通用工具方法
文档内所有通用方法:
OpennessAssemblyResolver:DLL 注册表解析EnumerateDeviceItems:递归遍历设备子项FindPlcSoftware:查找 PLC 程序容器GoOfflineIfNeeded:批量切离线CompileBlock:编译块并输出日志GenerateScl:生成 SCL 代码GenerateLadder:生成 LAD 梯形图
9. 标准工作流程与速查清单
9.1 一次性环境准备(仅首次配置)
- 安装 TIA Portal V18 + Visual Studio 2022
- 安装 .NET Framework 4.8
- 将当前 Windows 用户加入
Siemens TIA Openness用户组,注销重登 - 项目引用
Siemens.Engineering.dll,配置Private=False
9.2 代码运行标准流程
- 注册
AssemblyResolve(必须最先执行) - 调用
TiaPortal.GetProcesses()+Attach()附加博图进程,手动授权 - 读取项目,调用
FindPlcSoftware定位 PLC 容器 - 执行
GoOfflineIfNeeded,强制切换离线模式 - 提前创建 PLC 变量表与变量(规避梯形图
???) - 目标程序块若未编译,先执行
Compile() - 导出原生块 XML,修改
<NetworkSource>注入梯形图网络 - 导入 XML 覆盖原块,再次编译验证
- 保存项目,流程结束
文档使用说明
- 格式:Markdown 格式,可直接导入 Typora、GitBook、语雀、CSDN 等平台;全选复制可粘贴至 Word。
- SEO 优化:全文自然植入核心关键词,层级清晰、目录索引完善,适合知识库 / 技术博客发布。
- 代码说明:所有 C# 代码基于 TIA Portal V18 + .NET Framework 4.8 实测通过,可直接复用。
浙公网安备 33010602011771号