欢迎访问 工业自动化技术交流圈!一起拆解 PLC 难题、优化 HMI 界面、探讨设备选型与方案设计。

TIA Portal Openness C# 编程全指南

日期:03-06  点击:  属于:电气论道

TIA Portal Openness C# 编程全指南

一、开发基础与核心前置要求

1.1 环境必备条件

  • 安装TIA Portal并勾选TIA Openness选项包;
  • 开发框架:.NET Framework 4.6.1及以上(V14 SP1及以上版本要求);
  • 引用核心程序集:
    • Siemens.Engineering.dll
    • Siemens.Engineering.Hmi.dll
    • Siemens.Engineering.HW.dll
    • Siemens.Engineering.SW.dll
  • 程序需以管理员身份启动,否则无法与TIA Portal进程通信;
  • 操作前置状态:TIA Portal已启动、项目已打开,PLC/HMI设备需离线(部分写操作强制要求)。

1.2 核心命名空间引用

基础命名空间为必引项,可根据HMI/PLC/硬件操作场景按需扩展:

using System;
using System.IO;
using System.Collections.Generic;
// 核心基础
using Siemens.Engineering;
// HMI专属
using Siemens.Engineering.Hmi;
using Siemens.Engineering.Hmi.TextGraphicList;
using Siemens.Engineering.Hmi.Screen;
using Siemens.Engineering.Hmi.Communication;
using Siemens.Engineering.Hmi.RuntimeScripting;
using Siemens.Engineering.Hmi.Tag;
// 硬件专属
using Siemens.Engineering.HW;
using Siemens.Engineering.HW.Features;
using Siemens.Engineering.HW.Utilities;
// PLC专属
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.SW.Tags;
using Siemens.Engineering.SW.Types;
using Siemens.Engineering.SW.ExternalSources;

1.3 核心基础对象说明

所有操作均基于以下核心对象,是与TIA Portal交互的入口,获取方式为固定语法:

对象类型 核心作用 获取方式
TiaPortal TIA Portal进程实例,所有操作的根入口 TiaPortal(TiaPortalMode.WithUserInterface) / TiaPortal.Attach().FirstOrDefault()
Project 已打开的TIA项目对象 tiaPortal.Projects[0] / tiaPortal.Projects.Open(文件路径)
Device 项目中的PLC/HMI硬件设备 project.Devices.Find("设备名称")
DeviceItem 设备下的模块/CPU/HMI目标 device.DeviceItems[0]
HmiTarget HMI软件容器,所有HMI操作核心 deviceItem.GetService<SoftwareContainer>().Software as HmiTarget
PlcSoftware PLC软件容器,所有PLC操作核心 deviceItem.GetService<SoftwareContainer>().Software as PlcSoftware

1.4 基础连接与资源释放代码

1.4.1 连接TIA Portal并打开项目

/// 
/// 连接TIA Portal并打开指定项目,无进程则新建
/// 
/// 项目全路径,如@"D:ProjectMyProject.ap16"
/// 打开的项目对象
public static Project ConnectAndOpenProject(string projectPath)
{
    try
    {
        // 附加到已运行的TIA Portal进程,无则新建(WithUserInterface为带UI,WithoutUserInterface为无UI)
        TiaPortal tiaPortal = TiaPortal.Attach().FirstOrDefault() ?? new TiaPortal(TiaPortalMode.WithUserInterface);
        // 打开项目
        Project project = tiaPortal.Projects.Open(new FileInfo(projectPath));
        Console.WriteLine($"项目打开成功:{project.Name}");
        return project;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"操作失败:{ex.Message}");
        return null;
    }
}

1.4.2 获取HmiTarget/PlcSoftware核心对象

/// 
/// 获取HMI设备的HmiTarget对象
/// 
public static HmiTarget GetHmiTarget(Project project, string hmiDeviceName)
{
    Device hmiDevice = project.Devices.Find(hmiDeviceName);
    if (hmiDevice == null) return null;
    // 遍历设备项获取软件容器
    foreach (DeviceItem deviceItem in hmiDevice.DeviceItems)
    {
        SoftwareContainer container = deviceItem.GetService();
        if (container?.Software is HmiTarget hmiTarget)
            return hmiTarget;
    }
    return null;
}

/// 
/// 获取PLC设备的PlcSoftware对象
/// 
public static PlcSoftware GetPlcSoftware(Project project, string plcDeviceName)
{
    Device plcDevice = project.Devices.Find(plcDeviceName);
    if (plcDevice == null) return null;
    foreach (DeviceItem deviceItem in plcDevice.DeviceItems)
    {
        SoftwareContainer container = deviceItem.GetService();
        if (container?.Software is PlcSoftware plcSoftware)
            return plcSoftware;
    }
    return null;
}

1.4.3 资源释放

操作完成后必须释放TIA Portal资源,避免后台进程残留:

