0

現在、特定のメッセージが表示されたときに GPIO アクションをトリガーすることになっている Raspberry Pi 用のドアベル アプリケーションに取り組んでいます。

したがって、私はメインクラスを持っていますdoorbell:

ドアベル.h

class doorbell {
public:

    // ...
    void registerMqttListener();

    // ...
}

ドアベル.cpp

#include "doorbell.h"
#include "threadLauncher.h"

doorbell::doorbell() {
    // ...

    threadLauncher& launcher { threadLauncher::inst() };

    launcher.go(std::make_unique<std::thread>(&doorbell::registerMqttListener, this));
}

void doorbell::registerMqttListener()
{
    // create our MQTT client and start subscription
    mqttClient client(_conf);

    client.subscribeToKnownTopic();
}

そして、ドアベル クラスから開始されたスレッドを処理する単純なthreadLauncherクラス:

threadLauncher.h

#include <vector>
#include <thread>
#include <functional>

class threadLauncher
{
public:
    void go(const std::string& shellCmd);
    void go(std::unique_ptr<std::thread> thread);

    std::string execShellCmd(std::string const& cmd);

    static threadLauncher& inst()
    {
        static threadLauncher inst;
        return inst;
    }
private:
    threadLauncher() {}
    ~threadLauncher();

    std::vector<std::unique_ptr<std::thread>> _asyncThreads;
};

スレッドランチャー.cpp

#include "threadLauncher.h"

#include "log.h"

void threadLauncher::go(const std::string& args)
{
    _asyncThreads.emplace_back(std::make_unique<std::thread>(&threadLauncher::execShellCmd, this, args));
}

void threadLauncher::go(std::unique_ptr<std::thread> thread)
{
    _asyncThreads.emplace_back(std::move(thread));
}

std::string threadLauncher::execShellCmd(std::string const& cmd)
{
    std::array<char, 128> buffer;
    std::string result;

    std::unique_ptr<FILE, decltype(&pclose)> pipe { popen(cmd.c_str(), "r"), pclose };

    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }

    while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
        result += buffer.data();
    }

    return result;
}

threadLauncher::~threadLauncher()
{
    for (auto& at : _asyncThreads) {
        if (at) {
            if (at->joinable()) {
                at->join();
            }

            at.release();
        }
    }
}

最後にmqttClient、MQTT を処理するクラスがあります。それはmosquittoppに基づいています:

mqttClient.h

#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H

#include <string>

// 3rd-party
#include <mosquittopp.h>

#include "config.h"

class mqttClient : public mosqpp::mosquittopp
{
public:
    mqttClient();

    ~mqttClient();

    bool send(const std::string& message);
    bool subscribeTo(const std::string& topic);
    bool subscribeToKnownTopic();

private:
    std::string _host;
    std::string _id;
    std::string _topic;
    std::string _token;

    int _port;
    int _keepalive;

    void on_connect(int rc) override;
    void on_disconnect(int rc) override;
    void on_publish(int mid) override;
    void on_message(const struct mosquitto_message* message) override;
    void on_subscribe(int mid, int qos_count, const int* granted_qos) override;
};

#endif // MQTTCLIENT_H

mqttClient.cpp

#include "mqttClient.h"

#include <iostream>

#include "threadLauncher.h"


mqttClient::mqttClient()
{
    _id = "myid";
    _topic = "mytopic";
    _host = "myhost";
    _port = 1337;
    _token = "mytoken";

    mosqpp::lib_init();

    connect(_host.c_str(), _port, _keepalive);

    loop_start();
}

mqttClient::~mqttClient()
{
    loop_stop();
    mosqpp::lib_cleanup();
}

/**
 * @brief Publishes a given message to the MQTT client
 * @param message - The message to be sent.
 * @return success or failure.
*/
bool mqttClient::send(std::string const& message)
{
    int retval = publish(nullptr, _topic.c_str(), message.length(), message.c_str(), 1, false);
    return MOSQ_ERR_SUCCESS == retval;
}

