Vai al contenuto
PLC Forum


Ricerca tramite SCL e puntatori ANY di una stringa all'interno di una DB


Zob

Messaggi consigliati

Ciao a tutti,

sto buttando giù qualche idea per realizzare in SCL una ricerca all'interno di una DB di una stringa (nome utente) passata da pannello. Documentandomi un poì in rete ho appreso come la cosa "migliore" sia affrontare il problema tramite puntatori ANY e BLKMOV (SFC20). Sequenzialmente le operazioni da fare sarebbero:

  • login da hmi in cui salvo il nome utente inserito: salvataggio della stringa inserita da pannello, fin qui nessun problema. Questra stringa sarà poi passata come parametro alla FB.
  • ricerca del nome utente all'interno della DB con tutti i nomi utente: creo due puntatori ANY a cui accedo tramite AT, in modo da poterli parametrizzare facilmente a mio piacimento.
    primo passo) Leggo i-esimo nome utente dalla DB utenti e lo sposto in una locazione di memoria fissata (in modo da usare sempre la stessa stringa per il confronto al passo successivo) tramite BLKMOV.
    secondo passo) Effettuo il confronto tra la stringa in ingresso alla funzione e quella appena spostata.
  • trovato il nome caricare i dati macchina per tale utente per visualizzarli a pannello in una determinata pagina: se il confronto da buon esito riparametrizzo gli stessi puntatori che utilizzo poi per spostare i dati utente dalla propria db verso quella visibile da pannello operatore. Se il confronto non da buon esito continuo la ricerca all'interno della DB utenti.

 

Evidentemente qualcosa mi sfugge in quanto la FB itera correttamente ma non sposta mai la stringa al primo passo), di conseguenza il confronto non da mai buon esito. Lascio in allegato ciò che ho prodotto. Qualche idea a riguardo sui possibili errori e/o soluzioni alternative?
 

 

(********************CODICE***********************)

 

FUNCTION_BLOCK FB150

 

VAR_TEMP
    // Variabili temporanee
    j,i,k:INT;
    
    //credo una struttura per accedere ai puntatori in maniera più semplice   
    pAnySource: ANY;
    pSource AT pAnySource: STRUCT
        S7Code:BYTE; //codice fisso a 16#10
        DataType:BYTE; //codice per il tipo di dato da indirizzare
        //16#01=BOOL, 16#02=BYTE, 16#03=CHAR, 16#04=WORD, 16#05=INT,
        //16#06=DWORD, 16#07=DINT, 16#08=REAL, 16#09=DATE, 16#0A=TOD,
        //16#0B=TIME, 16#0C=S5TIME, 16#0E=DT, 16#13=STRING  
        Lenght:INT; //fattore di ripetizione
        DBNumber:INT; // numero della db
        MemoryArea:BYTE; //codice area da puntare
        //16#81=Input area (I), 16#82=Output area (O), 16#83=Bit memory area (M),
        //16#84=Shared Data Block (DB), 16#85=Instance Data Block (DI),
        //16#86=Local data (L stack), 16#87=Previous local data (V)
        ByteAddressMSB:BYTE; //byte indirizzamento più significativo
        //da lasciare sempre a zero a meno che non si vogliano indirizzare
        //db con più di 8192 byte
        ByteAddressLSB:WORD; //byte indirizzamento meno significativo
    END_STRUCT;
    
    pAnyDest: ANY;
    pDest AT pAnyDest: STRUCT
        S7Code:BYTE;
        DataType:BYTE;  
        Lenght:INT;         
        DBNumber:INT;
        MemoryArea:BYTE;
        ByteAddressMSB:BYTE;
        ByteAddressLSB:WORD;
    END_STRUCT;

      
END_VAR

VAR
    tempUser: STRING[12];
    indice:INT:=0;
    UserDB:INT;
    ScanRange:INT:=0;
END_VAR

VAR_INPUT
    CurrentUser:STRING[12];
    DB_Base:INT;
    ActualParametersDB:INT;
END_VAR

VAR_OUTPUT
    //utente attualmente loggato (codice numerico)
    UserLogged:INT:=0;
    UserLoggedNotInDB:BOOL:=false;
END_VAR

VAR_IN_OUT
    NewLogin:BOOL;
    NewLogout:BOOL;    
END_VAR

//***************OPERAZIONI ESEGUITE SOLO AL LOGIN****************
IF NewLogin=true THEN
UserLoggedNotInDB:=false;
indice:=0;
scanrange:=228;
    
