Vai al contenuto
PLC Forum


Esempi Libnodave


Messaggi consigliati

Inserito:

Salve a tutti chiedevo se qualcuno di voi aveva esempi di utilizzo della DLL LibNoDave per la comunicazione tra plc Siemens e PC, in particolare dovrei interfacciarmi a un S7-200 da un PC tramite porta seriale, pertanto chiedevo se qualcuno di voi ha un esempio in Visual Basic, anche perchè nella documentazione allegata alla dll (versione 0.8.2) ho trovato ben poca roba, tra cui un presunto modulo VB che però si vede che è stato fatto per interfacciarsi con il pacchetto Office quindi lascio immaginare a voi che casino micidiale possa essere.

Vi ringrazio per la disponibilità


  • Risposte 53
  • Created
  • Ultima risposta

Top Posters In This Topic

  • Bruno

    17

  • Adriano71

    15

  • tavazzi

    7

  • varg

    5

Top Posters In This Topic

Inserita:

Avevo visto anchio quell'esempio infatto l'ho scaricato e a tempo perso "lavoro permettendo" lo sviscero...

Comunque ho notato che stai facendo un Esempio in VB6 perfetto se serve una mano in qualche modo mi offro. Almeno mi auguro che questo esempio possa diventare un aiuto per tutti, Me compreso.

Chiedo una cosa il tuo esempio a cui stai lavorando su che plc e che tipo di connessione si basa ?

Inserita:

Ho appena messo l'esempio nell'area up-download files sezione Home-Visual Basic.

E' la prima realese di un software didattico (deve essere semplice non deve essere pensato per un impianto),

è pensato per connettersi ad un PLC per volta e basta, utilizzando una delle seguenti modalità:

PG adapter Seriale per S7-300 - MPI for S7 300/400

PG adapter Seriale per S7-300 - MPI for S7 300/400, "Andrew's version"

PG adapter Seriale per S7-300 - MPI for S7 300/400, Step 7 Version, not yet implemented

PG adapter Seriale per S7-200 - PPI for S7 200 (utilizzare baud-rate 9600) (non testato in quanto mi manca PLC)

Adattatore Seriale per S5 - S5 via programming interface (non testato in quanto mi manca PLC)

Interfaccia PG-PC Siemens - S7 using Siemens libraries & drivers for transport

Scheda Ethernet - ISO over TCP

- MPI with IBH NetLink MPI to ethernet gateway

- PPI with IBH NetLink PPI to ethernet gateway

I metodi di collegamento in grassetto sono stati testati, gli altri sto aspettando l'occasione.

Per quanto riguarda la modalità ISOonTCP ho una dozzina di impianti funzionanti senza problemi, gli altri sono test in laboratorio ma con successo.

Se notate qualcosa contattatemi pure attraverso PLC Forum o all'indirizzo **[at]**[dot]it, fra poco (ho già un progetto funzionante) pubblicherò un esempio stand-alone sviluppato in C# (utilizzo framework 2.0 e come IDE SharpDevelop e Visual C# Express 2005).

BR1

Inserita:

Dimenticavo nel caso di S7-200 il numero DB deve essere messo a 1....

ciao

Inserita:

Perfetto, siccome dispongo di un S7 200 Con Protocollo PPI provvedo a testare io e ti so dire qualcosa.

Inserita:

