- Create 4 infrastructure services (LLM, Storage, Platform, Voice) - Refactor all modules to pure business logic (no HTTP/SQLite/Win32) - Add bundled SQLite amalgamation for MinGW compatibility - Make OpenSSL optional in CMake configuration - Fix topic naming convention (colon format) - Add succession documentation Build status: CMake config needs SQLite C language fix (documented) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
140 lines
4.4 KiB
C++
140 lines
4.4 KiB
C++
#include "PlatformService.hpp"
|
|
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
|
|
namespace aissia {
|
|
|
|
PlatformService::PlatformService() {
|
|
m_logger = spdlog::get("PlatformService");
|
|
if (!m_logger) {
|
|
m_logger = spdlog::stdout_color_mt("PlatformService");
|
|
}
|
|
}
|
|
|
|
bool PlatformService::initialize(grove::IIO* io) {
|
|
m_io = io;
|
|
|
|
// Create platform-specific window tracker
|
|
m_tracker = WindowTrackerFactory::create();
|
|
|
|
if (!m_tracker || !m_tracker->isAvailable()) {
|
|
m_logger->warn("Window tracker not available on this platform");
|
|
return true; // Non-fatal, module can work without tracking
|
|
}
|
|
|
|
if (m_io) {
|
|
grove::SubscriptionConfig config;
|
|
m_io->subscribe("platform:query_window", config);
|
|
}
|
|
|
|
m_logger->info("PlatformService initialized: {}", m_tracker->getPlatformName());
|
|
return true;
|
|
}
|
|
|
|
void PlatformService::configure(int pollIntervalMs, int idleThresholdSeconds) {
|
|
m_pollIntervalMs = pollIntervalMs;
|
|
m_idleThresholdSeconds = idleThresholdSeconds;
|
|
m_logger->debug("Configured: poll={}ms, idle={}s", pollIntervalMs, idleThresholdSeconds);
|
|
}
|
|
|
|
void PlatformService::process() {
|
|
if (!m_tracker || !m_tracker->isAvailable()) return;
|
|
|
|
// Use monotonic clock for timing
|
|
static auto startTime = std::chrono::steady_clock::now();
|
|
auto now = std::chrono::steady_clock::now();
|
|
float currentTime = std::chrono::duration<float>(now - startTime).count();
|
|
|
|
float pollIntervalSec = m_pollIntervalMs / 1000.0f;
|
|
if (currentTime - m_lastPollTime >= pollIntervalSec) {
|
|
m_lastPollTime = currentTime;
|
|
pollWindowInfo(currentTime);
|
|
}
|
|
|
|
// Handle query requests
|
|
if (m_io) {
|
|
while (m_io->hasMessages() > 0) {
|
|
auto msg = m_io->pullMessage();
|
|
if (msg.topic == "platform:query_window") {
|
|
publishWindowInfo();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlatformService::pollWindowInfo(float currentTime) {
|
|
std::string newApp = m_tracker->getCurrentAppName();
|
|
std::string newTitle = m_tracker->getCurrentWindowTitle();
|
|
|
|
// Check for app change
|
|
if (newApp != m_currentApp) {
|
|
int duration = static_cast<int>(currentTime - m_appStartTime);
|
|
|
|
if (!m_currentApp.empty() && duration > 0) {
|
|
publishWindowChanged(m_currentApp, newApp, duration);
|
|
}
|
|
|
|
m_currentApp = newApp;
|
|
m_currentWindowTitle = newTitle;
|
|
m_appStartTime = currentTime;
|
|
|
|
m_logger->debug("App: {} - {}", m_currentApp,
|
|
m_currentWindowTitle.size() > 50 ?
|
|
m_currentWindowTitle.substr(0, 50) + "..." : m_currentWindowTitle);
|
|
}
|
|
|
|
// Check idle state
|
|
bool isIdle = m_tracker->isUserIdle(m_idleThresholdSeconds);
|
|
|
|
if (isIdle && !m_wasIdle) {
|
|
m_logger->info("User idle detected ({}s)", m_idleThresholdSeconds);
|
|
if (m_io) {
|
|
auto event = std::make_unique<grove::JsonDataNode>("idle");
|
|
event->setInt("idleSeconds", m_tracker->getIdleTimeSeconds());
|
|
m_io->publish("platform:idle_detected", std::move(event));
|
|
}
|
|
}
|
|
else if (!isIdle && m_wasIdle) {
|
|
m_logger->info("User activity resumed");
|
|
if (m_io) {
|
|
auto event = std::make_unique<grove::JsonDataNode>("active");
|
|
m_io->publish("platform:activity_resumed", std::move(event));
|
|
}
|
|
}
|
|
|
|
m_wasIdle = isIdle;
|
|
|
|
// Publish periodic window info
|
|
publishWindowInfo();
|
|
}
|
|
|
|
void PlatformService::publishWindowInfo() {
|
|
if (!m_io || !m_tracker) return;
|
|
|
|
auto event = std::make_unique<grove::JsonDataNode>("window");
|
|
event->setString("appName", m_currentApp);
|
|
event->setString("windowTitle", m_currentWindowTitle);
|
|
event->setBool("isIdle", m_wasIdle);
|
|
event->setInt("idleSeconds", m_tracker->getIdleTimeSeconds());
|
|
m_io->publish("platform:window_info", std::move(event));
|
|
}
|
|
|
|
void PlatformService::publishWindowChanged(const std::string& oldApp,
|
|
const std::string& newApp,
|
|
int duration) {
|
|
if (!m_io) return;
|
|
|
|
auto event = std::make_unique<grove::JsonDataNode>("changed");
|
|
event->setString("oldApp", oldApp);
|
|
event->setString("newApp", newApp);
|
|
event->setInt("duration", duration);
|
|
m_io->publish("platform:window_changed", std::move(event));
|
|
}
|
|
|
|
void PlatformService::shutdown() {
|
|
m_tracker.reset();
|
|
m_logger->info("PlatformService shutdown");
|
|
}
|
|
|
|
} // namespace aissia
|