Sergio Bertana
Risposte nei forum create
-
AutorePost
-
Sergio Bertana
Amministratore del forumSorry but i don’t understand your needs why you have to dynamically allocate the FBs. You need use them and then you destroy after use? and why you have to destroy them you need to save memory?
To obtain the same result as in the TWINCAT with LogicLab program you can write:
VAR SquareArea : REAL; (* Square area *) SquarePerimeter : REAL; (* Square perimeter *) RForm : Form; (* Reference to Form *) MySquare : Square; (* Square form *) END_VAR MySquare.Side:=10; //Square side RForm:=MySquare; //Reference to Form SquareArea:=RForm.Area(); //Square area SquarePerimeter:=RForm.Perimeter(); //Square perimeter
You simply static instantiate the objects and use them.
Sergio Bertana
Amministratore del forumIntato se vai alla pagina di definizione errori e cerchi l’errore 9951085, troverai l’indicazione “Terminato tempo utilizzo per FB protetta”. Quindi non hai definito correttamente la licenza per l’utilizzo dei PWM, per questo che terminato il tempo di funzionamento in demo hai il blocco di alcuni dei PWM in modo casuale.
Quindi la prima operazione è impostare correttamente la licenza, trattandosi di licenza DID verificare se il tipo di modulo e il numero seriale corrispondono al modulo CPU che stai utilizzando. Nel tuo programma non trovo la funzione SysPCodeVerify di impostazione licenza quindi immagino sia stata definita da pagina Web.
Guardando il file di log che hai inviato non vedo stranezze, dando una occhiata agli ultimi records vedo:
[16/05/2025 13:45:01.000] [L] SFW198[ 5003] MPS054C110-00488 power on [16/05/2025 13:45:01.000] [L] SFW198[ 5005] Starting Op. System SFW210B040 [16/05/2025 13:45:06.931] [L] SFR050[14025] Ethernet Link ok [16/05/2025 13:45:07.513] [L] SFW198[ 6000] Run ApplID:0x61FCA90C [19/05/2025 07:39:31.007] [L] SFW198[ 6010] Stop ApplID:0x61FCA90C [19/05/2025 07:39:33.715] [L] SFW198[ 6050] LogicLab executable download end [19/05/2025 07:39:36.168] [L] SFW198[ 6000] Run ApplID:0xF5BA33F9
L’ultima volta il sistema è stato acceso il 16/05 alle 13:45, poi al 19/05 alle 7:39 hai caricato il programma con LogicLab, la fase di caricamento esegue uno stop del programma e poi manda in esecuzione il programma indicandone il valore di HASH.
Maggio 19, 2025 alle 8:02 am in risposta a: Lettura dati seriale da dispositivo su pannello cMT #82334Sergio Bertana
Amministratore del forumLa domanda è molto generica, non dici di che tipo di protocollo dispone il tuo strumento di misura.
Se è uno dei protocolli gestiti nativamente dal pannello puoi fare riferimento Guida connessioni PLC per pannelli Weintek.
Se invece si tratta di un protocollo proprietario puoi utilizzare il Free protocol che ti permette da macro con le funzioni INPORT ed OUTPORT di ricevere ed inviare dati su qualsiasi interfaccia sia seriale RS232/485 che TCP/IP.
Se cerchi nel forum Free protocol puoi trovare informazioni utili, ti rimando a questo topic dove viene gestita una stampante. Oppure a questo topic dove è gestita una comunicazione TCP/IP.
Maggio 16, 2025 alle 3:56 pm in risposta a: Impostare da PLC su HMI l’indirizzo IP di comunicazione #82310Sergio Bertana
Amministratore del forumVisto che acquisendo nuovi indirizzi IP da un server DHCP sia il PLC che il pannello la comunicazione Modbus TCP non è più operativa, l’unica soluzione che posso proporre è di connettere l’HMI al PLC oltre che via ethernet anche con una connessione seriale. L’idea è di definire su HMI un nuovo Device/Server di tipo ELSIST MODBUS RTU che comunicherà in RS232 con il modulo PLC.
Tramite questa comunicazione Modbus RTU l’HMI potrà acquisire dal PLC l’indirizzo IP da assegnare alla connessione Modbus TCP che è la connessione principale quella in cui transita tutta la comunicazione tra HMI e PLC. Ipotizzando di avere due device nell’HMI avremo che il dispositivo Modbus TCP sarà il Local Device 4, i registri di definizione del suo indirizzo IP sono LW-9600, LW-9601, LW-9602, LW-9603, quindi per impostarli dovremo programmare un Data Transfer globale di un array di 4 variabili UINT dal PLC all’HMI (Screenshot).
In questo modo modificando i valori nell’array variamo l’indirizzo sull’HMI, per accettare la variazione occorre forzare la riconnessione del dispostivo 4, settando LB-10070. Per la sua attivazione ho realizzato una macro eseguita a tempo nell’HMI che alla variazione del valore nei registri di definizione indirizzo IP esegue il set del bit di force. La macro utilizza 2 LW nell’HMI (LW-0, LW-1) per salvare l’indirizzo IP attivo Ecco il listato.
// ============================================================================= // MACRO "ReconnectDevice" // ============================================================================= // La macro è eseguita a tempo, viene controllato valore 4 LW di definizione // indirizzo IP PLC a cui connettersi. Su variazione valore viene settato bit // LB-10070 forced to reconnect device 4 (ethernet) // ----------------------------------------------------------------------------- macro_command main() bool SBit=true short LWValue=0 unsigned int IPRead=0, IPMemo=0 // Si utilizzano 2 LW per salvare valore a 32 bits indirizzo IP. GetData(LWValue, "Local HMI", LW, 9600, 1) //Device 4's IP0 IPRead=LWValue GetData(LWValue, "Local HMI", LW, 9601, 1) //Device 4's IP1 IPRead=(IPRead*256)+LWValue GetData(LWValue, "Local HMI", LW, 9602, 1) //Device 4's IP2 IPRead=(IPRead*256)+LWValue GetData(LWValue, "Local HMI", LW, 9603, 1) //Device 4's IP3 IPRead=(IPRead*256)+LWValue // Leggo valore di IPMemo e confronto con valore IP letto. GetData(IPMemo, "Local HMI", LW, 0, 1) //IP memo if (IPRead <> IPMemo) then SetData(IPRead, "Local HMI", LW, 0, 1) //IP memo SetData(SBit, "Local HMI", LB, 10070, 1) end if end macro_command
Nel PLC SlimLine occorre realizzare un programma che trasferisce l’indirizzo IP nelle variabili acquisite dal data transfer dell’HMI. Conviene eseguire il programma in task Back, l’indirizzo IP potrebbe cambiare durante il funzionamento il server DHCP potrebbe inviare un nuovo indirizzo.
VAR i : UDINT; (* Auxiliary variable *) IPValue : ARRAY[0..3] OF BYTE; (* IP address value *) HMIIP AT %MW100.16 : ARRAY[0..3] OF UINT; (* HMI IP definition *) END_VAR // ***************************************************************************** // PROGRAM "SetHMIIPAddress" // ***************************************************************************** // Read some ETH0 informations. // ----------------------------------------------------------------------------- // ------------------------------------------------------------------------- // READ ETH0 INFORMATIONS // ------------------------------------------------------------------------- // Read the IP address and return it as a string. eTO_JUNK(SysOSIDValue(FALSE, OSID_ETH0_ACT_IP, ADR(IPValue))); FOR i:=0 TO 3 DO HMIIP[i]:=TO_UINT(IPValue[i]); END_FOR; // [End of file]
Sergio Bertana
Amministratore del forumVenendo al tuo programma vediamo le inesattezze, ce ne sono molte…
- Il TCPClient è sempre in connessione. Occorre connettersi solo quando si invia la eMail e sconnettersi al termine dell’invio. Se vedi nei ns esempi il comando di Connect è gestito dal FB di invio eMail TCPClient(Connect:=Mailer.Connect).
- Esegui un invio a raffica di tutti i comandi HELO, username, password. L’invio dei dati và fatto in risposta alle richieste del server.
- Terminato l’invio occore atendere l’Ok dal server e solo dopo inviare il comando di QUIT.
- Inviato il comando di QUIT occorre chiudere la connessione con il server.
- Ho visto che hai scritto SysFWrite e non Sysfwrite, siccome non hai errori di compilazione immagino che non hai settato il controllo maiuscole/minuscole nel progetto.
Riguardo all’errore:
i := Sysfwrite(Fp, ADR('EHLO smtp.office365.com' + CHR(13) + CHR(10)), Sysstrlen('EHLO smtp.office365.com' + CHR(13) + CHR(10)));
La funzione Sysfwrite, ha come 1° parametro l’indirizzo della stringa da inviare, come 2° parametro la lunghezza della stringa, come 3° parametro il numero di stringhe e come 4° parametro lo stream di comunicazione a cui inviare i dati. Quindi và scritta in questo modo:
eTO_JUNK(Sysfwrite(ADR('EHLO smtp.office365.com$r$n'), Sysstrlen(ADR('EHLO smtp.office365.com$r$n')), 1, Fp));
Ho sostituito i:= con la nuova definizione eTO_JUNK. Ho sostituito CHR(13) e CHR(10) con le rispettive codifiche $r e $n vedi articolo.
Attenzione alla capienza del buffer dello stream di comunicazione, nel TCP client hai definito il buffer di trasmissione a 256 caratteri ma la somma delle tue scritture Sysfwrite credo abbia dimensione maggiore quindi i caratteri che non ci stanno nel buffer vengono scartati.
Sergio Bertana
Amministratore del forumPer inviare l’eMail dal un sistema SlimLine devi utilizzare il FB EMailSend, puoi utilizzare l’esempio ST_EMailSend in fondo alla pagina. Colgo l’occasione della domanda per evidenziare alcuni punti.
- L’invio eMail senza crittografia TLS ormai non è quasi piu accettato dai servers SMTP, nel’esempio ho utilizzato Virgilio che sembra ancora accettare invio non crittografato.
- Per poter inviare eMail su qualunque server SMTP occorre utilizzare la crittografia come riportato nell’esempio ST_TLSEMailSend.
- In alternativa al FB SysTLSClient eseguire un programma mail relay su un PC, SlimLine invia al relay l’eMail non crittografata ed il relay l’inoltrerà al server pubblico crittografato.
- Per utilizzare come server di inoltro GMail occorre utilizzare la password generata dal servizio Password per le app. Rimando alla lettura di questa nota applicativa.
Sergio Bertana
Amministratore del forumI’ve rewritten your program in a form I supposed was correct but there’s two compiling errors.
VAR SquareArea : REAL; (* Square area *) SquarePerimeter : REAL; (* Square perimeter *) RectangleArea : REAL; (* Rectangle area *) RectanglePerimeter : REAL; (* Rectangle perimeter *) RForm : Form; (* Reference to Form *) MySquare : Square^; (* Reference to Square *) MyRectangle : Rectangle^; (* Reference to Rectangle *) END_VAR // Program initializations. IF (SysFirstLoop) THEN // MySquare:=SysMAlloc(SIZEOF(Square)); // MyRectangle:=SysMAlloc(SIZEOF(Rectangle)); MySquare^.Side:=10; //Square side MyRectangle^.Width:=20; //Rectangle width MyRectangle^.Height:=4; //Rectangle height END_IF; // Calculates area and perimeter. RForm:=MySquare^; //Reference to Form SquareArea:=RForm.Area(); //Square area SquarePerimeter:=RForm.Perimeter(); //Square perimeter RForm:=MyRectangle^; //Reference to Form RectangleArea:=RForm.Area(); //Rectangle area RectanglePerimeter:=RForm.Perimeter(); //Rectangle perimeter
I’ve to ask to Axel support how to manage this scenario.
I’ve a question, why you would dynamically instantiate the FBs? Is not easiest simply instantiate they in the data memory?
Maggio 15, 2025 alle 4:43 pm in risposta a: Invio eMail da dispositivo STE2 su server SMTP autenticato #82264Sergio Bertana
Amministratore del forumI prezzi sono visibili direttamente dal sito su registrazione, oltre ai prezzi è visibile anche la disponibilità del prodotto.
Il prodotto può inviare eMail su allarme senza bisogno di licenze, in questa nota applicativa si trovano le impostazioni per impostare come server SMTP di inoltro un qualsiasi account di GMail.
In aggiunta all’invio autonomo delle eMail il prodotto può essere connesso al portale HWg-Cloud senza bisogno di licenze mentre è prevista una licenza per il portale SensDesk (Vedi articolo).
Sergio Bertana
Amministratore del forumYes, also in LogicLab as in many modern IEC 61131-3 environments such as TwinCAT 3, Codesys v3.x, it is possible to dynamically assign a Function Block (FB) to a variable of interface type, provided that the FB implements that interface. The idea is that you can use an interface as an abstract type and dynamically assign an FB that implements it usually through pointer dereferencing (^) or a type cast.
You can download Polymorphism a LogicLab program with an example of use the polymorphism, essentially referring to the screenshot yo can see the Form interface that refer to a trigonometric figure that has two method prototypes.
- Area: This method calculates the area of the form
- Perimeter: This method calculates the perimeter of the form
Then two FBs are been defined each implement the Form interface, with the code to calculate the perimeter and area of the relate trigonometric figure. In the FBReference program the RForm variable of Form interface type is istantiated. Then by referencing RForm to different trigonometric figure is possible to calculate perimeter and area of them.
Sergio Bertana
Amministratore del forumIn effetti LogicLab permette di definire la copia di variabili sia di tipo array che di tipo struttura, ma non permette operazioni di confronto.
Nelle nostre librerie è disponibile la funzione MemCompare che oltre ad eseguire la comparazione tra qualsiasi tipo di variabile se il parametro Update è attivo esegue anche la copia del valore della variabile definita in Source nella variabile definita in Destination.
Quindi il tuo programma diventerà:
VAR ChkOk : BOOL; MyStruct1 : MYSTRUCT; MyStruct2 : MYSTRUCT; END_VAR MyStruct2:=MyStruct1; ChkOk:=NOT(MemCompare(ADR(MyStruct2), ADR(MyStruct1), SIZEOF(MyStruct2), FALSE));
Maggio 13, 2025 alle 10:16 am in risposta a: Acquisizione in corrente 4-20mA su ingressi analogici modulo CPU #82216Sergio Bertana
Amministratore del forumSu qualsiasi sistema gli ingressi in mA in realtà sono in volt in quanto i convertitori A/D convertono segnali in volt. Quindi per acquisire una corrente si utilizza una resistenza di shunt di valore predefinito e si acquisisce la tensione ai sui capi rilevando la corrente con la legge di Ohm (I=V/R).
Sui nostri moduli CPU SlimLine gli ingressi sono 0-10Volt quindi inserendo una resistenza di shunt da 500Ohm avremo alla corrente di 20mA i 10Volt in ingresso. Se guardi alla definizione AI_MODE del parametro Mode del FB SysGetAnInp vedrai che le acquisizioni in corrente non sono possibili sui moduli CPU. E se le imposti nel programma il FB SysGetAnInp avrà l’uscita di Fault sempre attiva. Con il comando SysLog vedrai il messaggio: User program error:9983200 e dalla pagina errori:
9983200 Il modo acquisizione definito in “Mode” non è corretto SysGetAnInp.
Venendo alla tua necessità di acquisire un trasduttore 4-20mA con gli ingressi analogici del modulo CPU dovrai mettere tra l’ingresso e massa una resistenza da 500Ohm ed acquisire l’ingresso in tensione 0-10Volt. Potrai con la funzione ValueScale trasformare i 2-10Volt acquisiti dall’ingresso nella grandezza ingegneristica rilevata dal trasduttore.
Sergio Bertana
Amministratore del forumLe versioni dei firmware sui moduli sono aggiornate…
Ma riguardando il tuo programma mi sono accorto che nella task Slow esegui una ventina di acquisizioni analogiche (FB SysGetAnInp), come riportato nel capitolo Multiplexing acquisizione del FB l’acquisizione analogica sul bus di estensione richiede un tempo variabile tra i 300-500uS quindi di media occorrono circa 8mS per acquisire tutti i moduli, considerando che il task Slow viene seguito ogni 10mS re dovendo anche gestrire sia gli I/O logici che le uscite PWM si rischia di saturare completamente il tempo e non lasciare più tempo per eseguire la task Back.
Quindi il consiglio è di sequenziare le acquisizioni analogiche tanto è inutile eseguirle ogni 10mS visto che il tempo di conversione A/D è di minimo 60mS e che quando si acquisiscono temperature le variazioni sono molto lente. Alcune informazioni aggiuntive:
- Mi sono perso l’inizio della storia, questo programma inizialmente funzionava e poi ha smesso di funzionare, oppure si hanno sempre avuti problemi fin dall’inizio.
- Le uscite PWM che hanno problemi sono sempre e solo quelle del modulo di estensione oppure sono anche quelle del modulo CPU.
- Puoi inviare al supporto tecnico il file di log del sistema. Lo trovi nella cartella C:\System e lo puoi scaricare via FTP.
- Puoi verificare i tempi di esecuzione delle varie tasks. Puoi mettere in debug la variabili con i tempi presenti in libreria.
Sergio Bertana
Amministratore del forumHo analizzato il programma che hai mandato è molto complesso quindi mi è difficile capire cosa succede ma ho notato alcune cose:
Hai eseguito in task Fast il programma CYCLE che gestisce l’abilitazione delle tue FB di gestione PWM le quali però sono eseguite in task Slow. Questa soluzione non dà vantaggi perchè i tempi di gestione del PWM sono comunque quelli della task Slow. Inoltre gestire variabili cross task con la task Fast può nascondere delle insidie la task Fast non gestisce l’immagine di processo sulle variabili. Quindi il primo consiglio è di spostare il programma CYCLE in task Slow.
Puoi dirmi la versione del firmware del modulo CPU e la versione del firmware del modulo di I/O che gestisce il PWM. Per visualizzare la versione devi connetterti in telnet ed utilizzare il comando SLineInfos.
Quando dici “Talvolta almeno una delle uscite PWM non si ferma anche se Frequency e Duty sono a zero” intendi che non si ferma immediatamente oppure che continua a generare l’uscita.
Quando dici “Talvolta almeno una delle uscite PWM non parte anche se Frequency e Duty sono diversi da zero” in questo caso il valore di Frequency è comunque maggiore del valore minimo gestito da quel canale PWM. I valori li trovi qui nel capitolo “Moduli supportati”.
Per completezza e rifacendomi al cast delle variabili ho visto che la compilazione del tuo programma genera 260 warnings. Certo le warnings solitamente non generano problemi ma attenzione warning come questa
P_OB1(58) - warning G0082: DUTY_C_M3 => Writing a signed value into an unsigned variable
Satnno ad indicare che stai copiando il valore di una variabile con segno nel tuo caso di tipo INT in una variabile senza segno nel tuo caso UINT. Ora se il valore della variabile INT diventa negativo (Ad esempio -1) il valore della variabile UINT divente 65535. Quindi in questi casi bisogna essere certi che la variabile con segno non diventi mai negativa.
Maggio 8, 2025 alle 10:09 am in risposta a: Computo ore funzionamento dispositivi con FB TimeBalancing #82161Sergio Bertana
Amministratore del forumQuindi con selettore in AUT dovrai abilitare il relativo bit in DEnable così da permettere al FB di gestire la pompa in base alla richiesta.
Con il selettore in MAN dovrai disabilitare il relativo bit in DEnable così da impedire al FB di gestire la pompa. E poi dovrai gestire l’incremento del tempo utilizzando l’ingresso che indica lo stato di contatore attivo.
Sergio Bertana
Amministratore del forumLa soluzione è facilmente realizzabile utilizzando il FB HTTPClient. Ti posto il programma HTTPLIAcquire che fà quello che ti serve.
PROGRAM HTTPLIAcquire VAR IRequest : BOOL; (* Input request *) CaseNr : USINT; (* Program case *) Errors : UDINT; (* Execution errors *) APtr : PVOID; (* Auxiliary pointer *) Page : STRING[ 512 ]; (* Page string *) LInput : ARRAY[0..1] OF BOOL; (* Input status *) TCPClient : SysTCPClient; (* TCP client management *) HTTPRq : HTTPClient_v5; (* HTTP client *) END_VAR // ***************************************************************************** // PROGRAM "HTTPLIAcquire" // ***************************************************************************** // Acquisizione ingressi via HTTP. // ----------------------------------------------------------------------------- // ------------------------------------------------------------------------- // INITIALIZATIONS // ------------------------------------------------------------------------- // Program initializations. IF (SysFirstLoop) THEN // Set TCPClient parameters. TCPClient.PeerAdd:=ADR('127.0.0.1'); //Peer address TCPClient.PeerPort:=80; //Peer port TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address TCPClient.LocalPort:=0; //Local port TCPClient.FlushTm:=50; //Flush time (mS) TCPClient.LifeTm:=20; //Life time (S) TCPClient.RxSize:=512; //Rx buffer size TCPClient.TxSize:=512; //Tx buffer size // Set HTTPClient parameters. HTTPRq.SpyOn:=TRUE; //Activate the spy HTTPRq.KeepAlive:=FALSE; //HTTP keep-alive HTTPRq.RMethod:=HTTP_REQUEST#HTTP_GET; //HTTP request method HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname HTTPRq.Page:=ADR('InputRead.html'); //Web page HTTPRq.Request:=eNULL; //Request string HTTPRq.Header:=eNULL; //HTTP header HTTPRq.DBSize:=512; //Data buffer size HTTPRq.Timeout:=T#10s; //Execution timeout END_IF; // ------------------------------------------------------------------------- // MANAGE CONNECTION // ------------------------------------------------------------------------- // Manage the connection. TCPClient(Connect:=HTTPRq.Connect); //TCPClient management HTTPRq(Enable:=TRUE, File:=TCPClient.File); //HTTP client IF (HTTPRq.Fault) THEN CaseNr:=0; END_IF; // ------------------------------------------------------------------------- // PROGRAM SEQUENCIES // ------------------------------------------------------------------------- // Program sequencies. CASE (CaseNr) OF // --------------------------------------------------------------------- // Initialize the time to manage acquisition delay. 0: HTTPRq.Send:=FALSE; //Send request IF NOT(IRequest) THEN RETURN; END_IF; eTO_JUNK(Sysmemset(ADR(Page), 0, SIZEOF(Page))); //Empty page string IRequest:=FALSE; //Input request HTTPRq.Send:=TRUE; //Send request CaseNr:=CaseNr+1; //Program case // --------------------------------------------------------------------- // Trasferisco in "Page" i dati ricevuti. 1: IF ((HTTPRq.DBChars <> 0) AND HTTPRq.HPSelector) THEN IF ((Sysstrlen(ADR(Page))+HTTPRq.DBChars) < SIZEOF(Page)) THEN eTO_JUNK(Sysmemmove(TO_DWORD(ADR(Page))+Sysstrlen(ADR(Page)), HTTPRq.DBAddress, HTTPRq.DBChars)); END_IF; END_IF; // Sul "Done" terminato ricezione risposta. // Controllo se ricevuto dati dal server. IF NOT(HTTPRq.Done) THEN RETURN; END_IF; CaseNr:=0; //Program case IF NOT(HTTPRq.PLoad) THEN Errors:=Errors+1; RETURN; END_IF; // Sul "PLoad" in "Page" ho la stringa ricevuta "&0&0&2&1&1&". // Decodifica valori ricevuti, qui posso fare tutti i controlli. // Intanto se s tringa sempre stessa lunghezza la posso verificare. IF (Sysstrlen(ADR(Page)) <> 11) THEN Errors:=Errors+1; RETURN; END_IF; // Se stringa prima del 1° dato è sempre "&0&0&2&" per leggere il dato. APtr:=SysStrFind(ADR(Page), ADR('&0&0&2&'), FIND_GET_END); IF (APtr = eNULL) THEN Errors:=Errors+1; RETURN; END_IF; IF NOT(SysVsscanf(APtr, ADR('%d'), BOOL_TYPE, ADR(LInput[0]))) THEN Errors:=Errors+1; RETURN; END_IF; IF NOT(SysVsscanf(APtr+2, ADR('%d'), BOOL_TYPE, ADR(LInput[1]))) THEN Errors:=Errors+1; RETURN; END_IF; END_CASE; // [End of file]
Per provarlo ho caricato una pagina con la stringa dfi testo direttamente nella cartella Web dello SlimLine e quindi mi sono connesso al localhost 127.0.0.1. Nel tuo programma dovrai modificare:
TCPClient.PeerAdd:=ADR('192.168.100.101'); //Peer address HTTPRq.Page:=ADR('input.cgi#'); //Web page
-
AutorePost