#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; }