Ciao Bruno, credo di aver chattato con te un po' di tempo fa' a proposito di un discorso simile : comunicazione Pc-PLC via rete usando il protocollo Fetch-Write e usando Delphi sul lato PC. Avevo usato un tuo esmpio Visual Basic e tutto e' andato a buon fine. Mi e' rimasta pero' la curiosita' di capire se quella strada era realmente vantaggiosa rispetto a ProDave (anche perche' credevo che questo non andasse con Ethernet, mentre invece scopro il contrario). Hai fatto qualche valutazione/test su quantita' dati scambiabili, affidabilita', complessita', ecc. ?. Ciao e grazie in anticipo, Sergio Tavazzi

Inserita:

Ciao

Mi ricordo dello scambio di informazioni fatte...

Per quanto riguarda ProDave si funziona anche in Ethernet (solo dall'ultima release la 6.0), ma io non l'ho abbracciata in quanto per installarlo devi pagare il pacchetto runtime (uno per ogni installazione !)

L'esempio in questione utilizza le librerie LibNoDave (che non sono Siemens !!!) che sono opensource e rilasciate sotto licenza LGPL per cui puoi sviluppare in tuo protocollo e installarlo come vuoi.

Come detto anche in altri messaggi è che oltre alla tipologia di licenza tu puoi preparare un protocollo di comunicazione che funziona con diversi tipi di PLC (S5, S7-200, S7-300) e diversi tipi di interfaccia.

Per quanto riguarda limitazioni: il pacchetto base della Siemens non può essere più lungo di 220 byte, ma nessuno ti vieta di preparare funzioni (tipo ReadPLC o WritePLC) che si occupano dello spacchettamento e rincompattamento dei messaggi.

La funzionalità del protocollo Fetch/Write (che era nato per S5) è limitato a scambi di word (e non byte) e richiede una configurazione sul lato PLC (abilitare il protocollo e configurare le porte socket per la connessione), nella modalità Ethernet ISOonTCP puoi accedere sia a Byte che a Word e non si richiede nessuna consifurazione sul PLC (il protocollo è sempre abilitato e la porta socket è la 112).

Spero di essere stato chiaro.

Ciao

P.S.

Fate attenzione LibNoDave NON è Prodave, quest'ultimo è un pacchetto Siemens con autorizzazione da comperare e consegnare con il software al cliente!!!

Inserita:

Chiarissimo, ma' hai anche potuto valutare la quantita' max di dati (bytes o words, non importa) scambiabili x secondo ?. Ciao e grazie, Sergio Tavazzi

Inserita:

Naturalmete le tempistiche dipendono dal tipo di collegamento (Interfaccia seriale, interfaccia USB, CP integrata, Ethernet) e se tieni sempre aperta la connessione o se la chiudi e la riapri ogni volta.

Nell'esempio che trovi nel sito (quello sopra citato), visualizzo il tempo (in millisecondi) dell'operazione eseguita (un esempio tra i 70 e 90 msec per leggere 150 bytes via ethernet dalla mia postazione al PLC passando per tre switch in cascata).

Ho fatto delle prove con il PLC virtuale della Siemens ed è stato molto bello (velocissimo).

Fammi sapere se le prove ti vanno a buon fine.

Per Varg se riresci a provare con S7-200 fammi sapere se funziona, per gli altri in settimana mi arriva un S5 e vi saprò dire se leprove sono OK.

BR1

Inserita:

S7-200 Collegamento PPI

Connessione: OK

Stop: OK

Run: OK

Provato a leggere DB (Byte): OK

Provato a scrivere DB (Byte): OK

Provato a leggere DB (Byte) + Elementi: OK

Faccio altre prove appena possibile.

Saluti

Inserita:

Grazie Varg dei test....

scusami non ho capito la dichiarazione:

Provato a leggere DB (Byte) + Elementi: OK

cosa intendi ?

Ciao

Inserita:

Si scusami, intendevo che ho provato ad aumentare il campo elementi per ricevere in un unico colpo più DB appena riesco faccio un paio di test anche per i Merker

  • 2 weeks later...
Inserita:

X Bruno,

scusa se non ho capito bene, ma hai scritto che con protocollo S5 su TCP/IP c'e' una limitazione a 220 byte.

Ebbene, io e' parecchio tempo che uso C# 2005 Express in connessione con piu' socket asincroni verso CP343Lean e la limitazione non esiste, ossia:

1. alla richiesta la CP343 ti risponde con un pacchetto lungo non piu' di 220 byte.

2. dopo averli letti, rilancio una chiamata callback per leggere i restanti.

Io normalmente leggo anche 10Kbyte su DB con una sola richiesta.

Se vuoi info fammi sapere.

Saluti

Inserita:

Forse non mi ero spiegato bene...

la limitazione a cui mi riferivo era sul pacchetto usato nelle librerie LibNoDave, mentre immagino che tu ti riferisca al protocollo Fetch/Write, che comunque anch'esso in passato aveva una quantità limitata di BYTE per ogni scambio (superata con schede che permettevano i messaggi lunghi).

Non penso che sia possibile la lettura di messaggi lunghi 10KB con il pacchetto dati di LibNoDave.

Toglimi una curiosità: cosa in tendi per "rilancio una chiamata callback per leggere i restanti" ? Controlli il buffer del socket via polling o ti alzi un evento ?

Ciao

BR1

Inserita:

Scusa x il ritardo, ma ero ad una messa in servizio...

Dunque, io utilizzo C# 2005 Express, e mi sono generato due classi. Una per leggere ed un'altra per scrivere (su plc Fetch/write).

Per quanto riguarda la lettura, passo indirizzo e poi avvio la lettura ciclica, ossia:

1. la S7Read, prima apre connessione a socket remoto.

2. se connessione ok, lancia richiesta di lettura dati e genera funzione di callback (così chiamata in C#, simile ad evento).

3. quando il callback mi risponde, se dati validi, controllo lunghezza buffer.

Se buffer.lenght minore della richiesta, memorizzo lunghezza attuale e buffer dati, dopodiche rilancio chiamata callback. (in questo modo non appena mi arrivano i restanti, riesegue il punto 3 fino ad ottenere lunghezza richiesta).

Se buffer.lenght uguale lunghezza richiesta, analizzo intestazione messaggio S5.

4. dopo lettura valida, ho un eventuale timer per ritardo nuova richiesta di lettura, oppure rilancio immediatamente la chiamata ad un'altra lettura.

In questo modo, da plc mi copio in un DB tutti i dati che il pc deve leggere (es. 2kb)

Ad avvio software PC inizializzo come da procedura sopra, e la classe indicata, mi genera un evento quando ha letto tutti i dati. Poi automaticamente si rilancia.

NO, non utilizzo il pooling, in questo modo il PC è libero di gestire la grafica e l'HMI.

La classe write, è ancora più bella, lancio la funzione di inizializzazione con indirizzo, porta, blocco dati, lunghezza e array di byte, e la classe mi rilancia un evento di stato fine scrittura.

Se ne lancia per esempio 10 in contemporanea, automaticamente ne lancia uno dopo l'altro.

Se ti interessa, posso fare un copia ed incolla della classe read e write.

Saluti Adriano

Inserita:

Scusa se mi intrometto, ma a me quel copia e incolla interesserebbe molto ... . Mi piacerebbe anche capire qual'e' il rendimento effettivo di tutto il marchingegno. Per es. se mi dici che trasferisci 2 k (supponiamo in ambo i sensi), quante volte riesci a farlo al minuto ? ogni altro esmpio e' benvenuto purche' si riesca a quantificare un po' la resa del sistema. Grazie in anticipo, Sergio

Inserita: (modificato)

QUESTA e' la classe S7SockerRDClass

Piu' avanti trovate come richiamarla dal supervisore

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace xxxxxx
{
    public delegate void S7SocketRDEvent(object sender,byte nDB,int Offset,int nByte, byte[] Values);
    public delegate void S7SocketStateEvent(object sender,byte nDB,int Offset,int nByte,bool Stato,int ID);
    
    
    public struct SocketData
    {
        public int Lunghezza_Richiesta;
        public int Lunghezza_Attuale;
        public byte[] buffer;
    }

    class S7SocketRDClass
    {
        #region Variabili
        public event S7SocketRDEvent S7DataIncoming;
        public event S7SocketStateEvent S7StateChange;
        IAsyncResult m_result;
        public AsyncCallback m_pfnCallBack;
        public Socket m_clientSocket;
        public byte[] Values=new byte[2050];
        public int lunBuff;
        public bool OK;
        public bool iConnected = false;
        public bool Disconnect;
        public byte nDB;
        public int OffsetDB;
        public int nByte;
        int nWord;
        public SocketData Ricezione;
        byte[] fetchResponse=new byte[11];
        public bool iEnable_Delay = false;
        public bool Busy = false;
        public int ID;
        public string ipAddr;
        public string portNumber;
        #endregion

        #region Timer
        System.Timers.Timer timer1 = new System.Timers.Timer(1000);

        public void InitTimer()
        {
            timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed);
            timer1.AutoReset = true;
            timer1.Enabled = false;
        }

        public void TimerDelay(int interval)
        {
            timer1.Interval = interval;
        }

        void timer1_Elapsed(object source, System.Timers.ElapsedEventArgs e)
        {
            timer1.Enabled = false;
            WaitForData();
        }

        public void EnableTimer(bool Stato)
        {
            timer1.Enabled = Stato;
        }
        
        public bool Enable_Delay
        {
            get { return iEnable_Delay;}
            set 
            {
                iEnable_Delay = value;
                if (iEnable_Delay & Connected & !Busy)
                {
                    InitTimer();
                    WaitForData();
                }
            }
        }

#endregion
        
        #region Gestione connessione

        public void CloseConnection()  
        {
            //Termina la connessione e distrugge il socket
            if (m_clientSocket != null)
            {
                m_clientSocket.Close();
                m_clientSocket = null;
                Connected = false;
                
            }
        }

        public void OpenConnection(string IPAddr, string PortNumber)
        {
            Ricezione.buffer = new byte[9999];
          
            //Apre la connessione creando il socket
            //Se non ho passato tutti parametri, sospendo
            
            if (IPAddr == "" || PortNumber == "")
            {
                Connected = false;
                return;
            }
            ipAddr = IPAddr;
            portNumber = PortNumber;
            try
            {
                //Creo istanza al socket
                m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //Converto indirizzo IP e numero porta
                IPAddress ip = IPAddress.Parse(IPAddr);
                int iPortNo = Convert.ToInt16(PortNumber);
                //Creazione ENDPOINT
                IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);
                //Imposto timeout
                
                //Creo connessione con EndPoint
                m_clientSocket.Connect(ipEnd);
                //Verifico se connesso
                if (m_clientSocket.Connected)
                {
                    Connected = true;
                     WaitForData();
                }
                else
                {
                    Connected = false;
                }
            }
            catch (SocketException)
            {
                Connected = false;
            }
            //Inizializzo i valori del fetchResponse buffer
            fetchResponse[0] = 83;
            fetchResponse[1] = 53;
            fetchResponse[2] = 16;
            fetchResponse[3] = 1;
            fetchResponse[4] = 3;
            fetchResponse[5] = 6;
            fetchResponse[6] = 15;
            fetchResponse[7] = 3;
            fetchResponse[8] = 0;//0 se nessun errore
            fetchResponse[9] = 255;
            fetchResponse[10] = 7;
        }

    #endregion

        #region CallBack e classe SocketPacket

        void WaitForData()
        {
            try
            {
                Busy = true; 
                Invia();
                if (m_pfnCallBack == null)
                {
                    m_pfnCallBack = new AsyncCallback(OnDataReceived);
                }
                SocketPacket theSocPkt = new SocketPacket();
                theSocPkt.thisSocket = m_clientSocket;
                m_result = m_clientSocket.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, m_pfnCallBack, theSocPkt);
            } catch (SocketException)
            {
                Connected = false;
            }
        }
        
        public class SocketPacket
        {
            public Socket thisSocket;
            public byte[] dataBuffer = new byte[2050];
        }

        public void Invia()
        {
            try
            {
                Ricezione.Lunghezza_Attuale = 0;
                Ricezione.Lunghezza_Richiesta = nByte;
                
                nWord = nByte / 2;
                byte[] buffByte = new byte[16];
                //Creazione messaggio S5 (16 byte)
                buffByte[0] = 83;
                buffByte[1] = 53;
                buffByte[2] = 16;
                buffByte[3] = 1;
                buffByte[4] = 3;
                buffByte[5] = 5;
                buffByte[6] = 3;
                buffByte[7] = 8;
                buffByte[8] = 1;
                buffByte[9] = nDB;
                buffByte[10] = Convert.ToByte(OffsetDB / 256);
                buffByte[11] = Convert.ToByte(OffsetDB % 256);
                buffByte[12] = Convert.ToByte(nWord / 256);
                buffByte[13] = Convert.ToByte(nWord % 256);
                buffByte[14] = 255;
                buffByte[15] = 2;
                if (m_clientSocket != null) m_clientSocket.Send(buffByte);
                else Connected = false;
            }
            catch (SocketException) 
            {
                Connected = false; 
            }
        }

        public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                //Attivo il socket ID
                SocketPacket theSockId=(SocketPacket)asyn.AsyncState;
                

                //Termino lettura e leggo numero di byte ricevuti
                int iRx=theSockId.thisSocket.EndReceive(asyn);
                if (iRx + Ricezione.Lunghezza_Attuale != Ricezione.Lunghezza_Richiesta + 16)
                {
                    Values = theSockId.dataBuffer;
                    Array.Copy(Values, 0, Ricezione.buffer, Ricezione.Lunghezza_Attuale, iRx);
                    Ricezione.Lunghezza_Attuale += iRx;
                    m_result = m_clientSocket.BeginReceive(theSockId.dataBuffer, 0, theSockId.dataBuffer.Length, SocketFlags.None, m_pfnCallBack, theSockId);
                    return;
                }
                else
                {
                    Values = theSockId.dataBuffer;
                    Array.Copy(Values, 0, Ricezione.buffer, Ricezione.Lunghezza_Attuale, iRx);
                    Ricezione.Lunghezza_Attuale += iRx;

                }
                

                lunBuff=Ricezione.Lunghezza_Attuale;
                if (lunBuff == nByte  + 16)
                {
                    Values = Ricezione.buffer;
                    OK = true;
                    //Controllo con fetchResponse
                    for (int i = 0; i < 11; i++)
                    {
                        if (fetchResponse[i] != Values[i])
                        {
                            OK = false;
                        }
                    }
                }
                else
                {
                    OK = false;
                }
                if (OK)
                {
                    byte[] V=new byte[nByte];
                    Array.Copy(Values, 16,V,0,nByte);
                    S7DataIncoming(this, nDB, OffsetDB, nByte, V);
                }
                if (Disconnect)
                {
                    CloseConnection();
                    Disconnect=false;
                }
                else
                {
                    if (iEnable_Delay)
                    {
                        timer1.Enabled = true;
                    }
                    else
                    {
                        WaitForData();
                    }
                }
            } catch (SocketException)
            {
                Connected=false;
            }
            Busy = false;
        }


