package org.droidplanner.services.android.impl.core.drone.variables;
import android.os.Handler;
import android.os.RemoteException;
import org.droidplanner.services.android.impl.core.MAVLink.MavLinkCommands;
import org.droidplanner.services.android.impl.core.drone.DroneInterfaces.DroneEventsType;
import org.droidplanner.services.android.impl.core.drone.DroneInterfaces.OnDroneListener;
import org.droidplanner.services.android.impl.core.drone.DroneVariable;
import org.droidplanner.services.android.impl.core.drone.autopilot.Drone;
import org.droidplanner.services.android.impl.core.drone.autopilot.MavLinkDrone;
import com.o3dr.services.android.lib.coordinate.LatLong;
import com.o3dr.services.android.lib.drone.attribute.AttributeType;
import com.o3dr.services.android.lib.drone.attribute.error.CommandExecutionError;
import com.o3dr.services.android.lib.drone.property.Altitude;
import com.o3dr.services.android.lib.drone.property.Gps;
import com.o3dr.services.android.lib.model.ICommandListener;
import com.o3dr.services.android.lib.model.SimpleCommandListener;
import timber.log.Timber;
public class GuidedPoint extends DroneVariable implements OnDroneListener<MavLinkDrone> {
private GuidedStates state = GuidedStates.UNINITIALIZED;
private LatLong coord = new LatLong(0, 0);
private double altitude = 0.0; //altitude in meters
private Runnable mPostInitializationTask;
private final Handler handler;
public enum GuidedStates {
UNINITIALIZED, IDLE, ACTIVE
}
public GuidedPoint(MavLinkDrone myDrone, Handler handler) {
super(myDrone);
this.handler = handler;
myDrone.addDroneListener(this);
}
@Override
public void onDroneEvent(DroneEventsType event, MavLinkDrone drone) {
switch (event) {
case HEARTBEAT_FIRST:
case HEARTBEAT_RESTORED:
case MODE:
if (isGuidedMode(myDrone)) {
initialize();
} else {
disable();
}
break;
case DISCONNECTED:
case HEARTBEAT_TIMEOUT:
disable();
default:
break;
}
}
public static boolean isGuidedMode(MavLinkDrone drone) {
if (drone == null)
return false;
final int droneType = drone.getType();
final ApmModes droneMode = drone.getState().getMode();
if (Type.isCopter(droneType)) {
return droneMode == ApmModes.ROTOR_GUIDED;
}
if (Type.isPlane(droneType)) {
return droneMode == ApmModes.FIXED_WING_GUIDED;
}
if (Type.isRover(droneType)) {
return droneMode == ApmModes.ROVER_GUIDED || droneMode == ApmModes.ROVER_HOLD;
}
return false;
}
public void pauseAtCurrentLocation(ICommandListener listener) {
if (state == GuidedStates.UNINITIALIZED) {
changeToGuidedMode(myDrone, listener);
} else {
newGuidedCoord(getGpsPosition());
state = GuidedStates.IDLE;
}
}
private LatLong getGpsPosition() {
return getGpsPosition(myDrone);
}
private static LatLong getGpsPosition(Drone drone) {
final Gps droneGps = (Gps) drone.getAttribute(AttributeType.GPS);
return droneGps == null ? null : droneGps.getPosition();
}
public static void changeToGuidedMode(MavLinkDrone drone, ICommandListener listener) {
final State droneState = drone.getState();
final int droneType = drone.getType();
if (Type.isCopter(droneType)) {
droneState.changeFlightMode(ApmModes.ROTOR_GUIDED, listener);
} else if (Type.isPlane(droneType)) {
//You have to send a guided point to the plane in order to trigger guided mode.
forceSendGuidedPoint(drone, getGpsPosition(drone), getDroneAltConstrained(drone));
} else if (Type.isRover(droneType)) {
droneState.changeFlightMode(ApmModes.ROVER_GUIDED, listener);
}
}
public void doGuidedTakeoff(final double alt, final ICommandListener listener) {
if (Type.isCopter(myDrone.getType())) {
coord = getGpsPosition();
altitude = alt;
state = GuidedStates.IDLE;
changeToGuidedMode(myDrone, new SimpleCommandListener() {
@Override
public void onSuccess() {
MavLinkCommands.sendTakeoff(myDrone, alt, listener);
myDrone.notifyDroneEvent(DroneEventsType.GUIDEDPOINT);
}
@Override
public void onError(int executionError) {
if (listener != null) {
try {
listener.onError(executionError);
} catch (RemoteException e) {
Timber.e(e, e.getMessage());
}
}
}
@Override
public void onTimeout() {
if (listener != null) {
try {
listener.onTimeout();
} catch (RemoteException e) {
Timber.e(e, e.getMessage());
}
}
}
});
} else {
if (listener != null) {
handler.post(new Runnable() {
@Override
public void run() {
try {
listener.onError(CommandExecutionError.COMMAND_UNSUPPORTED);
} catch (RemoteException e) {
Timber.e(e, e.getMessage());
}
}
});
}
}
}
public void newGuidedCoord(LatLong coord) {
changeCoord(coord);
}
public void newGuidedPosition(double latitude, double longitude, double altitude) {
MavLinkCommands.sendGuidedPosition(myDrone, latitude, longitude, altitude);
}
public void newGuidedVelocity(double xVel, double yVel, double zVel) {
MavLinkCommands.sendGuidedVelocity(myDrone, xVel, yVel, zVel);
}
public void newGuidedCoordAndVelocity(LatLong coord, double xVel, double yVel, double zVel) {
changeCoordAndVelocity(coord, xVel, yVel, zVel);
}
public void changeGuidedAltitude(double alt) {
changeAlt(alt);
}
public void forcedGuidedCoordinate(final LatLong coord, final ICommandListener listener) {
final Gps droneGps = (Gps) myDrone.getAttribute(AttributeType.GPS);
if (!droneGps.has3DLock()) {
postErrorEvent(handler, listener, CommandExecutionError.COMMAND_FAILED);
return;
}
if (isInitialized()) {
changeCoord(coord);
postSuccessEvent(handler, listener);
} else {
mPostInitializationTask = new Runnable() {
@Override
public void run() {
changeCoord(coord);
}
};
changeToGuidedMode(myDrone, listener);
}
}
public void forcedGuidedCoordinate(final LatLong coord, final double alt, final ICommandListener listener) {
final Gps droneGps = (Gps) myDrone.getAttribute(AttributeType.GPS);
if (!droneGps.has3DLock()) {
postErrorEvent(handler, listener, CommandExecutionError.COMMAND_FAILED);
return;
}
if (isInitialized()) {
changeCoord(coord);
changeAlt(alt);
postSuccessEvent(handler, listener);
} else {
mPostInitializationTask = new Runnable() {
@Override
public void run() {
changeCoord(coord);
changeAlt(alt);
}
};
changeToGuidedMode(myDrone, listener);
}
}
private void initialize() {
if (state == GuidedStates.UNINITIALIZED) {
coord = getGpsPosition();
altitude = getDroneAltConstrained(myDrone);
state = GuidedStates.IDLE;
myDrone.notifyDroneEvent(DroneEventsType.GUIDEDPOINT);
}
if (mPostInitializationTask != null) {
mPostInitializationTask.run();
mPostInitializationTask = null;
}
}
private void disable() {
if (state == GuidedStates.UNINITIALIZED)
return;
state = GuidedStates.UNINITIALIZED;
myDrone.notifyDroneEvent(DroneEventsType.GUIDEDPOINT);
}
private void changeAlt(double alt) {
switch (state) {
case UNINITIALIZED:
break;
case IDLE:
state = GuidedStates.ACTIVE;
/** FALL THROUGH **/
case ACTIVE:
altitude = alt;
sendGuidedPoint();
break;
}
}
private void changeCoord(LatLong coord) {
switch (state) {
case UNINITIALIZED:
break;
case IDLE:
state = GuidedStates.ACTIVE;
/** FALL THROUGH **/
case ACTIVE:
this.coord = coord;
sendGuidedPoint();
break;
}
}
private void changeCoordAndVelocity(LatLong coord, double xVel, double yVel, double zVel) {
switch (state) {
case UNINITIALIZED:
break;
case IDLE:
state = GuidedStates.ACTIVE;
/** FALL THROUGH **/
case ACTIVE:
this.coord = coord;
sendGuidedPointAndVelocity(xVel, yVel, zVel);
break;
}
}
private void sendGuidedPointAndVelocity(double xVel, double yVel, double zVel) {
if (state == GuidedStates.ACTIVE) {
forceSendGuidedPointAndVelocity(myDrone, coord, altitude, xVel, yVel, zVel);
}
}
private void sendGuidedPoint() {
if (state == GuidedStates.ACTIVE) {
forceSendGuidedPoint(myDrone, coord, altitude);
}
}
public static void forceSendGuidedPoint(MavLinkDrone drone, LatLong coord, double altitudeInMeters) {
drone.notifyDroneEvent(DroneEventsType.GUIDEDPOINT);
if (coord != null) {
MavLinkCommands.setGuidedMode(drone, coord.getLatitude(), coord.getLongitude(), altitudeInMeters);
}
}
public static void forceSendGuidedPointAndVelocity(MavLinkDrone drone, LatLong coord, double altitudeInMeters,
double xVel, double yVel, double zVel) {
drone.notifyDroneEvent(DroneEventsType.GUIDEDPOINT);
if (coord != null) {
MavLinkCommands.sendGuidedPositionAndVelocity(drone, coord.getLatitude(), coord.getLongitude(), altitudeInMeters, xVel,
yVel, zVel);
}
}
private static double getDroneAltConstrained(MavLinkDrone drone) {
final Altitude droneAltitude = (Altitude) drone.getAttribute(AttributeType.ALTITUDE);
double alt = Math.floor(droneAltitude.getAltitude());
return Math.max(alt, getDefaultMinAltitude(drone));
}
public LatLong getCoord() {
return coord;
}
public double getAltitude() {
return this.altitude;
}
public boolean isActive() {
return (state == GuidedStates.ACTIVE);
}
public boolean isIdle() {
return (state == GuidedStates.IDLE);
}
public boolean isInitialized() {
return !(state == GuidedStates.UNINITIALIZED);
}
public GuidedStates getState() {
return state;
}
public static float getDefaultMinAltitude(MavLinkDrone drone) {
final int droneType = drone.getType();
if (Type.isCopter(droneType)) {
return 2f;
} else if (Type.isPlane(droneType)) {
return 15f;
} else {
return 0f;
}
}
}