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 patrolRoute: List<String> = emptyList()
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")
override fun onCreate(savedInstanceState: Bundle?) {
@@ -196,6 +201,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
return
}
if (isAbort) {
isLeavingHomeBase = false
endNonSpecialTask("goTo aborted: $location, status=$status")
return
}
@@ -203,6 +209,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
if (lastArrivalLocation == location && now - lastArrivalAt < 5000L) {
return
}
isLeavingHomeBase = false
lastArrivalLocation = location
lastArrivalAt = now
prefs.edit().putString("current_location", location).apply()
@@ -244,7 +251,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
}
// 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
if (isSpecialModeEnabled() && currentTask.isEmpty()) {
Log.i("MainActivity", "Special task mode: Door logic skipped at Home Base.")
@@ -331,15 +338,20 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
setCurrentTask("")
}
fun startPatrolMode(route: List<String>) {
fun startPatrolMode(route: List<String>, times: Int = 1, waiting: Int = 3, nonStop: Boolean = false) {
if (route.isEmpty()) {
setCurrentTask("")
return
}
patrolRoute = route
patrolIndex = 0
patrolLoopsRemaining = times.coerceAtLeast(1)
patrolWaitingSeconds = waiting.coerceAtLeast(0)
patrolNonStop = nonStop
patrolMoveJob?.cancel()
setCurrentTask("patrol")
Log.i("MainActivity", "Patrol mode started: route=${route.joinToString()}")
moveToCurrentPatrolTarget()
}
private fun handlePatrolArrival(location: String) {
@@ -354,8 +366,50 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
patrolIndex = matchIndex + 1
}
if (patrolIndex >= patrolRoute.size) {
Log.i("MainActivity", "Patrol route completed: ${patrolRoute.joinToString()}")
setCurrentTask("")
patrolLoopsRemaining -= 1
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") {
patrolRoute = emptyList()
patrolIndex = 0
patrolLoopsRemaining = 1
patrolWaitingSeconds = 3
patrolNonStop = false
patrolMoveJob?.cancel()
patrolMoveJob = null
}
}

View File

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

View File

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