FUNCTION_BLOCK "FB_CommunicationWES_DeviceHub"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_IN_OUT 
      fbInOutSendFIFO : "LGF_FIFO";
      arFifoSendBuffer : Array[1..100] of "UDT_TelegramData_Send";
   END_VAR

   VAR 
      fbTeleHandler : "FB_TCP_LCOM_TeleHandler";
      xReset : Bool;
      xSendTele : Bool;
      stTelegramToSend : "UDT_TelegramData_Send";
      nStateReceive : Int;
      nStateSend : Int;
      xRetVal : Bool;
      stNewTelegram { S7_SetPoint := 'False'} : "UDT_TelegramData";
      stTeleGOHDRespM : "UDT_Telegram_GOHDRespM";
      rTrigConnectionActive {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
      xSendHeartbeat : Bool;
      tOnHeartbeat {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      sTelegramKennung : String;
   END_VAR

   VAR_TEMP 
      EmptyData : "UDatenSend" := ([10('$00')], (), (), (), (), (), [()], [()]);
      stLogTelegram : "UDT_TelegramData_Send";
      empty : "UDT_TelegramData_Send";
      xSuccess : Bool;
      i : Int;
   END_VAR

   VAR CONSTANT 
      c0000_WaitingReceive : Int := 0;
      c0100_GetNewTelegram : Int := 100;
      c0200_AnalyzeTelegram : Int := 200;
      c0999_ErrorReceive : Int := 999;
      c0000_WaitingSend : Int := 0;
      c0100_PrepareSendTelegram : Int := 100;
      c0200_ClearSend : Int := 200;
      c0999_ErrorSend : Int := 999;
   END_VAR


BEGIN
	REGION Info
	    // Kommunikation zu Fortna WES Device Hub
	    // Telegramme:
	    // Senden:
	    // CTMP      (Container Mapping)  | Von Schönenberger -> Fortna WES/DeviceHub
	    // UNLDResp  (Unload Response)    | Von Schönenberger -> Fortna WES/DeviceHub
	    
	    // Empfangen:
	    // WSAD (Workstation Activation)  | Fortna WES/DeviceHub -> Schönenberger
	    // INDX (Workstation Index)       | Fortna WES/DeviceHub -> Schönenberger
	    // ENCT (End Container)           | Fortna WES/DeviceHub -> Schönenberger
	    
	    //Specification v2.4
	END_REGION
	
	// DeviceHub
	// IP: 172.22.160.21
	// Port: 30636
	
	REGION Call TeleHandler
	    // hardware identifier of the local interface, PLC tags--Default tag table--System constant default value: PROFINET_interface_1 = 64
	    #fbTeleHandler.stInTcpTypeConfig.connection.interfaceID := "Local~PN-Interface_Customer";
	    //unique connection identifier (W#16#0001...W#16#0FFF)
	    #fbTeleHandler.stInTcpTypeConfig.connection.connectionID := 60;
	    //used protocol: 1=TCP protocol, 2=LCom protocol
	    #fbTeleHandler.stInTcpTypeConfig.connection.comService := "LCOM_TCP_CONNECTION";
	    //FALSE: passive (server), TRUE: active connection establishment (client)
	    #fbTeleHandler.stInTcpTypeConfig.connection.isClient := TRUE;
	    //TRUE: accept requests from all partners, FALSE: only specified partner is allowed in case of server
	    #fbTeleHandler.stInTcpTypeConfig.connection.acceptUnknownPartner := TRUE;
	    //local port number (possible values: 1...49151; caution: some may be reserved for internal use; recommended values: 2000...5000)
	    #fbTeleHandler.stInTcpTypeConfig.connection.localPort := 30636;
	    //partner port number, use only in case of client or specified partner (possible values: 1...49151; recommended values: 2000...5000)
	    #fbTeleHandler.stInTcpTypeConfig.connection.partnerPort := 30636;
	    
	    //partner IP address xxx.xxx.xxx.xxx = [1].[2].[3].[4], use only in case of client or specified partner
	    #fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[1] := 172;
	    #fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[2] := 22;
	    #fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[3] := 160;
	    #fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[4] := 21;
	    
	    #fbTeleHandler.stInTcpTypeConfig.connection.connectType := "LCOM_CON_TYPE_IPV4";
	    
	    // Telegramdefinition
	    #fbTeleHandler.stInTelegramDefinition.sStartMarker := '$02';
	    #fbTeleHandler.stInTelegramDefinition.sEndMarker := '$03';
	    #fbTeleHandler.stInTelegramDefinition.sStartMarkerSend := '$02';
	    #fbTeleHandler.stInTelegramDefinition.sEndMarkerSend := '$03';
	    #fbTeleHandler.stInTelegramDefinition.bSeparatorByte := '$1D'; //',';
	    #fbTeleHandler.stInTelegramDefinition.nLength := 0;
	    #fbTeleHandler.stInTelegramDefinition.nTelegramIDDataField := 0;
	    #fbTeleHandler.stInTelegramDefinition.xTelegramIDIsNumber := FALSE;
	    
	    #fbTeleHandler(xInReset := #xReset,
	                   xInOutSendTele := #xSendTele,
	                   stInTelegramSend := #stTelegramToSend,
	                   fbInOutSendFIFO := #fbInOutSendFIFO,
	                   arFifoSendBuffer := #arFifoSendBuffer);
	END_REGION
	
	REGION Reset SequenceNumber
	    #rTrigConnectionActive(CLK := #fbTeleHandler.xOutConnectionActive);
	    
	    IF #rTrigConnectionActive.Q THEN
	        "DB_CommunicationParameters".nSeqNumWESDeviceHub := 0;
	    END_IF;
	END_REGION
	
	REGION Send Heartbeat
	    IF #fbTeleHandler.xOutConnectionActive THEN
	        #xSendHeartbeat := TRUE;
	        
	        IF #tOnHeartbeat.Q THEN
	            #xSendHeartbeat := FALSE;
	            "FC_Send_HB"(sInPLCID := "cPLCID",
	                         nInPartnerType:= 2,
	                         FIFO := "DB_LGF_FIFO_CommWES",
	                         BUFFER := "DB_Buffer_CommunicationWES".BUFFER,
	                         nInOutSequenceNumber := "DB_CommunicationParameters".nSeqNumWESDeviceHub);
	        END_IF;
	    ELSE
	        #xSendHeartbeat := FALSE;
	    END_IF;
	    
	    #tOnHeartbeat(IN := #xSendHeartbeat,
	                  PT := t#15s);
	END_REGION
	
	REGION Sendefach verwalten / Telegramme verschicken
	    CASE #nStateSend OF
	        #c0000_WaitingSend:
	            REGION c0000: Waiting
	                IF "SS_Port1_SendDaten".Port1.TelegrammKennung[1] <> '$00' THEN
	                    #nStateSend := #c0100_PrepareSendTelegram;
	                END_IF;
	            END_REGION
	            
	        #c0100_PrepareSendTelegram:
	            REGION c0100: Prepare Send Telegram
	                Chars_TO_Strg(Chars := "SS_Port1_SendDaten".Port1.TelegrammKennung,
	                              pChars := 0,
	                              Cnt := 0,
	                              Strg => #sTelegramKennung);
	                
	                IF #sTelegramKennung = 'CTMP' THEN
	                    "FC_Send_CTMP"(sInPLCID := "cPLCID",
	                                   nInBarcode := "SS_Port1_SendDaten".Port1.TrolleyID,
	                                   sInStationName := "FC_NumberToWorkstation"("SS_Port1_SendDaten".Port1.StationsNr),
	                                   sInLaneNumber := DELETE(IN := DINT_TO_STRING("SS_Port1_SendDaten".Port1.Puffer_LinienNr), L := 1, P := 1),
	                                   FIFO := "DB_LGF_FIFO_CommWES",
	                                   BUFFER := "DB_Buffer_CommunicationWES".BUFFER,
	                                   nInOutSequenceNumber := "DB_CommunicationParameters".nSeqNumWESDeviceHub);
	                    
	                    #nStateSend := #c0200_ClearSend;
	                    
	                ELSIF #sTelegramKennung = 'UNLDResp' THEN
	                    "FC_Send_UNLDResp"(sInPLCID := "cPLCID",
	                                       nInBarcode := "SS_Port1_SendDaten".Port1.TrolleyID,
	                                       sInStationName := "FC_NumberToWorkstation"("SS_Port1_SendDaten".Port1.StationsNr),
	                                       sInLaneNumber := DELETE(IN := DINT_TO_STRING("SS_Port1_SendDaten".Port1.Puffer_LinienNr), L := 1, P := 1),
	                                       sInReasonCode := DELETE(IN := DINT_TO_STRING("SS_Port1_SendDaten".Port1.Status_1), L := 1, P := 1),
	                                       FIFO := "DB_LGF_FIFO_CommWES",
	                                       BUFFER := "DB_Buffer_CommunicationWES".BUFFER,
	                                       nInOutSequenceNumber := "DB_CommunicationParameters".nSeqNumWESDeviceHub);
	                    
	                    #nStateSend := #c0200_ClearSend;
	                ELSE
	                    #nStateSend := #c0999_ErrorSend; // ERROR
	                END_IF;
	            END_REGION
	
	            
	        #c0200_ClearSend: // Sendefach leeren
	            REGION c0200: Clear send
	                #EmptyData.TelegrammKennung[1] := '$00';
	                #EmptyData.TelegrammKennung[2] := '$00';
	                #EmptyData.TelegrammKennung[3] := '$00';
	                #EmptyData.TelegrammKennung[4] := '$00';
	                #EmptyData.TelegrammKennung[5] := '$00';
	                #EmptyData.TelegrammKennung[6] := '$00';
	                #EmptyData.TelegrammKennung[7] := '$00';
	                #EmptyData.TelegrammKennung[8] := '$00';
	                #EmptyData.TelegrammKennung[9] := '$00';
	                #EmptyData.TelegrammKennung[10] := '$00';
	                "SS_Port1_SendDaten".Port1 := #EmptyData;
	                
	                #nStateSend := #c0000_WaitingSend;
	            END_REGION
	
	            
	        #c0999_ErrorSend: //ERROR TODO
	            REGION c0999: Error Send
	                #EmptyData.TelegrammKennung[1] := '$00';
	                #EmptyData.TelegrammKennung[2] := '$00';
	                #EmptyData.TelegrammKennung[3] := '$00';
	                #EmptyData.TelegrammKennung[4] := '$00';
	                #EmptyData.TelegrammKennung[5] := '$00';
	                #EmptyData.TelegrammKennung[6] := '$00';
	                #EmptyData.TelegrammKennung[7] := '$00';
	                #EmptyData.TelegrammKennung[8] := '$00';
	                #EmptyData.TelegrammKennung[9] := '$00';
	                #EmptyData.TelegrammKennung[10] := '$00';
	                
	                "SS_Port1_SendDaten".Port1 := #EmptyData;
	                
	                #nStateSend := #c0000_WaitingSend;
	            END_REGION
	
	            
	    END_CASE;
	    
	END_REGION
	
	REGION Empfangsfach verwalten / Telegramme erhalten
	    CASE #nStateReceive OF
	        #c0000_WaitingReceive:
	            REGION c0000: Waiting
	                //New Telegram
	                IF #fbTeleHandler.xOutTelegramAvailable THEN
	                    #fbTeleHandler.xInGetFirst := TRUE;
	                    #nStateReceive := #c0100_GetNewTelegram;
	                END_IF;
	            END_REGION
	
	        #c0100_GetNewTelegram:
	            REGION c0100: Get new telegram
	                #stNewTelegram := #fbTeleHandler.stOutNewTelegram;
	                #fbTeleHandler.xInGetFirst := FALSE;
	                
	                IF "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[1] = '$00' THEN // Warte bis Empfangsbereich frei ist
	                    #stLogTelegram.nDataCount := #stNewTelegram.nDataCount;
	                    
	                    FOR #i := 1 TO 12 DO
	                        #stLogTelegram.arDataString[#i] := #stNewTelegram.arDataString[#i];
	                    END_FOR;
	                    
	                    REGION LOG
	                        IF "FB_CommunicationLogger_DB".fbTeleHandler.xOutConnectionActive THEN
	                            "FC_FIFO_Append"(DataIn := #stLogTelegram,
	                                             InitialItem := #empty,
	                                             MaxNo := 100,
	                                             Success => #xSuccess,
	                                             fifoBuffer := "DB_LGF_FIFO_CommLogger",
	                                             buffer := "DB_Buffer_CommunicationLogger".BUFFER);
	                        END_IF;
	                        
	                    END_REGION
	                    
	                    
	                    #nStateReceive := #c0200_AnalyzeTelegram;
	                END_IF;
	            END_REGION
	           
	        #c0200_AnalyzeTelegram:
	            REGION c0200: Analyze telegram
	                //Send ACK for these message types
	                IF (#stNewTelegram.arDataString[3] = 'WSAD') OR (#stNewTelegram.arDataString[3] = 'INDX') OR (#stNewTelegram.arDataString[3] = 'ENCT') THEN
	                    "FC_Send_ACK"(sInSeqNum := #stNewTelegram.arDataString[2], //Sequenznummer zurückgeben
	                                  FIFO := "DB_LGF_FIFO_CommWES",
	                                  BUFFER := "DB_Buffer_CommunicationWES".BUFFER);
	                END_IF;
	                
	                //Evaluate message depending on type
	                IF #stNewTelegram.arDataString[3] = 'WSAD' THEN // Workstation Activation
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[1] := 'W';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[2] := 'S';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[3] := 'A';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[4] := 'D';
	                    
	                    IF #stNewTelegram.arDataString[4] = 'GOH-PWS-101' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 101;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-102' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 102;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-103' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 103;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-104' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 104;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-105' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 105;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-106' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 106;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-107' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 107;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-108' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 108;
	                    END_IF;
	                    
	                    //Workstation Activation status (0 = inactive, 1 = active)
	                    IF (STRING_TO_DINT(#stNewTelegram.arDataString[5])) = 0 THEN
	                        "SS_Port1_EmpfangsDaten".Port1.Aktion_2[0] := FALSE;
	                    ELSIF (STRING_TO_DINT(#stNewTelegram.arDataString[5])) = 1 THEN
	                        "SS_Port1_EmpfangsDaten".Port1.Aktion_2[0] := TRUE;
	                    END_IF;
	                    
	                    //Workstation Function code (11 = Help Beacon On, 12 = Help Beacon off, other codes only info)
	                    "SS_Port1_EmpfangsDaten".Port1.Aktion_1 := STRING_TO_INT(#stNewTelegram.arDataString[6]);
	                    
	                ELSIF #stNewTelegram.arDataString[3] = 'INDX' THEN // INDEX
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[1] := 'I';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[2] := 'N';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[3] := 'D';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[4] := 'X';
	                    
	                    IF #stNewTelegram.arDataString[4] = 'GOH-PWS-101' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 101;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-102' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 102;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-103' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 103;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-104' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 104;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-105' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 105;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-106' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 106;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-107' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 107;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-108' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 108;
	                    END_IF;
	                    
	                    "SS_Port1_EmpfangsDaten".Port1.Puffer_LinienNr := STRING_TO_INT(#stNewTelegram.arDataString[7]);
	                    
	                ELSIF #stNewTelegram.arDataString[3] = 'ECNT' THEN //End Container
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[1] := 'E';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[2] := 'C';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[3] := 'N';
	                    "SS_Port1_EmpfangsDaten".Port1.TelegrammKennung[4] := 'T';
	                    
	                    IF #stNewTelegram.arDataString[4] = 'GOH-PWS-101' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 101;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-102' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 102;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-103' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 103;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-104' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 104;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-105' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 105;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-106' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 106;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-107' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 107;
	                    ELSIF #stNewTelegram.arDataString[4] = 'GOH-PWS-108' THEN
	                        "SS_Port1_EmpfangsDaten".Port1.StationsNr := 108;
	                    END_IF;
	                    
	                    "SS_Port1_EmpfangsDaten".Port1.TrolleyID := STRING_TO_DINT(#stNewTelegram.arDataString[6]);
	                    
	                    "SS_Port1_EmpfangsDaten".Port1.Puffer_LinienNr := STRING_TO_INT(#stNewTelegram.arDataString[7]);
	                ELSE
	                    ;//ERROR
	                END_IF;
	                
	                #nStateReceive := 0;
	            END_REGION
	
	    END_CASE;
	END_REGION
END_FUNCTION_BLOCK

