package org.droidplanner.services.android.impl.core.drone.variables; import android.os.Handler; import com.MAVLink.Messages.MAVLinkMessage; import com.MAVLink.common.msg_heartbeat; 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.MavLinkDrone; import timber.log.Timber; public class HeartBeat extends DroneVariable implements OnDroneListener<MavLinkDrone> { public static final long HEARTBEAT_NORMAL_TIMEOUT = 5000l; //ms private static final long HEARTBEAT_LOST_TIMEOUT = 15000l; //ms public static final int INVALID_MAVLINK_VERSION = -1; protected static final int FIRST_HEARTBEAT = 0; protected static final int LOST_HEARTBEAT = 1; protected static final int NORMAL_HEARTBEAT = 2; protected int heartbeatState = FIRST_HEARTBEAT; private short sysid = 1; private short compid = 1; /** * Stores the version of the mavlink protocol. */ private short mMavlinkVersion = INVALID_MAVLINK_VERSION; public final Handler watchdog; public final Runnable watchdogCallback = new Runnable() { @Override public void run() { onHeartbeatTimeout(); } }; public HeartBeat(MavLinkDrone myDrone, Handler handler) { super(myDrone); this.watchdog = handler; myDrone.addDroneListener(this); } public short getSysid() { return sysid; } public short getCompid() { return compid; } /** * @return the version of the mavlink protocol. */ public short getMavlinkVersion() { return mMavlinkVersion; } public void onHeartbeat(MAVLinkMessage msg) { msg_heartbeat heartBeatMsg = msg instanceof msg_heartbeat ? (msg_heartbeat) msg : null; if(heartBeatMsg != null){ sysid = validateToUnsignedByteRange(msg.sysid); compid = validateToUnsignedByteRange(msg.compid); mMavlinkVersion = heartBeatMsg.mavlink_version; } switch (heartbeatState) { case FIRST_HEARTBEAT: if(heartBeatMsg != null) { Timber.i("Received first heartbeat."); heartbeatState = NORMAL_HEARTBEAT; restartWatchdog(HEARTBEAT_NORMAL_TIMEOUT); myDrone.notifyDroneEvent(DroneEventsType.HEARTBEAT_FIRST); } break; case LOST_HEARTBEAT: myDrone.notifyDroneEvent(DroneEventsType.HEARTBEAT_RESTORED); // FALL THROUGH default: heartbeatState = NORMAL_HEARTBEAT; restartWatchdog(HEARTBEAT_NORMAL_TIMEOUT); break; } } public boolean hasHeartbeat() { return heartbeatState != FIRST_HEARTBEAT; } public boolean isConnectionAlive() { return heartbeatState != LOST_HEARTBEAT; } @Override public void onDroneEvent(DroneEventsType event, MavLinkDrone drone) { switch (event) { case DISCONNECTED: notifyDisconnected(); break; default: break; } } private void notifyDisconnected() { watchdog.removeCallbacks(watchdogCallback); heartbeatState = FIRST_HEARTBEAT; mMavlinkVersion = INVALID_MAVLINK_VERSION; } protected void onHeartbeatTimeout() { switch (heartbeatState) { case FIRST_HEARTBEAT: Timber.i("First heartbeat timeout."); myDrone.notifyDroneEvent(DroneEventsType.HEARTBEAT_TIMEOUT); break; default: heartbeatState = LOST_HEARTBEAT; restartWatchdog(HEARTBEAT_LOST_TIMEOUT); myDrone.notifyDroneEvent(DroneEventsType.HEARTBEAT_TIMEOUT); break; } } protected void restartWatchdog(long timeout) { // re-start watchdog watchdog.removeCallbacks(watchdogCallback); watchdog.postDelayed(watchdogCallback, timeout); } }