Vai al contenuto
PLC Forum


Snap7 E Vc++


Messaggi consigliati

Mi sono posto il problema degli errori che si possono commettere dovendo analizzare/comandare un bit all'interno di un byte o word, con l'utilizzo della libreria di Davide (grazie ancora, non smetterò mai di ringraziarti !!); probabilmente sono io che sono un po' distratto, ma, come dicevo, se devo leggere I2.1 dal PLC, normalmente non è conveniente (anche perchè sicuramente non sarà l'unico) leggere il solo bit, ma un byte o word o doppia word intere; quindi leggo, ad esempio, una word intera e poi, per analizzare il singolo bit, devo shiftare e poi fare una and con 0x01 eccetera, con la possibilità di errori, soprattutto se gli ingressi sono parecchi; la stessa cosa vale per il comando di un singolo bit.

Così mi sono scritto queste semplici funzioni che vorrei condividere con voi; le ho già provate, ma, in ogni caso, si accettano suggerimenti e/o critiche:

BOOL bGetPLCBit(const longword lwData, byte byByte, byte byBit)
{
    if (byByte > 3 || byBit > 7)
        return FALSE;

    byte byFlag = pow(2, byBit);

    return ((lwData >> (8 * byByte)) & byFlag);
};

Queste "estrae" lo stato da un byte, word, dword (anche se è dichiarata una dword dunziona benissimo lo stesso), ritornando un bool; come parametri occorre fornirgli byte e bit (non quelli diretti degli, ad esempio, ingressi, ma 0 per il primo byte, 1 per il secondo e così via della variabile utilizzata).

BOOL bSetPLCBit(byte* pData, byte byBit)
{
    if (byBit > 7)
        return FALSE;

    byBit = pow(2, byBit);

    *pData |= byBit;

    return TRUE;
};

BOOL bSetPLCBit(word* pData, byte byByte, byte byBit)
{
    if (byByte > 1 || byBit > 7)
        return FALSE;

#pragma pack(1)

    typedef struct
    {
        union
        {
            struct
            {
                byte b1, b2;
            } bb;
            
            word wValue;
        };
    }SW;

 #pragma pack()

    byte byFlag;
    SW     swResult;

    byFlag = pow(2, byBit);

    swResult.wValue  = *pData;

    if (byByte == 0)
        swResult.bb.b1 |= byFlag;
    else
        swResult.bb.b2 |= byFlag;
            
    *pData = swResult.wValue;

    return TRUE;
};

BOOL bSetPLCBit(longword* pData, byte byByte, byte byBit)
{
    if (byByte > 3 || byBit > 7)
        return FALSE;

#pragma pack(1)

    typedef struct
    {
        union
        {
            struct
            {
                byte b1, b2, b3, b4;
            } bb;
            
            longword lwValue;
        };
    }LW;

 #pragma pack()

    byte byFlag;
    LW     lwResult;

    byFlag = pow(2, byBit);

    lwResult.lwValue  = *pData;

    if (byByte == 0)
        lwResult.bb.b1 |= byFlag;
    else if (byByte == 1)
        lwResult.bb.b2 |= byFlag;
    else if (byByte == 2)
        lwResult.bb.b3 |= byFlag;
    else if (byByte == 3)
        lwResult.bb.b3 |= byFlag;
            
    *pData = lwResult.lwValue;

    return TRUE;
};

Queste 3 invece settano un bit, sempre della variabile utilizzata; stavolta ce n'è 3 diverse a seconda della variabile utilizzata, per poter lasciare inalterati gli altri bit; come parametri sempre byte e bit della variabile; se invece di |= byFlag utilizziamo &= ~byFlag diventa un reset del bit.

Ne ho scritta anche un altra (non la posto, è praticamente uguale) per i soli ingressi: con il byte seleziono il byte da un array e con il bit il solito confronto; così se ho bisogno dell'input I2.1, scrivi proprio bGetPLCInput(2, 1) ed ottengo il mio stato (l'array è nella classe principale, condiviso con tutto il resto delle classi.

L'ultima riguarda la comunicazione e volevo chiedere lumi a Davide:

BOOL bPLCActionIsCompleted(TS7Client* pPS7Client)
{
    if (pPS7Client == NULL)
        return TRUE;

    if (!pPS7Client->Connected())
        return TRUE;

    int nOPResult;

    return pPS7Client->CheckAsCompletion(&nOPResult);
};

