package com.dronecontrol.droneapi; import com.google.inject.Inject; import com.dronecontrol.droneapi.commands.Command; import com.dronecontrol.droneapi.commands.composed.GetConfigurationDataCommand; import com.dronecontrol.droneapi.commands.composed.PlayFlightAnimationCommand; import com.dronecontrol.droneapi.commands.composed.PlayLedAnimationCommand; import com.dronecontrol.droneapi.commands.composed.SetConfigValueCommand; import com.dronecontrol.droneapi.commands.composed.SwitchCameraCommand; import com.dronecontrol.droneapi.components.ErrorListenerComponent; import com.dronecontrol.droneapi.components.ReadyStateListenerComponent; import com.dronecontrol.droneapi.data.Config; import com.dronecontrol.droneapi.data.DroneConfiguration; import com.dronecontrol.droneapi.data.enums.Camera; import com.dronecontrol.droneapi.data.enums.ControllerState; import com.dronecontrol.droneapi.data.enums.DroneVersion; import com.dronecontrol.droneapi.data.enums.FlightAnimation; import com.dronecontrol.droneapi.data.enums.LedAnimation; import com.dronecontrol.droneapi.injection.Context; import com.dronecontrol.droneapi.listeners.ErrorListener; import com.dronecontrol.droneapi.listeners.NavDataListener; import com.dronecontrol.droneapi.listeners.ReadyStateChangeListener; import com.dronecontrol.droneapi.listeners.VideoDataListener; import org.apache.log4j.Logger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static com.google.common.base.Preconditions.checkState; public class DroneController { private static final int NUMBER_OF_THREADS = 1; private final Logger logger = Logger.getLogger(DroneController.class); private final ReadyStateListenerComponent readyStateListenerComponent; private final ErrorListenerComponent errorListenerComponent; private final DroneStartupCoordinator droneStartupCoordinator; private final CommandSenderCoordinator commandSender; private final NavigationDataRetriever navigationDataRetriever; private final VideoRetrieverP264 videoRetrieverP264; private final VideoRetrieverH264 videoRetrieverH264; private final InternalStateWatcher internalStateWatcher; private ExecutorService executor; private Config config; public static DroneController build() { return Context.getBean(DroneController.class); } @Inject public DroneController(ReadyStateListenerComponent readyStateListenerComponent, ErrorListenerComponent errorListenerComponent, DroneStartupCoordinator droneStartupCoordinator, CommandSenderCoordinator commandSenderCoordinator, NavigationDataRetriever navigationDataRetriever, VideoRetrieverP264 videoRetrieverP264, VideoRetrieverH264 videoRetrieverH264, InternalStateWatcher internalStateWatcher) { this.readyStateListenerComponent = readyStateListenerComponent; this.errorListenerComponent = errorListenerComponent; this.droneStartupCoordinator = droneStartupCoordinator; this.commandSender = commandSenderCoordinator; this.navigationDataRetriever = navigationDataRetriever; this.videoRetrieverP264 = videoRetrieverP264; this.videoRetrieverH264 = videoRetrieverH264; this.internalStateWatcher = internalStateWatcher; } public void startAsync(final Config config) { checkInitializationStateStarted(); initializeExecutor(); executor.submit(new Runnable() { @Override public void run() { try { start(config); } catch (Throwable e) { errorListenerComponent.emitError(e); } } }); } public void start(Config config) { checkInitializationStateStarted(); logger.info("Starting drone controller"); this.config = config; initializeExecutor(); droneStartupCoordinator.start(config); readyStateListenerComponent.emitReadyStateChange(ReadyStateChangeListener.ReadyState.READY); } private void initializeExecutor() { executor = Executors.newFixedThreadPool(NUMBER_OF_THREADS); } public void stop() { readyStateListenerComponent.emitReadyStateChange(ReadyStateChangeListener.ReadyState.NOT_READY); logger.info("Stopping drone controller"); droneStartupCoordinator.stop(); executor.shutdownNow(); } public boolean isInitialized() { return droneStartupCoordinator.getState() == ControllerState.READY; } public void addReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener) { readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener); } public void removeReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener) { readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener); } public void addErrorListener(ErrorListener errorListener) { errorListenerComponent.addErrorListener(errorListener); } public void removeErrorListener(ErrorListener errorListener) { errorListenerComponent.removeErrorListener(errorListener); } public void addNavDataListener(NavDataListener navDataListener) { navigationDataRetriever.addNavDataListener(navDataListener); } public void removeNavDataListener(NavDataListener navDataListener) { navigationDataRetriever.removeNavDataListener(navDataListener); } public void addVideoDataListener(VideoDataListener videoDataListener) { videoRetrieverH264.addVideoDataListener(videoDataListener); videoRetrieverP264.addVideoDataListener(videoDataListener); } public void removeVideoDataListener(VideoDataListener videoDataListener) { videoRetrieverH264.removeVideoDataListener(videoDataListener); videoRetrieverP264.removeVideoDataListener(videoDataListener); } public DroneVersion getDroneVersion() { checkInitializationState(); return droneStartupCoordinator.getDroneVersion(); } public DroneConfiguration getDroneConfiguration() { checkInitializationState(); return droneStartupCoordinator.getDroneConfiguration(); } public void takeOff() { checkInitializationState(); logger.debug("Taking off"); internalStateWatcher.requestTakeOff(); } public void land() { checkInitializationState(); logger.debug("Landing"); internalStateWatcher.requestLand(); } public void emergency() { checkInitializationState(); logger.debug("Setting emergency"); internalStateWatcher.requestEmergency(); } public void flatTrim() { checkInitializationState(); logger.debug("Flat trim"); internalStateWatcher.requestFlatTrim(); } public void move(float roll, float pitch, float yaw, float gaz) { checkInitializationState(); logger.trace(String.format("Moving - roll: %.2f, pitch: %.2f, yaw: %.2f, gaz: %.2f", roll, pitch, yaw, gaz)); internalStateWatcher.requestMove(roll, pitch, yaw, gaz); } public Future switchCamera(Camera camera) { checkInitializationState(); logger.debug(String.format("Changing camera to '%s'", camera.name())); return executeCommandsAsync(new SwitchCameraCommand(config.getLoginData(), camera), new GetConfigurationDataCommand()); } public Future playLedAnimation(LedAnimation ledAnimation, float frequency, int durationSeconds) { checkInitializationState(); logger.debug(String.format("Playing LED animation '%s'", ledAnimation.name())); return executeCommandsAsync(new PlayLedAnimationCommand(config.getLoginData(), ledAnimation, frequency, durationSeconds)); } public Future playFlightAnimation(FlightAnimation animation) { checkInitializationState(); logger.debug(String.format("Playing flight animation '%s'", animation.name())); return executeCommandsAsync(new PlayFlightAnimationCommand(config.getLoginData(), animation)); } public Future setConfigValue(String key, Object value) { checkInitializationState(); logger.debug(String.format("Setting config value '%s' to '%s'", key, value.toString())); return executeCommandsAsync(new SetConfigValueCommand(config.getLoginData(), key, value), new GetConfigurationDataCommand()); } public void executeCommands(Command... commands) { for (Command command : commands) { commandSender.executeCommand(command); } } public Future executeCommandsAsync(final Command... commands) { return executor.submit(new Runnable() { @Override public void run() { executeCommands(commands); } }); } private void checkInitializationState() { checkState(isInitialized(), "The drone controller is not yet fully initialized"); } private void checkInitializationStateStarted() { checkState(droneStartupCoordinator.getState() == ControllerState.STARTED, "The drone controller has already been initialized"); } }