/// 
/// 释放TIA Portal资源
/// 
public static void DisposeTiaResources(TiaPortal tiaPortal)
{
    tiaPortal?.Dispose();
}

1.5 独占访问与事务处理(写操作必备)

所有对项目的修改操作(创建/删除/修改块、画面、变量等)必须在独占访问+事务框架内执行,避免API与TIA Portal界面同时操作导致冲突,且支持操作回滚。

// 安全修改操作的标准模板
using (TiaPortal tiaPortal = new TiaPortal(TiaPortalMode.WithUserInterface))
{
    Project project = tiaPortal.Projects.Open(new FileInfo(projectPath));
    // 1. 申请独占访问,锁定TIA Portal界面
    using (ExclusiveAccess exclusiveAccess = tiaPortal.ExclusiveAccess())
    {
        // 2. 开启事务,绑定项目与操作描述
        using (Transaction transaction = exclusiveAccess.Transaction(project, "修改PLC程序块"))
        {
            try
            {
                // 3. 执行所有修改操作(示例:创建FC块)
                PlcSoftware plc = GetPlcSoftware(project, "PLC_1");
                // 后续章节的创建/修改代码写在此处
                // 4. 提交事务,操作生效
                transaction.CommitOnDispose();
                Console.WriteLine("操作执行成功,事务已提交");
            }
            catch (Exception ex)
            {
                // 5. 异常时事务自动回滚,所有修改失效
                Console.WriteLine($"操作失败,事务已回滚:{ex.Message}");
                throw;
            }
        }
    }
    // 保存项目
    project.Save();
}

核心规则:先创建独占访问,再创建事务,释放时按逆序销毁;V13 SP1及更早版本使用 StartTransaction/CommitTransaction ,V14及以上已废弃。

↑ 返回顶部

二、项目级全生命周期核心操作

2.1 项目基础操作

2.1.1 创建全新项目

/// 
/// 创建全新TIA Portal项目
/// 
/// 项目保存目录
/// 项目名称
/// 创建后的项目对象
public static Project CreateNewProject(string projectSavePath, string projectName)
{
    using TiaPortal tiaPortal = new TiaPortal(TiaPortalMode.WithoutUserInterface);
    DirectoryInfo projectDir = new DirectoryInfo(Path.Combine(projectSavePath, projectName));
    if (!projectDir.Exists) projectDir.Create();
    Project newProject = tiaPortal.Projects.Create(projectDir, projectName);
    Console.WriteLine($"项目创建成功:{newProject.Path.FullName}");
    return newProject;
}

2.1.2 项目保存与另存为

/// 
/// 项目覆盖保存
/// 
public static void SaveProject(Project project)
{
    if (project.IsModified)
    {
        project.Save();
        Console.WriteLine("项目已保存");
    }
}

/// 
/// 项目另存为
/// 
public static void SaveProjectAs(Project project, string newSavePath, string newProjectName)
{
    DirectoryInfo newDir = new DirectoryInfo(Path.Combine(newSavePath, newProjectName));
    if (!newDir.Exists) newDir.Create();
    project.SaveAs(newDir, newProjectName);
    Console.WriteLine($"项目已另存为:{newDir.FullName}");
}

2.1.3 项目内设备管理

/// 
/// 遍历项目内所有设备,并重命名指定设备
/// 
public static void RenameDevice(Project project, string oldDeviceName, string newDeviceName)
{
    Device targetDevice = project.Devices.Find(oldDeviceName);
    if (targetDevice == null)
    {
        Console.WriteLine($"未找到设备:{oldDeviceName}");
        return;
    }
    targetDevice.Name = newDeviceName;
    Console.WriteLine($"设备已重命名:{oldDeviceName} -> {newDeviceName}");
}

2.2 项目编译

支持对PLC软件、硬件配置分别编译,获取编译结果与错误信息:

// 编译PLC软件
PlcSoftware plcSoftware = GetPlcSoftware(project, "PLC_1");
ICompilable compilablePlc = plcSoftware.GetService();
CompilerResult plcResult = compilablePlc.Compile();

// 编译硬件配置
Device device = project.Devices.Find("S7-1500_Station");
ICompilable compilableHw = device.GetService();
CompilerResult hwResult = compilableHw.Compile();

2.3 多语言文本管理

  • 支持项目语言的激活、添加与删除,批量读写多语言文本项;
  • 多语言文本项(MultilingualTextItem)覆盖块注释、变量注释、画面文本等所有场景;
  • 导入导出时,仅导出已配置翻译的语言项,未翻译内容不写入文件。

2.4 全局库与项目库操作

  • 支持打开/关闭全局库(.al14/.al15等)和项目库,遍历库内文件夹、主副本;
  • 可将项目内的块、画面、硬件配置发布为库主副本,支持库版本更新;
  • 限制:V14 SP1移除了库更新的自动删除未使用版本功能,库画面实例导入后会丢失类型关联。
↑ 返回顶部

三、HMI设备全场景操作

