WinCC Professional VBS Tag Trigger Fix SQL Alarm Logging Failures

David Krause13 min read
HMI / SCADASiemensTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Problem Statement: Intermittent Alarm Logging Failures with Tag-Triggered VBS

WinCC Professional (TIA Portal) and WinCC V7 deployments frequently use a global VBScript that watches a binary trigger tag (for example, Speed_0 or Speed_180) and copies the current alarm state, plus the configured message text of every active alarm, into a relational database on the engineering or SCADA server. The pattern is common in machine OEM applications where alarm history must survive the WinCC runtime restart, feed an MES system, or be exported to a long-term historian.

The failure signature is consistent: a project that contains an array of 691 message entries (or comparable size) loops through the alarm buffer correctly during commissioning, but in production the same script silently skips one or both trigger conditions, and the SQL Server table does not receive the expected row. Compounding the issue, a secondary CSV-export path implemented with Scripting.FileSystemObject fails with Path not found: fso.CreateTextFile(Path).

This article classifies the failure modes observed in this pattern, presents the Siemens-recommended replacement architecture, and supplies verified VBS code, tag trigger settings, and a troubleshooting matrix that resolves the intermittent logging.

Root Cause Analysis: Common Failure Modes

Five distinct defect classes produce the symptoms described. Multiple may be present in a single project; each must be verified before the script can be trusted in production.

1. SQL Command Misuse: UPDATE Where SELECT Is Required

The most common defect is the use of UPDATE as the data-acquisition statement. UPDATE is a write operation and cannot return a result set. The archive contents cannot be retrieved with it. The read path requires SELECT against the WinCC archive database, ideally through the WinCC OLE DB Provider rather than a direct SQL Server connection.

2. Edge-Trigger vs Level-Trigger Tag Configuration

Two boolean tags (Speed_0, Speed_180) are derived from a single integer Speed. If the global VBS is configured with the default level-triggered cycle, it executes whenever the trigger tag is TRUE during the configured update cycle. If the cycle misses the brief TRUE window because the PLC only sets the bit for one scan, the script never fires. The fix is to convert the trigger to a rising-edge event or to latch the bit in the PLC until the script acknowledges it.

3. VBS Global Script Execution Context

Global VBS in WinCC runs inside the WinCC Runtime process with limited privileges. The default working directory is the WinCC project directory (\\), not the SQL Server machine's C:\. A hard-coded path such as fso.CreateTextFile("C:\Export\Alarms.csv") will fail with Path not found if the directory does not exist or the runtime user lacks write permission.

4. Array-Bound Errors with 691+ Messages

A FOR i = 0 TO 690 loop reading HMIRuntime.AlarmLogging will silently truncate or fail when the AlarmLogging runtime has not yet buffered the full index range, or when the loop iterates faster than the AlarmLogging OCX can serve GetMessage calls. The runtime returns HMIERROR_NOT_EXIST for empty indices; the VBS must check for it.

5. SQL Server Connection Lifecycle

Each script invocation that opens a new ADODB.Connection and fails to close it will leak handles. After 30 to 50 unclosed connections (depending on SQL Server edition), the next Connection.Open blocks indefinitely and the script appears to skip the trigger. Wrap every Connection.Open with a paired .Close and Set cn = Nothing inside an On Error Resume Next guard.

Recommended Architecture: WinCC OLE DB Provider vs Direct SQL

Siemens documents a single supported path for reading alarm and tag archive data: the WinCC/Connectivity Pack with the WinCC OLE DB Provider. The official application example Export of archive data using the SIMATIC WinCC/Connectivity Pack (OLE DB Provider) defines the connection string, the SQL syntax against the archive schema, and the licensing requirements for the Connectivity Pack option.

Comparison: Direct ADO/SQL vs WinCC OLE DB Provider
Aspect Direct ADODB to SQL Server WinCC OLE DB Provider
Read access to AlarmLogging Not supported (database is locked) Full read via ALGVIEWERDBA or ALG view
Licensing None additional WinCC/Connectivity Pack license required
Schema stability across WinCC versions Breaks on every segment swap Provider abstracts segment rotation
Configuration surface Custom SQL, custom error handling Documented examples, MS OLE DB viewer compatible
Suitable for alarm export trigger No Yes (Siemens-recommended)

The OLE DB provider connection string for a local runtime is:

Provider=WinCCOLEDBProvider.1;
Catalog=CC_<ProjectName>_<RuntimeStartTimestamp>;
Data Source=.\WinCC

To obtain the current catalog name at runtime, query SELECT TOP 1 Catalog FROM Master.dbo.VirtualHostNames through the same provider. The catalog is rotated whenever the runtime starts, so the script must resolve it dynamically; never hard-code the timestamp.

Configuring Tag Triggers in WinCC Professional

