// Siemens S7-1200/S7-1500 SCL teaching artifact // Reviewed: 2026-08-31 // Purpose: demonstrate bounded multi-cycle array work and bounded OB80 first-out capture. // This is not a TIA Portal import file, CPU configuration, OB80 interface, safety program, // physical I/O design, watchdog preset or production validation. Create the blocks in the // exact supported project, map the actual generated OB80 start information, compile, review, // and execute the acceptance matrix on an approved target. FUNCTION_BLOCK "FB_TimeErrorFirstOut" VAR_INPUT Capture : Bool; EventId : Word; FaultingOb : UInt; FaultingPriority : USInt; END_VAR VAR_OUTPUT Latched : Bool; EventCount : UDInt; FirstEventId : Word; FirstFaultingOb : UInt; FirstPriority : USInt; LastEventId : Word; LastFaultingOb : UInt; END_VAR BEGIN IF #Capture THEN #EventCount := #EventCount + UDINT#1; #LastEventId := #EventId; #LastFaultingOb := #FaultingOb; IF NOT #Latched THEN #Latched := TRUE; #FirstEventId := #EventId; #FirstFaultingOb := #FaultingOb; #FirstPriority := #FaultingPriority; END_IF; END_IF; END_FUNCTION_BLOCK // Call the first-out FB once per OB80 invocation with Capture := TRUE. // Use the exact start-information fields TIA Portal creates for the selected CPU/project. // Do not paste member names from another CPU generation or migrated structure. // There is intentionally no automatic reset inside the time-error handler. FUNCTION_BLOCK "FB_BoundedArrayWorker" VAR_INPUT Start : Bool; Revision : UDInt; ItemCount : UInt; // Accepted range: 0..500 MaxItemsPerCall : UInt; // Accepted range: 1..50 END_VAR VAR_IN_OUT Items : Array[0..499] of Int; END_VAR VAR_OUTPUT Busy : Bool; Done : Bool; Error : Bool; InvalidIndex : UInt; END_VAR VAR Index : UInt; CapturedRev : UDInt; Processed : UInt; END_VAR BEGIN #Done := FALSE; IF #Start AND NOT #Busy THEN #Error := FALSE; #InvalidIndex := UINT#0; #Index := UINT#0; #CapturedRev := #Revision; #Busy := (#ItemCount > UINT#0) AND (#ItemCount <= UINT#500) AND (#MaxItemsPerCall > UINT#0) AND (#MaxItemsPerCall <= UINT#50); IF NOT #Busy THEN #Error := TRUE; END_IF; END_IF; IF #Busy THEN IF #Revision <> #CapturedRev THEN #Busy := FALSE; #Error := TRUE; ELSE #Processed := UINT#0; WHILE (#Processed < #MaxItemsPerCall) AND (#Index < #ItemCount) DO IF (#Items[#Index] < INT#0) OR (#Items[#Index] > INT#1000) THEN #InvalidIndex := #Index; #Busy := FALSE; #Error := TRUE; ELSE #Index := #Index + UINT#1; #Processed := #Processed + UINT#1; END_IF; END_WHILE; IF #Busy AND (#Index >= #ItemCount) THEN #Busy := FALSE; #Done := TRUE; END_IF; END_IF; END_IF; END_FUNCTION_BLOCK // Required tests: zero count, maximum count, invalid MaxItemsPerCall, invalid first/last // item, revision change while Busy, held Start, second Start after Done, STOP/RUN, // power cycle according to configured retentivity, and peak-load cycle measurement.