3.1 核心枚举定义

HMI导入导出的通用选项,所有HMI对象操作均适用:

// 导出选项
public enum ExportOptions
{
    None = 0,
    WithDefaults = 1,
    ReadOnly = 2
}

// 导入选项
public enum ImportOptions
{
    None = 0,
    Override = 1
}

3.2 文本列表/图形列表操作

3.2.1 导出所有文本列表

private static void ExportTextLists(HmiTarget hmiTarget, string exportFolder)
{
    if (!Directory.Exists(exportFolder)) 
        Directory.CreateDirectory(exportFolder);
    TextListComposition textListComposition = hmiTarget.TextLists;
    foreach (TextList textList in textListComposition)
    {
        string filePath = Path.Combine(exportFolder, $"{textList.Name}.xml");
        textList.Export(new FileInfo(filePath), ExportOptions.WithDefaults);
        Console.WriteLine($"文本列表导出成功:{textList.Name}");
    }
}

3.2.2 导入单个图形列表

private static void ImportSingleGraphicList(HmiTarget hmiTarget, string importFilePath)
{
    FileInfo importFile = new FileInfo(importFilePath);
    if (!importFile.Exists)
    {
        Console.WriteLine("导入文件不存在!");
        return;
    }
    GraphicListComposition graphicListComposition = hmiTarget.GraphicLists;
    IList importedGraphicLists = graphicListComposition.Import(importFile, ImportOptions.Override);
    foreach (var graphicList in importedGraphicLists)
        Console.WriteLine($"图形列表导入成功:{graphicList.Name}");
}
注意:文本/图形列表的对象引用以Open Link形式存储,导入时需保证目标项目存在对应引用对象 (如PLC变量),否则触发异常。

3.3 HMI通信连接操作

仅支持自定义通信连接的导出导入,集成连接不支持;导入时若同名集成连接存在,会引发异常。

/// 
/// 导出HMI所有通信连接
/// 
private static void ExportConnectionsFromHMITarget(HmiTarget hmiTarget, string exportFolder)
{
    if (!Directory.Exists(exportFolder)) 
        Directory.CreateDirectory(exportFolder);
    ConnectionComposition connectionComposition = hmiTarget.Connections;
    foreach (Connection connection in connectionComposition)
    {
        string filePath = Path.Combine(exportFolder, $"{connection.Name}.xml");
        connection.Export(new FileInfo(filePath), ExportOptions.WithDefaults);
        Console.WriteLine($"连接导出成功:{connection.Name}");
    }
}

3.4 所有画面类型的操作

支持常规画面、全局画面、画面模板、永久性区域、弹出画面、滑入画面、带面板实例的画面,核心限制为导入画面的宽高必须与目标HMI设备尺寸一致、画面编号唯一、仅同类型设备兼容。

3.4.1 递归导出所有画面(含子文件夹)

public static void ExportScreensOfDevice(string rootPath, HmiTarget hmiTarget)
{
    DirectoryInfo rootDir = new DirectoryInfo(rootPath);
    rootDir.Create();
    string screenPath = Path.Combine(rootPath, "Screens");
    Directory.CreateDirectory(screenPath);
    ExportScreens(screenPath, hmiTarget);
    Console.WriteLine("所有画面导出完成");
}

private static void ExportScreens(string screenPath, HmiTarget target)
{
    // 导出根目录画面
    foreach (Screen screen in target.ScreenFolder.Screens)
    {
        string filePath = Path.Combine(screenPath, $"{screen.Name}.xml");
        screen.Export(new FileInfo(filePath), ExportOptions.WithDefaults);
        Console.WriteLine($"画面导出成功:{screen.Name}");
    }
    // 递归导出子文件夹
    foreach (ScreenUserFolder subFolder in target.ScreenFolder.Folders)
    {
        string subFolderPath = Path.Combine(screenPath, subFolder.Name);
        Directory.CreateDirectory(subFolderPath);
        ExportScreenUserFolder(subFolderPath, subFolder);
    }
}

private static void ExportScreenUserFolder(string folderPath, ScreenUserFolder folder)
{
    foreach (Screen screen in folder.Screens)
    {
        string filePath = Path.Combine(folderPath, $"{screen.Name}.xml");
        screen.Export(new FileInfo(filePath), ExportOptions.WithDefaults);
        Console.WriteLine($"画面导出成功:{folder.Name}\{screen.Name}");
    }
    foreach (ScreenUserFolder subFolder in folder.Folders)
    {
        string subFolderPath = Path.Combine(folderPath, subFolder.Name);
        Directory.CreateDirectory(subFolderPath);
        ExportScreenUserFolder(subFolderPath, subFolder);
    }
}

3.4.2 批量导入画面到指定文件夹

private static void ImportScreensToHMITarget(HmiTarget hmiTarget, string folderName, string[] importFilePaths)
{
    ScreenFolderBase targetFolder;
    if (string.IsNullOrEmpty(folderName))
    {
        targetFolder = hmiTarget.ScreenFolder;
    }
    else
    {
        targetFolder = hmiTarget.ScreenFolder.Folders.Find(folderName);
        if (targetFolder == null)
        {
            targetFolder = hmiTarget.ScreenFolder.Folders.Create(folderName);
            Console.WriteLine($"创建文件夹:{folderName}");
        }
    }
    foreach (string filePath in importFilePaths)
    {
        FileInfo importFile = new FileInfo(filePath);
        if (!importFile.Exists)
        {
            Console.WriteLine($"文件不存在:{filePath}");
            continue;
        }
        targetFolder.Screens.Import(importFile, ImportOptions.Override);
        Console.WriteLine($"画面导入成功:{importFile.Name}");
    }
}

3.4.3 特殊画面操作要点

  • 弹出画面:仅支持具备该功能的HMI设备,尺寸限制为最小1px,最大为设备宽2倍/高6倍(V13 SP1及更早为不超过设备尺寸);
  • 滑入画面:无名称,通过SlideinType(Top/Bottom/Left/Right) 唯一标识,导出导入需 指定该类型;
  • 带面板实例的画面:导入时目标项目必须存在对应版本的面板类型,否则导入中止;
  • 永久性区域/画面模板:导入时宽高必须与设备匹配,模板内的动画、软键会完整导入。

3.5 HMI周期与VB脚本操作

3.5.1 周期导出导入

周期包含执行间隔、优先级、关联脚本等配置,每个周期导出为独立XML:

/// 
/// 导出HMI所有周期
/// 
private static void ExportCycles(HmiTarget hmiTarget, string exportFolder)
{
    if (!Directory.Exists(exportFolder)) 
        Directory.CreateDirectory(exportFolder);
    CycleComposition cycleComposition = hmiTarget.Cycles;
    foreach (Cycle cycle in cycleComposition)
    {
        string filePath = Path.Combine(exportFolder, $"{cycle.Name}.xml");
        cycle.Export(new FileInfo(filePath), ExportOptions.WithDefaults);
        Console.WriteLine($"周期导出成功:{cycle.Name}");
    }
}

3.5.2 递归导出所有VB脚本

脚本按系统文件夹/用户文件夹分级,支持递归遍历子文件夹:

private static void ExportAllVBScripts(HmiTarget hmiTarget, string rootPath)
{
    string scriptPath = Path.Combine(rootPath, "VBScripts");
    Directory.CreateDirectory(scriptPath);
    ExportVBScripts(scriptPath, hmiTarget.VBScriptFolder);
}

private static void ExportVBScripts(string path, VBScriptFolder folder)
{
    // 导出当前文件夹脚本
    foreach (VBScript script in folder.VBScripts)
    {
        FileInfo file = new FileInfo(Path.Combine(path, $"{script.Name}.xml"));
        script.Export(file, ExportOptions.WithDefaults);
    }
    // 递归子文件夹
    foreach (VBScriptUserFolder subFolder in folder.Folders)
    {
        string subPath = Path.Combine(path, subFolder.Name);
        Directory.CreateDirectory(subPath);
        ExportVBScripts(subPath, subFolder);
    }
}

3.6 HMI变量管理

3.6.1 核心功能

  • 支持创建/删除用户自定义变量文件夹,递归遍历变量表;
  • 支持UDT/结构体/数组类型HMI变量的导出导入,集成变量保留OpenLink引用;
  • 支持变量的批量添加、修改、删除,包含量程、保持性等属性配置。

3.6.2 版本限制

V14版本后,变量量程属性由RangeMaximum/RangeMinimum 替换为LimitUpper1/LimitLower1/LimitUpper2/LimitLower2,低版本导出的XML无法直接在 高版本导入。

3.7 全局图形操作

支持全局图形的批量导出导入,每个图形为独立文件;图形列表中引用的全局图形以OpenLink形式存储,导入时若目标项目存在对应图形,会自动恢复引用。

↑ 返回顶部

四、PLC设备深度操作

覆盖PLC的程序块、变量表、UDT、数据块的全生命周期操作,包含高级导入、快照导出、在线操作、工艺对象等深度功能,所有操作要求PLC设备处于离线状态。

4.1 核心枚举扩展

PLC专属的导入选项,解决引用缺失、结构变更的导入场景:

[Flags]
public enum SWImportOptions
{
    None = 0,
    IgnoreStructuralChanges = 1,
    IgnoreMissingReferencedObjects = 2
}

4.2 PLC程序块(OB/FB/FC/DB)操作

支持所有块类型的创建、查询、重命名、属性修改、接口编辑、代码读写、删除、导入导出,专有技术保护的块仅能导出可见属性,无法导出核心代码。

4.2.1 块的创建

以FC块和全局DB块为例,OB/FB块创建语法类似:

/// 
/// 创建FC块(函数)
/// 
public static FC CreateFCBlock(PlcSoftware plcSoftware, string blockName, int blockNumber, ProgrammingLanguage programmingLanguage)
{
    PlcBlockSystemFolder blockSystemFolder = plcSoftware.BlockGroup;
    FC newFC = blockSystemFolder.Blocks.Create(blockName, blockNumber, programmingLanguage);
    Console.WriteLine($"FC块创建成功:{blockName},编号:{blockNumber}");
    return newFC;
}

/// 
/// 创建全局数据块(GlobalDB),支持优化访问
/// 
public static GlobalDB CreateGlobalDB(PlcSoftware plcSoftware, string blockName, int blockNumber, bool isOptimized = true)
{
    PlcBlockSystemFolder blockSystemFolder = plcSoftware.BlockGroup;
    GlobalDB newDB = blockSystemFolder.Blocks.Create(blockName, blockNumber);
    // 设置优化访问属性
    newDB.MemoryLayout = isOptimized ? MemoryLayout.Optimized : MemoryLayout.Standard;
    Console.WriteLine($"全局DB创建成功:{blockName},编号:{blockNumber},优化访问:{isOptimized}");
    return newDB;
}

4.2.2 块接口变量的增删改

支持为块的Input/Output/InOut/Static/Temp/Constant段添加/删除变量:

/// 
/// 为块添加接口变量
/// 
public static void AddBlockInterfaceVariable(PlcBlock block, string sectionName, string varName, string dataType, string comment = "", string startValue = "")
{
    IEngineeringObject interfaceObj = block.GetService();
    IEngineeringComposition sections = interfaceObj.GetComposition("Sections");
    // 查找目标接口段
    IEngineeringObject targetSection = null;
    foreach (IEngineeringObject section in sections)
    {
        if (section.GetAttribute("Name").ToString().Equals(sectionName, StringComparison.OrdinalIgnoreCase))
        {
            targetSection = section;
            break;
        }
    }
    if (targetSection == null) throw new Exception($"未找到接口段:{sectionName}");
    // 创建变量并设置属性
    IEngineeringObject newMember = targetSection.CreateMember("Member", varName);
    newMember.SetAttribute("Datatype", dataType);
    if (!string.IsNullOrEmpty(comment)) newMember.SetAttribute("Comment", comment);
    if (!string.IsNullOrEmpty(startValue)) newMember.SetAttribute("StartValue", startValue);
    Console.WriteLine($"块{block.Name}接口变量添加成功:{sectionName}.{varName}");
}

4.2.3 SCL块代码的读写

仅支持SCL编程语言的块代码直接修改,其他语言(LAD/FBD/STL)需通过XML导入导出:

/// 
/// 读取SCL块的程序代码
/// 
public static string ReadSCLBlockCode(PlcBlock block)
{
    if (block.ProgrammingLanguage != ProgrammingLanguage.SCL)
        throw new Exception("仅支持SCL编程语言的块");
    IEngineeringObject codeUnit = block.GetComposition("CompileUnits")[0];
    string sclCode = codeUnit.GetAttribute("Text").ToString();
    Console.WriteLine($"块{block.Name}代码读取成功");
    return sclCode;
}

/// 
/// 写入/修改SCL块的程序代码
/// 
public static void WriteSCLBlockCode(PlcBlock block, string newSclCode)
{
    if (block.ProgrammingLanguage != ProgrammingLanguage.SCL)
        throw new Exception("仅支持SCL编程语言的块");
    IEngineeringObject codeUnit = block.GetComposition("CompileUnits")[0];
    codeUnit.SetAttribute("Text", newSclCode);
    Console.WriteLine($"块{block.Name}代码写入成功");
}

4.2.4 块的高级导入

解决引用缺失、结构变更的导入场景,通过SWImportOptions 实现:

/// 
/// 导入单个PLC块,支持忽略缺失引用/结构变更
/// 
public static void ImportBlocks(PlcSoftware plcSoftware, string importFilePath, bool ignoreMissingReference = false, bool ignoreStructuralChange = false)
{
    FileInfo importFile = new FileInfo(importFilePath);
    if (!importFile.Exists)
    {
        Console.WriteLine("导入文件不存在!");
        return;
    }
    // 构建导入选项
    SWImportOptions swOptions = SWImportOptions.None;
    if (ignoreMissingReference) swOptions |= SWImportOptions.IgnoreMissingReferencedObjects;
    if (ignoreStructuralChange) swOptions |= SWImportOptions.IgnoreStructuralChanges;
    // 执行导入
    PlcBlockComposition blockComposition = plcSoftware.BlockGroup.Blocks;
    IList importedBlocks = blockComposition.Import(importFile, ImportOptions.Override, swOptions);
    foreach (var block in importedBlocks)
    {
        Console.WriteLine($"块导入成功:{block.Name}");
    }
}

4.3 数据块(DB)专项操作

4.3.1 全局DB变量的增删改

