feat(巡逻): 增强巡逻功能并支持循环、等待和非停止模式

- 在 MqttManager 中解析巡逻命令的 times、waiting 和 nonStop 参数
- 修改 MainActivity.startPatrolMode 以接收新参数并管理巡逻循环
- 实现 moveToCurrentPatrolTarget 和 scheduleNextPatrolMove 方法以支持连续巡逻
- 添加离开 Home Base 的状态跟踪以避免逻辑冲突
- 注释掉原有的 NavPatrol 调用以准备自定义巡逻实现
This commit is contained in:
2026-03-14 11:43:53 +08:00
parent 9756e71a23
commit 952c5234cf
3 changed files with 71 additions and 8 deletions

View File

@@ -56,6 +56,11 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
private var receptionDestination: String = "" private var receptionDestination: String = ""
private var patrolRoute: List<String> = emptyList() private var patrolRoute: List<String> = emptyList()
private var patrolIndex: Int = 0 private var patrolIndex: Int = 0
private var patrolLoopsRemaining: Int = 1
private var patrolWaitingSeconds: Int = 3
private var patrolNonStop: Boolean = false
private var patrolMoveJob: Job? = null
private var isLeavingHomeBase: Boolean = false
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -196,6 +201,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
return return
} }
if (isAbort) { if (isAbort) {
isLeavingHomeBase = false
endNonSpecialTask("goTo aborted: $location, status=$status") endNonSpecialTask("goTo aborted: $location, status=$status")
return return
} }
@@ -203,6 +209,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
if (lastArrivalLocation == location && now - lastArrivalAt < 5000L) { if (lastArrivalLocation == location && now - lastArrivalAt < 5000L) {
return return
} }
isLeavingHomeBase = false
lastArrivalLocation = location lastArrivalLocation = location
lastArrivalAt = now lastArrivalAt = now
prefs.edit().putString("current_location", location).apply() prefs.edit().putString("current_location", location).apply()
@@ -244,7 +251,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
} }
// Home Base logic // Home Base logic
if (lastArrivalLocation?.lowercase() == "home base") { if (currentTask == "" && lastArrivalLocation?.lowercase() == "home base" && !isLeavingHomeBase) {
// Check if special task mode is enabled, if so, skip door logic // Check if special task mode is enabled, if so, skip door logic
if (isSpecialModeEnabled() && currentTask.isEmpty()) { if (isSpecialModeEnabled() && currentTask.isEmpty()) {
Log.i("MainActivity", "Special task mode: Door logic skipped at Home Base.") Log.i("MainActivity", "Special task mode: Door logic skipped at Home Base.")
@@ -331,15 +338,20 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
setCurrentTask("") setCurrentTask("")
} }
fun startPatrolMode(route: List<String>) { fun startPatrolMode(route: List<String>, times: Int = 1, waiting: Int = 3, nonStop: Boolean = false) {
if (route.isEmpty()) { if (route.isEmpty()) {
setCurrentTask("") setCurrentTask("")
return return
} }
patrolRoute = route patrolRoute = route
patrolIndex = 0 patrolIndex = 0
patrolLoopsRemaining = times.coerceAtLeast(1)
patrolWaitingSeconds = waiting.coerceAtLeast(0)
patrolNonStop = nonStop
patrolMoveJob?.cancel()
setCurrentTask("patrol") setCurrentTask("patrol")
Log.i("MainActivity", "Patrol mode started: route=${route.joinToString()}") Log.i("MainActivity", "Patrol mode started: route=${route.joinToString()}")
moveToCurrentPatrolTarget()
} }
private fun handlePatrolArrival(location: String) { private fun handlePatrolArrival(location: String) {
@@ -354,8 +366,50 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
patrolIndex = matchIndex + 1 patrolIndex = matchIndex + 1
} }
if (patrolIndex >= patrolRoute.size) { if (patrolIndex >= patrolRoute.size) {
Log.i("MainActivity", "Patrol route completed: ${patrolRoute.joinToString()}") patrolLoopsRemaining -= 1
setCurrentTask("") if (patrolLoopsRemaining <= 0) {
Log.i("MainActivity", "Patrol route completed: ${patrolRoute.joinToString()}")
setCurrentTask("")
return
}
patrolIndex = 0
}
scheduleNextPatrolMove()
}
private fun moveToCurrentPatrolTarget() {
if (currentTask != "patrol") {
return
}
val target = patrolRoute.getOrNull(patrolIndex) ?: return
if (lastArrivalLocation?.equals(target, ignoreCase = true) == true) {
patrolIndex += 1
if (patrolIndex >= patrolRoute.size) {
Log.i("MainActivity", "Patrol route completed: ${patrolRoute.joinToString()}")
setCurrentTask("")
return
}
moveToCurrentPatrolTarget()
return
}
Log.i("MainActivity", "Patrol moving to next target: $target")
if (lastArrivalLocation?.equals("home base", ignoreCase = true) == true &&
!target.equals("home base", ignoreCase = true)
) {
isLeavingHomeBase = true
}
navCon.goTo(target, false)
}
private fun scheduleNextPatrolMove() {
patrolMoveJob?.cancel()
if (patrolNonStop || patrolWaitingSeconds <= 0) {
moveToCurrentPatrolTarget()
return
}
patrolMoveJob = mainScope.launch {
delay(patrolWaitingSeconds * 1000L)
moveToCurrentPatrolTarget()
} }
} }
@@ -377,6 +431,11 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
if (finalTask != "patrol") { if (finalTask != "patrol") {
patrolRoute = emptyList() patrolRoute = emptyList()
patrolIndex = 0 patrolIndex = 0
patrolLoopsRemaining = 1
patrolWaitingSeconds = 3
patrolNonStop = false
patrolMoveJob?.cancel()
patrolMoveJob = null
} }
} }

