VAR Internal Idecon FB_IDECON_Client False False Enable BOOL True False False MsgFilter UINT 17 False False fbTcpConnect SktTCPConnect False False fbSocketSend SktTCPSend False False fbSocketRcv SktTCPRcv False False fbClose SktClose False False Config _sSOCKET_ADDRESS (PortNo := 50000, IpAdr := '172.16.224.200') False False SocketID_Int _sSOCKET False False tmpIpAddress STRING[256] False False tmpPort UINT False False Connected BOOL False False bConnectDone BOOL False False bConnectBusy BOOL False False bConnectError BOOL False False wConnectErrorID WORD False False bSendDone BOOL False False bSendBusy BOOL False False bSendError BOOL False False wSendErrorID WORD False False bRcvDone BOOL False False bRcvBusy BOOL False False bRcvError BOOL False False wRcvErrorID WORD False False RxChunk ARRAY[0..511] OF BYTE False False RxChunkLen UINT False False RxChunkValid BOOL False False RcvTimeout UINT 50 False False TxConsumed BOOL False False prevTxOutValid BOOL False False sendTrigger BOOL False False Command STRING[255] False False CommandTrigger BOOL False False prevCommandTrigger BOOL False False tonPollingSTATSV TON False False tonPollingSTATP TON False False prevPollSVQ BOOL False False prevPollPQ BOOL False False pollResetSV BOOL False False pollResetP BOOL False False prevLastMessage STRING[512] False False newMsg BOOL False False newWeightMsg BOOL False False prevWeightMsg BOOL False False totalRejected DINT False False TxChunk ARRAY[0..511] OF BYTE False False TxChunkLen UINT False False iTx UINT False False TxBuffer ARRAY[0..511] OF BYTE False False tmpWeightData Idecon_WeightData False False tmpStatPData Idecon_StatPData False False rTrigRcv R_TRIG False False bExecuteRcv BOOL False False iRcvState INT False False END VAR VAR External G_Idecon_Config False G_System_Error False G_ProgressiveID False G_OPCUA_Idecon False G_Idecon_StatP False G_Idecon_LastWeight False G_Idecon_LastSTATSV False G_Idecon_WorkMode False G_Idecon_BatchCode False G_Idecon_ProductionOrder False END VAR // ---------- Program ProcessIdecon---------------// IF NOT Enable THEN Connected := FALSE; RxChunkLen := 0; RxChunkValid := FALSE; TxConsumed := FALSE; Command := ''; CommandTrigger := FALSE; prevCommandTrigger := FALSE; prevTxOutValid := FALSE; pollResetSV := FALSE; pollResetP := FALSE; END_IF; tmpIpAddress := Config.IpAdr; tmpPort := Config.PortNo; fbTcpConnect( Execute := (Enable AND NOT Connected), DstAdr := tmpIpAddress, // Passiamo la variabile semplice (FIX per "Member of structure") DstTcpPort := tmpPort, // Passiamo la variabile semplice SrcTcpPort := 0, // (Opzionale) 0 = Assegnazione automatica porta locale Socket => SocketID_Int, Done => bConnectDone, Busy => bConnectBusy, Error => bConnectError, ErrorID => wConnectErrorID ); IF bConnectDone THEN Connected := TRUE; END_IF; IF bConnectError THEN Connected := FALSE; END_IF; // --------------------------------------------------------- // MACCHINA A STATI "INFINITY LOOP" (Per lettura continua) // --------------------------------------------------------- CASE iRcvState OF 0: // STATO IDLE: Pronti a ricevere? // Se siamo connessi e il blocco non è occupato o in errore... IF Connected AND NOT bRcvBusy AND NOT bRcvError THEN bExecuteRcv := TRUE; // ALZA IL FRONTE DI SALITA iRcvState := 10; // Passa in attesa END_IF; 10: // STATO WAIT: Aspetta i dati IF bRcvDone THEN // Dati ricevuti! Andiamo a resettare iRcvState := 20; ELSIF bRcvError OR NOT Connected THEN // Se cade la connessione o c'è errore socket iRcvState := 99; END_IF; 20: // STATO RESET (FONDAMENTALE PER LEGGERE IL SUCCESSIVO) bExecuteRcv := FALSE; // Abbassa Execute // Aspettiamo che il blocco confermi di essersi spento (Done deve tornare False) // Senza questo check, il riarmo è troppo veloce e fallisce! IF NOT bRcvDone AND NOT bRcvBusy THEN iRcvState := 0; // Torna all'inizio per leggere il prossimo END_IF; 99: // STATO ERRORE bExecuteRcv := FALSE; // Se l'errore sparisce o la connessione cade, resetta la macchina IF NOT bRcvError THEN iRcvState := 0; END_IF; END_CASE; // --- Chiamata al Blocco (Fuori dal CASE) --- fbSocketRcv( Execute := bExecuteRcv, Socket := SocketID_Int, TimeOut := 1, //RcvTimeout, // Assicurati sia basso (es. 10 o 50 ms) Size := UINT#512, RcvDat := RxChunk[0], Done => bRcvDone, Busy => bRcvBusy, Error => bRcvError, ErrorID => wRcvErrorID, RcvSize => RxChunkLen ); rTrigRcv(CLK := bRcvDone); RxChunkValid := rTrigRcv.Q AND (RxChunkLen > 0); // Gestione disconnessione su errore grave IF bRcvError THEN Connected := FALSE; iRcvState := 99; END_IF; tonPollingSTATSV(IN := (Enable AND Connected AND NOT pollResetSV), PT := T#2S); tonPollingSTATP(IN := (Enable AND Connected AND NOT pollResetP), PT := T#5S); pollResetSV := FALSE; pollResetP := FALSE; CommandTrigger := FALSE; IF tonPollingSTATSV.Q AND NOT prevPollSVQ THEN Command := 'STATSV'; CommandTrigger := TRUE; pollResetSV := TRUE; ELSIF tonPollingSTATP.Q AND NOT prevPollPQ THEN Command := 'STATREQ'; CommandTrigger := TRUE; pollResetP := TRUE; END_IF; prevPollSVQ := tonPollingSTATSV.Q; prevPollPQ := tonPollingSTATP.Q; Idecon( Enable := Enable, MsgFilter := MsgFilter, RxIn := RxChunk, RxInLen := RxChunkLen, RxInValid := RxChunkValid, TxConsumed := TxConsumed, Command := Command, CommandTrigger := CommandTrigger ); tmpWeightData := Idecon.Weight; tmpStatPData := Idecon.StatP; // Gestione Messaggi newMsg := (Idecon.LastMessage <> prevLastMessage); prevLastMessage := Idecon.LastMessage; newWeightMsg := newMsg AND IDECON_StartsWith(Idecon.LastMessage, 'WEIGHT='); IF newWeightMsg AND NOT prevWeightMsg THEN G_ProgressiveID := G_ProgressiveID + 1; END_IF; prevWeightMsg := newWeightMsg; G_Idecon_LastSTATSV := Idecon.LastSTATSV; IF tmpWeightData.Valid THEN G_Idecon_LastWeight.DateTimeText := tmpWeightData.DateTimeText; G_Idecon_LastWeight.ProductionOrder := tmpWeightData.ProductionOrder; G_Idecon_LastWeight.BatchCode := tmpWeightData.BatchCode; G_Idecon_LastWeight.RecipeName := tmpWeightData.RecipeName; G_Idecon_LastWeight.LineCode := tmpWeightData.LineCode; G_Idecon_LastWeight.SerialNumber := tmpWeightData.SerialNumber; G_Idecon_LastWeight.WeightMg := tmpWeightData.WeightMg; G_Idecon_LastWeight.DeltaToNominalMg:= tmpWeightData.DeltaToNominalMg; G_Idecon_LastWeight.Classification := tmpWeightData.Classification; G_Idecon_LastWeight.Valid := TRUE; END_IF; // --- E anche qui usiamo tmpStatPData --- IF tmpStatPData.Valid THEN G_Idecon_StatP.TotalProducts := tmpStatPData.TotalProducts; G_Idecon_StatP.TotalAccepted := tmpStatPData.TotalAccepted; G_Idecon_StatP.RejectedMinus := tmpStatPData.RejectedMinus; G_Idecon_StatP.RejectedMinusMinus := tmpStatPData.RejectedMinusMinus; G_Idecon_StatP.RejectedPlus := tmpStatPData.RejectedPlus; G_Idecon_StatP.RejectedPlusPlus := tmpStatPData.RejectedPlusPlus; G_Idecon_StatP.CannotBeWeighed := tmpStatPData.CannotBeWeighed; G_Idecon_StatP.MetalCategory := tmpStatPData.MetalCategory; G_Idecon_StatP.Valid := TRUE; END_IF; G_System_Error := bConnectError OR bSendError OR bRcvError OR Idecon.Error; IF G_Idecon_StatP.Valid AND (G_Idecon_StatP.TotalProducts >= G_Idecon_StatP.TotalAccepted) THEN totalRejected := G_Idecon_StatP.TotalProducts - G_Idecon_StatP.TotalAccepted; ELSE totalRejected := 0; END_IF; // OPC UA Mapping G_OPCUA_Idecon.Connected := Connected; G_OPCUA_Idecon.WorkMode := G_Idecon_WorkMode; G_OPCUA_Idecon.LastSTATSV := G_Idecon_LastSTATSV; G_OPCUA_Idecon.BatchCode := G_Idecon_BatchCode; G_OPCUA_Idecon.ProductionOrder := G_Idecon_ProductionOrder; G_OPCUA_Idecon.Error := G_System_Error; G_OPCUA_Idecon.ErrorId := UINT_TO_UDINT(WORD_TO_UINT(wConnectErrorID)); G_OPCUA_Idecon.LastWeight.ProgressiveId := G_ProgressiveID; G_OPCUA_Idecon.LastWeight.TimestampText := G_Idecon_LastWeight.DateTimeText; G_OPCUA_Idecon.LastWeight.ProductionOrder := G_Idecon_LastWeight.ProductionOrder; G_OPCUA_Idecon.LastWeight.BatchCode := G_Idecon_LastWeight.BatchCode; G_OPCUA_Idecon.LastWeight.RecipeName := G_Idecon_LastWeight.RecipeName; G_OPCUA_Idecon.LastWeight.LineCode := G_Idecon_LastWeight.LineCode; G_OPCUA_Idecon.LastWeight.SerialNumber := G_Idecon_LastWeight.SerialNumber; G_OPCUA_Idecon.LastWeight.Weight_g := DINT_TO_LREAL(G_Idecon_LastWeight.WeightMg) / LREAL#1000.0; G_OPCUA_Idecon.LastWeight.Delta_g := DINT_TO_LREAL(G_Idecon_LastWeight.DeltaToNominalMg) / LREAL#1000.0; G_OPCUA_Idecon.LastWeight.Classification := G_Idecon_LastWeight.Classification; G_OPCUA_Idecon.LastWeight.Expelled := FALSE; G_OPCUA_Idecon.LastWeight.Valid := G_Idecon_LastWeight.Valid; G_OPCUA_Idecon.Stats.TotalProducts := G_Idecon_StatP.TotalProducts; G_OPCUA_Idecon.Stats.TotalAccepted := G_Idecon_StatP.TotalAccepted; G_OPCUA_Idecon.Stats.TotalRejected := totalRejected; G_OPCUA_Idecon.Stats.RejectedPlusPlus := G_Idecon_StatP.RejectedPlusPlus; G_OPCUA_Idecon.Stats.CannotBeWeighed := G_Idecon_StatP.CannotBeWeighed; G_OPCUA_Idecon.Stats.MetalCategory := G_Idecon_StatP.MetalCategory; G_OPCUA_Idecon.Stats.Valid := G_Idecon_StatP.Valid; // Invio Socket sendTrigger := (Idecon.TxOutValid AND NOT prevTxOutValid); prevTxOutValid := Idecon.TxOutValid; IF sendTrigger THEN // Copiamo i dati dall'output dell'FB al buffer LOCALE // Questo evita l'errore "Member of function block instance variable" TxBuffer := Idecon.TxOut; END_IF; fbSocketSend( Execute := (Enable AND Connected AND sendTrigger), Socket := SocketID_Int, SendDat := TxBuffer[0], // --- CORREZIONE 2: Usa buffer locale --- Size := Idecon.TxOutLen, Done => bSendDone, Busy => bSendBusy, Error => bSendError, ErrorID => wSendErrorID ); TxConsumed := bSendDone; IF bSendError THEN Connected := FALSE; END_IF; fbClose( Execute := (Enable AND NOT Connected), Socket := SocketID_Int );