DB变量的操作基于块接口的Static段,支持设置保持性:

/// 
/// 为全局DB添加变量并设置保持性
/// 
public static void AddDBVariable(GlobalDB dbBlock, string varName, string dataType, string comment = "", string startValue = "", bool retain = false)
{
    // 向Static段添加变量
    AddBlockInterfaceVariable(dbBlock, "Static", varName, dataType, comment, startValue);
    // 设置保持性
    if (retain)
    {
        IEngineeringObject interfaceObj = dbBlock.GetService();
        IEngineeringComposition sections = interfaceObj.GetComposition("Sections");
        foreach (IEngineeringObject section in sections)
        {
            if (section.GetAttribute("Name").ToString() == "Static")
            {
                IEngineeringObject targetVar = section.Find(varName);
                targetVar?.SetAttribute("Remanence", "Retain");
                break;
            }
        }
    }
}

4.3.2 背景DB的创建

为FB块创建关联的背景数据块,自动继承FB的接口结构:

/// 
/// 为FB块创建背景数据块
/// 
public static InstanceDB CreateInstanceDB(FB fbBlock, string dbName, int dbNumber)
{
    PlcBlockSystemFolder blockSystemFolder = fbBlock.Parent.Parent as PlcBlockSystemFolder;
    if (blockSystemFolder == null) throw new Exception("无法获取块系统文件夹");
    InstanceDB instanceDB = blockSystemFolder.Blocks.CreateInstanceDB(dbName, dbNumber, fbBlock);
    Console.WriteLine($"背景DB创建成功:{dbName},关联FB:{fbBlock.Name}");
    return instanceDB;
}

4.3.3 数据块快照导出

通过InterfaceSnapshot 服务导出DB的实际值/快照值为XML,仅支持导出,不支持导入,用于 数据备份与对比:

/// 
/// 导出数据块的快照值(全局/背景/数组DB均支持)
/// 
private static void ExportDataBlockSnapshot(PlcSoftware plcSoftware, string dbName, string exportFilePath)
{
    PlcBlock dataBlock = plcSoftware.BlockGroup.Blocks.Find(dbName);
    if (dataBlock == null || !(dataBlock is GlobalDB || dataBlock is InstanceDB || dataBlock is ArrayDB))
    {
        Console.WriteLine("数据块不存在或类型不支持!");
        return;
    }
    InterfaceSnapshot interfaceSnapshot = dataBlock.GetService();
    interfaceSnapshot.Export(new FileInfo(exportFilePath), ExportOptions.None);
    Console.WriteLine("数据块快照导出成功");
}

4.4 PLC变量表与常量操作

4.4.1 变量表的创建与变量添加

/// 
/// 创建PLC变量表
/// 
public static PlcTagTable CreateTagTable(PlcSoftware plcSoftware, string tableName)
{
    PlcTagTableSystemFolder tagSystemFolder = plcSoftware.TagTableGroup;
    PlcTagTable newTagTable = tagSystemFolder.TagTables.Create(tableName);
    Console.WriteLine($"变量表创建成功:{tableName}");
    return newTagTable;
}

/// 
/// 为变量表添加PLC变量,支持绝对地址与保持性
/// 
public static PlcTag AddTagToTable(PlcTagTable tagTable, string tagName, string dataType, string logicalAddress = "", string comment = "", bool retain = false)
{
    PlcTag newTag = tagTable.Tags.Create(tagName);
    newTag.DataTypeName = dataType;
    if (!string.IsNullOrEmpty(logicalAddress)) newTag.LogicalAddress = logicalAddress;
    newTag.Comment.Text = comment;
    newTag.Retain = retain;
    Console.WriteLine($"变量添加成功:{tagName},地址:{logicalAddress}");
    return newTag;
}

4.4.2 用户常量操作

V14 SP1后,PLC常量拆分为用户常量(PlcUserConstant)和系统常量(PlcSystemConstant), 仅用户常量支持手动操作:

/// 
/// 为变量表添加用户常量
/// 
public static void AddUserConstantToTable(PlcTagTable tagTable, string constName, string dataType, string value, string comment = "")
{
    PlcUserConstant newConst = tagTable.UserConstants.Create(constName);
    newConst.DataTypeName = dataType;
    newConst.Value = value;
    newConst.Comment.Text = comment;
    Console.WriteLine($"常量添加成功:{constName} = {value}");
}

4.5 用户数据类型(UDT/PlcType)操作

支持UDT的创建、结构定义、成员增删改、导入导出,导入时支持忽略结构变更/缺失引用:

