MQTT ready to use
change subscription logic - now keep subscriptions independently from connecting state. No unregisters on disconnect, but resubscriptions on connect. So one-time subscription on app start and just connect() on lost connection - all subscriptions keeps
This commit is contained in:
@@ -50,7 +50,6 @@ public:
|
|||||||
void connect(const PIString & address, const PIString & client, const PIString & username = {}, const PIString & password = {});
|
void connect(const PIString & address, const PIString & client, const PIString & username = {}, const PIString & password = {});
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
|
|
||||||
void subscribe(const PIString & topic, MessageFunction functor, QoS qos = QoS::Level1);
|
void subscribe(const PIString & topic, MessageFunction functor, QoS qos = QoS::Level1);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -68,6 +67,8 @@ public:
|
|||||||
bool isConnecting() const { return m_status == Connecting; }
|
bool isConnecting() const { return m_status == Connecting; }
|
||||||
bool isConnected() const { return m_status == Connected; }
|
bool isConnected() const { return m_status == Connected; }
|
||||||
|
|
||||||
|
PIStringList usedTopics() const;
|
||||||
|
|
||||||
EVENT0(connected);
|
EVENT0(connected);
|
||||||
EVENT1(disconnected, PIMQTT::Error, code);
|
EVENT1(disconnected, PIMQTT::Error, code);
|
||||||
EVENT1(receivedUnhandled, PIMQTT::MessageConst, message);
|
EVENT1(receivedUnhandled, PIMQTT::MessageConst, message);
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ struct PIMQTT::Client::Endpoint: public PIHTTP::ServerEndpoint {
|
|||||||
PIMQTT::Client::MessageFunction function;
|
PIMQTT::Client::MessageFunction function;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TopicUsage {
|
||||||
|
int counter = 0;
|
||||||
|
PIMQTT::QoS qos = PIMQTT::QoS::Level1;
|
||||||
|
};
|
||||||
|
|
||||||
struct EndpointsStorage {
|
struct EndpointsStorage {
|
||||||
PIMap<uint, PIMap<PIString, PIVector<PIMQTT::Client::Endpoint>>> prepared; // [priority][topic] -> endpoints
|
PIMap<uint, PIMap<PIString, PIVector<PIMQTT::Client::Endpoint>>> prepared; // [priority][topic] -> endpoints
|
||||||
PIMap<PIString, int> topics_binded; // [topic] -> count
|
PIMap<PIString, TopicUsage> topics_binded; // [topic] -> TopicUsage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -117,13 +122,21 @@ void PIMQTT::Client::disconnect() {
|
|||||||
|
|
||||||
void PIMQTT::Client::subscribe(const PIString & topic, MessageFunction functor, QoS qos) {
|
void PIMQTT::Client::subscribe(const PIString & topic, MessageFunction functor, QoS qos) {
|
||||||
if (is_destoying) return;
|
if (is_destoying) return;
|
||||||
worker->enqueueTask([this, topic, functor, qos] { subscribeInternal({topic, functor, qos}); });
|
Subscribe sub{topic, functor, qos};
|
||||||
|
// piCout << "subscribe" << topic;
|
||||||
|
PIString mqtt_topic = registerSubscribe(sub);
|
||||||
|
if (mqtt_topic.isEmpty()) return;
|
||||||
|
sub.topic = mqtt_topic;
|
||||||
|
worker->enqueueTask([this, sub] { subscribeInternal(sub); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::unsubscribe(const PIString & topic) {
|
void PIMQTT::Client::unsubscribe(const PIString & topic) {
|
||||||
if (is_destoying) return;
|
if (is_destoying) return;
|
||||||
worker->enqueueTask([this, topic] { unsubscribeInternal(topic); });
|
// piCout << "unsubscribe" << topic;
|
||||||
|
PIString mqtt_topic = unregisterSubscribe(topic);
|
||||||
|
if (mqtt_topic.isEmpty()) return;
|
||||||
|
worker->enqueueTask([this, mqtt_topic] { unsubscribeInternal(mqtt_topic); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -142,15 +155,27 @@ void PIMQTT::Client::publish(const MessageConst & msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIStringList PIMQTT::Client::usedTopics() const {
|
||||||
|
return PRIVATE->endpoints.getRef()->topics_binded.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::unsubscribeAll() {
|
void PIMQTT::Client::unsubscribeAll() {
|
||||||
unsubscribe("#");
|
{
|
||||||
|
auto ref = PRIVATE->endpoints.getRef();
|
||||||
|
auto tit = ref->topics_binded.makeIterator();
|
||||||
|
while (tit.next()) {
|
||||||
|
if (tit.value().counter <= 0) continue;
|
||||||
|
PIString mqtt_topic = tit.key();
|
||||||
|
worker->enqueueTask([this, mqtt_topic] { unsubscribeInternal(mqtt_topic); });
|
||||||
|
}
|
||||||
|
}
|
||||||
unregisterAll();
|
unregisterAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::mqtt_connectionLost() {
|
void PIMQTT::Client::mqtt_connectionLost() {
|
||||||
piCoutObj << "mqtt_connectionLost";
|
// piCoutObj << "mqtt_connectionLost";
|
||||||
unregisterAll();
|
|
||||||
PRIVATE->connected = false;
|
PRIVATE->connected = false;
|
||||||
changeStatus(Idle);
|
changeStatus(Idle);
|
||||||
disconnected(Error::ServerUnavailable);
|
disconnected(Error::ServerUnavailable);
|
||||||
@@ -163,7 +188,7 @@ void PIMQTT::Client::mqtt_deliveryComplete(int token) {}
|
|||||||
void PIMQTT::Client::mqtt_messageArrived(MessageMutable & msg) {
|
void PIMQTT::Client::mqtt_messageArrived(MessageMutable & msg) {
|
||||||
PIStringList in_path = msg.topicList();
|
PIStringList in_path = msg.topicList();
|
||||||
PIMQTT::Client::MessageFunction function;
|
PIMQTT::Client::MessageFunction function;
|
||||||
piCoutObj << "mqtt_messageArrived";
|
// piCoutObj << "mqtt_messageArrived";
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
auto ref = PRIVATE->endpoints.getRef();
|
auto ref = PRIVATE->endpoints.getRef();
|
||||||
@@ -203,7 +228,7 @@ PIString PIMQTT::Client::registerSubscribe(const Subscribe & sub) {
|
|||||||
piCoutObj << "Warning: subscribe to empty topic, ignore";
|
piCoutObj << "Warning: subscribe to empty topic, ignore";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
piCout << sub.topic << "->" << topic << ep.priority;
|
// piCout << sub.topic << "->" << topic << ep.priority;
|
||||||
bool is_new_topic = false;
|
bool is_new_topic = false;
|
||||||
auto ref = PRIVATE->endpoints.getRef();
|
auto ref = PRIVATE->endpoints.getRef();
|
||||||
auto & eps_by_topic(ref->prepared[ep.priority][topic]);
|
auto & eps_by_topic(ref->prepared[ep.priority][topic]);
|
||||||
@@ -214,10 +239,11 @@ PIString PIMQTT::Client::registerSubscribe(const Subscribe & sub) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
eps_by_topic << ep;
|
eps_by_topic << ep;
|
||||||
auto & counter(ref->topics_binded[topic]);
|
auto & usage(ref->topics_binded[topic]);
|
||||||
is_new_topic = counter == 0;
|
is_new_topic = usage.counter == 0;
|
||||||
++counter;
|
++usage.counter;
|
||||||
if (!is_new_topic) return {};
|
if (!is_new_topic) return {};
|
||||||
|
usage.qos = sub.qos;
|
||||||
return topic;
|
return topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +256,7 @@ PIString PIMQTT::Client::unregisterSubscribe(const PIString & mqtt_topic) {
|
|||||||
piCoutObj << "Warning: unsubscribe from empty topic, ignore";
|
piCoutObj << "Warning: unsubscribe from empty topic, ignore";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
piCout << mqtt_topic << "->" << topic << ep.priority;
|
// piCout << mqtt_topic << "->" << topic << ep.priority;
|
||||||
auto ref = PRIVATE->endpoints.getRef();
|
auto ref = PRIVATE->endpoints.getRef();
|
||||||
auto pit = ref->prepared.makeIterator();
|
auto pit = ref->prepared.makeIterator();
|
||||||
while (pit.next()) { // by priority
|
while (pit.next()) { // by priority
|
||||||
@@ -242,12 +268,15 @@ PIString PIMQTT::Client::unregisterSubscribe(const PIString & mqtt_topic) {
|
|||||||
for (int i = 0; i < eps.size_s(); ++i) {
|
for (int i = 0; i < eps.size_s(); ++i) {
|
||||||
if (eps[i].path == ep.path) {
|
if (eps[i].path == ep.path) {
|
||||||
eps.remove(i);
|
eps.remove(i);
|
||||||
auto & counter(ref->topics_binded[tit.key()]);
|
auto & usage(ref->topics_binded[tit.key()]);
|
||||||
--counter;
|
--usage.counter;
|
||||||
PIString ret;
|
PIString ret;
|
||||||
if (counter <= 0) ret = tit.key();
|
if (usage.counter <= 0) {
|
||||||
|
ret = tit.key();
|
||||||
|
ref->topics_binded.remove(tit.key());
|
||||||
|
}
|
||||||
if (eps.isEmpty()) {
|
if (eps.isEmpty()) {
|
||||||
piCout << "remove topics" << tit.key();
|
// piCout << "remove topics" << tit.key();
|
||||||
pit.value().remove(tit.key());
|
pit.value().remove(tit.key());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -295,6 +324,12 @@ void PIMQTT::Client::connectInternal(const ConnectInfo & ci) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PRIVATE->connected = true;
|
PRIVATE->connected = true;
|
||||||
|
PIMap<PIString, TopicUsage> topics_binded;
|
||||||
|
{ topics_binded = PRIVATE->endpoints.getRef()->topics_binded; }
|
||||||
|
auto it = topics_binded.makeIterator();
|
||||||
|
while (it.next()) {
|
||||||
|
if (it.value().counter > 0) subscribeInternal({it.key(), nullptr, it.value().qos});
|
||||||
|
}
|
||||||
changeStatus(Connected);
|
changeStatus(Connected);
|
||||||
connected();
|
connected();
|
||||||
}
|
}
|
||||||
@@ -324,15 +359,10 @@ void PIMQTT::Client::publishInternal(const MessageConst & m) {
|
|||||||
|
|
||||||
void PIMQTT::Client::subscribeInternal(const Subscribe & sub) {
|
void PIMQTT::Client::subscribeInternal(const Subscribe & sub) {
|
||||||
if (!PRIVATE->client) return;
|
if (!PRIVATE->client) return;
|
||||||
piCout << "";
|
// piCout << "subscribeInternal" << sub.topic;
|
||||||
piCout << "subscribeInternal" << sub.topic;
|
int ret = MQTTClient_subscribe(PRIVATE->client, sub.topic.dataUTF8(), static_cast<int>(sub.qos));
|
||||||
PIString topic = registerSubscribe(sub);
|
|
||||||
if (topic.isEmpty()) return;
|
|
||||||
piCout << "NEW" << topic; // << PRIVATE->endpoints.getRef()->size();
|
|
||||||
int ret = MQTTClient_subscribe(PRIVATE->client, topic.dataUTF8(), static_cast<int>(sub.qos));
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
if (ret != MQTTCLIENT_SUCCESS) {
|
||||||
piCoutObj << "Failed to subscribe" << topic << ", code" << ret;
|
piCoutObj << "Failed to subscribe" << sub.topic << ", code" << ret;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,20 +370,15 @@ void PIMQTT::Client::subscribeInternal(const Subscribe & sub) {
|
|||||||
|
|
||||||
void PIMQTT::Client::unsubscribeInternal(const PIString & mqtt_topic) {
|
void PIMQTT::Client::unsubscribeInternal(const PIString & mqtt_topic) {
|
||||||
if (!PRIVATE->client) return;
|
if (!PRIVATE->client) return;
|
||||||
piCout << "";
|
// piCout << "unsubscribeInternal" << mqtt_topic;
|
||||||
piCout << "unsubscribeInternal" << mqtt_topic;
|
int ret = MQTTClient_unsubscribe(PRIVATE->client, mqtt_topic.dataUTF8());
|
||||||
PIString topic = unregisterSubscribe(mqtt_topic);
|
|
||||||
if (topic.isEmpty()) return;
|
|
||||||
piCout << "DEL" << topic; // << PRIVATE->endpoints.getRef()->size();
|
|
||||||
int ret = MQTTClient_unsubscribe(PRIVATE->client, topic.dataUTF8());
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
if (ret != MQTTCLIENT_SUCCESS) {
|
||||||
piCoutObj << "Failed to unsubscribe" << topic << ", code" << ret;
|
piCoutObj << "Failed to unsubscribe" << mqtt_topic << ", code" << ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::destroy() {
|
void PIMQTT::Client::destroy() {
|
||||||
unregisterAll();
|
|
||||||
if (!PRIVATE->client) return;
|
if (!PRIVATE->client) return;
|
||||||
if (PRIVATE->connected) MQTTClient_disconnect(PRIVATE->client, 1000);
|
if (PRIVATE->connected) MQTTClient_disconnect(PRIVATE->client, 1000);
|
||||||
MQTTClient_destroy(&PRIVATE->client);
|
MQTTClient_destroy(&PRIVATE->client);
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ int main(int argc, char * argv[]) {
|
|||||||
PIMQTT::Client cl;
|
PIMQTT::Client cl;
|
||||||
cl.setConnectTimeout(2_s);
|
cl.setConnectTimeout(2_s);
|
||||||
|
|
||||||
|
cl.subscribe("api/v1/all/bort{A}/f",
|
||||||
|
[](const PIMQTT::MessageConst & msg) { piCout << "1" << msg.topicList() << msg.pathArguments() << msg.body().size(); });
|
||||||
|
cl.subscribe("api/v1/all/task{T}/f",
|
||||||
|
[](const PIMQTT::MessageConst & msg) { piCout << "2" << msg.topicList() << msg.pathArguments() << msg.body().size(); });
|
||||||
|
cl.subscribe("api/v1/all/*/f",
|
||||||
|
[](const PIMQTT::MessageConst & msg) { piCout << "3" << msg.topicList() << msg.pathArguments() << msg.body().size(); });
|
||||||
|
cl.subscribe("api/v1/all2/**",
|
||||||
|
[](const PIMQTT::MessageConst & msg) { piCout << "4" << msg.topicList() << msg.pathArguments() << msg.body().size(); });
|
||||||
CONNECTL(&cl, connected, [&cl] {
|
CONNECTL(&cl, connected, [&cl] {
|
||||||
piCout << "connected";
|
piCout << "connected";
|
||||||
// cl.subscribe("api/v1/plugins");
|
// cl.subscribe("api/v1/plugins");
|
||||||
@@ -71,18 +79,6 @@ int main(int argc, char * argv[]) {
|
|||||||
// cl.subscribe("api/v1/*/{taskID}/status");
|
// cl.subscribe("api/v1/*/{taskID}/status");
|
||||||
// cl.subscribe("api/v1/bort/list");
|
// cl.subscribe("api/v1/bort/list");
|
||||||
// cl.subscribe("api/v1/all");
|
// cl.subscribe("api/v1/all");
|
||||||
cl.subscribe("api/v1/all/bort{A}/f", [](const PIMQTT::MessageConst & msg) {
|
|
||||||
piCout << "1" << msg.topicList() << msg.pathArguments() << msg.body().size();
|
|
||||||
});
|
|
||||||
cl.subscribe("api/v1/all/task{T}/f", [](const PIMQTT::MessageConst & msg) {
|
|
||||||
piCout << "2" << msg.topicList() << msg.pathArguments() << msg.body().size();
|
|
||||||
});
|
|
||||||
cl.subscribe("api/v1/all/*/f", [](const PIMQTT::MessageConst & msg) {
|
|
||||||
piCout << "3" << msg.topicList() << msg.pathArguments() << msg.body().size();
|
|
||||||
});
|
|
||||||
cl.subscribe("api/v1/all2/**", [](const PIMQTT::MessageConst & msg) {
|
|
||||||
piCout << "4" << msg.topicList() << msg.pathArguments() << msg.body().size();
|
|
||||||
});
|
|
||||||
// cl.subscribe("/zigbee2mqtt");
|
// cl.subscribe("/zigbee2mqtt");
|
||||||
// cl.subscribe("/zigbee2mqtt/+/status/");
|
// cl.subscribe("/zigbee2mqtt/+/status/");
|
||||||
// cl.subscribe("/zigbee2mqtt/*/status/");
|
// cl.subscribe("/zigbee2mqtt/*/status/");
|
||||||
@@ -103,6 +99,9 @@ int main(int argc, char * argv[]) {
|
|||||||
|
|
||||||
cl.connect("localhost", "PIP");
|
cl.connect("localhost", "PIP");
|
||||||
|
|
||||||
|
piSleep(6.);
|
||||||
|
cl.unsubscribeAll();
|
||||||
|
|
||||||
kbd.enableExitCapture('Q');
|
kbd.enableExitCapture('Q');
|
||||||
WAIT_FOR_EXIT
|
WAIT_FOR_EXIT
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user