Loong list of updates and changes that was done in the end of the project.

Added live FTP download of images while flying, added auto report by mail with these images as attachement while flying (for imidiet reporting)
Added more mavlink support, and improved mission handling...
Uppgraded to 4.16.1 DJI SDK
Latest code not tested ... have funn.
Esse commit está contido em:
The1only
2022-05-23 13:14:00 +02:00
commit 6ebb4f04da
31 arquivos alterados com 2147 adições e 571 exclusões
+5
Ver Arquivo
@@ -36,5 +36,10 @@
<option name="name" value="$USER_HOME$/Android/Sdk/extras/m2repository" />
<option name="url" value="file:$USER_HOME$/Android/Sdk/extras/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://maven.java.net/content/groups/public/" />
</remote-repository>
</component>
</project>
+1 -1
Ver Arquivo
@@ -1,4 +1,4 @@
# RosettaDrone 2 is now updated to DJI SDK 4.14 beta 1...
# RosettaDrone 2 is now updated to DJI SDK 4.16.1...
Rosetta Drone 2 tested on **DJI Air, Mavic 2 x and Matrice 210 V2, Mavic Pro, Mavic AIR series
and supports Android 5.1 and newer, with DJI SDK 4.14 and Androidx lib
+46 -25
Ver Arquivo
@@ -1,17 +1,28 @@
apply plugin: 'com.android.application'
android {
signingConfigs {
release {
}
}
compileSdkVersion 28
defaultConfig {
applicationId "sq.rogue.rosettadrone"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName '5.1.1'
versionName '5.2.0' // This is the one to modify...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
repositories {
jcenter()
maven {
url "https://maven.java.net/content/groups/public/"
}
}
dexOptions {
javaMaxHeapSize "4g"
}
@@ -19,16 +30,20 @@ android {
buildTypes {
release {
minifyEnabled false
debuggable false
jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
debuggable true
jniDebuggable true
jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
renderscriptOptimLevel 0
}
}
packagingOptions {
pickFirst 'META-INF/LICENSE.txt' // picks the JavaMail license file
exclude 'META-INF/rxjava.properties'
}
@@ -76,8 +91,9 @@ android {
defaultConfig {
ndk {
// abiFilters 'armeabi-v7a'
// abiFilters 'armeabi-v7a', 'arm64-v8a'
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
abiFilters 'armeabi-v7a', 'arm64-v8a'
// DJI has removed x86 support...
// abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
@@ -85,20 +101,21 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// ndkVersion '21.4.7075529'
// We need to be restrict as we get error on newer versions...
buildToolsVersion '30.0.2'
// buildToolsVersion '30.0.2'
ndkVersion '22.0.7026061'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0', {
androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.squareup:otto:1.3.8'
implementation('com.dji:dji-sdk:4.14-trial1', {
implementation('com.dji:dji-sdk:4.16.1', {
/**
* Uncomment the "library-anti-distortion" if your app does not need Anti Distortion for Mavic 2 Pro and Mavic 2 Zoom.
* Uncomment the "fly-safe-database" if you need database for release, or we will download it when DJISDKManager.getInstance().registerApp
@@ -108,38 +125,42 @@ dependencies {
exclude module: 'library-anti-distortion'
exclude module: 'fly-safe-database'
})
compileOnly 'com.dji:dji-sdk-provided:4.14-trial1'
compileOnly 'com.dji:dji-sdk-provided:4.16.1'
implementation 'com.dji:dji-uxsdk:4.14-trial1'
implementation 'com.dji:dji-uxsdk:4.16'
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.2.1'
testImplementation 'junit:junit:4.13.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.4.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.7.22'
testImplementation 'org.mockito:mockito-android:2.7.22'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.core:core:1.3.2'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.core:core:1.6.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-basement:17.5.0'
implementation 'com.google.android.gms:play-services-base:17.5.0'
implementation 'com.google.android.gms:play-services-maps:18.0.2'
implementation 'com.google.android.gms:play-services-basement:18.0.2'
implementation 'com.google.android.gms:play-services-base:18.0.1'
implementation 'com.google.android.gms:play-services-gcm:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.1.0'
implementation 'com.google.android.gms:play-services-location:19.0.1'
implementation 'com.google.maps:google-maps-services:0.15.0'
implementation 'com.google.maps:google-maps-services:0.18.0'
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.31'
implementation 'javax.mail:mail:1.4.7'
// exclude 'javax.activation:activation:1.1'
// implementation 'com.sun.mail:android-activation:1.6.6'
}
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
import android.media.Ringtone;
@@ -67,6 +68,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
private Button mBtnTest;
private int hiddenkey = 0;
private Handler mUIHandler;
private static boolean running = false;
private KeyListener firmVersionListener = new KeyListener() {
@Override
@@ -158,14 +160,26 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(this.running == false){
Log.v(TAG, "First time ... Registrer Receiver");
this.running = true;
}
checkAndRequestPermissions();
//---------------- Force Landscape ether ways...
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
setContentView(R.layout.activity_connection);
initUI();
// Register the broadcast receiver for receiving the device connection's changes.
Log.v(TAG, "Registrer Receiver");
IntentFilter filter = new IntentFilter();
filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
registerReceiver(mReceiver, filter);
}
protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -197,6 +211,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
if (KeyManager.getInstance() != null) {
KeyManager.getInstance().removeListener(firmVersionListener);
}
unregisterReceiver(mReceiver);
super.onDestroy();
}
@@ -270,6 +285,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mTextModelAvailable = (TextView) findViewById(R.id.text_model_available);
mTextProduct = (TextView) findViewById(R.id.text_product_info);
mBtnOpen = (Button) findViewById(R.id.btn_start);
mBtnOpen.setOnClickListener(this);
mBtnOpen.setEnabled(false);
@@ -299,7 +315,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
@Override
public void run() {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
Ringtone r = RingtoneManager. getRingtone(getApplicationContext(), notification);
r.play();
mBtnOpen.setEnabled(true);
@@ -322,9 +338,9 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mTextConnectionStatus.setText("Status: " + str + " connected");
if (null != mProduct.getModel()) {
mTextProduct.setText("" + mProduct.getModel().getDisplayName());
mTextModelAvailable.setText("" + mProduct.getModel().getDisplayName());
} else {
mTextProduct.setText(R.string.product_information);
mTextModelAvailable.setText("Model Not Available");
}
if (KeyManager.getInstance() != null) {
KeyManager.getInstance().addListener(firmkey, firmVersionListener);
@@ -356,7 +372,16 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
lTextConnectionStatus.setText("TestMode");
mUIHandler = new Handler(Looper.getMainLooper());
mUIHandler.postDelayed(startApp, 50);
hiddenkey = 6;
// hiddenkey = 6;
}
if (hiddenkey >= 10) {
showToast("TestMode disabled...");
TextView lTextConnectionStatus = (TextView) findViewById(R.id.text_model_test);
lTextConnectionStatus.setText("NormalMode");
// mUIHandler = new Handler(Looper.getMainLooper());
// mUIHandler.postDelayed(startApp, 50);
hiddenkey = 0;
}
break;
@@ -380,6 +405,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mBtnOpen.setEnabled(false);
unregisterReceiver(mReceiver);
Log.v(TAG, "Start Maintask");
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityIfNeeded(intent,0);
@@ -1,10 +1,14 @@
package sq.rogue.rosettadrone;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.MAVLink.MAVLinkPacket;
import com.MAVLink.Messages.MAVLinkMessage;
@@ -13,16 +17,15 @@ import com.MAVLink.common.msg_attitude;
import com.MAVLink.common.msg_autopilot_version;
import com.MAVLink.common.msg_battery_status;
import com.MAVLink.common.msg_command_ack;
import com.MAVLink.common.msg_file_transfer_protocol;
import com.MAVLink.common.msg_global_position_int;
import com.MAVLink.common.msg_gps_raw_int;
import com.MAVLink.common.msg_heartbeat;
import com.MAVLink.common.msg_home_position;
import com.MAVLink.common.msg_mission_ack;
import com.MAVLink.common.msg_mission_count;
import com.MAVLink.common.msg_mission_item;
import com.MAVLink.common.msg_mission_item_int;
import com.MAVLink.common.msg_mission_item_reached;
import com.MAVLink.common.msg_mission_request;
import com.MAVLink.common.msg_mission_request_int;
import com.MAVLink.common.msg_mission_request_list;
import com.MAVLink.common.msg_param_value;
@@ -45,6 +48,7 @@ import com.MAVLink.enums.MAV_RESULT;
import com.MAVLink.enums.MAV_STATE;
import com.MAVLink.enums.MAV_TYPE;
import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -52,6 +56,8 @@ import java.net.PortUnreachableException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
@@ -88,14 +94,22 @@ import dji.common.model.LocationCoordinate2D;
import dji.common.product.Model;
import dji.common.remotecontroller.HardwareState;
import dji.common.util.CommonCallbacks;
import dji.log.DJILog;
import dji.sdk.base.BaseProduct;
import dji.sdk.battery.Battery;
import dji.sdk.camera.Camera;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.gimbal.Gimbal;
import dji.sdk.media.DownloadListener;
import dji.sdk.media.FetchMediaTaskScheduler;
import dji.sdk.media.MediaFile;
import dji.sdk.media.MediaManager;
import dji.sdk.mission.MissionControl;
import dji.sdk.mission.followme.FollowMeMissionOperator;
import dji.sdk.mission.waypoint.WaypointMissionOperator;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKManager;
import sq.rogue.rosettadrone.settings.MailReport;
import static com.MAVLink.common.msg_set_position_target_global_int.MAVLINK_MSG_ID_SET_POSITION_TARGET_GLOBAL_INT;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_COMPONENT_ARM_DISARM;
@@ -106,6 +120,7 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_TAKEOFF;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE;
import static com.MAVLink.enums.MAV_COMPONENT.MAV_COMP_ID_AUTOPILOT1;
import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG;
import static sq.rogue.rosettadrone.util.getTimestampMicroseconds;
import static sq.rogue.rosettadrone.util.safeSleep;
@@ -180,6 +195,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private FlightMode lastMode = FlightMode.ATTI_HOVER;
private HardwareState.FlightModeSwitch rcmode = HardwareState.FlightModeSwitch.POSITION_ONE;
private static HardwareState.FlightModeSwitch avtivemode = HardwareState.FlightModeSwitch.POSITION_ONE; // Change this to decide what position the mode switch should be inn.
public msg_home_position home_position = new msg_home_position();
private SendVelocityDataTask mSendVirtualStickDataTask = null;
private Timer mSendVirtualStickDataTimer = null;
@@ -199,16 +215,51 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private Gimbal mGimbal = null;
private Rotation mRotation = null;
private Model m_model;
private List<String> m_mailToAddress = null;
public float mission_alt = 0;
Model m_model;
Camera m_camera;
Aircraft m_aircraft;
File destDir = new File(Environment.getExternalStorageDirectory().getPath() + "/DroneApp/");
String m_directory;
private MailReport SendMail;
private int mAIfunction_activation = 0;
public boolean mAutonomy = false;
public int mAirBorn = 0;
int mission_loaded = -1;
public int mission_started = 0;
// FTP...
public List<MediaFile> mediaFileList = new ArrayList<MediaFile>();
public int numFiles;
private MediaManager mMediaManager;
private MediaManager.FileListState currentFileListState = MediaManager.FileListState.UNKNOWN;
private FetchMediaTaskScheduler scheduler;
public String last_downloaded_file;
public int lastDownloadedIndex = -1;
public int currentProgress = -1;
enum camera_mode {
IDLE,
SUCCSESS,
DISCONNECTED,
MEDIAMANAGER,
DOWNLOADMODESUPPORTED,
FAILURE,
TIMEOUT
}
public camera_mode switch_camera_mode = camera_mode.IDLE;
DroneModel(MainActivity parent, DatagramSocket socket, boolean sim) {
this.parent = parent;
this.socket = socket;
this.numFiles = 0;
initFlightController(sim);
}
@@ -235,19 +286,25 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
miniPIDSide = new MiniPID(0.35, 0.0, 0.0);
miniPIDFwd = new MiniPID(0.35, 0.0, 0.0);
Aircraft aircraft = (Aircraft) RDApplication.getProductInstance(); //DJISimulatorApplication.getAircraftInstance();
if (aircraft == null || !aircraft.isConnected()) {
//------------------------------------------------------------
// Prepare mail handler and add the catalog for images...
// m_directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/accident");
SendMail = new MailReport(parent,parent.getApplicationContext().getContentResolver());
m_aircraft = (Aircraft) RDApplication.getProductInstance(); //DJISimulatorApplication.getAircraftInstance();
if (m_aircraft == null || !m_aircraft.isConnected()) {
parent.logMessageDJI("No target...");
mFlightController = null;
return;
} else {
m_model = aircraft.getModel();
m_model = m_aircraft.getModel();
if (m_model.equals("INSPIRE_1") || m_model.equals("INSPIRE_1_PRO") || m_model.equals("INSPIRE_1_RAW")) {
avtivemode = HardwareState.FlightModeSwitch.POSITION_THREE;
}
mGimbal = aircraft.getGimbal();
mFlightController = aircraft.getFlightController();
mGimbal = m_aircraft.getGimbal();
m_camera = RDApplication.getCameraInstance();
mFlightController = m_aircraft.getFlightController();
mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY);
@@ -294,7 +351,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
// SetMesasageBox("Controller Ready!!!!!");
}
LocationCoordinate3D getSimPos3D(){
LocationCoordinate3D getSimPos3D() {
sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(parent.getApplicationContext());
LocationCoordinate3D pos = new LocationCoordinate3D(
Double.parseDouble(Objects.requireNonNull(sharedPreferences.getString("pref_sim_pos_lat", "-1"))),
@@ -303,24 +360,24 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
);
// If this is the first time the app is running...
if(pos.getLatitude() == -1){
if (pos.getLatitude() == -1) {
sharedPreferences.getStringSet("pref_sim_pos_lat", Collections.singleton("60.4094"));
pos.setLatitude(60.4094);
}
if(pos.getLongitude() == -1){
if (pos.getLongitude() == -1) {
sharedPreferences.getStringSet("pref_sim_pos_lon", Collections.singleton("10.4911"));
pos.setLongitude(10.4911);
}
if(pos.getAltitude() == -1){ // Not Used...
if (pos.getAltitude() == -1) { // Not Used...
sharedPreferences.getStringSet("pref_sim_pos_alt", Collections.singleton("210.0"));
pos.setAltitude((float)210.0);
pos.setAltitude((float) 210.0);
}
return (pos);
}
LocationCoordinate2D getSimPos2D(){
LocationCoordinate2D getSimPos2D() {
LocationCoordinate3D pos3d = getSimPos3D();
LocationCoordinate2D pos = new LocationCoordinate2D(pos3d.getLatitude(),pos3d.getLongitude());
LocationCoordinate2D pos = new LocationCoordinate2D(pos3d.getLatitude(), pos3d.getLongitude());
return (pos);
}
@@ -343,6 +400,17 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
});
}
// These are no long used as this is an internal process now, sending data to RemoteConfig...
void setAiIP(final String ip)
{}
void setAiPort(final int port)
{}
void setAIenable( final boolean enable)
{
parent.mMavlinkReceiver.AIenabled = enable;
parent.Set_ai_mode();
}
void setMaxHeight(final int height) {
djiAircraft.getFlightController().setMaxFlightHeight(height, djiError -> {
if (djiError == null) {
@@ -475,33 +543,33 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
void setWaypointMission(final WaypointMission wpMission) {
mission_loaded = -1;
DJIError load_error = getWaypointMissionOperator().loadMission(wpMission);
if (load_error != null)
parent.logMessageDJI("loadMission() returned error: " + load_error.toString());
else {
// We can not wait until we know the results as it takes too long for QGC on large missions...
send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
parent.logMessageDJI("Uploading mission");
getWaypointMissionOperator().uploadMission(
djiError -> {
if (djiError == null) {
while (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.UPLOADING) {
safeSleep(200);
safeSleep(100);
}
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
send_text("Mission uploaded and ready to execute!");
parent.logMessageDJI("Mission uploaded and ready to execute!");
mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED;
send_mission_ack(mission_loaded);
} else {
send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ERROR);
send_text("Error uploading waypoint mission to drone.");
parent.logMessageDJI("Error uploading waypoint mission to drone.");
mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ERROR;
send_mission_ack(mission_loaded);
}
} else {
send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ERROR);
send_text("\"Error uploading: \" + djiError.getDescription()");
parent.logMessageDJI("Error uploading: " + djiError.getDescription());
parent.logMessageDJI(("Please try re-uploading"));
mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ERROR;
send_mission_ack(mission_loaded);
}
//parent.logMessageDJI("New state: " + getWaypointMissionOperator().getCurrentState().getName());
});
@@ -594,16 +662,13 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
mControllerVoltage_pr = (batteryState.getRemainingChargeInPercent());
if (mControllerVoltage_pr > 90) {
lastState = 100;
}
else if (mControllerVoltage_pr < 20 && lastState == 100) {
} else if (mControllerVoltage_pr < 20 && lastState == 100) {
lastState = 20;
SetMesasageBox("Controller Battery Warning 20% !!!!!");
}
else if (mControllerVoltage_pr < 10 && lastState == 20) {
} else if (mControllerVoltage_pr < 10 && lastState == 20) {
lastState = 10;
SetMesasageBox("Controller Battery Warning 10% !!!!!");
}
else if (mControllerVoltage_pr < 5 && lastState == 10) {
} else if (mControllerVoltage_pr < 5 && lastState == 10) {
lastState = 5;
SetMesasageBox("Controller Battery Warning 5% !!!!!");
}
@@ -631,16 +696,13 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
if (mCVoltage_pr > 90) {
mlastState = 100;
}
else if (mCVoltage_pr <= 20 && mlastState == 100) {
} else if (mCVoltage_pr <= 20 && mlastState == 100) {
mlastState = 20;
SetMesasageBox("Drone Battery Warning 20% !!!!!");
}
else if (mCVoltage_pr <= 10 && mlastState == 20) {
} else if (mCVoltage_pr <= 10 && mlastState == 20) {
mlastState = 10;
SetMesasageBox("Drone Battery Warning 10% !!!!!");
}
else if (mCVoltage_pr <= 5 && mlastState == 10) {
} else if (mCVoltage_pr <= 5 && mlastState == 10) {
mlastState = 5;
SetMesasageBox("Drone Battery Warning 5% !!!!!");
}
@@ -742,6 +804,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void armMotors() {
if (mSafetyEnabled) {
send_text(parent.getResources().getString(R.string.safety_launch));
parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
send_command_ack(MAV_CMD_COMPONENT_ARM_DISARM, MAV_RESULT.MAV_RESULT_DENIED);
} else {
@@ -822,6 +885,9 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg_autopilot_version msg = new msg_autopilot_version();
msg.capabilities = MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_COMMAND_INT;
msg.capabilities |= MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_MISSION_INT;
msg.os_sw_version = 0x040107;
msg.middleware_sw_version = 0x040107;
msg.flight_sw_version = 0x040107;
sendMessage(msg);
}
@@ -989,7 +1055,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private void send_altitude() {
msg_altitude msg = new msg_altitude();
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
msg.altitude_relative = (int) (coord.getAltitude() * 1000);
msg.altitude_relative = (float) (coord.getAltitude() ); //* 1000);
m_alt = msg.altitude_relative;
sendMessage(msg);
}
@@ -1014,6 +1080,67 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
sendMessage(msg);
}
void send_command_ftp_string_ack(int message_id, String data, byte[] header) {
msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
byte[] byteString = data.getBytes();
short[] shortArray = new short[251];
if (header != null) {
for (int i = 0; i < header.length; i++) {
short s = (short) (header[i] & 0xFF);
shortArray[i] = s;
}
}
int sizeCounter = 0;
for (int i = 0; i < byteString.length; i++) {
int added = 12 + i;
short s = byteString[i];
shortArray[added] = s;
sizeCounter += 1;
}
shortArray[3] = (short) 128;
shortArray[4] = (short) sizeCounter;
msg.payload = shortArray;
msg.msgid = message_id;
sendMessage(msg);
}
void send_command_ftp_bytes_ack(int message_id, byte[] data) {
msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
short[] shortArray = new short[data.length];
for (int i = 0; i < data.length; i++) {
short s = (short) (data[i] & 0xFF);
shortArray[i] = s;
}
shortArray[3] = (short) 128;
msg.payload = shortArray;
msg.msgid = message_id;
sendMessage(msg);
}
void send_command_ftp_nak(int message_id, int errorCode, int failCode) {
msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
short[] shortArray = new short[251];
shortArray[3] = (short) 129; //nak code
if (failCode != 0) {
shortArray[4] = (short) 2; //error size
shortArray[13] = (short) failCode;
} else {
shortArray[4] = (short) 1; //error size
}
shortArray[12] = (short) errorCode; // the error code
msg.msgid = message_id;
sendMessage(msg);
}
private void send_global_position_int() {
msg_global_position_int msg = new msg_global_position_int();
@@ -1021,6 +1148,9 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg.lat = (int) (coord.getLatitude() * Math.pow(10, 7));
msg.lon = (int) (coord.getLongitude() * Math.pow(10, 7));
// This is a bad idea, but the best we can do...
msg.alt = (int) ((coord.getAltitude()+mission_alt) * Math.pow(10, 3));
// NOTE: Commented out this field, because msg.relative_alt seems to be intended for altitude above the current terrain,
// but DJI reports altitude above home point.
// Mavlink: Millimeters above ground (unspecified: presumably above home point?)
@@ -1048,20 +1178,42 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
public double get_current_lat() {
if (mFlightController == null) {
Log.d(TAG, "getSimPos3D().getLatitude()="+getSimPos3D().getLatitude());
return getSimPos3D().getLatitude();
}
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
Log.d(TAG, "coord.getLatitude()="+coord.getLatitude());
return coord.getLatitude();
}
public double get_current_lon() {
if (mFlightController == null)
return getSimPos3D().getLongitude();
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
return coord.getLongitude();
}
public float get_current_alt() {
if (mFlightController == null)
return getSimPos3D().getAltitude();
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
return coord.getAltitude();
}
public float get_current_head() {
if (mFlightController == null)
return (float) 123.45;
double yaw = djiAircraft.getFlightController().getState().getAttitude().yaw;
if (yaw < 0)
yaw += 360;
return (float) yaw;
}
private void send_gps_raw_int() {
msg_gps_raw_int msg = new msg_gps_raw_int();
@@ -1146,8 +1298,11 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
mAIfunction_activation = 0;
}
mAutonomy = false;
// pauseWaypointMission(); // TODO:: halt mission for safety...
mAutonomy = false; // TODO:: This variable needs to be checked elseware in the code...
if(mission_started == 1) {
pauseWaypointMission(); // TODO:: halt mission for safety...
}
}
msg.chan8_raw = (mAIfunction_activation * 100) + 1000;
@@ -1210,12 +1365,10 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
void send_home_position() {
msg_home_position msg = new msg_home_position();
msg.latitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLatitude() * Math.pow(10, 7));
msg.longitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLongitude() * Math.pow(10, 7));
// msg.altitude = (int) (djiAircraft.getFlightController().getState().getHomePointAltitude());
msg.altitude = (int) (djiAircraft.getFlightController().getState().getGoHomeHeight());
home_position.latitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLatitude() * Math.pow(10, 7));
home_position.longitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLongitude() * Math.pow(10, 7));
home_position.altitude = (int) (djiAircraft.getFlightController().getState().getTakeoffLocationAltitude());
//home_position.altitude = (int) (djiAircraft.getFlightController().getState().getGoHomeHeight());
// msg.x = 0;
// msg.y = 0;
@@ -1223,7 +1376,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
// msg.approach_x = 0;
// msg.approach_y = 0;
// msg.approach_z = 0;
sendMessage(msg);
sendMessage(home_position);
}
void set_home_position(double lat, double lon) {
@@ -1423,19 +1576,18 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg.mission_type = MAV_MISSION_TYPE.MAV_MISSION_TYPE_MISSION;
// Get the full expanded list...
msg.count = send_mission_item(-1);
Log.d(TAG, "Mission Count: "+msg.count);
Log.d(TAG, "Mission Count: " + msg.count);
sendMessage(msg);
}
// This is a bit tricky, as DJI and MAVlink pack mission ithems very different...
// Firste we expand the full mission list and then we pick the one we need...
int send_mission_item(int i)
{
int send_mission_item(int i) {
ArrayList<msg_mission_item_int> msglist = new ArrayList<>();
WaypointMission x = getWaypointMissionOperator().getLoadedMission();
// If a mission loaded...
if(x!=null) {
if (x != null) {
// For all DJI mission items...
for (Waypoint wp : Objects.requireNonNull(x).getWaypointList()) {
@@ -1523,10 +1675,10 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
}
}
if(i>=0)
if (i >= 0)
sendMessage(msglist.get(i));
}
Log.d(TAG, "Mission return: "+i);
Log.d(TAG, "Mission return: " + i);
return (msglist.size());
}
@@ -1545,6 +1697,18 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
sendMessage(msg);
}
// Send mission accepted back to Mavlink...
void send_text(String status) {
parent.logMessageDJI("Mission status: " + status);
msg_statustext msg = new msg_statustext();
int i = 0;
for( char x:status.toCharArray()){
msg.text[i++]=(byte)x;
}
sendMessage(msg);
}
public void fetch_gcs_mission() {
request_mission_list();
}
@@ -1567,12 +1731,14 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void startWaypointMission() {
mAutonomy = false;
mission_started = 1;
parent.logMessageDJI("start WaypointMission()");
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("start WaypointMission() - WaypointMissionOperator null");
return;
}
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
parent.logMessageDJI("Ready to execute mission!\n");
} else {
@@ -1582,7 +1748,11 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
if (mSafetyEnabled) {
parent.logMessageDJI("You must turn off the safety to start mission");
send_text("You must turn off the safety to start mission");
} else {
// Set default camera angle at 45deg down...
do_set_Gimbal(9, (float)((-45.0*5.5)+1500.0));
getWaypointMissionOperator().startMission(djiError -> {
if (djiError != null)
parent.logMessageDJI("Error: " + djiError.toString());
@@ -1594,6 +1764,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
public void stopWaypointMission() {
mAutonomy = false;
mission_started = 0;
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("stopWaypointMission() - mWaypointMissionOperator null");
return;
@@ -1612,6 +1783,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void pauseWaypointMission() {
mAutonomy = false;
mission_started = 0;
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("pauseWaypointMission() - mWaypointMissionOperator null");
return;
@@ -1630,11 +1802,6 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void resumeWaypointMission() {
mAutonomy = false;
if (isSafetyEnabled()) {
parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
return;
}
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("resumeWaypointMission() - mWaypointMissionOperator null");
@@ -1648,6 +1815,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
parent.logMessageDJI("Error: " + djiError.toString());
else {
parent.logMessageDJI("Mission resumed!\n");
mission_started = 1;
mGCSCommandedMode = NOT_USING_GCS_COMMANDED_MODE;
}
});
@@ -1661,6 +1829,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
if (mSafetyEnabled) {
parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
send_text(parent.getResources().getString(R.string.safety_launch));
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
return;
}
@@ -1672,84 +1841,28 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
return;
}
/*
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
startWaypointMission();
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
} else {
parent.logMessageDJI("Initiating takeoff");
djiAircraft.getFlightController().startTakeoff(djiError -> {
if (djiError != null) {
parent.logMessageDJI("Error: " + djiError.toString());
// send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_FAILED);
mAirBorn = 1;
} else {
parent.logMessageDJI("Takeoff successful!\n");
// send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
mAirBorn = 2;
}
mGCSCommandedMode = NOT_USING_GCS_COMMANDED_MODE;
});
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.EXECUTION_PAUSED) {
resumeWaypointMission();
}
// Wait for command to be executed...
Log.d(TAG,"Wait for takeoff...");
while( mAirBorn == 0) {
safeSleep(250);
}
// Check if ok or failed...
if(mAirBorn == 1) {
Log.d(TAG,"Takeoff failed...");
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_FAILED);
}
else{
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_IN_PROGRESS);
int timeout = 0;
// If we took off, wait for command co complete...
Log.d(TAG,"Wait for first altitude...");
while (m_alt < 1000.0 || timeout > (20*(1/0.250))) {
timeout+= 1;
safeSleep(250);
else {
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
Log.d(TAG, "Start Mission...");
// But how about takeoff....
startWaypointMission();
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
} else {
FlightControllerState coord = djiAircraft.getFlightController().getState();
TimeLine.TimeLinetakeOff(coord.getAircraftLocation().getLatitude(), coord.getAircraftLocation().getLongitude(), alt, 0);
TimeLine.startTimeline();
Log.d(TAG, "Takeoff started...");
}
Log.d(TAG,"Takeoff success...");
safeSleep(500);
timeout = 0;
// If we are airborne, or was airborne from the start...
if (m_alt >= 800) {
float desired_alt = alt - m_alt;
Log.d(TAG, "Climb out...from: "+m_alt+" to: "+desired_alt+" set: "+alt);
// Climb out to 99% of requested altitude, however 100% is set, just to avoid deadlock...
while (m_alt < desired_alt || timeout > (30*(1/0.250))){
do_set_motion_relative(MAV_CMD_NAV_TAKEOFF, 0, 0, (alt - m_alt) / (float) 1000.0, 0, 0, 0, 0, 0, 0b00011111111000);
timeout+= 1;
safeSleep(250);
}
Log.d(TAG, "Climb out completed..."+m_alt);
}else{
Log.d(TAG, "Climb out failed...");
}
}
*/
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
// But how about takeoff....
startWaypointMission();
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
} else {
FlightControllerState coord = djiAircraft.getFlightController().getState();
TimeLine.TimeLinetakeOff(coord.getAircraftLocation().getLatitude(), coord.getAircraftLocation().getLongitude(), alt, 0);
TimeLine.startTimeline();
Log.d(TAG, "Takeoff started...");
}
}
void do_land() {
mAutonomy = false;
mission_started = 0;
parent.logMessageDJI("Initiating landing");
djiAircraft.getFlightController().startLanding(djiError -> {
if (djiError != null)
@@ -1764,6 +1877,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void do_go_home() {
mAutonomy = false;
mission_started = 0;
parent.logMessageDJI("Initiating Go Home");
djiAircraft.getFlightController().startGoHome(djiError -> {
if (djiError != null)
@@ -1776,7 +1890,6 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
/********************************************
* Motion implementation *
********************************************/
void do_set_Gimbal(float channel, float value) {
Rotation.Builder builder = new Rotation.Builder().mode(RotationMode.ABSOLUTE_ANGLE).time(2);
float param = (value - (float) 1500.0) / (float) 5.5;
@@ -1797,7 +1910,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
m_ServoSet = builder.build();
Log.e(TAG, "Set Gimbal Pos: " + value + " " +m_ServoPos_pitch + " : " + m_ServoPos_yaw);
mGimbal.rotate(m_ServoSet, djiError -> {
if (djiError != null)
parent.logMessageDJI("Error: " + djiError.toString());
@@ -2477,4 +2590,240 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
parent.logMessageDJI("Was: lat " + lat + " lon " + lon + " newlat " + newlat + " newlon " + newlon);
return new double[]{newlat, newlon};
}
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// FTP and file related functions
public void initMediaManager(List<String> address)
{
m_mailToAddress = address;
DJILog.e(TAG, "Addresses: "+address.toString());
parent.logMessageDJI("Image retrival started...");
switch_camera_mode = camera_mode.IDLE;
if (m_aircraft == null)
{
mediaFileList.clear();
DJILog.e(TAG, "Product disconnected");
switch_camera_mode = camera_mode.DISCONNECTED;
return;
} else {
if (null != m_camera && m_camera.isMediaDownloadModeSupported())
{
mMediaManager = m_camera.getMediaManager();
// Is actually of no interest...
if(!m_camera.isSSDSupported()){
parent.logMessageDJI("Internal ssd is not suported");
}
if (null != mMediaManager) {
mMediaManager.addUpdateFileListStateListener(this.updateFileListStateListener);
//switchCameraMode(SettingsDefinitions.CameraMode.MEDIA_DOWNLOAD);
Objects.requireNonNull(RDApplication.getCameraInstance()).setMode(SettingsDefinitions.CameraMode.MEDIA_DOWNLOAD, error -> {
if (error == null) {
parent.logMessageDJI("Set camera to MEDIA_DOWNLOAD success");
getFileList();
} else {
parent.logMessageDJI("Set camera to MEDIA_DOWNLOAD failed");
parent.logMessageDJI(error.toString());
}
});
// Is actually of no interest...
if (mMediaManager.isVideoPlaybackSupported()) {
parent.logMessageDJI("Camera support video playback!");
} else {
parent.logMessageDJI("Camera does not support video playback!");
}
scheduler = mMediaManager.getScheduler();
} else{
switch_camera_mode = camera_mode.MEDIAMANAGER;
}
} else if (null != m_camera && !m_camera.isMediaDownloadModeSupported()) {
parent.logMessageDJI("Media Download Mode not Supported");
switch_camera_mode = camera_mode.DOWNLOADMODESUPPORTED;
}
}
return;
}
public void getFileList() {
BaseProduct product = null;
if (RDApplication.getSim() == false) product = m_aircraft;
if (product != null){
if(product.isConnected()) {
mMediaManager = m_camera.getMediaManager();
if (mMediaManager != null) {
parent.logMessageDJI(currentFileListState.name());
if ((currentFileListState == MediaManager.FileListState.SYNCING) || (currentFileListState == MediaManager.FileListState.DELETING)) {
DJILog.e(TAG, "Media Manager is busy.");
parent.logMessageDJI("Media Manager is busy.");
} else {
mMediaManager.refreshFileListOfStorageLocation(SettingsDefinitions.StorageLocation.SDCARD, djiError -> {
if (null == djiError) {
//Reset data
if (currentFileListState != MediaManager.FileListState.INCOMPLETE) {
mediaFileList.clear();
}
mediaFileList = mMediaManager.getSDCardFileListSnapshot();
Collections.sort(mediaFileList, (lhs, rhs) -> {
if (lhs.getTimeCreated() < rhs.getTimeCreated()) {
return -1;
} else if (lhs.getTimeCreated() > rhs.getTimeCreated()) {
return 1;
}
return 0;
});
scheduler.resume(error -> {
if (error == null) {
}
});
if(numFiles != mediaFileList.size()){
numFiles = mediaFileList.size();
}
parent.logMessageDJI("Got Media Files: " + mediaFileList.size());
for(int i=0; i<mediaFileList.size(); i++) {
MediaFile file = mediaFileList.get(i);
String fileName = file.getFileName();
parent.logMessageDJI("FileName on Drone: " + fileName);
}
} else {
parent.logMessageDJI("Get Media File List Failed: " + djiError.getDescription());
}
});
}
} else {
parent.logMessageDJI("mMediaManager == null");
}
}else {
parent.logMessageDJI("product == None, not connected...");
}
} else {
parent.logMessageDJI("product == null, Product in SIM mode ?");
}
}
//Listeners
private MediaManager.FileListStateListener updateFileListStateListener = new MediaManager.FileListStateListener() {
@Override
public void onFileListStateChange(MediaManager.FileListState state) {
currentFileListState = state;
parent.logMessageDJI("Changed state to " + currentFileListState.name());
if(currentFileListState == MediaManager.FileListState.UP_TO_DATE){
if(m_mailToAddress != null) {
parent.logMessageDJI("getFile");
if (numFiles > 0) {
downloadFileByIndex(numFiles - 1);
}
}
}
}
};
public void downloadFileByName(final String name) {
for(int i=0; i<mediaFileList.size(); i++) {
if (mediaFileList.get(i).getFileName() == name) {
downloadFileByIndex(i);
}
}
}
public boolean downloadFileByIndex(final int index){
if ((mediaFileList.get(index).getMediaType() == MediaFile.MediaType.PANORAMA)
|| (mediaFileList.get(index).getMediaType() == MediaFile.MediaType.SHALLOW_FOCUS)) {
parent.logMessageDJI( "Media type is " + mediaFileList.get(index).getMediaType() + " This is not accaptable.");
return false;
}
mediaFileList.get(index).fetchFileData(destDir, null, new DownloadListener<String>() {
@Override
public void onFailure(DJIError error) {
parent.logMessageDJI( "Download File Failed" + error.getDescription());
}
@Override
public void onProgress(long total, long current) {
}
@Override
public void onRateUpdate(long total, long current, long persize) {
int tmpProgress = (int) (1.0 * current / total * 100);
if (tmpProgress != currentProgress) {
currentProgress = tmpProgress;
}
}
@Override
public void onRealtimeDataUpdate(byte[] bytes, long l, boolean b) {
}
@Override
public void onStart() {
currentProgress = 0;
}
@Override
public void onSuccess(String filePath) {
m_directory = filePath;
parent.logMessageDJI( "Download File Success: " + filePath + "/" + mediaFileList.get(index).getFileName());
// last_downloaded_file = filePath+ "/" + mediaFileList.get(index).getFileName();
last_downloaded_file = mediaFileList.get(index).getFileName();
RDApplication.getCameraInstance().setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, error -> {
if (error == null) {
parent.logMessageDJI("Set camera to SHOOT_PHOTO success");
sendmail(last_downloaded_file);
NotificationHandler.notifySnackbar(parent.findViewById(R.id.snack),R.string.release, LENGTH_LONG);
} else {
parent.logMessageDJI("Set camera to SHOOT_PHOTO failed");
parent.logMessageDJI(error.toString());
}
});
lastDownloadedIndex = index;
}
});
return true;
}
//--------------------------------------------------------------
void sendmail(String file_toSend)
{
Log.i(TAG, "File to send: "+file_toSend);
try {
Intent email = SendMail.createEmail(
m_mailToAddress,
"Status report",
"There is an issue: ",
get_current_lat(),
get_current_lon(),
get_current_alt(),
get_current_head(),
file_toSend,
m_directory
);
if(email != null) {
try {
parent.startActivity(Intent.createChooser(email, "Send mail..."));
Log.i(TAG, "Finished sending email...");
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(parent, "There is no email client installed.", Toast.LENGTH_SHORT).show();
}
}
}catch (Exception e) {
Toast.makeText(parent, "Can not send email: "+ e.toString(), Toast.LENGTH_SHORT).show();
}
}
void onFileListStateChange(MediaManager.FileListState state){
parent.logMessageDJI( "Files changed?");
}
// FTP and file related functions
//---------------------------------------------------------------------------------------
}
@@ -0,0 +1,234 @@
package sq.rogue.rosettadrone;
import com.MAVLink.common.msg_file_transfer_protocol;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static com.MAVLink.common.msg_file_transfer_protocol.MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL;
import static sq.rogue.rosettadrone.util.safeSleep;
public class FTPManager {
private MainActivity parent;
private DroneModel mModel;
private File currentFile;
private byte[][] currentFileInPacket;
public FTPManager(MainActivity parent, DroneModel mModel) {
this.parent = parent;
this.mModel = mModel;
this.currentFile = null;
this.currentFileInPacket = null;
}
public void manage_ftp(msg_file_transfer_protocol msg_ftp_item){
int session_id = new Short(msg_ftp_item.payload[2]).intValue();
int opCode = new Short(msg_ftp_item.payload[3]).intValue();
int offset = getOffset(msg_ftp_item.payload);
// DEBUG
// parent.logMessageDJI("opCode: " + opCode);
// parent.logMessageDJI("session_id: " + session_id);
// parent.logMessageDJI("Offset: " + offset);
// parent.logMessageDJI("code was " + opCode);
switch (opCode) {
case 3: // Return list
fetchFiles(offset);
break;
case 4: // Open file for reading
int file_id = new Short(msg_ftp_item.payload[12]).intValue();
openFile(file_id, session_id);
break;
case 5: // Read file
readFile(session_id, offset, msg_ftp_item.payload);
break;
case 8: // Remove file
break;
}
}
public int getOffset(short[] payload){
// parent.logMessageDJI("CAME HERE!");
byte[] byteArray = new byte[4];
for(int i = 0; i <= 3; i++){
int added = 8 + i;
short x = new Short(payload[added]);
byteArray[i] = (byte)(x & 0xff);
};
int offset = ByteBuffer.wrap(byteArray).getInt();
return offset;
}
public void fetchFiles(int offset){
mModel.initMediaManager(null);
// TODO: Wait for answer hmm will this work???...
do {
safeSleep(500);
}while(mModel.switch_camera_mode == DroneModel.camera_mode.IDLE);
parent.logMessageDJI("Getting files");
//parent.getFilesDir();
if(parent.mediaFileList.size() < offset) {
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
} else {
String dir_items = "";
String add_dir = "";
for (int i = offset; i < parent.mediaFileList.size(); i++) {
add_dir = "F" + parent.mediaFileList.get(i).getFileName() + "\\t" + parent.mediaFileList.get(i).getFileSize() + "\\0";
if (dir_items.getBytes().length + add_dir.getBytes().length < (251 - 12)) {
dir_items += add_dir;
}
}
parent.logMessageDJI("total: " + dir_items.getBytes().length + ": " + dir_items);
mModel.send_command_ftp_string_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, dir_items, null);
}
}
public void openFile(int file_id, int session_id){
parent.logMessageDJI("Download file by index " + file_id);
if(parent.lastDownloadedIndex != file_id) {
mModel.downloadFileByIndex(file_id);
// wait for done
while (parent.mModel.currentProgress != -1){
safeSleep(1000);
parent.logMessageDJI("Waiting for current progress: " + parent.mModel.currentProgress);
};
}
if(!parent.downloadError){
parent.logMessageDJI("No error while downloading");
currentFileInPacket = new byte[0][0];
currentFile = null;
parent.logMessageDJI("Reset currentfile and currentfileinpackets");
byte[] header = new byte[12];
header[2] = (byte)session_id;
header[4] = 4;
parent.logMessageDJI(parent.last_downloaded_file);
try {
currentFile = new File(parent.last_downloaded_file);
} catch (Exception e){
parent.logMessageDJI(e.toString());
}
// convert to byte array
try (InputStream is = new FileInputStream(currentFile)) {
if (currentFile.length() > Integer.MAX_VALUE) {
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
parent.logMessageDJI("File to big!");
}
parent.logMessageDJI("read file to bytes...");
byte[] fetchedFile = readFileToBytes(currentFile);
// byte [x,2,e,d,f,ge,e,e,s,df,s]
parent.logMessageDJI("Current file in bytes: " + fetchedFile.length);
int payload_data_size = (251-12);
int total_file_size = (int) currentFile.length();
float total_packets_float = (float) total_file_size/payload_data_size;
int total_packets = (int) Math.ceil(total_packets_float);
int bytes_to_add_size = 0;
int current_byte = 0;
parent.logMessageDJI("Getting packets: " + total_packets);
currentFileInPacket = new byte[total_packets][];
try {
for (int i = 0; i < total_packets; i++) {
current_byte = payload_data_size * i; // 5.802.442 (+13 = 5.802.455)
bytes_to_add_size = payload_data_size; //239
if (current_byte + payload_data_size > fetchedFile.length) {
bytes_to_add_size = fetchedFile.length - current_byte;
parent.logMessageDJI("Getting a total of last byte size " + bytes_to_add_size); //13
}
currentFileInPacket[i] = new byte[bytes_to_add_size];
for (int j = 0; j < bytes_to_add_size; j++) {
currentFileInPacket[i][j] = fetchedFile[current_byte + j];
}
}
parent.logMessageDJI("Current file in bytes: " + total_file_size + ", total packets: " + total_packets);
} catch (Exception e){
parent.logMessageDJI("Exception: " + e.toString());
}
} catch (FileNotFoundException e) {
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
parent.logMessageDJI("FileNotFoundException: " + e.toString());
return;
} catch (IOException e) {
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
parent.logMessageDJI("IOException: " + e.toString());
return;
}
int file_size = (int)currentFile.length();
parent.logMessageDJI("File size: " + file_size);
mModel.send_command_ftp_string_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, file_size + "", null);
parent.logMessageDJI("Opened image " + currentFile.getName());
} else {
parent.logMessageDJI("There was an error");
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
}
}
public void readFile(int session_id, int offset, short[] payload){
try { //remove this try catch block after debugging
int bytes_to_add_size = currentFileInPacket[offset].length;
int bytes_with_header_size = bytes_to_add_size + 12;
byte[] data = new byte[bytes_with_header_size];
// Set offset from original payload
for (int i = 8; i <= 12; i++) {
short x = new Short(payload[i]);
data[i] = (byte) (x & 0xff);
}
// Set session
data[2] = (byte) session_id;
data[4] = (byte) bytes_to_add_size;
if (currentFile == null || currentFile.length() == 0) {
parent.logMessageDJI("File is null, sending error code");
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
return;
}
if (offset > (currentFileInPacket.length)) {
parent.logMessageDJI("offset > currentFileInPacket.length");
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 6, 0);
return;
}
try {
// We only need to rewrite byte 12 to bytes_to_add_size (which is the total bytes in currentFileInPacket[packet])
// Then we just read a packet byte by using the offset as packet_id and i from bytes_to_add_size
for (int i = 0; i < bytes_to_add_size; i++) {
int added = i + 12;
data[added] = currentFileInPacket[offset][i];
}
mModel.send_command_ftp_bytes_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, data);
} catch (Exception e) {
parent.logMessageDJI(e.toString());
mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 6, 0);
}
} catch (Exception e) {
parent.logMessageDJI(e.toString());
}
}
private byte[] readFileToBytes(File file) throws IOException {
byte[] bytes = new byte[(int) file.length()];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
//read file into bytes[]
fis.read(bytes);
} finally {
if (fis != null) {
fis.close();
}
}
return bytes;
}
}
@@ -15,11 +15,23 @@ import com.MAVLink.common.msg_param_request_read;
import com.MAVLink.common.msg_param_set;
import com.MAVLink.common.msg_set_mode;
import com.MAVLink.common.msg_set_position_target_global_int;
import com.MAVLink.common.msg_file_transfer_protocol;
import com.MAVLink.common.msg_set_position_target_local_ned;
import com.MAVLink.enums.MAV_CMD;
import com.MAVLink.enums.MAV_MISSION_RESULT;
import com.MAVLink.enums.MAV_MISSION_TYPE;
import com.MAVLink.enums.MAV_RESULT;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import dji.common.flightcontroller.FlightControllerState;
@@ -61,6 +73,7 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_DO_SET_MODE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_DO_SET_SERVO;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_GET_HOME_POSITION;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_MISSION_START;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_OVERRIDE_GOTO;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_LAND;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_LOITER_UNLIM;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH;
@@ -70,20 +83,33 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE;
import static com.MAVLink.enums.MAV_MISSION_TYPE.MAV_MISSION_TYPE_MISSION;
import static com.MAVLink.common.msg_file_transfer_protocol.MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL;
import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_HOLD_AT_SPECIFIED_POSITION;
import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_DO_CONTINUE;
import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_HOLD_AT_CURRENT_POSITION;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_DISTANCE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MAX_ALTITUDE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MAX_SPEED;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MIN_ALTITUDE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MIN_SPEED;
import static com.MAVLink.enums.MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_FTP;
import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG;
import static sq.rogue.rosettadrone.util.safeSleep;
public class MAVLinkReceiver {
public int AIactivityPort = 7001;
public String AIactivityIP = "127.0.0.1";
public boolean AIenabled = false;
public boolean AIstat = false;
private static final int MAV_MISSION_ACCEPTED = 0;
private final String TAG = this.getClass().getSimpleName();
private float m_autoFlightSpeed = 2.0f;
private float m_maxFlightSpeed = 5.0f;
private float m_maxFlightSpeed = 12.0f;
private final int WP_STATE_INACTIVE = 0;
private final int WP_STATE_REQ_COUNT = 1;
@@ -92,6 +118,7 @@ public class MAVLinkReceiver {
public boolean curvedFlightPath = true;
public float flightPathRadius = .2f;
DroneModel mModel;
FTPManager ftpManager;
private long mTimeStampLastGCSHeartbeat = 0;
private int mNumGCSWaypoints = 0;
private int wpState = 0;
@@ -100,6 +127,9 @@ public class MAVLinkReceiver {
private ArrayList<msg_mission_item_int> mMissionItemList;
private boolean isHome = true;
// private ArrayList<String> aiWP = new ArrayList<String>();
private String[] aiWP = new String[100]; // Max 100 wp in an AI mission for now...
public MAVLinkReceiver(MainActivity parent, DroneModel model) {
this.parent = parent;
@@ -111,7 +141,7 @@ public class MAVLinkReceiver {
// IS 0 is hart beat...
if (msg.msgid != 0) {
Log.d(TAG, msg.toString());
Log.d(TAG, "Message: " + msg);
// Log.d(TAG, "Message: " + msg);
// Log.d(TAG, String.valueOf(msg));
// Log.d(TAG, String.valueOf(msg.msgid));
}
@@ -181,11 +211,57 @@ public class MAVLinkReceiver {
break;
case MAV_CMD_DO_DIGICAM_CONTROL:
// DEPRECATED but still used by QGC
// mModel.do_set_Gimbal(9, (float)((-30.0*5.5)+1500.0)); // TODO:: Default point somewhat down... To be removed when Camera setting in mission works....
mModel.takePhoto();
break;
case MAV_CMD_MISSION_START:
mModel.startWaypointMission();
break;
case MAV_CMD_OVERRIDE_GOTO:
parent.logMessageDJI("Received MAV: MAV_CMD_OVERRIDE_GOTO");
if (mModel.getSystemId() != msg_cmd.target_system) {
break;
}
mModel.pauseWaypointMission();
if (msg_cmd.param2 == MAV_GOTO_HOLD_AT_CURRENT_POSITION) {
int x = (int) mModel.get_current_lat();
int y = (int) mModel.get_current_lon();
int z = (int) mModel.get_current_alt();
Log.d(TAG, "Lat = " + x);
Log.d(TAG, "Lon = " + y);
Log.d(TAG, "ALT = " + z);
mModel.do_set_motion_absolute(
(double) x,
(double) y,
z,
msg_cmd.param4,
0,
0,
0,
0,
0b0000111111111000);
} else if (msg_cmd.param2 == MAV_GOTO_HOLD_AT_SPECIFIED_POSITION) {
Log.d(TAG, "Lat = " + msg_cmd.param5);
Log.d(TAG, "Lon = " + msg_cmd.param6);
Log.d(TAG, "ALT = " + msg_cmd.param7);
mModel.do_set_motion_absolute(
(double) msg_cmd.param5,
(double) msg_cmd.param6,
msg_cmd.param7,
msg_cmd.param4,
0,
0,
0,
0,
0b0000111111111000);
}
if(msg_cmd.param1 == MAV_GOTO_DO_CONTINUE) {
mModel.resumeWaypointMission();
}
mModel.send_command_ack(MAV_CMD_OVERRIDE_GOTO, MAV_RESULT.MAV_RESULT_ACCEPTED);
break;
case MAV_CMD_CONDITION_YAW:
Log.d(TAG, "Yaw = " + msg_cmd.param1);
@@ -209,6 +285,11 @@ public class MAVLinkReceiver {
case MAV_CMD_DO_SET_SERVO:
mModel.do_set_Gimbal(msg_cmd.param1, msg_cmd.param2);
break;
// CUSTOM
case MAV_PROTOCOL_CAPABILITY_FTP:
parent.logMessageDJI("Received MAV: MAV_PROTOCOL_CAPABILITY_FTP");
mModel.send_command_ack(MAV_PROTOCOL_CAPABILITY_FTP, MAV_RESULT.MAV_RESULT_ACCEPTED);
break;
// JUMP is just a test function to enter the Timeline...
case MAV_CMD_DO_JUMP:
Log.d(TAG, "Start Timeline...");
@@ -409,6 +490,8 @@ public class MAVLinkReceiver {
// We are done fetching a complete mission from the GCS...
if (msg_item.seq == mNumGCSWaypoints - 1) {
// mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_ACCEPTED);
Log.d(TAG, "Mission Loaded from host...!");
wpState = WP_STATE_INACTIVE;
finalizeNewMission();
} else {
@@ -491,6 +574,14 @@ public class MAVLinkReceiver {
ym.getWaypointList().clear();
}
break;
case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
msg_file_transfer_protocol msg_ftp_item = (msg_file_transfer_protocol) msg;
ftpManager.manage_ftp(msg_ftp_item);
break;
default:
parent.logMessageDJI("Received (unkown) message");
parent.logMessageDJI( String.valueOf(msg.msgid));
break;
}
}
@@ -547,158 +638,250 @@ public class MAVLinkReceiver {
Log.d(TAG, "==============================");
boolean stopUpload = false;
boolean takeoff_received = false;
int errorCode = 0;
float base_alt = 0; // The DJI Drone got bad absolute Altitude, so we calibrate it with what the mission planner expects.
boolean triggerDistanceEnabled = false;
float triggerDistance = 0;
waypoint_loop:
// If we got our position...
// if(mModel.get_current_lat()+mModel.get_current_lon()+mModel.get_current_alt() > 0) {
// currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), mModel.get_current_alt());
// }
for (msg_mission_item_int m : this.mMissionItemList) {
Log.d(TAG, "Command: " +String.valueOf(m));
if (AIstat == true)
{
// aiWP = "";
int num = 0;
switch (m.command) {
for (msg_mission_item_int m : this.mMissionItemList) {
Log.d(TAG, "AI Command: " + String.valueOf(m));
case MAV_CMD.MAV_CMD_NAV_TAKEOFF:
Log.d(TAG, "Takeoff...");
switch (m.command) {
case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
// if we got an item (Start item) already we got a position, now we just add altitude.
if(currentWP != null){
currentWP.altitude = m.z;
}else{
if(m.x == 0 || m.y == 0) {
currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), m.z);
}else {
currentWP = new Waypoint(m.x,m.z, m.z);
}
}
dji_wps.add(currentWP);
break;
if(num ==0)
mModel.mission_alt = m.z;
case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
Log.d(TAG, "Waypoint: " + m.x/10000000.0 + ", " + m.y/10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
// If this is a start item let's store the position ...
if(m.frame != 3 || Float.isNaN(m.z) ) {
currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), 0);
aiWP[num++] = "WP," +
String.valueOf(num) + "," +
String.valueOf(m.x / 10000000.0) + "," +
String.valueOf(m.y / 10000000.0) + "," +
String.valueOf(m.z-mModel.mission_alt) + "";
break;
}
if ((m.z) > 500) { // TODO: Shuld reqest max altitude not assume 500...
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_ALTITUDE, null, null));
stopUpload = true;
mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
break waypoint_loop;
} else if ((m.z) < -200) { // TODO: hmm so we can not take off from a mountain and fly down?? ...
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_ALTITUDE, null, null));
stopUpload = true;
mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
break waypoint_loop;
}
currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
// If delay at wp...
if (m.param1 > 0)
currentWP.addAction(new WaypointAction(WaypointActionType.STAY, (int) m.param1 * 1000)); // milliseconds...
// If rotate at wp...
if (m.param2 > 0)
currentWP.addAction(new WaypointAction(WaypointActionType.ROTATE_AIRCRAFT, (int) (m.param2 * 180.0 / 3.141592))); // +-180 deg...
// This is set in the RosettaDrone2 settings...
if (curvedFlightPath) {
currentWP.cornerRadiusInMeters = flightPathRadius;
}
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_DO_CHANGE_SPEED:
Log.d(TAG, "Change Speed: " + m.x/10000000.0 + ", " + m.y/10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
if (m.param2 < -15) {
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_SPEED,
null, null));
stopUpload = true;
break waypoint_loop;
}
else if (m.param2 > 15) {
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_SPEED,
null, null));
stopUpload = true;
break waypoint_loop;
}
else {
mBuilder.autoFlightSpeed(m.param2);
}
break;
case MAV_CMD.MAV_CMD_DO_MOUNT_CONTROL:
Log.d(TAG, "Set gimbal pitch: " + m.param1);
currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
currentWP.addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, (int) m.param1));
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_IMAGE_START_CAPTURE:
Log.d(TAG, "MAV_CMD_IMAGE_START_CAPTURE");
currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
currentWP.addAction(new WaypointAction(WaypointActionType.START_TAKE_PHOTO, 0));
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_DO_SET_CAM_TRIGG_DIST:
Log.d(TAG, "MAV_CMD_DO_SET_CAM_TRIGG_DIST");
if (!triggerDistanceEnabled) {
if (m.param1 != 0) {
triggerDistanceEnabled = true;
triggerDistance = m.param1;
} else {
triggerDistance = 0;
}
}
break;
case MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH:
Log.d(TAG, "MAV_CMD_NAV_RETURN_TO_LAUNCH");
mBuilder.finishedAction(WaypointMissionFinishedAction.GO_HOME);
break;
case MAV_CMD.MAV_CMD_NAV_DELAY:
Log.d(TAG, "MAV_CMD_NAV_DELAY");
break;
case MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE:
Log.d(TAG, "MAV_CMD_VIDEO_START_CAPTURE");
break;
case MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE:
Log.d(TAG, "MAV_CMD_VIDEO_STOP_CAPTURE");
break;
case MAV_CMD.MAV_CMD_CONDITION_YAW:
Log.d(TAG, "MAV_CMD_CONDITION_YAW");
break;
case MAV_CMD.MAV_CMD_DO_DIGICAM_CONTROL:
Log.d(TAG, "MAV_CMD_DO_DIGICAM_CONTROL");
break;
case MAV_CMD.MAV_CMD_SET_CAMERA_ZOOM:
Log.d(TAG, "MAV_CMD_SET_CAMERA_ZOOM");
break;
case MAV_CMD.MAV_CMD_SET_CAMERA_FOCUS:
Log.d(TAG, "MAV_CMD_SET_CAMERA_FOCUS");
break;
}
}
// Send mission to the AIactivityModule
// Log.d(TAG, aiWP);
if(num > 0){
aiWP[num++] = "WP,9999,0,0,0"; // End of mission...
try {
InetAddress address = InetAddress.getByName(AIactivityIP);
try {
DatagramSocket mSocketUDP = new DatagramSocket();
for(int x=0; x < num; x++){
byte[] byteArrray = aiWP[x].getBytes();
DatagramPacket p = new DatagramPacket(byteArrray, byteArrray.length, address, AIactivityPort);
try {
mSocketUDP.send(p);
// mSocketUDP.close(); // It this needed / wanted...
} catch (IOException e) {
Log.e(TAG, "Error sending AI datagram socket", e);
}
aiWP[x]="";
}
} catch (SocketException e) {
Log.e(TAG, "Error creating AI datagram socket", e);
}
} catch (UnknownHostException e) {
Log.e(TAG, "Error setting address to AI datagram socket", e);
}
}
stopUpload = true;
NotificationHandler.notifySnackbar(parent.findViewById(R.id.snack),R.string.ai_uploaded_active, LENGTH_LONG);
} else {
waypoint_loop:
for (msg_mission_item_int m : this.mMissionItemList) {
Log.d(TAG, "Command: " + String.valueOf(m));
switch (m.command) {
case MAV_CMD.MAV_CMD_NAV_TAKEOFF:
Log.d(TAG, "Takeoff...");
// if we got an item (Start item) already we got a position, now we just add altitude.
if (currentWP != null) {
currentWP.altitude = m.z;
} else {
if (m.x == 0 || m.y == 0) {
currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), m.z);
} else {
currentWP = new Waypoint(m.x, m.z, m.z);
}
}
Log.d(TAG, "Takeoff at " + currentWP);
dji_wps.add(currentWP);
takeoff_received = true;
break;
case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
Log.d(TAG, "Waypoint: " + m.x / 10000000.0 + ", " + m.y / 10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
// We only support some frma models... 0 = Global, 3 = Lat,Lon,HomeAlt relative
if (m.frame != 0 && m.frame != 3) {
Log.d(TAG, "Waypoint frame error! " + m.frame);
break;
}
// If this is a start item let's store the altitude and make a base as the drone's altitude is quite bad ...
if (takeoff_received == false && m.command == 16 && m.seq == 0) {
Log.d(TAG, "Waypoint error 1");
// if(mModel.get_current_alt() > 0.5 && m.z > 0.5) {
// base_alt = mModel.get_current_alt() - m.z;
// }
currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z);
base_alt = m.z;
mModel.mission_alt = m.z;
break;
}
// If this is a start item let's store the position ...
if (m.frame == 0) {
Log.d(TAG, "Waypoint rescaled..." + m.z + " " + base_alt); //mModel.home_position.altitude);
m.z = m.z - base_alt;
// m.z = (m.z - mModel.home_position.altitude)-base_alt;
Log.d(TAG, "= " + m.z);
}
if ((m.z) > 500) { // TODO: Shuld reqest max altitude not assume 500...
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_ALTITUDE, null, null));
stopUpload = true;
mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
Log.d(TAG, "Waypoint error 2");
break; // waypoint_loop;
} else if ((m.z) < -200) { // TODO: hmm so we can not take off from a mountain and fly down?? ...
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_ALTITUDE, null, null));
stopUpload = true;
mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
Log.d(TAG, "Waypoint error 3");
break; // waypoint_loop;
}
currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z); // TODO check altitude conversion
// If delay at wp...
if (m.param1 > 0)
currentWP.addAction(new WaypointAction(WaypointActionType.STAY, (int) m.param1 * 1000)); // milliseconds...
// If rotate at wp...
if (m.param2 > 0)
currentWP.addAction(new WaypointAction(WaypointActionType.ROTATE_AIRCRAFT, (int) (m.param2 * 180.0 / 3.141592))); // +-180 deg...
// This is set in the RosettaDrone2 settings...
if (curvedFlightPath) {
currentWP.cornerRadiusInMeters = flightPathRadius;
}
Log.d(TAG, "Waypoint OK");
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_DO_CHANGE_SPEED:
Log.d(TAG, "Change Speed: " + m.x / 10000000.0 + ", " + m.y / 10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
if (m.param2 < -15) {
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_SPEED,
null, null));
stopUpload = true;
break; // waypoint_loop;
} else if (m.param2 > 15) {
parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_SPEED,
null, null));
stopUpload = true;
break; // waypoint_loop;
} else {
mBuilder.autoFlightSpeed(m.param2);
}
break;
case MAV_CMD.MAV_CMD_DO_MOUNT_CONTROL:
Log.d(TAG, "Set gimbal pitch: " + m.param1);
// currentWP = new Waypoint(mModel.get_current_lat(),mModel.get_current_lon(),mModel.get_current_alt()); // TODO check altitude conversion
currentWP = dji_wps.get(dji_wps.size() - 1);
dji_wps.remove(dji_wps.size() - 1);
currentWP.addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, (int) m.param1));
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_IMAGE_START_CAPTURE:
Log.d(TAG, "MAV_CMD_IMAGE_START_CAPTURE");
currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z); // TODO check altitude conversion
currentWP.addAction(new WaypointAction(WaypointActionType.START_TAKE_PHOTO, 0));
dji_wps.add(currentWP);
break;
case MAV_CMD.MAV_CMD_DO_SET_CAM_TRIGG_DIST:
Log.d(TAG, "MAV_CMD_DO_SET_CAM_TRIGG_DIST");
if (!triggerDistanceEnabled) {
if (m.param1 != 0) {
triggerDistanceEnabled = true;
triggerDistance = m.param1;
} else {
triggerDistance = 0;
}
}
break;
case MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH:
Log.d(TAG, "MAV_CMD_NAV_RETURN_TO_LAUNCH");
mBuilder.finishedAction(WaypointMissionFinishedAction.GO_HOME);
break;
case MAV_CMD.MAV_CMD_NAV_DELAY:
Log.d(TAG, "MAV_CMD_NAV_DELAY");
break;
case MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE:
Log.d(TAG, "MAV_CMD_VIDEO_START_CAPTURE");
break;
case MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE:
Log.d(TAG, "MAV_CMD_VIDEO_STOP_CAPTURE");
break;
case MAV_CMD.MAV_CMD_CONDITION_YAW:
Log.d(TAG, "MAV_CMD_CONDITION_YAW");
break;
case MAV_CMD.MAV_CMD_DO_DIGICAM_CONTROL:
Log.d(TAG, "MAV_CMD_DO_DIGICAM_CONTROL");
break;
case MAV_CMD.MAV_CMD_SET_CAMERA_ZOOM:
Log.d(TAG, "MAV_CMD_SET_CAMERA_ZOOM");
break;
case MAV_CMD.MAV_CMD_SET_CAMERA_FOCUS:
Log.d(TAG, "MAV_CMD_SET_CAMERA_FOCUS");
break;
}
}
}
if (stopUpload) {
Log.d(TAG, "Waypoint upload aborted due to invalid parameters");
Log.d(TAG, "Waypoint upload aborted due to invalid parameters or AI mode");
mModel.send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
mModel.send_text("eSmart AI Mission uploaded !");
} else {
Log.d(TAG, "Speed for mission will be " + mBuilder.getAutoFlightSpeed() + " m/s");
Log.d(TAG, "==============================");
@@ -721,13 +904,13 @@ public class MAVLinkReceiver {
logWaypointstoRD(correctedWps);
Log.d(TAG, "WP size " + correctedWps.size());
safeSleep(200);
// safeSleep(200);
mBuilder.waypointList(correctedWps).waypointCount(correctedWps.size());
safeSleep(200);
// safeSleep(200);
WaypointMission builtMission = mBuilder.build();
safeSleep(200);
// safeSleep(200);
mModel.setWaypointMission(builtMission);
safeSleep(200);
// safeSleep(200);
}
mMissionItemList=null; // Flush the mission list...
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -7,6 +7,9 @@ import com.secneo.sdk.Helper;
import dji.sdk.base.BaseProduct;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.camera.Camera;
import dji.sdk.products.Aircraft;
import dji.sdk.products.HandHeld;
public class RDApplication extends Application {
@@ -59,6 +62,17 @@ public class RDApplication extends Application {
simulatorApplication.setContext(this);
}
}
public static synchronized Camera getCameraInstance() {
if (getProductInstance() == null) return null;
Camera camera = null;
if (getProductInstance() instanceof Aircraft){
camera = ((Aircraft) getProductInstance()).getCamera();
} else if (getProductInstance() instanceof HandHeld) {
camera = ((HandHeld) getProductInstance()).getCamera();
}
return camera;
}
}
@@ -0,0 +1,113 @@
// Does not work...
package sq.rogue.rosettadrone.settings;
//import android.se.omapi.Session;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.PasswordAuthentication;
import java.security.Security;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class GMailSender extends javax.mail.Authenticator {
private String mailhost = "smtp.gmail.com";
private String user;
private String password;
private Session session;
static {
java.security.Security.addProvider(new JSSEProvider());
}
public GMailSender(String user, String password) {
this.user = user;
this.password = password;
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.host", mailhost);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.quitwait", "false");
session = Session.getDefaultInstance(props, this);
}
protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
return new javax.mail.PasswordAuthentication(user, password);
}
public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {
try{
MimeMessage message = new MimeMessage(session);
DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));
message.setSender(new InternetAddress(sender));
message.setSubject(subject);
message.setDataHandler(handler);
if (recipients.indexOf(',') > 0)
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));
else
message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));
Transport.send(message);
} catch (Exception e) {
e.printStackTrace();
Log.d("TEST","Error: "+ e.toString());
}
}
public class ByteArrayDataSource implements DataSource {
private byte[] data;
private String type;
public ByteArrayDataSource(byte[] data, String type) {
super();
this.data = data;
this.type = type;
}
public ByteArrayDataSource(byte[] data) {
super();
this.data = data;
}
public void setType(String type) {
this.type = type;
}
public String getContentType() {
if (type == null)
return "application/octet-stream";
else
return type;
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
public String getName() {
return "ByteArrayDataSource";
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("Not Supported");
}
}
}
@@ -0,0 +1,46 @@
package sq.rogue.rosettadrone.settings;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Alexander Y. Kleymenov
* @version $Revision$
*/
import java.security.AccessController;
import java.security.Provider;
public final class JSSEProvider extends Provider {
public JSSEProvider() {
super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {
public Void run() {
put("SSLContext.TLS",
"org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
put("Alg.Alias.SSLContext.TLSv1", "TLS");
put("KeyManagerFactory.X509",
"org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
put("TrustManagerFactory.X509",
"org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
return null;
}
});
}
}
@@ -0,0 +1,115 @@
package sq.rogue.rosettadrone.settings;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.mail.Session;
import dji.sdk.media.MediaFile;
import sq.rogue.rosettadrone.MainActivity;
//public class MailReport extends AsyncTask<Void,Void,Void> { // extends AppCompatActivity {
public class MailReport extends javax.mail.Authenticator{
private final String TAG = MailReport.class.getSimpleName();
private MainActivity parent;
private ContentResolver contentResolver;
private File imageDirectory;
//---------------------------------------------------------------------------------------
public MailReport(MainActivity parent, ContentResolver resolver)
{
this.parent = parent;
contentResolver = resolver;
}
//---------------------------------------------------------------------------------------
private ArrayList<Uri> getUriListForImages(String filename) throws Exception
{
ArrayList<Uri> myList = new ArrayList<Uri>();
String[] fileList = imageDirectory.list();
if(fileList.length != 0) {
Log.d(TAG,"FileLength: "+fileList.length);
for(int i=0; i<fileList.length; i++)
{
int err = filename.compareTo(fileList[i]);
Log.d(TAG,"Files: "+filename+" -> "+fileList[i]+" err: "+err);
if(err==0) {
Log.d(TAG,"Found... ");
try {
ContentValues values = new ContentValues(7);
values.put(MediaStore.Images.Media.TITLE, fileList[i]);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileList[i]);
values.put(MediaStore.Images.Media.DATE_TAKEN, new Date().getTime());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.ImageColumns.BUCKET_ID, imageDirectory.getAbsolutePath().hashCode());
values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, fileList[i]);
values.put("_data", imageDirectory.getAbsolutePath() + "/" + fileList[i]);
Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
myList.add(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Log.d(TAG,"Uri: "+myList);
return myList;
}
//---------------------------------------------------------------------------------------
public Intent createEmail(List<String> to, String subject, String message, double lat, double lon, double alt, double head, String filename, String dir)
{
imageDirectory = new File(dir);
message+="\nAt position Lat: "+lat+" Lon: "+lon+" Alt: "+alt + " Heading: "+head;
//message+="\nURL: https://www.google.com/maps/search/?api=1&query="+lat+","+lon+"\n";
message+="\nURL: http://maps.google.com/maps?z=1&t=h&q=loc:"+lat+","+lon+"\n";
Intent email = new Intent(Intent.ACTION_SEND_MULTIPLE);
email.setData(Uri.parse("mailto:")); // only email apps should handle this
String to_address = "";
for(String address:to){
if(address.length() > 3) {
to_address=to_address+", "+address;
}
}
Log.d(TAG, "Mail to use: " + to_address);
email.putExtra(Intent.EXTRA_EMAIL, new String[]{to_address});
email.putExtra(Intent.EXTRA_SUBJECT, subject);
email.putExtra(Intent.EXTRA_TEXT, message);
try {
Log.d(TAG,"File: "+ filename);
ArrayList<Uri> uriList = getUriListForImages(filename);
email.putExtra(Intent.EXTRA_STREAM, uriList);
} catch (Exception e){
Log.d(TAG,"Missing file: "+ e.toString());
}
//need this to prompts email client only
email.setType("message/rfc822");
// email.setType("text/html");
Log.d(TAG,"Email: "+ email);
return email;
}
//---------------------------------------------------------------------------------------------
}
@@ -56,27 +56,19 @@ import sq.rogue.rosettadrone.DJISimulatorApplication;
import sq.rogue.rosettadrone.R;
import sq.rogue.rosettadrone.RDApplication;
//public class HelpActivity extends AppCompatActivity implements OnMapReadyCallback{
//public class HelpActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback, OnMapReadyCallback{
public class MapActivity extends FragmentActivity implements View.OnClickListener, GoogleMap.OnMapClickListener, OnMapReadyCallback {
private static final String TAG = MapActivity.class.getSimpleName();
private GoogleMap gMap;
private Button locate, add, clear, exit;
private Button config, upload, start, stop;
private boolean isAdd = false;
private double droneLocationLat = 30, droneLocationLng = 60;
private final Map<Integer, Marker> mMarkers = new ConcurrentHashMap<Integer, Marker>();
private Marker droneMarker = null;
private float altitude = 100.0f;
private float mSpeed = 10.0f;
private List<Waypoint> waypointList = new ArrayList<>();
public static WaypointMission.Builder waypointMissionBuilder;
private FlightController mFlightController;
private WaypointMissionOperator instance;
@@ -96,8 +88,8 @@ public class MapActivity extends FragmentActivity implements View.OnClickListene
@Override
protected void onDestroy() {
if (mReceiver.getResultCode() < 0)
unregisterReceiver(mReceiver);
// if (mReceiver.getResultCode() < 0)
// unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
@@ -196,7 +196,43 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
}
});
findPreference("pref_sim_pos_lat").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
findPreference("pref_email_name1").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_APP_REPORT_EMAIL = true;
return true;
}
});
findPreference("pref_email_name2").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_APP_REPORT_EMAIL = true;
return true;
}
});
findPreference("pref_email_name3").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_APP_REPORT_EMAIL = true;
return true;
}
});
findPreference("pref_email_name4").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_APP_REPORT_EMAIL = true;
return true;
}
});
findPreference("pref_external_video").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
@@ -212,11 +248,28 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
return false;
}
});
findPreference("pref_sim_pos_lat").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
if (Double.parseDouble((String) newValue) >= -90 && Double.parseDouble((String) newValue) <= 90) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_VIDEO_ADDRESS_CHANGED = true;
return true;
}
} catch (NumberFormatException ignored) {
}
NotificationHandler.notifyAlert(SettingsFragment.this.getActivity(), TYPE_APP_NAME,
null, null);
return false;
}
});
findPreference("pref_sim_pos_lon").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
if (Integer.parseInt((String) newValue) >= 1 && Integer.parseInt((String) newValue) <= 65535) {
if (Double.parseDouble((String) newValue) >= 0 && Double.parseDouble((String) newValue) <= 180) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_VIDEO_ADDRESS_CHANGED = true;
return true;
@@ -97,7 +97,7 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
@Override
protected void onDestroy() {
unregisterReceiver(mReceiver);
// unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
@@ -161,9 +161,9 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
setContentView(R.layout.activity_waypoint1);
IntentFilter filter = new IntentFilter();
filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
registerReceiver(mReceiver, filter);
// IntentFilter filter = new IntentFilter();
// filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
// registerReceiver(mReceiver, filter);
initUI();
@@ -311,7 +311,7 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
//Create MarkerOptions object
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_drone));
runOnUiThread(new Runnable() {
@Override
@@ -116,7 +116,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
@Override
protected void onDestroy() {
unregisterReceiver(mReceiver);
// unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
@@ -198,9 +198,9 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
setContentView(R.layout.activity_waypoint2);
IntentFilter filter = new IntentFilter();
filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
registerReceiver(mReceiver, filter);
// IntentFilter filter = new IntentFilter();
// filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
// registerReceiver(mReceiver, filter);
if (getWaypointMissionOperator() == null) {
setResultToToast("Not support Waypoint2.0");
@@ -426,7 +426,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
//Create MarkerOptions object
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_drone));
runOnUiThread(new Runnable() {
@Override
@@ -718,7 +718,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
.setMissionID(new Random().nextInt(65535))
.setFinishedAction(mFinishedAction)
.setGotoFirstWaypointMode(firstMode)
.setMaxFlightSpeed(mSpeed)
.setMaxFlightSpeed((float)12.0) //mSpeed)
.setAutoFlightSpeed(mSpeed);
getWaypointMissionOperator().loadMission(waypointMissionBuilder.build(), new CommonCallbacks.CompletionCallback<DJIWaypointV2Error>() {
@@ -82,6 +82,7 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
setListeners();
}
@@ -152,10 +153,44 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
return false;
}
});
findPreference("pref_drone_multi_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
findPreference("pref_ai_port").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
if (Integer.parseInt((String) newValue) >= 1000 && Integer.parseInt((String) newValue) <= 32000) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_AI_PORT_CHANGED = true;
return true;
}
} catch (NumberFormatException ignored) {
}
return false;
}
});
findPreference("pref_ai_telemetry_enabled").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_AI_ENABLED_CHANGED = true;
return true;
}
});
findPreference("pref_ai_ip").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_AI_IP_CHANGED = true;
return true;
}
});
findPreference("pref_drone_multi_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_MULTI_MODE_CHANGED = true;
return true;
@@ -163,8 +198,8 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
});
findPreference("pref_drone_leds").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
public boolean onPreferenceChange(Preference preference, Object newValue)
{
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_LEDS_CHANGED = true;
return true;
@@ -183,7 +218,6 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
findPreference("pref_drone_flight_path_radius").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
if (Float.parseFloat((String) newValue) >= .2f && Float.parseFloat((String) newValue) <= 1000) {
MainActivity.FLAG_PREFS_CHANGED = true;
@@ -204,8 +204,10 @@ public class RtpSocket implements Runnable {
if (dport != 0 && rtcpPort != 0) {
if(dest.isMulticastAddress()) {
mTransport = TRANSPORT_MULTICAST;
Log.d(TAG, "Use Multicast...");
}else {
mTransport = TRANSPORT_UDP;
Log.d(TAG, "Use Unicast...");
}
mPort = dport;
Log.d(TAG, "setDestination: " + dest + ":" + dport);
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 9.3 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 22 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 41 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 38 KiB

+2 -1
Ver Arquivo
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+60 -9
Ver Arquivo
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
@@ -231,10 +231,12 @@
android:orientation="horizontal">
<dji.ux.widget.PreFlightStatusWidget
android:layout_weight="1"
android:layout_width="180dp"
android:layout_height="25dp" />
<ImageView
android:layout_weight="1"
android:layout_marginTop="3dp"
android:id="@+id/dji_conn"
android:layout_width="18dp"
@@ -243,6 +245,7 @@
app:layout_collapseMode="parallax" />
<TextView
android:layout_weight="1"
android:layout_marginTop="3dp"
android:layout_width="35dp"
android:layout_height="20dp"
@@ -250,6 +253,7 @@
android:textColor="@color/white" />
<ImageView
android:layout_weight="1"
android:layout_marginTop="3dp"
android:id="@+id/gcs_conn"
android:layout_width="18dp"
@@ -258,6 +262,7 @@
app:layout_collapseMode="parallax" />
<TextView
android:layout_weight="1"
android:layout_marginTop="3dp"
android:layout_width="35dp"
android:layout_height="20dp"
@@ -265,41 +270,50 @@
android:textColor="@color/white" />
<dji.ux.widget.FlightModeWidget
android:layout_weight="1"
android:layout_marginTop="2dp"
android:layout_width="103dp"
android:layout_height="22dp" />
<dji.ux.widget.GPSSignalWidget
android:layout_weight="1"
android:layout_width="44dp"
android:layout_height="22dp" />
<dji.ux.widget.VisionWidget
android:layout_weight="1"
android:layout_width="22dp"
android:layout_height="22dp" />
<dji.ux.widget.RemoteControlSignalWidget
android:layout_weight="1"
android:layout_width="38dp"
android:layout_height="22dp" />
<dji.ux.widget.VideoSignalWidget
android:layout_weight="1"
android:layout_width="38dp"
android:layout_height="22dp" />
<dji.ux.widget.WiFiSignalWidget
android:layout_weight="1"
android:layout_width="25dp"
android:layout_height="20dp" />
<dji.ux.widget.BatteryWidget
android:layout_weight="1"
android:layout_width="96dp"
android:layout_height="22dp"
custom:excludeView="singleVoltage" />
<dji.ux.widget.ConnectionWidget
android:layout_weight="1"
android:layout_marginTop="3dp"
android:layout_width="28dp"
android:layout_height="18dp" />
<Button
android:layout_weight="1"
android:id="@+id/btn_safety"
android:layout_marginTop="2dp"
android:layout_width="25dp"
@@ -307,12 +321,14 @@
android:background="@drawable/ic_lock_outline_secondary_24dp" />
<TextView
android:layout_weight="1"
android:layout_marginTop="3dp"
android:layout_width="25dp"
android:layout_height="20dp"
android:text="" />
<Button
android:layout_weight="1"
android:id="@+id/btn_config"
android:layout_width="25dp"
android:layout_height="20dp"
@@ -375,24 +391,48 @@
<!--Take off and return home buttons on left -->
<LinearLayout
android:layout_width="40dp"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="12dp"
android:layout_marginTop="80dp"
android:layout_marginTop="60dp"
android:orientation="vertical">
<Button
android:id="@+id/btn_takeoff"
android:layout_width="46dp"
android:layout_height="40dp"
android:layout_marginTop="12dp"
android:background="@drawable/quadcopter_start"
android:alpha="0.75"
android:text=""
android:textColor="@color/colorWhite"
android:textSize="12sp" />
<Button
android:id="@+id/btn_rth"
android:layout_width="46dp"
android:layout_height="40dp"
android:layout_marginTop="12dp"
android:background="@drawable/return_home"
android:alpha="0.75"
android:text=""
android:textColor="@color/colorWhite"
android:textSize="12sp" />
<!--
<dji.ux.widget.TakeOffWidget
android:id="@+id/Takeoff"
android:visibility="invisible"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="12dp" />
<dji.ux.widget.ReturnHomeWidget
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="12dp" />
-->
<Button
android:id="@+id/btn_AI_start"
@@ -400,17 +440,28 @@
android:layout_height="40dp"
android:layout_marginTop="12dp"
android:background="@mipmap/track_right"
android:alpha="0.50"
android:alpha="0.75"
android:text=""
android:textColor="@color/colorWhite"
android:textSize="12sp" />
</LinearLayout>
<Button
android:id="@+id/btn_Report"
android:layout_width="46dp"
android:layout_height="40dp"
android:layout_marginTop="12dp"
android:background="@mipmap/track_report"
android:alpha="0.75"
android:text=""
android:textColor="@color/colorWhite"
android:textSize="12sp" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
+7
Ver Arquivo
@@ -17,6 +17,13 @@
android:onClick="onMenuItemClick"
android:title="@string/settings" />
<item
android:id="@+id/action_AI"
android:orderInCategory="502"
app:showAsAction="never"
android:onClick="onMenuItemClick"
android:title="@string/AIsettings" />
<item
android:id="@+id/action_waypoint1"
android:orderInCategory="503"
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 27 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 36 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 29 KiB

+13
Ver Arquivo
@@ -3,6 +3,10 @@
<!-- Drone -->
<string name="pref_drone_id">ID</string>
<string name="pref_email1">Email Address 1</string>
<string name="pref_email2">Email Address 2</string>
<string name="pref_email3">Email Address 3</string>
<string name="pref_email4">Email Address 4</string>
<!-- Drone RTL -->
<string name="pref_drone_rtl_cat">RTL/RTH</string>
@@ -83,7 +87,10 @@
<!-- Network -->
<string name="pref_gcs_ip">GCS IP address</string>
<string name="pref_ai_ip">AI Assist IP address</string>
<string name="pref_telem_port">Telemetry port</string>
<string name="pref_ai_port">AI Assist port</string>
<string name="pref_ai_telemetry">Enable AI Assist</string>
<string name="pref_secondary_telemetry">Enable secondary telemetry output</string>
<string name="pref_secondary_telemetry_ip">Secondary telemetry IP</string>
<string name="pref_secondary_telemetry_port">Secondary telemetry port</string>
@@ -134,14 +141,20 @@
<string name="safety_on">SAFETY ENABLED</string>
<string name="safety_off">SAFETY DISABLED - READY TO FLY</string>
<string name="safety_launch">Disable safety to takeoff.</string>
<string name="hold">Please wait while image is grabbed!</string>
<string name="ai_not_active">AI not activated in setup menu!</string>
<string name="ai_uploaded_active">AI mission sent to AI module!</string>
<string name="release">Report Sent!</string>
<!-- Preference Menus -->
<string name="settings">Settings</string>
<string name="AIsettings">AI Settings</string>
<string name="drone_settings">Drone Configuration</string>
<!-- Preference Groupings -->
<string name="telemetry_settings">Telemetry</string>
<string name="ai_settings">AI Assist</string>
<string name="video_settings">Video</string>
<string name="log_settings">Logs</string>
+67 -8
Ver Arquivo
@@ -13,6 +13,38 @@
android:key="pref_drone_id"
android:title="@string/pref_drone_id" />
<PreferenceCategory
android:key="drone_name_prefs"
android:title="App Name">
<androidx.preference.EditTextPreference
android:defaultValue="RosettaDrone 2"
android:key="pref_app_name"
android:title="@string/pref_app" />
</PreferenceCategory>
<PreferenceCategory
android:title="Report Email[s]:">
<androidx.preference.EditTextPreference
android:defaultValue=" "
android:key="pref_email_name1"
android:title="@string/pref_email1" />
<androidx.preference.EditTextPreference
android:defaultValue=" "
android:key="pref_email_name2"
android:title="@string/pref_email2" />
android:title="Report Email 3:">
<androidx.preference.EditTextPreference
android:defaultValue=" "
android:key="pref_email_name3"
android:title="@string/pref_email3" />
android:title="Report Email 4:">
<androidx.preference.EditTextPreference
android:defaultValue=" "
android:key="pref_email_name4"
android:title="@string/pref_email4" />
</PreferenceCategory>
<PreferenceCategory
android:key="drone_rtl_prefs"
android:title="@string/pref_drone_rtl_cat">
@@ -42,16 +74,15 @@
android:defaultValue="2" />
</PreferenceCategory>
<PreferenceCategory
android:key="drone_name_prefs"
android:title="App Name">
android:title="Use External H.264 input">
<androidx.preference.EditTextPreference
android:defaultValue="RosettaDrone 2"
android:key="pref_app_name"
android:title="@string/pref_app" />
android:defaultValue="0"
android:key="pref_external_video"
android:title="Port Number" />
</PreferenceCategory>
<PreferenceCategory
android:key="drone_heading_prefs"
android:title="@string/pref_drone_heading_cat">
@@ -64,7 +95,7 @@
android:defaultValue="0"
android:enabled="false" />
</PreferenceCategory>
v
<PreferenceCategory
android:key="sim_pos_prefs"
android:title="Sim Settings">
@@ -137,6 +168,34 @@ v
android:defaultValue="true" />
</PreferenceCategory>
<PreferenceCategory
android:key="ai_prefs"
android:title="@string/ai_settings"
android:tooltipText="Will enable sending mission data back out to the AI module, rather than to the DJI drone.">
<androidx.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_ai_telemetry_enabled"
android:title="@string/pref_ai_telemetry" />
<androidx.preference.EditTextPreference
android:defaultValue="127.0.0.1"
android:dependency="pref_ai_telemetry_enabled"
android:digits="0123456789."
android:inputType="number"
android:key="pref_ai_ip"
android:title="@string/pref_ai_ip" />
<androidx.preference.EditTextPreference
android:dependency="pref_ai_telemetry_enabled"
android:defaultValue="2000"
android:key="pref_ai_port"
android:title="@string/pref_ai_port" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
<androidx.preference.SwitchPreferenceCompat
+3 -1
Ver Arquivo
@@ -1,4 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
ext {
ndkVersion = '22.0.7026061'
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
+2 -2
Ver Arquivo
@@ -1,6 +1,6 @@
#Thu Dec 10 10:55:57 CET 2020
#Mon Mar 08 18:34:57 CET 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip