package com.codegy.aerlink.connection.command; import android.util.Log; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; /** * FIFO command queue, handles waiting for new command and retrying */ public class CommandQueueThread extends Thread { private static final String LOG_TAG = CommandQueueThread.class.getSimpleName(); private static final int RETRY_TIME = 1600; private CommandHandler handler; private Command currentCommand; private BlockingQueue<Command> queue = new LinkedBlockingDeque<>(); private volatile boolean run = true; private volatile boolean ready = false; private final Object lock = new Object(); /** * Creates a new CommandQueueThread with a command handler * @param handler command handler */ public CommandQueueThread(CommandHandler handler) { this.handler = handler; } @Override public void run() { while (run) { try { waitAndSendNextCommand(); } catch (InterruptedException ignored) {} } } /** * Kill the thread, discards all commands in queue */ public void kill() { run = false; // Interrupt in case the thread is waiting for a command interrupt(); } private void waitAndSendNextCommand() throws InterruptedException { synchronized(lock) { while (!ready) { Log.d(LOG_TAG, "Waiting for ready..."); lock.wait(); } Log.d(LOG_TAG, "Waiting for command..."); // If currentCommand is not null, it means it has failed // try again with the same command unless it has tried to many times if (currentCommand == null || !currentCommand.shouldRetryAgain()) { currentCommand = queue.take(); } Log.d(LOG_TAG, "Handling command..."); handler.handleCommand(currentCommand); lock.wait(RETRY_TIME); } } /** * Updates the ready status, pausing or starting the thread * @param ready new ready value */ public void setReady(boolean ready) { if (this.ready == ready) { return; } this.ready = ready; // Interrupt in case the thread is waiting for a command interrupt(); Log.d(LOG_TAG, "Ready: " + ready); } /** * Add command to queue * @param command command to be added */ public void put(Command command) { queue.offer(command); Log.d(LOG_TAG, "Command added: " + command.isWriteCommand()); } /** * Clear all commands in queue */ public void clear() { queue.clear(); Log.d(LOG_TAG, "Commands cleared"); } /** * Remove current command and keep going with the next one */ public void remove() { synchronized(lock) { Log.d(LOG_TAG, "Command sent"); if (currentCommand != null) { currentCommand.completeWithSuccess(); currentCommand = null; } lock.notify(); } } /** * Move current command to back and keep going with the next one */ public void moveToBack() { synchronized(lock) { if (currentCommand != null) { if (currentCommand.shouldRetryAgain()) { Log.d(LOG_TAG, "Command moved to back"); queue.offer(currentCommand); } else { Log.d(LOG_TAG, "Command failed too many times"); currentCommand.completeWithFailure(); } } currentCommand = null; } } }