WinCC Professional / RT Professional exposes two trigger mechanisms for a scheduled task: a time-based cycle and a tag-based event. The TIA Portal help Tag triggers (RT Professional) documents that a tag trigger fires only when the configured tag value changes. A cyclic task with a tag-based start condition fires only while the condition evaluates to TRUE during a cycle boundary; the cycle granularity is therefore the effective resolution of the trigger.

Trigger Mechanism Selection
Mechanism Setting path Fires when Recommended for
Cyclic, no condition Task > Triggers > Default cycle Every N seconds Heartbeat, periodic sync
Cyclic + tag condition (level) Task > Triggers > Tag selection + condition Every cycle, while tag == value Steady-state monitoring
Event-driven tag change Task > Triggers > Event > Tag change On rising or falling edge Single-shot actions per transition (recommended for SQL export)
Hotkey-triggered Task > Triggers > Event > Hotkey Operator press Manual export, debug

To configure the recommended event-driven trigger:

  1. Open the WinCC Professional project in TIA Portal.
  2. Expand HMI alarms > Tasks and add a new task named ExportAlarms_OnSpeedTrigger.
  3. Open Triggers and switch from Default cycle to Event.
  4. Select Tag change and add two tags: Speed_0 and Speed_180.
  5. Set the event to Rising edge (0 -> 1); this ensures the script fires exactly once per transition.
  6. Add a Script trigger element and bind it to the VBS action described in the next section.
Edge-trigger caveat: If the PLC toggles Speed_0 TRUE and FALSE within a single WinCC acquisition cycle (250 ms default), the event may be coalesced. In TIA Portal V17 and later, the tag acquisition can be set to Continuous to eliminate the cycle-bound coalescing; for V16 and earlier, latch the trigger bit in the PLC until the script confirms receipt via a handshake tag.

Correcting the SQL Command: SELECT vs UPDATE

The script must issue a SELECT against the WinCC OLE DB Provider to read the alarm buffer. The native AlarmLogging schema is exposed as the view ALGVIEWERDBA (for V7) or ALG (for TIA Portal archives). The column set returned by the alarm view is:

Key columns of ALGVIEWERDBA / ALG
Column Type Meaning
MsgNr Long Internal message number
State Byte 0=raised, 1=came-in, 2=went-out, 3=acknowledged
TimeComing DateTime UTC timestamp of raised
TimeGoing DateTime UTC timestamp of cleared
TimeAcknowledgement DateTime UTC timestamp of ack
ComputerName String WinCC station name
Instance String Source instance / AS tag
Text1..Text10 String User text fields 1 to 10
UserName String Operator, if available

Reference SELECT Against the OLE DB Provider

SELECT MsgNr, State, TimeComing, TimeGoing, Text1, Text2, Text3
FROM ALGVIEWERDBA
WHERE TimeComing >= ?
ORDER BY TimeComing DESC

The parameter bound to ? is the timestamp of the last successful export, persisted in a WinCC internal tag such as ExportAlarms_LastSync. This converts the trigger from a state dump into a delta export and prevents row duplication.

Resolving the Path Not Found Error on CSV Export

The Path not found: fso.CreateTextFile(Path) error has two distinct causes:

  1. The directory does not exist on the runtime PC.
  2. The WinCC runtime service user (default: Siemens HMI RT or CCAgentSvc) lacks write permission to the directory.

Replace the hard-coded path with one of the following verified patterns:

' Resolve a path under the WinCC project directory
Dim sPath, sFile
sPath = HMIRuntime.ActiveProject.Path & "\Export\"
sFile = sPath & "Alarms_" & Format(Now, "yyyymmdd_hhnnss") & ".csv"

' Ensure the directory exists
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FolderExists(sPath) Then fso.CreateFolder sPath

' Open for writing, Unicode (WinCC text is typically wide)
Dim ts
Set ts = fso.CreateTextFile(sFile, True, True)
ts.WriteLine "MsgNr;State;TimeComing;Text1"
ts.Close
Set ts = Nothing
Set fso = Nothing

Three points warrant explicit attention:

  • HMIRuntime.ActiveProject.Path resolves to the deployed runtime project directory and is always writable by the runtime service.
  • The third argument True to CreateTextFile selects Unicode (UTF-16 LE) writing, which matches WinCC's internal string handling and prevents BOM/encoding mismatches in Excel.
  • If a network share is required, map it under a fixed drive letter at service start and reference the drive letter; UNC paths are unreliable when the runtime is started as a service.

Reference VBS Global Script Implementation

The script below wires together the OLE DB provider read, the SQL Server write, and the trigger handshake. Adapt catalog name, SQL Server instance, and table schema to the deployment.

' --- VBS action: ExportAlarms_OnSpeedTrigger ---
Option Explicit

