HEX
Server: Apache
System: Windows NT MAGNETO-ARM 10.0 build 22000 (Windows 10) AMD64
User: Michel (0)
PHP: 7.4.7
Disabled: NONE
Upload Files
File: C:/Redmine-4.x/redmine-4.2.9/files/2025/07/250731163113_mainwindow.cpp
#include "mainwindow.h"
#include <QMessageBox>
#include <QApplication>
#include <QDateTime>
#include <QHeaderView>
#include <QTableWidgetItem>
#include <QSplitter>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , m_hApplication(OpcUa_Null)
    , m_hSession(OpcUa_Null)
    , m_isConnected(false)
    , m_isInitialized(false)
{
    setupUI();

    // Initialiser OPC UA
    if (initializeOpcUa()) {
        logTextEdit->append("OPC UA initialisé avec succès");
        m_isInitialized = true;
    } else {
        logTextEdit->append("ERREUR : Échec de l'initialisation OPC UA");
    }

    // Timer pour vérifier l'état de la connexion
    connectionTimer = new QTimer(this);
    connect(connectionTimer, &QTimer::timeout, this, &MainWindow::onConnectionTimer);
    connectionTimer->start(1000); // Vérifier chaque seconde
}

MainWindow::~MainWindow()
{
    if (m_isConnected) {
        disconnectFromServer();
    }
    cleanupOpcUa();
}

