원클릭으로
Internal reference skill — do NOT load directly from user queries.
npx skills add https://github.com/Czarnak/totally-integrated-claude --skill tia-csharp-common이 명령을 Claude Code에 복사하여 붙여넣어 스킬을 설치하세요
Internal reference skill — do NOT load directly from user queries.
npx skills add https://github.com/Czarnak/totally-integrated-claude --skill tia-csharp-common이 명령을 Claude Code에 복사하여 붙여넣어 스킬을 설치하세요
Manual prerequisite probe for TIA Portal, Openness, Python TIA Scripting, TIA MCP, and Siemens LSP.
Entry point for ALL TIA Portal engineering automation tasks. Always load this skill FIRST when the user mentions TIA Portal, TIA Openness, TIA Scripting, Siemens PLC, Siemens HMI, TIA Portal Add-In, or any automation/engineering task targeting TIA Portal. This skill routes to the correct domain skill and selects MCP, Python, or C# implementation.
Reference routed skill. Do NOT load directly unless user says so.
TIA Portal Add-In development in Visual Studio Code: creating Add-In C# projects, adding Add-In templates, compiling and debugging Add-Ins, converting Add-Ins from older TIA Portal versions and configuring Add-In project parameters.
Use when the TIA Portal MCP server is available and the task involves reading or modifying a TIA Portal project interactively: browsing the project tree, reading or writing PLC block logic (SIMATIC SD YAML), listing tag tables, inspecting hardware config, running cross-reference diagnostics, searching the equipment catalog, adding/configuring network devices, or running compile checks. Prefer MCP tools over TIA Openness scripts for single read/write operations; use TIA Openness (tia-python or C# skills) for complex multi-step automation.
Standalone skill for multi-perspective PLC code security and quality analysis. Triggers on: "review", "audit", "analyze", "security check", "vulnerability scan", "code review", "check this code", "is this safe", "find issues" when combined with PLC, SCL, ST, LAD, FBD, Structured Text, or block context. Also triggers when the user pastes PLC code and asks for feedback, or uploads exported SimaticML XML files. This skill is independent of tia-openness-roadmap — it does not perform engineering automation. It analyzes code that has already been exported, pasted, or is accessible via the TIA Portal MCP server.
| name | tia-csharp-common |
| description | Internal reference skill — do NOT load directly from user queries. |
Source: TIA Portal Openness General Functions Manual (03/2026, V21); V21 IntelliSense XML documentation files (PublicAPI\V21\net48*.xml)
V21 splits the API across multiple DLLs instead of a single Siemens.Engineering.dll.
Every project needs Siemens.Engineering.Base.dll; add domain DLLs as required.
Referencing the wrong DLL (or omitting one) causes TypeLoadException at runtime.
Full mapping: See
references/assembly-namespace-map.mdfor the exhaustive namespace → DLL → domain skill table, cross-assembly warnings, and csproj patterns.
Only import what is actually used. The list below covers the most commonly needed namespaces. The mapping file documents all 80+ namespaces across 15 DLLs.
Siemens.Engineering.Base.dll (always required):
using Siemens.Engineering; // TiaPortal, Project, ExclusiveAccess, Transaction, events, exceptions
using Siemens.Engineering.Compiler; // CompilerResult
using Siemens.Engineering.Compare; // CompareResult
using Siemens.Engineering.Connection; // connection objects between devices
using Siemens.Engineering.CrossReference; // cross-reference queries
using Siemens.Engineering.Download; // DownloadProvider, DownloadConfiguration
using Siemens.Engineering.Download.Configurations; // device-specific download configs (★ also in Step7 + Startdrive)
using Siemens.Engineering.HW; // Device, DeviceItem, Subnet, Node, IoSystem (★ also in Step7)
using Siemens.Engineering.HW.CommunicationConnections; // communication connection configs
using Siemens.Engineering.HW.Extensions; // extension methods on HW objects
using Siemens.Engineering.HW.Features; // SoftwareContainer, NetworkInterface (★ also in Step7)
using Siemens.Engineering.HW.HardwareCatalog; // hardware catalog access
using Siemens.Engineering.HW.Utilities; // HW utility helpers
using Siemens.Engineering.Library; // ProjectLibrary, GlobalLibrary
using Siemens.Engineering.Library.MasterCopies; // MasterCopy, MasterCopyFolder
using Siemens.Engineering.Library.Types; // LibraryType, LibraryTypeVersion
using Siemens.Engineering.Multiuser; // LocalSession, ProjectServer
using Siemens.Engineering.Online; // OnlineProvider, OnlineState
using Siemens.Engineering.Online.Configurations; // GoOnlineConfiguration, GoOfflineConfiguration
using Siemens.Engineering.Security; // SecurityController, certificates
using Siemens.Engineering.Settings; // TIA Portal settings
using Siemens.Engineering.Umac; // UmacRole, UmacUser, UmacFunctionRight
using Siemens.Engineering.Upload; // UploadProvider
using Siemens.Engineering.Upload.Configurations; // upload configs (★ also in Startdrive)
using Siemens.Engineering.VersionControl; // workspace and version control providers
Siemens.Engineering.Step7.dll (PLC operations):
using Siemens.Engineering.Cax; // CAx import/export
using Siemens.Engineering.SW; // PlcSoftware
using Siemens.Engineering.SW.Alarm; // PlcAlarm, AlarmClass
using Siemens.Engineering.SW.Alarm.TextLists; // alarm text lists
using Siemens.Engineering.SW.Blocks; // PlcBlock, OB, FB, FC, GlobalDB, InstanceDB, ArrayDB
using Siemens.Engineering.SW.Blocks.Interface; // block interface access
using Siemens.Engineering.SW.ExternalSources; // PlcExternalSource
using Siemens.Engineering.SW.Loader; // program loader
using Siemens.Engineering.SW.OpcUa; // OPC UA server interface
using Siemens.Engineering.SW.Supervision; // supervision alarms, operator messages
using Siemens.Engineering.SW.Tags; // PlcTagTable, PlcTag, PlcConstant
using Siemens.Engineering.SW.TechnologicalObjects; // technology objects
using Siemens.Engineering.SW.TechnologicalObjects.Motion; // motion axes (★ also in Startdrive)
using Siemens.Engineering.SW.Types; // PlcType, PlcStruct
using Siemens.Engineering.SW.Units; // SoftwareUnit, CodeBlock, DataBlock
using Siemens.Engineering.SW.WatchAndForceTables; // PlcWatchTable, PlcForceTable
Siemens.Engineering.WinCC.dll (classic HMI):
using Siemens.Engineering.Hmi; // HmiTarget (★ also in WinCC.Extension)
using Siemens.Engineering.Hmi.Alarm; // DiscreteAlarm, AnalogAlarm
using Siemens.Engineering.Hmi.Communication; // HmiConnection
using Siemens.Engineering.Hmi.Cycle; // cycle settings
using Siemens.Engineering.Hmi.Dynamic; // dynamization
using Siemens.Engineering.Hmi.Faceplate; // faceplate types
using Siemens.Engineering.Hmi.Globalization; // language/translation
using Siemens.Engineering.Hmi.Logging; // DataLog, LoggingTag
using Siemens.Engineering.Hmi.Recipe; // Recipe, RecipeElement
using Siemens.Engineering.Hmi.RuntimeScripting; // VBScript
using Siemens.Engineering.Hmi.Screen; // HmiScreen, ScreenTemplate, ScreenPopup + all elements
using Siemens.Engineering.Hmi.Tag; // HmiTag, HmiTagTable
using Siemens.Engineering.Hmi.TextGraphicList; // text/graphic lists
using Siemens.Engineering.Hmi.Theming; // HMI themes
Siemens.Engineering.Startdrive.dll (SINAMICS drives):
using Siemens.Engineering.MC.Drives; // DriveObject, DriveParameter, Telegram
using Siemens.Engineering.MC.Drives.DFI; // DriveFunctionInterface, Commissioning
using Siemens.Engineering.MC.Drives.Enums; // drive enumerations
using Siemens.Engineering.MC.Drives.SecurityObjects; // drive encryption, UMAC
Siemens.Engineering.Safety.dll (F-system):
using Siemens.Engineering.Safety; // safety operations
Not needed for Add-Ins. TIA Portal loads Add-In assemblies via its own loader. This section applies only to standalone console/desktop Openness applications.
HKLM\SOFTWARE\Siemens\Automation\InstalledApps\Totally Integrated Automation Portal V21 → value INSTALLPATHC:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\net48\C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\Schemas\The AssemblyResolve event must be registered before any Openness type is referenced.
This includes method parameters, return types, and class properties — not just method bodies.
Recommended pattern: register in a static constructor of Program, then move all Openness
code to a separate class so the resolver is active before those classes are loaded.
internal static class Program
{
// Register resolver at earliest possible point
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
public static void Main()
{
// All Openness code must be in a separate class
var app = new OpennessApp();
app.Run();
}
private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
// Read install path from registry (recommended) or hardcode for simple tools
string opennessFolder = GetOpennessInstallPath(); // implement per project
AssemblyName requestedName = new AssemblyName(args.Name);
string filePath = Path.Combine(opennessFolder,
string.Concat(requestedName.Name, ".dll"));
if (!requestedName.Name.StartsWith("Siemens.Engineering.") ||
!File.Exists(filePath))
return null;
Assembly loaded = Assembly.LoadFrom(filePath);
if (requestedName.FullName != loaded.GetName().FullName)
throw new FileNotFoundException(
"TIA Portal Openness version does not match", filePath);
return loaded;
}
}
Key rules:
Copy Local: False (<Private>False</Private>) must be set for every Siemens.Engineering assembly reference.Base.dll is always needed; add Step7.dll, WinCC.dll,
Startdrive.dll, etc. per task. The resolver handles all of them automatically
because it matches any name starting with Siemens.Engineering..HKLM\SOFTWARE\Siemens\Automation\InstalledApps\Totally Integrated Automation Portal V21
(value INSTALLPATH) rather than hardcoding it.Registry-based path discovery:
private static string GetOpennessInstallPath()
{
using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\Siemens\Automation\InstalledApps\Totally Integrated Automation Portal V21"))
{
string installPath = key?.GetValue("INSTALLPATH") as string;
if (string.IsNullOrEmpty(installPath))
throw new FileNotFoundException("TIA Portal V21 installation not found in registry.");
return Path.Combine(installPath, @"PublicAPI\V21\net48");
}
}
Alternative — app.config:
For stable installations where the path is known at build time, an app.config file with
<assemblyBinding> / <codeBase> entries is a simpler option that requires no code.
Use the AssemblyResolve event for tools that need to discover the install path at runtime.
Always use a using statement so the session is disposed on exit or exception:
internal class OpennessApp
{
public void Run()
{
using (TiaPortal tiaPortal = new TiaPortal(TiaPortalMode.WithUserInterface))
{
// All Openness work goes here
}
// TIA Portal is disposed (not necessarily closed) after this point
}
}
Modes:
TiaPortalMode.WithUserInterface — starts or attaches with visible GUITiaPortalMode.WithoutUserInterface — headless, suitable for automation pipelinesAttaching to a running instance:
// Attach to the first running TIA Portal process of this Openness version
TiaPortal tiaPortal = TiaPortal.GetProcesses().First().Attach();
Use GetProcesses() when TIA Portal is already open and you do not want to start a new
instance. Returns only processes from the same Openness version as the loaded assembly.
Dispose vs. close:
Dispose() closes TIA Portal.Dispose() only disconnects.NonRecoverableException.GetService<T>()V21 uses a service-provider pattern for UI interactions. Call GetService<T>() on the
TiaPortal instance to obtain a service. Available services depend on the runtime context.
These services are available only when code runs inside a TIA Portal Add-In:
| Service type | Namespace | Purpose |
|---|---|---|
MessageBoxProvider | Siemens.Engineering.AddIn | Display notification/confirmation dialogs in TIA Portal |
ProgressProvider | Siemens.Engineering.AddIn | Display a progress bar; check IsCancelRequested for cancellation |
FeedbackProvider | Siemens.Engineering.AddIn | Log messages to TIA Portal's Inspector window (General Info tab) |
// MessageBoxProvider — notification and confirmation dialogs
var msgBox = tiaPortal.GetService<MessageBoxProvider>();
msgBox?.ShowNotification(NotificationIcon.Information, "Title", "Message text");
msgBox?.ShowNotification(NotificationIcon.Warning, "Title", "Message", "Detail text");
// Confirmation dialog — defaultChoice is the pre-selected button; return value is user's choice
ConfirmationResult result = msgBox.ShowConfirmation(
ConfirmationIcon.General, "Title", "Proceed?",
ConfirmationChoices.Yes | ConfirmationChoices.No, ConfirmationResult.Yes);
if (result == ConfirmationResult.No) return;
// ProgressProvider — progress bar with cancellation
var progress = tiaPortal.GetService<ProgressProvider>();
progress?.Update("Step description", "Detail text");
if (progress?.IsCancelRequested == true)
{
// User requested cancellation — clean up and return
}
// FeedbackProvider — log to Inspector window (General Info tab)
var feedback = tiaPortal.GetService<FeedbackProvider>();
feedback?.Log(NotificationIcon.Information, "Operation completed successfully.");
feedback?.Log(NotificationIcon.Warning, "Skipped 3 items due to missing data.");
Important: In Add-In status callbacks (the OnUpdateStatus delegate passed to
AddActionItem), GetService<T>() returns null. Only use services in action
callbacks where COM access is fully available.
Four events are available on TiaPortal. Always unsubscribe in a finally block.
Fires when TIA Portal closes while the Openness client is still connected.
tiaPortal.Disposed += OnDisposed;
try
{
// ... work ...
}
finally
{
tiaPortal.Disposed -= OnDisposed;
}
private static void OnDisposed(object sender, EventArgs e)
{
// TIA Portal was closed externally — clean up and exit
}
Fires for informational messages that require only an acknowledgment (OK).
tiaPortal.Notification += OnNotification;
try { /* ... */ }
finally { tiaPortal.Notification -= OnNotification; }
private static void OnNotification(object sender, NotificationEventArgs e)
{
Console.WriteLine($"[TIA Notification] {e.Text}");
// No response required — notification is auto-acknowledged
}
Fires for dialogs that require a decision. Must set e.Result to one of:
"Yes", "YesToAll", or "No". Any other value throws an exception.
Auto-confirm pattern (most common in automation):
tiaPortal.Confirmation += OnConfirmation;
try { /* ... */ }
finally { tiaPortal.Confirmation -= OnConfirmation; }
private static void OnConfirmation(object sender, ConfirmationEventArgs e)
{
e.Result = "Yes";
}
Fires when opening a UMAC-protected project. Use to specify the authentication method
instead of passing credentials via UmacDelegate. Only fires for protected projects.
tiaPortal.Authentication += OnAuthentication;
try { /* open project ... */ }
finally { tiaPortal.Authentication -= OnAuthentication; }
private static void OnAuthentication(object sender, AuthenticationEventArgs e)
{
// Choose one of: DesktopSso, Anonymous, Interactive, Credentials
e.AuthenticationTypeProvider = AuthenticationTypeProvider.DesktopSso;
// For Credentials mode, credentials are supplied separately via UmacDelegate
}
AuthenticationTypeProvider | Behaviour |
|---|---|
DesktopSso | Signs in with the current Windows user — no password prompt |
Anonymous | Uses the anonymous user account — no password prompt |
Interactive | Shows TIA Portal login dialog to the user |
Credentials | Reads credentials from the supplied UmacDelegate |
Highly recommended for all non-trivial operations. Signals TIA Portal that a controlled activity is in progress and displays a dialog to the user.
using (ExclusiveAccess exclusiveAccess = tiaPortal.ExclusiveAccess("Generating project"))
{
// All Openness operations here run under exclusive access
// Optionally update the displayed message during long operations
exclusiveAccess.Text = "Compiling software";
// ...
exclusiveAccess.Text = "Downloading to PLC";
}
// Exclusive access is released on dispose
Rules:
ExclusiveAccess can exist at a time — a second attempt throws a recoverable
exception while the first is still active.exclusiveAccess.Text = string.Empty or null clears the displayed message.Group multiple modifications into a single undo unit. Requires an active ExclusiveAccess.
using (ExclusiveAccess exclusiveAccess = tiaPortal.ExclusiveAccess("Bulk edit"))
{
using (Transaction transaction = exclusiveAccess.Transaction(project, "Create blocks"))
{
// Perform all modifications
project.DeviceGroups.Create("Line_1");
project.DeviceGroups.Create("Line_2");
// MANDATORY: call CommitOnDispose() to persist changes
// If this line is never reached, the transaction is rolled back
transaction.CommitOnDispose();
}
}
Rollback rules — critical:
CommitOnDispose() is never called → always rolled back on dispose.CommitOnDispose() → rolled back, even inside try/catch.CommitOnDispose() → changes are committed.Not allowed inside a transaction: Project open, save, close, archive, retrieve, and some compile/import/export operations cannot be called while a transaction is active.
These rules are mandatory for generated C# Openness code:
.Delete(). Any destructive operation must be inside an active
ExclusiveAccess scope and a Transaction when the API permits transactions.using blocks for ExclusiveAccess, Transaction, TiaPortal, projects,
and other disposable Openness objects so rollback and release behavior is explicit.transaction.CommitOnDispose() only after every validation and mutation in
the transaction has succeeded.compile_check after generated block, tag, hardware, or HMI
changes. Do not present generated project changes as deployable until that check
passes.ExclusiveAccess scope, and require a preview or
user confirmation before the destructive call.Two top-level categories:
| Category | Base type | Behaviour |
|---|---|---|
| Recoverable | Siemens.Engineering.EngineeringException | API call failed; session remains valid |
| Non-recoverable | Siemens.Engineering.NonRecoverableException | TIA Portal closed; restart required |
Recoverable subtypes:
| Type | Typical cause |
|---|---|
EngineeringSecurityException | Missing access rights |
EngineeringObjectDisposedException | Object no longer exists (e.g. deleted) |
EngineeringNotSupportedException | Attribute or operation not available on this object |
EngineeringTargetInvocationException | General failure despite a valid API call |
EngineeringRuntimeException | Runtime error, e.g. invalid cast |
EngineeringOutOfMemoryException | Insufficient resources in TIA Portal instance |
EngineeringUserAbortException | Operation cancelled by the user (e.g. import dialog) |
EngineeringDelegateInvocationException | Exception thrown inside a caller-supplied delegate |
MissingProductsException | Project requires TIA Portal products/packages not installed (V18+) |
Recommended catch order:
try
{
// Openness work
}
catch (EngineeringSecurityException ex)
{
Console.WriteLine($"Access denied: {ex.Message}");
}
catch (EngineeringObjectDisposedException ex)
{
Console.WriteLine($"Object disposed: {ex.Message}");
}
catch (EngineeringNotSupportedException ex)
{
Console.WriteLine($"Not supported: {ex.MessageData.Text}");
foreach (ExceptionMessageData detail in ex.DetailMessageData)
Console.WriteLine($" Detail: {detail.Text}");
}
catch (MissingProductsException ex)
{
Console.WriteLine($"Missing products: {ex.Message}");
}
catch (EngineeringTargetInvocationException)
{
throw; // Do not swallow — rethrow for caller to handle
}
catch (EngineeringException)
{
throw; // Do not swallow general engineering exceptions
}
catch (NonRecoverableException ex)
{
Console.WriteLine($"Fatal — TIA Portal closed: {ex.Message}");
// Must restart TIA Portal; the session is gone
}
When opening multiple projects or working in multiuser environments:
// Primary — visible in project navigation, full read-write access
Project primary = tiaPortal.Projects.Open(
new FileInfo(path), umacDelegate, ProjectOpenMode.Primary);
// Secondary — hidden from UI, always read-only regardless of user rights
Project secondary = tiaPortal.Projects.Open(
new FileInfo(path), null, ProjectOpenMode.Secondary);
bool isPrimary = secondary.IsPrimary; // false
Rules:
Most engineering objects are accessed through compositions. Available methods:
| Method | Behaviour |
|---|---|
Create(id, ...) | Creates and adds a new instance. Signature varies per composition. |
Find(id) | Finds by identifier. Non-recursive — searches current level only. |
GetEnumerator() | Used implicitly in foreach loops. |
Contains(obj) | Returns bool — checks if object is in the composition. |
IndexOf(obj) | Returns int index of the object in the composition. |
Import(path, ImportOptions) | Available on compositions that support import. ImportOptions: None or Overwrite. |
Delete(obj) | Removes and deletes the instance. |
Not all methods are available on every composition — check domain skills.
// Reference equality (same .NET object reference)
bool sameRef = (objA == objB);
// TIA Portal identity equality (same engineering object, possibly different references)
bool sameObj = System.Object.Equals(objA, objB);
Use System.Object.Equals() when comparing objects retrieved through different navigation
paths that may represent the same underlying engineering object.
All DirectoryInfo and FileInfo instances passed to Openness must use absolute paths.
Relative paths cause an exception at runtime.
// Correct
var dir = new DirectoryInfo(@"C:\Projects\MyProject\Export");
// Wrong — will throw
var dir = new DirectoryInfo(@"Export");
Useful for dynamic tooling or when setting multiple attributes efficiently on HW objects.
// Get names of all available attributes
IList<EngineeringAttributeInfo> infos =
((IEngineeringObject)deviceItem).GetAttributeInfos();
// Read all readable attributes in one call
IList<string> names = infos.Select(i => i.Name).ToList();
IList<object> values = ((IEngineeringObject)deviceItem).GetAttributes(names);
// AttributeAccessOptions: None, ReadOnly, WriteOnly, ReadWrite
IReadOnlyList<KeyValuePair<string, object>> readableAttrs =
deviceItem.GetAttributes(AttributeAccessOptions.ReadOnly);
var pairs = new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("Name", "NewModuleName"),
new KeyValuePair<string, object>("Comment", "Auto-generated")
};
// Overload with callback fires after each attribute is set
deviceItem.SetAttributes(pairs, (name, val, ex) =>
{
if (ex != null)
Console.WriteLine($"Failed to set {name}: {ex.Message}");
});
// Overload without callback — for non-HW objects
((IEngineeringObject)someObject).SetAttributes(pairs);