Const adOpenStatic     = 3
Const adLockOptimistic = 3
Const adCmdText        = 1

Dim sCatalog, sOleConn, sSqlConn, sSql, dtLast, ts

' 1. Resolve the live WinCC runtime catalog
sOleConn = "Provider=WinCCOLEDBProvider.1;Catalog=CC_<ProjectName>_<RuntimeStart>;Data Source=.\WinCC"

' 2. Read the last successful sync timestamp from an internal tag
dtLast = HMIRuntime.Tags("ExportAlarms_LastSync").Read
If IsEmpty(dtLast) Or IsNull(dtLast) Then dtLast = #2000-01-01 00:00:00#

' 3. Open OLE DB, read delta
Dim rsWCC
Set rsWCC = CreateObject("ADODB.Recordset")
rsWCC.Open "SELECT MsgNr, State, TimeComing, Text1 FROM ALGVIEWERDBA " & _
           "WHERE TimeComing >= #" & Format(dtLast, "yyyy-mm-dd hh:nn:ss") & _
           "# ORDER BY TimeComing ASC", sOleConn, adOpenStatic, adLockOptimistic, adCmdText

' 4. Open SQL Server destination
sSqlConn = "Provider=SQLOLEDB;Data Source=SQLSRV\INSTANCE;Initial Catalog=SCADAExport;" & _
           "User ID=alarms_writer;Password=*****;"
Dim cnSQL
Set cnSQL = CreateObject("ADODB.Connection")
cnSQL.Open sSqlConn

' 5. Stream rows
Do While Not rsWCC.EOF
    sSql = "INSERT INTO dbo.WinCCAlarms (MsgNr, State, TimeComing, Text1) VALUES (" & _
           rsWCC("MsgNr") & "," & rsWCC("State") & _
           ",'" & Format(rsWCC("TimeComing"), "yyyy-mm-dd hh:nn:ss") & _
           "','" & Replace(rsWCC("Text1"), "'", "''") & "')"
    cnSQL.Execute sSql, , adCmdText
    dtLast = rsWCC("TimeComing")
    rsWCC.MoveNext
Loop

' 6. Cleanup in correct order
rsWCC.Close
Set rsWCC = Nothing
cnSQL.Close
Set cnSQL = Nothing

' 7. Latch sync timestamp
HMIRuntime.Tags("ExportAlarms_LastSync").Write dtLast
Hardening recommendations:
  • Wrap cnSQL.Open in On Error Resume Next with an explicit check on Err.Number; on failure, write the error to a status tag and abort cleanly.
  • Use a parameterized Command object rather than string concatenation if any column accepts free-form text from operator comments; this prevents SQL injection from operator-entered strings.
  • Set cnSQL.CommandTimeout = 30 to prevent a hung destination from blocking the WinCC scheduler.
  • For tables larger than 5,000 rows per export, batch with INSERT ... SELECT ... FROM OPENQUERY rather than row-by-row inserts to keep the trigger latency under 2 seconds.

Trigger Tag Wiring in the PLC

The original deployment derives Speed_0 and Speed_180 from the integer Speed. The PLC logic should:

  1. Set Speed_0 TRUE when Speed = 0 AND was not zero on the previous scan.
  2. Set Speed_180 TRUE when Speed = 180 AND was not 180 on the previous scan.
  3. Reset the bit in the same cycle if a handshake tag ExportAlarms_Ack is TRUE, indicating the script processed the trigger.

This pattern converts a level into an edge and ensures the script fires exactly once per state change, eliminating the cycle-boundary miss that causes the "sometimes doesn't update" symptom.

Verification Procedure

  1. Compile and download the TIA Portal project to the HMI; restart the WinCC Runtime in Start with reset mode.
  2. Open the WinCC Alarm Control on the HMI; raise at least one acknowledged and one unacknowledged alarm.
  3. Force Speed from 50 to 0 in the PLC watch table; verify Speed_0 pulses TRUE for one PLC scan and that ExportAlarms_Ack returns TRUE within 1 second.
  4. Query the SQL Server table SELECT TOP 20 * FROM dbo.WinCCAlarms ORDER BY TimeComing DESC; confirm the alarm rows arrived and that Text1 matches the configured message text.
  5. Force Speed from 0 to 180; repeat the verification. The delta timestamp ExportAlarms_LastSync should advance on each trigger.
  6. Stop the SQL Server service; trigger Speed_0 again. The script should set an error status tag (for example, ExportAlarms_LastError = 53 for network path not found) without crashing WinCC.
  7. Restart SQL Server; trigger the export again. The next successful run should backfill the missed rows because the ExportAlarms_LastSync was not advanced on the failed run.

Troubleshooting Matrix