Mi sono accorto (c'è anche scritto sulla documentazione) che se si inviano troppi comandi, l'ultimo va in errore (Job Pending); con questa semplice funzione (inserita in un ciclo di attesa con timeout) si ritarda l'esecuzione del comando; quello che mi chiedo è come mai funziona anche con la comunicazione sincrona, mentre invece CheckAsCompletation è per la asincrona ?

Da prove effettuate funziona benissimo e, come dicevo, mi previene l'eventuale errore di Job Pending, quindi ...

Spero possa essere utile.

Modificato: da Gabriele Riva
Inseriti i tag "CODE"
Link al commento
Condividi su altri siti


Ciao, ottimo lavoro ;)

E' sicuramente più comodo leggere i dati a pacchetti e poi estrarre le varie informazioni.

Avevo in programma di implementare in questa versione delle funzioni helper ma poi non ho avuto tempo e c'erano comunque dei bugfix da pubblicare.

Mi sento padrone di fare uso e abuso del tuo codice :D

Qualcosa del genere l'ho implementata in Moka7 : assegnato un buffer di byte generico ci sono dei metodi di una classe helper che permettono di leggere/scrivere tutti i dati primitivi di S7 in qualunque posizione.

Li erano obbligatori, Java non permette di avere struct da riempire con dei dati, in questo modo si lavora sempre con un' Array di byte ed è tutto più semplice.

Non ho i sorgenti sotto mano, vado a memoria, CheckAsCompletion funziona sempre.

Dato che tu puoi usare funzioni sincrone e asincrone a piacimento per lo stesso client, la libreria "non sa", quando chiami CheckAsCompletion se la funzione precedente era sincrona o asincrona, lei ti restituisce sempre lo stato del Client (occupato o libero) ovviamente dopo una funzione sincrona avrai sempre il Client libero dato che questa termina solo quando è completa.

Inoltre tu puoi usare (con le dovute cautele) i metodi dello stesso Client in vari thread contemporaneamente e CheckAsCompletion può tornare comoda.

Quando torno a casa comunque verifico ;)

Ne approfitto per fare gli auguri di buona Pasqua a tutto il forum !!!

Davide

Link al commento
Condividi su altri siti

Ciao, ottimo lavoro ...

Mi sento padrone di fare uso e abuso del tuo codice ...

Grazie, ma non lo sento "mio": è comunque un metodo imparato anni fa sui microprocessori da qualcuno mooolto più bravo di me, che, come me, ama condividere.

Pensavo addirittura di passare come parametro una stringa di testo del tipo "%I2.1", ma mi è sembrato un po' troppo eccessivo ...

CheckAsCompletion funziona sempre ...

Quando torno a casa comunque verifico ...

L'idea (devo ancora arrivarci, sono impantanato sulla grafica e sui testi delle lingue) è quella di utilizzare un solo thread molto veloce (massimo 2), poi, a seconda delle necessità, eseguire tutte le letture dal PLC, magari con tempi diversi a seconda delle priorità, utilizzando dei contatori all'interno del thread per differenziare il tutto; quando arriva la richiesta del PLC, invio un messaggio alla finestra principale, la quale espleterà le sue funzioni (caricamento numeri lotto, barcode, salvataggio report, etc.), ed infine dovrà scrivere la risposta; è a questo punto che mi serve una funzione del genere; avviso il thread che devo scrivere, poi con questa funzione in un loop con timeout, aspetto di poterlo fare e poi scrivo la risposta.

Ho provato questo metodo in una applicazione di test e sembra funzionare alla grande: niente "JobPending" e stiamo parlando di qualche centinaia di millisecondi massimo (le letture sono parecchie) che, per quello che devo fare io (c'è l'operatore che deve espletare una serie di operazioni, nel frattempo) sono nulla.

Se me lo verifichi, meglio ancora: sarà ancora più sicuro del risultato. :worthy:

Ne approfitto per fare gli auguri di buona Pasqua a tutto il forum !!!

Ricambio di cuore gli auguri e li estendo anch'io nuovamente a tutto il forum.