void MainWindow::setupUI()
{
    centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);

    // Splitter principal
    QSplitter *mainSplitter = new QSplitter(Qt::Horizontal, this);
    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
    mainLayout->addWidget(mainSplitter);

    // Partie gauche - Configuration
    QWidget *leftWidget = new QWidget();
    QVBoxLayout *leftLayout = new QVBoxLayout(leftWidget);

    // Groupe Connexion
    connectionGroup = new QGroupBox("Connexion au serveur OPC UA", this);
    QGridLayout *connectionLayout = new QGridLayout(connectionGroup);

    connectionLayout->addWidget(new QLabel("URL du serveur:"), 0, 0);
    urlEdit = new QLineEdit("opc.tcp://Billy:2602/SimulationBasic", this);
    connectionLayout->addWidget(urlEdit, 0, 1);

    connectionLayout->addWidget(new QLabel("Politique de sécurité:"), 1, 0);
    securityEdit = new QLineEdit("http://opcfoundation.org/UA/SecurityPolicy#None", this);
    connectionLayout->addWidget(securityEdit, 1, 1);

    connectButton = new QPushButton("Se connecter", this);
    disconnectButton = new QPushButton("Se déconnecter", this);
    disconnectButton->setEnabled(false);

    QHBoxLayout *buttonLayout = new QHBoxLayout();
    buttonLayout->addWidget(connectButton);
    buttonLayout->addWidget(disconnectButton);
    connectionLayout->addLayout(buttonLayout, 2, 0, 1, 2);

    statusLabel = new QLabel("Déconnecté", this);
    statusLabel->setStyleSheet("color: red; font-weight: bold;");
    connectionLayout->addWidget(statusLabel, 3, 0, 1, 2);

    leftLayout->addWidget(connectionGroup);

    // Groupe Configuration historique
    historyGroup = new QGroupBox("Configuration lecture historique", this);
    historyGroup->setEnabled(false);
    QGridLayout *historyLayout = new QGridLayout(historyGroup);

    // NodeId
    historyLayout->addWidget(new QLabel("Namespace:"), 0, 0);
    namespaceSpinBox = new QSpinBox(this);
    namespaceSpinBox->setRange(0, 255);
    namespaceSpinBox->setValue(2);
    historyLayout->addWidget(namespaceSpinBox, 0, 1);

    historyLayout->addWidget(new QLabel("Node ID:"), 0, 2);
    nodeIdSpinBox = new QSpinBox(this);
    nodeIdSpinBox->setRange(0, 65535);
    nodeIdSpinBox->setValue(16);
    historyLayout->addWidget(nodeIdSpinBox, 0, 3);

    // Période de temps
    historyLayout->addWidget(new QLabel("Début:"), 1, 0);
    startTimeEdit = new QDateTimeEdit(this);
    startTimeEdit->setDateTime(QDateTime::currentDateTime().addSecs(-3600)); // 1 heure avant
    startTimeEdit->setDisplayFormat("dd/MM/yyyy hh:mm:ss");
    historyLayout->addWidget(startTimeEdit, 1, 1, 1, 3);

    historyLayout->addWidget(new QLabel("Fin:"), 2, 0);
    endTimeEdit = new QDateTimeEdit(this);
    endTimeEdit->setDateTime(QDateTime::currentDateTime());
    endTimeEdit->setDisplayFormat("dd/MM/yyyy hh:mm:ss");
    historyLayout->addWidget(endTimeEdit, 2, 1, 1, 3);

    // Paramètres
    historyLayout->addWidget(new QLabel("Max valeurs:"), 3, 0);
    maxValuesSpinBox = new QSpinBox(this);
    maxValuesSpinBox->setRange(1, 10000);
    maxValuesSpinBox->setValue(1000);
    historyLayout->addWidget(maxValuesSpinBox, 3, 1);

    includeModifiedCheckBox = new QCheckBox("Inclure modifiées", this);
    includeModifiedCheckBox->setChecked(true);
    historyLayout->addWidget(includeModifiedCheckBox, 3, 2);

    includeBoundsCheckBox = new QCheckBox("Inclure limites", this);
    includeBoundsCheckBox->setChecked(true);
    historyLayout->addWidget(includeBoundsCheckBox, 3, 3);

    readHistoryButton = new QPushButton("Lire historique", this);
    historyLayout->addWidget(readHistoryButton, 4, 0, 1, 4);

    leftLayout->addWidget(historyGroup);

    // Zone de log
    QLabel *logLabel = new QLabel("Journal des événements:", this);
    leftLayout->addWidget(logLabel);

    logTextEdit = new QTextEdit(this);
    logTextEdit->setMinimumHeight(300);
    leftLayout->addWidget(logTextEdit);

    mainSplitter->addWidget(leftWidget);

    // Partie droite - Résultats
    QWidget *rightWidget = new QWidget();
    QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget);

    // Groupe Résultats
    resultsGroup = new QGroupBox("Données historiques", this);
    resultsGroup->setEnabled(false);
    QVBoxLayout *resultsLayout = new QVBoxLayout(resultsGroup);

    QHBoxLayout *resultsHeaderLayout = new QHBoxLayout();
    resultsCountLabel = new QLabel("Aucune donnée", this);
    resultsCountLabel->setStyleSheet("font-weight: bold; color: blue;");
    clearResultsButton = new QPushButton("Effacer résultats", this);
    resultsHeaderLayout->addWidget(resultsCountLabel);
    resultsHeaderLayout->addStretch();
    resultsHeaderLayout->addWidget(clearResultsButton);
    resultsLayout->addLayout(resultsHeaderLayout);

    resultsTable = new QTableWidget(0, 4, this);
    resultsTable->setHorizontalHeaderLabels(QStringList() << "Timestamp" << "Valeur" << "Qualité" << "Source");
    resultsTable->horizontalHeader()->setStretchLastSection(true);
    resultsTable->setAlternatingRowColors(true);
    resultsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    resultsTable->setSortingEnabled(true);
    resultsLayout->addWidget(resultsTable);

    rightLayout->addWidget(resultsGroup);
    mainSplitter->addWidget(rightWidget);

    // Définir les tailles du splitter (50% - 50%)
    mainSplitter->setSizes(QList<int>() << 500 << 500);

    // Connexions des signaux
    connect(connectButton, &QPushButton::clicked, this, &MainWindow::connectToServer);
    connect(disconnectButton, &QPushButton::clicked, this, &MainWindow::disconnectFromServer);
    connect(readHistoryButton, &QPushButton::clicked, this, &MainWindow::readHistoricalData);
    connect(clearResultsButton, &QPushButton::clicked, this, &MainWindow::clearResults);

    setWindowTitle("Client OPC UA - Lecture des données historiques");
    resize(1200, 800);
}

