package org.droidplanner.services.android.impl.core.drone.variables;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import com.MAVLink.ardupilotmega.msg_ekf_status_report;
import com.MAVLink.enums.EKF_STATUS_FLAGS;
import org.droidplanner.services.android.impl.core.MAVLink.MavLinkCommands;
import org.droidplanner.services.android.impl.core.MAVLink.WaypointManager;
import org.droidplanner.services.android.impl.core.drone.DroneInterfaces.DroneEventsType;
import org.droidplanner.services.android.impl.core.drone.DroneVariable;
import org.droidplanner.services.android.impl.core.drone.autopilot.MavLinkDrone;
import org.droidplanner.services.android.impl.core.drone.autopilot.generic.GenericMavLinkDrone;
import org.droidplanner.services.android.impl.core.model.AutopilotWarningParser;
import com.o3dr.services.android.lib.drone.attribute.error.CommandExecutionError;
import com.o3dr.services.android.lib.model.ICommandListener;
import com.o3dr.services.android.lib.model.action.Action;
import timber.log.Timber;
public class State extends DroneVariable<GenericMavLinkDrone> {
private static final long ERROR_TIMEOUT = 5000l;
private final static Action requestHomeUpdateAction = new Action(MavLinkDrone.ACTION_REQUEST_HOME_UPDATE);
private final AutopilotWarningParser warningParser;
private msg_ekf_status_report ekfStatus;
private boolean isEkfPositionOk;
private String errorId;
private boolean armed = false;
private boolean isFlying = false;
private ApmModes mode = ApmModes.UNKNOWN;
// flightTimer
// ----------------
private long startTime = 0;
private final Handler handler;
private final Runnable watchdogCallback = new Runnable() {
@Override
public void run() {
resetWarning();
}
};
public State(GenericMavLinkDrone myDrone, Handler handler, AutopilotWarningParser warningParser) {
super(myDrone);
this.handler = handler;
this.warningParser = warningParser;
this.errorId = warningParser.getDefaultWarning();
resetFlightStartTime();
}
public boolean isArmed() {
return armed;
}
public boolean isFlying() {
return isFlying;
}
public ApmModes getMode() {
return mode;
}
public String getErrorId() {
return errorId;
}
public void setIsFlying(boolean newState) {
if (newState != isFlying) {
isFlying = newState;
myDrone.notifyDroneEvent(DroneEventsType.STATE);
if (isFlying) {
resetFlightStartTime();
}
}
}
public boolean parseAutopilotError(String errorMsg) {
String parsedError = warningParser.parseWarning(myDrone, errorMsg);
if (parsedError == null || parsedError.trim().isEmpty())
return false;
if (!parsedError.equals(this.errorId)) {
this.errorId = parsedError;
myDrone.notifyDroneEvent(DroneEventsType.AUTOPILOT_WARNING);
}
handler.removeCallbacks(watchdogCallback);
this.handler.postDelayed(watchdogCallback, ERROR_TIMEOUT);
return true;
}
public void repeatWarning() {
if (errorId == null || errorId.length() == 0 || errorId.equals(warningParser.getDefaultWarning()))
return;
handler.removeCallbacks(watchdogCallback);
this.handler.postDelayed(watchdogCallback, ERROR_TIMEOUT);
}
public void setArmed(boolean newState) {
if (this.armed != newState) {
this.armed = newState;
myDrone.notifyDroneEvent(DroneEventsType.ARMING);
if (newState) {
WaypointManager waypointManager = myDrone.getWaypointManager();
if(waypointManager != null) {
waypointManager.getWaypoints();
}
}
}
checkEkfPositionState(this.ekfStatus);
}
public void setMode(ApmModes mode) {
if (this.mode != mode) {
this.mode = mode;
myDrone.notifyDroneEvent(DroneEventsType.MODE);
}
}
public void changeFlightMode(ApmModes mode, final ICommandListener listener) {
if (this.mode == mode) {
if (listener != null) {
handler.post(new Runnable() {
@Override
public void run() {
try {
listener.onSuccess();
} catch (RemoteException e) {
Timber.e(e, e.getMessage());
}
}
});
}
return;
}
if (ApmModes.isValid(mode)) {
MavLinkCommands.changeFlightMode(myDrone, mode, listener);
} else {
if (listener != null) {
handler.post(new Runnable() {
@Override
public void run() {
try {
listener.onError(CommandExecutionError.COMMAND_FAILED);
} catch (RemoteException e) {
Timber.e(e, e.getMessage());
}
}
});
}
}
}
private void resetWarning() {
String defaultWarning = warningParser.getDefaultWarning();
if (defaultWarning == null)
defaultWarning = "";
if (!defaultWarning.equals(this.errorId)) {
this.errorId = defaultWarning;
myDrone.notifyDroneEvent(DroneEventsType.AUTOPILOT_WARNING);
}
}
// flightTimer
// ----------------
private void resetFlightStartTime() {
startTime = SystemClock.elapsedRealtime();
}
public long getFlightStartTime() {
return startTime;
}
public msg_ekf_status_report getEkfStatus() {
return ekfStatus;
}
public void setEkfStatus(msg_ekf_status_report ekfState) {
if (this.ekfStatus == null || !areEkfStatusEquals(this.ekfStatus, ekfState)) {
this.ekfStatus = ekfState;
myDrone.notifyDroneEvent(DroneEventsType.EKF_STATUS_UPDATE);
}
}
private void checkEkfPositionState(msg_ekf_status_report ekfStatus) {
if (ekfStatus == null)
return;
int flags = ekfStatus.flags;
boolean isOk = this.armed
? (flags & EKF_STATUS_FLAGS.EKF_POS_HORIZ_ABS) != 0
&& (flags & EKF_STATUS_FLAGS.EKF_CONST_POS_MODE) == 0
: (flags & EKF_STATUS_FLAGS.EKF_POS_HORIZ_ABS) != 0
|| (flags & EKF_STATUS_FLAGS.EKF_PRED_POS_HORIZ_ABS) != 0;
if (isEkfPositionOk != isOk) {
isEkfPositionOk = isOk;
myDrone.notifyDroneEvent(DroneEventsType.EKF_POSITION_STATE_UPDATE);
if(isEkfPositionOk){
myDrone.executeAsyncAction(requestHomeUpdateAction, null);
}
}
}
private static boolean areEkfStatusEquals(msg_ekf_status_report one, msg_ekf_status_report two) {
return one == two || !(one == null || two == null) && one.toString().equals(two.toString());
}
public boolean isEkfPositionOk() {
return isEkfPositionOk;
}
}