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