FUNCTION_BLOCK "ReadSessionDiagnostics"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      sessionNameSubstring { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : String;
      nodeIdExtension { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : WString;
      read { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      enable { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
   END_VAR

   VAR_OUTPUT 
      done { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      busy { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      error { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      status { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DWord;
      sessionsFound { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt;
   END_VAR

   VAR_IN_OUT 
      readValue : Variant;
   END_VAR

   VAR 
      statRead { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statReadHandled { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statREQ1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statREQ2 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statSessionsFound { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt;
      statSessionIdentifier { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : WString;
      statState { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : USInt := #STATE_FINDSESSION;
      statDone { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statError { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statStatus { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DWord;
      statBusy { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      statConnectionHdl { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DWord := -42;
      statNodeCount1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt := #NODES_COUNT_1;
      statNodeIds1 {InstructionName := 'OPC_UA_NodeId'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : Array[0..#NODES_UPPERBOUND_1] of OPC_UA_NodeId := [((), WSTRING#'3707', ())];
      statNodeStatusList1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[0..#NODES_UPPERBOUND_1] of DWord;
      statTimeStamps1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[0..#NODES_UPPERBOUND_1] of LDT;
      statVariable1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         SessionDiagnosticsArray { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : Array[0.."SESSIONS_UPPERBOUND"] of "typeSessionDiagnostics";
      END_STRUCT;
      statNodeCount2 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt := #NODES_COUNT_2;
      statNodeIds2 {InstructionName := 'OPC_UA_NodeId'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : Array[0..#NODES_UPPERBOUND_2] of OPC_UA_NodeId := [(1, #NODEID_SUFFIX, 1)];
      statNodeStatusList2 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[0..#NODES_UPPERBOUND_2] of DWord;
      statTimeStamps2 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[0..#NODES_UPPERBOUND_2] of LDT;
      statVariable2SessionDiagnostics { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : "typeSessionDiagnostics";
      END_STRUCT;
      statVariable2ServiceCounter { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : "typeServiceCounter";
      END_STRUCT;
      statVariable2UDInt { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UDInt;
      END_STRUCT;
      statVariable2LDT { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : LDT;
      END_STRUCT;
      statVariable2LReal { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : LReal;
      END_STRUCT;
      statVariable2Array05String { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[0..5] of String;
      END_STRUCT;
      statVariable2String { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : String;
      END_STRUCT;
      statVariable2ApplicationDescription { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : "typeApplicationDescription";
      END_STRUCT;
      statVariable2OpcUaNodeId { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Struct
         Value {InstructionName := 'OPC_UA_NodeId'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : OPC_UA_NodeId;
      END_STRUCT;
      instOpcUaReadList1 {InstructionName := 'OPC_UA_ReadList'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : OPC_UA_ReadList;
      instOpcUaReadList2 {InstructionName := 'OPC_UA_ReadList'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : OPC_UA_ReadList;
   END_VAR

   VAR_TEMP 
      tempRead1Done : Bool;
      tempRead1Error : Bool;
      tempRead1Busy : Bool;
      tempRead1Status : DWord;
      tempRead2Done : Bool;
      tempRead2Error : Bool;
      tempRead2Busy : Bool;
      tempRead2Status : DWord;
      tempSessionCounter : DInt;
      tempStringFound : Int;
   END_VAR

   VAR CONSTANT 
      STATE_IDLE : USInt := 0;
      STATE_FINDSESSION_PRE : USInt := 10;
      STATE_FINDSESSION : USInt := 11;
      STATE_READSESSIONDIAGNOSTICS_PRE : USInt := 20;
      STATE_READSESSIONDIAGNOSTICS : USInt := 21;
      STATE_ERROR : USInt := 99;
      STATUS_GOOD : DWord := 16#00000000;
      STATUS_NODENOTEXISTING : DWord := 16#80340000;
      STATUS_NODATA : DWord := 16#80370000;
      STATUS_UNEXPECTEDTYPE : DWord := 16#82000000;
      STATUS_INCORRECTSTATE : DWord := 16#82010000;
      STATUS_NOSESSIONFOUND : DWord := 16#90010000;
      NODES_UPPERBOUND_1 : DInt := 0;
      NODES_COUNT_1 : UInt := 1;
      NODES_UPPERBOUND_2 : DInt := 0;
      NODES_COUNT_2 : UInt := 1;
      NODEID_PREFIX : WString := WSTRING#'Session_';
      NODEID_SUFFIX : WString := WSTRING#'SessionDiagnostics';
      NODEID_SEPERATOR : WString := WSTRING#'.';
   END_VAR


BEGIN
	REGION Description header
	    //==================================================================================
	    // SIEMENS / (C) Copyright 2023
	    //----------------------------------------------------------------------------------
	    // Title: GetLastContact
	    // Comment/Function: reads out the Server node data of the internal OPC UA server to retrieve the LastContactTime
	    // Library/Family: 
	    // Author: SIEMENS SIMATIC Systems Support (SUP)
	    // Target System: 
	    // Engineering: TIA Portal (>= V18)
	    // Restrictions: 
	    // Requirements: FW 3.0 (1500)
	    //----------------------------------------------------------------------------------
	    // Change log table:
	    // Version | Date | Expert in charge | Changes applied
	    //-------------|------------|------------------|------------------------------------
	    // 001.000.000 | 2023-02-13 | SUP SPH | First released version
	    //==================================================================================
	END_REGION Description header
	
	REGION OPC_UA_ReadList Instances
	    
	    // 1 - Read SessionDiagnosticsArray
	    #instOpcUaReadList1(REQ := #statREQ1,
	                        ConnectionHdl := #statConnectionHdl,
	                        NodeHdlCount := #statNodeCount1,
	                        NodeHdls := #statNodeIds1,
	                        Done => #tempRead1Done,
	                        Busy => #tempRead1Busy,
	                        Error => #tempRead1Error,
	                        Status => #tempRead1Status,
	                        NodeStatusList := #statNodeStatusList1,
	                        TimeStamps := #statTimeStamps1,
	                        Variable := #statVariable1);
	    
	    // 2 - Read SessionDiagnostics
	    REGION #instOPC_UA_ReadList2
	        
	        CASE TypeOf(#readValue) OF
	                
	            typeSessionDiagnostics:
	                //addressing SessionDiagnostiocs as a whole
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2SessionDiagnostics);
	                
	            typeServiceCounter:
	                //e.g. addressing SessionDiagnostics.ReadCount or .WriteCount
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2ServiceCounter);
	                
	            UDInt:
	                //e.g. addressing SessionDiagnostics.MaxResponseMessageSize
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2UDInt);
	                
	            LDT:
	                //e.g. addressing SessionDiagnostics.ClientConnectionTime or .ClientLastContactTime
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2LDT);
	                
	            LReal:
	                //e.g. addressing SessionDiagnostics.ActualSessionTimeout
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2LReal);
	                
	            String:
	                //e.g. addressing SessionDiagnostics.ServerUri or .EndpointUrl
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2String);
	                
	            typeApplicationDescription:
	                //e.g. addressing SessionDiagnostics.ClientDescription
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2ApplicationDescription);
	                
	            OPC_UA_NodeId:
	                //e.g. addressing SessionDiagnostics.SessionId
	                #instOpcUaReadList2(REQ := #statREQ2,
	                                    ConnectionHdl := #statConnectionHdl,
	                                    NodeHdlCount := #statNodeCount2,
	                                    NodeHdls := #statNodeIds2,
	                                    Done => #tempRead2Done,
	                                    Busy => #tempRead2Busy,
	                                    Error => #tempRead2Error,
	                                    Status => #tempRead2Status,
	                                    NodeStatusList := #statNodeStatusList2,
	                                    TimeStamps := #statTimeStamps2,
	                                    Variable := #statVariable2OpcUaNodeId);
	            ELSE
	                #statStatus := #STATUS_UNEXPECTEDTYPE;
	                #statState := #STATE_ERROR;
	        END_CASE;
	    END_REGION #instOPC_UA_ReadList2
	    
	END_REGION OPC_UA_ReadList Instances
	
	REGION Check Trigger Input
	    
	    IF (#statState <> #STATE_ERROR) THEN
	        IF NOT #read AND NOT #enable THEN
	            #statState := #STATE_IDLE;
	            #statReadHandled := FALSE;
	        ELSE
	            IF #statReadHandled = FALSE THEN
	                #statRead := TRUE;
	                #statReadHandled := TRUE;
	            END_IF;
	        END_IF;
	    END_IF;
	    
	END_REGION Check Trigger Input
	
	REGION State Handling
	    
	    CASE #statState OF
	            
	        #STATE_IDLE: //idle mode, waiting for start command
	            REGION #STATE_IDLE
	                #statREQ2 := FALSE;
	                
	                #statDone := FALSE;
	                #statError := FALSE;
	                #statStatus := #STATUS_GOOD;
	                #statSessionsFound := 0;
	                
	                IF (#statRead) THEN
	                    #statRead := FALSE;
	                    #statNodeIds2[0].Identifier := #NODEID_SUFFIX;
	                    #statBusy := TRUE;
	                    #statState := #STATE_FINDSESSION_PRE;
	                ELSE
	                    #statBusy := FALSE;
	                    #statREQ1 := FALSE;
	                END_IF;
	            END_REGION #STATE_IDLE
	            
	        #STATE_FINDSESSION_PRE: //prepare finding the required client session
	            REGION #STATE_FINDSESSION_PRE
	                #statREQ1 := TRUE;
	                #statSessionsFound := 0;
	                #statState := #STATE_FINDSESSION;
	            END_REGION #STATE_FINDSESSION_PRE
	        #STATE_FINDSESSION: //try to find required session name in current sessions
	            REGION #STATE_FINDSESSION
	                
	                //check if execution is finished
	                IF #tempRead1Done OR #tempRead1Error THEN
	                    
	                    //reset trigger flag
	                    #statREQ1 := FALSE;
	                    
	                    //check if everything was successful
	                    IF #tempRead1Done AND NOT #tempRead1Error AND #statNodeStatusList1[0] = #STATUS_GOOD THEN
	                        
	                        //Search specified client session substring in list of returned sessions
	                        FOR #tempSessionCounter := 0 TO "SESSIONS_UPPERBOUND" DO
	                            
	                            IF (LEN(IN := #statVariable1.SessionDiagnosticsArray[#tempSessionCounter].sessionName) > 0) THEN
	                                
	                                //find sessionNameSubstring in sessionName
	                                #tempStringFound := FIND(IN1 := #statVariable1.SessionDiagnosticsArray[#tempSessionCounter].sessionName, IN2 := #sessionNameSubstring);
	                                
	                                IF #tempStringFound > 0 THEN
	                                    
	                                    #statSessionsFound += 1;
	                                    
	                                    //first best result is taken for later use
	                                    IF (#statSessionsFound = 1) THEN
	                                        #statSessionIdentifier := #statVariable1.SessionDiagnosticsArray[#tempSessionCounter].sessionId.Identifier;
	                                    END_IF;
	                                    
	                                END_IF;
	                            END_IF;
	                            
	                        END_FOR;
	                        
	                        //no matching session name was found
	                        IF NOT (#statSessionsFound > 0) THEN
	                            #statStatus := #STATUS_NOSESSIONFOUND;
	                        END_IF;
	                        
	                    ELSIF #tempRead1Error THEN//error occurred
	                        #statStatus := #tempRead1Status;
	                    ELSIF #statNodeStatusList1[0] = #STATUS_NODATA THEN//read delivered no data
	                        #statStatus := #STATUS_NODATA;
	                    ELSIF #statNodeStatusList1[0] <> #STATUS_GOOD THEN//read returned bad node status
	                        #statStatus := #statNodeStatusList1[0];
	                    END_IF;
	                    
	                    //if the required session was found go to next state
	                    IF (#statSessionsFound > 0) THEN
	                        #statState := #STATE_READSESSIONDIAGNOSTICS_PRE;
	                    ELSE
	                        IF (#enable) THEN //try to find session repeatedly
	                            #statState := #STATE_FINDSESSION_PRE;
	                        ELSE //go to idle mode because only a single read was requested
	                            #statState := #STATE_IDLE;
	                            #statError := TRUE;
	                        END_IF;
	                    END_IF;
	                    
	                END_IF;
	            END_REGION #STATE_FINDSESSION
	            
	        #STATE_READSESSIONDIAGNOSTICS_PRE: //prepare reading the SessionDiagnostics or a subelement
	            REGION #STATE_READSESSIONDIAGNOSTICS_PRE
	                
	                //Build session-specific NodeId of SessionDiagnostics node
	                #statNodeIds2[0].Identifier := CONCAT_WSTRING(IN1 := #NODEID_PREFIX, IN2 := #statSessionIdentifier, IN3 := WSTRING#'.', IN4 := #NODEID_SUFFIX);
	                
	                //if a subelement is to be addressed, extend the NodeId
	                IF (LEN(#nodeIdExtension) > 0) THEN
	                    //check whether NodeId extension was already given with a '.'
	                    IF (FIND(IN1 := #nodeIdExtension, IN2 := #NODEID_SEPERATOR) = 1) THEN
	                        #statNodeIds2[0].Identifier := CONCAT_WSTRING(IN1 := #statNodeIds2[0].Identifier, IN2 := #nodeIdExtension);
	                    ELSE
	                        #statNodeIds2[0].Identifier := CONCAT_WSTRING(IN1 := #statNodeIds2[0].Identifier, IN2 := #NODEID_SEPERATOR, IN3 := #nodeIdExtension);
	                    END_IF;
	                END_IF;
	                
	                #statREQ2 := TRUE;
	                #statDone := FALSE;
	                #statState := #STATE_READSESSIONDIAGNOSTICS;
	            END_REGION #STATE_READSESSIONDIAGNOSTICS_PRE
	        #STATE_READSESSIONDIAGNOSTICS: //read SessionDiagnostics or a subelement
	            REGION #STATE_READSESSIONDIAGNOSTICS
	                
	                //check if execution is finished
	                IF #tempRead2Done OR #tempRead2Error THEN
	                    
	                    //reset trigger flag
	                    #statREQ2 := FALSE;
	                    
	                    //check if everything was successful
	                    IF #tempRead2Done AND NOT #tempRead2Error AND #statNodeStatusList2[0] = #STATUS_GOOD THEN
	                        
	                        #statDone := TRUE;
	                        
	                        IF #enable THEN
	                            #statState := #STATE_READSESSIONDIAGNOSTICS_PRE; //read values repeatedly
	                        ELSE
	                            #statState := #STATE_IDLE; //go to idle mode because only a single read was requested
	                        END_IF;
	                        
	                    ELSE
	                        IF #statNodeStatusList2[0] = #STATUS_NODENOTEXISTING THEN //session specific node is not existing
	                            #statStatus := #STATUS_NODENOTEXISTING;
	                            
	                            IF #enable THEN
	                                #statState := #STATE_FINDSESSION_PRE; //go back to finding the session again
	                            ELSE
	                                #statError := TRUE;
	                                #statState := #STATE_IDLE; //go to idle mode because only a single read was requested
	                            END_IF;
	                        ELSIF #statNodeStatusList2[0] <> #STATUS_GOOD THEN//read returned bad node status
	                            #statStatus := #statNodeStatusList2[0];
	                            
	                            IF #enable THEN
	                                #statState := #STATE_READSESSIONDIAGNOSTICS_PRE; //try reading the node again
	                            ELSE
	                                #statError := TRUE;
	                                #statState := #STATE_IDLE; //go to idle mode because only a single read was requested
	                            END_IF;
	                        ELSE//read instruction returned any unexpected status
	                            #statStatus := #tempRead2Status;
	                            
	                            IF #enable THEN
	                                #statState := #STATE_READSESSIONDIAGNOSTICS_PRE; //try read again
	                            ELSE
	                                #statError := TRUE;
	                                #statState := #STATE_IDLE; //go to idle mode because only a single read was requested
	                            END_IF;
	                        END_IF;
	                    END_IF;
	                    
	                END_IF;
	                
	            END_REGION #STATE_READSESSIONDIAGNOSTICS
	            
	        #STATE_ERROR: //something is configured incorrectly. no further execution any more
	            REGION #STATE_ERROR
	                #statError := TRUE;
	                #statBusy := FALSE;
	                #statDone := FALSE;
	            END_REGION #STATE_ERROR
	            
	        ELSE //should never come here
	            #statStatus := #STATUS_INCORRECTSTATE;
	            #statState := #STATE_ERROR;
	            
	    END_CASE;
	END_REGION State Handling
	
	REGION Set Output
	    IF (#statDone AND #statState <> #STATE_ERROR) THEN
	        
	        //pass the matching read value back to the InOut Variant readValue
	        CASE TypeOf(#readValue) OF
	            typeSessionDiagnostics:
	                VariantPut(SRC := #statVariable2SessionDiagnostics.Value,
	                           DST := #readValue);
	            typeServiceCounter:
	                VariantPut(SRC := #statVariable2ServiceCounter.Value,
	                           DST := #readValue);
	            UDInt:
	                VariantPut(SRC := #statVariable2UDInt.Value,
	                           DST := #readValue);
	            LDT:
	                VariantPut(SRC := #statVariable2LDT.Value,
	                           DST := #readValue);
	            LReal:
	                VariantPut(SRC := #statVariable2LReal.Value,
	                           DST := #readValue);
	            String:
	                VariantPut(SRC := #statVariable2String.Value,
	                           DST := #readValue);
	            typeApplicationDescription:
	                VariantPut(SRC := #statVariable2ApplicationDescription.Value,
	                           DST := #readValue);
	            OPC_UA_NodeId:
	                VariantPut(SRC := #statVariable2OpcUaNodeId.Value,
	                           DST := #readValue);
	            ELSE
	                #statStatus := #STATUS_UNEXPECTEDTYPE;
	                #statState := #STATE_ERROR;
	        END_CASE;
	    END_IF;
	    
	    //set output signals
	    #sessionsFound := #statSessionsFound;
	    #error := #statError;
	    #status := #statStatus;
	    #busy := #statBusy;
	    #done := #statDone;
	END_REGION Set Output
	
END_FUNCTION_BLOCK