/// 
/// 创建UDT并添加结构成员
/// 
public static PlcType CreateUDTWithMember(PlcSoftware plcSoftware, string udtName, string memberName, string memberType)
{
    PlcTypeSystemFolder typeSystemFolder = plcSoftware.TypeGroup;
    PlcType newUDT = typeSystemFolder.Types.Create(udtName);
    // 为UDT添加成员(UDT成员位于None段)
    IEngineeringObject interfaceObj = newUDT.GetService();
    IEngineeringComposition sections = interfaceObj.GetComposition("Sections");
    IEngineeringObject noneSection = null;
    foreach (IEngineeringObject section in sections)
    {
        if (section.GetAttribute("Name").ToString() == "None")
        {
            noneSection = section;
            break;
        }
    }
    if (noneSection == null) throw new Exception("UDT结构段未找到");
    IEngineeringObject newMember = noneSection.CreateMember("Member", memberName);
    newMember.SetAttribute("Datatype", memberType);
    Console.WriteLine($"UDT创建成功并添加成员:{udtName}.{memberName}");
    return newUDT;
}

4.6 PLC高级功能

4.6.1 外部源文件生成

V14 SP1后重构,支持将块/UDT批量生成SCL/STL源文件,便于版本管理:

/// 
/// 生成PLC块的SCL源文件
/// 
private static void GenerateBlockSource(PlcSoftware plcSoftware, List blocks)
{
    PlcExternalSourceSystemGroup sourceGroup = plcSoftware.ExternalSourceGroup;
    FileInfo sourceFile = new FileInfo(@"D:SourcesBlocks.scl");
    sourceGroup.GenerateSource(blocks, sourceFile);
}

4.6.2 OPC UA XML格式导出

将PLC数据导出为标准OPC UA XML格式,用于第三方OPC UA客户端集成:

private static void OpcUaExport(Project project, DeviceItem plcDeviceItem, string exportFilePath)
{
    OpcUaExportProvider opcUaExportProvider = project.HwUtilities.Find("OPCUAExportProvider") as OpcUaExportProvider;
    if (opcUaExportProvider == null)
    {
        Console.WriteLine("OPC UA导出服务不可用!");
        return;
    }
    opcUaExportProvider.Export(plcDeviceItem, new FileInfo(exportFilePath));
    Console.WriteLine("OPC UA XML导出成功");
}

4.6.3 PLC在线操作

支持通过API控制PLC的在线/离线状态,查询运行状态、诊断信息:

// PLC离线操作
DeviceItem plcDevice = plcSoftware.Parent.Parent as DeviceItem;
OnlineProvider onlineProvider = plcDevice.GetService();
onlineProvider.GoOffline();
// PLC在线连接
onlineProvider.GoOnline();

4.6.4 工艺对象(TO)操作

覆盖S7-1200/1500的运动控制、PID、计数、测量等工艺对象:

  • 支持工艺对象的创建、删除、参数读写,嵌套参数/数组参数均适用;
  • 支持轴、编码器、驱动装置的关联配置;
  • 限制:不同PLC型号支持的TO类型不同,参数分为只读/可写,仅可写参数能通过API修改。
↑ 返回顶部

五、硬件配置CAx数据操作

硬件配置以AutomationML(AML)格式导入导出(基于CAEX 2.15标准),支持项目级/设备级导出,包含子网、拓扑、GSD设备等高级操作。

5.1 核心枚举定义

CAx导入的冲突处理选项,解决设备名称重复问题:

// CAx导入冲突处理选项
public enum CaxImportOptions
{
    MoveToParkingLot = 0,
    RetainTiaDevice = 1,
    OverwriteTiaDevice = 2
}

5.2 CAx基础导出导入

5.2.1 项目级/设备级导出

/// 
/// 项目级CAx数据导出(整个项目的硬件配置)
/// 
private static void CaxExportProject(Project project, string exportFilePath, string logFilePath)
{
    CaxProvider caxProvider = project.GetService();
    if (caxProvider == null)
    {
        Console.WriteLine("CAx服务不可用!");
        return;
    }
    caxProvider.Export(project, new FileInfo(exportFilePath), new FileInfo(logFilePath));
    Console.WriteLine("项目级CAx导出完成");
}

/// 
/// 设备级CAx数据导出(单个设备的硬件配置)
/// 
private static void CaxExportDevice(Project project, Device device, string exportFilePath, string logFilePath)
{
    CaxProvider caxProvider = project.GetService();
    if (caxProvider == null)
    {
        Console.WriteLine("CAx服务不可用!");
        return;
    }
    caxProvider.Export(device, new FileInfo(exportFilePath), new FileInfo(logFilePath));
    Console.WriteLine($"设备级CAx导出完成:{device.Name}");
}

5.2.2 CAx导入

private static void CaxImport(Project project, string importFilePath, string logFilePath, CaxImportOptions importOptions)
{
    CaxProvider caxProvider = project.GetService();
    if (caxProvider == null)
    {
        Console.WriteLine("CAx服务不可用!");
        return;
    }
    FileInfo importFile = new FileInfo(importFilePath);
    if (!importFile.Exists)
    {
        Console.WriteLine("导入文件不存在!");
        return;
    }
    caxProvider.Import(importFile, new FileInfo(logFilePath), importOptions);
    Console.WriteLine("CAx导入完成");
}