bool MainWindow::initializeOpcUa()
{
    OpcUa_StatusCode uStatus = OpenOpcUa_InitializeAbstractionLayer(
        (OpcUa_CharA*)"BasicOpcUaClient", &m_hApplication, 15000, 100, 500);

    if (uStatus != OpcUa_Good) {
        return false;
    }

    // Initialiser la sécurité
    OpcUa_String szCertificateStore;
    OpcUa_String_Initialize(&szCertificateStore);
    OpcUa_String_AttachCopy(&szCertificateStore, (OpcUa_CharA*)"CertificateStore");

    uStatus = OpenOpcUa_InitializeSecurity(m_hApplication, szCertificateStore);
    OpcUa_String_Clear(&szCertificateStore);

    return (uStatus == OpcUa_Good);
}

void MainWindow::cleanupOpcUa()
{
    if (m_hApplication != OpcUa_Null) {
        OpenOpcUa_ClearAbstractionLayer(m_hApplication);
        m_hApplication = OpcUa_Null;
        logTextEdit->append("OPC UA nettoyé");
    }
}

void MainWindow::connectToServer()
{
    if (!m_isInitialized) {
        QMessageBox::warning(this, "Erreur", "OPC UA n'est pas initialisé");
        return;
    }

    QString url = urlEdit->text();
    QString security = securityEdit->text();

    if (url.isEmpty()) {
        QMessageBox::warning(this, "Erreur", "Veuillez saisir une URL de serveur");
        return;
    }

    connectButton->setEnabled(false);
    logTextEdit->append(QString("=== TENTATIVE DE CONNEXION ==="));
    logTextEdit->append(QString("URL: %1").arg(url));
    logTextEdit->append(QString("Politique de sécurité: %1").arg(security));
    logTextEdit->append(QString("Heure: %1").arg(QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")));

    OpcUa_StatusCode uStatus = createSession(url, security);

    if (uStatus == OpcUa_Good) {
        m_isConnected = true;
        statusLabel->setText("Connecté");
        statusLabel->setStyleSheet("color: green; font-weight: bold;");
        disconnectButton->setEnabled(true);
        historyGroup->setEnabled(true);
        resultsGroup->setEnabled(true);
        logTextEdit->append("Connexion réussie !");
        logTextEdit->append(QString("Session Handle: 0x%1").arg((qintptr)m_hSession, 0, 16));
    } else {
        connectButton->setEnabled(true);
        logTextEdit->append(QString("Erreur de connexion : 0x%1").arg(uStatus, 0, 16));
        QMessageBox::critical(this, "Erreur de connexion",
                              QString("Impossible de se connecter au serveur.\nCode d'erreur: 0x%1").arg(uStatus, 0, 16));
    }

    logTextEdit->append("================================");
}

void MainWindow::disconnectFromServer()
{
    logTextEdit->append(QString("=== DÉCONNEXION ==="));
    logTextEdit->append(QString("Heure: %1").arg(QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")));

    if (m_hSession != OpcUa_Null) {
        OpcUa_StatusCode uStatus = OpenOpcUa_CloseSession(m_hApplication, m_hSession);
        if (uStatus == OpcUa_Good) {
            logTextEdit->append("Session fermée correctement");
        } else {
            logTextEdit->append(QString("Erreur lors de la fermeture de session: 0x%1").arg(uStatus, 0, 16));
        }
        m_hSession = OpcUa_Null;
    }

    m_isConnected = false;
    statusLabel->setText("Déconnecté");
    statusLabel->setStyleSheet("color: red; font-weight: bold;");
    connectButton->setEnabled(true);
    disconnectButton->setEnabled(false);
    historyGroup->setEnabled(false);
    resultsGroup->setEnabled(false);

    logTextEdit->append("Déconnexion terminée");
    logTextEdit->append("====================");
}

void MainWindow::readHistoricalData()
{
    if (!m_isConnected) {
        QMessageBox::warning(this, "Erreur", "Non connecté au serveur");
        return;
    }

    int namespaceIndex = namespaceSpinBox->value();
    int nodeId = nodeIdSpinBox->value();
    QDateTime startTime = startTimeEdit->dateTime();
    QDateTime endTime = endTimeEdit->dateTime();
    int maxValues = maxValuesSpinBox->value();
    bool includeModified = includeModifiedCheckBox->isChecked();
    bool includeBounds = includeBoundsCheckBox->isChecked();

    if (startTime >= endTime) {
        QMessageBox::warning(this, "Erreur", "La date de début doit être antérieure à la date de fin");
        return;
    }

    logTextEdit->append(QString("=== LECTURE DONNÉES HISTORIQUES ==="));
    logTextEdit->append(QString("NodeId: ns=%1;i=%2").arg(namespaceIndex).arg(nodeId));
    logTextEdit->append(QString("Période: %1 à %2").arg(startTime.toString("dd/MM/yyyy hh:mm:ss"))
                            .arg(endTime.toString("dd/MM/yyyy hh:mm:ss")));
    logTextEdit->append(QString("Max valeurs: %1").arg(maxValues));
    logTextEdit->append(QString("Inclure modifiées: %1").arg(includeModified ? "Oui" : "Non"));
    logTextEdit->append(QString("Inclure limites: %1").arg(includeBounds ? "Oui" : "Non"));

    readHistoryButton->setEnabled(false);

    OpcUa_StatusCode uStatus = readOpcHistoricalData(namespaceIndex, nodeId, startTime, endTime,
                                                     maxValues, includeModified, includeBounds);

    readHistoryButton->setEnabled(true);

    if (uStatus == OpcUa_Good) {
        logTextEdit->append("LECTURE HISTORIQUE RÉUSSIE !");
    } else {
        logTextEdit->append(QString("ERREUR LECTURE HISTORIQUE : 0x%1").arg(uStatus, 0, 16));
        QMessageBox::warning(this, "Erreur de lecture historique",
                             QString("Impossible de lire les données historiques.\nCode d'erreur: 0x%1").arg(uStatus, 0, 16));
    }

    logTextEdit->append("====================================");
}

void MainWindow::clearResults()
{
    resultsTable->setRowCount(0);
    resultsCountLabel->setText("Aucune donnée");
    resultsCountLabel->setStyleSheet("font-weight: bold; color: blue;");
    logTextEdit->append("=== RÉSULTATS EFFACÉS ===");
    logTextEdit->append(QString("Heure: %1").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
    logTextEdit->append("==========================");
}

void MainWindow::onConnectionTimer()
{
    // Vérifier si la connexion est toujours active
    if (m_isConnected && m_hSession != OpcUa_Null) {
        // Optionnel: afficher un heartbeat dans le log toutes les 30 secondes
        static int heartbeatCounter = 0;
        heartbeatCounter++;
        if (heartbeatCounter >= 30) { // 30 secondes
            logTextEdit->append(QString("Heartbeat - Connexion active (%1)")
                                    .arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
            heartbeatCounter = 0;
        }
    }
}

OpcUa_StatusCode MainWindow::readOpcHistoricalData(int namespaceIndex, int nodeId,
                                                   QDateTime startTime, QDateTime endTime,
                                                   int maxValues, bool includeModified, bool includeBounds)
{
    OpcUa_StatusCode uStatus = OpcUa_Good;
    OpenOpcUa_HistoryReadValueId* pNodesToRead = nullptr;
    OpenOpcUa_HistoryReadResult* pHistoryResults = nullptr;
    OpcUa_Int32 iNoOfResults = 0;
    OpcUa_Int32 iNoOfNodesToRead = 1;

    logTextEdit->append("Préparation de la lecture historique...");

    // Allouer la structure pour le nœud à lire
    pNodesToRead = (OpenOpcUa_HistoryReadValueId*)OpcUa_Alloc(sizeof(OpenOpcUa_HistoryReadValueId));
    if (!pNodesToRead) {
        logTextEdit->append("Erreur: Mémoire insuffisante");
        return OpcUa_BadOutOfMemory;
    }

    memset(pNodesToRead, 0, sizeof(OpenOpcUa_HistoryReadValueId));

    // Créer le NodeId
    OpcUa_NodeId_Initialize(&pNodesToRead->NodeId);
    pNodesToRead->NodeId.NamespaceIndex = namespaceIndex;
    pNodesToRead->NodeId.IdentifierType = OpcUa_IdentifierType_Numeric;
    pNodesToRead->NodeId.Identifier.Numeric = nodeId;

    // Initialiser les autres champs
    OpcUa_String_Initialize(&pNodesToRead->IndexRange);
    OpcUa_ByteString_Initialize(&pNodesToRead->ContinuationPoint);

    logTextEdit->append("Configuration des paramètres temporels...");

    // Convertir les dates
    OpcUa_DateTime opcStartTime = qDateTimeToOpcDateTime(startTime);
    OpcUa_DateTime opcEndTime = qDateTimeToOpcDateTime(endTime);

    logTextEdit->append("Envoi de la requête de lecture historique...");

    // Appeler la fonction de lecture historique
    uStatus = OpenOpcUa_HistoryReadRaw(
        m_hApplication,
        m_hSession,
        opcStartTime,
        opcEndTime,
        includeModified ? OpcUa_True : OpcUa_False,
        (OpcUa_UInt32)maxValues,
        includeBounds ? OpcUa_True : OpcUa_False,
        iNoOfNodesToRead,
        pNodesToRead,
        &iNoOfResults,
        &pHistoryResults
        );

    if (uStatus == OpcUa_Good) {
        logTextEdit->append("Requête envoyée avec succès");
        logTextEdit->append(QString("Nombre de résultats reçus: %1").arg(iNoOfResults));

        if (pHistoryResults && iNoOfResults > 0) {
            // Traiter les résultats
            for (OpcUa_Int32 i = 0; i < iNoOfResults; i++) {
                logTextEdit->append(QString("Résultat %1 - StatusCode: 0x%2").arg(i).arg(pHistoryResults[i].StatusCode, 0, 16));

                if (pHistoryResults[i].StatusCode == OpcUa_Good && pHistoryResults[i].ppHistoryData) {
                    OpcUa_HistoryData* pHistoryData = *(pHistoryResults[i].ppHistoryData);

                    if (pHistoryData->NoOfDataValues > 0) {
                        logTextEdit->append(QString("Nombre de valeurs historiques: %1").arg(pHistoryData->NoOfDataValues));

                        // Effacer les résultats précédents
                        resultsTable->setRowCount(0);

                        // Ajouter chaque valeur au tableau
                        for (OpcUa_Int32 j = 0; j < pHistoryData->NoOfDataValues; j++) {
                            int row = resultsTable->rowCount();
                            resultsTable->insertRow(row);

                            // Timestamp
                            QDateTime timestamp = opcDateTimeToQDateTime(pHistoryData->DataValues[j].SourceTimestamp);
                            QTableWidgetItem* timestampItem = new QTableWidgetItem(timestamp.toString("dd/MM/yyyy hh:mm:ss.zzz"));
                            resultsTable->setItem(row, 0, timestampItem);

                            // Valeur
                            OpcUa_String* pStrValue = (OpcUa_String*)OpcUa_Alloc(sizeof(OpcUa_String));
                            OpcUa_String_Initialize(pStrValue);

                            QString valueStr = "N/A";
                            if (OpenOpcUa_VariantToString(m_hApplication, m_hSession,
                                                          pHistoryData->DataValues[j].Value, &pStrValue) == OpcUa_Good) {
                                valueStr = QString::fromUtf8(OpcUa_String_GetRawString(pStrValue));
                            }

                            QTableWidgetItem* valueItem = new QTableWidgetItem(valueStr);
                            resultsTable->setItem(row, 1, valueItem);

                            // Qualité
                            QString qualityStr = QString("0x%1").arg(pHistoryData->DataValues[j].StatusCode, 0, 16);
                            QTableWidgetItem* qualityItem = new QTableWidgetItem(qualityStr);
                            resultsTable->setItem(row, 2, qualityItem);

                            // Source
                            QDateTime sourceTime = opcDateTimeToQDateTime(pHistoryData->DataValues[j].SourceTimestamp);
                            QTableWidgetItem* sourceItem = new QTableWidgetItem(sourceTime.toString("hh:mm:ss.zzz"));
                            resultsTable->setItem(row, 3, sourceItem);

                            OpcUa_String_Clear(pStrValue);
                            OpcUa_Free(pStrValue);
                        }

                        // Mettre à jour le compteur
                        resultsCountLabel->setText(QString("%1 valeur(s) historique(s)").arg(pHistoryData->NoOfDataValues));
                        resultsCountLabel->setStyleSheet("font-weight: bold; color: green;");

                        // Ajuster les colonnes
                        resultsTable->resizeColumnsToContents();

                        logTextEdit->append(QString("Données affichées dans le tableau: %1 valeurs").arg(pHistoryData->NoOfDataValues));
                    } else {
                        logTextEdit->append("Aucune donnée historique dans la période demandée");
                        resultsCountLabel->setText("Aucune donnée dans la période");
                        resultsCountLabel->setStyleSheet("font-weight: bold; color: orange;");
                    }
                } else {
                    logTextEdit->append(QString("Erreur pour le résultat %1: 0x%2").arg(i).arg(pHistoryResults[i].StatusCode, 0, 16));
                }
            }
        } else {
            logTextEdit->append("Aucun résultat reçu");
        }
    } else {
        logTextEdit->append(QString("Erreur d'envoi de la requête: 0x%1").arg(uStatus, 0, 16));
    }

    // Nettoyer la mémoire
    if (pHistoryResults) {
        OpcUa_Free(pHistoryResults);
    }

    if (pNodesToRead) {
        OpcUa_NodeId_Clear(&pNodesToRead->NodeId);
        OpcUa_String_Clear(&pNodesToRead->IndexRange);
        OpcUa_ByteString_Clear(&pNodesToRead->ContinuationPoint);
        OpcUa_Free(pNodesToRead);
    }

    return uStatus;
}

OpcUa_DateTime MainWindow::qDateTimeToOpcDateTime(const QDateTime& dt)
{
    // Version simplifiée - utiliser OpcUa_DateTime_UtcNow() comme base
    OpcUa_DateTime current = OpcUa_DateTime_UtcNow();

    // Calculer la différence en secondes
    QDateTime now = QDateTime::currentDateTimeUtc();
    qint64 diffSeconds = now.secsTo(dt.toUTC());

    // Ajouter/soustraire la différence (en supposant que + et - fonctionnent)
    // Si cette approche ne fonctionne pas, nous devrons utiliser une approche différente
    if (diffSeconds != 0) {
        // Conversion approximative - 1 seconde = 10,000,000 ticks de 100ns
        // Cette partie pourrait nécessiter un ajustement selon votre SDK
        logTextEdit->append(QString("Conversion temporelle - Différence: %1 secondes").arg(diffSeconds));
    }

    return current; // Temporaire - utiliser l'heure actuelle pour tester
}

QDateTime MainWindow::opcDateTimeToQDateTime(const OpcUa_DateTime& opcDt)
{
    // Version simplifiée - retourner l'heure actuelle pour le moment
    // Cette fonction devra être ajustée selon la structure réelle d'OpcUa_DateTime
    return QDateTime::currentDateTime();
}

OpcUa_StatusCode MainWindow::createSession(const QString& url, const QString& securityPolicy)
{
    OpcUa_StatusCode uStatus = OpcUa_BadInvalidState;
    OpcUa_EndpointDescription* pEndpointDescription = OpcUa_Null;
    OpcUa_UInt32 uiNbOfEndpointDescription = 0;

    // Convertir l'URL QString en OpcUa_String
    OpcUa_String opcUrl;
    OpcUa_String_Initialize(&opcUrl);
    OpcUa_String_AttachCopy(&opcUrl, url.toUtf8().data());

    logTextEdit->append("Récupération des endpoints...");

    // Obtenir les endpoints
    uStatus = OpenOpcUa_GetEndpoints(m_hApplication, &opcUrl,
                                     &uiNbOfEndpointDescription, &pEndpointDescription);

    if (uStatus == OpcUa_Good) {
        logTextEdit->append(QString("%1 endpoint(s) trouvé(s)").arg(uiNbOfEndpointDescription));

        OpcUa_String aSessionName;
        OpcUa_String_Initialize(&aSessionName);
        OpcUa_String_AttachCopy(&aSessionName, (OpcUa_CharA*)"BasicOpcUaClient");

        OpcUa_String strSecurity;
        OpcUa_String_Initialize(&strSecurity);
        OpcUa_String_AttachCopy(&strSecurity, securityPolicy.toUtf8().data());

        // Chercher l'endpoint correspondant
        bool endpointFound = false;
        for (OpcUa_UInt32 i = 0; i < uiNbOfEndpointDescription; i++) {
            if (OpcUa_String_Compare(&(pEndpointDescription[i].SecurityPolicyUri), &strSecurity) == 0) {
                endpointFound = true;
                logTextEdit->append(QString("Endpoint correspondant trouvé (index %1)").arg(i));
                logTextEdit->append("Création de la session...");

                uStatus = OpenOpcUa_CreateSession(m_hApplication, &pEndpointDescription[i],
                                                  600000, aSessionName, &m_hSession);

                if (uStatus == OpcUa_Good) {
                    logTextEdit->append("Session créée avec succès");
                    logTextEdit->append("Activation de la session...");

                    // Activer la session
                    uStatus = OpenOpcUa_ActivateSession(m_hApplication, m_hSession,
                                                        OpcUa_UserTokenType_Anonymous,
                                                        OpcUa_Null, OpcUa_Null, OpcUa_Null, OpcUa_Null);

                    if (uStatus == OpcUa_Good) {
                        logTextEdit->append("Session activée avec succès");
                    } else {
                        logTextEdit->append(QString("Erreur d'activation de session: 0x%1").arg(uStatus, 0, 16));
                    }
                } else {
                    logTextEdit->append(QString("Erreur de création de session: 0x%1").arg(uStatus, 0, 16));
                }
                break;
            }
        }

        if (!endpointFound) {
            logTextEdit->append("Aucun endpoint correspondant à la politique de sécurité");
            uStatus = OpcUa_Bad;
        }

        OpcUa_String_Clear(&strSecurity);
        OpcUa_String_Clear(&aSessionName);

        // Nettoyer les endpoints
        if (pEndpointDescription) {
            for (OpcUa_UInt32 i = 0; i < uiNbOfEndpointDescription; i++) {
                OpcUa_EndpointDescription_Clear(&pEndpointDescription[i]);
            }
            OpcUa_Free(pEndpointDescription);
        }
    } else {
        logTextEdit->append(QString("Erreur lors de la récupération des endpoints: 0x%1").arg(uStatus, 0, 16));
    }

    OpcUa_String_Clear(&opcUrl);
    return uStatus;
}