Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Controllare connessione (ping verso un IP)



  • Ciao a tutti.

    Sto realizzando un app che deve girare su Raspbian, e fra le cose che faccio ci sono anche delle chiamate verso delle API che in alcuni casi si trovano in una centralina separata.

    Quindi sulla vecchia webapp io controllavo con un plugin javascript chiamato offline.js, la presenza o meno della connessione.

    Nel plugin specificavo qual'era l'IP che dovevo controllare. Faceva un check ogni 5 secondi, e se c'era connessione mostravo un pallino verde sull'interfaccia.

    Anche quando la connessione andava giù e poi tornava su facevo delle cose: per esempio quando tornava facevo di nuovo la chiamata alle API, quando andava giù fermavo i timer, e cose del genere.

    Per l'interfaccia uso QML, e in C++ al momento ho il solo file main.cpp che al momento l'unica cosa che fa è chiamare il file main.qml.

    Essendo nuovo non so neanche come si scambiano informazioni fra c++ e qml (nel caso in qml puro non si possa fare).

    Ve ne sarei grato se mi aiutate a fare questa cosa per controllare se un IP risponde o meno e mostrare questo pallino verde sull'interfaccia.

    Grazie



  • @marco_88

    class PingDaemon : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(bool connectionIsValid READ connectionIsValid NOTIFY connectionIsValidChanged)
    
    public:
        explicit PingDaemon(QObject *parent = nullptr);
    
        void start(int interval, const QString &host = "8.8.8.8");
        void stop();
    
        bool connectionIsValid() const;
    
    Q_SIGNALS:
        void connectionIsValidChanged();
    
    private Q_SLOTS:
        void ping();
    
    private:
        QTimer m_pingTimer{};
        QString m_currentHost{};
        bool m_connectionIsValid{false};
    };
    
    PingDaemon::PingDaemon(QObject *parent)
        : QObject(parent)
    {
        connect(&m_pingTimer, &QTimer::timeout, this, &PingDaemon::ping);
    
        QProcess p;
        p.start(QStringLiteral("which ping"));
        p.waitForFinished();
    
        if (p.exitCode() == QProcess::CrashExit) {
            throw std::logic_error("Application 'ping' not found.");
        }
    }
    
    void PingDaemon::start(int interval, const QString &host)
    {
        m_currentHost = host;
        m_pingTimer.start(interval * 1000);
        ping();
    }
    
    void PingDaemon::stop()
    {
        m_pingTimer.stop();
    }
    
    bool PingDaemon::connectionIsValid() const
    {
        return m_connectionIsValid;
    }
    
    void PingDaemon::ping()
    {
        const auto host = m_currentHost;
        std::thread([host, this] {
            const auto cmd = "ping -c 1 -W 1 " + host;
            qDebug() << cmd;
            const auto ret = ms::CliUtils::execCmd(cmd);
            const auto currentConnectionStatus = (ret.exitCode == 0);
    
            if (m_connectionIsValid != currentConnectionStatus) {
                m_connectionIsValid = currentConnectionStatus;
                Q_EMIT connectionIsValidChanged();
            }
        })
            .detach();
    }
    
    


  • Ciao

    Ti ringrazio per la risposta e per il codice che hai postato, però mi dovresti spiegare come usarlo. Io sono nuovo di questo fantastico mondo.

    Prima domanda: questo codice lo devo mettere sul main.cpp o devo creare un nuovo file cpp dove mettere questo codice?

    Seconda domanda: dal mio file main.qml come faccio a ottenere l'informazione sulla connessione? Voglio mettere questo pallino sulla toolbar dell'app.

    Terza domanda: dove specifico l'intervallo per il ping?

    Vorrei che fosse più immediato e veloce possibile a capire se c'è connessione o no. Che il pallino rosso o verde cambi nella maniera più instantanea possibile senza causare rallentamenti all'app. E inoltre dovrei usare questa informazione anche quando richiamo delle request in funzioni javascript che richiamo da QML.

    Grazie



  • @marco_88
    eh diciamo che se non sai il c++ diventa difficile ehehe... Comunque questi due sono un file cpp e un file hpp poi li integri le progetto, istanzi nel main un oggetto di tipo PingDaemon e poi lo esponi in QML, c'e' gia' la property connectionIsValid per verificare la connessione



  • Ok ora provo. Quindi devo creare 2 file un file lo chiamo checkconnection.cpp e l'altro checkconnection.hpp. Li aggiungo al progetto e dal file main.cpp devo vedere come instanziare questo oggetto PingDaemon. Poi su qml posso sapere se c'è o no connessione usando la propriety connectionIsValid. Tutto corretto a livello teorico?



  • Ho modificato il file main.cpp in questo modo:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQuickStyle>
    #include <QProcess>
    #include <QThread>
    #include <QDebug>
    #include "checkconnection.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    
        QGuiApplication app(argc, argv);
        QQuickStyle::setStyle("Material");
    
        QQmlApplicationEngine engine;
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    PingDaemon::PingDaemon(QObject *parent)
        : QObject(parent)
    {
        connect(&m_pingTimer, &QTimer::timeout, this, &PingDaemon::ping);
    
        QProcess p;
        p.start(QStringLiteral("which ping"));
        p.waitForFinished();
    
        if (p.exitCode() == QProcess::CrashExit) {
            throw std::logic_error("Application 'ping' not found.");
        }
    }
    
    void PingDaemon::start(int interval, const QString &host)
    {
        m_currentHost = host;
        m_pingTimer.start(interval * 1000);
        ping();
    }
    
    void PingDaemon::stop()
    {
        m_pingTimer.stop();
    }
    
    bool PingDaemon::connectionIsValid() const
    {
        return m_connectionIsValid;
    }
    
    void PingDaemon::ping()
    {
        const auto host = m_currentHost;
        std::thread([host, this] {
            const auto cmd = "ping -c 1 -W 1 " + host;
            qDebug() << cmd;
            const auto ret = ms::CliUtils::execCmd(cmd);
            const auto currentConnectionStatus = (ret.exitCode == 0);
    
            if (m_connectionIsValid != currentConnectionStatus) {
                m_connectionIsValid = currentConnectionStatus;
                Q_EMIT connectionIsValidChanged();
            }
        })
            .detach();
    }
    

    Poi ho creato il file checkconnection.h così:

    #ifndef CHECKCONNECTION_H
    #define CHECKCONNECTION_H
    
    #include <QObject>
    #include <QTimer>
    
    class PingDaemon : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(bool connectionIsValid READ connectionIsValid NOTIFY connectionIsValidChanged)
    
    public:
        explicit PingDaemon(QObject *parent = nullptr);
    
        void start(int interval, const QString &host = "8.8.8.8");
        void stop();
    
        bool connectionIsValid() const;
    
    Q_SIGNALS:
        void connectionIsValidChanged();
    
    private Q_SLOTS:
        void ping();
    
    private:
        QTimer m_pingTimer{};
        QString m_currentHost{};
        bool m_connectionIsValid{false};
    };
    
    #endif
    

    Ma nel file main.cpp ho un errore su questa riga:

    const auto ret = ms::CliUtils::execCmd(cmd);
    

    Mi sottolinea ms:: e mi dice questo:

    use of undeclared identifier ms
    

    Poi mi da un errore anche sulla seguente riga:

    PingDaemon::PingDaemon(QObject *parent)
        : QObject(parent)
    

    L'errore è il seguente:

    error: undefined reference to `vtable for PingDaemon'
    

    Grazie



  • Sono riuscito a compilare, e una volta integrato provo a leggerlo facendo così:

    PingDaemon {
                    id: checkNetwork
                }
                Label {
                    horizontalAlignment: Qt.AlignHCenter
                    verticalAlignment: Qt.AlignVCenter
                    color: 'white'
                    font.bold: true
                    text: checkNetwork.connectionIsValid
                }
    

    Mi risponde sempre false però.

    Ho fatto bee oppure ho sbagliato qualcosa per cercare di leggere lo stato da QML?

    Grazie



  • @marco_88
    Guarda se vuoi posso offrirti supporto tramite altri canali, scrivimi in dm, perche' altrimenti qui diventerebbe un po' lunga la questione... cioe' non sapendo le basi si fa fatica a spiegarsi nei commenti



  • Ho fatto progressi:) Fammi sapere però dove sbaglio perchè ricevo sempre false, quando mi aspeteterei true perchè quell'ip se faccio il ping mi risponde.

    Ho solo cambiato questa parte perchè usando Linux ho letto che per fare il ping si faceva così:

    void PingDaemon::ping()
    {
        const auto host = m_currentHost;
        std::thread([host, this] {
            const auto cmd = "ping -c 1 -W 1 " + host;
            qDebug() << cmd;
            const auto ret = QProcess::execute(cmd);
            const auto currentConnectionStatus = (ret == 0);
    
            if (m_connectionIsValid != currentConnectionStatus) {
                m_connectionIsValid = currentConnectionStatus;
                Q_EMIT connectionIsValidChanged();
            }
        })
            .detach();
    }
    

    Grazie


Log in to reply