Vai al contenuto

Problema con funzione SysVarsnprintf

Home Forum Programmazione IEC 61131 (LogicLab) Problema con funzione SysVarsnprintf

Stai visualizzando 9 post - dal 1 a 9 (di 9 totali)
  • Autore
    Post
  • #35808
    Lucio
    Partecipante

    Ho un problema nello scrivere una stringa con la funzione SysVarsnprintf. Devo scrivere su una stringa i valori di alcuni DI e il tempo di sistema; la sistassi che uso è la seguente dove PresenzaTensione è associato a DI00 del modulo CPU (IX255.0).

    i:=SysVarsnprintf(ADR(Str_w), 16, ‘pf=%d’, UDINT_TYPE, ADR(SysDateTime));
    i:=SysVarsnprintf(ADR(Str_w)+LEN(Str_w), 16, ‘,%d’, BOOL_TYPE, ADR(PresenzaTensione));

    La stringa di uscita è  ‘pf=1433945090,0’ dove il tempo si incrementa ma la variabile PresenzaTensione rimane sempre a 0 anche se dalla watch la vedo TRUE;

    Se provo a sostituire PresenzaTensione con una variabile mappata in memoria invece che su un ingresso fisico funziona correttamente. Dove sbaglio ?

    #38957
    Sergio Bertana
    Amministratore del forum

    Non è un tuo errore ma è una anomalia del compilatore LogicLab, il problema tira in ballo la gestione dell’immagine di processo degli I/O digitali. Questo argomento lo abbiamo già trattato in altri topics (Vedi questoquesto e questo).

    In pratica tu stai gestendo correttamente la funzione SysVarsnprintf in una task in Back mentre gli I/O sono automaticamente acquisiti in Slow, quindi tu visualizzi non l’ingresso reale ma la sua immagine di processo ricreata automaticamente da LogicLab prima di eseguire i programmi della task Slow. Il bug è che il compilatore avendo tu utilizzato l’attributo ADR per la variabile non si accorge del’uso della stessa ed in una eccessiva ottimizzazione non esegue l’aggiornamento della immagine di processo lasciando la variabile sempre a 0.

    Workaround, puoi semplicemente dichiarare una variabile Dummy di tipo BOOL ed aggiungere nel tuo programma in Back il codice

    IF (FALSE) THEN Dummy:=PresenzaTensione; END_IF;

    Se hai piu ingressi, nella IF puoi copiare ogni ingresso sempre su Dummy, tanto non conta che la copia sia eseguita (La IF non viene mai eseguita) ma il compilatore “vede” l’utilizzo della variabile allocata sull’ingresso reale e provvede automaticamente ad aggiornare l’immagine di processo. Provvedo ad informare Axel del problema.

    #38978
    Sergio Bertana
    Amministratore del forum

    Posto la risposta ricevuta da Axel.

    L’immagine di processo viene attivata solo se il compilatore si accorge che si stà utilizzando una variabile che fa riferimento a un datablock con immagini di processo. Con i puntatori, non è possibile sapere compile-time a cosa si punta occorre quindi effettuare nel programma almeno un accesso alla variabile.

    Quindi la risposta avvalora la soluzione proposta è sufficente nel programma richiamare la variabile, questo permette al compilatore di “vederla” come referenziata e quindi provvede a gestirne l’immagine di processo.

    #39022
    Michele
    Partecipante

    Abbiamo anche noi un problema con Sysvarsnprintf, ma è di natura diversa. Partiamo dalla premessa che esistono due macchine con il software PLC da me creato, la prima costruita a settembre 2014 funziona correttamente ed ha il firmware Sfw184a890, la seconda costruita due mesi fa con il firmware Sfw184a960 ogni tanto toppa.

    Il software PLC è lo stesso. Il problema risiede nel fatto che per la trasmissione dati a PLC viene inviata una stringa ASCII contenente le variabili del pezzo testato dalla macchina. Un valore, in particolare la resistenza di continuità, certe volte non viene stampato sulla stringa… ma non viene stampato niente da quella Sysvarsnprintf in particolare, neanche la virgola… mentre tutte le altre Sysvarsnprintf funzionano correttamente. La riga è la seguente:

    NrChrs := NrChrs + SysVarsnprintf(ADR(dati_array[NrChrs]), 5+1, ‘,%4.0f’, REAL_TYPE, ADR(MData[i].conti_resistenza));

    La creazione della stringa è la seguente:

    NrChrs := SysVarsnprintf(ADR(dati_array), 19+1, ‘<CODIC:%u’, UDINT_TYPE, ADR(MData[i].codic));
    NrChrs := NrChrs + SysVarsnprintf(ADR(dati_array[NrChrs]), 18+1, ‘;LOTTO:%u’, UDINT_TYPE, ADR(MData[i].lotto));
    NrChrs := NrChrs + SysVarsnprintf(ADR(dati_array[NrChrs]), 12+1, ‘;PEZZO:%u’, UINT_TYPE, ADR(MData[i].pezzo));

    NrChrs := NrChrs + SysVarsnprintf(ADR(dati_array[NrChrs]), 5+1, ‘,%4.0f’, REAL_TYPE, ADR(MData[i].conti_resistenza));

    LRC:=0; (* Longitudinal redundancy check *)
    FOR j:=1 TO TO_INT(NrChrs – 1) DO LRC := LRC + TO_USINT(dati_array[j]); END_FOR;
    LRC := (LRC XOR 16#FF)+ 1; (* Longitudinal redundancy check *)
    NrChrs := NrChrs + SysVarsnprintf(ADR(dati_array[NrChrs]), 5+1, ‘>%2X$r$n’, USINT_TYPE, ADR(LRC));

    A volte non stampa nulla, neanche la virgola prima del REAL conti_resistenza, mandando in eccezione il PC.
    Secondo voi ci potrebbe essere una soluzione o provo a caricare il firmware precedente sulla CPU ?

    #39023
    Sergio Bertana
    Amministratore del forum

    La funzione SysVarsnprintf è esattamente la stessa su entrambe le due versioni del firmware che stai utilizzando. La funzione si appoggia sulla printf che è una funzione di libreria del compilatore GNU CC e quindi potrebbe esserci una qualche differenza in quella libreria.

    Il mio consiglio è di utilizzare una variabile STRING e non un ARRAY di BYTE come variabile di appoggio della stringa in uscita dalla SysVarsnprintf.

    IF (SysVarsnprintf(ADR(SOut), 19+1, ‘<CODIC:%u’, UDINT_TYPE, ADR(MData[i].codic))) == EOF) THEN Flt:=Flt OR 1; END_IF
    IF (SysVarsnprintf(ADR(SOut)+LEN(SOut), 18+1, ‘;LOTTO:%u’, UDINT_TYPE, ADR(MData[i].lotto)) == EOF) THEN Flt:=Flt OR 2; END_IF;

    In questo modo un errore nella funzione può essere controllato dal valore che assume Flt al termine e soratutto evita che la somma dei caratteri stampati sia errata. Se errore nella conversione del valore la funzione SysVarsnprintf ritorna EOF. EOF corrisponde al valore -1, questo potrebbe se sommato ad una variabile intera spostare completamente l’indice dell’array di appoggio mandando in eccezione il PLC.

    #39072
    Michele
    Partecipante

    Usare la stringa va bene ma a quel punto però come calcolo il valore di check della comunicazione ?
    Uso un semplice LRC Longitudinal Redundancy Check, infatti con l’array di byte sommo tutti i byte con un ciclo for e sommo 1… con una stringa come faccio ?
    Esiste una funzione per ottenere il valore come byte di un carattere di una stringa tramite indice ?

    #39073
    Sergio Bertana
    Amministratore del forum

    Per accedere ai caratteri di una stringa devi utilizzare un puntatore. Dichiari una variabile pointer ad USINT (Esempio Ptr come @USINT) e poi usi il pointer per iterare sulla stringa. Ecco come fare nel tuo caso:

    LRC:=0; (* Longitudinal redundancy check *)
    FOR Ptr:=ADR(SOut) TO TO_UDINT(ADR(SOut)+LEN(SOut)-1) DO LRC:=LRC+@Ptr; END_FOR;
    LRC:=(LRC XOR 16#FF)+1; (* Longitudinal redundancy check *)

    Per semplificarti ho scritto un semplice programma LogicLab che fà quello che ti serve (Stampa e Download programma).

    #39420
    Alberto
    Partecipante

    Da un pò di tempo stò cercando una soluzione ma non riesco a trovare l’errore, il problema è la stampa su file di una stringa utilizzando la funzione SysVarsnprintf. Questo il pezzo del programma problematico (ricavato dagli esempi trovati sul forum) prima del ciclo FOR vengono inseriti il NrOfLog, la data e l’ora ottenuti da SysDateTime per un totale di 23 caratteri.

    TabMedieT è una Array 32,4, devo ricavare diversi valori e trasferirli sul file, la stampa funziona bene per un ciclo FOR fino a 3 (95 caratteri, cioè 23+72) a 99 caratteri il fine stringa e ritorno a capo sono ignorati e la stringa prosegue con gli stessi valori inseriti precedentemente. LogRecord è un array string 255.

    FOR Idx:= 0 TO 3 DO
       i:=SysVarsnprintf(ADR(LogRecord)+LEN(LogRecord), 9, ‘%07.3f;’, REAL_TYPE, ADR(TabMedieT[Idx,0]));
       i:=SysVarsnprintf(ADR(LogRecord)+LEN(LogRecord), 9, ‘%07.3f;’, REAL_TYPE, ADR(TabMedieT[Idx,2]));
    END_FOR;

    LogRecord:=CONCAT(LogRecord,’$r$n’);
    i:= Sysfseek(Fp, TO_DINT(NrOfLog * LEN(LogRecord)),ID_SEEK_SET);
    i:=Sysfwrite(ADR(LogRecord), To_INT(LEN(LogRecord)), 1, Fp);
    i:=Sysfclose(Fp);

    #39421
    Sergio Bertana
    Amministratore del forum

    Ho creato il programma Printf, con l’essenza del tuo ed ho potuto verificare che il problema è nella CONCAT la quale inspiegabilmente taglia la stringa a 100 caratteri. Per evidenziare anche agli altri utenti del forum il problema ecco la stampa del programma, come vedi ho utilizzato la nuova funzione SysLWVarsnprintf che ha il vantaggio di accodare automaticamente la stampa al contenuto del buffer.

    Nota nella SysLWVarsnprintf definisci la lunghezza della stringa risultato in questo modo la funzione provvede automaticamente a limitare la lunghezza dell’output per non sforare.

    Ho sostituito la CONCAT con un’altra chiamata alla funzione SysLWVarsnprintf per inserire il di fine stringa, inoltre siccome nel debug delle stringhe LogicLab non visualizza stringhe lunghe, ho inviato la stringas in uscita sulla porta COM0 in questo modo la posso catturare con Toolly (Screenshot) per verificare che funzioni (Download programma).

Stai visualizzando 9 post - dal 1 a 9 (di 9 totali)
  • Devi essere connesso per rispondere a questo topic.