Refactored logging system to require a TAG as first argument on all LOG_* macros, enabling per-subsystem log filtering and cleaner output. Each subsystem now defines its own TAG (e.g. "BellEngine", "Player"). Also overhauled Logging.hpp/cpp with improved level control and output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
170 lines
5.6 KiB
C++
170 lines
5.6 KiB
C++
#include "ClientManager.hpp"
|
|
|
|
#define TAG "ClientManager"
|
|
#include "../Logging/Logging.hpp"
|
|
|
|
ClientManager::ClientManager() {
|
|
LOG_INFO(TAG, "Client Manager initialized !");
|
|
}
|
|
|
|
ClientManager::~ClientManager() {
|
|
_clients.clear();
|
|
LOG_INFO(TAG, "Client Manager destroyed");
|
|
}
|
|
|
|
void ClientManager::addClient(AsyncWebSocketClient* client, DeviceType deviceType) {
|
|
if (!isValidClient(client)) {
|
|
LOG_WARNING(TAG, "Client Manager - Cannot add invalid client");
|
|
return;
|
|
}
|
|
|
|
uint32_t clientId = client->id();
|
|
_clients[clientId] = ClientInfo(client, deviceType);
|
|
|
|
LOG_INFO(TAG, "Client Manager - Client #%u added as %s device", clientId, deviceTypeToString(deviceType));
|
|
}
|
|
|
|
void ClientManager::removeClient(uint32_t clientId) {
|
|
auto it = _clients.find(clientId);
|
|
if (it != _clients.end()) {
|
|
LOG_INFO(TAG, "Client Manager - Client #%u removed (%s device)", clientId,
|
|
deviceTypeToString(it->second.deviceType));
|
|
_clients.erase(it);
|
|
}
|
|
}
|
|
|
|
void ClientManager::updateClientType(uint32_t clientId, DeviceType deviceType) {
|
|
auto it = _clients.find(clientId);
|
|
if (it != _clients.end()) {
|
|
DeviceType oldType = it->second.deviceType;
|
|
it->second.deviceType = deviceType;
|
|
LOG_INFO(TAG, "Client Manager - Client #%u type updated from %s to %s", clientId,
|
|
deviceTypeToString(oldType), deviceTypeToString(deviceType));
|
|
}
|
|
}
|
|
|
|
void ClientManager::updateClientLastSeen(uint32_t clientId) {
|
|
auto it = _clients.find(clientId);
|
|
if (it != _clients.end()) {
|
|
it->second.lastSeen = millis();
|
|
}
|
|
}
|
|
|
|
bool ClientManager::isClientConnected(uint32_t clientId) const {
|
|
auto it = _clients.find(clientId);
|
|
if (it != _clients.end()) {
|
|
return it->second.isConnected &&
|
|
isValidClient(it->second.client);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ClientManager::DeviceType ClientManager::getClientType(uint32_t clientId) const {
|
|
auto it = _clients.find(clientId);
|
|
return (it != _clients.end()) ? it->second.deviceType : DeviceType::UNKNOWN;
|
|
}
|
|
|
|
ClientManager::ClientInfo* ClientManager::getClientInfo(uint32_t clientId) {
|
|
auto it = _clients.find(clientId);
|
|
return (it != _clients.end()) ? &it->second : nullptr;
|
|
}
|
|
|
|
bool ClientManager::sendToClient(uint32_t clientId, const String& message) {
|
|
auto it = _clients.find(clientId);
|
|
if (it != _clients.end() && isValidClient(it->second.client)) {
|
|
it->second.client->text(message);
|
|
updateClientLastSeen(clientId);
|
|
LOG_DEBUG(TAG, "Client Manager - Message sent to client #%u: %s", clientId, message.c_str());
|
|
return true;
|
|
}
|
|
|
|
LOG_WARNING(TAG, "Client Manager - Failed to send message to client #%u - client not found or invalid", clientId);
|
|
return false;
|
|
}
|
|
|
|
void ClientManager::sendToMasterClients(const String& message) {
|
|
int count = 0;
|
|
for (auto& pair : _clients) {
|
|
if (pair.second.deviceType == DeviceType::MASTER &&
|
|
isValidClient(pair.second.client)) {
|
|
pair.second.client->text(message);
|
|
updateClientLastSeen(pair.first);
|
|
count++;
|
|
}
|
|
}
|
|
LOG_DEBUG(TAG, "Client Manager - Message sent to %d master client(s): %s", count, message.c_str());
|
|
}
|
|
|
|
void ClientManager::sendToSecondaryClients(const String& message) {
|
|
int count = 0;
|
|
for (auto& pair : _clients) {
|
|
if (pair.second.deviceType == DeviceType::SECONDARY &&
|
|
isValidClient(pair.second.client)) {
|
|
pair.second.client->text(message);
|
|
updateClientLastSeen(pair.first);
|
|
count++;
|
|
}
|
|
}
|
|
LOG_DEBUG(TAG, "Client Manager - Message sent to %d secondary client(s): %s", count, message.c_str());
|
|
}
|
|
|
|
void ClientManager::broadcastToAll(const String& message) {
|
|
int count = 0;
|
|
for (auto& pair : _clients) {
|
|
if (isValidClient(pair.second.client)) {
|
|
pair.second.client->text(message);
|
|
updateClientLastSeen(pair.first);
|
|
count++;
|
|
}
|
|
}
|
|
LOG_DEBUG(TAG, "Client Manager - Message broadcasted to %d client(s): %s", count, message.c_str());
|
|
}
|
|
|
|
void ClientManager::cleanupDisconnectedClients() {
|
|
auto it = _clients.begin();
|
|
while (it != _clients.end()) {
|
|
if (!isValidClient(it->second.client)) {
|
|
LOG_DEBUG(TAG, "Client Manager - Cleaning up disconnected client #%u", it->first);
|
|
it->second.isConnected = false;
|
|
it = _clients.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
String ClientManager::getClientListJson() const {
|
|
StaticJsonDocument<512> doc;
|
|
JsonArray clients = doc.createNestedArray("clients");
|
|
|
|
for (const auto& pair : _clients) {
|
|
JsonObject client = clients.createNestedObject();
|
|
client["id"] = pair.first;
|
|
client["type"] = deviceTypeToString(pair.second.deviceType);
|
|
client["connected"] = isValidClient(pair.second.client);
|
|
client["last_seen"] = pair.second.lastSeen;
|
|
}
|
|
|
|
String result;
|
|
serializeJson(doc, result);
|
|
return result;
|
|
}
|
|
|
|
const char* ClientManager::deviceTypeToString(DeviceType type) const {
|
|
switch (type) {
|
|
case DeviceType::MASTER: return "master";
|
|
case DeviceType::SECONDARY: return "secondary";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
ClientManager::DeviceType ClientManager::stringToDeviceType(const String& typeStr) const {
|
|
if (typeStr == "master") return DeviceType::MASTER;
|
|
if (typeStr == "secondary") return DeviceType::SECONDARY;
|
|
return DeviceType::UNKNOWN;
|
|
}
|
|
|
|
bool ClientManager::isValidClient(AsyncWebSocketClient* client) const {
|
|
return client != nullptr && client->status() == WS_CONNECTED;
|
|
}
|