MQTTSuite
Loading...
Searching...
No Matches
Mqtt.cpp
Go to the documentation of this file.
1/*
2 * MQTTSuite - A lightweight MQTT Integration System
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2022, 2023, 2024, 2025, 2026
5 * Tobias Pfeil
6 * 2025, 2026
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation, either version 3 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22/*
23 * MIT License
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 * THE SOFTWARE.
42 */
43
44#include "Mqtt.h"
45
46#include "lib/MappingAdminRouter.h"
47#include "lib/MqttMapper.h"
48
49#include <iot/mqtt/Topic.h>
50#include <iot/mqtt/packets/Connack.h>
51#include <iot/mqtt/packets/Publish.h>
52
53#ifndef DOXYGEN_SHOULD_SKIP_THIS
54
55#include <algorithm>
56#include <functional>
57
58#endif
59
60namespace mqtt::mqttintegrator::lib {
61
62 std::set<Mqtt*> Mqtt::mqttInstances;
63
65 utils::Timeval when = 0;
66 std::size_t seq = 0;
67 iot::mqtt::packets::Publish publish;
68 utils::Timeval delay;
69 };
70
71 Mqtt::Mqtt(const std::string& connectionName,
72 std::shared_ptr<mqtt::lib::MqttMapper> mqttMapper,
73 const std::string& sessionStoreFileName)
74 : iot::mqtt::client::Mqtt(connectionName, //
75 mqttMapper->getClientId(),
76 mqttMapper->getKeepAlive(),
77 sessionStoreFileName)
78 , mqttMapper(mqttMapper)
80 , delayedQueue(this) {
81 mqttInstances.insert(this);
82 }
83
84 Mqtt::~Mqtt() {
85 mqttInstances.erase(this);
86 }
87
88 mqtt::lib::admin::ReloadResult Mqtt::updateSubscriptions(bool mustReconnect) {
89 mqtt::lib::admin::ReloadResult reloadResult;
90
91 reloadResult.instances = mqttInstances.size();
92 if (mustReconnect) {
93 reloadResult.mode = "reconnect";
94 } else {
95 reloadResult.mode = "hot";
96 }
97
98 for (Mqtt* mqtt : mqttInstances) {
99 if (mustReconnect) {
100 mqtt->sendDisconnect();
101 } else {
102 auto [subscribeCount, unsubscribeCount] = mqtt->resubscribe();
103
104 reloadResult.subscribed += subscribeCount;
105 reloadResult.unsubscribed += unsubscribeCount;
106 }
107 }
108
109 return reloadResult;
110 }
111
113 const auto& [cleanSession, //
114 willTopic,
115 willMessage,
116 WillQoS,
117 willRetain,
118 username,
119 password] = mqttMapper->getConnectPayload();
120
121 sendConnect(cleanSession, willTopic, willMessage, WillQoS, willRetain, username, password);
122 }
123
124 bool Mqtt::onSignal(int signum) {
125 sendDisconnect();
126 return Super::onSignal(signum);
127 }
128
129 void Mqtt::onConnack(const iot::mqtt::packets::Connack& connack) {
130 if (connack.getReturnCode() == 0 && !connack.getSessionPresent()) {
131 sendSubscribe(currentSubscriptions);
132 }
133 }
134
135 void Mqtt::onPublish(const iot::mqtt::packets::Publish& publish) {
136 const auto& [immediatePublishes, scheduledPublishes] = mqttMapper->getMappings(publish);
137
138 for (const mqtt::lib::MqttMapper::ScheduledPublish& delayedPublish : scheduledPublishes) {
139 delayedQueue.delayPublish(delayedPublish.delay, delayedPublish.publish);
140 }
141
142 for (const iot::mqtt::packets::Publish& immediatePublish : immediatePublishes) {
143 sendPublish(
144 immediatePublish.getTopic(), immediatePublish.getMessage(), immediatePublish.getQoS(), immediatePublish.getRetain());
145 }
146 }
147
148 std::pair<std::size_t, std::size_t> Mqtt::resubscribe() {
149 std::list<iot::mqtt::Topic> newSubscriptions = mqttMapper->extractSubscriptions();
150
151 std::list<std::string> topicsToUnsubscribe;
152 for (const auto& currentTopic : currentSubscriptions) {
153 const bool existsInNew = std::any_of(newSubscriptions.begin(), newSubscriptions.end(), [&](const auto& newTopic) {
154 return currentTopic.getName() == newTopic.getName() && currentTopic.getQoS() == newTopic.getQoS();
155 });
156
157 if (!existsInNew) {
158 topicsToUnsubscribe.push_back(currentTopic.getName());
159 }
160 }
161
162 if (!topicsToUnsubscribe.empty()) {
163 sendUnsubscribe(topicsToUnsubscribe);
164 }
165
166 std::list<iot::mqtt::Topic> topicsToSubscribe;
167 for (const auto& newTopic : newSubscriptions) {
168 const bool existsInOld = std::any_of(currentSubscriptions.begin(), currentSubscriptions.end(), [&](const auto& currentTopic) {
169 return currentTopic.getName() == newTopic.getName() && currentTopic.getQoS() == newTopic.getQoS();
170 });
171
172 if (!existsInOld) {
173 topicsToSubscribe.push_back(newTopic);
174 }
175 }
176
177 if (!topicsToSubscribe.empty()) {
178 sendSubscribe(topicsToSubscribe);
179 }
180
181 currentSubscriptions = newSubscriptions;
182
183 return {topicsToSubscribe.size(), topicsToUnsubscribe.size()};
184 }
185
187 if (a.when != b.when) {
188 return a.when > b.when;
189 }
190
191 return a.seq > b.seq;
192 }
193
195 : mqtt(mqtt) {
196 }
197
199 delayTimer.cancel();
200 }
201
203 const auto now = utils::Timeval::currentTime();
204
205 while (!empty() && top().when <= now) {
206 const iot::mqtt::packets::Publish duePublish = top().publish;
207 pop();
208
209 mqtt->sendPublish(duePublish.getTopic(), duePublish.getMessage(), duePublish.getQoS(), duePublish.getRetain());
210 }
211 }
212
214 delayTimer.cancel();
215
216 auto delay = top().when - utils::Timeval::currentTime();
217 if (delay < utils::Timeval{}) {
218 delay = utils::Timeval{};
219 }
220
221 delayTimer = core::timer::Timer::singleshotTimer(
222 [this]() {
224
225 if (!empty()) {
227 }
228 },
229 delay);
230 }
231
232 void Mqtt::DelayedQueue::delayPublish(const utils::Timeval& delay, const iot::mqtt::packets::Publish& publish) {
233 minHeap.emplace(utils::Timeval::currentTime() + delay, nextSeq++, publish, delay);
235 }
236
237 bool Mqtt::DelayedQueue::empty() const {
238 return minHeap.empty();
239 }
240
242 return minHeap.top();
243 }
244
246 minHeap.pop();
247 }
248
249} // namespace mqtt::mqttintegrator::lib
std::string getClientId() const
std::list< iot::mqtt::Topic > extractSubscriptions() const
MappedPublishes getMappings(const iot::mqtt::packets::Publish &publish)
uint16_t getKeepAlive() const
ConnectParameter getConnectPayload() const
std::priority_queue< ScheduledPublish, std::vector< ScheduledPublish >, EarlierFirst > minHeap
Definition Mqtt.h:122
ScheduledPublish const & top() const
Definition Mqtt.cpp:241
void delayPublish(const utils::Timeval &delay, const iot::mqtt::packets::Publish &publish)
Definition Mqtt.cpp:232
static std::set< Mqtt * > mqttInstances
Definition Mqtt.h:129
void onPublish(const iot::mqtt::packets::Publish &publish) final
Definition Mqtt.cpp:135
void onConnack(const iot::mqtt::packets::Connack &connack) final
Definition Mqtt.cpp:129
std::pair< std::size_t, std::size_t > resubscribe()
Definition Mqtt.cpp:148
static mqtt::lib::admin::ReloadResult updateSubscriptions(bool mustReconnect)
Definition Mqtt.cpp:88
std::shared_ptr< mqtt::lib::MqttMapper > mqttMapper
Definition Mqtt.h:100
std::list< iot::mqtt::Topic > currentSubscriptions
Definition Mqtt.h:101
bool onSignal(int signum) final
Definition Mqtt.cpp:124
iot::mqtt::client::Mqtt Super
Definition Mqtt.h:88
iot::mqtt::packets::Publish publish
Definition MqttMapper.h:80
bool operator()(const ScheduledPublish &a, const ScheduledPublish &b) const
Definition Mqtt.cpp:186
iot::mqtt::packets::Publish publish
Definition Mqtt.cpp:67