FUNCTION "FC_TargetDecision" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      arInStorageLineCalc : Array[1.."cNoLines"] of Int;
      arInStorage : Array[*] of "stStorageLine";
      arInSenStore : Array[*] of "UDT_Sensor";
      nInDestGeneral : Int;
      nInNoOfLines : Int;
      nInStorageThreshold : Int;
      nInMode : USInt;   // 1 = abwechselnd; 2 = gleichmäßig füllen; 3 = nacheinander auffüllen
   END_VAR

   VAR_OUTPUT 
      xOutBypass : Bool;
   END_VAR

   VAR_IN_OUT 
      nInOutTarget : USInt;
      xInOutFree : Bool;
      stInOutConveyorStations : "stConveyorStations";
      stInOutCarrier : "stCarrier";
      stInOutJamBypass : "stJamData";
   END_VAR

   VAR_TEMP 
      nIdx : Int;   // Schleifenindex
      nIdx2 : Int;
      nIdxMem : Int;   // gemerkter Wert vom Schleifenindex
      nTarget : Int;
      nTargetMaxOnTheWay : Int;
      nSum : Int;   // Summe der Lagerbestände
      nAverage : Int;   // Durchschnitt der Lagerfüllstände
      nThrTop : Int;   // Obere Schwelle für Durchschnitt
      nThrLow : Int;   // Untere Schwelle für Durchschnitt
      nNoOfSameTarget1 : Int;
   END_VAR