//creo il puntatore iniziale che poi andrò a variare mano a mano
pSource.s7code:=16#10; //fisso
pSource.DataType:=16#13; //indirizzo stringhe
pSource.Lenght:=1; //quante stringhe
pSource.DBNumber:=210; //db dove sono salvati gli users
pSource.MemoryArea:=16#84;//codice per le db
pSource.ByteAddressMSB:=0;
pSource.ByteAddressLSB:=0;//parto dal primo bit della db
//il puntatore appena creato equivarrebbe a: P#DB210.DBX0.0 STRING 1

//il secondo puntatore lo faccio puntare sempre allo stesso punto, ad
//ogni iterazione trasferisco la stringa puntata da pSource nel punto
//puntato da pDest
pDest.s7code:=16#10; //fisso
pDest.DataType:=16#13;
pDest.Lenght:=1;
pDest.DBNumber:=211; //db dove salvare la stringa (al momento non il db di istanza)
pDest.MemoryArea:=16#84;//codice per le db
pDest.ByteAddressMSB:=0;
pDest.ByteAddressLSB:=0;
//il puntatore appena creato equivarrebbe a: P#DB211.DBX0.0 STRING 1

//ricerco l'user inserito all'interno della db degli utenti
FOR i:=0 TO 9 BY 1 DO
        
        //ricalcolo indice pSource
        //la db degli user è strutturata: nome user (14byte) + psw user (14byte)
        //quindi ad ogni iterazione bisogna aggiungere al LSB 228 (28*8 bit)
        indice:=0 + (i*scanrange);
        pSource.ByteAddressLSB:= INT_TO_WORD(indice);                
        j:=BLKMOV(srcblk:=pSource, dstblk:=pDest);
        
        //confronto tra stringhe
        //UserScanner.UserScanned = P#db211.dbx0.0 STRING 1
        IF CurrentUser=UserScanner.UserScanned THEN
            UserLogged:=i+1;
            UserDB:=DB_Base+UserLogged;
            //carico la db dello user trovato nella db200
            //riparametrizzo i puntatori per il trasferimento
            pSource.s7code:=16#10; //fisso
            pSource.DataType:=16#02; //indirizzo dei byte
            pSource.Lenght:=300; //quanti byte
            pSource.DBNumber:=UserDB; //db utente
            pSource.MemoryArea:=16#84;//codice per le db
            pSource.ByteAddressMSB:=0;
            pSource.ByteAddressLSB:=0;
            
            pDest.s7code:=16#10; //fisso
            pDest.DataType:=16#02; //indirizzo dei byte
            pDest.Lenght:=300;
            pDest.DBNumber:=DB_Base;  
            pDest.MemoryArea:=16#84;//codice per le db
            pDest.ByteAddressMSB:=0;
            pDest.ByteAddressLSB:=0;
            
            k:=BLKMOV(srcblk:=pSource, dstblk:=pDest);
            
            i:=10; //esco dal ciclo for quando ho trovato l'utente
            NewLogin:=false;
        END_IF;
        
        IF i=9 AND NewLogin=true THEN
            UserLoggedNotInDB:=true;
            NewLogin:=false;
        END_IF;         
                  
END_FOR;

END_IF;

END_FUNCTION_BLOCK

 

 

 

Grazie mille

Link al commento
Condividi su altri siti


Ciao, credo di aver capito cosa vuoi fare dalla descrizione iniziale che hai fatto, ma poi leggendo il codice non riesco proprio a capire alcune cose:

 

prima dici:

Quote

    #pSource.DBNumber := 210; //db dove sono salvati gli users

 

poi, subito dopo invece trovo:

Quote

#UserDB := #DB_Base + #UserLogged;

....

#pSource.DBNumber := #UserDB; //db utente

 

ovvero prima dici che tutti gli user sono salvati nel DB numero 210 e subito dopo invece sembra che ogni user abbia un suo DB.

 

inoltre potresti spiegare cosa c'è nel DB "UserScanner" e perchè lo usi?

 

Grazie,

Ciao

Fabio

 

 

 

Link al commento
Condividi su altri siti

generalmente in SCL , non si può usare una stringa per confrontarla direttamente con un'altra.Il problema si risolve utilizzando una variabile locale di appoggio per memorizzare la stringa .Meglio se la variabile d'appoggio intermedia si trova in un DB globale in quanto lo stack limitato dei una funzione potrebbe creare problemi non indifferenti

Link al commento
Condividi su altri siti

Quote

generalmente in SCL , non si può usare una stringa per confrontarla direttamente con un'altra.Il problema si risolve utilizzando una variabile locale di appoggio per memorizzare la stringa .Meglio se la variabile d'appoggio intermedia si trova in un DB globale in quanto lo stack limitato dei una funzione potrebbe creare problemi non indifferenti

 

