package com.dronecontrol.droneapi;
import com.google.inject.Inject;
import com.dronecontrol.droneapi.commands.composed.InitializeConfigurationCommand;
import com.dronecontrol.droneapi.components.AddressComponent;
import com.dronecontrol.droneapi.data.Config;
import com.dronecontrol.droneapi.data.DroneConfiguration;
import com.dronecontrol.droneapi.data.NavData;
import com.dronecontrol.droneapi.data.enums.ControllerState;
import com.dronecontrol.droneapi.data.enums.DroneVersion;
import com.dronecontrol.droneapi.listeners.DroneConfigurationListener;
import com.dronecontrol.droneapi.listeners.NavDataListener;
import com.dronecontrol.droneapi.listeners.ReadyStateChangeListener;
import org.apache.log4j.Logger;
import static com.google.common.base.Preconditions.checkState;
import static com.dronecontrol.droneapi.helpers.ThreadHelper.sleep;
public class DroneStartupCoordinator implements ReadyStateChangeListener, NavDataListener, DroneConfigurationListener
{
private static final int STOP_TIMEOUT = 3000;
private final Logger logger = Logger.getLogger(DroneStartupCoordinator.class);
private final CommandSenderCoordinator commandSenderCoordinator;
private final AddressComponent addressComponent;
private final VersionReader versionReader;
private final CommandSender commandSender;
private final NavigationDataRetriever navigationDataRetriever;
private final VideoRetrieverP264 videoRetrieverP264;
private final VideoRetrieverH264 videoRetrieverH264;
private final ConfigurationDataRetriever configurationDataRetriever;
private Config config;
private ControllerState currentState;
private DroneVersion droneVersion;
private DroneConfiguration droneConfiguration;
@Inject
public DroneStartupCoordinator(CommandSenderCoordinator commandSenderCoordinator, AddressComponent addressComponent, VersionReader versionReader,
CommandSender commandSender, NavigationDataRetriever navigationDataRetriever,
VideoRetrieverP264 videoRetrieverP264, VideoRetrieverH264 videoRetrieverH264,
ConfigurationDataRetriever configurationDataRetriever)
{
this.commandSenderCoordinator = commandSenderCoordinator;
this.addressComponent = addressComponent;
this.versionReader = versionReader;
this.commandSender = commandSender;
this.navigationDataRetriever = navigationDataRetriever;
this.videoRetrieverP264 = videoRetrieverP264;
this.videoRetrieverH264 = videoRetrieverH264;
this.configurationDataRetriever = configurationDataRetriever;
addListeners(commandSender);
currentState = ControllerState.STARTED;
}
private void addListeners(CommandSender commandSender)
{
commandSender.addReadyStateChangeListener(this);
configurationDataRetriever.addReadyStateChangeListener(this);
navigationDataRetriever.addReadyStateChangeListener(this);
videoRetrieverH264.addReadyStateChangeListener(this);
videoRetrieverP264.addReadyStateChangeListener(this);
navigationDataRetriever.addNavDataListener(this);
configurationDataRetriever.addDroneConfigurationListener(this);
}
public void start(Config config)
{
for (int currentTry = 0; currentTry <= config.getMaxStartupRetries(); currentTry++)
{
try
{
startConnecting(config);
break;
} catch (Exception e)
{
performStartupErrorActions(config, currentTry, e);
}
}
}
private void startConnecting(Config config)
{
this.config = config;
checkIfDroneIsReachable();
determineDroneVersion();
startWorkers();
waitForState(ControllerState.WORKERS_READY);
logger.info("Workers are ready to be used");
login();
logger.info("Logged in successfully");
startVideoRetriever();
waitForState(ControllerState.READY);
logger.info("Drone setup complete");
}
private void performStartupErrorActions(Config config, int currentTry, Exception e)
{
logger.warn("There was an error while connecting: " + e.getMessage());
stop();
if (currentTry == config.getMaxStartupRetries())
{
throw new IllegalStateException(e);
} else
{
sleep(STOP_TIMEOUT);
}
}
private void checkIfDroneIsReachable()
{
//checkState(addressComponent.isReachable(config.getDroneIpAddress(), Config.REACHABLE_TIMEOUT), "The drone could not be pinged");
logger.info("The drone could be pinged");
}
private void determineDroneVersion()
{
droneVersion = versionReader.getDroneVersion(config.getDroneIpAddress(), config.getFtpPort());
}
private void startWorkers()
{
commandSender.start(config.getDroneIpAddress(), config.getCommandPort());
configurationDataRetriever.start(config.getDroneIpAddress(), config.getConfigDataPort());
navigationDataRetriever.start(config.getDroneIpAddress(), config.getNavDataPort());
}
private void login()
{
if (droneVersion == DroneVersion.AR_DRONE_1)
{
commandSenderCoordinator.executeCommand(new InitializeConfigurationCommand(config.getLoginData(), config.getArDrone1VideoCodec()));
} else
{
commandSenderCoordinator.executeCommand(new InitializeConfigurationCommand(config.getLoginData(), config.getArDrone2VideoCodec()));
}
}
private void startVideoRetriever()
{
if (droneVersion == DroneVersion.AR_DRONE_1)
{
videoRetrieverP264.start(config.getDroneIpAddress(), config.getVideoDataPort());
} else
{
videoRetrieverH264.start(config.getDroneIpAddress(), config.getVideoDataPort());
}
}
private void waitForState(ControllerState state)
{
while (currentState != state)
{
sleep(Config.WAIT_TIMEOUT);
}
}
public void stop()
{
currentState = ControllerState.STOPPED;
configurationDataRetriever.stop();
navigationDataRetriever.stop();
commandSender.stop();
stopVideoRetriever();
}
private void stopVideoRetriever()
{
if (droneVersion == DroneVersion.AR_DRONE_1)
{
videoRetrieverP264.stop();
} else
{
videoRetrieverH264.stop();
}
}
@Override
public void onDroneConfiguration(DroneConfiguration configuration)
{
droneConfiguration = configuration;
}
@Override
public void onNavData(NavData navData)
{
//noinspection StatementWithEmptyBody
if (navData.getState().isCommunicationProblemOccurred())
{
// TODO if there are problems, reset the command sender sequence number
// There is no clear reference in the manual for that
// (@see Developer Manual, page 40 ("How do the client and the droneapi synchronize?")
}
}
@Override
public void onReadyStateChange(ReadyState readyState)
{
if (readyState == ReadyStateChangeListener.ReadyState.READY)
{
currentState = currentState.getNextState();
}
}
public ControllerState getState()
{
return currentState;
}
public DroneVersion getDroneVersion()
{
return droneVersion;
}
public DroneConfiguration getDroneConfiguration()
{
return droneConfiguration;
}
}