BEGIN
	(*
	Funktion weist ab AddHanger-Sensor das Ziel des aktuellen Carriers zu, prüft aber vorher ob Ziel frei ist. Falls nicht, wird nächstes freies Ziel gesucht.
	Falls alles voll ist, wird Stop-Befehl für Band ausgegeben
	*)
	
	//Beispiel für Materialnummer Stand IBN:
	//95001412-95001419 -> Sitz
	//95001420-9500142x -> Lehne
	//#nInProductNr    DInt
	
	//************TEST FÜR DURCHSCHNITT**************
	// FOR #nIdx := 1 TO "cNoLines" DO //Summe der Lagerlinien-Füllstände merken
	//     #nSum += #arInStorage[#nIdx].stData.Status.nCount;
	// END_FOR;
	// IF #nSum > 0 AND #nSum <= ("cNoLines" * "cNoPositions") THEN
	//     //Durchschnitt berechnen sowie obere/untere Schwelle, aber nur wenn Durchschnitt oberhalb eines festgelegten Wertes liegt
	//     #nAverage := #nSum / "cNoLines";
	//     #nThrTop := #nAverage * "cAverageThrTop";
	//     #nThrLow := #nAverage * "cAverageThrLow";
	// END_IF;
	// "cAverageMin"...
	
	REGION Normierung für diesen FC
	    IF #nInOutTarget < 11 OR #nInOutTarget > #nInNoOfLines + 10 THEN // > nInNoLines hinzugefügt, für unterschiedlich große Lager-TRO 
	        #nTarget := 1; //Wenn Ziel außerhalb des gültigen Bereichs, fange mit 1 an
	    ELSE
	        #nTarget := #nInOutTarget - 10; //nTarget benutzt Zahlen 1-21 statt von außen ab 11 beginnend zur einfacheren Handhabung
	    END_IF;
	END_REGION
	
	CASE #nInMode OF
	    1:
	        REGION ABWECHSELND - Ziele prüfen falls aktuelles belegt
	            IF #arInSenStore[#nTarget].xJam OR #arInStorage[#nTarget].stData.Status.nCount >= #nInStorageThreshold OR #arInStorageLineCalc[#nTarget] >= #nInStorageThreshold THEN //Wenn Ziel voll, dann....
	                REGION Ziel korrigieren
	                    IF #nTarget = #nInNoOfLines THEN //Wenn Ziel die letzte Linie ist, suche von 1. bis vorletzte Linie
	                        REGION Suche von 1. bis vorletzte
	                            FOR #nIdx := 1 TO (#nInNoOfLines - 1) DO
	                                IF NOT #arInSenStore[#nIdx].xJam AND #arInStorage[#nIdx].stData.Status.nCount < #nInStorageThreshold AND #arInStorageLineCalc[#nIdx] < #nInStorageThreshold THEN
	                                    #nInOutTarget := INT_TO_USINT(#nIdx + 10); //nächstes freies Ziel. Fertig
	                                    RETURN;
	                                END_IF;
	                            END_FOR;
	                        END_REGION
	                        #xInOutFree := FALSE; //nichts freies gefunden -> Band stoppen
	                        RETURN;
	                    ELSE
	                        REGION Suche bis zum Schluss...
	                            FOR #nIdx := (#nTarget + 1) TO #nInNoOfLines DO //Suche AB der aktuellen Linie...
	                                IF NOT #arInSenStore[#nIdx].xJam AND #arInStorage[#nIdx].stData.Status.nCount < #nInStorageThreshold AND #arInStorageLineCalc[#nIdx] < #nInStorageThreshold THEN
	                                    #nInOutTarget := INT_TO_USINT(#nIdx + 10); //nächstes freies Ziel. Fertig
	                                    RETURN;
	                                END_IF;
	                            END_FOR;
	                        END_REGION
	                        REGION ...und ggf. von 1. bis zum aktuellen
	                            FOR #nIdx := 1 TO (#nTarget - 1) DO //...falls nichst gefunden, suche BIS zur aktuellen Linie
	                                IF NOT #arInSenStore[#nIdx].xJam AND #arInStorage[#nIdx].stData.Status.nCount < #nInStorageThreshold AND #arInStorageLineCalc[#nIdx] < #nInStorageThreshold THEN
	                                    #nInOutTarget := INT_TO_USINT(#nIdx + 10); //nächstes freies Ziel. Fertig
	                                    RETURN;
	                                END_IF;
	                            END_FOR;
	                        END_REGION
	                        #xInOutFree := FALSE;  //nichts freies gefunden -> Band stoppen
	                        RETURN;
	                    END_IF;
	                END_REGION
	            ELSE
	                REGION Ziel i.O.
	                    #nInOutTarget := INT_TO_USINT(#nTarget + 10); //keine Korrektur nötig und übernehme Ziel einfach
	                END_REGION
	            END_IF;
	        END_REGION
	    2:
	        REGION GLEICHMÄßIG BEFÜLLEN - Ziele prüfen
	            FOR #nIdx := 1 TO #nInNoOfLines DO
	                IF #nIdx = 1 THEN
	                    IF NOT #arInSenStore[#nIdx].xJam AND #arInStorage[#nIdx].stData.Status.nCount < #nInStorageThreshold THEN
	                        #nIdxMem := #nIdx; //Wenn beim 1. Schleifendurchlauf in L1 Platz ist, merke die 1
	                    END_IF;
	                ELSIF #nIdx > 1 THEN
	                    // ab den folgenden Schleifendurchläufen, merke die Linie, welche weniger drin hat als die zuvor gemerkte (falls Platz ist)
	                    IF NOT #arInSenStore[#nIdx].xJam AND #arInStorage[#nIdx].stData.Status.nCount < #nInStorageThreshold AND
	                        #arInStorage[#nIdx].stData.Status.nCount < #arInStorage[#nIdxMem].stData.Status.nCount THEN
	                        #nIdxMem := #nIdx;
	                    END_IF;
	                END_IF;
	            END_FOR;
	            // nach der Schleife: Wenn nichts gemerkt wurde, heißt es dass alle Linien voll sind -> Band stoppen
	            IF #nIdxMem = 0 THEN
	                #xInOutFree := FALSE; //nichts freies gefunden -> Band stoppen
	            ELSE
	                #nInOutTarget := INT_TO_USINT(#nIdxMem + 10); //nächstes freies Ziel. Fertig
	            END_IF;
	        END_REGION
	    3:
	        REGION JEDE LINIE NACHEINANDER BEFÜLLEN - Ziele prüfen
	            
	            //PRÜFEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	            REGION Richtungswechsel ermitteln TO DO ÜBERARBEITEN
	                (* IF #stInOutConveyorStations.stData.nTrolleysOnline > 0 THEN //wenn etwas unterwegs ist, ermittle ob der aktuelle Carrier ein kleineres Ziel hat als diese
	                    FOR #nIdx2 := 1 TO #stInOutConveyorStations.stData.nTrolleysOnline DO
	                        IF #nIdx2 = 1 THEN
	                            #nTargetMaxOnTheWay := #stInOutConveyorStations.arTrolleysOnline[#nIdx2].stPinValues.nTarget;
	                        ELSE
	                            IF #stInOutConveyorStations.arTrolleysOnline[#nIdx2].stPinValues.nTarget > #nTargetMaxOnTheWay THEN
	                                #nTargetMaxOnTheWay := #stInOutConveyorStations.arTrolleysOnline[#nIdx2].stPinValues.nTarget;
	                            END_IF;
	                        END_IF;
	                    END_FOR;
	                    IF (#stInOutCarrier.nDestination - #nInDestGeneral) > (#nTargetMaxOnTheWay - 10) THEN //Wenn aktueller Carrier ein höheres Ziel hat als die unterwegs, mach nichts
	                        #xInOutFree := FALSE;
	                        RETURN;
	                    END_IF;
	                END_IF; *)
	                //Bearbeitung kann weiter gehen weil aktueller Carrier niedrigeres Ziel hat oder nichts
	            END_REGION
	            (*
	            Für Änderung dass man abwartet dass CarrierOnline leer ist vor Richtungswechsel:
	            -nicht TargetMaxOnTheWay ermitteln mit Schleife sondern einfach Ziel des ersten OnTheWay abfragen
	            -NoOfSameTarget muss nicht mit Schleife ermittelt werden sondern einfach von TrolleysOnline der nCount abgefragt werden
	            -.....            
	            *)
	            REGION Linie ermitteln bzw. prüfen
	               //IF #stInOutCarrier.nDestination = 0 OR (#stInOutCarrier.nDestination >= #nInDestGeneral AND (#stInOutCarrier.nDestination <= #nInDestGeneral + #nInNoOfLines) AND NOT #stInOutCarrier.bStatus.%X5) THEN
	                    IF #stInOutCarrier.nDestination = 0 OR (#stInOutCarrier.nDestination = #nInDestGeneral) THEN
	                    (*normal Ziel ermitteln für neue Carrier*)
	                    FOR #nIdx := 1 TO #nInNoOfLines DO
	                        IF #stInOutConveyorStations.stData.nTrolleysOnline > 0 THEN //zähle gleiche Ziele zusammen erst wenn überhaupt etwas unterwegs ist
	                            FOR #nIdx2 := 1 TO #stInOutConveyorStations.stData.nTrolleysOnline DO
	                                IF #stInOutConveyorStations.arTrolleysOnline[#nIdx2].stPinValues.nTarget = #nIdx + 10 THEN
	                                    #nNoOfSameTarget1 += 1; //addiere die gleichen vergebenen Ziele auf dem Pinband
	                                END_IF;
	                            END_FOR;
	                        END_IF;
	                        
	                        IF NOT #arInStorage[#nIdx].stData.Status.bByte.%X2 AND // Einfahrt gesperrt (HMI-Schnittstelle, manueller Eingriff)
	                            NOT #arInSenStore[#nIdx].xJam AND //Phys. Stau
	                            NOT #arInStorage[#nIdx].stData.Status.bByte.%X5 AND //reservierte Linie nicht anfahren
	                            (#arInStorage[#nIdx].stData.Status.nCount + #nNoOfSameTarget1) < #nInStorageThreshold THEN
	                            #nInOutTarget := INT_TO_USINT(#nIdx + 10); //nächstes freies Ziel. Fertig
	                            #xInOutFree := TRUE;
	                            RETURN;
	                        END_IF;
	                        #nNoOfSameTarget1 := 0; // Nullen
	                        IF #nIdx = #nInNoOfLines THEN
	                            #xInOutFree := FALSE; //nichts freies gefunden -> Band stoppen        
	                        END_IF;
	                    END_FOR;
	                    // ELSIF #stInOutCarrier.nDestination > #nInDestGeneral AND #stInOutCarrier.nDestination <= #nInDestGeneral + 21 THEN //zwischen 21101 und 21121
	             //   ELSIF (#stInOutCarrier.nDestination >= #nInDestGeneral AND #stInOutCarrier.nDestination <= #nInDestGeneral + #nInNoOfLines AND #stInOutCarrier.bStatus.%X5) THEN
	                ELSIF ((#stInOutCarrier.nDestination > #nInDestGeneral) AND (#stInOutCarrier.nDestination <= (#nInDestGeneral + #nInNoOfLines))) THEN
	                    (*hat schon Ziel in diesem Block, daher prüfen ob diese Linie frei*)
	                    
	                    IF #stInOutConveyorStations.stData.nTrolleysOnline > 0 THEN //zähle gleiche Ziele zusammen erst wenn überhaupt etwas unterwegs ist
	                        FOR #nIdx2 := 1 TO #stInOutConveyorStations.stData.nTrolleysOnline DO
	                            IF #stInOutConveyorStations.arTrolleysOnline[#nIdx2].stPinValues.nTarget = (#stInOutCarrier.nDestination - #nInDestGeneral) + 10 THEN
	                                #nNoOfSameTarget1 += 1; //addiere die gleichen vergebenen Ziele auf dem Pinband
	                            END_IF;
	                        END_FOR;
	                    END_IF;
	                    IF NOT #arInSenStore[#stInOutCarrier.nDestination - #nInDestGeneral].xJam AND //phys. Stau
	                        NOT #arInStorage[#stInOutCarrier.nDestination - #nInDestGeneral].stData.Status.bByte.%X2 AND  //Einfahrt gesperrt (HMI, manuell)
	                        (#arInStorage[#stInOutCarrier.nDestination - #nInDestGeneral].stData.Status.nCount + #nNoOfSameTarget1) < #nInStorageThreshold THEN
	                        #nInOutTarget := INT_TO_USINT((#stInOutCarrier.nDestination - #nInDestGeneral) + 10); //nächstes freies Ziel. Fertig
	                        #xInOutFree := TRUE;
	                        RETURN;
	                    END_IF;
	                    #xInOutFree := FALSE; //nichts freies gefunden -> Band stoppen     
	                ELSE
	                    (*Bypass*)
	                    #nInOutTarget := INT_TO_USINT(#nInNoOfLines + 10 + 1);
	                    #xOutBypass := TRUE;
	                    IF NOT (#stInOutJamBypass.Status.nCount >= #stInOutJamBypass.Config.nCapacitySet) THEN
	                        #xInOutFree := TRUE;
	                    END_IF;
	                END_IF;
	            END_REGION
	            
	        END_REGION
	    ELSE
	        #xInOutFree := FALSE; //falsche Eingabe -> Band stoppen
	END_CASE;
	
	
END_FUNCTION