#endregion

        public bool Connected
        {
            get { return iConnected; }
            set
            {
                iConnected = value;
                S7StateChange(this, nDB, OffsetDB, nByte, iConnected,ID);
            }

        }
    }

    
}
Questi invece le procedure da seguire per richiamare la classe
S7SocketRDClass[] s7SK = new S7SocketRDClass[6];  //Dichiarazione di 6 differenti socket indicizzati
Inizializzazione.... mostro solo quella con indice 0
            for (int i = 0; i < 5; i++)
            {
                s7SK[i] = new S7SocketRDClass();
//Eventi che vengono richiamati quando ricevo dei dati validi, o quando lo stato passa
//da Online a Offline
                s7SK[i].S7DataIncoming += new S7SocketRDEvent(s7Socket_S7DataIncoming);
                s7SK[i].S7StateChange+=new S7SocketStateEvent(s7Socket_S7StateChange);
            }
            s7SK[0].nDB = 103;
            s7SK[0].OffsetDB = 0;
            s7SK[0].nByte =1220; //Numero di byte da leggere
            s7SK[0].InitTimer();
            s7SK[0].TimerDelay(100);
            if (ipPLC == "127.0.0.1") s7SK[0].Enable_Delay = true;
            else s7SK[0].Enable_Delay = false;
            s7SK[0].ID = 1;
            s7SK[0].OpenConnection(ipPLC, "2000");
