Jelen feladatban több automata adatait kell összegyűjteni egy adatbázisba. Ehhez vagy külön - külön megírjuk minden automata vezérlőprogramját vagy többszálú (multithread) programozással futtatjuk a lekérdező alkalmazásokat. Multithread programozásnál figyelni kell a futó szálak prioritására. Mire gondolok? Ha vannak mondjuk soros és tcp kapcsolatot használó automaták, akkor a sorosnak kell nagyobb prioritást adni az érzékenyebb adatátvitel miatt. Ezzel lelassítjuk a többi szál futását. Elméletben igen sok szálat indíthatunk, gyakorlatilag a szinkronizálási és üzenetváltási gondok miatt nem érdemes ezt a megoldást erőltetni. Tapasztalatom szerint többszálú megoldással négy modemet tud egy program kiszolgálni. Ráadásul, ha leáll vagy befagy a szálakat kezelő program, akkor a többinek is kakukk. Tehát jobb megírni a külön futó vezérlő programokat. Technikailag nem kell többet programozni, mindkét megoldás - az adatfeldolgozás szempontjából - ugyanazokat az elemeket tartalmazza.
Egy programnak másikból történő indítására a QProcess osztály szolgál. Az indító utasítások:
- start -> elindítja a programot új processként, ha még nem fut
- execute -> elindítja a programot új processként és vár a befejezésre
- startDetached -> elindítja a programot új processként és leválik róla (elfelejti)
Mindhárom megoldásnál QStringList-ben tudjuk átadni az indító paramétereket
const QString & program, const QStringList & arguments
formában. Nekünk a startDetached kell. A vonatkozó programrészlet:
void StartCommForm::inditas_leallitas(QTableView *nezet, QString to_do) {
QSqlQuery query(db_leiro), allapot(db_leiro);
query.prepare("select * from automatak where automata_id = :0");
QString automata_id;
QStringList params;
QItemSelectionModel *munka = nezet->selectionModel();
QList<QModelIndex> kivalasztottak(munka->selectedRows(3));
for (int i = 0; i < kivalasztottak.count(); i++) {automata_id = kivalasztottak.at(i).data().toString();
if (to_do == "I"){
query.bindValue(0,automata_id);
query.exec();
query.next();
for (int j = 4; j < 13; j++){
params << query.value(j).toString();
QProcess progi;
if (progi.startDetached(query.value(2).toString(), params) == false){ // nem indult el
allapot.prepare("update automatak set hiba = :0, fut = 'Nem' where automata_id = :1");
allapot.bindValue(0, hibakodok.at(progi.error()));
allapot.bindValue(1, automata_id);
allapot.exec();
}else{ // elindult
allapot.prepare("update automatak set hiba = :0, fut = 'Igen' where automata_id = :1");
allapot.bindValue(0, hibakodok.at(6));
allapot.bindValue(1, automata_id);
allapot.exec();
} // elindult vagy nem if vege
}
}else{
allapot.prepare("update automatak set hiba = :0, fut = 'Nem' where automata_id = :1");
allapot.bindValue(0, hibakodok.at(6));
allapot.bindValue(1, automata_id);
allapot.exec();
} // inditas vagy leallitas if vege
} // for
Nézzük részleteiben:
inditas_leallitas(widget.indito_tableView, "I");
Átadom az automatákat tartalmazó tableView-t, és "I"ndítás vagy "L"eállítás kell-e.
QItemSelectionModel *munka = nezet->selectionModel();
QList<QModelIndex> kivalasztottak(munka->selectedRows(3));
for (int i = 0; i < kivalasztottak.count(); i++) {automata_id = kivalasztottak.at(i).data().toString();
A tableView-t multiselectre állítottam, így nem kell ctrl-t nyomkodni ha több elemet akarok kiválasztani. A kiválasztott sorok harmadik oszlopának tartalmát betöltöm egy QList-be, és ezt pörgetem végig. Így tudni fogom az indítandó automaták egyedi azonosítóját. Ennek alapján megtudom az összes adatot az automatákról:
select * from automatak where automata_id = :0
A params QStringList-ben a kommunikációra vonatkozó összes paramétert betöltöm, majd az automatát vezérlő program kiválogatja a neki kellőket.
A startDetach vissztérő állapotától függően írom vissza az adatbázisba az automata futási állapotát.
QStringList hibakodok << "FailedToStart" << "Crashed" << "Timedout" << "ReadError" << "WriteError" << "UnknownError" << "Running";
Az utolsó else ágba kerül majd a leállítás, amit egy local kliens - szerver tcp kapcsolaton keresztül oldunk meg. Konkrétan az indító progi futtat majd egy szerver ágat, amire a kliensek minden adatátviteli ciklus után felkapcsolódnak. Ha megkapják a leállító üzenetet, befejezik a futást. Felmerülhet a kérdés, hogy miért ezt a megoldást választom. Mert csak ez multiplatformos, a D-Bus (ha jól tudom) linux specifikus. A Sharedmemory pedig a leírás szerint is beragad linux alatt és csak kézzel (ipcs, ipcrm) lehet kipiszkálni.
De hogy veszem át a paramétereket az indított programban? Így:
osztaly::osztaly(QStringList params){
QStringList adatok(params);qDebug() << adatok.count();
}
osztaly::~osztaly(){
}
//---------------------------------------------------------------------
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
osztaly x(app.arguments());
return app.exec();
}
Az app.arguments() kimenete egy stringlist. Ezt átadom a futtató osztály konstruktorának, és innen már egyenes az út.