47HotPlugManager::HotPlugManager() : pollingCount(0), oneShotMode(false),
48 nonUdevPollingDurationSeconds(-1), initialPollingDurationSeconds(-1),
49 udevEventReceived(false)
52 udevContext =
nullptr;
53 udevMonitor =
nullptr;
56 hotPlugTimer.setSingleShot(
false);
57 hotPlugTimer.callOnTimeout(std::bind(&HotPlugManager::checkHotPlugEvents,
this));
60 mainThreadDebounceTimer.setSingleShot(
true);
61 mainThreadDebounceTimer.setInterval(100);
62 mainThreadDebounceTimer.callOnTimeout(std::bind(&HotPlugManager::checkHotPlugEvents,
this));
70HotPlugManager::~HotPlugManager()
85void HotPlugManager::registerHandler(std::shared_ptr<HotPlugCapableDevice> handler)
87 std::unique_lock<std::recursive_mutex> lock(handlersMutex);
89 for (
const auto& existingHandler : registeredHandlers)
91 if (existingHandler == handler)
93 LOG_DEBUG(
"Attempted to register an already registered HotPlugCapableDevice handler.");
97 registeredHandlers.push_back(handler);
98 LOG_DEBUG(
"HotPlugCapableDevice handler registered.");
101void HotPlugManager::unregisterHandler(std::shared_ptr<HotPlugCapableDevice> handler)
103 std::unique_lock<std::recursive_mutex> lock(handlersMutex);
104 auto it = std::remove_if(registeredHandlers.begin(), registeredHandlers.end(),
105 [&](
const std::shared_ptr<HotPlugCapableDevice> &h)
110 if (it != registeredHandlers.end())
112 registeredHandlers.erase(it, registeredHandlers.end());
113 LOG_DEBUG(
"HotPlugCapableDevice handler unregistered.");
117 LOG_DEBUG(
"Attempted to unregister a HotPlugCapableDevice handler that was not registered.");
121void HotPlugManager::start(uint32_t intervalMs,
bool oneShot)
123 if (hotPlugTimer.isActive())
125 LOG_DEBUG(
"HotPlugManager already running.");
129 this->oneShotMode.store(oneShot);
130 pollingCount.store(0);
133 if (udevMonitor && udevContext && udevCallbackId >= 0)
139 int maxInitialPolls = initialPollingDurationSeconds.load();
140 if (maxInitialPolls == -1)
145 hotPlugTimer.setSingleShot(
false);
146 hotPlugTimer.setInterval(1000);
147 hotPlugTimer.callOnTimeout([
this, maxInitialPolls]()
149 if (pollingCount.load() < maxInitialPolls)
151 checkHotPlugEvents();
153 LOGF_DEBUG(
"HotPlugManager: Initial polling count: %d/%d", pollingCount.load(), maxInitialPolls);
155 if (pollingCount.load() >= maxInitialPolls)
157 LOGF_DEBUG(
"HotPlugManager: Initial polling finished (%d times).", maxInitialPolls);
160 if (this->oneShotMode.load())
162 LOG_DEBUG(
"HotPlugManager: Hotplugging disabled (one-shot mode) after initial polling.");
166 LOG_DEBUG(
"HotPlugManager: Now monitoring udev events via event loop callback.");
173 hotPlugTimer.start();
177 LOGF_DEBUG(
"HotPlugManager started with initial polling (1000ms interval, %d times - default)%s.", maxInitialPolls,
178 this->oneShotMode.load() ?
", then disabled" :
", then event-driven via callback");
182 LOGF_DEBUG(
"HotPlugManager started with initial polling (1000ms interval, %d times)%s.", maxInitialPolls,
183 this->oneShotMode.load() ?
", then disabled" :
", then event-driven via callback");
190 hotPlugTimer.setSingleShot(
false);
191 hotPlugTimer.setInterval(intervalMs);
192 nonUdevPollingStartTime = std::chrono::steady_clock::now();
194 hotPlugTimer.callOnTimeout([
this]()
196 checkHotPlugEvents();
199 int maxDuration = nonUdevPollingDurationSeconds.load();
200 if (maxDuration == -1)
208 auto now = std::chrono::steady_clock::now();
209 auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - nonUdevPollingStartTime).count();
211 if (elapsed >= maxDuration)
214 LOGF_DEBUG(
"HotPlugManager: Non-udev polling stopped after %d seconds (max %d seconds reached).",
215 elapsed, maxDuration);
219 hotPlugTimer.start();
222 int maxDuration = nonUdevPollingDurationSeconds.load();
223 if (maxDuration == -1)
225 LOGF_DEBUG(
"HotPlugManager started with continuous polling interval: %u ms (udev not available). Max duration: %d seconds (default).",
226 intervalMs, MAX_NON_UDEV_POLL_DURATION_SECONDS);
228 else if (maxDuration == 0)
230 LOGF_DEBUG(
"HotPlugManager started with continuous polling interval: %u ms (udev not available). Max duration: unlimited.",
235 LOGF_DEBUG(
"HotPlugManager started with continuous polling interval: %u ms (udev not available). Max duration: %d seconds.",
236 intervalMs, maxDuration);
241void HotPlugManager::stop()
243 if (hotPlugTimer.isActive())
246 LOG_DEBUG(
"HotPlugManager stopped polling timer.");
250 if (mainThreadDebounceTimer.isActive())
252 mainThreadDebounceTimer.stop();
253 LOG_DEBUG(
"HotPlugManager stopped debounce timer.");
260void HotPlugManager::setNonUdevPollingDuration(
int seconds)
262 nonUdevPollingDurationSeconds.store(seconds);
268 else if (seconds == 0)
270 LOG_DEBUG(
"HotPlugManager: Non-udev polling duration set to unlimited.");
274 LOGF_DEBUG(
"HotPlugManager: Non-udev polling duration set to %d seconds.", seconds);
278void HotPlugManager::setInitialPollingDuration(
int seconds)
281 if (seconds != -1 && seconds > 0)
286 initialPollingDurationSeconds.store(seconds);
294 LOGF_DEBUG(
"HotPlugManager: Initial polling duration set to %d seconds.", seconds);
298void HotPlugManager::checkHotPlugEvents()
300 std::unique_lock<std::recursive_mutex> lock(handlersMutex);
301 LOG_DEBUG(
"HotPlugManager: Checking for hot-plug events...");
303 for (
const auto& handler : registeredHandlers)
305 LOG_DEBUG(
"HotPlugManager: Checking handler for a device type.");
308 std::map<std::string, std::shared_ptr<DefaultDevice>> managedDevices = handler->getManagedDevices();
309 std::set<std::string> currentlyManaged;
310 for (
const auto& pair : managedDevices)
312 currentlyManaged.insert(pair.first);
316 std::vector<std::string> discoveredIdentifiers = handler->discoverConnectedDeviceIdentifiers();
317 std::set<std::string> currentConnected(discoveredIdentifiers.begin(), discoveredIdentifiers.end());
322 for (
const std::string& identifier : currentlyManaged)
324 if (currentConnected.find(identifier) == currentConnected.end())
327 LOGF_DEBUG(
"HotPlugManager: Device disconnected: %s", identifier.c_str());
328 std::shared_ptr<DefaultDevice> deviceToRemove = managedDevices.at(identifier);
329 handler->destroyDevice(deviceToRemove);
336 for (
const std::string& identifier : currentConnected)
338 if (currentlyManaged.find(identifier) == currentlyManaged.end())
341 LOGF_DEBUG(
"HotPlugManager: New device connected: %s", identifier.c_str());
342 std::shared_ptr<DefaultDevice> newDevice = handler->createDevice(identifier);
346 newDevice->ISGetProperties(
nullptr);
350 LOGF_ERROR(
"HotPlugManager: Failed to create device for identifier: %s", identifier.c_str());
358bool HotPlugManager::initUdev()
360 udevContext = udev_new();
363 LOG_ERROR(
"HotPlugManager: Failed to create udev context.");
367 udevMonitor = udev_monitor_new_from_netlink(udevContext,
"udev");
370 LOG_ERROR(
"HotPlugManager: Failed to create udev monitor.");
371 udev_unref(udevContext);
372 udevContext =
nullptr;
377 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor,
"usb",
nullptr);
378 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor,
"usb_device",
nullptr);
380 if (udev_monitor_enable_receiving(udevMonitor) < 0)
382 LOG_ERROR(
"HotPlugManager: Failed to enable udev monitor receiving.");
383 udev_monitor_unref(udevMonitor);
384 udevMonitor =
nullptr;
385 udev_unref(udevContext);
386 udevContext =
nullptr;
391 int fd = udev_monitor_get_fd(udevMonitor);
394 LOG_ERROR(
"HotPlugManager: Failed to get udev monitor file descriptor.");
395 udev_monitor_unref(udevMonitor);
396 udevMonitor =
nullptr;
397 udev_unref(udevContext);
398 udevContext =
nullptr;
403 if (udevCallbackId < 0)
405 LOG_ERROR(
"HotPlugManager: Failed to register udev callback with event loop.");
406 udev_monitor_unref(udevMonitor);
407 udevMonitor =
nullptr;
408 udev_unref(udevContext);
409 udevContext =
nullptr;
413 LOGF_DEBUG(
"HotPlugManager: udev monitor initialized successfully (callback ID: %d).", udevCallbackId);
417void HotPlugManager::deinitUdev()
420 if (udevCallbackId >= 0)
424 LOG_DEBUG(
"HotPlugManager: udev callback unregistered from event loop.");
429 udev_monitor_unref(udevMonitor);
430 udevMonitor =
nullptr;
431 LOG_DEBUG(
"HotPlugManager: udev monitor deinitialized.");
435 udev_unref(udevContext);
436 udevContext =
nullptr;
437 LOG_DEBUG(
"HotPlugManager: udev context deinitialized.");
444void HotPlugManager::udevCallbackWrapper(
int fd,
void* userdata)
447 HotPlugManager* manager =
static_cast<HotPlugManager*
>(userdata);
450 manager->handleUdevEvent(
fd);
455void HotPlugManager::handleUdevEvent(
int fd)
459 udev_device* dev = udev_monitor_receive_device(udevMonitor);
462 const char* action = udev_device_get_action(dev);
463 const char* subsystem = udev_device_get_subsystem(dev);
464 const char* devnode = udev_device_get_devnode(dev);
466 LOGF_DEBUG(
"HotPlugManager: udev event: %s %s %s", action ? action :
"N/A",
467 subsystem ? subsystem :
"N/A", devnode ? devnode :
"N/A");
470 udevEventReceived.store(
true);
473 mainThreadDebounceTimer.start();
475 udev_device_unref(dev);
479 LOG_ERROR(
"HotPlugManager: udev_monitor_receive_device returned null.");
void IERmCallback(int callbackid)
int IEAddCallback(int readfiledes, void(*fp)(int, void *), void *p)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
Namespace to encapsulate INDI client, drivers, and mediator classes.
const int MAX_INITIAL_POLL
const int NON_UDEV_POLL_INTERVAL_MS
const int MAX_NON_UDEV_POLL_DURATION_SECONDS