Se la variabile Enbled_Delay e' TRUE, al termine di una lettura attende il ritardo TimerDalay(ms) e ne rilancia un'altra. Se FALSE, appena finita la lettura, ne rilancia un'altra. Evento lanciato quando ho dei dati in lettura PS: siccome l'evento appartiene ad un diverso thread rispetto al form che lo intercetta, prestare attenzione a generare errori di cross-thread quando si aggiorna la grafica. Per info, se si genera errore vedere . di C#
private void s7Socket_S7DataIncoming(object sender, byte nDB, int Offset, int nByte, byte[] Values)
        {
            switch (nDB)
            {
                case 101:       //Dati ciclo
                    plc.DB101.Valori = Values;
                    break;
                case 102:       //Dati macchina
                    plc.RiempiDB102(Values);
                    break;
                case 103:       //Letture
                    plc.RiempiLetture(Values);
                    break;
                case 104:       //Funzioni
                    plc.DB104.Valori = Values;
                    break;
                case 107:       //Allarmi 
                    plc.RiempiAllarmiRes(Values);
                    break;
            }
        }
Eventi lanciato in caso di variazione ONLine - OFFLine
private void s7Socket_S7StateChange(object sender, byte nDB, int Offset, int nByte, bool State,int ID)
        {
  //State=true ==> ONLINE
//State=false ==> OFFLINE          
}
Ricordate di nel FormClosed di mettere
s7SK[0].Disconnect = true;

