Problem Statement
When WinCC v6.0 SP4 reads process values from a SIMATIC S7-200 CPU over PC Access (S7-200 PC Access V1.0 SP6 or V2.0 OPC DA server), every WinCC tag that has a Global Script (C or VBS) configured with the trigger "Tag - On change" fires whenever the tag value transitions - regardless of why the transition occurred. Three distinct events produce a burst of unintended script execution:
- WinCC Runtime startup. WinCC initializes all configured tags. PC Access returns its cached or initial value, WinCC applies the configured update, and every tag trigger fires once during the start sequence.
-
Communication loss. When PC Access loses the S7-200 link (cable break, CP 243-1 power-cycle, S7-200 STOP transition, hub failure), the tag's quality code flips from
OPC_QUALITY_GOOD (0xC0)to a BAD quality, typically0x18 = OPC_QUALITY_BAD_COMM_FAILURE. WinCC's quality handling applies the configured substitute value, which in most projects differs from the last good process value, so every script bound to that tag fires. - Communication restoration. PC Access re-establishes the link and refreshes every item. The newly read S7-200 value (which may or may not equal the substitute value) is written into the WinCC tag, and every tag-triggered script fires a second time - within the same second, on every tag.
The user-visible result is a burst of VB and C actions the moment the link comes back, regardless of whether the actual process value changed. On a panel with several hundred tag-triggered scripts this stalls Runtime for several seconds and can trigger unintended output writes if those scripts contain SetTag / SetTagFloatWait calls back to the PLC. The question "is it normal after a lost comm, and how do I stop WinCC from calling every event when communication is restored?" is one of the most common WinCC v6.0 issues on S7-200 installations. The answer is: yes it is normal, and the only reliable control point is to gate the script body itself.
Why Tag-Triggered Actions Fire on Communication Events
The WinCC v6.0 Global Script runtime executes an action the moment any of the configured triggers become true. The trigger types available in the action properties dialog (right-click action > Properties > Triggers) are summarized below.
| Trigger type | WinCC v6.0 setting | Fires when |
|---|---|---|
| Standard trigger (time) | Trigger > Time > e.g. 1 s cyclic | Selected time elapsed |
| Tag trigger (cyclic) | Trigger > Tag > cyclic, e.g. 500 ms | Selected cycle elapsed |
| Tag trigger (on change) | Trigger > Tag > on change | WinCC tag value differs from last reported value |
| Picture trigger | Trigger > Picture > Open / Close | Picture loaded / unloaded |
| Window trigger | Trigger > Window > Open / Close | Window loaded / unloaded |
The critical detail: "On change" is implemented as a value-edge detector, not a process-edge detector. The WinCC Tag Manager compares the current tag value (whatever the OPC DA server returned) to the previous one it has stored. Any difference - whether the PLC really changed its process variable, whether the OPC layer forced a substitute value, or whether Runtime simply initialized the tag with its starting value - counts as a change and fires the action.
The "Global Script runtime" option in the WinCC project properties (Computer > Properties > Startup > Global Script runtime) is therefore not the place to suppress this behavior: turning it off disables the entire script engine, including the C and VBS actions that are supposed to run on demand from picture events. The same applies to the "Start Global Script runtime after X seconds delay" option - it only delays the first firing, it does not filter re-firings after a reconnect.
WinCC v6.0 SP4 Tag Quality Model and OPC Quality Codes
Every WinCC tag carries a 16-bit quality code that mirrors the OPC DA quality byte returned by PC Access. The WinCC Information System documents the quality-code constants available to VBS and C actions. The values most often seen on an S7-200 link are listed below.
| Dec | Hex | OPC constant | Meaning for S7-200 over PC Access |
|---|---|---|---|
| 0 | 0x00 | OPC_QUALITY_BAD | Value unusable; check configuration |
| 4 | 0x04 | OPC_QUALITY_BAD_CONFIG_ERROR | Item path invalid in PC Access |
| 8 | 0x08 | OPC_QUALITY_BAD_NOT_CONNECTED | PC Access has not yet connected to the S7-200 |
| 12 | 0x0C | OPC_QUALITY_BAD_DEVICE_FAILURE | S7-200 returned a hardware error |
| 16 | 0x10 | OPC_QUALITY_BAD_SENSOR_FAILURE | Sensor signal out of range |
| 20 | 0x14 | OPC_QUALITY_BAD_LAST_KNOWN | No fresh value; last value retained |
| 24 | 0x18 | OPC_QUALITY_BAD_COMM_FAILURE | PC Access lost the PPI / MPI / TCP link |
| 28 | 0x1C | OPC_QUALITY_BAD_OUT_OF_SERVICE | Item disabled in PC Access |
| 64 | 0x40 | OPC_QUALITY_UNCERTAIN | Value usable but suspect |
| 80 | 0x50 | OPC_QUALITY_UNCERTAIN_SENSOR_CAL | Calibration overdue |
| 192 | 0xC0 | OPC_QUALITY_GOOD | Fresh value from S7-200 |
Three values drive the gating logic:
-
192 (0xC0)- GOOD - normal process value, safe to execute the script body. -
24 (0x18)- BAD_COMM_FAILURE - PC Access has just declared the link lost. WinCC is applying the substitute value, the script is firing on the substitute, not on a real change. -
8 (0x08)- BAD_NOT_CONNECTED - the first phase of startup, before the link is even up.
The recommended pattern in the WinCC v6.0 Information System ("Working with WinCC > Tag Management > Quality of Tags") is to read the tag quality in the action body and short-circuit early when the value is not GOOD. The quality code is accessible from VBS as HMIRuntime.Tags("Tagname").Quality and from C as GetTagQuality(lpszTagName) declared in apdefap.h. The bit layout of the quality word is also available as QualityStamp and SubstituteValueUsed in the same property set.
Pre-Deployment: S7-200 and PC Access Setup Verification
Before applying the gating logic, the PC Access / S7-200 side must be configured so that comm-loss / restore events are recognized cleanly by WinCC. The following checklist prevents the quality from flipping for unrelated reasons (which would defeat the gate).
- Open S7-200 PC Access and confirm the S7-200 station is in the "Connected" state, not "Connecting" or "Disconnected".
- Configure the PC Access item update rate to the project default (typically 500 ms for HMI, 1000 ms for SCADA). Faster rates amplify the re-firing burst after reconnect.
- For PPI / MPI links, set the Retry Attempts to at least 3 and Timeout to 1000 ms in PC Access > right-click station > Properties > Communication. This prevents PC Access from declaring BAD_COMM_FAILURE on a single missed scan.
- For Ethernet (CP 243-1), enable the "Keep-alive" option in the CP 243-1 configuration (STEP 7 Micro/WIN > Ethernet Wizard > Configuration > Keep-alive timer = 30 s) so the TCP session is held open and PC Access does not have to renegotiate it on every reconnect.
- In the WinCC Tag Manager, open each affected tag and confirm Update = "On change" (not "Cyclic only") and that Substitute value is set deliberately (see Solution 3).
- Verify that the S7-200 CPU is in RUN, not STOP. PC Access returns BAD_DEVICE_FAILURE for a CPU in STOP, which can be confused with a cable fault.
Solution 1 - Internal Tag Gating (Runtime + Connection Health Flags)
The internal-tag gate uses two WinCC internal tags (binary) that are driven by a single health-check action running on a 1 s cycle. All other tag-triggered scripts read those two flags and abort early if either flag is not set. This is the pattern the Siemens WinCC v6.0 support team recommends in production S7-200 panels.
Step 1 - Define the two internal tags in WinCC Tag Manager.
| Tag name | Type | Length | Initial value | Update |
|---|---|---|---|---|
| @RuntimeStarted | Binary Tag | 1 bit | 0 | Cyclic 1 s |
| @LinkToS7200OK | Binary Tag | 1 bit | 0 | Cyclic 1 s |
Use the "@" prefix to mark them as internal (no external connection) and set the update to "On change" in the Quality / Substitute dialog so they do not fire value-changed events themselves.
Step 2 - Configure a 1 s cyclic action "Init_RuntimeFlags" as the first action to run.
In Global Script > New Action > Properties > Triggers, set the trigger to a 1 s cyclic time trigger and set the "Global Script runtime must be running" check. Use the project property "Start sequence: First action to run" to ensure this action is always scheduled before any tag-triggered action.
' VBS - Init_RuntimeFlags.vbs
' Set RuntimeStarted after 5 s of stable Runtime.
' Set LinkToS7200OK only when a representative OPC tag has quality = 192.
Dim tElapsed, tStart, oTag, qResult
tStart = HMIRuntime.StartTime ' returns Date as Variant
' Wait 5 s after Runtime start to let WinCC and PC Access settle.
If DateDiff("s", tStart, Now) >= 5 Then
HMIRuntime.Tags("@RuntimeStarted").Write 1
End If
' Quality probe on a fast-changing process tag (e.g. S7-200 V200.0 = 1 Hz heartbeat).
Set oTag = HMIRuntime.Tags("S7200_Heartbeat")
oTag.Read
qResult = oTag.Quality
If qResult = 192 Then
HMIRuntime.Tags("@LinkToS7200OK").Write 1
Else
HMIRuntime.Tags("@LinkToS7200OK").Write 0
End If
Step 3 - Gate every tag-triggered script on both flags.
' VBS - example tag-triggered action (header)
Dim fRun, fLink, oTrig
Set oTrig = HMIRuntime.Tags("S7200_TagThatTriggersMe")
oTrig.Read
fRun = HMIRuntime.Tags("@RuntimeStarted").Value
fLink = HMIRuntime.Tags("@LinkToS7200OK").Value
If fRun = 0 Or fLink = 0 Then
' Abort: Runtime not stable or link is down.
Exit Sub
End If
' ... real script body ...
The two-flag gate guarantees that even if the tag-trigger fires 200 times in one second during a reconnect cascade, every gated script short-circuits as long as @LinkToS7200OK is still 0. The health-check action then re-asserts the flag on the next 1 s tick once the heartbeat tag comes back GOOD.
Solution 2 - Quality-Based Gating Inside the Script Body
The quality gate inspects the trigger tag's own quality and only executes the script body when the quality is GOOD (192). This is the most localized fix: it does not require any other tags, it does not depend on a separate health action, and it works even if you forget to add the internal-tag gate. The cost is one extra Read per script firing, which is negligible compared to the cost of an unwanted SetTag.
' VBS - Quality-gated action body
Dim oTrig, q
Set oTrig = HMIRuntime.Tags("S7200_ProcessValue_1")
oTrig.Read
q = oTrig.Quality
' 192 = OPC_QUALITY_GOOD
If q <> 192 Then
Exit Sub ' substitute value, comm-loss, or initial value - do nothing
End If
' --- real action body starts here ---
Dim processValue
processValue = oTrig.Value
' ...
C equivalent (apdefap.h):
#include "apdefap.h"
void OnChange_ProcessValue_1(char* lpszTagName)
{
DWORD q = GetTagQuality(lpszTagName);
if (q != 0xC0) // 192 dec, OPC_QUALITY_GOOD
return;
float pv = GetTagFloat(lpszTagName);
// ... real action body ...
}
Quality gate edge cases.
| Scenario | Tag quality | Gate result | Note |
|---|---|---|---|
| Runtime just started, link up | GOOD (192) | Script runs | First legitimate run |
| Runtime just started, link down | BAD_NOT_CONNECTED (8) | Aborted | Correct behavior |
| Link drops mid-Runtime | BAD_COMM_FAILURE (24) | Aborted | Substitute value ignored |
| Link restored, fresh value | GOOD (192) | Script runs | Intended behavior |
| PLC in STOP, link up | BAD_DEVICE_FAILURE (12) | Aborted | Prevents stale state writes |
For C actions, also consider GetTagQualityState and the QualityStamp property in WinCC v6.2 / v6.2 SP2, which exposes the time of the last successful read. Combining the quality gate with a "value changed since last good read" check eliminates the residual issue of a fresh process value that happens to equal the substitute value (the gate would still fire, but the body can detect that the timestamp is the same as the last good read).
Solution 3 - Substitute Value Configuration
The substitute value is the value WinCC pushes into a tag when the OPC quality is not GOOD. The wrong substitute value is the root cause of most "tag-triggered script fired unexpectedly" complaints. Two rules eliminate the noise at the source.
- Set the substitute value to the same value the tag would have when the process is idle / safe. For a digital input, this is the OFF state (0). For a 4-20 mA analog, this is 0.0 (or 4.0 if your engineering units are 0-based). For a 16-bit counter, it is 0. The point is: a comm-loss transition must look identical to a normal idle transition.
- Disable the substitute value entirely ("Initial value = Substitute value = Quality of the tag"). In WinCC v6.0, open the tag, select "Properties > Update", and check "Apply substitute value only on configured quality code". This forces the tag value to retain its last good process value when the quality is BAD, so the tag-trigger sees no edge on comm loss or restore.
Complete Script Examples (VBS and C)
The example below combines Solutions 1 and 2 in a single tag-triggered action. The action is bound to the tag S7200_Temp_SP (a setpoint from the S7-200) and writes the value to a data-log tag Archive_SP. Without gating, this action fires on every reconnect cascade and floods the archive with duplicate rows.
' VBS - ArchiveSetpoint.vbs
' Trigger: tag "S7200_Temp_SP", on change
' Action: append value to archive tag only if link is healthy and value is fresh.
Const QUALITY_GOOD = 192
Dim oTrig, q, v, fRun, fLink, oRun, oLink
Set oTrig = HMIRuntime.Tags("S7200_Temp_SP")
Set oRun = HMIRuntime.Tags("@RuntimeStarted")
Set oLink = HMIRuntime.Tags("@LinkToS7200OK")
oTrig.Read
oRun.Read
oLink.Read
fRun = oRun.Value
fLink = oLink.Value
q = oTrig.Quality
v = oTrig.Value
' --- Gate block (3 conditions) ---
If fRun = 0 Then Exit Sub ' Runtime not yet stable
If fLink = 0 Then Exit Sub ' S7-200 link is down or not yet up
If q <> QUALITY_GOOD Then Exit Sub ' Substitute or initial value
' --- Real action body ---
HMIRuntime.Tags("Archive_SP").Write v
HMRuntime.Trace "Archive_SP updated to " & v & " at " & Now & vbCrLf
C equivalent with both gates.
#include "apdefap.h"
#define QUALITY_GOOD 0xC0 // 192 decimal, OPC_QUALITY_GOOD
void OnChange_ArchiveSetpoint(char* lpszPictureName, char* lpszObjectName, char* lpszTagName)
{
// Solution 1: internal flag gate
if (GetTagByte("@RuntimeStarted") == 0) return;
if (GetTagByte("@LinkToS7200OK") == 0) return;
// Solution 2: quality gate
if (GetTagQuality("S7200_Temp_SP") != QUALITY_GOOD) return;
// Real body
float v = GetTagFloat("S7200_Temp_SP");
SetTagFloat("Archive_SP", v);
printf("Archive_SP updated to %f\n", v);
}
Health-probe action (companion to the example above).
' VBS - HeartbeatMonitor.vbs
' Trigger: 1 s cyclic time trigger (NOT a tag trigger)
' Purpose: keeps @LinkToS7200OK in sync with the S7-200 heartbeat tag quality.
Const QUALITY_GOOD = 192
Const QUALITY_BAD_COMM = 24
Const QUALITY_NOT_CONNECTED = 8
Const QUALITY_BAD_DEVICE = 12
Dim oHB, q
Set oHB = HMIRuntime.Tags("S7200_Heartbeat") ' S7-200 V200.0 toggled every 1 s in OB1
oHB.Read
q = oHB.Quality
Select Case q
Case QUALITY_GOOD
HMIRuntime.Tags("@LinkToS7200OK").Write 1
HMIRuntime.Tags("@LastQualityCode").Write 192
Case QUALITY_BAD_COMM, QUALITY_NOT_CONNECTED, QUALITY_BAD_DEVICE
HMIRuntime.Tags("@LinkToS7200OK").Write 0
HMIRuntime.Tags("@LastQualityCode").Write q
Case Else
HMIRuntime.Tags("@LinkToS7200OK").Write 0
HMIRuntime.Tags("@LastQualityCode").Write q
End Select
WinCC v6.0 SP4 Configuration Checklist
| # | Configuration point | Where | Required value |
|---|---|---|---|
| 1 | Global Script runtime on startup | Computer > Properties > Startup | Enabled (do not turn off to suppress triggers) |
| 2 | Init_RuntimeFlags scheduled first | Global Script > Action > Properties > Start order | Priority 1 (lowest number = first) |
| 3 | HeartbeatMonitor cyclic trigger | Action > Triggers | 1 s time trigger |
| 4 | @RuntimeStarted internal tag | Tag Management > Internal Tags | Binary, init 0, "Apply substitute" off |
| 5 | @LinkToS7200OK internal tag | Tag Management > Internal Tags | Binary, init 0, "Apply substitute" off |
| 6 | S7200_Heartbeat process tag | Tag Management > S7-200 PC Access | V200.0, BOOLEAN, update 1 s |
| 7 | Substitute value on S7-200 tags | Tag > Properties > Update | Set to idle / safe process value (see Solution 3) |
| 8 | PC Access retry / timeout | PC Access > Station > Properties > Communication | Retry >= 3, Timeout = 1000 ms |
| 9 | CP 243-1 keep-alive | STEP 7 Micro/WIN > Ethernet Wizard | Keep-alive = 30 s, Run mode = Use Keep-alive |
| 10 | Quality gate in every tag-triggered script | Each Global Script action | Quality = 192 check, then abort |
Verification and Test Procedure
Validate the gating in a controlled sequence before deploying to a live panel. Each step must be performed on a panel with at least 50 tag-triggered actions bound to live S7-200 values, and a process simulator that can hold the S7-200 values constant while the link is cycled.
- Baseline - link up, process stable. Confirm no script fires for at least 30 s (check WinCC Diagnostic > GSC Runtime > Action call count).
-
Simulated comm loss. Disconnect the PPI cable (or pull the CP 243-1 patch cable). Within 2 s, confirm
@LinkToS7200OK = 0,@LastQualityCode = 24, and the "GSC Runtime > Action call count" for every gated action stays at its baseline value (no fires). -
Link reconnected, process unchanged. Restore the cable. Confirm within 3 s that
@LinkToS7200OK = 1and that no gated action fired during the reconnection window. The only allowed fires are the HeartbeatMonitor (1 s cyclic) and the Init_RuntimeFlags (1 s cyclic). - Link reconnected, process changed during outage. Change the S7-200 process value during the outage (e.g. ramp a setpoint from 50 to 75). After reconnection, confirm the gated action fires exactly once with value = 75.
- PLC STOP / RUN cycle. Toggle the S7-200 from RUN to STOP and back. Confirm that all tag qualities cycle 192 -> 12 -> 192, the link flag drops to 0 in the BAD_DEVICE_FAILURE phase, and that the gate correctly suppresses script execution until the link flag is re-asserted.
-
Runtime cold start with link down. Stop WinCC Runtime, pull the cable, restart Runtime. Confirm the Init_RuntimeFlags sets
@RuntimeStarted = 1after 5 s, but@LinkToS7200OKstays 0 until the cable is restored.
A successful run of all six steps confirms the gate. If step 3 still shows unwanted fires, the most common cause is a tag whose substitute value differs from the value the PLC was holding just before the outage - revisit Solution 3 and set the substitute equal to the last good process value, or disable the substitute entirely.
Migration Notes - Behavior in WinCC Unified (TIA Portal V20)
The root cause (a tag-update event after reconnect) carries forward into WinCC Unified, but the event model is significantly different. The newer runtime exposes a richer set of explicit events on the tag and on the connection, which makes the gate far easier to express.
| Aspect | WinCC v6.0 SP4 (Classic) | WinCC Unified V20 (TIA Portal) |
|---|---|---|
| Tag trigger model | Implicit "on change" in Global Script action | Explicit event "Tag value changed" on the tag |
| Quality exposure | Read via .Quality inside the script body |
Separate event "Quality changed" on the tag |
| Connection state | No first-class event; must be inferred from quality | Event "Connection state changed" on the HMI connection |
| Script editor | C / VBS in Global Script container | JavaScript inside a Scheduled Task or Screen event |
| Gating pattern | Internal-tag flag + quality check (this article) | Subscribe to "Connection state changed" once, set a single internal flag, gate all tag-changed handlers on that flag |
The WinCC Unified event documentation (Basics on the events (RT Unified)) lists the supported events per object and notes that the runtime fires "Tag value changed" and "Quality changed" as two independent events, so a single reconnect that re-establishes a fresh value can be observed as one "Quality changed (BAD -> GOOD)" event followed by one "Tag value changed" event. A CWC implementation pattern for this is documented in the engineering CWC guide for WinCC Unified. The internal-flag pattern from Solution 1 still applies - it is just expressed in JavaScript on the connection-state event rather than in a 1 s cyclic C action.
Troubleshooting Matrix
| Symptom | Most likely cause | Diagnostic | Fix |
|---|---|---|---|
| All tag-triggered scripts fire at Runtime start, link is up | Init_RuntimeFlags action not scheduled first; @RuntimeStarted still 0 | Check @RuntimeStarted value at t = 6 s in WinCC Tag Simulator |
Move Init_RuntimeFlags to start-order priority 1; increase delay to 10 s if PC Access is slow |
| Scripts fire when link drops, do not fire when link restores | Substitute value happens to equal last good value, no second edge | Log @LastQualityCode on every state change |
Acceptable; gate is doing its job on the restore edge |
| Scripts still fire on reconnect even after gate applied | The action body has no quality check and the script is a C action that does not call GetTagQuality | Open the action in Global Script editor, check first 5 lines | Add quality check or internal-flag check at the top of every action |
| @LinkToS7200OK flickers 0/1/0/1 once a second | PC Access item update rate faster than S7-200 scan, or CP 243-1 keep-alive off | PC Access > right-click station > Test; STEP 7 Micro/WIN Ethernet Wizard > Keep-alive | Set PC Access update to 1000 ms; enable Keep-alive 30 s on CP 243-1 |
| Scripts fire but quality reads back as 192 immediately | The quality variable was cached at the top of the action before the value was read | Print oTrig.Quality after oTrig.Read
|
Always .Read immediately before .Quality in the same line |
| Scripts fire only on the FIRST reconnect after a long stable period | Substitute value in the tag differs from process value at reconnect | Enable "Apply substitute value" logging in WinCC Diagnostic | Set substitute equal to idle value, or disable substitute (Solution 3) |
| Scripts fire continuously, every 1 s, even with no comm events | Action trigger is cyclic, not on-change - the gate works but the trigger fires anyway | Action > Properties > Triggers | Change trigger to "On change"; the gate is still required for the reconnect edge |
| No scripts fire at all after the gate is added | @RuntimeStarted or @LinkToS7200OK stays 0 forever | Add trace to Init_RuntimeFlags: HMIRuntime.Trace "Init tick at " & Now
|
Check that Init_RuntimeFlags is enabled in Global Script runtime; check that the 1 s time trigger is correctly assigned |
Frequently Asked Questions
Is it normal for WinCC v6.0 to call all tag-triggered events when communication with the S7-200 is restored?
Yes. The "On change" trigger is a value-edge detector at the WinCC tag layer. The substitute value applied during comm loss, followed by the fresh process value after restoration, count as two consecutive edges and fire the action twice. There is no project property to disable this; you must gate the script body.
Can I disable the Global Script runtime at WinCC startup to stop this?
No. Turning off "Global Script runtime" in the computer's startup properties disables the entire script engine, including scripts you want to keep running (e.g. picture Open events). The correct control point is inside the action body, not in the startup properties.
How do I detect a comm-loss event from inside a VBS action?
Read HMIRuntime.Tags("Tagname").Quality immediately after .Read. A value of 24 (0x18) means OPC_QUALITY_BAD_COMM_FAILURE, 8 (0x08) means OPC_QUALITY_BAD_NOT_CONNECTED, 192 (0xC0) means OPC_QUALITY_GOOD. Skip the action body when quality is not 192.
What is the difference between an internal-tag gate and a quality-code gate?
The internal-tag gate uses a separate WinCC internal tag, set by a 1 s cyclic health-check action, to enable or disable large blocks of logic across the project. The quality-code gate uses the tag's own quality code to short-circuit an individual action. They are complementary: the quality gate handles per-tag spikes inside a single action, the internal-tag gate handles project-wide enable/disable and works even on tags whose quality has not yet updated.
Does this behavior change if I migrate to WinCC Unified (TIA Portal V20)?
The underlying cause (a tag-update event after reconnect) is similar, but WinCC Unified exposes an explicit "Connection state changed" event on the HMI connection, so you can subscribe to that event once and set a single internal flag rather than polling a heartbeat tag. See the WinCC Unified event documentation for the new event model.