• Non ci sono risultati.

Verranno ora mostrati e spiegati alcuni frammenti di codice delle operazioni principali.

6.2.1

Rilevamento dispositivo

void DeviceDetector::detectFtdiDevices(){

vector<string> serials = FtdiDevice::detectDevice(); string serial;

QList<string> currentDevices;

//check for new devices and emit deviceDetected if there is a new one

for(unsigned i=0;i<serials.size();i++){

serial = serials[i];

serial = serial.substr(0,serial.length()-1);

if(serial!="" && !currentDevices.contains(serial)){ currentDevices.push_back(serial);

if (!devices.contains(serial)){ devices.push_back(serial);

Device *d = new FtdiDevice(serial); emit deviceDetected(d); } } } foreach(string s,devices){ if(!currentDevices.contains(s)){ devices.removeOne(s); emit deviceRemoved(QString::fromStdString(s)); } } }

Listato 6.1: Estratto del codice di DeviceDetector: metodo detectFtdiDevices()

Il codice mostrato nel frammento 6.1 descrive l’operazione di rilevamento del dispositivo. Dopo aver chiamato il metodo statico detectDevice() della classe FtdiDevice, che restituisce un vector di stringhe con il numero seriale, viene iterata questa lista, prendendo il numero seriale senza l’ultimo carattere.

Questo perch´e quando un dispositivo viene connesso, avendo il chip Ftdi due canali, questo viene rilevato due volte come numeroserialeA e numeroserialeB. Viene verificata la presenza di questo dispositivo nella lista dei dispositivi at-

58 CAPITOLO 6. IMPLEMENTAZIONE tuali e, se non presente, viene aggiunta la stringa agli attuali dispositivi, viene inizializzato il dispositivo e viene emessa una signal con il puntatore a questo. Nella seconda iterazione viene invece verificato se nella lista dei dispositivi attuali ne `e presente uno che non `e pi`u collegato all’USB. Se questo viene rilevato viene emessa una signal con il numero seriale del dispositivo, in modo che il modulo superiore si occupi di de-allocarlo.

6.2.2

Apertura del dispositivo

[...]

//select channel A or B and update the serial num

int channel = config->getDriver().channel; char channelWord; switch(channel){ case 0: channelWord = ’A’; break; case 1: channelWord = ’B’; break; default: channelWord = ’B’; }

this->serialNumber = this->serialNumber + channelWord;

//calculates the key to be used in the decrypting of eeprom data

calculateKey();

// open the device

result = FT_OpenEx((PVOID)serialNumber.c_str(), FT_OPEN_BY_SERIAL_NUMBER, handle);

if(result != FT_OK){

DeviceOpeningException e(result);

throw e;

}

this->opened = true;

CAPITOLO 6. IMPLEMENTAZIONE 59

this->setEEProm(

MemoryLoader::loadFromEEPromName(utils::selectEEProm(qSerial), key,handle));

//Try a command to see if the device is opened

result = FT_GetQueueStatus(*handle, &FTQBytes); if(result != FT_OK){

DeviceOpeningException e(result);

throw e;

}

[Other methods of FTDI driver are called to initialize the device] checkDate();

checkVersion();

eeprom->loadGainAndOffset(channelDetails,MODE_NUMBER,RANGE_NUMBER); initializeBuffer();

[...]

Listato 6.3: Estratto del codice di FtdiDevice: apertura del dispositivo(2) Dopo una prima fase di selezione e apertura del corretto file di configurazione, che `e stata omessa dal codice mostrato per motivi di spazio, viene selezionato, a seconda di come indicato nel config file, il canale da aprire (tra i canali A e B in uscita dal chip FTDI). Viene quindi aggiornato il numero seriale e chiamato il metodo che calcola la chiave di decodifica dei dati.

Dopodich´e viene aperta la connessione al dispositivo attraverso l’utilizzo della funzione FT OPEN del driver FTDI. Viene caricata la EEPROM utiliz- zando la funzione di utility come factory, e successivamente vengono chiamati i metodi di checkDate() per verificare la data di scadenza dell’eventuale periodo di prova del dispositivo, checkVersion() per caricare le informazioni sulla sotto- versione, loadGainAndOffset() per caricare i dati di calibrazione del dispositivo e initializeBuffer() per inizializzare il buffer utilizzato nella comunicazione con il DeviceManager.

60 CAPITOLO 6. IMPLEMENTAZIONE

6.2.3

Lettura dei dati

[...]

//Reads if there is something in the queue

result = FT_GetQueueStatus(*handle, &FTQBytes/*FT_Q_Bytes*/); if (result != FT_OK){

cout << "error " << result << endl; emit deviceNotFound();

break ;

}

//calculates the packet number, if there are no packets go back to the while condition