/**
 * @brief Subscribes to a given topic.
 * @param topic - The topic to be subscribed to.
 * @return success or failure.
*/
bool mqttClient::subscribeTo(std::string const& topic)
{
    int retval = subscribe(nullptr, topic.c_str(), 1);
    return MOSQ_ERR_SUCCESS == retval;
}

/**
 * @brief Subscribes to a topic that was set on instantiation of the mqttClient object.
 * @return success or failure.
*/
bool mqttClient::subscribeToKnownTopic()
{
    int retval = subscribe(nullptr, _topic.c_str(), 1);
    return MOSQ_ERR_SUCCESS == retval;
}

/**
 * @brief Connected callback.
 * @param rc - return code
*/
void mqttClient::on_connect(int rc)
{
    if (0 == rc) {
        std::cout << "[MQTT] Connected (" + std::to_string(rc) + ").";
    } else {
        std::cout << "[MQTT] Connection failed (" + std::to_string(rc) + ")!";
    }
}

/**
 * @brief Disconnect callback.
 * @author taibsu
 * @param rc - return code
*/
void mqttClient::on_disconnect(int rc)
{
}

/**
 * @brief TODO implement
*/
void mqttClient::on_publish(int mid)
{

}

/**
 * @brief Message callback.
 * @author taiBsu
 * @param message - The message to be reacted to.
 * @TODO All this stuff should be called from another class.
*/
void mqttClient::on_message(const struct mosquitto_message* message)
{
    std::string msg { reinterpret_cast<char*>(message->payload) };

    if (msg.find(_token) == std::string::npos) {
        return;
    }

    // @TODO rework this

    if (msg.find("onOpenDoor") != std::string::npos) {
        std::string openDoorCmd { "python /home/pi/open.py" };

        threadLauncher::inst().go(openDoorCmd);
    }

    if (msg.find("onPlayNotAtHome") != std::string::npos) {
        std::string notAtHomeCmd { "aplay /home/pi/sounds/notAtHome.wav" };

        threadLauncher::inst().go(notAtHomeCmd);
    }

    if (msg.find("onPlayOnOurWay") != std::string::npos) {
        std::string onOurWayCmd { "aplay /home/pi/sounds/onOurWay.wav" };

        threadLauncher::inst().go(onOurWayCmd);
    }

    if (msg.find("onTakePicture") != std::string::npos) {
        std::string chat_id { msg.substr(msg.find_last_of(':') + 1, msg.size() - 1) };
        std::string quality { "80" };
        std::string delay { "1s" };
        std::string rotation { "270" };
        std::string iso { "600" };
        std::string width { "600" };
        std::string height { "800" };
        std::string pictureName { "door_manual.jpg" };

        std::string cmd {
            "raspistill -q " + quality +
            " -t " + delay +
            " --rotation " + rotation +
            " -ISO " + iso +
            " -w " + width +
            " -h " + height +
            " -o " + pictureName
        };

        threadLauncher::inst().go(cmd);
    }
}

/**
 * @brief Subscription callback
*/
void mqttClient::on_subscribe(int mid, int qos_count, const int* granted_qos)
{
    std::cout << "[MQTT] Subscription started for topic " + _topic + ".";
}

最後に、私のエントリ ポイント:

main.cpp

#include "doorbell.h"

int main() {
    doorbell bell;

    while (true) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

これが私の問題です:

Raspberry Pi 4 B でアプリケーションを起動すると、4 つの CPU のいずれかで 100% の CPU 負荷がかかり続けます。

launcher.go(std::make_unique<std::thread>(&doorbell::registerMqttListener, this));コンストラクターで行を削除するとdoorbell.cpp、すべて正常に機能します。

これはmosquittoppライブラリの誤った動作である可能性がありますか、それともここで何か間違ったことをしていますか?

編集:ここにhtop出力があります: HTOP出力

追加の質問: Visual Studio で IoT デバイスをリモート デバッグするときに、CPU や RAM の使用状況をプロファイルするにはどうすればよいですか?

編集:loop_start()からの関数mosquittoppが 100% の CPU 負荷を引き起こしている ことを確認できるようになりました。これは mosquittopp の問題ですか、それとも間違った方法で使用していますか?

4

1 に答える 1