Overview of Alarm Duration in WinCC V7.3
WinCC V7.3 computes the duration of a process alarm as the wall-clock difference between the came-in event (State = 1) and the went-out event (State = 2). The result is written to the TimeDiff column of the long-term archive table dbo.MsArcLong as a BIGINT value expressed in milliseconds. The row written at the came-in transition stores TimeDiff = 0; the row written at the went-out transition stores the elapsed dwell time in milliseconds. The short-term mirror table dbo.MsArcShort shares the same schema and is rotated more aggressively.
Operators, quality engineers, and plant historians routinely request that a fixed offset be added to this value. Typical motivations include:
- Compensating for known I/O debounce or scan-time latency that the field operator does not see on the WinCC side.
- Logging the acknowledge-to-clear interval rather than the raw came-in to went-out interval.
- Aligning the WinCC archive with an enterprise reporting tool that subtracts a known filter dwell time.
- Padding the duration so that short transients do not appear as zero-second alarms in the operator log.
Because WinCC derives TimeDiff strictly from the two state transitions, the only stable injection points for a constant offset are at the source (the PLC) or before the row is committed to the archive (a WinCC VBScript or C-script hook). Direct UPDATE statements against the SQL archive are unreliable because the single-segment circular buffer deletes old rows when the configured size is exceeded, and the same rows are touched by WinCC's own flush transaction every 500 ms.
This reference covers the three field-proven approaches: PLC-side timestamp injection using the SIMATIC Alarm_8 telegram family, server-side message generation from a WinCC Global Script, and a minimum pulse-width approach in the PLC that keeps the alarm bit active long enough to absorb the desired offset.
Alarm Lifecycle and State Model
Every alarm bit in WinCC V7.3 traverses a finite state machine. Understanding the transitions is required before manipulating any field, because TimeDiff is only finalized when the went-out row is written.
Figure 1 — WinCC alarm state machine. TimeDiff is committed only on the State=2 transition.
Key behaviors of the state machine that matter for duration manipulation:
- The came-in row is committed as soon as the alarm bit becomes true. At this point
TimeDiff = 0andTimeGone = NULL. - The went-out row is committed as soon as the bit becomes false. WinCC stamps the row with the current system time and writes
TimeDiff = TimeGone − TimeComein milliseconds. - State 3 (acknowledged) and State 4 (reset) are operator-driven and do not affect
TimeDiff. - If the alarm bit is acknowledged but not cleared, the archive holds two rows: one State=1 (TimeDiff=0) and one State=3 (acknowledgment, no time fields populated).
Archive Database Schema: dbo.MsArcLong
WinCC Alarm Logging writes the active and historical messages to a Microsoft SQL Server database. The default database name in V7.3 follows the pattern CC_ALG_<ServerName>_<Date>_<Time>_<Seq>, and the long-term archive table is dbo.MsArcLong. The same schema is mirrored in the short-term table dbo.MsArcShort, which is rotated more frequently to bound the runtime memory footprint.
| Column | Type | Meaning |
|---|---|---|
MsgNr |
int | Internal WinCC message number. Mirrors the configured alarm number. |
State |
tinyint | 1=Came In, 2=Went Out, 3=Acknowledged, 4=Reset. |
TimeCome |
datetime | UTC timestamp of the came-in event. |
TimeGone |
datetime | UTC timestamp of the went-out event. NULL while the alarm is active. |
TimeDiff |
bigint | Duration in milliseconds. 0 at came-in, populated at went-out. |
AckTime |
datetime | Time the operator acknowledged the alarm. NULL if not configured for ack. |
MsgText |
nvarchar(255) | Resolved text including process-value substitutions. |
UserName |
nvarchar(64) | Operator or service account that triggered/acked the row. |
ComputerName |
nvarchar(64) | WinCC server that wrote the row. |
Class |
int | Alarm class ID as configured in Alarm Logging. |
Type |
int | Alarm type ID (e.g., fault, warning, status). |
Several additional columns are populated only when the configuration enables them: AGNumber, AGSubNumber, AGOBNumber, CPU, LoopFunc, Priority, Info1..Info8, and ASFlags. The full DDL is exposed in the WinCC Information Server documentation; the abbreviated schema above covers the columns you need to read or write for duration work.
MsArcLong directly with SQL, convert to local time with DATEADD(hh, DATEDIFF(hh, GETUTCDATE(), GETDATE()), TimeCome) or use the WinCC OLE-DB provider that applies the offset for you.Why Direct SQL Manipulation Fails (Archive Rotation)
The most common reason direct UPDATE calls against MsArcLong.TimeDiff are reported as "not working" is the single-segment circular buffer behavior of the WinCC archive.
Figure 2 — Single-segment archive. Rows older than OL are deleted when the segment wraps; an external UPDATE between t4 and N is lost as soon as the pointer passes it.
Specifically, the Alarm Logging runtime performs these actions every 500 ms (default flush interval):
- Reads pending messages from the internal ring buffer.
- Inserts a
MsArcLongrow for each pending State transition. TheINSERTis performed through the SQL Server OLE-DB provider in a single batch. - When the row count of
MsArcLongexceeds the configured single-segment size (default 1,000,000 rows in WinCC V7.3), the oldest rows are deleted in the same transaction.
If you execute UPDATE MsArcLong SET TimeDiff = TimeDiff + 180000 WHERE MsgNr = 12345 between flushes, the change is visible in your reporting tool until the next wrap. Once the write pointer passes that row, the original INSERT from WinCC is physically deleted and your change is lost. There is no edit history or delta log that you can replay.
UPDATE statement can also break WinCC's own internal consistency checks. The Alarm Control uses TimeDiff as a hint for grouping came-in/went-out pairs in the message window. Corrupting that value can produce phantom rows in the operator view until the next restart of the WinCC service.The two robust patterns that avoid the rotation problem entirely are described in the next sections.
Method 1 — PLC-Side Timestamp Injection with Alarm_8
The SIMATIC Alarm_8 message family (SFC 17 ALARM_8, SFC 18 ALARM_8P, and on S7-1500 the Program_Alarm instructions) is the only mechanism in the standard firmware that lets the PLC pass a custom timestamp for an alarm. WinCC receives the timestamp as part of the alarm frame and uses it directly when it writes TimeCome and TimeGone to MsArcLong. By advancing the supplied timestamp by a fixed offset, the resulting TimeDiff is exactly the value the operator expects to see.
Function-block selection:
| Controller | Block | Stamp type | Max associated values |
|---|---|---|---|
| S7-300 / S7-400 | SFC 17 ALARM_8
|
Implicit (system tick) | 10 |
| S7-300 / S7-400 | SFC 18 ALARM_8P
|
Custom UTC timestamp | 10 |
| S7-1200 / S7-1500 |
Program_Alarm (TIA) |
Custom DTL timestamp | 16 |
For a +3 minute offset, the procedure is to call SFC 18 / Program_Alarm with a DATE_AND_TIME (DT) or DTL input that is 180 seconds ahead of the real PLC clock. WinCC commits TimeCome as the supplied timestamp; when the bit later returns false, the same offset is present in TimeGone, so the resulting TimeDiff equals the true dwell time plus 180 000 ms.
SCL example for S7-1500 (TIA Portal V15.1 or later) with a 180 s offset:
// FB "AlarmWithOffset" - S7-1500
VAR CONSTANT
OFFSET_S : TIME := T#3m; // 3 minute bias
END_VAR
VAR
bAlarm : BOOL; // edge-detected alarm
bAlarmOld : BOOL;
dtStamp : DTL; // custom timestamp passed to WinCC
fbPgm : Program_Alarm;
END_VAR
IF bAlarm AND NOT bAlarmOld THEN
dtStamp := SYSTEM_CLOCK_READ(); // DTL with current UTC
dtStamp.MINUTE := (dtStamp.MINUTE + 3) MOD 60;
// Add hour carry if minute wrapped (DTL has no native TIME add)
IF dtStamp.MINUTE < 3 THEN
dtStamp.HOUR := (dtStamp.HOUR + 1) MOD 24;
END_IF;
fbPgm.SIG := TRUE;
fbPgm.TIMESTAMP := dtStamp;
fbPgm.ID := 16#0001; // configured alarm number
fbPgm.EV_ID := 1;
fbPgm.SD_1 := REAL_TO_STRING(aiValue1);
fbPgm(); // call the instruction
END_IF;
IF NOT bAlarm AND bAlarmOld THEN
fbPgm.SIG := FALSE;
fbPgm();
END_IF;
bAlarmOld := bAlarm;
Equivalent call on S7-300/400 with SFC 18 ALARM_8P:
// SCL - S7-300/400
VAR
bAlarm : BOOL;
dtStamp : DATE_AND_TIME; // 8-byte DT
bTrig : BOOL;
END_VAR
IF bAlarm AND NOT bAlarmOld THEN
dtStamp := DT_AND_TOD_TO_DT( // build DT from system clock
DTL_TO_DATE(SYSTEM_CLOCK()),
TOD#00:00:00.000);
// add 3 min to the DT using IEC standard helper FB
bTrig := TRUE;
CALL "ALARM_8P" (
SIG := bTrig,
ID := W#16#1,
EV_ID := DW#16#1,
SEVERITY := W#16#1,
TIME := dtStamp, // user-supplied stamp
FORMAT_1 := 1,
SD_1 := aiValue1
);
END_IF;
bTrig := bAlarm;
bAlarmOld := bAlarm;
Notes on this approach:
- WinCC Alarm Logging must be configured with the matching event ID and the message frame must be a free-form alarm (not a discrete alarm) so that the timestamp slot is honored.
- The Alarm Logging Routing plug-in on the WinCC side (Computer → Properties → Alarm Logging) must be set to Time stamp comes from PLC; otherwise WinCC overwrites the value with its own local clock.
- Carrying a DTL minute wrap correctly across hour boundaries requires the small carry logic shown above; for offsets longer than 60 minutes extend the carry to the HOUR field as well.
- On S7-1200, the OPC UA channel on the WinCC side can be configured to honor the source timestamp. Enable Use source timestamp in the OPC UA channel diagnostics to prevent the UA server from overwriting the value.
Method 2 — WinCC VBScript / C-Script for User-Defined Messages
If the PLC firmware does not expose the Alarm_8 / Program_Alarm interface, the next-best option is to write the message from a WinCC Global Action using the Alarm Logging OCX. The OCX allows you to specify both the TimeCome and the TimeGone timestamps explicitly, which means you can inject any offset you want at the moment the row is committed to MsArcLong.
Siemens documents the pattern in the WinCC FAQ "How do you generate user-defined operator input messages in WinCC?". The technique is to use HMIRuntime.AlarmLogging in VBScript, build a Msg object with the operator-input message number, and call CreateMsg followed by WriteMsg with explicit timestamps. The example below extends that pattern to add a 3-minute bias.
' WinCC Global Action - VBScript
Const OFFSET_MIN = 3
Dim ms, msg, nowUtc, biased
Set ms = HMIRuntime.AlarmLogging ' Alarm Logging message object
Set msg = ms.CreateMsg() ' factory for a user-defined message
' Configure message number 1001 (must exist in Alarm Logging)
msg.MsgNumber = 1001
msg.State = 1 ' came-in
' Build the biased timestamp in WinCC time (local project time)
nowUtc = CDate(FormatDateTime(Now, vbShortDate) & " " & _
FormatDateTime(Now, vbShortTime))
biased = DateAdd("n", OFFSET_MIN, nowUtc)
msg.TimeStamp = biased ' explicit came-in stamp
ms.WriteMsg msg ' commit to archive
' --- later, when the bit clears ---
Set msg = ms.CreateMsg()
msg.MsgNumber = 1001
msg.State = 2 ' went-out
msg.TimeStamp = DateAdd("n", OFFSET_MIN, Now)
ms.WriteMsg msg
The C-script equivalent is more verbose because the Alarm Logging COM interface exposes fewer helpers, but the same TimeStamp field is available via LPMSG_RTC structures. Engineers working in C should use the MSRTWriteMsg API and pass a SYSTEMTIME filled with the biased value.
Important limitations of the script approach:
- The user-defined message must be configured in the Alarm Logging editor as an Operator Input Message with the desired class, otherwise the WinCC message window will reject the row.
- Time synchronization between the WinCC server and the operator station is critical. Drift of more than 1 s will show up as a phantom came-in row in the operator log because the script's
Nowis earlier than the actual displayed time. - Scripts run in the WinCC service context. A crash of the WinCC Alarm Logging service halts the generation of new rows, so this pattern is not a substitute for a PLC-based alarm path on safety-relevant events.
- On redundant WinCC pairs, the script must run on the server that owns the archive segment. Use
@SERVERNAMEin the trigger condition to gate execution.
Method 3 — Minimum Message Bit Duration via Pulse Encoder
The third pattern does not touch the database at all; it changes the physical truth that WinCC is logging. By holding the alarm bit set for a minimum dwell time in the PLC, the operator-visible duration is automatically extended by that amount. The mechanism is a standard IEC pulse encoder, available on every S7-300/400/1200/1500 controller.
For S7-1500 the canonical FB is TP (pulse timer). The block accepts a PT preset time and emits a one-shot pulse of that duration on the rising edge of the input:
// SCL - extend alarm dwell by 180 s
VAR
bRawAlarm : BOOL; // raw field bit
bExtAlarm : BOOL; // extended bit fed to WinCC
fbTP : TP; // pulse timer, instance DB
tPreset : TIME := T#180s;
END_VAR
fbTP(IN := bRawAlarm,
PT := tPreset,
Q => bExtAlarm);
The extended bit bExtAlarm is the one tied to the WinCC message tag. The actual fault clear must be handled in the operator workflow so the message goes out only when the extended pulse ends, not when the raw field bit returns false. If the raw alarm has already cleared before TP releases the output, WinCC will see a 180 s dwell and the archive's TimeDiff will be 180 000 ms by construction.
For S7-300/400 the same logic uses SFB 3 TP from the standard library. The block is identical in behavior: rising edge on IN starts the pulse, Q is held for PT regardless of the input state during the pulse.
Prerequisites and Toolchain
Before selecting a method, confirm the following in the live project:
- WinCC V7.3 SP3 or later, with the Alarm Logging option licensed.
- SQL Server 2008 R2 or later for the archive database, with the WinCC connectivity pack installed.
- Either a STEP 7 V5.5 project (for S7-300/400) or a TIA Portal V15.1+ project (for S7-1200/1500) with online access to the controller.
- Write access to the WinCC project directory and to the
CC_ALG_<…>database under a service account that has at leastdb_datareaderon the archive. - The WinCC Information Server or a custom OLE-DB consumer if you intend to verify the rows in the archive.
| Method | Toolchain additions | Typical effort |
|---|---|---|
| Alarm_8 / Program_Alarm | TIA Portal library import; WinCC event-ID mapping | 0.5-1.5 person-day per tag |
| WinCC Global Action | Alarm Logging user-defined message; VBScript editor | 0.25-0.5 person-day per tag |
| Pulse encoder | Standard library TP FB; minor PLC rewiring |
0.1-0.2 person-day per tag |
Step-by-Step — C-Script TimeDiff Adjustment in WinCC
- In the WinCC Explorer, open Alarm Logging and create a new user-defined message under class Warning with message number
1001. The text should contain a placeholder such asDuration: %d sso the dynamic value can be substituted. - Open Global Script → C-Scripts and add a new function
Add3MinToAlarm. The function reads the current alarm bit from the internal tag list, builds anLPMSG_RTCwith a biased timestamp, and callsMSRTCreateMsg+MSRTWriteMsg. - Use
SYSTEMTIME sys;filled with the biased time:sys.wMinute = (sys.wMinute + 3) % 60;with carry intowHourif the minute wrapped. Pass&sysas the timestamp argument. - Compile the C action and bind it to a one-second trigger under Global Script → Actions. The trigger must be fast enough to capture the went-out transition before the archive's 500 ms flush commits the natural
TimeDiff. - Verify in the SQL archive that rows for message 1001 now show
TimeDiff = natural_dwell + 180000consistently across several test cycles.
Step-by-Step — STEP 7 / TIA Portal Pulse Encoder Programming
- Open the PLC project in TIA Portal and navigate to the program block that owns the raw alarm bit (for example,
OB1or a cyclic alarm FB). - Insert a new instance DB and add a static variable of type
TP(orIEC_TIMERon older firmware). Name itstatExtPulse. - Wire the raw alarm bit to
IN, setPT := T#180s, and routeQto the newbExtAlarmtag that is already bound to the WinCC message in the tag management. - Download the hardware configuration and the program block to the controller. Force
bRawAlarmin a watch table to confirm the 180 s pulse onbExtAlarm. - Cross-check from the WinCC side using the Tag Simulation tool: the alarm row in the message window should remain visible for exactly 180 s, with
TimeDiff= 180 000 ms in the archive.
Verification Procedures
After any of the three methods is deployed, run the following verification pass before signing off the change:
- Force the alarm bit true for exactly 10 s and observe the operator log. The displayed duration must equal the configured offset plus 10 s.
- Force the alarm bit for 300 s and confirm the displayed duration scales linearly (offset + 300 s).
- Run a 24-hour soak test. Inspect the SQL archive for any rows where
TimeDiff < OFFSET; those are the rows that did not receive the bias and indicate either a missed trigger or a WinCC service restart between the came-in and went-out events. - Force a WinCC service restart while an alarm is active. After the restart, verify that the bias is applied uniformly. A missed bias on the went-out row is the most common sign that the bias was implemented in the script path rather than at the PLC source.
- Confirm the archive rotation is no longer the failure mode. The single-segment circular buffer in V7.3 still rolls over, but the rows being written already contain the correct bias, so a wrap does not destroy the offset.
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Bias not visible in operator log | WinCC Alarm Logging routing set to "local time stamp" | Computer → Properties → Alarm Logging → Time stamp = "from PLC" |
| Bias lost after firmware update on S7-1500 | OPC UA channel reverts to server time | Enable "Use source timestamp" in OPC UA channel diagnostics |
| UPDATE on MsArcLong disappears after 1 hour | Single-segment wrap deleted the row | Move bias to PLC or script; do not edit the live archive |
| Bias appears as 3 h on the next calendar day | DTL minute carry forgot to wrap hour | Add the IF dtStamp.MINUTE < 3 block shown in Method 1 |
| Ghost rows after restart of WinCC service | External UPDATE corrupted the came-in/went-out pair ID | Restore the archive from the last good backup; switch to a PLC-based method |
| Script does not fire | Global Action trigger period > 1000 ms | Reduce trigger to 250 ms; verify under Global Script → Diagnostics |
| TP pulse shorter than configured | OB1 cycle time exceeds PT | Move TP to a cyclic OB (e.g., OB35 at 100 ms) and confirm with watch table |
| Bias differs by 1 s between operators | WinCC server not NTP-synchronized | Enable NTP on both server and client; verify drift with w32tm /monitor
|
| Bias is correct locally but wrong on Information Server | Information Server reads TimeDiff raw without applying timezone |
Apply UTC-to-local conversion in the reporting query; do not edit the archive |
FAQ
Why does direct UPDATE on dbo.MsArcLong.TimeDiff fail?
WinCC Alarm Logging uses a single-segment circular buffer that physically deletes the oldest rows when the configured size is exceeded. Any external UPDATE is overwritten or lost as soon as the write pointer passes that row. Move the bias to the PLC (Alarm_8 / Program_Alarm) or to a WinCC script that writes the message before commit.
Can I use SFC 17 ALARM_8 instead of SFC 18?
No. SFC 17 ALARM_8 always uses the controller's system clock and does not accept a user-supplied timestamp. Only SFC 18 ALARM_8P and the TIA Program_Alarm family accept a custom DATE_AND_TIME / DTL input. For a constant offset you must use the parameterized variant.
How much offset is practical with a TP pulse?
Anywhere from 100 ms to several hours. The block has no upper bound on PT, but very long pulses will mask fast transients that the operator should see. Field practice is to keep PT at the minimum value that satisfies the reporting requirement, typically 3 minutes, matching the example in this article.
Does the VBScript approach work on S7-1200 with OPC UA?
Yes, provided the WinCC project is configured with the OPC UA channel and the user-defined message is registered in Alarm Logging. The script reads Now from the WinCC server clock, so time drift between the server and the controller is the main risk. Enable NTP on both nodes and verify drift is below 1 s with w32tm /monitor.
What happens to the bias when the archive segment rolls over?
Nothing. The bias is applied at the moment the row is written (either by the PLC frame or by the WinCC script). The wrap deletes whole rows, but every row that survives the wrap already contains the correct TimeDiff. There is no "old" or "new" archive that disagrees on the bias.