packNum = FTQBytes / config->getPacketSize(); [...]

bufferSize = packNum * config->getPacketSize();

//Reads the data from the FTDI chip

result = FT_Read(*handle, recieveBuffer, bufferSize, &readResult); if (result != FT_OK){

[...] }

data = recieveBuffer; int *sync;

//Calculates the sync

scartati = 0;

sync = (int*) recieveBuffer;

while( *sync != config->getSyncWord()){

data++;

sync = (int*) data; scartati++;

}

//sync if needed

if(scartati>0){ emit outOfSync();

result = FT_Read(*handle, recieveBuffer+bufferSize, scartati, &readResult);

}

CAPITOLO 6. IMPLEMENTAZIONE 61

index = 0;

for(int i=0;i<packNum;i++){

/*

* skipping the sync in the beginning of every packet * */

index += config->getSyncBytes();

for(int j=0;j<config->getSyncEvery();j++){ c=0;

for(int ch=0;ch<config->getChannelsCount();ch++){

unsigned char* p = recieveBuffer + index;

value = 0;

for(int b=0;b<config->getChannelBytes();b++){ value <<= 8;

value += *(p+b); }

index += config->getChannelBytes();

/*set the values in the packet calling the mapping method calculateValue

with the channel number as parameter.

If is the Vc channel, use -1 as channel number*/

if (ch==vChannel.channelIndex){ packet->setVc(calculateValue(value,-1)); } else { packet->setValue(c,calculateValue(value,c)); c++; }

//if vchannel is not present

if(vChannel.channelIndex==-1){ packet->setVc(0); } } this->writePacket(packet); } } [...]

Listato 6.5: Estratto del codice di FtdiDevice: lettura dal dispositivo (2) Gli estratti di codice 6.4 6.5 rappresentano una parte del metodo chiave del software, ossia la comunicazione tra FtdiDevice e il driver FTDI per la lettura dei dati, con l’estrazione di questi dai pacchetti ricevuti.

Viene letto lo stato del buffer USB e viene contato il numero di pacchetti presenti. `E poi eseguita una lettura di dati per quell’esatto numero di byte, lasciando nel buffer dati di pacchetti non completi eventualmente in eccesso.

Una parte molto importante del codice `e la sincronizzazione: se il primo da- to corrisponde alla word di sincronizzazione si prosegue nella lettura; nel caso

62 CAPITOLO 6. IMPLEMENTAZIONE contrario vengono contati i byte che ci sono prima del sync seguente, e ven- gono ricaricati i dati mancanti per completare l’ultimo pacchetto, segnalando attraverso una signal che c’`e stata una perdita di sincronismo.

Dopodich`e per ogni pacchetto, attraverso l’uso di puntatori e operazioni di base come degli shift per massimizzare le prestazioni, vengono scorsi tutti i dati e aggiunti al pacchetto da scrivere nel buffer in uscita. A questo punto viene chiamato il metodo writePacket() che provvede ad inserire nel buffer il pacchetto.

6.2.4

Invio di una configurazione al dispositivo

void FtdiDevice::doWrite(){

// Clean TX buffer

FT_STATUS r = FT_Purge(*handle, FT_PURGE_TX);

unsigned long result = 0;

int size = config->getConfigBytesNumber();

unsigned char* buffer = config->getConfigState();

// Transmit

r = FT_Write(*handle, buffer, size, &result); writing = false;

}

Listato 6.6: Estratto del codice di FtdiDevice: invio di una configurazione L’invio di una configurazione al device viene effettuato recuperando lo stato dal Config e trasmettendolo, attraverso il driver FTDI, al dispositivo.

CAPITOLO 6. IMPLEMENTAZIONE 63

6.2.5

Distribuzione dei dati

void DeviceManager::run(){ device->open();

device->startReading();

Thread *t = new Thread(device); t->start();

while(!stopped){

DataPacket value = device->read();

for(unsigned i = 0; i < consumers->size(); i++){

consumers->at(i)->receiveData(value); } } this->device->stopReading(); this->device->close(); delete t; this->deleteLater(); }

Listato 6.7: Estratto del codice di DeviceManager: lettura e ridistribuzione dei dati

Al momento dell’esecuzione, il DeviceManager apre il dispositivo a lui associato e gli comunica di iniziare a leggere e fa partire la lettura attraverso l’uso di un Thread.

Fino a quando la variabile stopped `e falsa, viene chiamato il metodo read() sul dispositivo e, ricevuto un DataPacket, viene mandato a tutti i consumer associati. All’uscita del ciclo viene detto al dispositivo di smettere di leggere e viene chiuso, oltre a deallocare il thread utilizzato e lo stesso DeviceManager.

Documenti correlati