diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f106cca..93e831d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,15 @@ + + + @@ -31,6 +40,8 @@ + + diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt index 323d9a1..3b73f03 100644 --- a/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt @@ -18,6 +18,7 @@ import com.robotemi.sdk.listeners.OnRobotReadyListener import com.robotemi.sdk.listeners.OnDetectionStateChangedListener.Companion.DETECTED import com.robotemi.sdk.listeners.OnDetectionStateChangedListener.Companion.IDLE import com.robotemi.sdk.navigation.listener.OnReposeStatusChangedListener +import com.robotemi.sdk.permission.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -25,7 +26,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.cancel class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnGoToLocationStatusChangedListener, - OnDetectionStateChangedListener, OnReposeStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener { + OnDetectionStateChangedListener, OnReposeStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener, + OnRequestPermissionResultListener { private lateinit var robot: Robot private lateinit var binding: ActivityMainBinding @@ -33,13 +35,14 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG private val mainScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) private lateinit var prefs: SharedPreferences private lateinit var navCon: NavController + private lateinit var permissionManager: PermissionManager + private var lastArrivalLocation: String? = null private var lastArrivalAt: Long = 0L private val fixedFaceScale = 1.0f private val baseFaceSizeDp = 1000f + private var currentTask: String = "" - // Reception mode - private var isReceptionMode = false private var receptionLocation: String = "" private var receptionText: String = "" private var receptionDestination: String = "" @@ -54,6 +57,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) robot = Robot.getInstance() navCon = NavController(robot) + permissionManager = PermissionManager(robot) prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE) @@ -82,6 +86,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG robot.addOnGoToLocationStatusChangedListener(this) robot.addOnDetectionStateChangedListener(this) robot.addOnReposeStatusChangedListener(this) + robot.addOnRequestPermissionResultListener(this) prefs.registerOnSharedPreferenceChangeListener(this) mqttManager?.connect() } @@ -93,6 +98,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG robot.removeOnGoToLocationStatusChangedListener(this) robot.removeOnDetectionStateChangedListener(this) robot.removeOnReposeStatusChangedListener(this) + robot.removeOnRequestPermissionResultListener(this) prefs.unregisterOnSharedPreferenceChangeListener(this) mqttManager?.disconnect() } @@ -108,6 +114,21 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG override fun onRobotReady(isReady: Boolean) { if (isReady) { Log.i("MainActivity", "Robot is ready!") + permissionManager.checkAndRequestPermissions() + } + } + + override fun onRequestPermissionResult( + permission: Permission, + grantResult: Int, + requestCode: Int + ) { + if (requestCode == PermissionManager.REQUEST_CODE_TEMI_PERMISSIONS) { + if (grantResult == Permission.GRANTED) { + Log.i("MainActivity", "Temi permission GRANTED: $permission") + } else { + Log.w("MainActivity", "Temi permission DENIED: $permission") + } } } @@ -115,7 +136,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG mqttManager?.handleTtsStatusChange(ttsRequest) when (ttsRequest.status) { TtsRequest.Status.STARTED -> { - Log.i("MainActivity", "TTS started: ${ttsRequest.speech}") + Log.i("MainActivity", "TTS started") binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.TALKING } TtsRequest.Status.COMPLETED -> { @@ -152,8 +173,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG } override fun onDetectionStateChanged(state: Int) { - // Reception mode logic - if (isReceptionMode && lastArrivalLocation == receptionLocation) { + if (currentTask == "reception" && lastArrivalLocation == receptionLocation) { when (state) { DETECTED -> { if (binding.btnReception.visibility != android.view.View.VISIBLE) { @@ -171,34 +191,34 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG } // Home Base logic - if (lastArrivalLocation?.lowercase() == "home base") { - when (state) { - DETECTED -> { - mainScope.launch { - HttpManager.workflow_execute( - context = this@MainActivity, - apiKey = "wf_865e80f5fc1a4a319474a21d47470863", - workflowId = "2031297462423851009", - inputs = emptyMap() - ) - } - } - IDLE -> { - mainScope.launch { - HttpManager.workflow_execute( - context = this@MainActivity, - apiKey = "wf_c02aa853371345dbb29572641d083c24", - workflowId = "2031634633458520065", - inputs = emptyMap() - ) - } - } - } - } + // if (lastArrivalLocation?.lowercase() == "home base") { + // when (state) { + // DETECTED -> { + // mainScope.launch { + // HttpManager.workflow_execute( + // context = this@MainActivity, + // apiKey = "wf_865e80f5fc1a4a319474a21d47470863", + // workflowId = "2031297462423851009", + // inputs = emptyMap() + // ) + // } + // } + // IDLE -> { + // mainScope.launch { + // HttpManager.workflow_execute( + // context = this@MainActivity, + // apiKey = "wf_c02aa853371345dbb29572641d083c24", + // workflowId = "2031634633458520065", + // inputs = emptyMap() + // ) + // } + // } + // } + // } } fun startReceptionMode(location: String, text: String, destination: String) { - isReceptionMode = true + currentTask = "reception" receptionLocation = location receptionText = text receptionDestination = destination @@ -209,7 +229,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG } private fun stopReceptionMode() { - isReceptionMode = false + currentTask = "" receptionLocation = "" receptionText = "" receptionDestination = "" @@ -217,6 +237,11 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG Log.i("MainActivity", "Reception mode stopped") } + fun setCurrentTask(task: String) { + currentTask = task + Log.i("MainActivity", "Current task set to: $task") + } + override fun onReposeStatusChanged(status: Int, description: String) { when (status) { OnReposeStatusChangedListener.REPOSING_COMPLETE -> { @@ -266,4 +291,5 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG binding.animatedEmojiView.layoutParams = params binding.animatedEmojiView.requestLayout() } + } diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt index 5de334d..c793f99 100644 --- a/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt @@ -161,6 +161,10 @@ class MqttManager( private fun handleJsonCommand(obj: JSONObject) { val action = obj.optString("action", obj.optString("cmd", obj.optString("type", ""))).lowercase() when (action) { + "charge" -> { + speak("前往充电桩", "zh") + navController.charge() + } "goto" -> { val location = obj.optString("location", obj.optString("target", "")) goTo(location) @@ -184,7 +188,25 @@ class MqttManager( stopTts() } "patrol" -> { - navController.randomPatrol() + (context as? MainActivity)?.setCurrentTask("patrol") + speak("接到巡逻任务", "zh") + val flag = obj.optBoolean("flag", true) + if (flag) { + Log.d(TAG, "navController.randomPatrol() called.") + navController.randomPatrol() + } else { + val locationsArray = obj.optJSONArray("locations") + if (locationsArray != null && locationsArray.length() > 0) { + val locations = List(locationsArray.length()) { + locationsArray.getString(it) + } + Log.d(TAG, "navController.NavPatrol() called with locations: $locations") + navController.NavPatrol(locations) + } else { + Log.w(TAG, "Patrol command received without locations, falling back to random patrol.") + navController.randomPatrol() + } + } } "reception" -> { val location = obj.optString("location", "前台") @@ -230,9 +252,7 @@ class MqttManager( } private fun stopTts() { - // Clear buffer speechBuffer.setLength(0) - scope.launch(Dispatchers.Main) { ttsQueue.clear() isTtsBusy = false @@ -254,7 +274,6 @@ class MqttManager( private fun speak(text: String, langCode: String?) { val content = text.trim() if (content.isEmpty()) { - // Log.w(TAG, "Speak ignored: empty text") // Too noisy for stream? return } val language = resolveLanguage(langCode) diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt index 0436020..0515873 100644 --- a/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt @@ -5,6 +5,13 @@ import com.robotemi.sdk.Robot class NavController(private val robot: Robot) { private val TAG = "NavController" + private var playmode = false + + fun charge(): Boolean { + playmode = !playmode + robot.goTo("home base", playmode) + return true + } fun goTo(location: String, backwards: Boolean = false): Boolean { robot.goTo(location, backwards) @@ -20,8 +27,12 @@ class NavController(private val robot: Robot) { return robot.locations } - fun patrol(locations: List, nonStop: Boolean = false, times: Int = 1, waiting: Int = 3) { - robot.patrol(locations, nonStop, times, waiting) + fun NavPatrol(locations: List, nonStop: Boolean = false, times: Int = 1, waiting: Int = 3) { + try { + robot.patrol(locations, nonStop, times, waiting) + } catch (e: Exception) { + Log.e(TAG, "patrol() command failed.", e) + } } fun repose(): Boolean { @@ -31,12 +42,14 @@ class NavController(private val robot: Robot) { fun randomPatrol() { val allLocations = getAllLocations() - if (allLocations.size < 3) { + val availablePatrolLocations = allLocations.filter { !it.equals("home base", ignoreCase = true) } + if (availablePatrolLocations.size < 3) { + Log.w(TAG, "Patrol command ignored: Not enough valid locations (excluding home base). Need at least 3, but found ${availablePatrolLocations.size}.") return } - val patrolCount = (3..minOf(6, allLocations.size)).random() - val patrolLocations = allLocations.shuffled().take(patrolCount) - - patrol(patrolLocations, false, 1, 3) + 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) } } diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/PermissionManager.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/PermissionManager.kt new file mode 100644 index 0000000..2672bdb --- /dev/null +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/PermissionManager.kt @@ -0,0 +1,35 @@ +package com.example.lzwcai_terminal_temi + +import android.util.Log +import com.robotemi.sdk.Robot +import com.robotemi.sdk.permission.Permission + +class PermissionManager(private val robot: Robot) { + + private val TAG = "PermissionManager" + + companion object { + const val REQUEST_CODE_TEMI_PERMISSIONS = 101 + } + + private val requiredPermissions = listOf( + Permission.MAP, + Permission.SEQUENCE, + Permission.FACE_RECOGNITION, + Permission.SETTINGS + ) + + fun checkAndRequestPermissions() { + + val neededPermissions = requiredPermissions.filter { + robot.checkSelfPermission(it) != Permission.GRANTED + } + + if (neededPermissions.isNotEmpty()) { + Log.i(TAG, "Requesting Temi permissions: $neededPermissions") + robot.requestPermissions(neededPermissions, REQUEST_CODE_TEMI_PERMISSIONS) + } else { + Log.i(TAG, "All required Temi permissions are already granted.") + } + } +} diff --git a/test.py b/test.py new file mode 100644 index 0000000..24cffd1 --- /dev/null +++ b/test.py @@ -0,0 +1,100 @@ +import paho.mqtt.client as mqtt +import json +import time +import sys +import random + +# --- MQTT 配置 --- +# 重要:请将这里替换为您的 MQTT 代理的 IP 地址 +BROKER_IP = "192.168.2.236" +BROKER_PORT = 1883 +MQTT_TOPIC = "robot/cmd" +MQTT_USERNAME = "lzwc" +MQTT_PASSWORD = "Lzwc@4187." + +# --- 要进行流式传输的文本 --- +# 您可以更改此文本 +TEXT_TO_STREAM = "你好,我是一个先进的人工智能助理。我的设计目标是理解并生成自然语言,从而能够与人类进行流畅的对话。我可以回答问题、提供信息、撰写文章,甚至进行一些基础的编程任务。这个流式传输演示旨在展示我如何将长篇回复分解成小的数据块,并实时发送给客户端,从而创造出一种更具互动性和即时性的体验。希望这次演示能够清晰地展示我的能力。" + +# --- 流媒体参数 --- +# 每个数据块中发送的字符长度范围 +MIN_CHUNK_SIZE = 2 +MAX_CHUNK_SIZE = 8 +# 块之间的延迟(秒) +STREAM_DELAY = 0.1 + +def on_connect(client, userdata, flags, rc): + """客户端连接到代理时的回调。""" + if rc == 0: + print("成功连接到 MQTT 代理!") + else: + print(f"连接失败, 返回码 {rc}\n") + sys.exit(1) + +def on_publish(client, userdata, mid): + """消息发布时的回调。""" + # 默认注释掉,因为可能会产生大量输出 + # print(f"已发布消息,mid: {mid}") + pass + +def create_mqtt_client(): + """创建并配置 MQTT 客户端。""" + client = mqtt.Client() + client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD) + client.on_connect = on_connect + client.on_publish = on_publish + return client + +def stream_text(client, text_to_stream): + """将给定的文本以数据块的形式流式传输到 MQTT 主题。""" + print(f"开始向主题 '{MQTT_TOPIC}' 流式传输文本:") + print(f"文本: {text_to_stream}") + + index = 0 + while index < len(text_to_stream): + # 生成一个随机的块大小 + chunk_size = random.randint(MIN_CHUNK_SIZE, MAX_CHUNK_SIZE) + # 获取块,确保不会超出文本长度 + chunk = text_to_stream[index:index + chunk_size] + index += len(chunk) + + # 创建 JSON 负载 + payload = { + "action": "stream", + "text": chunk, + "lang": "zh" # 假设是中文,如果需要可以更改 + } + payload_json = json.dumps(payload, ensure_ascii=False) + + # 发布消息 + result = client.publish(MQTT_TOPIC, payload_json) + + # 检查发布是否成功 + if result.rc != mqtt.MQTT_ERR_SUCCESS: + print(f"发送消息失败: {mqtt.error_string(result.rc)}") + else: + print(f"已发送块: '{chunk}'") + + # 等待一小段时间以模拟流式传输 + time.sleep(STREAM_DELAY) + + print("\n流式传输完成。") + +def main(): + """运行 MQTT 流式脚本的主函数。""" + client = create_mqtt_client() + + try: + client.connect(BROKER_IP, BROKER_PORT) + except Exception as e: + print(f"连接到代理 {BROKER_IP}:{BROKER_PORT} 时出错。请检查 IP 地址。") + print(f"错误: {e}") + sys.exit(1) + + stream_text(client, TEXT_TO_STREAM) + + client.disconnect() + print("已从 MQTT 代理断开。") + +if __name__ == "__main__": + main()