E grazie ancora per aver scritto una libreria così completa !! (l'ho detto che non avrei mai smesso di ringraziare !! :thumb_yello: :thumb_yello: :thumb_yello: )

Link al commento
Condividi su altri siti

Mi sono accorto adesso che Gabriele ha inserito i tag "CODE" nel copia/incolla del codice postato (a proposito: grazie !! :thumb_yello: ).

Non avevo notato la possibilità di utilizzarli: mi spiegate cosa sono e a cosa servono ? E' meglio utilizzarli ogni volta che si posta del codice ?

Grazie

Link al commento
Condividi su altri siti

Te lo confermo, CheckAsCompletion ritorna lo stato di job.Pending (la variabile interna settata all'inizio del Job e resettata alla fine).

Come detto dopo una funzione sincrona ritorna sempre true, dopo una asincrona ritorna true quando il Job è completo.

Si le velocità sono alte, motivo per cui parecchi mi scrivono che stanno abbandonando OPC server per Snap7. Lo svantaggio è che devi scrivere il codice di gestione a mano.

Se però consideri che anche con OPC devi scriverti la business logic della tua applicazione, alla fine non c'è un grosso risparmio a meno di non prevedere che "la stessa applicazione" debba girare con S7, AB, Omron ecc..

In alcune applicazioni non ci sono alternative, in una linea, per una transazione completa PLC->MS SQL->PLC abbiamo toccato 57 ms (erano comunque pochi dati, circa 128 byte) e li non c'era l'operatore, sopra i 200 ms saremmo stati fuori cadenza.

Saluti

Davide

Link al commento
Condividi su altri siti

Te lo confermo ...

Grazie mille, se avevo ancora dei dubbi, adesso me li hai tolti tutti.

Se però consideri che anche con OPC devi scriverti la business logic della tua applicazione ...

Infatti, non c'è il vantaggio con OPC, sono d'accordissimo con te; a meno di non dover comunicare senza ethernet, ma finora mi è capitato solo una volta con il 200 (quindi non potrà capitarmi più ...).

Si le velocità sono alte ...

... per una transazione completa PLC->MS SQL->PLC abbiamo toccato 57 ms ...

All'anima !! :thumb_yello: :thumb_yello: :thumb_yello:

Mi riprometto una cosa, così almeno qualcosa in cambio te la posso offrire: appena terminato 'sto benedetto lavoro (che tra l'altro mi sta facendo smadonnare come pochi solo a scriverlo, figurarsi quando sarò in macchina), do' una sistemata a tutte le mie "classi prototipo" che uso di solito (banner di allarme, gestione password e via dicendo) e cerco il modo di postarle qui sul forum, in modo che siano a disposizione di tutti.

L'unico problema è che scrivo ancora con le MFC (un po' per pigrizia, ma anche per sfiducia) e saranno da convertire ...

Ciao e grazie ancora.

Link al commento
Condividi su altri siti

  • 2 weeks later...

Mi sono dimenticato di una cosa che potrebbe essere importante : per utilizzare le librerie snap7 con VC++ 6 e 2008 con le MFC, occorre modificare 2 cose:

1 - La definizione di byte la segnala come doppia: io l'ho modificata come S7byte.

2 - Se si vuole importare nel progetto anche il file snap7.cpp (non credo sia indispensabile), occorre inserire in testa al file #include "stdafx.h", altrimenti segnala errori a go go.

Link al commento
Condividi su altri siti

Thanks ;) Lo inserisco nella documentazione della prossima release.

VC++ 6 non l'ho mai usato, per il 2008 ho creato una soluzione per ricompilare la libreria, ma non ho mai usato il wrapper+MFC.

Non mi è chiara la necessità di stdafx.h ma mi fido assolutamente :)

Link al commento
Condividi su altri siti

E' l'header file di base per le MFC: senza quello non ti fa fare nulla e devi inserirlo in ogni file di implementazione che utilizzi.

Link al commento
Condividi su altri siti

  • 3 months later...

Ciao a tutti,

il progetto è terminato (anche se l'avvio in produzione sarà solo a settembre) e devo dire che è un piacere "vedere" la comunicazione fra PC e PLC:

6d3fd136db0bcf75ec6556b3449c0273.jpg

I led che vedete nella foto riguardano lo stato delle stazioni e dei pezzi caricati sulla tavola e si accendono e cambiano colore in base allo stato proprio grazie allo Snap7 scritto da Davide, oltre a, ovviamente tutto il resto ... ; quindi ancora una volta:

GRAZIE DAVIDE !!

Link al commento
Condividi su altri siti

Un post del 21 Agosto per un lavoro in consegna a Settembre mi fa pensare che non sono l'unico poveraccio ad aver lavorato in questo periodo :lol: (*)

grazie allo Snap7 scritto da Davide, oltre a, ovviamente tutto il resto ...

Snap7 è solo una libreria di base, è tutto il resto che conta ;) , non esistono tool che fanno andare le macchine da soli.

