feat: 添加权限管理并增强导航功能
- 新增 PermissionManager 用于检查和请求 Temi 机器人权限 - 在 AndroidManifest 中声明所需的权限元数据 - 为 NavController 添加充电功能和改进的巡逻逻辑 - 扩展 MQTT 命令支持,包括充电和可配置巡逻 - 添加 Python 测试脚本用于 MQTT 流式文本测试 - 使用任务状态跟踪替代原有的接待模式标志
This commit is contained in:
@@ -20,6 +20,15 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.robotemi.sdk.metadata.SKILL"
|
android:name="com.robotemi.sdk.metadata.SKILL"
|
||||||
android:value="@string/app_name" />
|
android:value="@string/app_name" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="@string/metadata_permissions"
|
||||||
|
android:value="com.robotemi.permission.settings,
|
||||||
|
com.robotemi.permission.face_recognition,
|
||||||
|
com.robotemi.permission.map,
|
||||||
|
com.robotemi.permission.sequence,
|
||||||
|
com.robotemi.permission.meetings" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
@@ -31,6 +40,8 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".SettingsActivity" />
|
<activity android:name=".SettingsActivity" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<service android:name="org.eclipse.paho.android.service.MqttService" />
|
<service android:name="org.eclipse.paho.android.service.MqttService" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@@ -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.DETECTED
|
||||||
import com.robotemi.sdk.listeners.OnDetectionStateChangedListener.Companion.IDLE
|
import com.robotemi.sdk.listeners.OnDetectionStateChangedListener.Companion.IDLE
|
||||||
import com.robotemi.sdk.navigation.listener.OnReposeStatusChangedListener
|
import com.robotemi.sdk.navigation.listener.OnReposeStatusChangedListener
|
||||||
|
import com.robotemi.sdk.permission.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -25,7 +26,8 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnGoToLocationStatusChangedListener,
|
class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnGoToLocationStatusChangedListener,
|
||||||
OnDetectionStateChangedListener, OnReposeStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
OnDetectionStateChangedListener, OnReposeStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
|
OnRequestPermissionResultListener {
|
||||||
|
|
||||||
private lateinit var robot: Robot
|
private lateinit var robot: Robot
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
@@ -33,13 +35,14 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
private val mainScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
private val mainScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
private lateinit var prefs: SharedPreferences
|
private lateinit var prefs: SharedPreferences
|
||||||
private lateinit var navCon: NavController
|
private lateinit var navCon: NavController
|
||||||
|
private lateinit var permissionManager: PermissionManager
|
||||||
|
|
||||||
private var lastArrivalLocation: String? = null
|
private var lastArrivalLocation: String? = null
|
||||||
private var lastArrivalAt: Long = 0L
|
private var lastArrivalAt: Long = 0L
|
||||||
private val fixedFaceScale = 1.0f
|
private val fixedFaceScale = 1.0f
|
||||||
private val baseFaceSizeDp = 1000f
|
private val baseFaceSizeDp = 1000f
|
||||||
|
private var currentTask: String = ""
|
||||||
|
|
||||||
// Reception mode
|
|
||||||
private var isReceptionMode = false
|
|
||||||
private var receptionLocation: String = ""
|
private var receptionLocation: String = ""
|
||||||
private var receptionText: String = ""
|
private var receptionText: String = ""
|
||||||
private var receptionDestination: String = ""
|
private var receptionDestination: String = ""
|
||||||
@@ -54,6 +57,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
||||||
robot = Robot.getInstance()
|
robot = Robot.getInstance()
|
||||||
navCon = NavController(robot)
|
navCon = NavController(robot)
|
||||||
|
permissionManager = PermissionManager(robot)
|
||||||
|
|
||||||
prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
robot.addOnGoToLocationStatusChangedListener(this)
|
robot.addOnGoToLocationStatusChangedListener(this)
|
||||||
robot.addOnDetectionStateChangedListener(this)
|
robot.addOnDetectionStateChangedListener(this)
|
||||||
robot.addOnReposeStatusChangedListener(this)
|
robot.addOnReposeStatusChangedListener(this)
|
||||||
|
robot.addOnRequestPermissionResultListener(this)
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
prefs.registerOnSharedPreferenceChangeListener(this)
|
||||||
mqttManager?.connect()
|
mqttManager?.connect()
|
||||||
}
|
}
|
||||||
@@ -93,6 +98,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
robot.removeOnGoToLocationStatusChangedListener(this)
|
robot.removeOnGoToLocationStatusChangedListener(this)
|
||||||
robot.removeOnDetectionStateChangedListener(this)
|
robot.removeOnDetectionStateChangedListener(this)
|
||||||
robot.removeOnReposeStatusChangedListener(this)
|
robot.removeOnReposeStatusChangedListener(this)
|
||||||
|
robot.removeOnRequestPermissionResultListener(this)
|
||||||
prefs.unregisterOnSharedPreferenceChangeListener(this)
|
prefs.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
mqttManager?.disconnect()
|
mqttManager?.disconnect()
|
||||||
}
|
}
|
||||||
@@ -108,6 +114,21 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
override fun onRobotReady(isReady: Boolean) {
|
override fun onRobotReady(isReady: Boolean) {
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
Log.i("MainActivity", "Robot is ready!")
|
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)
|
mqttManager?.handleTtsStatusChange(ttsRequest)
|
||||||
when (ttsRequest.status) {
|
when (ttsRequest.status) {
|
||||||
TtsRequest.Status.STARTED -> {
|
TtsRequest.Status.STARTED -> {
|
||||||
Log.i("MainActivity", "TTS started: ${ttsRequest.speech}")
|
Log.i("MainActivity", "TTS started")
|
||||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.TALKING
|
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.TALKING
|
||||||
}
|
}
|
||||||
TtsRequest.Status.COMPLETED -> {
|
TtsRequest.Status.COMPLETED -> {
|
||||||
@@ -152,8 +173,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetectionStateChanged(state: Int) {
|
override fun onDetectionStateChanged(state: Int) {
|
||||||
// Reception mode logic
|
if (currentTask == "reception" && lastArrivalLocation == receptionLocation) {
|
||||||
if (isReceptionMode && lastArrivalLocation == receptionLocation) {
|
|
||||||
when (state) {
|
when (state) {
|
||||||
DETECTED -> {
|
DETECTED -> {
|
||||||
if (binding.btnReception.visibility != android.view.View.VISIBLE) {
|
if (binding.btnReception.visibility != android.view.View.VISIBLE) {
|
||||||
@@ -171,34 +191,34 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Home Base logic
|
// Home Base logic
|
||||||
if (lastArrivalLocation?.lowercase() == "home base") {
|
// if (lastArrivalLocation?.lowercase() == "home base") {
|
||||||
when (state) {
|
// when (state) {
|
||||||
DETECTED -> {
|
// DETECTED -> {
|
||||||
mainScope.launch {
|
// mainScope.launch {
|
||||||
HttpManager.workflow_execute(
|
// HttpManager.workflow_execute(
|
||||||
context = this@MainActivity,
|
// context = this@MainActivity,
|
||||||
apiKey = "wf_865e80f5fc1a4a319474a21d47470863",
|
// apiKey = "wf_865e80f5fc1a4a319474a21d47470863",
|
||||||
workflowId = "2031297462423851009",
|
// workflowId = "2031297462423851009",
|
||||||
inputs = emptyMap<String, Any>()
|
// inputs = emptyMap<String, Any>()
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
IDLE -> {
|
// IDLE -> {
|
||||||
mainScope.launch {
|
// mainScope.launch {
|
||||||
HttpManager.workflow_execute(
|
// HttpManager.workflow_execute(
|
||||||
context = this@MainActivity,
|
// context = this@MainActivity,
|
||||||
apiKey = "wf_c02aa853371345dbb29572641d083c24",
|
// apiKey = "wf_c02aa853371345dbb29572641d083c24",
|
||||||
workflowId = "2031634633458520065",
|
// workflowId = "2031634633458520065",
|
||||||
inputs = emptyMap<String, Any>()
|
// inputs = emptyMap<String, Any>()
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startReceptionMode(location: String, text: String, destination: String) {
|
fun startReceptionMode(location: String, text: String, destination: String) {
|
||||||
isReceptionMode = true
|
currentTask = "reception"
|
||||||
receptionLocation = location
|
receptionLocation = location
|
||||||
receptionText = text
|
receptionText = text
|
||||||
receptionDestination = destination
|
receptionDestination = destination
|
||||||
@@ -209,7 +229,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stopReceptionMode() {
|
private fun stopReceptionMode() {
|
||||||
isReceptionMode = false
|
currentTask = ""
|
||||||
receptionLocation = ""
|
receptionLocation = ""
|
||||||
receptionText = ""
|
receptionText = ""
|
||||||
receptionDestination = ""
|
receptionDestination = ""
|
||||||
@@ -217,6 +237,11 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
Log.i("MainActivity", "Reception mode stopped")
|
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) {
|
override fun onReposeStatusChanged(status: Int, description: String) {
|
||||||
when (status) {
|
when (status) {
|
||||||
OnReposeStatusChangedListener.REPOSING_COMPLETE -> {
|
OnReposeStatusChangedListener.REPOSING_COMPLETE -> {
|
||||||
@@ -266,4 +291,5 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
|||||||
binding.animatedEmojiView.layoutParams = params
|
binding.animatedEmojiView.layoutParams = params
|
||||||
binding.animatedEmojiView.requestLayout()
|
binding.animatedEmojiView.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,6 +161,10 @@ class MqttManager(
|
|||||||
private fun handleJsonCommand(obj: JSONObject) {
|
private fun handleJsonCommand(obj: JSONObject) {
|
||||||
val action = obj.optString("action", obj.optString("cmd", obj.optString("type", ""))).lowercase()
|
val action = obj.optString("action", obj.optString("cmd", obj.optString("type", ""))).lowercase()
|
||||||
when (action) {
|
when (action) {
|
||||||
|
"charge" -> {
|
||||||
|
speak("前往充电桩", "zh")
|
||||||
|
navController.charge()
|
||||||
|
}
|
||||||
"goto" -> {
|
"goto" -> {
|
||||||
val location = obj.optString("location", obj.optString("target", ""))
|
val location = obj.optString("location", obj.optString("target", ""))
|
||||||
goTo(location)
|
goTo(location)
|
||||||
@@ -184,7 +188,25 @@ class MqttManager(
|
|||||||
stopTts()
|
stopTts()
|
||||||
}
|
}
|
||||||
"patrol" -> {
|
"patrol" -> {
|
||||||
|
(context as? MainActivity)?.setCurrentTask("patrol")
|
||||||
|
speak("接到巡逻任务", "zh")
|
||||||
|
val flag = obj.optBoolean("flag", true)
|
||||||
|
if (flag) {
|
||||||
|
Log.d(TAG, "navController.randomPatrol() called.")
|
||||||
navController.randomPatrol()
|
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" -> {
|
"reception" -> {
|
||||||
val location = obj.optString("location", "前台")
|
val location = obj.optString("location", "前台")
|
||||||
@@ -230,9 +252,7 @@ class MqttManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stopTts() {
|
private fun stopTts() {
|
||||||
// Clear buffer
|
|
||||||
speechBuffer.setLength(0)
|
speechBuffer.setLength(0)
|
||||||
|
|
||||||
scope.launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
ttsQueue.clear()
|
ttsQueue.clear()
|
||||||
isTtsBusy = false
|
isTtsBusy = false
|
||||||
@@ -254,7 +274,6 @@ class MqttManager(
|
|||||||
private fun speak(text: String, langCode: String?) {
|
private fun speak(text: String, langCode: String?) {
|
||||||
val content = text.trim()
|
val content = text.trim()
|
||||||
if (content.isEmpty()) {
|
if (content.isEmpty()) {
|
||||||
// Log.w(TAG, "Speak ignored: empty text") // Too noisy for stream?
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val language = resolveLanguage(langCode)
|
val language = resolveLanguage(langCode)
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import com.robotemi.sdk.Robot
|
|||||||
|
|
||||||
class NavController(private val robot: Robot) {
|
class NavController(private val robot: Robot) {
|
||||||
private val TAG = "NavController"
|
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 {
|
fun goTo(location: String, backwards: Boolean = false): Boolean {
|
||||||
robot.goTo(location, backwards)
|
robot.goTo(location, backwards)
|
||||||
@@ -20,8 +27,12 @@ class NavController(private val robot: Robot) {
|
|||||||
return robot.locations
|
return robot.locations
|
||||||
}
|
}
|
||||||
|
|
||||||
fun patrol(locations: List<String>, nonStop: Boolean = false, times: Int = 1, waiting: Int = 3) {
|
fun NavPatrol(locations: List<String>, nonStop: Boolean = false, times: Int = 1, waiting: Int = 3) {
|
||||||
|
try {
|
||||||
robot.patrol(locations, nonStop, times, waiting)
|
robot.patrol(locations, nonStop, times, waiting)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "patrol() command failed.", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun repose(): Boolean {
|
fun repose(): Boolean {
|
||||||
@@ -31,12 +42,14 @@ class NavController(private val robot: Robot) {
|
|||||||
|
|
||||||
fun randomPatrol() {
|
fun randomPatrol() {
|
||||||
val allLocations = getAllLocations()
|
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
|
return
|
||||||
}
|
}
|
||||||
val patrolCount = (3..minOf(6, allLocations.size)).random()
|
val patrolCount = (3..minOf(6, availablePatrolLocations.size)).random()
|
||||||
val patrolLocations = allLocations.shuffled().take(patrolCount)
|
val patrolLocations = availablePatrolLocations.shuffled().take(patrolCount)
|
||||||
|
Log.i(TAG, "Starting random patrol with $patrolCount locations: ${patrolLocations.joinToString()}.")
|
||||||
patrol(patrolLocations, false, 1, 3)
|
NavPatrol(patrolLocations, false, 1, 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
test.py
Normal file
100
test.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user