#include #include #include #include #include #include #include #include #include #include #include // ============================================================================ // shipheart_api - Phase 4 : première tranche verticale, domaine "circuit" // ---------------------------------------------------------------------------- // Deux endpoints réels, remplaçant le spike (/ping conservé comme sonde de vie // générique, /test-db retiré car superflu désormais) : // - GET /elements/familles?type=circuit // - GET /elements?type=circuit&famille= // Logique et requêtes SQL reprises à l'identique de home.qml (init_circuits(), // mise_a_jour_onglets_circuits(), mise_a_jour_circuits() - cf synthèse §20.4) // et des constantes documentées dans le schéma (TB_Circuits.Etat, cf §20). // ============================================================================ // Types de familles (cf TB_Familles.Type_source / TB_Events.Type_source) : static const int TYPE_SOURCE_CIRCUIT = 1; // États circuit (cf commentaire colonne TB_Circuits.Etat dans le schéma) : static const int ETAT_CIRCUIT_OK = 0; static const int ETAT_CIRCUIT_LIMITATION = 1; static const int ETAT_CIRCUIT_PROTECTION = 2; static const int ETAT_CIRCUIT_DISJONCTE = 3; static const int ETAT_CIRCUIT_ABSENCE_TERRE = 8; static const int ETAT_CIRCUIT_ABSENCE_MASSE = 9; static const int ETAT_COMMANDE_CARTE_OK = 0; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); // --- Connexion MariaDB locale --- QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("localhost"); db.setUserName("koriolan"); db.setPassword("Mp7Hgf12bV9zm"); db.setDatabaseName("shipheart"); if (!db.open()) { qCritical() << "Connexion MariaDB échouée :" << db.lastError().text(); return 1; } qInfo() << "Connexion MariaDB réussie."; QHttpServer server; // Sonde de vie générique, sans BDD : server.route("/ping", []() { QJsonObject obj; obj["status"] = "ok"; return QHttpServerResponse(QJsonDocument(obj).toJson()); }); // ------------------------------------------------------------------ // GET /elements/familles?type=circuit // Liste des familles avec état d'alarme agrégé (fusionne les deux // requêtes onglets de mise_a_jour_onglets_circuits()). // ------------------------------------------------------------------ server.route("/elements/familles", [](const QHttpServerRequest &request) { QUrlQuery query(request.query()); const QString type = query.queryItemValue("type"); if (type != "circuit") { QJsonObject err; err["status"] = "erreur"; err["message"] = "type non supporté (seul 'circuit' est implémenté à ce stade)"; return QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotImplemented); } QJsonArray familles; QSqlQuery q1(QString("SELECT Cle_primaire, Nom FROM TB_Familles WHERE Type_source=%1 ORDER BY Priorite_affichage").arg(TYPE_SOURCE_CIRCUIT)); while (q1.next()) { const int idFamille = q1.value(0).toInt(); const QString nom = q1.value(1).toString(); int etat = 0; // Alarmes (Events) liées à cette famille : QSqlQuery q2; q2.prepare("SELECT COUNT(*) FROM TB_Events WHERE Type_source=:type AND Validite=1 AND Alarme_source>0 " "AND Etat_test=1 AND ID_Source IN (SELECT Cle_primaire FROM TB_Circuits WHERE ID_Famille=:famille)"); q2.bindValue(":type", TYPE_SOURCE_CIRCUIT); q2.bindValue(":famille", idFamille); q2.exec(); if (q2.next() && q2.value(0).toInt() > 0) etat = 1; // État agrégé des circuits de la famille : QSqlQuery q3; q3.prepare("SELECT Etat, Etat_commande_carte, Etat_systeme FROM TB_Circuits " "WHERE ID_Famille=:famille AND Priorite_affichage_accueil>0"); q3.bindValue(":famille", idFamille); q3.exec(); while (q3.next()) { const int etatCircuit = q3.value(0).toInt(); const int etatCommandeCarte = q3.value(1).toInt(); const int etatSysteme = q3.value(2).toInt(); if (etatSysteme != 0 || etatCircuit == ETAT_CIRCUIT_LIMITATION || etatCircuit == ETAT_CIRCUIT_PROTECTION || etatCircuit == ETAT_CIRCUIT_DISJONCTE || etatCircuit == ETAT_CIRCUIT_ABSENCE_TERRE || etatCircuit == ETAT_CIRCUIT_ABSENCE_MASSE) { etat = 2; } else if (etat < 2 && (etatCommandeCarte != ETAT_COMMANDE_CARTE_OK || etatCircuit != ETAT_CIRCUIT_OK)) { etat = 1; } } QJsonObject obj; obj["id"] = idFamille; obj["nom"] = nom; obj["etat"] = etat; familles.append(obj); } QJsonObject reponse; reponse["status"] = "ok"; reponse["familles"] = familles; return QHttpServerResponse(QJsonDocument(reponse).toJson()); }); // ------------------------------------------------------------------ // GET /elements?type=circuit&famille= // Liste des circuits de la famille - sert chargement initial ET // rafraîchissement périodique (même requête que init_circuits() et // mise_a_jour_circuits(), cf §20.4 : ce n'est pas deux besoins // différents, mais un seul, appelé deux fois). // ------------------------------------------------------------------ server.route("/elements", [](const QHttpServerRequest &request) { QUrlQuery query(request.query()); const QString type = query.queryItemValue("type"); const QString familleStr = query.queryItemValue("famille"); if (type != "circuit") { QJsonObject err; err["status"] = "erreur"; err["message"] = "type non supporté (seul 'circuit' est implémenté à ce stade)"; return QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotImplemented); } bool ok = false; const int idFamille = familleStr.toInt(&ok); if (!ok) { QJsonObject err; err["status"] = "erreur"; err["message"] = "paramètre 'famille' manquant ou invalide"; return QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::BadRequest); } QJsonArray elements; QSqlQuery q; q.prepare( "SELECT Cle_primaire, Etat_systeme, Nom, Etat_commande_carte, Mode_commande, Etat, Consigne, Old_consigne, " "Mode_test, Tension, Intensite, Frequence, " "(SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus=TB_Circuits.Adresse_module) AS type_circuit, " "No_circuit, " "(SELECT COUNT(*) FROM TB_Events WHERE Validite=1 AND Visibilite=1 AND " "(Action_circuits LIKE CONCAT(TB_Circuits.Cle_primaire,'/%') OR Action_circuits LIKE CONCAT('%;', TB_Circuits.Cle_primaire,'/%'))) AS nb_events, " "Validation_cdes_events, " "(SELECT COUNT(*) FROM TB_Events WHERE Validite=1 AND Type_source=:type AND Alarme_source>0 AND Etat_test=1 " "AND ID_Source=TB_Circuits.Cle_primaire) AS Nb_alarmes " "FROM TB_Circuits WHERE Priorite_affichage_accueil>0 AND ID_Famille=:famille " "ORDER BY Priorite_affichage_accueil, Cle_primaire" ); q.bindValue(":type", TYPE_SOURCE_CIRCUIT); q.bindValue(":famille", idFamille); q.exec(); while (q.next()) { QJsonObject obj; obj["id"] = q.value(0).toInt(); obj["etat_systeme"] = q.value(1).toInt(); obj["nom"] = q.value(2).toString(); obj["etat_commande_carte"] = q.value(3).toInt(); obj["mode_commande"] = q.value(4).toInt(); obj["etat"] = q.value(5).toInt(); obj["consigne"] = q.value(6).toInt(); obj["old_consigne"] = q.value(7).toInt(); obj["mode_test"] = q.value(8).toInt(); obj["tension"] = q.value(9).toDouble(); obj["intensite"] = q.value(10).toDouble(); obj["frequence"] = q.value(11).toDouble(); obj["type_circuit"] = q.value(12).toInt(); obj["no_circuit"] = q.value(13).toInt(); obj["validation_cdes_events"] = (q.value(14).toInt() == 0) ? -1 : q.value(15).toInt(); obj["etat_events"] = q.value(16).toInt(); elements.append(obj); } QJsonObject reponse; reponse["status"] = "ok"; reponse["elements"] = elements; return QHttpServerResponse(QJsonDocument(reponse).toJson()); }); auto tcpServer = new QTcpServer(); if (!tcpServer->listen(QHostAddress::Any, 8080) || !server.bind(tcpServer)) { qCritical() << "Impossible de démarrer le serveur HTTP sur le port 8080."; delete tcpServer; return 1; } qInfo() << "shipheart_api en écoute sur le port" << tcpServer->serverPort(); return app.exec(); }