优化和路径规划新适配
This commit is contained in:
@@ -70,7 +70,10 @@ class _DroneOsdCardState extends State<DroneOsdCard> {
|
||||
_subscription = _dataSource.droneOsdStream.listen((osd) {
|
||||
if (!mounted) return;
|
||||
|
||||
debugPrint('📥 [DroneOsdCard] 收到无人机 OSD 数据');
|
||||
// 🔥 打印完整的 MQTT 原始数据(不做任何解析)
|
||||
debugPrint('\n========== 📥 [无人机OSD] 完整原始数据 ==========');
|
||||
debugPrint('${osd.rawData}');
|
||||
debugPrint('===========================================\n');
|
||||
|
||||
setState(() {
|
||||
_currentOsd = osd;
|
||||
@@ -115,7 +118,9 @@ class _DroneOsdCardState extends State<DroneOsdCard> {
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('📊 [DroneOsdCard] 数据键: ${droneData.keys.toList()}');
|
||||
// 🔥 打印完整原始数据(用于调试)
|
||||
debugPrint('📊 [DroneOsdCard] drone/host 数据键: ${droneData.keys.toList()}');
|
||||
debugPrint('📊 [DroneOsdCard] 完整原始数据: $droneData');
|
||||
|
||||
// ========== 1. 基础飞行信息 ==========
|
||||
// 无人机高度(height / altitude)
|
||||
@@ -321,28 +326,28 @@ class _DroneOsdCardState extends State<DroneOsdCard> {
|
||||
? const Color(0xFF00B42A)
|
||||
: const Color(0xFFFF7D00),
|
||||
},
|
||||
{
|
||||
'icon': Icons.signal_cellular_alt,
|
||||
'label': '遥控信号',
|
||||
'key': 'rcSignal',
|
||||
'newValue': rcSignal != null ? '$rcSignal%' : null,
|
||||
'color': rcSignal != null && rcSignal > 80
|
||||
? const Color(0xFF00B42A)
|
||||
: rcSignal != null && rcSignal > 50
|
||||
? const Color(0xFFFF7D00)
|
||||
: const Color(0xFFF53F3F),
|
||||
},
|
||||
{
|
||||
'icon': Icons.video_label,
|
||||
'label': '图传信号',
|
||||
'key': 'videoSignal',
|
||||
'newValue': videoSignal != null ? '$videoSignal%' : null,
|
||||
'color': videoSignal != null && videoSignal > 80
|
||||
? const Color(0xFF00B42A)
|
||||
: videoSignal != null && videoSignal > 50
|
||||
? const Color(0xFFFF7D00)
|
||||
: const Color(0xFFF53F3F),
|
||||
},
|
||||
// {
|
||||
// 'icon': Icons.signal_cellular_alt,
|
||||
// 'label': '遥控信号',
|
||||
// 'key': 'rcSignal',
|
||||
// 'newValue': rcSignal != null ? '$rcSignal%' : null,
|
||||
// 'color': rcSignal != null && rcSignal > 80
|
||||
// ? const Color(0xFF00B42A)
|
||||
// : rcSignal != null && rcSignal > 50
|
||||
// ? const Color(0xFFFF7D00)
|
||||
// : const Color(0xFFF53F3F),
|
||||
// },
|
||||
// {
|
||||
// 'icon': Icons.video_label,
|
||||
// 'label': '图传信号',
|
||||
// 'key': 'videoSignal',
|
||||
// 'newValue': videoSignal != null ? '$videoSignal%' : null,
|
||||
// 'color': videoSignal != null && videoSignal > 80
|
||||
// ? const Color(0xFF00B42A)
|
||||
// : videoSignal != null && videoSignal > 50
|
||||
// ? const Color(0xFFFF7D00)
|
||||
// : const Color(0xFFF53F3F),
|
||||
// },
|
||||
{
|
||||
'icon': Icons.flight,
|
||||
'label': '飞行模式',
|
||||
@@ -350,22 +355,22 @@ class _DroneOsdCardState extends State<DroneOsdCard> {
|
||||
'newValue': flightMode,
|
||||
'color': const Color(0xFF165DFF),
|
||||
},
|
||||
{
|
||||
'icon': Icons.radio_button_checked,
|
||||
'label': '任务进度',
|
||||
'key': 'mission',
|
||||
'newValue': missionProgress != null ? '$missionProgress%' : null,
|
||||
'color': const Color(0xFF00B42A),
|
||||
},
|
||||
{
|
||||
'icon': Icons.map,
|
||||
'label': '航点',
|
||||
'key': 'waypoint',
|
||||
'newValue': (currentWaypoint != null && waypointCount != null)
|
||||
? '$currentWaypoint/$waypointCount'
|
||||
: null,
|
||||
'color': const Color(0xFF722ED1),
|
||||
},
|
||||
// {
|
||||
// 'icon': Icons.radio_button_checked,
|
||||
// 'label': '任务进度',
|
||||
// 'key': 'mission',
|
||||
// 'newValue': missionProgress != null ? '$missionProgress%' : null,
|
||||
// 'color': const Color(0xFF00B42A),
|
||||
// },
|
||||
// {
|
||||
// 'icon': Icons.map,
|
||||
// 'label': '航点',
|
||||
// 'key': 'waypoint',
|
||||
// 'newValue': (currentWaypoint != null && waypointCount != null)
|
||||
// ? '$currentWaypoint/$waypointCount'
|
||||
// : null,
|
||||
// 'color': const Color(0xFF722ED1),
|
||||
// },
|
||||
];
|
||||
|
||||
// 应用缓存逻辑:有新值则更新缓存,否则使用旧值
|
||||
|
||||
@@ -135,10 +135,9 @@ class DroneStationItemCard extends StatelessWidget {
|
||||
// ],
|
||||
],
|
||||
),
|
||||
//
|
||||
// const SizedBox(height: 12),
|
||||
//
|
||||
// // 电量卡片
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// 🔥 电量状态 - 已隐藏
|
||||
// _buildInfoCard(
|
||||
// title: '电量状态',
|
||||
// icon: Icons.battery_full_rounded,
|
||||
@@ -146,10 +145,10 @@ class DroneStationItemCard extends StatelessWidget {
|
||||
// value: station.capacityPercent != null ? '${station.capacityPercent}%' : '未知',
|
||||
// progressValue: station.capacityPercent,
|
||||
// ),
|
||||
//
|
||||
|
||||
// const SizedBox(height: 12),
|
||||
//
|
||||
// // 环境信息卡片
|
||||
|
||||
// 🔥 环境监测 - 已隐藏
|
||||
// _buildSectionCard(
|
||||
// title: '环境监测',
|
||||
// icon: Icons.thermostat_rounded,
|
||||
|
||||
@@ -66,7 +66,10 @@ class _DroneStationOsdCardState extends State<DroneStationOsdCard> {
|
||||
_subscription = _dataSource.stationOsdStream.listen((osd) {
|
||||
if (!mounted) return;
|
||||
|
||||
//debugPrint('📥 [DroneStationOsdCard] 收到机场 OSD 原始数据');
|
||||
// 🔥 打印完整的 MQTT 原始数据(不做任何解析)
|
||||
debugPrint('\n========== 📥 [机场OSD] 完整原始数据 ==========');
|
||||
debugPrint('${osd.rawData}');
|
||||
debugPrint('===========================================\n');
|
||||
|
||||
setState(() {
|
||||
_currentOsd = osd;
|
||||
@@ -95,8 +98,11 @@ class _DroneStationOsdCardState extends State<DroneStationOsdCard> {
|
||||
return;
|
||||
}
|
||||
|
||||
// debugPrint('📊 [DroneStationOsdCard] 开始解析 OSD 数据...');
|
||||
//debugPrint('📊 [DroneStationOsdCard] host 数据键: ${hostData.keys.toList()}');
|
||||
// 🔥 打印完整的 host 数据键,查看所有可用字段
|
||||
debugPrint('📊 [DroneStationOsdCard] host 数据键: ${hostData.keys.toList()}');
|
||||
|
||||
// 🔥 打印完整原始数据(用于调试)
|
||||
debugPrint('📊 [DroneStationOsdCard] 完整原始数据: $hostData');
|
||||
|
||||
// ========== 1. 电池相关 ==========
|
||||
// 提取无人机电量(从 drone_battery_maintenance_info.batteries[0].capacity_percent)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
|
||||
import 'package:maibu_satabot_v2/core/app/app_user_state.dart'; // 🔥 导入 AppUserState
|
||||
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
|
||||
import 'package:maibu_satabot_v2/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart';
|
||||
|
||||
@@ -15,7 +16,6 @@ class RobotHeaderCard extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RobotHeaderCardState extends State<RobotHeaderCard> {
|
||||
String _videoStreamUrl = '';
|
||||
int _currentViewIndex = 0; // 0=前, 1=后, 2=左, 3=右, 4=上
|
||||
|
||||
// 视角配置
|
||||
@@ -30,30 +30,7 @@ class _RobotHeaderCardState extends State<RobotHeaderCard> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initVideoUrl();
|
||||
}
|
||||
|
||||
/// 初始化视频流 URL
|
||||
void _initVideoUrl() {
|
||||
final userState = context.read<AppUserCubit>().state;
|
||||
final deviceId = widget.robot['id'] as String?;
|
||||
|
||||
debugPrint(' [RobotHeaderCard] 开始初始化视频URL');
|
||||
debugPrint('🎬 [RobotHeaderCard] deviceId: $deviceId');
|
||||
debugPrint('🎬 [RobotHeaderCard] user: ${userState.user}');
|
||||
debugPrint('🎬 [RobotHeaderCard] token: ${userState.user?.token}');
|
||||
|
||||
if (deviceId != null && deviceId.isNotEmpty && userState.user != null && userState.user!.token != null) {
|
||||
setState(() {
|
||||
_videoStreamUrl = "webrtc://${TCPConsts.TCP_IP}/live/livestream/$deviceId?token=${userState.user!.token}";
|
||||
});
|
||||
debugPrint('✅ [RobotHeaderCard] 视频URL初始化成功: $_videoStreamUrl');
|
||||
} else {
|
||||
debugPrint('❌ [RobotHeaderCard] 视频URL初始化失败');
|
||||
debugPrint(' - deviceId.isEmpty: ${deviceId == null || deviceId.isEmpty}');
|
||||
debugPrint(' - user == null: ${userState.user == null}');
|
||||
debugPrint(' - token == null: ${userState.user?.token == null}');
|
||||
}
|
||||
debugPrint('🎬 [RobotHeaderCard] 初始化 - robot: ${widget.robot}');
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -147,26 +124,59 @@ class _RobotHeaderCardState extends State<RobotHeaderCard> {
|
||||
aspectRatio: 16 / 9,
|
||||
child: Container(
|
||||
color: Colors.black, // 🔥 强制整个视频区域为黑色背景
|
||||
child: _videoStreamUrl.isNotEmpty
|
||||
? WebRTCLocalPlayer(
|
||||
streamUrl: _videoStreamUrl,
|
||||
showLeftPip: false, // 不显示悬浮小窗
|
||||
showRightPip: false,
|
||||
isFrontMain: _currentViewIndex == 0, // 根据当前视角决定主画面
|
||||
)
|
||||
: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.videocam_off, size: 48, color: Colors.white54),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'无视频信号',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 14, fontWeight: FontWeight.w500),
|
||||
child: BlocBuilder<AppUserCubit, AppUserState>(
|
||||
builder: (context, userState) {
|
||||
final deviceId = widget.robot['name'] as String?; // 🔥 使用 name 字段(长序列号)
|
||||
String videoStreamUrl = '';
|
||||
|
||||
debugPrint('🎬 [RobotHeaderCard] BlocBuilder 重建');
|
||||
debugPrint(' - deviceId (name): $deviceId');
|
||||
debugPrint(' - user != null: ${userState.user != null}');
|
||||
debugPrint(' - token != null: ${userState.user?.token != null}');
|
||||
debugPrint(' - TCP_IP: ${TCPConsts.TCP_IP}');
|
||||
|
||||
if (deviceId != null &&
|
||||
deviceId.isNotEmpty &&
|
||||
userState.user != null &&
|
||||
userState.user!.token != null) {
|
||||
videoStreamUrl = "webrtc://${TCPConsts.TCP_IP}/live/livestream/$deviceId?token=${userState.user!.token}";
|
||||
debugPrint('✅ [RobotHeaderCard] 视频URL构建成功: $videoStreamUrl');
|
||||
} else {
|
||||
debugPrint('❌ [RobotHeaderCard] 视频URL构建失败');
|
||||
if (deviceId == null || deviceId.isEmpty) {
|
||||
debugPrint(' - 原因: deviceId 为空');
|
||||
}
|
||||
if (userState.user == null) {
|
||||
debugPrint(' - 原因: user 为 null');
|
||||
}
|
||||
if (userState.user?.token == null) {
|
||||
debugPrint(' - 原因: token 为 null');
|
||||
}
|
||||
}
|
||||
|
||||
return videoStreamUrl.isNotEmpty
|
||||
? WebRTCLocalPlayer(
|
||||
key: ValueKey(videoStreamUrl),
|
||||
streamUrl: videoStreamUrl,
|
||||
showLeftPip: false,
|
||||
showRightPip: false,
|
||||
mainViewAlignment: _viewConfigs[_currentViewIndex]['alignment'] as Alignment,
|
||||
)
|
||||
: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.videocam_off, size: 48, color: Colors.white54),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'暂无视频',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -224,3 +234,5 @@ class _RobotHeaderCardState extends State<RobotHeaderCard> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class RobotItemCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
name,
|
||||
alias != null && alias!.isNotEmpty ? alias! : '暂无别名',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -84,13 +84,13 @@ class RobotItemCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
alias != null && alias!.isNotEmpty
|
||||
? '别名: $alias'
|
||||
: 'ID: $id',
|
||||
name, // 🔥 显示完整的长序列号 (deviceName)
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontSize: 11,
|
||||
color: Color(0xFF86909C),
|
||||
),
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user