Claude Session mit Empfehlungen zur Standardisierung der Library hinzugefügt. Manuelle Prüfung jedes Bausteins noch nötig

This commit is contained in:
2026-06-29 10:24:45 +02:00
parent d4ce88715e
commit 3bf291fdf8
13 changed files with 1442 additions and 0 deletions
@@ -0,0 +1,25 @@
FUNCTION "FC_Int2String" : String
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
nInNumber : Int;
END_VAR
VAR_TEMP
sTmp : String;
END_VAR
BEGIN
// Funktion wandelt Int zu String. Im Gegensatz zu "Int_To_String" wird das "+" entfernt. Bei negativen Zahlen bleibt das "-" erhalten.
// Autor: Christian Forkl
// 28.07.2021
#sTmp := INT_TO_STRING(#nInNumber);
IF #nInNumber >= 0 THEN
#FC_Int2String := RIGHT(IN := #sTmp, L := (LEN(#sTmp) - 1));
ELSE
#FC_Int2String := #sTmp;
END_IF;
END_FUNCTION
@@ -0,0 +1,36 @@
FUNCTION_BLOCK "FB_FortnaTele_Placeholder"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR
Static_1 : "UDT_Payload_CRCanM";
Static_2 : "UDT_Payload_CReqM";
Static_3 : "UDT_Payload_CRRespM";
Static_4 : "UDT_Payload_CSRespM";
Static_5 : "UDT_Payload_CTMP";
Static_6 : "UDT_Payload_ECNT";
Static_7 : "UDT_Payload_GOHDReqM";
Static_8 : "UDT_Payload_GOHDRespM";
Static_9 : "UDT_Payload_GOHDUM";
Static_10 : "UDT_Payload_INDX";
Static_11 : "UDT_Payload_UNLDResp";
Static_12 : "UDT_Payload_WSAD";
Static_13 : "UDT_Telegram_CRCanM";
Static_14 : "UDT_Telegram_CReqM";
Static_15 : "UDT_Telegram_CRRespM";
Static_16 : "UDT_Telegram_CSRespM";
Static_17 : "UDT_Telegram_CTMP";
Static_18 : "UDT_Telegram_ECNT";
Static_19 : "UDT_Telegram_GOHDReqM";
Static_20 : "UDT_Telegram_GOHDRespM";
Static_21 : "UDT_Telegram_GOHDUM";
Static_22 : "UDT_Telegram_INDX";
Static_23 : "UDT_Telegram_UNLDResp";
Static_24 : "UDT_Telegram_WSAD";
Static_25 : "UDT_Header";
Static_26 : "UDT_Workstation";
END_VAR
BEGIN
END_FUNCTION_BLOCK
@@ -0,0 +1,207 @@
FUNCTION_BLOCK "FB_CommunicationLogger"
{ 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";
nState : Int;
xRetVal : Bool;
stNewTelegram { S7_SetPoint := 'False'} : "UDT_TelegramData";
stTeleGOHDRespM : "UDT_Telegram_GOHDRespM";
arPayloadHelp : Array[0..200] of Byte;
cnt : UInt;
LStream_JsonDeserializer_Instance : "LStream_JsonDeserializer";
typeElement { S7_SetPoint := 'False'} : Array[0..10] of "LStream_typeElement" := [(2, 'DecisionPoint', (), (), ()), (3, 'GIN', (), (), ()), (2, 'Barcode', (), (), ()), (1, 'Actions', (), (), true), ((), (), (), (), ()), ((), (), (), (), ()), ((), (), (), (), ()), ((), (), (), (), ()), ((), (), (), (), ()), ((), (), (), (), ()), ((), (), (), (), ())];
xDone : Bool;
rTrigConnectionActive {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
xSendHeartbeat : Bool;
tOnHeartbeat {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
END_VAR
VAR_TEMP
xStart : Bool;
test : "UDT_TelegramData_Send";
END_VAR
BEGIN
REGION Info
// Kommunikation zu Fortna WCSBL
// Telegramme:
// Senden:
// GOHDReqM (Zielanfrage) | Von Schönenberger -> Fortna WCSBL
// GOHDUM (Update) | Von Schönenberger -> Fortna WCSBL
// Empfangen:
// GOHDRespM (Antwort auf Zielanfrage) | Von Fortna WCSBL -> Schönenberger
END_REGION
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_SchSy"; //"Local"~PN-Interface_Customer";
// WICHTIG: connectionID muss je PLC eindeutig sein!
// UH01=99, UH02=100 (absichtlich verschieden -- pro PLC anpassen)
#fbTeleHandler.stInTcpTypeConfig.connection.connectionID := 99;
//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 := 4711;
//partner port number, use only in case of client or specified partner (possible values: 1...49151; recommended values: 2000...5000)
#fbTeleHandler.stInTcpTypeConfig.connection.partnerPort := 4711;
//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] := 192;
#fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[2] := 168;
#fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[3] := 3;
#fbTeleHandler.stInTcpTypeConfig.connection.partnerIP.ADDR[4] := 245;
#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,
xInSendTele := true,
xInOutSendTele := #xSendTele,
stInTelegramSend := #stTelegramToSend,
fbInOutSendFIFO:= #fbInOutSendFIFO,
arFifoSendBuffer:=#arFifoSendBuffer);
END_REGION
REGION Reset SequenceNumber
#rTrigConnectionActive(CLK:=#fbTeleHandler.xOutConnectionActive);
IF #rTrigConnectionActive.Q THEN
"DB_CommunciationParametersLogger".nSeqNum := 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",
FIFO := "DB_LGF_FIFO_CommLogger",
BUFFER := "DB_Buffer_CommunicationLogger".BUFFER,
nInOutSequenceNumber:="DB_CommunciationParametersLogger".nSeqNum);
END_IF;
ELSE
#xSendHeartbeat := FALSE;
END_IF;
#tOnHeartbeat(IN := #xSendHeartbeat,
PT := t#15s); *)
END_REGION
REGION Empfangen
//Wird nicht benötigt
CASE #nState OF
0: // Waiting
//New Telegram
IF #fbTeleHandler.xOutTelegramAvailable THEN
#fbTeleHandler.xInGetFirst := TRUE;
#nState := 100;
END_IF;
100: // Get new Telegram
#stNewTelegram := #fbTeleHandler.stOutNewTelegram;
#fbTeleHandler.xInGetFirst := FALSE;
#nState := 200;
200: //Analyze Telegram
#stTeleGOHDRespM.stHeader.sPlcId := #stNewTelegram.arDataString[1];
#stTeleGOHDRespM.stHeader.sSeqNum := #stNewTelegram.arDataString[2];
#stTeleGOHDRespM.stHeader.sMessageType := #stNewTelegram.arDataString[3];
IF #stTeleGOHDRespM.stHeader.sMessageType = 'GOHDRespM' THEN
Strg_TO_Chars(Strg := #stNewTelegram.arDataString[4],
pChars := 0,
Cnt => #cnt,
Chars := #arPayloadHelp);
#typeElement[0].type := 2;
#typeElement[0].key := 'DecisionPoint';
#typeElement[1].type := 3;
#typeElement[1].key := 'GIN';
#typeElement[2].type := 2;
#typeElement[2].key := 'Barcode';
#typeElement[3].type := 1;
#typeElement[3].key := 'Actions';
#xStart := TRUE;
IF #xDone THEN
#xStart := FALSE;
#stTeleGOHDRespM.stPayload.sDecisionPoint := #typeElement[0].value;
STRG_VAL(IN := #typeElement[1].value,
FORMAT := 0,
P := 1,
OUT => #stTeleGOHDRespM.stPayload.nGin);
STRG_VAL(IN := #typeElement[2].value,
FORMAT := 0,
P := 1,
OUT => #stTeleGOHDRespM.stPayload.nBarcode);
#stTeleGOHDRespM.stPayload.arActions[0] := #typeElement[4].value;
#stTeleGOHDRespM.stPayload.arActions[1] := #typeElement[5].value;
#stTeleGOHDRespM.stPayload.arActions[2] := #typeElement[6].value;
#stTeleGOHDRespM.stPayload.arActions[3] := #typeElement[7].value;
#stTeleGOHDRespM.stPayload.arActions[4] := #typeElement[8].value;
// WICHTIG: "DB_JamArea0102" ist UH01-spezifisch!
// Beim Einsatz in anderen PLCs diesen Block anpassen oder parametrisieren.
IF #stTeleGOHDRespM.stPayload.sDecisionPoint = 'GOH.Rec.01' THEN
IF "DB_JamArea0102".stJam0102_4.arCarrier[1].nBarcode = #stTeleGOHDRespM.stPayload.nBarcode THEN
IF #stTeleGOHDRespM.stPayload.arActions[0] = '1' THEN
"DB_JamArea0102".stJam0102_4.arCarrier[1].nDestination := "cDestInboundOut";
ELSIF #stTeleGOHDRespM.stPayload.arActions[0] = '-1' THEN
"DB_JamArea0102".stJam0102_4.arCarrier[1].nDestination := "cDestClearing";
END_IF;
END_IF;
END_IF;
#xDone := FALSE;
#nState := 0;
END_IF;
ELSE
#nState := 0;
END_IF;
END_CASE;
END_REGION
REGION LStream
#LStream_JsonDeserializer_Instance(execute := #xStart,
tree := #typeElement,
raw := #arPayloadHelp);
IF #LStream_JsonDeserializer_Instance.done THEN
#xDone := TRUE;
END_IF;
END_REGION
END_FUNCTION_BLOCK
@@ -0,0 +1,200 @@
FUNCTION_BLOCK "FB_Visu_Jam"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_IN_OUT
arInOutJam : Array[*] of "stCarrier"; // Jam Daten
stInOutHMIJam : "UDT_Visu_Jam_List";
stInOutHMIData : "UDT_HMI_JamData"; // Jam Daten HMI Schnittstelle
END_VAR
VAR
xReInit : Bool;
nUpperBound : Int;
END_VAR
VAR_TEMP
nIdx : Int;
nIdxMerker : Int;
END_VAR
BEGIN
// Hier werden die Carrierlisten für die Visu erstellt
// Auf Knopfdruck am HMI werden Aktionen durchgeführt, um die Liste zu scrollen
// Aktuell werden immer 10 Carrier gleichzeitig angezeigt
#stInOutHMIJam.#upperBound := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJam, DIM := 1));
#nUpperBound := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJam, DIM := 1));
REGION Jam Daten Liste
// Beim ersten Aufruf nach reinit ausführen, damit Daten drinstehen
IF NOT #xReInit THEN
#stInOutHMIJam.nIndex := 1;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#xReInit := TRUE;
END_IF;
// Wenn nichts an der Liste gemacht wird, Daten nur aktualisieren
IF NOT #stInOutHMIJam.#DecOne AND NOT #stInOutHMIJam.#DecFive AND NOT #stInOutHMIJam.#IncOne AND NOT #stInOutHMIJam.#IncFive AND NOT #stInOutHMIJam.#ScrollEnd AND NOT #stInOutHMIJam.#ScrollStart THEN
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
END_IF;
// Liste um eins nach oben bewegen
IF #stInOutHMIJam.#DecOne THEN
#stInOutHMIJam.#nIndex -= 1;
IF #stInOutHMIJam.#nIndex < 1 THEN
#stInOutHMIJam.#nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#DecOne := FALSE;
END_IF;
// Liste um fünf nach oben bewegen
IF #stInOutHMIJam.#DecFive THEN
#stInOutHMIJam.#nIndex -= 5;
IF #stInOutHMIJam.#nIndex < 1 THEN
#stInOutHMIJam.#nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#DecFive := FALSE;
END_IF;
// Liste um eins nach unten bewegen
IF #stInOutHMIJam.#IncOne THEN
#stInOutHMIJam.#nIndex += 1;
IF #stInOutHMIJam.#nIndex > #stInOutHMIJam.#upperBound - 9 THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex <= 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#IncOne := FALSE;
END_IF;
// Liste um fünf nach unten bewegen
IF #stInOutHMIJam.#IncFive THEN
#stInOutHMIJam.#nIndex += 5;
IF #stInOutHMIJam.#nIndex > #stInOutHMIJam.#upperBound - 9 THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex < 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#IncFive := FALSE;
END_IF;
// Zum Listenanfang springen
IF #stInOutHMIJam.#ScrollStart THEN
#stInOutHMIJam.#nIndex := 1;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#ScrollStart := FALSE;
END_IF;
// Zum Listenende springen
IF #stInOutHMIJam.#ScrollEnd THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex < 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#ScrollEnd := FALSE;
END_IF;
END_REGION
REGION FC_CAR_MOVE
// "FC_CARR_MOVE"(nInCommand := #stHMIJam.stCarMove.nCommand,
// nInJamNr := #nInJamNr,//braucht nicht von HMI zu kommen - #stHMIJam.stCarMove.nJamNr,
// nInTarget := #stHMIJam.stCarMove.nTarget,
// xInStart := #stHMIJam.stCarMove.xStart,
// xOutError => #stHMIJam.stCarMove.xError);
// #stInOutHMIJam.stCarMove.xStart := FALSE;
"FC_CarrierManipulator"(stInOutHMIJamData := #stInOutHMIData,
stInOutHMIJamList := #stInOutHMIJam,
arInOutJam := #arInOutJam,
arInOutHyperJam := "DB_HyperJam".stHyperJam.arCarrier);
#stInOutHMIJam.stCarMove.xStart := FALSE;
END_REGION
REGION Manuelle Zielvergabe für Jam
IF #stInOutHMIData.stStatus.bByte.%X7 THEN //%X7: Manuelle Zielvergabe aktiviert
#arInOutJam[1].nDestination := #stInOutHMIData.stJamConfig.nManualDest; //Sorgt dafür, dass Carrier ein festes Ziel bekommen
END_IF;
END_REGION
END_FUNCTION_BLOCK
@@ -0,0 +1,197 @@
FUNCTION_BLOCK "FB_Visu_JamStorage"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
nInJamCount : UInt;
END_VAR
VAR_IN_OUT
arInOutJam : Array[*] of "stCarrier"; // Jam Daten
stInOutHMIJam : "UDT_Visu_Jam_List";
stInOutHMIData : "UDT_HMI_StorageData"; // Jam Daten HMI Schnittstelle
END_VAR
VAR
xReInit : Bool;
nUpperBound : Int;
END_VAR
VAR_TEMP
nIdx : Int;
nIdxMerker : Int;
END_VAR
BEGIN
// Hier werden die Carrierlisten für die Visu erstellt
// Auf Knopfdruck am HMI werden Aktionen durchgeführt, um die Liste zu scrollen
// Aktuell werden immer 10 Carrier gleichzeitig angezeigt
#stInOutHMIJam.#upperBound := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJam, DIM := 1));
#nUpperBound := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJam, DIM := 1));
REGION Jam Daten Liste
// Beim ersten Aufruf nach reinit ausführen, damit Daten drinstehen
IF NOT #xReInit THEN
#stInOutHMIJam.nIndex := 1;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#xReInit := TRUE;
END_IF;
// Wenn nichts an der Liste gemacht wird, Daten nur aktualisieren
IF NOT #stInOutHMIJam.#DecOne AND NOT #stInOutHMIJam.#DecFive AND NOT #stInOutHMIJam.#IncOne AND NOT #stInOutHMIJam.#IncFive AND NOT #stInOutHMIJam.#ScrollEnd AND NOT #stInOutHMIJam.#ScrollStart THEN
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
END_IF;
// Liste um eins nach oben bewegen
IF #stInOutHMIJam.#DecOne THEN
#stInOutHMIJam.#nIndex -= 1;
IF #stInOutHMIJam.#nIndex < 1 THEN
#stInOutHMIJam.#nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#DecOne := FALSE;
END_IF;
// Liste um fünf nach oben bewegen
IF #stInOutHMIJam.#DecFive THEN
#stInOutHMIJam.#nIndex -= 5;
IF #stInOutHMIJam.#nIndex < 1 THEN
#stInOutHMIJam.#nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#DecFive := FALSE;
END_IF;
// Liste um eins nach unten bewegen
IF #stInOutHMIJam.#IncOne THEN
#stInOutHMIJam.#nIndex += 1;
IF #stInOutHMIJam.#nIndex > #stInOutHMIJam.#upperBound - 9 THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex <= 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#IncOne := FALSE;
END_IF;
// Liste um fünf nach unten bewegen
IF #stInOutHMIJam.#IncFive THEN
#stInOutHMIJam.#nIndex += 5;
IF #stInOutHMIJam.#nIndex > #stInOutHMIJam.#upperBound - 9 THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex < 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#IncFive := FALSE;
END_IF;
// Zum Listenanfang springen
IF #stInOutHMIJam.#ScrollStart THEN
#stInOutHMIJam.#nIndex := 1;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#ScrollStart := FALSE;
END_IF;
// Zum Listenende springen
IF #stInOutHMIJam.#ScrollEnd THEN
#stInOutHMIJam.#nIndex := #stInOutHMIJam.#upperBound - 9;
IF #stInOutHMIJam.nIndex < 0 THEN
#stInOutHMIJam.nIndex := 1;
END_IF;
#nIdxMerker := #stInOutHMIJam.nIndex;
FOR #nIdx := 1 TO 10 DO
#stInOutHMIJam.arJamList[#nIdx - 1] := #arInOutJam[#nIdxMerker];
#nIdxMerker += 1;
IF #nIdx >= #nUpperBound OR #nIdxMerker > #nUpperBound THEN
EXIT;
END_IF;
END_FOR;
#stInOutHMIJam.#ScrollEnd := FALSE;
END_IF;
END_REGION
REGION FC_CAR_MOVE
// "FC_CARR_MOVE"(nInCommand := #stHMIJam.stCarMove.nCommand,
// nInJamNr := #nInJamNr,//braucht nicht von HMI zu kommen - #stHMIJam.stCarMove.nJamNr,
// nInTarget := #stHMIJam.stCarMove.nTarget,
// xInStart := #stHMIJam.stCarMove.xStart,
// xOutError => #stHMIJam.stCarMove.xError);
#stInOutHMIJam.stCarMove.xStart := FALSE;
END_REGION
REGION HMI
IF #stInOutHMIData.stStatus.bByte.%X7 THEN //%X7: Manuelle Zielvergabe aktiviert
#arInOutJam[1].nDestination := #stInOutHMIData.stConfig.nCapacitySetHMI; //Sorgt dafür, dass Carrier ein festes Ziel bekommen
END_IF;
END_REGION
END_FUNCTION_BLOCK
@@ -0,0 +1,159 @@
FUNCTION "FC_CarrierManipulator" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_IN_OUT
stInOutHMIJamData : "UDT_HMI_JamData"; // Jam Daten HMI
stInOutHMIJamList : "UDT_Visu_Jam_List"; // Jam Liste HMI
arInOutJam : Array[*] of "stCarrier"; // Jam SPS
arInOutHyperJam : Array[*] of "stCarrier";
END_VAR
VAR_TEMP
tmpEmptyCarr : "stCarrier";
nCounter : DInt;
nArrayMax : Int;
nIdx : Int;
nStatus : Int;
xResult : Bool;
END_VAR
VAR CONSTANT
cFirstAhead : Int := 1; // Carrier weiterbuchen
cLastBack : Int := 2; // Carrier zurückbuchen
cSwapFirstLast : Int := 3; // Ersten und letzten Carrier im Jam tauschen
cDeleteAll : Int := 4; // Alle Jam Daten löschen (macht Sinn, wenn Jam unklar und man einfach bereinigen will)
cDeletePos : Int := 5; // Position, falls bestimmter Carrier gelöscht werden soll (macht Sinn, wenn man Carrier entnimmt)
cPutToHyperJam : Int := 6; // Position, falls bestimmter Carrier zum HyperJam soll (macht Sinn, wenn Carrier nicht auffindbar)
END_VAR
BEGIN
REGION Info
(*
Funktionen
- Alle carrier daten in einem jam leeren - check
- bestimmten carrier löschen - check
MUBEA bereits gemacht:
- Carrier in Hyper-Jam - check
- Weiterbuchen
- Zurückbuchen
- Tauschen (erster und letzter)
*)
END_REGION
REGION Array-Grenzen
#nArrayMax := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJam, DIM := 1));
END_REGION
REGION Befehl ausführen
CASE #stInOutHMIJamList.stCarMove.nCommand OF
#cDeleteAll:
REGION Jam bereinigen
IF #stInOutHMIJamList.stCarMove.xStart THEN
IF NOT #arInOutJam[1].bStatus.%X0 OR NOT #stInOutHMIJamData.stStatus.bByte.%X2 OR NOT #stInOutHMIJamData.stStatus.bByte.%X3 THEN
#stInOutHMIJamList.stCarMove.xStart := FALSE;
RETURN;
END_IF;
FOR #nIdx := 1 TO #nArrayMax DO
IF #arInOutJam[#nIdx].bStatus.%X0 THEN
#arInOutJam[#nIdx] := #tmpEmptyCarr; // Carrierdaten löschen/überschreiben
ELSE
EXIT; //fertig, nichts mehr zu tun
END_IF;
END_FOR;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
END_IF;
END_REGION
#cDeletePos:
REGION Bestimmten Carrier löschen
IF #stInOutHMIJamList.stCarMove.xStart THEN
IF #stInOutHMIJamList.stCarMove.nPosDeleteCarrier < 1 OR #stInOutHMIJamList.stCarMove.nPosDeleteCarrier > #nArrayMax OR NOT #arInOutJam[1].bStatus.%X0 OR NOT #stInOutHMIJamData.stStatus.bByte.%X2 OR NOT #stInOutHMIJamData.stStatus.bByte.%X3 THEN
//Wenn Positionsangabe zu klein oder zu groß ist, oder in Jam ist gar kein Carrier online, dann beende
#stInOutHMIJamList.stCarMove.nPosDeleteCarrier := 1;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
RETURN;
END_IF;
//lösche den ausgewählten
#arInOutJam[#stInOutHMIJamList.stCarMove.nPosDeleteCarrier] := #tmpEmptyCarr;
IF #stInOutHMIJamList.stCarMove.nPosDeleteCarrier < #nArrayMax THEN
//Rücke danach auf um Lücke zu beseitigen aber nur wenn es nicht die letzte Stelle war
#nStatus := MOVE_BLK_VARIANT(SRC := #arInOutJam,
COUNT := INT_TO_UDINT(#nArrayMax - #stInOutHMIJamList.stCarMove.nPosDeleteCarrier),
SRC_INDEX := #stInOutHMIJamList.stCarMove.nPosDeleteCarrier,
DEST_INDEX := #stInOutHMIJamList.stCarMove.nPosDeleteCarrier - 1,
DEST => #arInOutJam);
//letztes löschen damit dort nichts doppeltes drinsteht
#arInOutJam[#nArrayMax] := #tmpEmptyCarr;
END_IF;
//fertig, setze Startbefehl zurück und Position auf Default
#stInOutHMIJamList.stCarMove.nPosDeleteCarrier := 1;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
END_IF;
END_REGION
#cPutToHyperJam:
REGION Carrier zum Hyper-Jam
IF #stInOutHMIJamList.stCarMove.xStart THEN
IF #stInOutHMIJamList.stCarMove.nPosTransferHj < 1 OR #stInOutHMIJamList.stCarMove.nPosTransferHj > #nArrayMax OR NOT #arInOutJam[1].bStatus.%X0 OR NOT #stInOutHMIJamData.stStatus.bByte.%X2 OR NOT #stInOutHMIJamData.stStatus.bByte.%X3 THEN
//Wenn Positionsangabe zu klein oder zu groß ist, oder in Jam ist gar kein Carrier online, dann beende
#stInOutHMIJamList.stCarMove.nPosTransferHj := 1;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
RETURN;
END_IF;
IF #arInOutJam[#stInOutHMIJamList.stCarMove.nPosTransferHj].nBarcode > 2 THEN
REGION normale Barcodes
//Füge Carrier am Ende des Hyperjams ein nur wenn nicht Dummy oder BadRead, also nur bei normalem Code
"FC_TSHR_AddAtEnd"(stInTrolley := #arInOutJam[#stInOutHMIJamList.stCarMove.nPosTransferHj],
nOutCounter => #nCounter,
xOutResult => #xResult,
arTSHR := #arInOutHyperJam);
//lösche den ausgewählten
#arInOutJam[#stInOutHMIJamList.stCarMove.nPosTransferHj] := #tmpEmptyCarr;
IF #stInOutHMIJamList.stCarMove.nPosTransferHj < #nArrayMax THEN
//Rücke danach auf um Lücke zu beseitigen aber nur wenn es nicht die letzte Stelle war
#nStatus := MOVE_BLK_VARIANT(SRC := #arInOutJam,
COUNT := INT_TO_UDINT(#nArrayMax - #stInOutHMIJamList.stCarMove.nPosTransferHj),
SRC_INDEX := #stInOutHMIJamList.stCarMove.nPosTransferHj,
DEST_INDEX := #stInOutHMIJamList.stCarMove.nPosTransferHj - 1,
DEST => #arInOutJam);
//letztes löschen damit dort nichts doppeltes drinsteht
#arInOutJam[#nArrayMax] := #tmpEmptyCarr;
END_IF;
//fertig, setze Startbefehl zurück und Position auf Default
#stInOutHMIJamList.stCarMove.nPosTransferHj := 1;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
END_REGION
ELSE
REGION Dummy oder unbekannt
//Falls doch Dummy oder unbekannter Barcode, lösche nur
#arInOutJam[#stInOutHMIJamList.stCarMove.nPosTransferHj] := #tmpEmptyCarr;
IF #stInOutHMIJamList.stCarMove.nPosTransferHj < #nArrayMax THEN
//Rücke danach auf um Lücke zu beseitigen aber nur wenn es nicht die letzte Stelle war
#nStatus := MOVE_BLK_VARIANT(SRC := #arInOutJam,
COUNT := INT_TO_UDINT(#nArrayMax - #stInOutHMIJamList.stCarMove.nPosTransferHj),
SRC_INDEX := #stInOutHMIJamList.stCarMove.nPosTransferHj,
DEST_INDEX := #stInOutHMIJamList.stCarMove.nPosTransferHj - 1,
DEST => #arInOutJam);
//letztes letztes löschen damit dort nichts doppeltes drinsteht
#arInOutJam[#nArrayMax] := #tmpEmptyCarr;
END_IF;
//fertig, setze Startbefehl zurück und Position auf Default
#stInOutHMIJamList.stCarMove.nPosTransferHj := 1;
#stInOutHMIJamList.stCarMove.xStart := FALSE;
END_REGION
END_IF;
END_IF;
END_REGION
END_CASE;
END_REGION
END_FUNCTION
@@ -0,0 +1,27 @@
FUNCTION "FC_GET_PROFINET_DEVICE_STATES" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_TEMP
nRetValExist : Int;
nRetValFaulty : Int;
END_VAR
BEGIN
REGION Device States
REGION Get MODE Exist
#nRetValExist := DeviceStates(LADDR := "Local~PROFINET_IO-System", // IO-System
MODE := 4, // vorhanden / nicht vorhanden
STATE := "DB_PROFINET_DEVICE_STATES".arPN_DEV_STATE_EXIST); // Status
END_REGION
REGION Get MODE Fault
#nRetValFaulty := DeviceStates(LADDR := "Local~PROFINET_IO-System", // IO-System
MODE := 2, // Störung
STATE := "DB_PROFINET_DEVICE_STATES".arPN_DEV_STATE_FAULTY); // Status
END_REGION
END_REGION
END_FUNCTION
@@ -0,0 +1,32 @@
ORGANIZATION_BLOCK "Programming error"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
BEGIN
"DB_Error".BlockNr := #BlockNr;
"DB_Error".Reaction := #Reaction;
"DB_Error".Fault_ID := #Fault_ID;
"DB_Error".BlockType := #BlockType;
"DB_Error".Area := #Area;
"DB_Error".DBNr := #DBNr;
"DB_Error".Csg_OBNr := #Csg_OBNr;
"DB_Error".Csg_Prio := #Csg_Prio;
"DB_Error".Width := #Width;
"DB_Error".xErrorActive := TRUE; //Wird am Anfang von OB1 wieder auf FALSE gesetzt
CASE #BlockType OF
16#01:
//Fehler in OB
"DB_Error".sErrorText := CONCAT_STRING(IN1 := 'Programming Error in OB ', IN2 := UINT_TO_STRING(#BlockNr));
16#02:
//Fehler in FC
"DB_Error".sErrorText := CONCAT_STRING(IN1 := 'Programming Error in FC ', IN2 := UINT_TO_STRING(#BlockNr));
16#03:
//Fehler in FB
"DB_Error".sErrorText := CONCAT_STRING(IN1 := 'Programming Error in FB ', IN2 := UINT_TO_STRING(#BlockNr));
16#06:
//Fehler in DB
"DB_Error".sErrorText := CONCAT_STRING(IN1 := 'Programming Error in DB ', IN2 := UINT_TO_STRING(#BlockNr));
END_CASE;
END_ORGANIZATION_BLOCK
@@ -0,0 +1,72 @@
FUNCTION_BLOCK "FB-OPC_UA_System-Stat"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
nInSystemStat : Int; // Main State
xInFireAlarm : Bool; // Feueralarm
xInSafetyStop : Bool; // Not-Halt
xInError : Bool; // Störung
xInCommunicationError : Bool; // Kommunikationsstörung
END_VAR
VAR_OUTPUT
nOutScadaStatus : Int;
END_VAR
BEGIN
//Priority Chain
//Emergency
//Fault/Restore
//Manual
//Energy Saving
//Cleaning / Warning
//Running/Stopped
IF #xInFireAlarm THEN
#nOutScadaStatus := 1;
ELSIF #xInSafetyStop THEN
#nOutScadaStatus := 3;
ELSIF #xInError THEN
#nOutScadaStatus := 5;
ELSIF #xInCommunicationError THEN
#nOutScadaStatus := 23;
ELSIF #nInSystemStat = "E_DeviceState.eExecute" THEN
#nOutScadaStatus := 19;
ELSIF (#nInSystemStat = "E_DeviceState.eStopped") OR (#nInSystemStat = "E_DeviceState.eIdle") THEN
#nOutScadaStatus := 17;
END_IF;
(* CASE #nInSystemStat OF
"E_DeviceState.eStopped":
#nOutScadaStatus := 17;
"E_DeviceState.eIdle":
#nOutScadaStatus := 17;
"E_DeviceState.eExecute":
#nOutScadaStatus := 19;
"E_DeviceState.eHolding":
#nOutScadaStatus := 3;
"E_DeviceState.eHeld":
#nOutScadaStatus := 13;
"E_DeviceState.eSuspended":
#nOutScadaStatus := 13;
"E_DeviceState.eStarting",
120,
"E_DeviceState.eUnholding",
"E_DeviceState.eUnsuspending",
"E_DeviceState.eStopping":
#nOutScadaStatus := 7;
ELSE
#nOutScadaStatus := 5;
END_CASE; *)
END_FUNCTION_BLOCK
@@ -0,0 +1,62 @@
FUNCTION "FC_S7" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
wInID : Word;
END_VAR
VAR_IN_OUT
xInOutStart : Bool;
xInOutError : Bool;
wInOutStatus : Word;
InOutAddr : Remote;
InOutOrigin : Variant;
END_VAR
VAR_TEMP
xDone : Bool;
xError : Bool;
wStatus : Word;
END_VAR
BEGIN
REGION PUT
"PUT_DB"(REQ := #xInOutStart,
ADDR_1 := #InOutAddr,//P#DB99.DBX68.0 BYTE 1,//Partner CPU,
SD_1 := #InOutOrigin,//Eigene CPU
ID := #wInID);
IF "PUT_DB".ERROR THEN
#xInOutError := "PUT_DB".ERROR;
END_IF;
IF "PUT_DB".STATUS <> 0 THEN
#wInOutStatus := "PUT_DB".STATUS;
END_IF;
IF "PUT_DB".DONE THEN
#xInOutStart := FALSE;
END_IF;
END_REGION
// REGION GET
// #rTrigGET(CLK := "DB_S7".xGetStart);
// IF #rTrigGET.Q THEN
// "DB_Test".rTimeResult := RUNTIME("DB_Test".rTimeMem);
// END_IF;
// "GET_DB"(REQ := "DB_S7".xGetStart,
// NDR => #xTemp,
// ADDR_1 := P#DB99.DBX2.0 DINT 1,//Partner CPU
// RD_1 := "DB_S7".arCarrier[1].nBarcode, //Eigene CPU
// ID := W#16#117);
// IF "GET_DB".NDR THEN
// "DB_Test".rTimeResult := RUNTIME("DB_Test".rTimeMem);
// "DB_S7".xGetStart := FALSE;
// END_IF;
// END_REGION
END_FUNCTION
@@ -0,0 +1,104 @@
FUNCTION_BLOCK "FB_PerfTest"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
nInSepNo : Int; // Nummer welche hinter Sep-Konstante steckt
xInSepFlag : Bool; // Flanke vom Release pro MiniTrolley
xInReset : Bool;
tInTimeMeasurement : Time := T#86400s; // Default 24h
nInGoalFlags : Int; // Anzahl der Releases welche Messung stoppen
END_VAR
VAR
tOnTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'; S7_SetPoint := 'False'} : TON_TIME;
xTestActive : Bool; // true = Messung läuft
xTimerRun : Bool; // Signal für Timer
nCount : Int; // MiniTrolleys pro Sep während Messung
Date_And_TimeStart : Date_And_Time; // Beginn mit erstem MiniTrolley
Date_And_TimeLap : Date_And_Time; // Zeit bei jedem weiteren MiniTrolley
tTimeResult : Time; // jeweiliges Ergebnis
rTrigRun {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
fTrigRun {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
fTrigSepFlag {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
END_VAR
VAR RETAIN
arTrolley : Array[1..500] of Struct
nCountNo : Int; // Aktuelle Schaltung
tTimeEvent : Date_And_Time; // Zeitstempel bei Release
tTimeResult : Time; // Differenz seit Start
END_STRUCT;
nTrolleyNo : Int; // aktuelle Zeile in Array in welche geschrieben wird
END_VAR
VAR_TEMP
nTmpTimeReturn : Int;
nIdx : Int;
END_VAR
BEGIN
IF #nTrolleyNo = 0 THEN
#nTrolleyNo := 1;
END_IF;
REGION Flanken erfassen
#rTrigRun(CLK := #xTestActive);
#fTrigRun(CLK := #xTestActive);
#fTrigSepFlag(CLK := #xInSepFlag);
END_REGION
REGION Nullen bei neuer Messung
IF #xInReset THEN
#Date_And_TimeLap := DT#1990-01-01-00:00:00;
#Date_And_TimeStart := DT#1990-01-01-00:00:00;
#tTimeResult := T#0s;
#nTrolleyNo := 1;
FOR #nIdx := 1 TO 500 DO
#arTrolley[#nIdx].nCountNo := 0;
#arTrolley[#nIdx].tTimeEvent := DT#1990-01-01-00:00:00;
#arTrolley[#nIdx].tTimeResult := T#0s;
END_FOR;
ELSIF #rTrigRun.Q THEN
#Date_And_TimeLap := DT#1990-01-01-00:00:00;
#Date_And_TimeStart := DT#1990-01-01-00:00:00;
#tTimeResult := T#0s;
#nTrolleyNo := 1;
#xTimerRun := TRUE;
END_IF;
END_REGION
REGION Sep-Flanken während Messung erfassen
//Während Messtimer läuft und 1. Flanke kommt, ermittle Zeit und schreibe sie weg und erhöhe Pos für nächste Zeile.
//Danach so weiter machen aber dann immer die Zeitdifferenz wegschreiben
IF #tOnTimer.ET > T#0s AND #tOnTimer.ET < #tInTimeMeasurement AND #fTrigSepFlag.Q THEN
IF #nTrolleyNo = 1 THEN //Nur beim ersten Trolley die Startzeit ermitteln
#nTmpTimeReturn := RD_SYS_T(#Date_And_TimeStart);
END_IF;
#nTmpTimeReturn := RD_SYS_T(#Date_And_TimeLap); //Flankenzeitpunkt für Differenz aus diesem und Startzeitpunkt
#tTimeResult := T_DIFF(IN1 := #Date_And_TimeLap, IN2 := #Date_And_TimeStart);
//Daten wegschreiben
#arTrolley[#nTrolleyNo].nCountNo := #nTrolleyNo;
#arTrolley[#nTrolleyNo].tTimeEvent := #Date_And_TimeLap;
#arTrolley[#nTrolleyNo].tTimeResult := #tTimeResult;
//erhöhen für nächste Zeile oder beende Messung
IF #nTrolleyNo <= #nInGoalFlags THEN
#nTrolleyNo += 1;
ELSIF #tOnTimer.ET >= #tInTimeMeasurement OR #fTrigRun.Q OR #nTrolleyNo > #nInGoalFlags THEN
#xTimerRun := FALSE;
#xTestActive := FALSE;
END_IF;
END_IF;
IF #tOnTimer.ET >= #tInTimeMeasurement OR #fTrigRun.Q OR #nTrolleyNo > #nInGoalFlags THEN
#xTimerRun := FALSE;
#xTestActive := FALSE;
END_IF;
END_REGION
REGION Timer
#tOnTimer(IN := #xTimerRun,
PT := #tInTimeMeasurement);
END_REGION
END_FUNCTION_BLOCK