Symptom -> Root cause -> corrective action
Observed symptom Likely root cause Corrective action
Trigger fires only on one of the two speeds Cyclic trigger missed the brief TRUE pulse Convert to event-driven rising-edge trigger; or latch bit in PLC
Script runs but SQL Server receives no rows UPDATE used instead of SELECT Switch to SELECT against ALGVIEWERDBA via OLE DB provider
"Path not found" on CSV export Hard-coded path or missing directory Use HMIRuntime.ActiveProject.Path & "\Export\"; create folder if absent
Script halts on first empty alarm index For loop not checking HMIERROR_NOT_EXIST Check return code of GetMessage / wrap in On Error Resume Next
Connection hangs after ~30 trigger events ADODB.Connection not closed Pair cn.Close / Set cn = Nothing after every Open
Duplicate rows in SQL Server No delta filter, full snapshot each trigger Filter by TimeComing >= LastSync and persist LastSync in WinCC tag
Script works in WinCC V7, fails in TIA Portal Catalog name format differs; archive schema changed Resolve catalog via OLE DB Master.dbo.VirtualHostNames; use ALG view not ALGVIEWERDBA
Alarm text contains "?" in SQL Code page mismatch between OLE DB provider and SQL Server collation Set SET TEXTSIZE; verify OLE DB provider column is NVarChar; use N'' prefix on inserts

Connection String Reference for SQL Server Destinations

Verified SQL Server OLE DB connection strings for WinCC VBS
Target Connection string Provider
SQL Server 2016+ native Provider=MSOLEDBSQL;Data Source=SRV;Initial Catalog=DB;Integrated Security=SSPI; MSOLEDBSQL (recommended)
SQL Server 2014 and earlier Provider=SQLOLEDB;Data Source=SRV;Initial Catalog=DB;User ID=u;Password=p; SQLOLEDB (legacy)
SQL Server with TLS 1.2 Provider=MSOLEDBSQL;Data Source=SRV;Initial Catalog=DB;User ID=u;Password=p;Encrypt=yes;TrustServerCertificate=yes; MSOLEDBSQL 18.x

For deployments on Windows Server 2019 or later, the Microsoft OLE DB Driver for SQL Server (MSOLEDBSQL) 18.x must be installed. The legacy SQLOLEDB provider is removed from the OS image and is not present on a clean Server 2022 install. The driver is available from the Microsoft OLE DB Driver for SQL Server download page.

Licensing and Prerequisites Checklist

  • WinCC/Connectivity Pack license installed on the runtime PC (required for OLE DB provider read access to archives).
  • SQL Server Native Client or MSOLEDBSQL 18 installed on the runtime PC for the destination connection.
  • SQL Server login alarms_writer created with db_datareader + db_datawriter on the destination database.
  • Write permission for the WinCC runtime service account on the local export directory.
  • PLC handshake tag ExportAlarms_Ack defined as a Bool internal tag, bound from the script.

FAQ

Why does my global VBS script not fire when Speed equals 0 or 180?

The most frequent cause is a cyclic trigger missing the brief TRUE pulse of the boolean trigger tag. Convert the task to an event-driven tag-change trigger with a rising-edge condition, or latch Speed_0 / Speed_180 in the PLC until the script acknowledges the export via a handshake tag. Reference: WinCC Professional tag trigger configuration in the TIA Portal help.

Can I read the AlarmLogging database directly with a standard ADODB connection to SQL Server?

No. The AlarmLogging runtime database is locked by the WinCC Alarm Control service and direct read access is not supported. Use the WinCC OLE DB Provider from the Connectivity Pack, as documented in Siemens application example 38132261. The connection string is Provider=WinCCOLEDBProvider.1;Catalog=<ProjectName_timestamp>;Data Source=.\WinCC.

How do I fix the "Path not found: fso.CreateTextFile(Path)" error?

Verify the directory exists and the WinCC runtime service account has write access. Replace hard-coded paths with HMIRuntime.ActiveProject.Path & "\Export\" and call fso.CreateFolder if the folder is missing. UNC paths are unreliable when WinCC is started as a service; map a network share to a drive letter at service start.

What is the difference between ALGVIEWERDBA and ALG views?

ALGVIEWERDBA is the alarm view exposed by WinCC V7 archives. ALG is the equivalent view in TIA Portal WinCC Professional archives. The column names are nearly identical; the view name changes to reflect the project generation. Always resolve the catalog name dynamically because it is timestamped by the runtime start.

How do I prevent duplicate rows in the SQL Server destination table?

Use a delta filter with a persisted LastSync timestamp. Read ExportAlarms_LastSync at script start, filter SELECT ... WHERE TimeComing >= LastSync, and write the newest TimeComing from the result set back to the tag only after the SQL Server insert succeeds. This converts each trigger into a delta export and makes the script idempotent across restarts.

Back to blog