2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
49#include <core/socket/SocketAddress.h>
50#include <core/socket/stream/SocketConnection.h>
51#include <express/Response.h>
52#include <iot/mqtt/MqttContext.h>
54#include <nlohmann/json.hpp>
55#include <nlohmann/json_fwd.hpp>
56#include <web/http/server/SocketContext.h>
70namespace mqtt::mqttbroker::
lib {
82 static std::string
windowId(
const std::string& clientId) {
83 std::ostringstream windowId(
"window");
84 for (
char ch : clientId) {
85 if (std::isalnum(
static_cast<
unsigned char>(ch))) {
88 windowId << std::hex << std::uppercase << std::setw(2) << std::setfill(
'0')
89 <<
static_cast<
int>(
static_cast<
unsigned char>(ch));
93 return windowId.str();
96 static std::string
href(
const std::string& text,
const std::string& url,
const std::string& windowId, uint16_t width, uint16_t height) {
97 return "<a href=\"#\" onClick=\""
101 "if (!localStorage.getItem(key)) "
102 " localStorage.setItem(key, key + '-' + Math.random().toString(36).substr(2, 6)); "
103 "let uniqueId = localStorage.getItem(key); "
104 "if (!window._openWindows) window._openWindows = {}; "
105 "if (!window._openWindows[uniqueId] || window._openWindows[uniqueId].closed) { "
106 " window._openWindows[uniqueId] = window.open('" +
107 url +
"', uniqueId, 'width=" + std::to_string(width) +
", height=" + std::to_string(height) +
108 ",location=no, menubar=no, status=no, toolbar=no'); "
110 " window._openWindows[uniqueId].focus(); "
112 "style=\"color:inherit;\">" +
122 mqtt->getConnectionName(),
123 mqtt->getMqttContext()->getSocketConnection()->getLocalAddress().toString(),
124 mqtt->getMqttContext()->getSocketConnection()->getRemoteAddress().toString(),
125 "<button class=\"red-btn\" onClick=\"disconnectClient('" + mqtt->getClientId() +
"')\">Disconnect</button>"});
129 modelMap.emplace(clientId, std::move(mqttModelEntry));
143 const Mqtt* mqtt =
nullptr;
145 auto modelIt =
modelMap.find(clientId);
164 response->getSocketContext()->onDisconnected([
this, &eventReceiver]() {
172 mqttModel.second
.getMqtt()->getConnectionName(),
173 mqttModel.second
.getMqtt()->getMqttContext()->getSocketConnection()->getLocalAddress().toString(),
174 mqttModel.second
.getMqtt()->getMqttContext()->getSocketConnection()->getRemoteAddress().toString(),
175 "<button class=\"red-btn\" onClick=\"disconnectClient('" + mqttModel.second
.getMqtt()->getClientId() +
176 "')\">Disconnect</button>"});
198 return mqtt->getMqttContext()->getSocketConnection()->getSocketContext()->getOnlineSince();
202 return mqtt->getMqttContext()->getSocketConnection()->getSocketContext()->getOnlineDuration();
209 void MqttModel::
sendEvent(
const std::string& data,
const std::string& event,
const std::string& id) {
211 if (
const auto& response = eventReceiver
.response.lock()) {
212 if (response->isConnected()) {
213 if (!event.empty()) {
214 response->sendFragment(
"event:" + event);
217 response->sendFragment(
"id:" + id);
219 response->sendFragment(
"data:" + data);
220 response->sendFragment();
226 std::string
MqttModel::timePointToString(
const std::chrono::time_point<std::chrono::system_clock>& timePoint) {
227 std::time_t time = std::chrono::system_clock::to_time_t(timePoint);
228 std::tm* tm_ptr = std::gmtime(&time);
231 std::string onlineSince =
"Formatting error";
234 if (std::strftime(buffer,
sizeof(buffer),
"%Y-%m-%d %H:%M:%S", tm_ptr)) {
235 onlineSince = std::string(buffer) +
" UTC";
241 std::string
MqttModel::durationToString(
const std::chrono::time_point<std::chrono::system_clock>& bevore,
242 const std::chrono::time_point<std::chrono::system_clock>& later) {
243 using seconds_duration_type = std::chrono::duration<std::chrono::seconds::rep>::rep;
245 seconds_duration_type totalSeconds = std::chrono::duration_cast<std::chrono::seconds>(later - bevore).count();
248 seconds_duration_type days = totalSeconds / 86400;
249 seconds_duration_type remainder = totalSeconds % 86400;
250 seconds_duration_type hours = remainder / 3600;
251 remainder = remainder % 3600;
252 seconds_duration_type minutes = remainder / 60;
253 seconds_duration_type seconds = remainder % 60;
256 std::ostringstream oss;
258 oss << days <<
" day" << (days == 1 ?
"" :
"s") <<
", ";
260 oss << std::setw(2) << std::setfill(
'0') << hours <<
":" << std::setw(2) << std::setfill(
'0') << minutes <<
":" << std::setw(2)
261 << std::setfill(
'0') << seconds;
270 response->sendFragment(
":keep-alive");
271 response->sendFragment();
bool operator==(const EventReceiver &other)
std::weak_ptr< express::Response > response
EventReceiver(const std::shared_ptr< express::Response > &response)
core::timer::Timer heartbeatTimer
MqttModelEntry(const Mqtt *mqtt)
std::string onlineSince() const
const Mqtt * getMqtt() const
std::string onlineDuration() const
void publish(const iot::mqtt::packets::Publish &publish)
void sendEvent(const std::string &data, const std::string &event="", const std::string &id="")
std::chrono::time_point< std::chrono::system_clock > onlineSinceTimePoint
void delClient(const std::string &clientId)
void addClient(const std::string &clientId, Mqtt *mqtt)
static MqttModel & instance()
std::list< EventReceiver > eventReceiverList
std::map< std::string, MqttModelEntry > modelMap
void addEventReceiver(const std::shared_ptr< express::Response > &response, const std::string &lastEventId)
std::string onlineSince()
std::string onlineDuration()
std::map< std::string, MqttModelEntry > & getClients()
const Mqtt * getMqtt(const std::string &clientId)
static std::string href(const std::string &text, const std::string &url, const std::string &windowId, uint16_t width, uint16_t height)
static std::string windowId(const std::string &clientId)