From 607f1750ebaa39c30600115a41756d3d4421e34a Mon Sep 17 00:00:00 2001 From: Bastien Wermeille <5320541+Ph0tonic@users.noreply.github.com> Date: Fri, 22 May 2020 21:52:57 +0200 Subject: [PATCH 1/4] Implement Drone measures callback Co-authored-by: Dragoo417 <17319255+Dragoo417@users.noreply.github.com> --- app/src/main/java/ch/epfl/sdp/drone/Drone.kt | 35 ++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt index 4cf4bbe6b..f79ed8470 100644 --- a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt +++ b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt @@ -27,6 +27,8 @@ object Drone { private const val WAIT_TIME_S: Long = 1 + val onMeasureTakenCallbacks = mutableListOf<(LatLng, Double) -> Unit>() + private val disposables: MutableList = ArrayList() val positionLiveData: MutableLiveData = MutableLiveData() val batteryLevelLiveData: MutableLiveData = MutableLiveData() @@ -38,14 +40,14 @@ object Drone { val isConnectedLiveData: MutableLiveData = MutableLiveData(false) val isMissionPausedLiveData: MutableLiveData = MutableLiveData(true) - lateinit var getSignalStrength: () -> Double - /*Will be useful later on*/ val debugGetSignalStrength: () -> Double = { positionLiveData.value?.distanceTo(LatLng(47.3975, 8.5445)) ?: 0.0 } private val instance: System = DroneInstanceProvider.provide() + var getSignalStrength: () -> Double = debugGetSignalStrength + init { disposables.add(instance.telemetry.flightMode.distinctUntilChanged() @@ -128,6 +130,35 @@ object Drone { } ) ) + this.missionLiveData.value = missionPlan.missionItems + val isConnectedCompletable = instance.core.connectionState + .filter { state -> state.isConnected } + .firstOrError() + .toCompletable() + + disposables.add(isConnectedCompletable + .andThen(instance.mission.setReturnToLaunchAfterMission(true)) + .andThen(instance.mission.uploadMission(missionPlan)) + .andThen(instance.action.arm()) + .andThen(instance.mission.startMission()) + .subscribe( + { ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) }, + { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) }) + ) + + disposables.add(instance.mission.missionProgress.subscribe { + val missionItem = missionLiveData.value?.get(it.current)!! + val location = LatLng(missionItem.latitudeDeg, missionItem.longitudeDeg) + val signal = getSignalStrength() + onMeasureTaken(location, signal) + }) + //TODO See what to do with disposables added + } + + private fun onMeasureTaken(location: LatLng, signalStrength: Double) { + onMeasureTakenCallbacks.forEach { + it(location, signalStrength) + } } /** From 3bc4b8f2bf486cfc4b498dfb5aa99359220c0415 Mon Sep 17 00:00:00 2001 From: Bastien Wermeille <5320541+Ph0tonic@users.noreply.github.com> Date: Fri, 22 May 2020 22:43:27 +0200 Subject: [PATCH 2/4] Update drone to generate measures during mission progress Co-authored-by: Dragoo417 <17319255+Dragoo417@users.noreply.github.com> --- .../sdp/AaaLocationWithoutPermissionTest.kt | 2 +- .../data_manager/HeatmapDataManagerTest.kt | 6 ++- .../epfl/sdp/{ui => }/drone/DroneErrorTest.kt | 2 +- .../sdp/{ui => }/drone/DroneInstanceMock.kt | 15 +++--- .../ch/epfl/sdp/{ui => }/drone/DroneTest.kt | 13 +---- .../java/ch/epfl/sdp/drone/DroneUtilsTest.kt | 1 - .../java/ch/epfl/sdp/ui/MainActivityTest.kt | 2 +- .../sdp/ui/drone/DroneStatusFragmentTest.kt | 1 + .../ui/drone/ReturnDroneDialogFragmentTest.kt | 1 + .../epfl/sdp/ui/home/LoginNavFragmentTest.kt | 2 +- .../ch/epfl/sdp/ui/maps/MapActivityTest.kt | 2 +- .../offline/OfflineManagerActivityTest.kt | 2 +- .../sdp/utils/LocationWithPermissionTest.kt | 2 +- .../data_manager/HeatmapDataManager.kt | 9 ++-- app/src/main/java/ch/epfl/sdp/drone/Drone.kt | 54 ++++++------------- .../java/ch/epfl/sdp/ui/maps/MapActivity.kt | 5 +- 16 files changed, 47 insertions(+), 72 deletions(-) rename app/src/androidTest/java/ch/epfl/sdp/{ui => }/drone/DroneErrorTest.kt (99%) rename app/src/androidTest/java/ch/epfl/sdp/{ui => }/drone/DroneInstanceMock.kt (89%) rename app/src/androidTest/java/ch/epfl/sdp/{ui => }/drone/DroneTest.kt (92%) diff --git a/app/src/androidTest/java/ch/epfl/sdp/AaaLocationWithoutPermissionTest.kt b/app/src/androidTest/java/ch/epfl/sdp/AaaLocationWithoutPermissionTest.kt index ae93c04e0..211d5c457 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/AaaLocationWithoutPermissionTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/AaaLocationWithoutPermissionTest.kt @@ -7,7 +7,7 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject import androidx.test.uiautomator.UiSelector import ch.epfl.sdp.database.data_manager.MainDataManager -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.MainActivity import ch.epfl.sdp.utils.CentralLocationManager import org.hamcrest.CoreMatchers.equalTo diff --git a/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt b/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt index f96a23f25..4fa5b8bb9 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt @@ -7,6 +7,7 @@ import ch.epfl.sdp.database.data.HeatmapData import ch.epfl.sdp.database.data.HeatmapPointData import ch.epfl.sdp.database.providers.HeatmapRepositoryProvider import ch.epfl.sdp.database.repository.IHeatmapRepository +import ch.epfl.sdp.utils.Auth import com.mapbox.mapboxsdk.geometry.LatLng import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat @@ -23,7 +24,6 @@ class HeatmapDataManagerTest { private val DUMMY_LOCATION = LatLng(0.123, 23.1234) } - @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @@ -34,6 +34,8 @@ class HeatmapDataManagerTest { ), DUMMY_HEATMAP_ID) val expectedGroupId = DUMMY_GROUP_ID + Auth.accountId.value = DUMMY_HEATMAP_ID + val repo = Mockito.mock(IHeatmapRepository::class.java) Mockito.`when`(repo.getGroupHeatmaps(expectedGroupId)).thenReturn(MutableLiveData(mutableMapOf())) @@ -52,6 +54,8 @@ class HeatmapDataManagerTest { val expectedGroupId = DUMMY_GROUP_ID val previousHeatMapData = HeatmapData(mutableListOf(), DUMMY_HEATMAP_ID) + Auth.accountId.value = DUMMY_HEATMAP_ID + val repo = Mockito.mock(IHeatmapRepository::class.java) Mockito.`when`(repo.getGroupHeatmaps(expectedGroupId)).thenReturn(MutableLiveData(mutableMapOf(Pair(DUMMY_HEATMAP_ID, MutableLiveData(previousHeatMapData))))) diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneErrorTest.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt similarity index 99% rename from app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneErrorTest.kt rename to app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt index 46a38d5ec..51d5b09ac 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneErrorTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt @@ -1,4 +1,4 @@ -package ch.epfl.sdp.ui.drone +package ch.epfl.sdp.drone import android.content.Intent import androidx.test.espresso.Espresso diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneInstanceMock.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt similarity index 89% rename from app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneInstanceMock.kt rename to app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt index f65876559..ced0a41d5 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneInstanceMock.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt @@ -1,6 +1,5 @@ -package ch.epfl.sdp.ui.drone +package ch.epfl.sdp.drone -import ch.epfl.sdp.drone.DroneInstanceProvider import io.mavsdk.System import io.mavsdk.action.Action import io.mavsdk.core.Core @@ -13,11 +12,11 @@ import org.mockito.Mockito import org.mockito.Mockito.`when` object DroneInstanceMock { - val droneSystem = Mockito.mock(System::class.java) - val droneTelemetry = Mockito.mock(Telemetry::class.java) - val droneCore = Mockito.mock(Core::class.java) - val droneMission = Mockito.mock(Mission::class.java) - val droneAction = Mockito.mock(Action::class.java) + val droneSystem: System = Mockito.mock(System::class.java) + val droneTelemetry: Telemetry = Mockito.mock(Telemetry::class.java) + val droneCore: Core = Mockito.mock(Core::class.java) + val droneMission: Mission = Mockito.mock(Mission::class.java) + val droneAction: Action = Mockito.mock(Action::class.java) init { DroneInstanceProvider.provide = { @@ -89,6 +88,8 @@ object DroneInstanceMock { .thenReturn(Completable.complete()) `when`(droneMission.clearMission()) .thenReturn(Completable.complete()) + `when`(droneMission.missionProgress) + .thenReturn(Flowable.empty()) //Action mocks `when`(droneAction.arm()) diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneTest.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt similarity index 92% rename from app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneTest.kt rename to app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt index f7e88ece4..0732e66fe 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt @@ -1,12 +1,8 @@ -package ch.epfl.sdp.ui.drone +package ch.epfl.sdp.drone import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.rule.ActivityTestRule import ch.epfl.sdp.database.data_manager.MainDataManager -import ch.epfl.sdp.drone.Drone -import ch.epfl.sdp.drone.DroneUtils -import ch.epfl.sdp.ui.maps.MapActivity import ch.epfl.sdp.utils.CentralLocationManager import com.mapbox.mapboxsdk.geometry.LatLng import io.mavsdk.telemetry.Telemetry @@ -36,13 +32,6 @@ class DroneTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() - @get:Rule - var mActivityRule = ActivityTestRule( - MapActivity::class.java, - true, - false) // Activity is not launched immediately - - @Before fun before() { DroneInstanceMock.setupDefaultMocks() diff --git a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneUtilsTest.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneUtilsTest.kt index 629173c90..4e43c8404 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneUtilsTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneUtilsTest.kt @@ -1,6 +1,5 @@ package ch.epfl.sdp.drone -import ch.epfl.sdp.ui.drone.DroneInstanceMock import com.mapbox.mapboxsdk.geometry.LatLng import io.mavsdk.mission.Mission import org.junit.Assert diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/MainActivityTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/MainActivityTest.kt index e3d7f8897..bc53160f8 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/MainActivityTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/MainActivityTest.kt @@ -31,7 +31,7 @@ import ch.epfl.sdp.database.repository.HeatmapRepository import ch.epfl.sdp.database.repository.MarkerRepository import ch.epfl.sdp.database.repository.SearchGroupRepository import ch.epfl.sdp.database.repository.UserRepository -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.settings.SettingsActivity import ch.epfl.sdp.utils.Auth import org.hamcrest.CoreMatchers.* diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt index 3e4daaa7c..cfb0fb0ce 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt @@ -28,6 +28,7 @@ import ch.epfl.sdp.database.repository.MarkerRepository import ch.epfl.sdp.database.repository.SearchGroupRepository import ch.epfl.sdp.database.repository.UserRepository import ch.epfl.sdp.drone.Drone +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.maps.MapActivity import ch.epfl.sdp.utils.Auth import ch.epfl.sdp.utils.CentralLocationManager diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/ReturnDroneDialogFragmentTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/ReturnDroneDialogFragmentTest.kt index 8699d8a37..b9d1ebf13 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/ReturnDroneDialogFragmentTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/ReturnDroneDialogFragmentTest.kt @@ -35,6 +35,7 @@ import ch.epfl.sdp.database.repository.MarkerRepository import ch.epfl.sdp.database.repository.SearchGroupRepository import ch.epfl.sdp.database.repository.UserRepository import ch.epfl.sdp.drone.Drone +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.maps.MapActivity import ch.epfl.sdp.utils.Auth import ch.epfl.sdp.utils.CentralLocationManager diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/home/LoginNavFragmentTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/home/LoginNavFragmentTest.kt index fce3196a4..1db068627 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/home/LoginNavFragmentTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/home/LoginNavFragmentTest.kt @@ -20,7 +20,7 @@ import androidx.test.rule.GrantPermissionRule.grant import androidx.test.uiautomator.UiDevice import ch.epfl.sdp.MainApplication import ch.epfl.sdp.R -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.MainActivity import ch.epfl.sdp.utils.Auth import com.google.android.gms.auth.api.signin.GoogleSignIn diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/maps/MapActivityTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/maps/MapActivityTest.kt index 1dc8a59c5..8fae50c54 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/maps/MapActivityTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/maps/MapActivityTest.kt @@ -41,7 +41,7 @@ import ch.epfl.sdp.drone.Drone import ch.epfl.sdp.mission.SimpleQuadStrategy import ch.epfl.sdp.mission.SpiralStrategy import ch.epfl.sdp.searcharea.QuadrilateralArea -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.maps.offline.OfflineManagerActivity import ch.epfl.sdp.utils.Auth import ch.epfl.sdp.utils.CentralLocationManager diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/maps/offline/OfflineManagerActivityTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/maps/offline/OfflineManagerActivityTest.kt index 27abd27a9..6cb624072 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/maps/offline/OfflineManagerActivityTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/maps/offline/OfflineManagerActivityTest.kt @@ -15,7 +15,7 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import ch.epfl.sdp.MainApplication.Companion.applicationContext import ch.epfl.sdp.R -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.map.MapUtils.getCameraWithParameters import ch.epfl.sdp.map.offline.OfflineRegionUtils.getRegionName import com.mapbox.mapboxsdk.geometry.LatLng diff --git a/app/src/androidTest/java/ch/epfl/sdp/utils/LocationWithPermissionTest.kt b/app/src/androidTest/java/ch/epfl/sdp/utils/LocationWithPermissionTest.kt index 7de368a5a..5c4d9a0d8 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/utils/LocationWithPermissionTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/utils/LocationWithPermissionTest.kt @@ -21,7 +21,7 @@ import ch.epfl.sdp.database.data.Role import ch.epfl.sdp.database.data_manager.MainDataManager import ch.epfl.sdp.database.repository.HeatmapRepository import ch.epfl.sdp.database.repository.MarkerRepository -import ch.epfl.sdp.ui.drone.DroneInstanceMock +import ch.epfl.sdp.drone.DroneInstanceMock import ch.epfl.sdp.ui.maps.MapActivity import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat diff --git a/app/src/main/java/ch/epfl/sdp/database/data_manager/HeatmapDataManager.kt b/app/src/main/java/ch/epfl/sdp/database/data_manager/HeatmapDataManager.kt index cfdd676d2..64f558d75 100644 --- a/app/src/main/java/ch/epfl/sdp/database/data_manager/HeatmapDataManager.kt +++ b/app/src/main/java/ch/epfl/sdp/database/data_manager/HeatmapDataManager.kt @@ -2,20 +2,19 @@ package ch.epfl.sdp.database.data_manager import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import ch.epfl.sdp.database.dao.FirebaseHeatmapDao -import ch.epfl.sdp.database.dao.HeatmapDao import ch.epfl.sdp.database.data.HeatmapData import ch.epfl.sdp.database.data.HeatmapPointData import ch.epfl.sdp.database.providers.HeatmapRepositoryProvider -import ch.epfl.sdp.database.repository.HeatmapRepository +import ch.epfl.sdp.utils.Auth import com.mapbox.mapboxsdk.geometry.LatLng -import com.mapbox.mapboxsdk.style.layers.HeatmapLayer class HeatmapDataManager { private val heatmapRepository = HeatmapRepositoryProvider.provide() - fun addMeasureToHeatmap(groupId: String, heatmapId: String, location: LatLng, intensity: Double) { + fun addMeasureToHeatmap(groupId: String, location: LatLng, intensity: Double) { + val heatmapId = Auth.accountId.value + val heatmaps = heatmapRepository.getGroupHeatmaps(groupId).value val heatmapData = if (heatmaps != null && heatmaps.containsKey(heatmapId)) { heatmaps[heatmapId]?.value?.copy()!! diff --git a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt index f79ed8470..ecd3570ff 100644 --- a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt +++ b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt @@ -4,6 +4,7 @@ import android.widget.Toast import androidx.lifecycle.MutableLiveData import ch.epfl.sdp.MainApplication import ch.epfl.sdp.R +import ch.epfl.sdp.database.data_manager.HeatmapDataManager import ch.epfl.sdp.ui.toast.ToastHandler import ch.epfl.sdp.utils.CentralLocationManager import com.mapbox.mapboxsdk.geometry.LatLng @@ -18,16 +19,10 @@ import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.sqrt - object Drone { - // Maximum distance between passes in the strategy - const val GROUND_SENSOR_SCOPE: Double = 15.0 - const val DEFAULT_ALTITUDE: Float = 10.0F - const val MAX_DISTANCE_BETWEEN_POINTS_IN_AREA = 1000 //meters - private const val WAIT_TIME_S: Long = 1 - val onMeasureTakenCallbacks = mutableListOf<(LatLng, Double) -> Unit>() + private val heatmapDataManager = HeatmapDataManager() private val disposables: MutableList = ArrayList() val positionLiveData: MutableLiveData = MutableLiveData() @@ -40,9 +35,11 @@ object Drone { val isConnectedLiveData: MutableLiveData = MutableLiveData(false) val isMissionPausedLiveData: MutableLiveData = MutableLiveData(true) + private val onMeasureTakenCallbacks = mutableListOf<(LatLng, Double) -> Unit>() + /*Will be useful later on*/ val debugGetSignalStrength: () -> Double = { - positionLiveData.value?.distanceTo(LatLng(47.3975, 8.5445)) ?: 0.0 + positionLiveData.value!!.distanceTo(LatLng(47.3975, 8.5445)) } private val instance: System = DroneInstanceProvider.provide() @@ -112,47 +109,30 @@ object Drone { /** * @param missionPlan : the MissionPlan the drone will follow */ - fun startMission(missionPlan: Mission.MissionPlan) { - disposables.add( - getConnectedInstance() - .andThen(instance.mission.setReturnToLaunchAfterMission(true)) - .andThen(instance.mission.uploadMission(missionPlan)) - .andThen(instance.action.arm()) - .andThen(instance.mission.startMission()) - .subscribe( - { - this.missionLiveData.value = missionPlan.missionItems - this.isMissionPausedLiveData.postValue(false) - ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) - }, - { - ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) - } - ) - ) + fun startMission(missionPlan: Mission.MissionPlan, groupId: String) { + Timber.w("startMission Drone") this.missionLiveData.value = missionPlan.missionItems + val isConnectedCompletable = instance.core.connectionState .filter { state -> state.isConnected } .firstOrError() .toCompletable() + val missionCallBack = { location: LatLng, signalStrength: Double -> + heatmapDataManager.addMeasureToHeatmap(groupId, location, signalStrength) + } + disposables.add(isConnectedCompletable .andThen(instance.mission.setReturnToLaunchAfterMission(true)) .andThen(instance.mission.uploadMission(missionPlan)) .andThen(instance.action.arm()) + .andThen { + onMeasureTakenCallbacks.add(missionCallBack) + Timber.w("Add callback for measure !") + } .andThen(instance.mission.startMission()) - .subscribe( - { ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) }, - { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) }) - ) + .subscribe()) - disposables.add(instance.mission.missionProgress.subscribe { - val missionItem = missionLiveData.value?.get(it.current)!! - val location = LatLng(missionItem.latitudeDeg, missionItem.longitudeDeg) - val signal = getSignalStrength() - onMeasureTaken(location, signal) - }) - //TODO See what to do with disposables added } private fun onMeasureTaken(location: LatLng, signalStrength: Double) { diff --git a/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt b/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt index 2c7dcc204..7cd824af4 100644 --- a/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt +++ b/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt @@ -45,6 +45,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback import com.mapbox.mapboxsdk.maps.Style import io.mavsdk.telemetry.Telemetry import kotlin.math.abs +import timber.log.Timber /** * Main Activity to display map and create missions. @@ -271,7 +272,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo */ fun addPointToHeatMap(location: LatLng, intensity: Double) { if (isMapReady) { - heatmapManager.addMeasureToHeatmap(MainDataManager.groupId.value!!, Auth.accountId.value!!, location, intensity) + heatmapManager.addMeasureToHeatmap(groupId, location, intensity) } } @@ -316,7 +317,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo fun launchMission() { val altitude = PreferenceManager.getDefaultSharedPreferences(this) .getString(this.getString(R.string.pref_key_drone_altitude), Drone.DEFAULT_ALTITUDE.toString()).toString().toFloat() - Drone.startMission(DroneUtils.makeDroneMission(missionBuilder.build(), altitude)) + Drone.startMission(DroneUtils.makeDroneMission(missionBuilder.build(), altitude), groupId) searchAreaBuilder.reset() } From ebfbcdac9cfab616c1be1f737381dad866ea0e34 Mon Sep 17 00:00:00 2001 From: Bastien Wermeille <5320541+Ph0tonic@users.noreply.github.com> Date: Sun, 31 May 2020 22:49:41 +0200 Subject: [PATCH 3/4] Heatmaps points are added during drone mission --- app/src/main/java/ch/epfl/sdp/drone/Drone.kt | 72 ++++++++++++++++++- .../ch/epfl/sdp/map/MapboxHeatmapPainter.kt | 11 +-- .../ch/epfl/sdp/map/MeasureHeatmapManager.kt | 4 +- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt index ecd3570ff..19bd6f2ea 100644 --- a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt +++ b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt @@ -1,5 +1,6 @@ package ch.epfl.sdp.drone +import android.util.Log import android.widget.Toast import androidx.lifecycle.MutableLiveData import ch.epfl.sdp.MainApplication @@ -13,6 +14,10 @@ import io.mavsdk.mission.Mission import io.mavsdk.telemetry.Telemetry import io.reactivex.Completable import io.reactivex.disposables.Disposable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber import java.util.concurrent.TimeUnit import kotlin.math.pow @@ -119,6 +124,7 @@ object Drone { .toCompletable() val missionCallBack = { location: LatLng, signalStrength: Double -> + Log.w("DRONE", "Mission Callback") heatmapDataManager.addMeasureToHeatmap(groupId, location, signalStrength) } @@ -133,11 +139,46 @@ object Drone { .andThen(instance.mission.startMission()) .subscribe()) + disposables.add( + getConnectedInstance() + .andThen(instance.mission.setReturnToLaunchAfterMission(true)) + .andThen(instance.mission.uploadMission(missionPlan)) + .andThen(instance.action.arm()) + .andThen { + onMeasureTakenCallbacks.add(missionCallBack) + Log.w("DRONE", "Add callback") + it.onComplete() + } + .andThen(instance.mission.startMission()) + .subscribe( + { ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) }, + { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) } + ) + ) + + disposables.add(instance.mission.missionProgress.subscribe { missionProgress -> + if (missionProgress.current == missionProgress.total) { + Log.w("DRONE", "Remove callback") + onMeasureTakenCallbacks.remove(missionCallBack) + } + Log.w("DRONE", "Mission progress YEAH !") + val missionItem = missionLiveData.value?.getOrNull(missionProgress.current - 1) + val location = missionItem?.longitudeDeg?.let { it1 -> LatLng(missionItem.latitudeDeg, it1) } + val signal = getSignalStrength() + Log.w("DRONE", "Signal strength $signal") + Log.w("DRONE", "Location $location") + location?.let { it1 -> onMeasureTaken(it1, signal) } + }) + //TODO See what to do with disposables added } private fun onMeasureTaken(location: LatLng, signalStrength: Double) { - onMeasureTakenCallbacks.forEach { - it(location, signalStrength) + GlobalScope.launch { + withContext(Dispatchers.Main) { + onMeasureTakenCallbacks.forEach { + it(location, signalStrength) + } + } } } @@ -174,7 +215,25 @@ object Drone { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) } ) +<<<<<<< HEAD ) +======= + } + + /** + * If the drone is not flying, it starts a mission with + * @param missionPlan + * + * If the drone is already flying, but paused, it restarts it + * If the drone is already flying and doing a mission, it pauses the mission + */ + fun startOrPauseMission(missionPlan: Mission.MissionPlan, groupId: String) { + if (this.isFlyingLiveData.value!!) { + disposables.add(if (this.isMissionPausedLiveData.value!!) restartMission() else pauseMission()) + } else { + startMission(missionPlan, groupId) + } +>>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen } /** @@ -205,6 +264,10 @@ object Drone { }, { ToastHandler().showToast(R.string.drone_home_error, Toast.LENGTH_SHORT) +<<<<<<< HEAD +======= + this.missionLiveData.value = null +>>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen } ) ) @@ -223,6 +286,7 @@ object Drone { .andThen(instance.mission.clearMission()) .andThen(instance.action.gotoLocation(returnLocation.latitude, returnLocation.longitude, 20.0F, 0F)) .subscribe( +<<<<<<< HEAD { this.missionLiveData.value = listOf(DroneUtils.generateMissionItem(returnLocation.latitude, returnLocation.longitude, returnLocation.altitude.toFloat())) ToastHandler().showToast(R.string.drone_user_success, Toast.LENGTH_SHORT) @@ -230,6 +294,10 @@ object Drone { { ToastHandler().showToast(R.string.drone_user_error, Toast.LENGTH_SHORT) } +======= + { ToastHandler().showToast(R.string.drone_user_success, Toast.LENGTH_SHORT) }, + { ToastHandler().showToast(R.string.drone_user_error, Toast.LENGTH_SHORT) } +>>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen ) ) diff --git a/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt b/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt index 893fb52cd..f55247738 100644 --- a/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt +++ b/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt @@ -12,6 +12,7 @@ import com.mapbox.mapboxsdk.style.layers.CircleLayer import com.mapbox.mapboxsdk.style.layers.PropertyFactory import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions import com.mapbox.mapboxsdk.style.sources.GeoJsonSource +import java.util.* class MapboxHeatmapPainter(val style: Style, val heatmapData: MutableLiveData, @@ -56,7 +57,9 @@ class MapboxHeatmapPainter(val style: Style, } } - private val geoJsonSource: GeoJsonSource = GeoJsonSource(heatmapData.value!!.uuid, GeoJsonOptions() + private val uuidSource = UUID.randomUUID().toString() + + private val geoJsonSource: GeoJsonSource = GeoJsonSource(uuidSource, GeoJsonOptions() .withCluster(true) .withClusterProperty("intensities", Expression.literal("+"), Expression.get("intensity")) .withClusterMaxZoom(13) @@ -64,11 +67,10 @@ class MapboxHeatmapPainter(val style: Style, private val heatmapRedrawObserver = Observer { paint() } init { - val heatmapId = heatmapData.value!!.uuid!! + unclusteredLayerData(uuidSource, style, belowLayerId) + clusteredLayerData(uuidSource, style, belowLayerId) style.addSource(geoJsonSource) heatmapData.observeForever(heatmapRedrawObserver) - unclusteredLayerData(heatmapId, style, belowLayerId) - clusteredLayerData(heatmapId, style, belowLayerId) // first call is not triggered by observer paint() @@ -76,6 +78,7 @@ class MapboxHeatmapPainter(val style: Style, override fun onDestroy() { heatmapData.removeObserver(heatmapRedrawObserver) + geoJsonSource.setGeoJson(FeatureCollection.fromFeatures(listOf())) } private fun paint() { diff --git a/app/src/main/java/ch/epfl/sdp/map/MeasureHeatmapManager.kt b/app/src/main/java/ch/epfl/sdp/map/MeasureHeatmapManager.kt index a2291693d..38a2f5f3d 100644 --- a/app/src/main/java/ch/epfl/sdp/map/MeasureHeatmapManager.kt +++ b/app/src/main/java/ch/epfl/sdp/map/MeasureHeatmapManager.kt @@ -8,7 +8,7 @@ import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.Style -class MeasureHeatmapManager(val mapView: MapView, val mapboxMap: MapboxMap, val style: Style, val upperLayerId: String): Observer>> { +class MeasureHeatmapManager(val mapView: MapView, val mapboxMap: MapboxMap, val style: Style, val upperLayerId: String) : Observer>> { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) val heatmapPainters = mutableMapOf() @@ -23,7 +23,7 @@ class MeasureHeatmapManager(val mapView: MapView, val mapboxMap: MapboxMap, val // Observers for heatmap creation heatmaps.filter { !heatmapPainters.containsKey(it.key) } .forEach { (key, value) -> - heatmapPainters[key] = MapboxHeatmapPainter(style, value, upperLayerId) + heatmapPainters[key] = MapboxHeatmapPainter(mapboxMap.style!!, value, upperLayerId) } // Remove observers on heatmap deletion From b3fff7f1c0c7bdd0afb8b7758e298886f6a41c73 Mon Sep 17 00:00:00 2001 From: AlexisTabin Date: Thu, 4 Jun 2020 12:37:30 +0200 Subject: [PATCH 4/4] Add mock implementation on MissionProgress --- .../data_manager/HeatmapDataManagerTest.kt | 4 +- .../java/ch/epfl/sdp/drone/DroneErrorTest.kt | 45 +++----- .../ch/epfl/sdp/drone/DroneInstanceMock.kt | 16 ++- .../java/ch/epfl/sdp/drone/DroneTest.kt | 19 +++- .../sdp/ui/drone/DroneStatusFragmentTest.kt | 1 - .../database/data_manager/MainDataManager.kt | 4 + app/src/main/java/ch/epfl/sdp/drone/Drone.kt | 101 ++++++------------ .../ch/epfl/sdp/map/MapboxHeatmapPainter.kt | 10 +- .../java/ch/epfl/sdp/ui/camera/VlcFragment.kt | 3 +- .../java/ch/epfl/sdp/ui/maps/MapActivity.kt | 10 +- .../ui/search_group/edition/UserViewHolder.kt | 1 - 11 files changed, 92 insertions(+), 122 deletions(-) diff --git a/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt b/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt index 4fa5b8bb9..5de1e6088 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/database/data_manager/HeatmapDataManagerTest.kt @@ -40,7 +40,7 @@ class HeatmapDataManagerTest { Mockito.`when`(repo.getGroupHeatmaps(expectedGroupId)).thenReturn(MutableLiveData(mutableMapOf())) HeatmapRepositoryProvider.provide = { repo } - HeatmapDataManager().addMeasureToHeatmap(DUMMY_GROUP_ID, DUMMY_HEATMAP_ID, DUMMY_LOCATION, DUMMY_INTENSITY) + HeatmapDataManager().addMeasureToHeatmap(DUMMY_GROUP_ID, DUMMY_LOCATION, DUMMY_INTENSITY) Mockito.verify(repo, Mockito.times(1)).getGroupHeatmaps(expectedGroupId) Mockito.verify(repo, Mockito.times(1)).updateHeatmap(expectedGroupId, expectedHeatMapData) @@ -60,7 +60,7 @@ class HeatmapDataManagerTest { Mockito.`when`(repo.getGroupHeatmaps(expectedGroupId)).thenReturn(MutableLiveData(mutableMapOf(Pair(DUMMY_HEATMAP_ID, MutableLiveData(previousHeatMapData))))) HeatmapRepositoryProvider.provide = { repo } - HeatmapDataManager().addMeasureToHeatmap(DUMMY_GROUP_ID, DUMMY_HEATMAP_ID, DUMMY_LOCATION, DUMMY_INTENSITY) + HeatmapDataManager().addMeasureToHeatmap(DUMMY_GROUP_ID, DUMMY_LOCATION, DUMMY_INTENSITY) Mockito.verify(repo, Mockito.times(1)).getGroupHeatmaps(expectedGroupId) Mockito.verify(repo, Mockito.times(1)).updateHeatmap(expectedGroupId, expectedHeatMapData) diff --git a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt index 51d5b09ac..b7c582e5d 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneErrorTest.kt @@ -1,28 +1,16 @@ package ch.epfl.sdp.drone -import android.content.Intent -import androidx.test.espresso.Espresso -import androidx.test.espresso.assertion.ViewAssertions -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.espresso.matcher.RootMatchers -import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread -import ch.epfl.sdp.MainApplication -import ch.epfl.sdp.R -import ch.epfl.sdp.drone.Drone -import ch.epfl.sdp.drone.DroneUtils -import ch.epfl.sdp.ui.MainActivity import ch.epfl.sdp.utils.CentralLocationManager import com.mapbox.mapboxsdk.geometry.LatLng import io.mavsdk.telemetry.Telemetry import io.reactivex.Completable -import org.hamcrest.CoreMatchers +import io.reactivex.Flowable import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.nullValue import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers @@ -34,6 +22,7 @@ class DroneErrorTest { companion object { private const val DEFAULT_ALTITUDE = 10f + private const val DUMMY_GROUP_ID = "dummy_group_id" val someLocationsList = listOf( LatLng(47.398979, 8.543434), LatLng(47.398279, 8.543934), @@ -42,12 +31,6 @@ class DroneErrorTest { ) } - @get:Rule - val mActivityRule = IntentsTestRule( - MainActivity::class.java, - true, - false) - @Before fun before() { DroneInstanceMock.setupDefaultMocks() @@ -64,12 +47,14 @@ class DroneErrorTest { .thenReturn(Completable.error(Throwable("Error StartMission"))) `when`(DroneInstanceMock.droneMission.clearMission()) .thenReturn(Completable.error(Throwable("Error Clear Mission"))) + `when`(DroneInstanceMock.droneMission.missionProgress) + .thenReturn(Flowable.empty()) } @Test fun failToStartMissionResetMission() { runOnUiThread { - Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE)) + Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE), DUMMY_GROUP_ID) } assertThat(Drone.isMissionPausedLiveData.value, `is`(true)) @@ -91,7 +76,7 @@ class DroneErrorTest { } @Test - fun failToRestartMissionResetsMissionStatus() { + fun failToResumeMissionResetsMissionStatus() { runOnUiThread { Drone.isFlyingLiveData.value = true Drone.isMissionPausedLiveData.value = true @@ -113,12 +98,7 @@ class DroneErrorTest { } @Test - fun failToReturnToUserShowsToast() { - runOnUiThread{ - CentralLocationManager.currentUserPosition.value = LatLng(0.0, 0.0) - } - mActivityRule.launchActivity(Intent()) - + fun failToReturnToUserResetMission() { Mockito.reset(DroneInstanceMock.droneAction) //Action mocks @@ -135,11 +115,10 @@ class DroneErrorTest { `when`(DroneInstanceMock.droneAction.land()) .thenReturn(Completable.complete()) - Drone.returnToUserLocationAndLand() - - // Test that the toast is displayed - Espresso.onView(ViewMatchers.withText(MainApplication.applicationContext().getString(R.string.drone_user_error))) - .inRoot(RootMatchers.withDecorView(CoreMatchers.not(mActivityRule.activity.window.decorView))) - .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + runOnUiThread { + CentralLocationManager.currentUserPosition.value = LatLng(0.0, 0.0) + Drone.returnToUserLocationAndLand() + } + assertThat(Drone.missionLiveData.value, `is`(nullValue())) } } \ No newline at end of file diff --git a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt index ced0a41d5..bceea4bf1 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneInstanceMock.kt @@ -41,7 +41,8 @@ object DroneInstanceMock { .thenReturn(Flowable.fromArray( Telemetry.FlightMode.LAND, Telemetry.FlightMode.MISSION, - Telemetry.FlightMode.HOLD + Telemetry.FlightMode.HOLD, + Telemetry.FlightMode.MISSION )) `when`(droneTelemetry.armed) .thenReturn(Flowable.fromArray( @@ -49,7 +50,10 @@ object DroneInstanceMock { )) `when`(droneTelemetry.position) .thenReturn(Flowable.fromArray( - Telemetry.Position(0.0, 0.0, 0.0f, 0.0f) + Telemetry.Position(0.0, 0.0, 0.0f, 0.0f), + Telemetry.Position(0.1, 0.0, 0.0f, 0.0f), + Telemetry.Position(0.2, 0.0, 0.0f, 0.0f), + Telemetry.Position(0.3, 0.0, 0.0f, 0.0f) )) `when`(droneTelemetry.battery) .thenReturn(Flowable.fromArray( @@ -89,7 +93,13 @@ object DroneInstanceMock { `when`(droneMission.clearMission()) .thenReturn(Completable.complete()) `when`(droneMission.missionProgress) - .thenReturn(Flowable.empty()) + .thenReturn(Flowable.fromArray( + Mission.MissionProgress(0, 4), + Mission.MissionProgress(1, 4), + Mission.MissionProgress(2, 4), + Mission.MissionProgress(3, 4), + Mission.MissionProgress(4, 4) + )) //Action mocks `when`(droneAction.arm()) diff --git a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt index 0732e66fe..9f996d890 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/drone/DroneTest.kt @@ -12,15 +12,24 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito @RunWith(AndroidJUnit4::class) class DroneTest { + private fun any(): T { + Mockito.any() + return uninitialized() + } + + private fun uninitialized(): T = null as T + companion object { const val SIGNAL_STRENGTH = 1.0 private const val EPSILON = 1e-5 private const val DEFAULT_ALTITUDE = 10f + private const val DUMMY_GROUP_ID = "dummy_group_id" val someLocationsList = listOf( LatLng(47.398979, 8.543434), LatLng(47.398279, 8.543934), @@ -46,13 +55,13 @@ class DroneTest { } @Test - fun missionTestDoesNotCrash() { + fun startMissionUpdatesLiveData() { Drone.missionLiveData.value = null assertThat(Drone.missionLiveData.value, `is`(nullValue())) - Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE)) + Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE), DUMMY_GROUP_ID) - // This assert prevent the app to crash in cash the mission has not been updated + // This assert prevent the app to crash in case the mission has not been updated assertThat(Drone.missionLiveData.value, `is`(notNullValue())) assertThat(Drone.missionLiveData.value?.isEmpty(), `is`(false)) } @@ -64,7 +73,7 @@ class DroneTest { Drone.positionLiveData.value = expectedLatLng Drone.homeLocationLiveData.value = Telemetry.Position(expectedLatLng.latitude, expectedLatLng.longitude, 400f, 50f) - Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE)) + Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE), DUMMY_GROUP_ID) Drone.returnToHomeLocationAndLand() @@ -87,7 +96,7 @@ class DroneTest { val expectedLatLng = LatLng(47.297428, 8.445369) //Position near the takeoff CentralLocationManager.currentUserPosition.value = expectedLatLng - Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE)) + Drone.startMission(DroneUtils.makeDroneMission(someLocationsList, DEFAULT_ALTITUDE), DUMMY_GROUP_ID) assertThat(CentralLocationManager.currentUserPosition.value, `is`(notNullValue())) Drone.returnToUserLocationAndLand() diff --git a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt index cfb0fb0ce..3c03850cc 100644 --- a/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt +++ b/app/src/androidTest/java/ch/epfl/sdp/ui/drone/DroneStatusFragmentTest.kt @@ -48,7 +48,6 @@ class DroneStatusFragmentTest { private const val DEFAULT_ALTITUDE_DISPLAY = " 0.0 m" private const val FAKE_ACCOUNT_ID = "fake_account_id" private const val DUMMY_GROUP_ID = "DummyGroupId" - private const val MAP_LOADING_TIMEOUT = 1000L } private lateinit var preferencesEditor: SharedPreferences.Editor diff --git a/app/src/main/java/ch/epfl/sdp/database/data_manager/MainDataManager.kt b/app/src/main/java/ch/epfl/sdp/database/data_manager/MainDataManager.kt index 15de4e497..ae2e80da8 100644 --- a/app/src/main/java/ch/epfl/sdp/database/data_manager/MainDataManager.kt +++ b/app/src/main/java/ch/epfl/sdp/database/data_manager/MainDataManager.kt @@ -10,6 +10,7 @@ import ch.epfl.sdp.database.dao.OfflineMarkerDao import ch.epfl.sdp.database.data.Role import ch.epfl.sdp.database.repository.HeatmapRepository import ch.epfl.sdp.database.repository.MarkerRepository +import ch.epfl.sdp.utils.Auth object MainDataManager { @@ -50,6 +51,9 @@ object MainDataManager { cachedUserIdWhileOffline = groupId.value cachedUserRoleWhileOffline = role.value!! } + if (Auth.accountId.value == null) { + Auth.accountId.value = OFFLINE_MODE_USER_ID + } groupId.value = OFFLINE_MODE_USER_ID role.value = Role.OPERATOR offlineMode = true diff --git a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt index 19bd6f2ea..9c7c11ca2 100644 --- a/app/src/main/java/ch/epfl/sdp/drone/Drone.kt +++ b/app/src/main/java/ch/epfl/sdp/drone/Drone.kt @@ -25,6 +25,11 @@ import kotlin.math.roundToInt import kotlin.math.sqrt object Drone { + // Maximum distance between passes in the strategy + const val GROUND_SENSOR_SCOPE: Double = 5.0 + const val DEFAULT_ALTITUDE: Float = 10.0F + const val MAX_DISTANCE_BETWEEN_POINTS_IN_AREA = 1000 //meters + private const val WAIT_TIME_S: Long = 1 private val heatmapDataManager = HeatmapDataManager() @@ -44,7 +49,7 @@ object Drone { /*Will be useful later on*/ val debugGetSignalStrength: () -> Double = { - positionLiveData.value!!.distanceTo(LatLng(47.3975, 8.5445)) + 1000 / positionLiveData.value!!.distanceTo(LatLng(47.303584, 7.159724)).pow(2) } private val instance: System = DroneInstanceProvider.provide() @@ -115,30 +120,10 @@ object Drone { * @param missionPlan : the MissionPlan the drone will follow */ fun startMission(missionPlan: Mission.MissionPlan, groupId: String) { - Timber.w("startMission Drone") - this.missionLiveData.value = missionPlan.missionItems - - val isConnectedCompletable = instance.core.connectionState - .filter { state -> state.isConnected } - .firstOrError() - .toCompletable() - val missionCallBack = { location: LatLng, signalStrength: Double -> - Log.w("DRONE", "Mission Callback") heatmapDataManager.addMeasureToHeatmap(groupId, location, signalStrength) } - disposables.add(isConnectedCompletable - .andThen(instance.mission.setReturnToLaunchAfterMission(true)) - .andThen(instance.mission.uploadMission(missionPlan)) - .andThen(instance.action.arm()) - .andThen { - onMeasureTakenCallbacks.add(missionCallBack) - Timber.w("Add callback for measure !") - } - .andThen(instance.mission.startMission()) - .subscribe()) - disposables.add( getConnectedInstance() .andThen(instance.mission.setReturnToLaunchAfterMission(true)) @@ -146,30 +131,39 @@ object Drone { .andThen(instance.action.arm()) .andThen { onMeasureTakenCallbacks.add(missionCallBack) - Log.w("DRONE", "Add callback") it.onComplete() } .andThen(instance.mission.startMission()) .subscribe( - { ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) }, + { + this.missionLiveData.postValue(missionPlan.missionItems) + this.isMissionPausedLiveData.postValue(false) + ToastHandler().showToast(R.string.drone_mission_success, Toast.LENGTH_SHORT) + takeMeasure(missionCallBack) + }, { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) } ) ) + // TODO("See what to do with added disposables") + } - disposables.add(instance.mission.missionProgress.subscribe { missionProgress -> - if (missionProgress.current == missionProgress.total) { - Log.w("DRONE", "Remove callback") - onMeasureTakenCallbacks.remove(missionCallBack) - } - Log.w("DRONE", "Mission progress YEAH !") - val missionItem = missionLiveData.value?.getOrNull(missionProgress.current - 1) - val location = missionItem?.longitudeDeg?.let { it1 -> LatLng(missionItem.latitudeDeg, it1) } - val signal = getSignalStrength() - Log.w("DRONE", "Signal strength $signal") - Log.w("DRONE", "Location $location") - location?.let { it1 -> onMeasureTaken(it1, signal) } - }) - //TODO See what to do with disposables added + private fun takeMeasure(missionCallBack: (LatLng, Double) -> Unit) { + disposables.add( + getConnectedInstance() + .andThen(instance.mission.missionProgress) + .subscribe( + { missionProgress -> + if (missionProgress.current == missionProgress.total) { + onMeasureTakenCallbacks.remove(missionCallBack) + } + val missionItem = missionLiveData.value?.getOrNull(missionProgress.current - 1) + val location = missionItem?.longitudeDeg?.let { it1 -> LatLng(missionItem.latitudeDeg, it1) } + val signal = getSignalStrength() + location?.let { it1 -> onMeasureTaken(it1, signal) } + }, + {} + ) + ) } private fun onMeasureTaken(location: LatLng, signalStrength: Double) { @@ -215,25 +209,7 @@ object Drone { ToastHandler().showToast(R.string.drone_mission_error, Toast.LENGTH_SHORT) } ) -<<<<<<< HEAD ) -======= - } - - /** - * If the drone is not flying, it starts a mission with - * @param missionPlan - * - * If the drone is already flying, but paused, it restarts it - * If the drone is already flying and doing a mission, it pauses the mission - */ - fun startOrPauseMission(missionPlan: Mission.MissionPlan, groupId: String) { - if (this.isFlyingLiveData.value!!) { - disposables.add(if (this.isMissionPausedLiveData.value!!) restartMission() else pauseMission()) - } else { - startMission(missionPlan, groupId) - } ->>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen } /** @@ -259,15 +235,12 @@ object Drone { .andThen(instance.action.returnToLaunch()) .subscribe( { - this.missionLiveData.value = listOf(DroneUtils.generateMissionItem(returnLocation.latitude, returnLocation.longitude, returnLocation.altitude.toFloat())) + this.missionLiveData.postValue(listOf(DroneUtils.generateMissionItem(returnLocation.latitude, returnLocation.longitude, returnLocation.altitude.toFloat()))) ToastHandler().showToast(R.string.drone_home_success, Toast.LENGTH_SHORT) }, { + this.missionLiveData.postValue(null) ToastHandler().showToast(R.string.drone_home_error, Toast.LENGTH_SHORT) -<<<<<<< HEAD -======= - this.missionLiveData.value = null ->>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen } ) ) @@ -286,18 +259,14 @@ object Drone { .andThen(instance.mission.clearMission()) .andThen(instance.action.gotoLocation(returnLocation.latitude, returnLocation.longitude, 20.0F, 0F)) .subscribe( -<<<<<<< HEAD { - this.missionLiveData.value = listOf(DroneUtils.generateMissionItem(returnLocation.latitude, returnLocation.longitude, returnLocation.altitude.toFloat())) + this.missionLiveData.postValue(listOf(DroneUtils.generateMissionItem(returnLocation.latitude, returnLocation.longitude, returnLocation.altitude.toFloat()))) ToastHandler().showToast(R.string.drone_user_success, Toast.LENGTH_SHORT) }, { + this.missionLiveData.postValue(null) ToastHandler().showToast(R.string.drone_user_error, Toast.LENGTH_SHORT) } -======= - { ToastHandler().showToast(R.string.drone_user_success, Toast.LENGTH_SHORT) }, - { ToastHandler().showToast(R.string.drone_user_error, Toast.LENGTH_SHORT) } ->>>>>>> 54705bc3... Fix issues when new heatmps are added or removed and nothing change on screen ) ) diff --git a/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt b/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt index f55247738..f69756480 100644 --- a/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt +++ b/app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt @@ -30,11 +30,11 @@ class MapboxHeatmapPainter(val style: Style, unclustered.setProperties( PropertyFactory.circleColor( Expression.interpolate(Expression.linear(), Expression.get("intensity"), - Expression.stop(8, BLUE), - Expression.stop(8.5, CYAN), - Expression.stop(9, GREEN), - Expression.stop(9.5, ORANGE), - Expression.stop(10.0, RED) + Expression.stop(1, BLUE), + Expression.stop(3, CYAN), + Expression.stop(5, GREEN), + Expression.stop(7, ORANGE), + Expression.stop(9, RED) ) ), PropertyFactory.circleRadius(25f), diff --git a/app/src/main/java/ch/epfl/sdp/ui/camera/VlcFragment.kt b/app/src/main/java/ch/epfl/sdp/ui/camera/VlcFragment.kt index 943692ee8..99f995a86 100644 --- a/app/src/main/java/ch/epfl/sdp/ui/camera/VlcFragment.kt +++ b/app/src/main/java/ch/epfl/sdp/ui/camera/VlcFragment.kt @@ -19,7 +19,7 @@ class VlcFragment : Fragment() { companion object { private const val USE_TEXTURE_VIEW = false private const val ENABLE_SUBTITLES = false - private const val RTSP_SOURCE = "rtsp://192.168.1.131:8554/live" //must be your IP + private const val RTSP_SOURCE = "rtsp://192.168.1.120:8554/live" //must be your IP private val libvlcArguments = arrayListOf("-vvv")//, "--live-caching=200") } @@ -49,6 +49,7 @@ class VlcFragment : Fragment() { override fun onStart() { super.onStart() mMediaPlayer.attachViews(mVideoLayout, null, ENABLE_SUBTITLES, USE_TEXTURE_VIEW) + startVideo() } fun switchVideo(v: View) { diff --git a/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt b/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt index 7cd824af4..c7340c919 100644 --- a/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt +++ b/app/src/main/java/ch/epfl/sdp/ui/maps/MapActivity.kt @@ -16,6 +16,7 @@ import ch.epfl.sdp.R import ch.epfl.sdp.database.data.Role import ch.epfl.sdp.database.data_manager.HeatmapDataManager import ch.epfl.sdp.database.data_manager.MainDataManager +import ch.epfl.sdp.database.data_manager.MainDataManager.groupId import ch.epfl.sdp.database.data_manager.MarkerDataManager import ch.epfl.sdp.drone.Drone import ch.epfl.sdp.drone.DroneUtils @@ -45,7 +46,6 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback import com.mapbox.mapboxsdk.maps.Style import io.mavsdk.telemetry.Telemetry import kotlin.math.abs -import timber.log.Timber /** * Main Activity to display map and create missions. @@ -122,7 +122,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo } override fun onCreate(savedInstanceState: Bundle?) { - require(!MainDataManager.groupId.value.isNullOrEmpty()) { "MapActivity should be provided with a valid searchGroupId\n" } + require(!groupId.value.isNullOrEmpty()) { "MapActivity should be provided with a valid searchGroupId\n" } requireNotNull(Auth.accountId.value) { "You need to have an account ID set to access MapActivity" } requireNotNull(MainDataManager.role.value) { "MapActivity should be provided with a role" } @@ -255,7 +255,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo override fun onMapLongClick(position: LatLng): Boolean { // Need mapbox update to remove this test if (!longClickConsumed) { - markerManager.addMarkerForSearchGroup(MainDataManager.groupId.value!!, position) + markerManager.addMarkerForSearchGroup(groupId.value!!, position) } longClickConsumed = false return true @@ -272,7 +272,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo */ fun addPointToHeatMap(location: LatLng, intensity: Double) { if (isMapReady) { - heatmapManager.addMeasureToHeatmap(groupId, location, intensity) + heatmapManager.addMeasureToHeatmap(groupId.value!!, location, intensity) } } @@ -317,7 +317,7 @@ class MapActivity : MapViewBaseActivity(), OnMapReadyCallback, MapboxMap.OnMapLo fun launchMission() { val altitude = PreferenceManager.getDefaultSharedPreferences(this) .getString(this.getString(R.string.pref_key_drone_altitude), Drone.DEFAULT_ALTITUDE.toString()).toString().toFloat() - Drone.startMission(DroneUtils.makeDroneMission(missionBuilder.build(), altitude), groupId) + Drone.startMission(DroneUtils.makeDroneMission(missionBuilder.build(), altitude), groupId.value!!) searchAreaBuilder.reset() } diff --git a/app/src/main/java/ch/epfl/sdp/ui/search_group/edition/UserViewHolder.kt b/app/src/main/java/ch/epfl/sdp/ui/search_group/edition/UserViewHolder.kt index d03de901f..c66b17d5d 100644 --- a/app/src/main/java/ch/epfl/sdp/ui/search_group/edition/UserViewHolder.kt +++ b/app/src/main/java/ch/epfl/sdp/ui/search_group/edition/UserViewHolder.kt @@ -18,6 +18,5 @@ class UserViewHolder(inflater: LayoutInflater, parent: ViewGroup) : //TODO change this to username name.text = user.email removeUserButton.setOnClickListener { onRemoveListener.onItemClicked(user) } - } } \ No newline at end of file