package com.dronecontrol.droneapi;
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.ThreadComponent;
import com.dronecontrol.droneapi.components.UdpComponent;
import com.dronecontrol.droneapi.data.NavData;
import com.dronecontrol.droneapi.listeners.NavDataListener;
import com.dronecontrol.droneapi.listeners.ReadyStateChangeListener;
import com.dronecontrol.droneapi.navdata.NavigationDataDecoder;
import org.apache.log4j.Logger;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.Set;
import static com.dronecontrol.droneapi.helpers.ThreadHelper.sleep;
public class NavigationDataRetriever implements Runnable
{
public static final int RECEIVING_BUFFER_SIZE = 10240;
private final Logger logger = Logger.getLogger(NavigationDataRetriever.class);
private final ThreadComponent threadComponent;
private final AddressComponent addressComponent;
private final UdpComponent udpComponent;
private final ReadyStateListenerComponent readyStateListenerComponent;
private final ErrorListenerComponent errorListenerComponent;
private final NavigationDataDecoder decoder;
private final Set<NavDataListener> navDataListeners;
private byte[] receivingBuffer;
private DatagramPacket incomingDataPacket;
private String droneIpAddress;
private int navDataPort;
@Inject
public NavigationDataRetriever(ThreadComponent threadComponent, AddressComponent addressComponent, UdpComponent udpComponent,
ReadyStateListenerComponent readyStateListenerComponent, ErrorListenerComponent errorListenerComponent,
NavigationDataDecoder decoder)
{
super();
this.threadComponent = threadComponent;
this.addressComponent = addressComponent;
this.udpComponent = udpComponent;
this.readyStateListenerComponent = readyStateListenerComponent;
this.errorListenerComponent = errorListenerComponent;
this.decoder = decoder;
navDataListeners = Sets.newLinkedHashSet();
determineDatagramPackets();
}
public void start(String droneIpAddress, int navDataPort)
{
this.droneIpAddress = droneIpAddress;
this.navDataPort = navDataPort;
logger.info("Starting nav data thread");
threadComponent.start(this);
}
public void stop()
{
logger.info("Stopping nav data thread");
threadComponent.stopAndWait();
}
public void addReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener)
{
readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener);
}
public void removeReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener)
{
readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener);
}
public void addNavDataListener(NavDataListener navDataListener)
{
if (!navDataListeners.contains(navDataListener))
{
navDataListeners.add(navDataListener);
}
}
public void removeNavDataListener(NavDataListener navDataListener)
{
if (navDataListeners.contains(navDataListener))
{
navDataListeners.remove(navDataListener);
}
}
private void determineDatagramPackets()
{
receivingBuffer = new byte[RECEIVING_BUFFER_SIZE];
incomingDataPacket = new DatagramPacket(receivingBuffer, receivingBuffer.length);
}
@Override
public void run()
{
try
{
doRun();
} catch (Throwable e)
{
errorListenerComponent.emitError(e);
}
}
private void doRun()
{
connectToNavDataPort();
initializeCommunication();
readyStateListenerComponent.emitReadyStateChange(ReadyStateChangeListener.ReadyState.READY);
while (!threadComponent.isStopped())
{
try
{
udpComponent.receive(incomingDataPacket);
processData();
udpComponent.sendKeepAlivePacket();
} catch (Throwable e)
{
udpComponent.sendKeepAlivePacket();
logger.error(e.getMessage(), e);
}
}
disconnectFromNavDataPort();
}
private void connectToNavDataPort()
{
InetAddress address = addressComponent.getInetAddress(droneIpAddress);
logger.info(String.format("Connecting to nav data port %d", navDataPort));
udpComponent.connect(address, navDataPort);
}
private void initializeCommunication()
{
sleep(100);
udpComponent.sendKeepAlivePacket();
}
private void processData()
{
NavData navData = getNavData();
if (navData == null)
{
return;
}
logger.trace(String.format("Received nav data - battery level: %d percent, altitude: %.2f", navData.getBatteryLevel(), navData.getAltitude()));
for (NavDataListener listener : navDataListeners)
{
listener.onNavData(navData);
}
}
private NavData getNavData()
{
try
{
return decoder.getNavDataFrom(receivingBuffer, incomingDataPacket.getLength());
} catch (RuntimeException e)
{
// Happens from time to time
return null;
}
}
private void disconnectFromNavDataPort()
{
logger.info(String.format("Disconnecting from nav data port %d", navDataPort));
udpComponent.disconnect();
}
}