merge commit

This commit is contained in:
Chen Li
2025-08-22 17:35:02 +08:00
parent 926f162287
commit e39f42a3df
6 changed files with 71 additions and 59 deletions

View File

@@ -68,7 +68,7 @@ public class MainActivity extends FlutterActivity implements INativeNuiCallback
.setMethodCallHandler((call, result) -> {
Map<String, Object> args = (Map<String, Object>) call.arguments;
switch (call.method) {
case "start":
case "startTts":
Object isChinese = args.get("isChinese");
if (isChinese == null || isChinese.toString().isBlank()) {
return;
@@ -76,17 +76,17 @@ public class MainActivity extends FlutterActivity implements INativeNuiCallback
boolean isSuccess = startTts(Boolean.parseBoolean(isChinese.toString()));
result.success(isSuccess);
break;
case "send":
case "sendTts":
Object textArg = args.get("text");
if (textArg == null || textArg.toString().isBlank()) {
return;
}
sendTts(textArg.toString());
break;
case "complete":
case "completeTts":
completeTts();
break;
case "stop":
case "stopTts":
stopTts();
break;
default:
@@ -97,10 +97,10 @@ public class MainActivity extends FlutterActivity implements INativeNuiCallback
asrMethodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), ASR_CHANNEL);
asrMethodChannel.setMethodCallHandler((call, result) -> {
switch (call.method) {
case "start":
case "startAsr":
startAsr();
break;
case "stop":
case "stopAsr":
stopAsr();
break;
default:

View File

@@ -18,6 +18,7 @@ class PartScreen extends StatefulWidget {
class _PartScreenState extends State<PartScreen> {
final ScrollController _scrollController = ScrollController();
bool _isInitialized = false;
int _lastMessageCount = 0;
@override
void initState() {
@@ -97,9 +98,13 @@ class _PartScreenState extends State<PartScreen> {
alignment: Alignment.bottomCenter,
child: Consumer<MessageService>(
builder: (context, messageService, child) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
final messageCount = messageService.messages.length;
if (messageCount > _lastMessageCount) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
}
_lastMessageCount = messageCount;
return Container(
width: chatWidth,
constraints: BoxConstraints(

View File

@@ -9,8 +9,7 @@ class VehicleCommandService {
Future<VehicleCommandResponse?> getCommandFromText(String text) async {
try {
final uri = Uri.parse(
'http://143.64.185.20:18606/control');
final uri = Uri.parse('http://143.64.185.20:18606/control');
final response = await http.post(
uri,
@@ -24,9 +23,9 @@ class VehicleCommandService {
final commandList = decoded['commands'];
List<VehicleCommand> vehicleCommandList = (commandList as List)
.map<VehicleCommand>((item) => VehicleCommand.fromString(
item['command'] as String,
item['params'] as Map<String, dynamic>?,
item['error'] ?? ''))
item['command'] as String,
item['params'] as Map<String, dynamic>?,
item['error'] ?? ''))
.toList();
return VehicleCommandResponse(tips: tips, commands: vehicleCommandList);
} else {
@@ -40,30 +39,23 @@ class VehicleCommandService {
}
}
// 执行车辆控制命令的方法
Future<bool> executeCommand(VehicleCommand command) async {
Future<String> getControlResponse(List<String> successCommandList) async {
String reply = "";
try {
final uri = Uri.parse('http://143.64.185.20:18607/executeCommand');
final uri = Uri.parse('http://143.64.185.20:18606/control_resp');
final response = await http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: json.encode({
'command': command.commandString, // 使用getter转换为字符串
'params': command.params,
}),
body: json.encode(successCommandList),
);
if (response.statusCode == 200) {
print('命令执行成功: ${command.commandString}');
return true;
return response.body;
} else {
print('命令执行失败: ${response.statusCode}, ${response.body}');
return false;
print("请求控制回复失败: ${response.statusCode}");
}
} catch (e) {
print('命令执行出错: $e');
return false;
print('请求控制回复异常: $e');
}
return reply;
}
}

View File

@@ -33,7 +33,10 @@ class MessageService extends ChangeNotifier {
_asrChannel.setMethodCallHandler((call) async {
switch (call.method) {
case "onAsrResult":
replaceMessage(id: _latestUserMessageId!, text: call.arguments);
replaceMessage(
id: _latestUserMessageId!,
text: call.arguments,
status: MessageStatus.normal);
break;
case "onAsrStop":
int index = findMessageIndexById(_latestUserMessageId!);
@@ -45,8 +48,6 @@ class MessageService extends ChangeNotifier {
removeMessageById(_latestUserMessageId!);
return;
}
replaceMessage(
id: _latestUserMessageId!, status: MessageStatus.normal);
if (_asrCompleter != null && !_asrCompleter!.isCompleted) {
_asrCompleter!.complete(messages.last.text);
}
@@ -60,7 +61,7 @@ class MessageService extends ChangeNotifier {
// final AudioRecorderService _audioService = AudioRecorderService();
// final VoiceRecognitionService _recognitionService = VoiceRecognitionService();
final TextClassificationService _classificationService =
TextClassificationService();
TextClassificationService();
final VehicleCommandService _vehicleCommandService = VehicleCommandService();
final List<ChatMessage> _messages = [];
@@ -107,9 +108,8 @@ class MessageService extends ChangeNotifier {
_latestAssistantMessageId = null;
_isReplyAborted = false;
changeState(MessageServiceState.recording);
addMessage("", true, MessageStatus.listening);
_latestUserMessageId = messages.last.id;
_asrChannel.invokeMethod("start");
_latestUserMessageId = addMessage("", true, MessageStatus.listening);
_asrChannel.invokeMethod("startAsr");
} catch (e) {
print('录音开始出错: $e');
}
@@ -120,14 +120,16 @@ class MessageService extends ChangeNotifier {
notifyListeners();
}
void addMessage(String text, bool isUser, MessageStatus status) {
String addMessage(String text, bool isUser, MessageStatus status) {
String uuid = Uuid().v1();
_messages.add(ChatMessage(
id: Uuid().v1(),
id: uuid,
text: text,
isUser: isUser,
timestamp: DateTime.now(),
status: status));
notifyListeners();
return uuid;
}
Future<void> stopAndProcessVoiceInput() async {
@@ -137,7 +139,7 @@ class MessageService extends ChangeNotifier {
}
try {
changeState(MessageServiceState.recognizing);
_asrChannel.invokeMethod("stop");
_asrChannel.invokeMethod("stopAsr");
_asrCompleter = Completer<String>();
final recognizedText = await _asrCompleter!.future;
// final audioData = await _audioService.stopRecording();
@@ -194,8 +196,7 @@ class MessageService extends ChangeNotifier {
}
Future<void> reply(String text) async {
addMessage("", false, MessageStatus.thinking);
_latestAssistantMessageId = messages.last.id;
_latestAssistantMessageId = addMessage("", false, MessageStatus.thinking);
bool isChinese = CommonUtil.containChinese(text);
try {
if (_isReplyAborted) {
@@ -226,7 +227,7 @@ class MessageService extends ChangeNotifier {
return;
}
final vehicleCommandResponse =
await _vehicleCommandService.getCommandFromText(text);
await _vehicleCommandService.getCommandFromText(text);
if (vehicleCommandResponse == null) {
if (_isReplyAborted) {
return;
@@ -249,9 +250,11 @@ class MessageService extends ChangeNotifier {
if (!_isReplyAborted) {
if (await TtsUtil.start(isChinese) == true) {
TtsUtil.send(vehicleCommandResponse.tips!);
Future.delayed(const Duration(milliseconds: 300), () => TtsUtil.complete());
}
}
bool containOpenAC = false;
List<String> successCommandList = [];
for (var command in vehicleCommandResponse.commands) {
if (_isReplyAborted) {
return;
@@ -263,11 +266,15 @@ class MessageService extends ChangeNotifier {
if (command.type == VehicleCommandType.openAC) {
containOpenAC = true;
}
bool isSuccess;
if (containOpenAC && command.type == VehicleCommandType.changeACTemp) {
await Future.delayed(const Duration(milliseconds: 2000),
() => processCommand(command, isChinese));
isSuccess = await Future.delayed(const Duration(milliseconds: 2000),
() => processCommand(command, isChinese));
} else {
await processCommand(command, isChinese);
isSuccess = await processCommand(command, isChinese);
}
if (isSuccess) {
successCommandList.add(command.type.name);
}
}
replaceMessage(
@@ -275,12 +282,21 @@ class MessageService extends ChangeNotifier {
text: vehicleCommandResponse.tips!,
status: MessageStatus.normal);
notifyListeners();
if (_isReplyAborted || successCommandList.isEmpty) {
return;
}
String controlResponse =
await _vehicleCommandService.getControlResponse(successCommandList);
if (_isReplyAborted || controlResponse.isEmpty) {
return;
}
addMessage(controlResponse, false, MessageStatus.normal);
}
}
Future<void> processCommand(VehicleCommand command, bool isChinese) async {
String msg;
MessageStatus status;
Future<bool> processCommand(VehicleCommand command, bool isChinese) async {
String msg = "";
MessageStatus status = MessageStatus.normal;
final (isSuccess, result) = await CommandService.executeCommand(
command.type,
params: command.params);
@@ -299,19 +315,17 @@ class MessageService extends ChangeNotifier {
}
}
} else {
if (isSuccess) {
msg = isChinese
? "为您执行\"${command.type.chinese}\"成功"
: "Execute \"${command.type.english}\" successful";
status = MessageStatus.success;
} else {
if (!isSuccess) {
msg = isChinese
? "很抱歉,\"${command.type.chinese}\"失败"
: "Sorry, execute \"${command.type.english}\" unsuccessfully";
status = MessageStatus.failure;
}
}
addMessage(msg, false, status);
if (msg.isNotEmpty) {
addMessage(msg, false, status);
}
return isSuccess;
}
Future<void> answerWrongQuestion(bool isChinese) async {

View File

@@ -10,18 +10,18 @@ class TtsUtil {
}
static Future<bool> start(bool isChinese) async {
return await execute('start', {'isChinese': isChinese});
return await execute('startTts', {'isChinese': isChinese});
}
static Future<void> send(String text) async {
await execute('send', {'text': text});
await execute('sendTts', {'text': text});
}
static Future<void> complete() async {
await execute('complete');
await execute('completeTts');
}
static Future<void> stop() async {
await execute('stop');
await execute('stopTts');
}
}

View File

@@ -70,6 +70,7 @@ class _FloatingIconState extends State<FloatingIcon>
}
void _showFullScreen() async {
_hidePartScreen();
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const FullScreen(),