Io ho lavorato sulla 2.0 di DataRelay, un softwarino che ci permette di chiamare query/comandi SQL direttamente da PLC senza scrivere una riga di codice, molto comodo quando ti porti a spasso un componente con un barcode/datamatrix non associato ad una memoria di transito (tipo moby). Ma soprattutto abbiamo trovato pace con lo storico di produzione gestito direttamente dal programmatore PLC.

A settembre esce Snap7 1.3 platform release, sono supportate Spark e Mips (entrambe big-endian).

Quella Spark va ad aumentare la presenza di Snap7 nei grossi rack di raccolta dati per i quali ho avuto molte richieste di chiarimenti. E' un fenomeno interessante, sembra che molte corporate non siano soddisfatte dell'eccessiva stratificazione della famosa piramide (quella che ha alla base gli impianti ed in cima l'ERP) e creano sempre più spesso dei "ponticelli di bypass" andando a raccogliere i dati (sostanzialmente di produzione) direttamente dai PLC e per migliorarne la velocità di fruizione.

Quella mips è divertente, ha fatto i test un matto (come noi :smile:) che usa Snap7 come plugin di OpenWRT, praticamente ora sulle macchine usa dei router che comunicano direttamente in modo autonomo con i PLC.

Buon divertimento ;) (facciamo di necessità virtù)

Davide

(*) Al contrario di qualcun'altro che ha postato su fb delle fantastiche foro "caraibiche" ( :P invidia !!)

Link al commento
Condividi su altri siti

Snap7 è solo una libreria di base, è tutto il resto che conta ...

Forse, ma tu l'hai condivisa e a me hai facilitato parecchio la vita, e per questo ringrazio ...

Il resto di cui parli, purtroppo, non lo conosco (o meglio lo conosco poco): vedrò di informarmi ;)

Link al commento
Condividi su altri siti

Ciao,

trovate una raccolta di funzioni di ausilio per la libreria Snap7; ho riunito tutte le funzioni riportate in questa discussione, più alcune che mi sembravano utili; in particolare (ed in previsione di una possibile modifica al mio progetto ;) ; prevedere, bisogna sempre prevedere ed arrivare preparati !!) ci sono due funzioni per scrivere e leggere un dato STRING per l'S7-1200 (non mi pare che il 300 lo preveda ...).

Deve essere previsto il puntatore per la classe TS7Client e qualche altra variabile, da dichiarare nell'applicazione (quindi nella classe CApp) che, tramite una dichiarazione extern è "visibile" anche nel file di implementazione dove si trovano le funzioni; sarebbe stato meglio costruire una classe derivata, ma il tempo, per ora, è tiranno ...

Ovviamente ci sono le spiegazioni sia in inglese che in italiano.

L'ho messo nella sezione "Linguaggi C" perchè mi sembra in tema ed anche un po' sguarnita ...

Link al commento
Condividi su altri siti

  • 3 years later...
flyitalia2004

Buongiorno , ho realizzato un progetto in VS per la lettura degli sforzi di Ben 26 Axis con  due 840Dsl 4.7 usando un Timer Asisncrono  in  30ms . La lettura non ha problemi è molto precisa  , il problema e che spesso si scollega . Per ovviare il problema leggo un bit per creare l'evento  prima di accedere al Canale e controllo se ho la connessione , altrimenti mi riconnetto. Non capisco come mai accada questo ! Ho fatto girare le letture con il sim dello Step e netplc  per interi giorni e non ha mai ceduto . Se avete consigli ve ne sarei grato. Buona Giornata

Link al commento
Condividi su altri siti

Ciao,

ma stai utilizzando Snap 7 ? Per VS intendi Visual Studio ? Quale linguaggio ?

Se il server si sconette dovrebbe segnalarti un errore; bypassa la riconnessione e cerca di capire se esiste un errore.

Se esegui più letture contemporaneamente può essere un problema; leggiti la guida di CheckAsCompletation, potrebbe esserti di aiuto.

Link al commento
Condividi su altri siti

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...