package com.dronecontrol.droneapi;
import com.dronecontrol.droneapi.commands.ATCommand;
import com.dronecontrol.droneapi.commands.Command;
import com.dronecontrol.droneapi.commands.simple.WatchDogCommand;
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.listeners.ReadyStateChangeListener;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import org.apache.log4j.Logger;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.List;
import static com.dronecontrol.droneapi.helpers.ThreadHelper.sleep;
public class CommandSender implements Runnable
{
private final Logger logger = Logger.getLogger(CommandSender.class);
private final ThreadComponent threadComponent;
private final AddressComponent addressComponent;
private final UdpComponent udpComponent;
private final ReadyStateListenerComponent readyStateListenerComponent;
private final ErrorListenerComponent errorListenerComponent;
private final InternalStateWatcher internalStateWatcher;
private ReadyStateChangeListener.ReadyState readyState = ReadyStateChangeListener.ReadyState.NOT_READY;
private List<ATCommand> commandsToSend;
private int sequenceNumber = 1;
private String droneIpAddress;
private int commandPort;
@Inject
public CommandSender(ThreadComponent threadComponent, AddressComponent addressComponent, UdpComponent udpComponent,
ReadyStateListenerComponent readyStateListenerComponent, ErrorListenerComponent errorListenerComponent,
InternalStateWatcher internalStateWatcher)
{
this.threadComponent = threadComponent;
this.addressComponent = addressComponent;
this.udpComponent = udpComponent;
this.readyStateListenerComponent = readyStateListenerComponent;
this.errorListenerComponent = errorListenerComponent;
this.internalStateWatcher = internalStateWatcher;
commandsToSend = Lists.newArrayList();
}
public void start(String droneIpAddress, int commandPort)
{
this.droneIpAddress = droneIpAddress;
this.commandPort = commandPort;
logger.info("Starting command sender thread");
threadComponent.start(this);
}
public void stop()
{
logger.info("Stopping command sender thread");
threadComponent.stopAndWait();
}
public void addReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener)
{
readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener);
}
public void removeReadyStateChangeListener(ReadyStateChangeListener readyStateChangeListener)
{
readyStateListenerComponent.addReadyStateChangeListener(readyStateChangeListener);
}
public void sendCommand(ATCommand command)
{
queue(command);
}
private Command queue(ATCommand command)
{
commandsToSend.add(command);
return command;
}
@Override
public void run()
{
try
{
doRun();
} catch (Throwable e)
{
errorListenerComponent.emitError(e);
}
}
private void doRun()
{
int count = 1;
connectToCommandSenderPort();
while (!threadComponent.isStopped())
{
count = trySending(count);
changeReadyState();
}
disconnectFromCommandSenderPort();
}
private int trySending(int count)
{
try
{
count = sendPendingCommands(count);
} catch (Throwable e)
{
logger.warn("Exception while trying to send data: " + e.getMessage());
}
return count;
}
private void connectToCommandSenderPort()
{
InetAddress address = addressComponent.getInetAddress(droneIpAddress);
logger.info(String.format("Connecting to command send port %d", commandPort));
udpComponent.connect(address, commandPort);
}
private int sendPendingCommands(int count)
{
List<ATCommand> commands = getCommands(count);
for (ATCommand command : commands)
{
send(command);
}
sleep(15);
return count + 1;
}
private List<ATCommand> getCommands(int count)
{
List<ATCommand> commands = commandsToSend;
if (count % 10 == 1)
{
commands.add(new WatchDogCommand());
}
commands.addAll(internalStateWatcher.getCommandsToUpholdInternalState());
commandsToSend = Lists.newArrayList();
return commands;
}
private void send(ATCommand command)
{
if (command.isPreparationCommandNeeded())
{
sendCommandText(command.getPreparationCommandText(getSequenceNumber()));
}
sendCommandText(command.getCommandText(getSequenceNumber()));
}
private void sendCommandText(String commandText)
{
byte[] sendData = commandText.getBytes();
InetAddress address = addressComponent.getInetAddress(droneIpAddress);
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, commandPort);
// For debugging purposes ... used very often
/*if (!commandText.startsWith("AT*COMWDG"))
{
System.out.println(commandText);
}*/
udpComponent.send(sendPacket);
}
private int getSequenceNumber()
{
return sequenceNumber++;
}
private void changeReadyState()
{
if (readyState != ReadyStateChangeListener.ReadyState.READY)
{
readyState = ReadyStateChangeListener.ReadyState.READY;
readyStateListenerComponent.emitReadyStateChange(ReadyStateChangeListener.ReadyState.READY);
}
}
private void disconnectFromCommandSenderPort()
{
logger.info(String.format("Disconnecting from command send port %d", commandPort));
udpComponent.disconnect();
}
}