Ciao, si quello l'avevo capito, ed è il motivo per cui utilizza il DB 211. Per quanto riguarda UserScanner ho visto dopo che sarebbe il db211:

 

Quote

//UserScanner.UserScanned = P#db211.dbx0.0 STRING 1

 

La mia domanda principale era però un'altra, ovvero ogni user ha il suo DB? come fa pensare la seguente:

Quote

#UserDB := #DB_Base + #UserLogged;

....

#pSource.DBNumber := #UserDB; //db utente

 

oppure c'è un DB dove sono presenti tutti gli user?

Quote

    #pSource.DBNumber := 210; //db dove sono salvati gli users

 

inoltre potresti spiegare cosa è DB_base? Nella dichiarazione delle variabili non è presente alcuna descrizione, quindi diventa abbastanza difficile capire.

 

Link al commento
Condividi su altri siti

Ciao,

allora seguo con una descrizione un po' più dettagliata delle DB in gioco (scusatemi se non l'ho fatto prima):

  • DB 210 DB Users: è la db dove sono presenti tutte le associazioni utente+psw. E' la db che scandisco per vedere se l'utente inserito a pannello è presente tra quelli salvati in questa db.
  • DB 211 DB_Scanner: è la db che utilizzo come appoggio, ogni elemento che scandisco dalla db 210 lo colloco qua e dopodichè faccio il confronto con il nome utente inserito da pannello.
  • DB 200 DB Base: è la db che utilizzo per far vedere a pannello i dati macchina dell'utente attualmente loggato. Praticamente, a seguito dell'esito della ricerca eseguita sulla DB 210, in questa db vengono caricati i dati di una delle db DB 201--> DB 209. Queste ultime sono appunto le db che contengono i dati macchina aggiornati all'ultima sessione di lavoro dell'utente in questione.

 

Spero di essere stato un po' più chiaro!

Link al commento
Condividi su altri siti

io non ho mai usato e mai userò le porcherie di librerie inutili e casinose , metto a fuoco il problema e risolvo con la mia testa con funzioni veloci , funzionanti e stabili .

Ci metto meno tempo che star li a capire tutte le libro-porcherie di quei dementi di tedeschi che non hanno niente da fare che stare in ufficio a bere caffè e scrivere minkiate inutili.I problemi di stringhe , elaborazioni su di esse, tabelle , strutture , calcoli , ricerche , inserimenti etc , li ho sempre risolti in SCL con DB globali fatti ad hoc .Lo scanner lo utilizzo connesso ad un pc che programmo in C# .Quando mi son capitati scanner da connettere al plc ordinavo il loro convertitore profibus .Alcuni di essi , gli scanner più marcioni , mi davano i dati in due colpi avendo un buffer piccolo per quello che mi interessava , cosi che creavo una piccola macchina a stati dove leggevo in due colpi il buffer sincronizzandomi con i bit messi a disposizione dal dispositivo stesso .In generale devi avere un DB che funge da buffer per la lettura del dispositivo , una funzione che processa i dati e li interpreta , un'area di deposito per i dati elaborati e poi la visualizzazione 

Link al commento
Condividi su altri siti

Ciao,

certo si che creando db globali ad hoc il problema si risolve in quattro e quattr'otto, la mia intenzione era di voler creare un qualcosa di riutilizzabile il più possibile e per tale motivo stavo provando una soluzione tramite puntatori any (di cui tutti "sponsorizzano" l'uso in scl per effettuare un'operazione del genere...ma poi nel dettaglio in pochi ti sanno aiutare veramente)

Comunque non devo utilizzare nessuno scanner, il nome "DB Scanner" è solamente per identificare la db in cui metto ad ogni iterazione la stringa letta dalla db210 ;)

Link al commento
Condividi su altri siti

Quote

certo si che creando db globali ad hoc il problema si risolve in quattro e quattr'otto, la mia intenzione era di voler creare un qualcosa di riutilizzabile il più possibile e per tale motivo stavo provando una soluzione tramite puntatori any

Non credo che una strutturazione dei dati orientata all'utilizzo in SCL renda difficile un eventuale riutilizzo, anzi.

 

Relativamente al problema, sinceramente non ho analizzato il codice, ma l'uso dei puntatori ANY mi pare solo una complicazione.

Piuttosto, se proprio vuoi lavorare con i puntatori, vedi le istruzioni PEEK e POKE.

Però non ne vedo il motivo. Creando array e strutture ad hoc risolvi tutto senza complicarti la vita.

Link al commento
Condividi su altri siti

lavori con un DB globale e lo passi come IN/OUT alla tua funzione ....con il P# che non e' altro che  "&" del C .

Implicitamente una struttura piu o meno complessa passata come IN/OUT è un puntatore ad essa

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