Per quanto riguarda la tua domanda, ti confermo e' tranquillamente possibile eseguire letture di blocchi di 2kb al piu' in 1/10 sec. (in WiFi ottieni un valore simile)

Nel mio protocollo non ho preso in considerazione la lettura di M, E, A, ecc. SOLO DB

PC utilizzato: P4 512Mb scheda ethernet 100mb. PLC CPU3152DP (versione 2AG10) e CP343-Lean

Seguira' classe di scrittura verso PLC

Modificato: da Gabriele Corrieri
Inserita:

Dimenticavo, il numero di DB non puo' essere superiore a 255!

Nella 343 impostare come tempo di keep alive il valore di 10secondi.

Se si arresta il programma, aspettare almeno 10 secondi per rilanciarlo, in quanto la CP343 deve far "cadere" le connessioni precedenti.

Inserita:

Veramente notevole .. .Dimmi una cosa : le connessioni aperte sono sempre 2 (Fetch e Write), oppure come mi e' parso di capire, sono 6 come i sockets ? forse la domanda puo' sembrare stupida, ma preferisco chiarire bene tutto. Ciao , Sergio

Inserita:

Io nel PLC ho aperto 6 connessioni in FETCH dalla porta 2000 alla 2005.

Per quanto riguarda le Write ne uso 1 sulla porta 3000

