Na, ez nincs a Qt-ban. Van egy qextserialport nevű utólag elkészített osztály, ami elvileg multiplatformos megoldást kínál. Belenéztem a forráskódjába, találtam benne néhány to-do és to be implemented megjegyzés. Ettől függetlenül lehet, hogy működik a dolog, de nem próbáltam ki. Hacsak nem akarjuk két gépet összekötve tesztelni a soros port kezelőt, van itt egy megoldás:
- Virtual serial port emulator (VSPE)
- Putty
A VSPE létrehoz két virtuális soros portot, amikre putty-al rákapcsolódva mehet a kommunikáció. A Docklight szerepe a putty-hoz hasonlít, csak sokkal többet tud. Ajánlok még két leírást http://www.robbayer.com/files/serial-win.pdf, és http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c5425.
Ezenkívül az http://msdn.microsoft.com/en-us/ is sok segítséget ad.
1. Port megnyitása:
/*
adatok.at(3).toLocal8Bit().constData()
adatok.at(3) -> QString
adatok.at(3).toLocal8Bit() -> QByteArray
adatok.at(3).toLocal8Bit().constData() -> const char *
CreateFile -> WCHAR az első paraméter tipusa
CreateFileA -> char az első paraméter tipusa
*/
win_soros_leiro = CreateFileA(adatok.at(3).toAscii().constData(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (win_soros_leiro == INVALID_HANDLE_VALUE) {
log_iras(trUtf8("Soros port hiba kód: ") + QString::number(GetLastError()));
return false;
}
Az adatok.at(3) tartalmazza a port nevét (COM1: pl.). Az overlapped szerepe, hogy ne álljon meg a program futása a soros portról várt adatok bejöveteléig.
2. Paraméterek beállítása:
DCB dcb;
if (GetCommState(win_soros_leiro, &dcb) == false) {
log_iras(trUtf8("GetCommState: ") + QString::number(GetLastError()));
CloseHandle(win_soros_leiro);
return false;
}
strcpy(munka, adatok.at(4).toAscii().constData());
dcb.BaudRate = atoi(munka);
strcpy(munka, adatok.at(5).toAscii().constData());
dcb.ByteSize = atoi(&munka[0]);
strcpy(munka, adatok.at(6).toAscii().constData());
int_munka = atoi(&munka[0]);
int_munka--;
dcb.Parity = int_munka;
strcpy(munka, adatok.at(7).toAscii().constData());
int_munka = atoi(&munka[0]);
int_munka--;
dcb.StopBits = int_munka;
if (SetCommState(win_soros_leiro, &dcb) == false) {
log_iras(trUtf8("SetCommState: ") + QString::number(GetLastError()));
CloseHandle(win_soros_leiro);
return false;
}
A DCB struktúra tartalmazza a kommunikációs paramétereket: baud rate, paritás bit, stb. A CreateFile által visszaadott handler a win_soros_leiro. Kiolvassuk a DCB tartalmát, a szükséges elemeket módosítjuk, majd visszaírjuk az új tartalmat. Az adatok.at(6,7)-ből konvertálás után azért kell levonni 1-et, mert az adatbázisból az enum miatt 1-el nagyobb érték érkezik.
3. Időzítés beállítása:
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 2000;
timeouts.WriteTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
if (SetCommTimeouts(win_soros_leiro, &timeouts) == false) {
log_iras(trUtf8("SetCommTimeouts: ") + QString::number(GetLastError()));
CloseHandle(win_soros_leiro);
return false;
}
Tekintve, hogy a soros port olvasását végtelen ciklusban végezzük, nem ildomos azt állandóan pörgetni. Így beállítottam egy 2 másodperces timeout értéket, hogy ennyit várjon a következő karakter érkezésére. Ha jön adat megszakad a timeout, és beolvassa a portról a ReadFile, ha nem jön semmi, a gép végzi a saját dolgát.
4. Overlapped struct beállítása:
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.Internal = 0;
ov.InternalHigh = 0;
ov.hEvent = NULL;
5. Olvasunk:
int osztaly::olvasas() {
bool retVal;
DWORD atvitt_byte;
olv[0] = 0;
retVal = ReadFile(win_soros_leiro, &olv, sizeof (olv), NULL, &ov);
if ((retVal == false) && (GetLastError() != ERROR_IO_PENDING)) {
log_iras("ReadFile hiba: " + QString::number(GetLastError()));
return -1;
}
retVal = GetOverlappedResult(win_soros_leiro, &ov, &atvitt_byte, TRUE);
if (retVal == false) {
if (GetLastError() != ERROR_IO_INCOMPLETE) {
log_iras(trUtf8("GetOverlappedResult hiba: ") + QString::number(GetLastError()));
return -1;
}
} else {
feldolgozas(atvitt_byte);
}
return atvitt_byte;
}
Ezt a függvényt futtatjuk végtelen ciklusban. A következő pontban megmutatom, hogy lehet kilépni a ciklusból. Itt egyedül a GetOverlappedResult érdekes, ez mondja meg, hogy befejeződött-e az olvasás a portról. Ha az utolsó paraméter FALSE, akkor nem várja meg az eredményközléssel az olvasás végét, hanem örömében TRUE-t ad vissza, ha valami adat érkezett. A feldolgozásnak elég gyorsnak kell lennie, hogy ne fogja meg az egész ciklus futását. Talán itt érdemes egy másik szálat futtani. ReadFile-nál ERROR_IO_PENDING akkor áll be, ha még folyik az olvasás a portról, és így a GetOverlappedResult is vár.
6. Kilépés a ciklusból:
void osztaly::futtato() {
int retVal = -1;
leallito = false;
while (leallito == false) {
retVal = olvasas();
switch (retVal) {
case -1:
leallito = true;
break;
case 0:
server_komm();
break;
}
} // while
takarito();
return;
}
Ha az olvasas -1-et ad vissza, gáz van. Ha 0-t, a beállított timeout járt le, semmi adat nem érkezett a portról.
7. Programozott leállítás:
void osztaly::server_komm() {
int olv_bytes = 0;
char olv[5];
leallito = false;
kliens->disconnectFromServer();
kliens->connectToServer("qt_labor");
if (kliens->state() == QLocalSocket::ConnectedState) {
kliens->waitForReadyRead(1000);
olv_bytes = kliens->readLine(olv, 5);
}
qDebug() << olv << olv_bytes;
if (strcmp(olv, "STOP") == 0) {
leallito = true;
}
}