Complete Rebuild, with Subsystems for each component. RTOS Tasks. (help by Claude)
This commit is contained in:
167
vesper/src/ClientManager/ClientManager.cpp
Normal file
167
vesper/src/ClientManager/ClientManager.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "ClientManager.hpp"
|
||||
#include "../Logging/Logging.hpp"
|
||||
|
||||
ClientManager::ClientManager() {
|
||||
LOG_INFO("Client Manager Component - Initialized");
|
||||
}
|
||||
|
||||
ClientManager::~ClientManager() {
|
||||
_clients.clear();
|
||||
LOG_INFO("Client Manager Component - Destroyed");
|
||||
}
|
||||
|
||||
void ClientManager::addClient(AsyncWebSocketClient* client, DeviceType deviceType) {
|
||||
if (!isValidClient(client)) {
|
||||
LOG_ERROR("Cannot add invalid client");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t clientId = client->id();
|
||||
_clients[clientId] = ClientInfo(client, deviceType);
|
||||
|
||||
LOG_INFO("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("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("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("Message sent to client #%u: %s", clientId, message.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_WARNING("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("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("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("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("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;
|
||||
}
|
||||
Reference in New Issue
Block a user