Inserita:

Scusa, ma sono un po' imbranato con i forum, e dopo aver dato l'invio mi ricorda qualcos'altro.

Ebbene, io nel PLC leggo sempre 6 DB da DB101 a DB106.

Nella DB103 ho i valori di quote, tempi stati ecc. che mi occorrono abbastanza spesso (aggiornate almeno 5 volte al secondo), quindi imposto la classe per funzionare senza ritardi (vedi appunto precedente)

Le altre DB invece le imposto con ritardi differenti (per esempio la lettura degli allarmi la eseguo ogni secondo).

Siccome tutte le classi mi generano lo stesso evento, una volta lanciate (openconnection) quando ricevo l'evento leggo il numero di DB e copio i valori nell'array appropriato.

Nel resto del programma poi mi vado a leggere i valori direttamente nell'array (gli array anno lo stesso nome del DB: es. byte[] DB103=new byte[2048];)

Per girarti tutti i byte (da cpu PLC a INTEL) utilizzo la classe Marshal.

Inserita:

E' comparsa una faccina nel messaggio, volevo scrivere "puntoevirgola", "parentesichiusa"

Inserita:

Ciao

Ho visto il codice e immaginavo che usassi il protocollo FETCH/WRITE (sempre ottimo), anche io l'ho usato per un po', ma al momento sono stato attratto dalle librerie LibNoDave.

Ti consiglio di testarle (se le scarichi dal sito vi trovi anche un esempio in C# e funzionano), a prima vista potrebbe sembrare un poco più complesso ma hanno i loro vantaggi:

1) Non necessitano di nessuna configurazione sul PLC (non male quelle volete che vuoi leggere dati su un altro PLC)

2) Con lo stesso protocollo riesci a gestire diversi hardware (es: Ethernet e MPI)

comunque posta anche l'altra classe, lo scambio del software è sempre una cosa molto valida.

(Al massimo puoi mettere l'esempino nell'apposita area)

Ciao

BR1

Inserita:

Scusa, ma sono un novello del forum, come faccio ad allegare dei file?

Crea un account o accedi per commentare

Devi essere un utente per poter lasciare un commento

Crea un account

Registrati per un nuovo account nella nostra comunità. è facile!

Registra un nuovo account

Accedi

Hai già un account? Accedi qui.

Accedi ora

×
×
  • Crea nuovo/a...