View File

@@ -229,6 +229,9 @@ class MqttManager(
"patrol" -> { "patrol" -> {
speak("接到巡逻任务", "zh") speak("接到巡逻任务", "zh")
val flag = obj.optBoolean("flag", true) val flag = obj.optBoolean("flag", true)
val times = obj.optInt("times", 1)
val waiting = obj.optInt("waiting", obj.optInt("wait", 3))
val nonStop = obj.optBoolean("nonStop", obj.optBoolean("non_stop", false))
var patrolLocations: List<String> = emptyList() var patrolLocations: List<String> = emptyList()
if (flag) { if (flag) {
Log.d(TAG, "navController.randomPatrol() called.") Log.d(TAG, "navController.randomPatrol() called.")
@@ -239,8 +242,8 @@ class MqttManager(
val locations = List(locationsArray.length()) { val locations = List(locationsArray.length()) {
locationsArray.getString(it) locationsArray.getString(it)
} }
Log.d(TAG, "navController.NavPatrol() called with locations: $locations") Log.d(TAG, "Patrol route received with locations: $locations")
navController.NavPatrol(locations) // navController.NavPatrol(locations)
patrolLocations = locations patrolLocations = locations
} else { } else {
Log.w(TAG, "Patrol command received without locations, falling back to random patrol.") Log.w(TAG, "Patrol command received without locations, falling back to random patrol.")
@@ -250,7 +253,7 @@ class MqttManager(
scope.launch(Dispatchers.Main) { scope.launch(Dispatchers.Main) {
val activity = context as? MainActivity val activity = context as? MainActivity
if (patrolLocations.isNotEmpty()) { if (patrolLocations.isNotEmpty()) {
activity?.startPatrolMode(patrolLocations) activity?.startPatrolMode(patrolLocations, times, waiting, nonStop)
} else { } else {
activity?.setCurrentTask("") activity?.setCurrentTask("")
} }

View File

@@ -50,7 +50,8 @@ class NavController(private val robot: Robot) {
val patrolCount = (3..minOf(6, availablePatrolLocations.size)).random() val patrolCount = (3..minOf(6, availablePatrolLocations.size)).random()
val patrolLocations = availablePatrolLocations.shuffled().take(patrolCount) val patrolLocations = availablePatrolLocations.shuffled().take(patrolCount)
Log.i(TAG, "Starting random patrol with $patrolCount locations: ${patrolLocations.joinToString()}.") Log.i(TAG, "Starting random patrol with $patrolCount locations: ${patrolLocations.joinToString()}.")
NavPatrol(patrolLocations, false, 1, 5) // 官方 patrol 调用保留(部分版本可能无效)
// NavPatrol(patrolLocations, false, 1, 5)
return patrolLocations return patrolLocations
} }
} }