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.
| 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.
| 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:
- Open the WinCC Professional project in TIA Portal.
- Expand HMI alarms > Tasks and add a new task named
ExportAlarms_OnSpeedTrigger. - Open Triggers and switch from Default cycle to Event.
- Select Tag change and add two tags:
Speed_0andSpeed_180. - Set the event to Rising edge (0 -> 1); this ensures the script fires exactly once per transition.
- Add a Script trigger element and bind it to the VBS action described in the next section.
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:
| 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:
- The directory does not exist on the runtime PC.
- The WinCC runtime service user (default:
Siemens HMI RTorCCAgentSvc) 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.Pathresolves to the deployed runtime project directory and is always writable by the runtime service. - The third argument
TruetoCreateTextFileselects 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
- Wrap
cnSQL.OpeninOn Error Resume Nextwith an explicit check onErr.Number; on failure, write the error to a status tag and abort cleanly. - Use a parameterized
Commandobject 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 = 30to prevent a hung destination from blocking the WinCC scheduler. - For tables larger than 5,000 rows per export, batch with
INSERT ... SELECT ... FROM OPENQUERYrather 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:
- Set
Speed_0TRUE whenSpeed = 0AND was not zero on the previous scan. - Set
Speed_180TRUE whenSpeed = 180AND was not 180 on the previous scan. - Reset the bit in the same cycle if a handshake tag
ExportAlarms_Ackis 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
- Compile and download the TIA Portal project to the HMI; restart the WinCC Runtime in Start with reset mode.
- Open the WinCC Alarm Control on the HMI; raise at least one acknowledged and one unacknowledged alarm.
- Force
Speedfrom 50 to 0 in the PLC watch table; verifySpeed_0pulses TRUE for one PLC scan and thatExportAlarms_Ackreturns TRUE within 1 second. - Query the SQL Server table
SELECT TOP 20 * FROM dbo.WinCCAlarms ORDER BY TimeComing DESC; confirm the alarm rows arrived and thatText1matches the configured message text. - Force
Speedfrom 0 to 180; repeat the verification. The delta timestampExportAlarms_LastSyncshould advance on each trigger. - Stop the SQL Server service; trigger
Speed_0again. The script should set an error status tag (for example,ExportAlarms_LastError = 53for network path not found) without crashing WinCC. - Restart SQL Server; trigger the export again. The next successful run should backfill the missed rows because the
ExportAlarms_LastSyncwas not advanced on the failed run.
Troubleshooting Matrix
| 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
| 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_writercreated withdb_datareader+db_datawriteron the destination database. - Write permission for the WinCC runtime service account on the local export directory.
- PLC handshake tag
ExportAlarms_Ackdefined 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.