Count good parts, rejects, run time and downtime in a retentive PLC data block from mapped sensor signals, calculate the KPIs in the PLC, freeze a snapshot at shift end, let the panel export the snapshot to CSV, and clear the shift counters only after the panel confirms the export.
- Counters and totals live in the PLC with retentive memory, never only on the panel.
- Snapshot first, export second, clear last – and only on confirmed success.
- Shift totals reset; lifetime totals never do.
Production Data and Reset Rules
Everything on a shift report starts as a counter or a timer in the controller. Keep two sets of numbers: shift totals, reset at every shift change, and lifetime totals, which never reset and drive maintenance intervals. The safe end-of-shift sequence is always the same: freeze a snapshot, write the report from the snapshot, confirm the write, and only then clear the shift counters. A report generated from live counters loses the parts produced during the export; a reset that runs after a failed export loses a whole shift.
Lab Flow: From Outfeed Sensor to an Exported Shift Report
Stage 1Field Wiring and PLC Tag Table
Count from sensors that see the finished part. The running signal is the contactor feedback already used in the conveyor motor M1 example.
| PLC tag | Address | Type | Device |
|---|---|---|---|
B10_PartGood | %I4.0 | Bool | Diffuse sensor at the outfeed, 1 = part present |
B11_Reject | %I4.1 | Bool | Sensor at the reject chute |
S31_ShiftEnd | %I4.6 | Bool | Supervisor key-switch, momentary |
M1_Running | %I2.4 | Bool | Contactor K1 auxiliary (existing) |
Stage 2Input Mapping into DB_IO
// FC_InputMapping – production signals "DB_IO".B10_PartGood := "B10_PartGood"; "DB_IO".B11_Reject := "B11_Reject"; "DB_IO".S31_ShiftEnd := "S31_ShiftEnd"; // "DB_IO".M1_Running is already mapped in the interlocks lesson
Stage 3Logic: FB_ShiftReport
Set DB_Shift to retentive so counts survive a power cycle. Counter basics are in PLC counters CTU, CTD and CTUD; here the counting is done with edge detection in SCL so the totals can be DInt and reset under control.
// UDT_ShiftData: Good, Reject, RunSec, DownSec, Stops, LongestStop : DInt;
// Availability, RejectPct, PartsPerHour : Real;
// DB_Shift (DB50, retentive): Live, Snapshot : UDT_ShiftData; LifetimeGood : DInt;
// Cmd_ShiftEnd, ReportReady, ExportDone, ExportFailed : Bool;
// FB_ShiftReport – STAT: tSec, tExport : TON_TIME; GoodOld, RejOld, RunOld, EndOld : Bool; CurStop : DInt;
#tSec(IN := NOT #tSec.Q, PT := T#1S); // 1 s pulse, no %M clock memory
IF "DB_IO".B10_PartGood AND NOT #GoodOld THEN
"DB_Shift".Live.Good := "DB_Shift".Live.Good + 1;
"DB_Shift".LifetimeGood := "DB_Shift".LifetimeGood + 1;
END_IF;
IF "DB_IO".B11_Reject AND NOT #RejOld THEN "DB_Shift".Live.Reject := "DB_Shift".Live.Reject + 1; END_IF;
IF #RunOld AND NOT "DB_IO".M1_Running THEN "DB_Shift".Live.Stops := "DB_Shift".Live.Stops + 1; END_IF;
IF #tSec.Q THEN
IF "DB_IO".M1_Running THEN
"DB_Shift".Live.RunSec := "DB_Shift".Live.RunSec + 1; #CurStop := 0;
ELSE
"DB_Shift".Live.DownSec := "DB_Shift".Live.DownSec + 1; #CurStop := #CurStop + 1;
IF #CurStop > "DB_Shift".Live.LongestStop THEN "DB_Shift".Live.LongestStop := #CurStop; END_IF;
END_IF;
END_IF;
#GoodOld := "DB_IO".B10_PartGood; #RejOld := "DB_IO".B11_Reject; #RunOld := "DB_IO".M1_Running;
// KPIs calculated once, in the PLC
IF "DB_Shift".Live.RunSec + "DB_Shift".Live.DownSec > 0 THEN
"DB_Shift".Live.Availability := DINT_TO_REAL("DB_Shift".Live.RunSec) * 100.0
/ DINT_TO_REAL("DB_Shift".Live.RunSec + "DB_Shift".Live.DownSec);
"DB_Shift".Live.PartsPerHour := DINT_TO_REAL("DB_Shift".Live.Good) * 3600.0
/ DINT_TO_REAL("DB_Shift".Live.RunSec + "DB_Shift".Live.DownSec);
END_IF;
IF "DB_Shift".Live.Good + "DB_Shift".Live.Reject > 0 THEN
"DB_Shift".Live.RejectPct := DINT_TO_REAL("DB_Shift".Live.Reject) * 100.0
/ DINT_TO_REAL("DB_Shift".Live.Good + "DB_Shift".Live.Reject);
END_IF;
// Shift end: freeze snapshot, ask the HMI to export
IF ("DB_Shift".Cmd_ShiftEnd OR "DB_IO".S31_ShiftEnd) AND NOT #EndOld AND NOT "DB_Shift".ReportReady THEN
"DB_Shift".Snapshot := "DB_Shift".Live;
"DB_Shift".ReportReady := TRUE; "DB_Shift".ExportDone := FALSE; "DB_Shift".ExportFailed := FALSE;
END_IF;
#EndOld := "DB_Shift".Cmd_ShiftEnd OR "DB_IO".S31_ShiftEnd;
// Clear the shift ONLY after the HMI confirms the export
#tExport(IN := "DB_Shift".ReportReady AND NOT "DB_Shift".ExportDone, PT := T#30S);
IF "DB_Shift".ReportReady AND "DB_Shift".ExportDone THEN
"DB_Shift".Live.Good := 0; "DB_Shift".Live.Reject := 0; "DB_Shift".Live.RunSec := 0;
"DB_Shift".Live.DownSec := 0; "DB_Shift".Live.Stops := 0; "DB_Shift".Live.LongestStop := 0;
"DB_Shift".ReportReady := FALSE;
ELSIF #tExport.Q THEN
"DB_Shift".ExportFailed := TRUE; "DB_Shift".ReportReady := FALSE; // alarm, counters kept
END_IF;
Stage 4HMI Tags and the CSV Export Handshake
Drag DB_Shift into the HMI tag table. The panel must react to DB_Shift_ReportReady: on its value-change event, call a script that writes the snapshot and sets ExportDone only if the write succeeded. Alternatively, log the snapshot tags on demand into a segmented data log so each shift lands in its own file, as described in HMI trends and data logs.
' HMI script ShiftExport – called from the value-change event of DB_Shift_ReportReady
' Outline for panels that support file access in scripts; check your panel's scripting reference.
Sub ShiftExport()
If SmartTags("DB_Shift_ReportReady") = 0 Then Exit Sub ' event also fires on 1 → 0
On Error Resume Next
Dim fso, f, line
Set fso = CreateObject("FileSystemObject")
Set f = fso.OpenTextFile("\Storage Card USB\ShiftReport.csv", 8, True) ' 8 = append
line = Now & ";" & SmartTags("DB_Shift_Snapshot_Good") & ";" & SmartTags("DB_Shift_Snapshot_Reject") _
& ";" & SmartTags("DB_Shift_Snapshot_Availability") & ";" & SmartTags("DB_Shift_Snapshot_PartsPerHour")
f.WriteLine line
f.Close
If Err.Number = 0 Then SmartTags("DB_Shift_ExportDone") = 1 ' confirm ONLY after a good write
End Sub
| HMI tag | PLC member | Used by |
|---|---|---|
DB_Shift_Live_* | "DB_Shift".Live.* | KPI fields ① – ④ |
DB_Shift_Snapshot_* | "DB_Shift".Snapshot.* | Export script |
DB_Shift_Cmd_ShiftEnd | "DB_Shift".Cmd_ShiftEnd | End Shift button ⑥ |
DB_Shift_ReportReady | "DB_Shift".ReportReady | Value-change event → ShiftExport |
DB_Shift_ExportDone | "DB_Shift".ExportDone | Written by the script |
DB_Shift_ExportFailed | "DB_Shift".ExportFailed | Discrete alarm + status ⑦ |
Stage 5Link the Shift Screen Graphics
| # | Object | Properties | Tag / event |
|---|---|---|---|
| ① – ④ | Output fields in four blocks | Integers with thousands separator, times as h:min, percentages 99.9 % | DB_Shift_Live_* |
| ⑤ | Trend view | Logged curve of parts per hour, segmented per shift | Data log, see the trends lesson |
| ⑥ | Button END SHIFT / EXPORT | Authorisation Supervisor; confirmation dialog recommended | Press: SetBit, Release: ResetBit DB_Shift_Cmd_ShiftEnd |
| ⑦ | Status field + alarm | Text: written / pending / failed; red outline when failed | DB_Shift_ReportReady, _ExportFailed |
Who may end a shift or reset counters is defined in HMI user administration. Layout and colour follow HMI screen design best practices.
Stage 6Perform and Test: Animated Shift Report
Parts run down the line; the outfeed sensor B10 counts good parts and B11 counts rejects. Stop the machine to create downtime, then end the shift: the PLC freezes a snapshot, the panel exports it, and only after ExportDone are the shift counters cleared. Remove the USB stick first to see a failed export protect the data. An 8-hour shift runs in about 80 seconds.
| Shift | Good | Reject | Avail. | Parts/h | Export |
|---|
Troubleshooting Shift Reports
| Symptom | Likely cause | First check |
|---|---|---|
| Count jumps by many per part | Counting the level, not the edge | Edge detection on B10 in FB_ShiftReport |
| Counts lost after power failure | DB_Shift not retentive | Retain setting of DB_Shift |
| Shift counters reset but no file | Reset not tied to ExportDone | The clear condition must require ExportDone |
| Report shows wrong availability | KPI calculated on the panel | Calculate once in the PLC |
| ExportFailed every shift | Path wrong or stick not writable | Path in the script, storage card format |
Hands-On Lab: Shift Report from Outfeed Sensor to CSV
Hands-on- TIA Portal with WinCC Comfort and a panel or PLCSIM + HMI simulation
- A USB stick, SD card or simulated storage path
- Estimated time: 90 minutes
Wire the sensors and map PLC tags
Declare B10_PartGood, B11_Reject and S31_ShiftEnd as inputs and reuse M1_Running from the interlocks lesson.
Map inputs into DB_IO
Copy the sensors and the shift-end key into DB_IO in FC_InputMapping.
Write FB_ShiftReport
Create UDT_ShiftData and a retentive DB_Shift, count with edges, accumulate run and down time with a 1 s pulse, calculate KPIs, freeze a snapshot and clear only after ExportDone.
Create HMI tags and the export
Drag DB_Shift into the HMI tag table and attach the ShiftExport script (or a data-log export) to the value-change event of ReportReady.
Link the shift screen graphics
Add output fields for every KPI, the End Shift button with Supervisor rights and the export status and alarm.
Perform and test
Run a shift, create downtime, export with and without storage and confirm counters are cleared only after a successful export.
Production data survives restarts, shift and lifetime totals are separate, the export produces a readable CSV from a frozen snapshot, and a failed export never clears the counters.
Frequently asked questions
Should counters be kept in the PLC or on the HMI?
In the PLC with retentive memory. A panel can be replaced, restarted or swapped for a spare, and production data must not depend on it.
Why copy values to a snapshot before writing the report?
So parts produced during the export are not lost between the closing shift and the new one. The report is written from a frozen set of values while the live counters continue.
Can a Comfort panel write directly to a network drive?
Yes, if the path is reachable and the panel has permission. Always handle the case where the network is unavailable, so the export fails visibly instead of silently.
Where should availability and parts per hour be calculated?
In the PLC. Calculating on the panel means SCADA, reports and the panel can each show a slightly different number for the same shift.
Why count from the outfeed sensor and not the cycle start?
Counting commands means an aborted cycle still counts as production. A sensor that sees the finished part counts only what was really made.
What happens if the export fails?
The PLC waits for ExportDone. If it does not arrive within the timeout, ExportFailed raises an alarm and the shift counters are kept, so the shift can be exported again.