From ed3708f42a85d5a098611b280e79cdc11f565cc0 Mon Sep 17 00:00:00 2001 From: DFFM-maker Date: Fri, 26 Dec 2025 10:11:06 +0100 Subject: [PATCH] Fix: RAg version --- .../30609877-aa0b-458d-9b0c-36601bb29531.txt | 310 ++++++++++++++++++ .../7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt | 310 ++++++++++++++++++ __pycache__/app.cpython-310.pyc | Bin 4667 -> 5139 bytes app.py | 97 +++--- docker-compose.yml | 2 +- 5 files changed, 669 insertions(+), 50 deletions(-) create mode 100644 .files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt create mode 100644 .files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt diff --git a/.files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt b/.files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt new file mode 100644 index 00000000..0c1ca348 --- /dev/null +++ b/.files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt @@ -0,0 +1,310 @@ +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 +); diff --git a/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt b/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt new file mode 100644 index 00000000..0c1ca348 --- /dev/null +++ b/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt @@ -0,0 +1,310 @@ +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 +); diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc index 19b4f21cc689d8baf5ebceced6d14381fefb3b3d..b0399de53e80313ab61dd7881b8e7d3fde3cdcf5 100644 GIT binary patch literal 5139 zcmZ`-TW=f372cU$E|(NZQTL0TgtB8hOdUDC zmKS=hjvvf7f>tyEd2gld$%EaKgB6qt_KQCr(QEDRiVE{*)&qGj?9>}x*kJU1gCED+ z!gC2vcp8N=)>xk_Gd8w)kLi{bvuzfRE2E#G8X09-KUKC=wG$qMiPfpA)x=tZPLk5C z8#muy{@~)J#pT-NcW)-P_rMQTm^iKaJx};Dtnwt+4LwWBHtQv1jl$h zbf(_v&@g_K{d4l^=dVy z#VE}h3G1JDHGLWt)JWnvbcZxXN-VPiFR=WMWtP9UO!+Lj4>$@rjfqFA6X6ea#)u%n z?pd-BBdUe*1)ni`=!F{0rSHZeW3QG5nJ73?AF;TSy@!Crlqdg?8G9LrFA<9KSR*jO`T zCQM<8%-3AlTb!&%hh8_T*`;W75X$#;xG%hV{`~nQCxS+|g}4uW(t&GV3Mi!E$5!Y^ z9&iHRRc|+_XSfkGn}}*ZXluK;rBBcbWC+!_@A@lMOJWJbEv(DfJ zWZ>;jMzOBl(8wcCV)9}AjbQ0EcB#Rz>AB}gH54R#(}4~-OlxHek5OX|bAGTg{IMiG zA22cD0C}R;>zAmIUe}mH#B4EO0Wbju=P>vf3=UnV29Ks=wq^;83TulavNAFM#E>Ur z)MgA}&meM^jhOc{BD>9~4tv-eSD4X5Bt^ptfF7G)8Q<`Sh^7@t+pvhh=%*&(487MG ztdm>%mL=9|5Gwy)_`s;#4qATWC9ieI6E7+6fr6ScErdxk$@uL~S0#BpCV-!zBnM7% zdmYi}qLhL3DvLCoL>4MlC$V9XdaIt;DbOeR8`rK~yneCv;mvEZLMvqS3W*8x$y2(c z13Xt%E3xPZ@&)R1q!)IAcIYMSsW{Yz-#bS5CgekkC%%aQGz+j*i8*G9YbAuGv&nyu zZ$RJxx{?3y+3Fu?e8!^pl=0Ab7m;w)=yAo!T7#Auo8dU{i<2GJIBcc;e+yW*VgTbT zc_p?k8^1r^v!iKY$2RQubCHd0vL4lef+nn_+afz-khM}wynhVVv^ExZaq#}JbwlKk zDKdMuwl+N2ItyEulr8dccGuRo$j+>n{WMD_OKaNFL(%h>JlUvMb_36^bUdjLb8Y2U z!fqu^#!HDIkK^0Fqll`SIyT(G{jMiBYH8FZ84WE-wo~6|26Z8iKr$M-Jc#FDp^~tC zG&OJq={NMQ>FV%@tPT>b{lpGEO1O!QoaD8IHhv1%uh8y}n@=~^UZ8Qhn8so)ja+v) z9p5v(TTEB1!CAUZl{koEnj9u3h=F_^s~!&Y-5Ke^Hy{zdih^%afFli>HXe>3=gj`z zvMuhQHqNG*wMkIQcM5PU`qNHz8B-g0_I?Dc1OgfW{)x(G!1ta`oP_Y25bzj4%ZxN- z3UdWCVXs9*0WH95<{p=)Ap?gI*)7)3Zj)mgSCJ-(JV3&M#4of&?k6PjkXX_ZLq8!= zfJ9A86n{)YkCc9Fq&mDLbNId_v(iN-m2*_Lh>l1KtZ4X_*97zOPFF4xFPj7IWVLnL zN>XNuM)w332b^wB690{+_6FV_(7wutpgMChOQU0ZqZfDC-`u{<-l4=bIL=$KfSHA3lpL@#LB1l3eXTu9!jx5f<$#ZzKNFTtVzAb(ENe zQ^+1BWm-6ZbXgQ*!Szum`_b1|9oTnZ3^T4(P7Wjw#Ebyt(FZYkq zPW?$Syv^i)^!jD2U+4|RL;b03qgQxp_Mi99R?~4oV>$w39T)4#s@Y&Klhz|LMs6kC4UjCTx#wvW1>MBWC;M*xqK84-U^p zo_bCkL7qAaPBE$~{W7Sv)B!TRLw&V)x(Ho-+4j&0T-N9M(UHENE(IBfhw|Z#*uJZSnoy zOcv9OhV;`P=x0KI-*K&%r@rioUebHiu4bywFUcziDY=Lu8Y9@K5F}OFfvVgMy2v^2 z(C~{?5Mo8sm(q+8R7Bl>k}DVT(Xo8~rcV?^_?2#0Z;POU>;v*$(J+R}d`gJya3fS+ ztIE@@iA^UK;YY&dorJI7iL80`KqXA-)LAtX?hDsWv#b0C9pyL*%qL>%+8w$Thx;;Y zt{ENS;qLpL^gOa|42s+)3^wlcK)d8Bs)4>z< zaN>APPt*D+tB-_?e@ecGiSm6aH1b}j+7cBnQ*oAxb5zi^L{bV(*Rp-kJ*nhd)ZL|C=?GTOfguXzANp_$?Hz(j-`T~!nBpZn-LO9J` zts6uCR|(WmHI%5q+#At(YD^$lv%fkKA0X-_S3M=z6dzom_ zj%^-eC3D)exQ)>~#-@=IPx3N^8k$G7N^~=#GJlp;k!x246IJX22!?XE_dcGEN>($r-aMd1g(N G*#7|!%o=B6~-%zdfYgYHClY9y*h2d&#MnUVH8Vr%rn6sXp}NX`Fs6)l{-D+5_#nL(3 zwe?)a&FDJoW>K4Ur;&4Wjl7$OoF#0L5n16pu-qY$a|^!F8y0ynw9Vb3D7Yhjc5T!z zis3Ehj-fr?Z%>G#82Qw2C&j23L%m0giwV?w#iZDSdP?jSQ>gd(d~I6n6Vsm>_nEsN zy#s>EgEzTA>r)G(hXh+T$_M|2vl)wJmN>#wz6u(CnJ0x0g!G!~Ts`ocD#?TX4&eDH^LRX|BfH|jaCp=YGI?Z z2lD<()0fW<@9nRkR5&R9cucP~IMgXrPHcqoR@Cxpe&mi^zIbu&(p+`!%0l(sx#juC z`&X4}&7L@M^7T`dlV@M6oUEKWb9(mV$up-uc7XHTC#{S8a3)i6>uMq23q zanu%`b9kZ>3T3RZ9#>{;Z1XNd&x)B~f^Ts$Ih1HFUL^(kcY;V&!&_xL;bD|mEl;f` z)>;@elj60@OTSpYGIwr%xq5zKDRCO!EnfsOg0ge%$d^?a)_pmQuYF_4aa50oPI#>r zJ(bpGGFp>9%v0St`*RS9XqQDc&g3K-HQM*nzYH1<9({&YG?dX}+eVKIw#~X+@Rk5nIIV@>eUllnwQQU;qT?GF zr{BkTXsn!F>=b4`o@va8nF}+QW|m#M2J}=Pn{*7e@z`#wo5x>+;vK(<833Z3leG23 zY=#?&6DfIwU)38_VU;9~U^pWE7g>Rg;#Xk#A1prl#NzTOhHH49`7_be zTBcZ{3WiCl3$$xNuX>Rdq^~0r$1KXfa$#gF(zN)6!tk$&Sy^Oht zRFh}&MO4qAln|T4qrW|iMh8~vAxJ&7)Uvh^dL3>agiWnpw|E=9P)5*T^JcJ8&bUQ7 z#IBe-*2nGckR;H$g^%=`S3K!85LnLlKjLQ2U0z;ToQLW%sSXBIm1@avMhG(l-#Cu- zB7z8qwKB|NR2QDeS1|O9#>gkgNPoo>eTCxJDqVg4YpyFhZZFnSdtHSrmw;-o(gV@l3ybg1mf&I;nA~f4iQPi12Nm=w!K{-X>P3>={GFQLB2G~Y|C{hvR2^%@+asfj`X8e*o^#y{m2hBY<9L3orQcvp2+Vx%;H5}U*cqN1xR0ji)sSNFUl--n%FCC1hFK$3V#UYW?~U5w2xxyy9b|@mNz5iH%edv zu~{jl6rCcXrhHLpA0xT1djJ0Ri)Tb%DcxwRr{cbpp`0ysoNIm^jO>>>{BQ};I16Mc zz^pu!}nDA%KE(x#IlY(ZdCh2##X^@OXqxhG zMD;|46a-?kXYX1~W5Zg3!;<8A<5!<|Gb+30^l~Z>BF(G~fl9Ng&>KeEiL-<#5NT1* zPKj>IG40M2<3L3=bV#-pzq-6B?cE*{-xPyKNH(OCmy&kyXi)H^H=#YI(G zI|6Ht#baMGc?mWjkH=u~L*0pZqIVSbK#iF<)C=(h;tKCh#^b#rc0HpJ@nSr=&EiS% z+?GDgmu$ThkJC!}T+hejqO`>aPo*_*I4%yHpY@IrIT4D;_RC1Z)by8Z^IqRVd-PgI zv}Jyz(Ot{Xo4d9tA6=Ah!X@O-sdxiL`vnx=ef2wvAoF$aMku|GrW(qON#uwzx!9n~=_4I#F=n+XG`tF5P9_VZ$Bx$NBfJs z`fadeX+Ty52zC6GQesgU!WZ;;-^ObR7;}BYZ=S0B?6}S%XJ5T>wQNetCXk({5hO}7 zNJo9IAr~+#>AlX__3PJLn`$*|zEpYn?J{~eprsE-*?XI4>(ST0`Ag|SSVAn99xgSd zpbphx|9+|E%aZbEu@$`w$FKW3>q>GSXq{3m)johF22Q@>a?oD zTSy|X5M4YIj=N*_Camn$>q+MP<*RcSFD5obgO*Ob6N`+TI5#{n3qk2T_E_GNT86Fw z8o_R{B}3c{aZg>*#PU97++w>$2=j$b@uMW$*Seb%Fi}TuMo)Q8hIih*=VGr8k*p8BXur!(qI<_GP}$KPfst)#S$?6(^3caY!ID z;@mufHyz0E?~C>*h!WWnei>SaS@$x09~(6%*(iTmzg>P#U$=mXRWE4P1C=67(8f6x zA5rm3Dk!x}hM&2o==+UEf_#TW^i?f`%)xKocJuu!I$e8a=-r{YXtP;M^GJD} 12000: text = text[:12000] - - response = client.embed(model='nomic-embed-text', input=text) - # Gestione compatibilità risposta (embedding vs embeddings) - if 'embeddings' in response: - return response['embeddings'][0] - return response['embedding'] + try: + response = client.embed(model='nomic-embed-text', input=text) + + # Gestione risposta + if 'embeddings' in response: + return response['embeddings'][0] + return response.get('embedding') + except Exception as e: + print(f"Errore Embedding: {e}") + return [] -# Nuova funzione per CERCARE nei documenti (RAG) async def search_qdrant(query_text, user_role): """Cerca documenti pertinenti su Qdrant""" try: qdrant_client = await connect_to_qdrant() query_embedding = await get_embeddings(query_text) - # Cerca i 3 documenti più simili alla domanda + # Se non trova embedding (errore connessione), non cercare + if not query_embedding: + return "" + + # Cerca i 3 documenti più simili search_result = qdrant_client.search( collection_name="documents", query_vector=query_embedding, @@ -86,33 +94,31 @@ async def search_qdrant(query_text, user_role): ) contexts = [] - for hit in search_result: - if 'payload' in hit and 'file_name' in hit['payload']: - contexts.append(f"Documento: {hit['payload']['file_name']}") + # FIX: controllo sicurezza per evitare 'list index out of range' + if search_result: + for hit in search_result: + try: + if 'payload' in hit and 'file_name' in hit['payload']: + contexts.append(f"Documento: {hit['payload']['file_name']}") + except Exception: + pass return "\n".join(contexts) except Exception as e: - print(f"Errore ricerca: {e}") + print(f"Errore ricerca Qdrant: {e}") return "" @cl.on_chat_start async def chat_start(): - # Set the user's email to a hardcoded value for testing purposes + # Hardcode per test user_email = "admin@esempio.com" - - # Determine the user's role based on the email user_role = USER_ROLES.get(user_email, 'guest') - # Create workspace directory if it doesn't exist create_workspace(user_role) - # Initialize history in the session cl.user_session.set("history", []) - - # Set the user's role in the session cl.user_session.set("role", user_role) - # Send a welcome message based on the user's role if user_role == 'admin': await cl.Message(content="Welcome, Admin!").send() elif user_role == 'engineering': @@ -126,56 +132,51 @@ async def chat_start(): @cl.on_message async def message(message): - # Retrieve the user's role from the session user_role = cl.user_session.get("role", 'guest') if not user_role: await cl.Message(content="User role not found").send() return - # Initialize the Ollama client - ollama_api_base = os.getenv('OLLAMA_API_BASE', 'http://192.168.1.243:11434') - try: - client = ollama.Client(ollama_api_base) + # Client Ollama URL Hardcoded + client = ollama.Client(host=OLLAMA_URL) - # Retrieve the history from the session and limit it + # History & Sliding Window history = cl.user_session.get("history", []) history = limit_history(history) - # --- RAG STEP: Cerca nei documenti prima di chattare --- + # --- RAG STEP 1: Cerca nei documenti --- context_text = await search_qdrant(message.content, user_role) - # Se trova documenti, inietta il contesto come "System Message" + # Se trova contesto, iniettalo if context_text: system_prompt = f"Contexto dai documenti:\n{context_text}\n\nRispondi usando questo contesto." history.insert(0, {"role": "system", "content": system_prompt}) - # Append the new user message to the history history.append({"role": "user", "content": message.content}) - # Check for Uploads + # Gestione Upload e Indexing if message.elements: uploaded_files = [] for element in message.elements: try: - # Save file to disk dest_path = os.path.join(WORKSPACES_DIR, user_role, element.name) with open(element.path, 'rb') as src, open(dest_path, 'wb') as dst: shutil.copyfileobj(src, dst) - # Indexing on Qdrant if .txt if element.name.endswith('.txt'): with open(dest_path, 'r') as f: content = f.read() + # Indexing embeddings = await get_embeddings(content) - qdrant_client = await connect_to_qdrant() - point_id = uuid.uuid4() - point = PointStruct(id=point_id, vector=embeddings, payload={"file_name": element.name}) - qdrant_client.upsert(collection_name="documents", points=[point]) - - await cl.Message(content=f"Documento '{element.name}' indicizzato.").send() + if embeddings: + qdrant_client = await connect_to_qdrant() + point_id = uuid.uuid4() + point = PointStruct(id=point_id, vector=embeddings, payload={"file_name": element.name}) + qdrant_client.upsert(collection_name="documents", points=[point]) + await cl.Message(content=f"Documento '{element.name}' indicizzato.").send() uploaded_files.append(element.name) except Exception as e: @@ -184,10 +185,10 @@ async def message(message): if uploaded_files: await cl.Message(content=f"Files saved: {', '.join(uploaded_files)}").send() - # Call the model + # Chat response = client.chat(model='qwen2.5-coder:7b', messages=history) - # Extract code blocks + # Code Extracting code_blocks = re.findall(r"```python(.*?)```", response['message']['content'], re.DOTALL) elements = [] @@ -196,11 +197,9 @@ async def message(message): file_path = save_code_to_file(code, user_role) elements.append(cl.File(name=os.path.basename(file_path), path=file_path)) - # Append AI response to history history.append({"role": "assistant", "content": response['message']['content']}) cl.user_session.set("history", history) - # Send final message await cl.Message(content=response['message']['content'], elements=elements).send() except Exception as e: diff --git a/docker-compose.yml b/docker-compose.yml index 92cf6ea0..9e64ae6c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: - ai-station-net qdrant: - image: qdrant/qdrant:v1.0.0 + image: qdrant/qdrant:latest ports: - "6333:6333" volumes: