package it.moze.control.bot; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import io.quarkus.logging.Log; import it.moze.boundary.socket.BotWebSocket; import it.moze.control.bot.consumer.ScreenConsumerRunnable; import it.moze.control.bot.producer.ScreenGrabberRunnable; import it.moze.control.vision.ScreenshotService; import it.moze.entity.bot.Roi; import it.moze.entity.bot.constant.BotStatus; import it.moze.entity.vision.Capture; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class Bot { private BotStatus status = BotStatus.STOPPED; private final ScreenshotService screenshotService; private final ConfigurationService configurationService; private final BotWebSocket botWebsocket; private final BlockingQueue capturesQueue = new LinkedBlockingQueue<>(10); private ScheduledExecutorService producersExecutor; private Thread consumerThread; public Bot(BotWebSocket botWebsocket, ConfigurationService configurationService, ScreenshotService screenshotService) { this.screenshotService = screenshotService; this.configurationService = configurationService; this.botWebsocket = botWebsocket; } public void start() { if (status == BotStatus.STARTED) { Log.warn("Bot is already started."); return; } Log.info("Starting producers and consumer..."); List rois = configurationService.configuration().pages().get(0).rois(); producersExecutor = Executors.newScheduledThreadPool(rois.size()); // Reinitialize the consumer thread consumerThread = Thread.ofVirtual().name("consumer").unstarted(new ScreenConsumerRunnable(capturesQueue, this.botWebsocket)); // Start producers rois.forEach(roi -> { producersExecutor.scheduleAtFixedRate(new ScreenGrabberRunnable(roi, capturesQueue, screenshotService), 0, 16, TimeUnit.MILLISECONDS); }); // Start consumer thread consumerThread.start(); status = BotStatus.STARTED; } public void stop() { if (status == BotStatus.STOPPED) { Log.warn("Bot is already stopped."); return; } Log.info("Stopping producers executor..."); try { producersExecutor.shutdown(); // Initiates an orderly shutdown if (!producersExecutor.awaitTermination(1, TimeUnit.SECONDS)) { producersExecutor.shutdownNow(); // Force shutdown if tasks don't finish in time } } catch (InterruptedException e) { producersExecutor.shutdownNow(); Thread.currentThread().interrupt(); } Log.info("Stopping consumers executor..."); consumerThread.interrupt(); // Interrupt the consumer thread try { consumerThread.join(); // Wait for the consumer thread to terminate } catch (InterruptedException e) { consumerThread.interrupt(); Thread.currentThread().interrupt(); } status = BotStatus.STOPPED; } public BotStatus status() { return status; } }