package com.dronecontrol.droneapi; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.inject.Inject; import com.dronecontrol.droneapi.components.AddressComponent; import com.dronecontrol.droneapi.components.ErrorListenerComponent; import com.dronecontrol.droneapi.components.ReadyStateListenerComponent; import com.dronecontrol.droneapi.components.TcpComponent; import com.dronecontrol.droneapi.components.ThreadComponent; import com.dronecontrol.droneapi.data.DroneConfiguration; import com.dronecontrol.droneapi.listeners.DroneConfigurationListener; import com.dronecontrol.droneapi.listeners.ReadyStateChangeListener; import org.apache.log4j.Logger; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Collection; import java.util.Map; import java.util.Set; public class ConfigurationDataRetriever implements Runnable { public static final String SEPARATOR = " = "; private final Logger logger = Logger.getLogger(ConfigurationDataRetriever.class); private final ThreadComponent threadComponent; private final AddressComponent addressComponent; private final TcpComponent tcpComponent; private final ReadyStateListenerComponent readyStateListenerComponent; private final ErrorListenerComponent errorListenerComponent; private final Set<DroneConfigurationListener> droneConfigurationListeners; private String droneIpAddress; private int configDataPort; @Inject public ConfigurationDataRetriever(ThreadComponent threadComponent, AddressComponent addressComponent, TcpComponent tcpComponent, ReadyStateListenerComponent readyStateListenerComponent, ErrorListenerComponent errorListenerComponent) { this.threadComponent = threadComponent; this.addressComponent = addressComponent; this.tcpComponent = tcpComponent; this.readyStateListenerComponent = readyStateListenerComponent; this.errorListenerComponent = errorListenerComponent; droneConfigurationListeners = Sets.newHashSet(); } public void start(String droneIpAddress, int configDataPort) { this.droneIpAddress = droneIpAddress; this.configDataPort = configDataPort; logger.info("Starting config data thread"); threadComponent.start(this); } public void stop() { logger.info("Stopping config data thread"); threadComponent.stopAndWait(); } public void addReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener) { readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener); } public void removeReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener) { readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener); } public void addDroneConfigurationListener(DroneConfigurationListener droneConfigurationListener) { if (!droneConfigurationListeners.contains(droneConfigurationListener)) { droneConfigurationListeners.add(droneConfigurationListener); } } public void removeDroneConfigurationListener(DroneConfigurationListener droneConfigurationListener) { if (droneConfigurationListeners.contains(droneConfigurationListener)) { droneConfigurationListeners.remove(droneConfigurationListener); } } @Override public void run() { try { doRun(); } catch (Throwable e) { errorListenerComponent.emitError(e); } } private void doRun() { connectToConfigDataPort(); readyStateListenerComponent.emitReadyStateChange(ReadyStateChangeListener.ReadyState.READY); while (!threadComponent.isStopped()) { try { processData(readLines()); } catch (Throwable e) { logger.error("Error processing the config control data", e); } } disconnectFromConfigDataPort(); } private void connectToConfigDataPort() { logger.info(String.format("Connecting to config data port %d", configDataPort)); tcpComponent.connect(addressComponent.getInetAddress(droneIpAddress), configDataPort, 1000); } public Collection<String> readLines() { try { return doReadLines(); } catch (IOException | ClassNotFoundException e) { throw new IllegalStateException("Error receiving current lines", e); } } private Collection<String> doReadLines() throws IOException, ClassNotFoundException { Collection<String> receivedLines = Lists.newArrayList(); try { String line = tcpComponent.getReader().readLine(); while (line != null) { receivedLines.add(line); line = tcpComponent.getReader().readLine(); } } catch (SocketTimeoutException e) { // EOF is reached (this is a dirty workaround, but there is no indicator telling us when to stop) } return receivedLines; } private void processData(Collection<String> lines) { if (lines.size() == 0) { return; } logger.debug("Drone configuration data received"); DroneConfiguration droneConfiguration = getDroneConfiguration(lines); for (DroneConfigurationListener listener : droneConfigurationListeners) { listener.onDroneConfiguration(droneConfiguration); } } private DroneConfiguration getDroneConfiguration(Collection<String> lines) { Map<String, String> configMap = Maps.newHashMap(); for (String line : lines) { String[] configOption = line.split(SEPARATOR); if (configOption.length != 2) { continue; } configMap.put(configOption[0], configOption[1]); } return new DroneConfiguration(configMap); } private void disconnectFromConfigDataPort() { logger.info(String.format("Disconnecting from config data port %d", configDataPort)); tcpComponent.disconnect(); } }