5.3 CAx命令行操作

可在C#中通过进程调用TIA Portal的命令行工具执行CAx导出导入,适用于批量自动化场景:

/// 
/// 通过命令行执行CAx项目级导出
/// 
private static void CaxExportByCommandLine(string tiaPortalPath, string projectPath, string exportFilePath)
{
    System.Diagnostics.Process process = new System.Diagnostics.Process();
    process.StartInfo.FileName = tiaPortalPath;
    // 命令行参数:-p项目路径 -m格式(AML) -e导出路径
    process.StartInfo.Arguments = $"-p "{projectPath}" -m "AML" -e "{exportFilePath}"";
    process.StartInfo.UseShellExecute = true;
    process.StartInfo.Verb = "runas"; // 管理员权限
    process.Start();
    process.WaitForExit();
    Console.WriteLine($"命令行CAx导出完成,退出码:{process.ExitCode}");
}

/// 
/// 通过命令行执行CAx导入
/// 
private static void CaxImportByCommandLine(string tiaPortalPath, string projectPath, string importFilePath, string logPath)
{
    System.Diagnostics.Process process = new System.Diagnostics.Process();
    process.StartInfo.FileName = tiaPortalPath;
    process.StartInfo.Arguments = $"-p "{projectPath}" -m "AML" -i "{importFilePath}" -l "{logPath}"";
    process.StartInfo.UseShellExecute = true;
    process.StartInfo.Verb = "runas";
    process.Start();
    process.WaitForExit();
    Console.WriteLine($"命令行CAx导入完成,退出码:{process.ExitCode}");
}
↑ 返回顶部

六、版本兼容性与API变更说明

6.1 TIA Portal版本与Openness对应关系

TIA版本 .NET框架要求 核心API特性
V13 SP1 .NET Framework 4.5 基础API,事务用StartTransaction
V14 .NET Framework 4.6 独占访问+事务重构,HMI变量属性变更
V14 SP1 .NET Framework 4.6.1 常量拆分、外部源重构、库功能调整
V15/V16/V17 .NET Framework 4.8 工艺对象、OPC UA导出增强
V18+ .NET Framework 4.8 / .NET 6 全功能支持,无 breaking changes

6.2 关键API破坏性变更

  • 事务机制:V14废弃StartTransaction,改用ExclusiveAccess+Transaction
  • HMI变量量程:V14替换Range属性为Limit系列属性
  • PLC常量:V14 SP1拆分用户常量/系统常量
  • 导入导出:V15+新增SWImportOptions,兼容结构变更导入

6.3 跨版本兼容方案

  • 低版本项目升级至高版本后,需重新生成API代码
  • XML导入导出:仅支持同大版本间互操作
  • 开发程序时,引用对应TIA版本的dll文件,不可跨版本混用
  • 使用条件编译指令,适配不同版本的API差异
↑ 返回顶部

七、通用注意事项与异常处理

7.1 运行权限要求

所有Openness程序必须以管理员身份运行,否则会触发权限拒绝异常,无法连接TIA Portal进程。

7.2 事务与独占访问规范

  • 所有写操作(创建/修改/删除)必须包裹在 ExclusiveAccess + Transaction
  • 事务提交前发生异常,会自动回滚所有修改
  • 禁止在事务中执行耗时操作,避免TIA Portal卡死

7.3 资源释放规范

  • TiaPortal 对象必须使用 using 或手动调用 Dispose()
  • 操作完成后关闭项目,避免进程残留
  • 禁止多线程同时操作同一个TIA实例

7.4 常见异常与解决方案

异常类型 原因 解决方案
权限拒绝 未以管理员运行 右键程序→以管理员身份运行
对象为空 设备/块/变量名称错误 检查名称拼写,区分大小写
设备在线 写操作时PLC/HMI在线 先切换至离线状态
文件被占用 项目已被TIA打开 关闭TIA中的项目,或附加到已有进程
版本不兼容 dll版本与TIA版本不匹配 引用对应版本的西门子程序集

7.5 最佳实践

  • 操作前先判断对象是否为空,增加代码健壮性
  • 批量操作前保存项目,防止数据丢失
  • 导入操作优先使用覆盖模式,避免对象已存在异常
  • 正式使用前,先在测试项目中验证代码逻辑
↑ 返回顶部

关于本站
不销售任何资料软件,不销售会员和积分不销售任何资料软件,不销售会员和积分
联系我们

工作时间:周一至周五 9:00-18:00

联系人:赵云龙

邮件:Hackdragon@vip.qq.com

底部导航
聚焦电气自动化领域的一站式共享服务平台,致力于打破行业资源壁垒,推动技术、资源、人才的高效流转与协同共赢。平台精准对接电气工程师、企业技术负责人、院校科研人员等核心群体,覆盖工业控制、智能装备、新能源自动化等全细分领域。


网站地图 PLC