package com.dronecontrol.intelcontrol.control;
import com.google.inject.Inject;
import com.dronecontrol.droneapi.DroneController;
import com.dronecontrol.droneapi.data.NavData;
import com.dronecontrol.droneapi.data.enums.Camera;
import com.dronecontrol.droneapi.data.enums.FlightAnimation;
import com.dronecontrol.droneapi.data.enums.LedAnimation;
import com.dronecontrol.droneapi.listeners.NavDataListener;
import com.dronecontrol.droneapi.listeners.ReadyStateChangeListener;
import com.dronecontrol.intelcontrol.helpers.RaceTimer;
import com.dronecontrol.intelcontrol.ui.data.UIAction;
import com.dronecontrol.intelcontrol.ui.listeners.UIActionListener;
import com.dronecontrol.perceptual.data.body.Hand;
import com.dronecontrol.perceptual.data.body.Hands;
import com.dronecontrol.perceptual.data.events.DetectionData;
import com.dronecontrol.perceptual.data.events.GestureData;
import com.dronecontrol.perceptual.data.events.HandsDetectionData;
import com.dronecontrol.perceptual.helpers.CoordinateCalculator;
import com.dronecontrol.perceptual.helpers.CoordinateListener;
import com.dronecontrol.perceptual.listeners.DetectionListener;
import com.dronecontrol.perceptual.listeners.GestureListener;
import org.apache.log4j.Logger;
public class DroneInputController implements ReadyStateChangeListener, NavDataListener, UIActionListener,
DetectionListener<Hands>, GestureListener, CoordinateListener {
private static final float HEIGHT_THRESHOLD = 0.25f;
private final Logger logger = Logger.getLogger(DroneInputController.class);
private final DroneController droneController;
private final RaceTimer raceTimer;
private boolean flying = false;
private boolean ready = false;
private long lastCommandTimestamp;
private CoordinateCalculator coordinateCalculator;
@Inject
public DroneInputController(DroneController droneController, RaceTimer raceTimer, CoordinateCalculator coordinateCalculator) {
this.droneController = droneController;
this.raceTimer = raceTimer;
this.coordinateCalculator = coordinateCalculator;
coordinateCalculator.addCoordinateListener(this);
}
@Override
public void onAction(UIAction action) {
switch (action) {
case TAKE_OFF:
takeOff();
break;
case LAND:
land();
break;
case FLAT_TRIM:
flatTrim();
break;
case EMERGENCY:
emergency();
break;
case SWITCH_CAMERA:
switchCamera();
break;
case PLAY_LED_ANIMATION:
playLedAnimation();
break;
case PLAY_FLIGHT_ANIMATION:
playFlightAnimation();
break;
}
}
@Override
public void onNavData(NavData navData) {
flying = navData.getState().isFlying();
coordinateCalculator.setCurrentHeight(navData.getAltitude() < HEIGHT_THRESHOLD ? 0.0f : navData.getAltitude());
if (navData.getState().isEmergency()) {
raceTimer.stop();
}
}
private void takeOff() {
if (ready && !flying && coordinateCalculator.hasHandReferences()) {
raceTimer.start();
droneController.takeOff();
} else {
logger.warn("Cannot take off");
}
}
private void land() {
if (ready && flying) {
raceTimer.stop();
droneController.land();
}
}
private void flatTrim() {
if (ready) {
droneController.flatTrim();
}
}
private void emergency() {
if (ready) {
droneController.emergency();
}
}
private void switchCamera() {
if (ready) {
droneController.switchCamera(Camera.NEXT);
}
}
private void playLedAnimation() {
if (ready) {
droneController.playLedAnimation(LedAnimation.RED_SNAKE, 2.0f, 3);
}
}
private void playFlightAnimation() {
if (ready) {
droneController.playFlightAnimation(FlightAnimation.FLIP_LEFT);
}
}
private void move(float roll, float pitch, float yaw, float heightDelta) {
if (ready) {
droneController.move(2 * roll, 2 * pitch, 2 * yaw, heightDelta);
}
}
@Override
public void onReadyStateChange(ReadyState readyState) {
if (readyState == ReadyState.READY) {
ready = true;
} else if (readyState == ReadyState.NOT_READY) {
ready = false;
}
}
@Override
public void onGesture(GestureData gestureData) {
switch (gestureData.getGestureType()) {
case THUMBS_UP:
logger.info("Thumbs up detected.");
takeOff();
break;
case THUMBS_DOWN:
logger.info("Thumbs down detected.");
land();
break;
case BIG_FIVE:
logger.info("Big Five detected.");
emergency();
flatTrim();
break;
}
}
@Override
public void onDetection(DetectionData<Hands> handsDetectionData) {
HandsDetectionData data = (HandsDetectionData) handsDetectionData;
Hand leftHand = data.getLeftHand();
Hand rightHand = data.getRightHand();
if (leftHand.isActive() && rightHand.isActive()) {
lastCommandTimestamp = System.currentTimeMillis();
// As long as the drone is not in the air save last coordinates as
// reference
// This will stop, when the THUMBS_UP gesture is recognized
if (ready && !flying) {
coordinateCalculator.setRightHandReferenceCoordinate(rightHand.getCoordinate());
coordinateCalculator.setLeftHandReferenceCoordinate(leftHand.getCoordinate());
}
if (ready && flying) {
if (!coordinateCalculator.hasHandReferences()) {
coordinateCalculator.setRightHandReferenceCoordinate(rightHand.getCoordinate());
coordinateCalculator.setLeftHandReferenceCoordinate(leftHand.getCoordinate());
}
coordinateCalculator.calculateMoves(leftHand, rightHand);
}
} else if ((System.currentTimeMillis() - lastCommandTimestamp) >= 50) {
// Failsafe - If no information about hands is available don't move
move(0, 0, 0, 0);
}
}
@Override
public void onCoordinate(float roll, float pitch, float yaw, float heightDelta) {
//When changing the yaw stop rolling and pitching
if (Math.abs(coordinateCalculator.getYaw()) > CoordinateCalculator.MIN_YAW) {
move(0, 0, coordinateCalculator.getYaw(), coordinateCalculator.getHeightDelta());
} else {
move(coordinateCalculator.getRoll(), coordinateCalculator.getPitch(), 0, coordinateCalculator.getHeightDelta());
}
logger.trace(String.format("Roll: [%2.3f], Pitch: [%2.3f], Yaw: [%2.3f], Height: [%2.3f]", coordinateCalculator.getRoll(), coordinateCalculator.getPitch(), coordinateCalculator.getYaw(), coordinateCalculator.getHeightDelta()));
}
}