解决了渲染TCP推送数据,在切换功能后不稳定问题,保证了APP端的稳定性。
This commit is contained in:
@@ -185,13 +185,15 @@ Future<void> init() async {
|
|||||||
sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册
|
sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册
|
||||||
sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl()));
|
sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl()));
|
||||||
sl.registerLazySingleton(() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl(),sl()));
|
sl.registerLazySingleton(() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl(),sl()));
|
||||||
sl.registerFactory(() => RemoteControlCubit(sl(),sl(),sl()));
|
|
||||||
/* // 工厂模式(留存,每次获取新实例)r
|
// 🔥 DeviceStatusBloc 必须在 RemoteControlCubit 之前注册(单例)
|
||||||
|
sl.registerLazySingleton(() => DeviceStatusBloc(
|
||||||
sl.registerFactory(() => DeviceStatusBloc(sl<NetMessageDispatcher>()));
|
sl<NetMessageDispatcher>(),
|
||||||
*/
|
client: sl<TcpClient>(), // 🔥 传入 TcpClient
|
||||||
//单例模式(全局共享一个实例)
|
));
|
||||||
sl.registerLazySingleton(() => DeviceStatusBloc(sl<NetMessageDispatcher>()));
|
|
||||||
|
// 🔥 RemoteControlCubit 注入 DeviceStatusBloc(工厂模式,每次新建)
|
||||||
|
sl.registerFactory(() => RemoteControlCubit(sl(), sl(), sl(), sl()));
|
||||||
|
|
||||||
/// . 路径生成
|
/// . 路径生成
|
||||||
sl.registerLazySingleton<PathHttpDatasource>(
|
sl.registerLazySingleton<PathHttpDatasource>(
|
||||||
|
|||||||
@@ -88,6 +88,27 @@ class NetMessageDispatcher {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//机器状态页面专用
|
||||||
|
Stream<String> onStringMessageOfdeviceStatus() {
|
||||||
|
//print("0x02--TCP拦截推送解析开始");
|
||||||
|
_logger.log('0x02--TCP拦截推送解析开始');
|
||||||
|
// 🔥 关键修复:使用asBroadcastStream()确保多个监听者都能收到数据
|
||||||
|
return onCommand(0x02).map((p) {
|
||||||
|
try {
|
||||||
|
// 尝试解码
|
||||||
|
final result = utf8.decode(p.payload, allowMalformed: true); // 允许乱码,防止报错中断流
|
||||||
|
//print('✅ 解码 0x02 成功:$result'); // 🔥 关键日志:看这里打印了吗?
|
||||||
|
_logger.log('✅ 解码 0x02 成功:$result');
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
// print('❌ 解码 0x02 失败:$e, 原始字节:${p.payload}');
|
||||||
|
_logger.log('❌ 解码 0x02 失败:$e, 原始字节:${p.payload}');
|
||||||
|
return ''; // 返回空字符串,避免流中断
|
||||||
|
}
|
||||||
|
}).asBroadcastStream();
|
||||||
|
/* ///return onCommand(0x02).map((p) => utf8.decode(p.payload));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//监听路径规划的开始作业的指令的应答
|
//监听路径规划的开始作业的指令的应答
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,21 @@ import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
|
|||||||
|
|
||||||
import '../../../../core/logging/i_logger_service.dart';
|
import '../../../../core/logging/i_logger_service.dart';
|
||||||
import '../../../../core/network/net_message_dispatcher.dart';
|
import '../../../../core/network/net_message_dispatcher.dart';
|
||||||
|
import '../../../../core/network/tcp/tcp_client.dart';
|
||||||
import 'device_status_event.dart';
|
import 'device_status_event.dart';
|
||||||
import 'device_status_state.dart';
|
import 'device_status_state.dart';
|
||||||
|
|
||||||
class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||||
final NetMessageDispatcher _dispatcher;
|
final NetMessageDispatcher _dispatcher;
|
||||||
|
final TcpClient tcpClient; // 🔥 新增:直接访问 TcpClient
|
||||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||||
|
|
||||||
// 🔥 保存订阅引用,用于管理生命周期
|
// 🔥 保存订阅引用,用于管理生命周期
|
||||||
StreamSubscription? _tcpSubscription;
|
StreamSubscription? _tcpSubscription;
|
||||||
|
|
||||||
DeviceStatusBloc(this._dispatcher) : super(DeviceStatusInitial()) {
|
DeviceStatusBloc(this._dispatcher, {TcpClient? client})
|
||||||
|
: tcpClient = client ?? GetIt.I<TcpClient>(),
|
||||||
|
super(DeviceStatusInitial()) {
|
||||||
// 🔥 核心改动:直接在构造函数中建立TCP监听,类似RemoteControlCubit
|
// 🔥 核心改动:直接在构造函数中建立TCP监听,类似RemoteControlCubit
|
||||||
_initDirectTcpListener();
|
_initDirectTcpListener();
|
||||||
|
|
||||||
@@ -29,7 +33,10 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
on<PushMessageReceived>(_handlePushMessageReceived);
|
on<PushMessageReceived>(_handlePushMessageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔥 新增:重新初始化TCP监听器(用于页面重新进入时调用)
|
// 🔥 已废弃:重新初始化TCP监听器会导致数据流中断
|
||||||
|
// DeviceStatusBloc是单例,TCP监听器在构造函数中建立后应始终保持活跃
|
||||||
|
// 任何页面只需通过 BlocBuilder 或 stream 订阅状态即可
|
||||||
|
/*
|
||||||
void reinitializeListener() {
|
void reinitializeListener() {
|
||||||
debugPrint('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
|
debugPrint('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
|
||||||
_logger.log('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
|
_logger.log('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
|
||||||
@@ -40,11 +47,11 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消旧的订阅
|
// ❌ 取消旧的订阅会导致数据流中断!
|
||||||
debugPrint('📝 [DeviceStatusBloc] 取消旧订阅');
|
debugPrint('📝 [DeviceStatusBloc] 取消旧订阅');
|
||||||
_tcpSubscription?.cancel();
|
_tcpSubscription?.cancel();
|
||||||
|
|
||||||
// 重置状态为初始状态
|
// ❌ 重置状态会清空UI显示的数据!
|
||||||
debugPrint('📝 [DeviceStatusBloc] emit Initial状态');
|
debugPrint('📝 [DeviceStatusBloc] emit Initial状态');
|
||||||
emit(DeviceStatusInitial());
|
emit(DeviceStatusInitial());
|
||||||
|
|
||||||
@@ -52,16 +59,31 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
debugPrint('📝 [DeviceStatusBloc] 调用 _initDirectTcpListener');
|
debugPrint('📝 [DeviceStatusBloc] 调用 _initDirectTcpListener');
|
||||||
_initDirectTcpListener();
|
_initDirectTcpListener();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// 🔥 新增:直接监听TCP 0x02指令,实时解析并emit状态
|
// 🔥 新增:直接监听TCP 0x02指令,实时解析并emit状态
|
||||||
void _initDirectTcpListener() {
|
void _initDirectTcpListener() {
|
||||||
debugPrint('🔗 [DeviceStatusBloc] 初始化直接TCP监听器(类似RemoteControlCubit)');
|
debugPrint('🔗 [DeviceStatusBloc] 初始化直接TCP监听器 - 使用 tcpClient.packetStream');
|
||||||
_logger.log('🔗 [DeviceStatusBloc] 初始化直接TCP监听器');
|
_logger.log('🔗 [DeviceStatusBloc] 初始化直接TCP监听器');
|
||||||
|
|
||||||
_tcpSubscription = _dispatcher.onStringMessage().listen(
|
// 🔥 关键修复:直接监听 tcpClient.packetStream,不经过 dispatcher 的 filtered stream
|
||||||
(message) {
|
// 这样即使没有其他监听者,TCP流也不会暂停
|
||||||
|
_tcpSubscription = tcpClient.packetStream
|
||||||
|
.where((p) => p.command == 0x02)
|
||||||
|
.map((p) {
|
||||||
|
try {
|
||||||
|
final result = utf8.decode(p.payload, allowMalformed: true);
|
||||||
|
debugPrint('✅ [DeviceStatusBloc] 收到0x02数据: $result');
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ [DeviceStatusBloc] 解码失败: $e');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.listen(
|
||||||
|
(message) {
|
||||||
//debugPrint('📩 [DeviceStatusBloc] 直接收到0x02数据,长度:${message.length}');
|
//debugPrint('📩 [DeviceStatusBloc] 直接收到0x02数据,长度:${message.length}');
|
||||||
|
|
||||||
if (message.isEmpty) {
|
if (message.isEmpty) {
|
||||||
//debugPrint('⚠️ [DeviceStatusBloc] 消息为空,跳过');
|
//debugPrint('⚠️ [DeviceStatusBloc] 消息为空,跳过');
|
||||||
return;
|
return;
|
||||||
@@ -115,6 +137,7 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
obstacleFlag: status.obstacleFlag,
|
obstacleFlag: status.obstacleFlag,
|
||||||
);
|
);
|
||||||
final newGps = GPSEntity(status.latitude, status.longitude);
|
final newGps = GPSEntity(status.latitude, status.longitude);
|
||||||
|
debugPrint('📤 [DeviceStatusBloc] emit DeviceStatusUpdated - 电压:${status.voltage}, 电量:${status.battery}, 模式:${status.controlMode}');
|
||||||
emit(DeviceStatusUpdated(newStatus, newGps));
|
emit(DeviceStatusUpdated(newStatus, newGps));
|
||||||
}
|
}
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
@@ -135,11 +158,12 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
|
|
||||||
// 🔥 重置时仅清空状态
|
// 🔥 重置时仅清空状态
|
||||||
Future<void> _handleReset(DeviceStatusReset event, Emitter<DeviceStatusState> emit) async {
|
Future<void> _handleReset(DeviceStatusReset event, Emitter<DeviceStatusState> emit) async {
|
||||||
debugPrint('🔄 [DeviceStatusBloc] 收到重置事件:清空状态');
|
debugPrint('🔄 [DeviceStatusBloc] 收到重置事件:清空状态 - 当前状态:${state.runtimeType}');
|
||||||
_logger.log('🔄 [DeviceStatusBloc] 收到重置事件:清空状态');
|
_logger.log('🔄 [DeviceStatusBloc] 收到重置事件:清空状态 - 当前状态:${state.runtimeType}');
|
||||||
|
|
||||||
// 只 emit 初始状态,让 UI 清除旧设备的数据
|
// 只 emit 初始状态,让 UI 清除旧设备的数据
|
||||||
emit(DeviceStatusInitial());
|
emit(DeviceStatusInitial());
|
||||||
|
debugPrint('⚠️ [DeviceStatusBloc] 已emit DeviceStatusInitial');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleDeviceStatusLoaded(
|
Future<void> _handleDeviceStatusLoaded(
|
||||||
@@ -189,7 +213,7 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
|||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
debugPrint('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
|
debugPrint('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
|
||||||
_logger.log('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
|
_logger.log('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
|
||||||
_tcpSubscription?.cancel();
|
//_tcpSubscription?.cancel();
|
||||||
// 🔥 关键修复:不调用 super.close(),保持 BLoC 活跃
|
// 🔥 关键修复:不调用 super.close(),保持 BLoC 活跃
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class DeviceStatusCubit extends Cubit<DeviceStatusState> {
|
|||||||
|
|
||||||
_stringMessageSub?.cancel();
|
_stringMessageSub?.cancel();
|
||||||
|
|
||||||
_stringMessageSub = _dispatcher.onStringMessage().listen((message) {
|
_stringMessageSub = _dispatcher.onStringMessageOfdeviceStatus().listen((message) {
|
||||||
if (message.isEmpty) {
|
if (message.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import '../../../../core/di/injection.dart';
|
|||||||
import '../../../../core/network/net_message_dispatcher.dart';
|
import '../../../../core/network/net_message_dispatcher.dart';
|
||||||
import '../../../../core/network/protocol_decoder.dart';
|
import '../../../../core/network/protocol_decoder.dart';
|
||||||
import '../../../devices/presentation/bloc/devices_cubit.dart';
|
import '../../../devices/presentation/bloc/devices_cubit.dart';
|
||||||
import '../../../devices/presentation/bloc/device_status_cubit.dart';
|
import '../../../devices/presentation/bloc/device_status_bloc.dart';
|
||||||
|
import '../../../devices/presentation/bloc/device_status_event.dart';
|
||||||
import '../../../devices/presentation/bloc/device_status_state.dart';
|
import '../../../devices/presentation/bloc/device_status_state.dart';
|
||||||
|
|
||||||
// 配置:数据超时时间(5秒)
|
// 配置:数据超时时间(5秒)
|
||||||
@@ -64,8 +65,9 @@ class _RunningStatusPageState extends State<RunningStatusPage> with WidgetsBindi
|
|||||||
// 🔥 注册静态实例
|
// 🔥 注册静态实例
|
||||||
_instance = this;
|
_instance = this;
|
||||||
debugPrint('🚀 [RunningStatusPage] initState 被调用');
|
debugPrint('🚀 [RunningStatusPage] initState 被调用');
|
||||||
// 🔥 简化:直接重置状态,不需要重新初始化监听器
|
// 🔥 使用 DeviceStatusBloc
|
||||||
context.read<DeviceStatusCubit>().reset();
|
// context.read<DeviceStatusBloc>().add(DeviceStatusReset());
|
||||||
|
|
||||||
// 🔥 关键:立即启动超时计时器,如果5秒内没收到数据就显示"暂无数据"
|
// 🔥 关键:立即启动超时计时器,如果5秒内没收到数据就显示"暂无数据"
|
||||||
_startDataTimeoutTimer();
|
_startDataTimeoutTimer();
|
||||||
|
|
||||||
@@ -841,7 +843,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> with WidgetsBindi
|
|||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
BlocBuilder<DeviceStatusCubit, DeviceStatusState>(
|
BlocBuilder<DeviceStatusBloc, DeviceStatusState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
// 新增:超时状态下显示--
|
// 新增:超时状态下显示--
|
||||||
String qual = _isDataTimeout ? '--' : '--';
|
String qual = _isDataTimeout ? '--' : '--';
|
||||||
@@ -949,7 +951,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> with WidgetsBindi
|
|||||||
),
|
),
|
||||||
|
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: BlocBuilder<DeviceStatusCubit, DeviceStatusState>(
|
child: BlocBuilder<DeviceStatusBloc, DeviceStatusState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
debugPrint('🎨 [UI-Build] BlocBuilder 重建!当前状态类型:${state.runtimeType}');
|
debugPrint('🎨 [UI-Build] BlocBuilder 重建!当前状态类型:${state.runtimeType}');
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:maibu_satabot_v2/features/home/presentation/pages/productDesc.da
|
|||||||
|
|
||||||
import '../../../../core/di/injection.dart';
|
import '../../../../core/di/injection.dart';
|
||||||
import '../../../../core/router/route_paths.dart';
|
import '../../../../core/router/route_paths.dart';
|
||||||
import '../../../devices/presentation/bloc/device_status_cubit.dart';
|
import '../../../devices/presentation/bloc/device_status_bloc.dart';
|
||||||
import '../../../remote_control/presentation/bloc/remote_control_cubit.dart';
|
import '../../../remote_control/presentation/bloc/remote_control_cubit.dart';
|
||||||
import '../../../remote_control/presentation/pages/remote_control_page.dart';
|
import '../../../remote_control/presentation/pages/remote_control_page.dart';
|
||||||
import '../pages/route_plan_page.dart';
|
import '../pages/route_plan_page.dart';
|
||||||
@@ -31,9 +31,8 @@ class HomeRoutes {
|
|||||||
GoRoute(path: RoutePaths.usage, builder: (context, state) => const ManualPage()),
|
GoRoute(path: RoutePaths.usage, builder: (context, state) => const ManualPage()),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: RoutePaths.runningStatus,
|
path: RoutePaths.runningStatus,
|
||||||
// builder: (context, state) => const RunningStatusPage(),
|
builder: (context, state) => BlocProvider.value(
|
||||||
builder: (context, state) => BlocProvider(
|
value: sl<DeviceStatusBloc>(), // 使用全局单例 DeviceStatusBloc,确保数据流不中断
|
||||||
create: (_) => DeviceStatusCubit(sl()), // 使用简化版 Cubit
|
|
||||||
child: const RunningStatusPage(),
|
child: const RunningStatusPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:maibu_satabot_v2/core/router/route_paths.dart';
|
|||||||
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
|
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
|
||||||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
|
||||||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart';
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart';
|
||||||
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart';
|
||||||
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_state.dart';
|
||||||
|
|
||||||
class MachineDetailsPage extends StatefulWidget {
|
class MachineDetailsPage extends StatefulWidget {
|
||||||
final DeviceEntity? device;
|
final DeviceEntity? device;
|
||||||
@@ -26,6 +28,10 @@ class _MachineDetailsPageState extends State<MachineDetailsPage> {
|
|||||||
BuildContext? _loadingDialogContext; // 解绑加载弹窗
|
BuildContext? _loadingDialogContext; // 解绑加载弹窗
|
||||||
BuildContext? _updateLoadingContext; // 修改名称加载弹窗
|
BuildContext? _updateLoadingContext; // 修改名称加载弹窗
|
||||||
bool _isUpdatingName = false;
|
bool _isUpdatingName = false;
|
||||||
|
|
||||||
|
// 🔥 保存 stream 引用,避免每次 rebuild 都重新获取
|
||||||
|
late Stream<DeviceStatusState> _deviceStatusStream;
|
||||||
|
late DeviceStatusState _initialState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -33,6 +39,12 @@ class _MachineDetailsPageState extends State<MachineDetailsPage> {
|
|||||||
_deviceName = (widget.device?.deviceAlias?.trim().isEmpty ?? true) ? '未知设备' : widget.device!.deviceAlias!;
|
_deviceName = (widget.device?.deviceAlias?.trim().isEmpty ?? true) ? '未知设备' : widget.device!.deviceAlias!;
|
||||||
_deviceId = widget.device?.deviceName ?? '';
|
_deviceId = widget.device?.deviceName ?? '';
|
||||||
_nameController = TextEditingController(text: _deviceName);
|
_nameController = TextEditingController(text: _deviceName);
|
||||||
|
|
||||||
|
// 🔥 在 initState 中保存 stream 和 initial state
|
||||||
|
final bloc = context.read<DeviceStatusBloc>();
|
||||||
|
_deviceStatusStream = bloc.stream;
|
||||||
|
_initialState = bloc.state;
|
||||||
|
debugPrint('📦 [MachineDetails] initState - 初始状态: ${_initialState.runtimeType}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -188,6 +200,9 @@ class _MachineDetailsPageState extends State<MachineDetailsPage> {
|
|||||||
children: [
|
children: [
|
||||||
_buildDeviceStatusCard(currentDevice),
|
_buildDeviceStatusCard(currentDevice),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
// 🔥 新增:实时状态卡片
|
||||||
|
_buildRealTimeStatusCard(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
_buildBasicInfoCard(context, currentDevice),
|
_buildBasicInfoCard(context, currentDevice),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildResourceCenterCard(),
|
_buildResourceCenterCard(),
|
||||||
@@ -200,6 +215,64 @@ class _MachineDetailsPageState extends State<MachineDetailsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🔥 新增:实时状态卡片(显示控制模式、电压、电量)
|
||||||
|
Widget _buildRealTimeStatusCard() {
|
||||||
|
return StreamBuilder<DeviceStatusState>(
|
||||||
|
stream: _deviceStatusStream,
|
||||||
|
initialData: _initialState,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final blocState = snapshot.data;
|
||||||
|
debugPrint('🔍 [MachineDetails] StreamBuilder 收到状态: ${blocState?.runtimeType}');
|
||||||
|
|
||||||
|
String controlMode = '未知';
|
||||||
|
String voltage = '--';
|
||||||
|
String battery = '--';
|
||||||
|
|
||||||
|
if (blocState is DeviceStatusUpdated) {
|
||||||
|
debugPrint('✅ [MachineDetails] 有实时数据 - 电压:${blocState.status.voltage}, 电量:${blocState.status.battery}');
|
||||||
|
controlMode = blocState.status.controlMode == '3' ? '远程模式' : '本地模式';
|
||||||
|
voltage = '${blocState.status.voltage}V';
|
||||||
|
battery = '${blocState.status.battery}%';
|
||||||
|
} else {
|
||||||
|
debugPrint('⚠️ [MachineDetails] 没有实时数据,状态类型: ${blocState?.runtimeType}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2))],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 12),
|
||||||
|
child: Text('实时状态', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
|
||||||
|
),
|
||||||
|
_buildStatusRow('控制模式', controlMode),
|
||||||
|
const Divider(height: 24, color: Color(0xFFF0F0F0)),
|
||||||
|
_buildStatusRow('电池电压', voltage),
|
||||||
|
const Divider(height: 24, color: Color(0xFFF0F0F0)),
|
||||||
|
_buildStatusRow('剩余电量', battery),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusRow(String label, String value) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(label, style: const TextStyle(fontSize: 16, color: Color(0xFF333333))),
|
||||||
|
Text(value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF007AFF))),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 设备状态卡片(保持不变)
|
/// 设备状态卡片(保持不变)
|
||||||
Widget _buildDeviceStatusCard(DeviceEntity device) {
|
Widget _buildDeviceStatusCard(DeviceEntity device) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class RemoteHttpDatasource {
|
|||||||
'Authorization': 'Bearer ${token}',
|
'Authorization': 'Bearer ${token}',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
data: {'platform': "app"});
|
data: {'platform': "app",'type':2});
|
||||||
try {
|
try {
|
||||||
final responseData = response.data as Map<String, dynamic>;
|
final responseData = response.data as Map<String, dynamic>;
|
||||||
//debugPrint("📊 [HTTP 响应] 完整数据:$responseData");
|
//debugPrint("📊 [HTTP 响应] 完整数据:$responseData");
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart';
|
||||||
|
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_state.dart';
|
||||||
import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart';
|
import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart';
|
||||||
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
|
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
|
||||||
|
|
||||||
@@ -23,7 +25,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
|||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||||
final NetMessageDispatcher dispatcher;
|
final NetMessageDispatcher dispatcher;
|
||||||
StreamSubscription? _stringMessageSub; //
|
final DeviceStatusBloc deviceStatusBloc; // 🔥 注入 DeviceStatusBloc
|
||||||
|
StreamSubscription? _deviceStatusSub; // 🔥 订阅 DeviceStatusBloc 的状态流
|
||||||
static const platform = MethodChannel('com.maibu.satabot/ping');
|
static const platform = MethodChannel('com.maibu.satabot/ping');
|
||||||
int _currentPing = 50;
|
int _currentPing = 50;
|
||||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||||
@@ -34,77 +37,48 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
RemoteControlCubit(this._repository, this._requestControlPermissionUseCase, this.dispatcher)
|
RemoteControlCubit(
|
||||||
: super(
|
this._repository,
|
||||||
|
this._requestControlPermissionUseCase,
|
||||||
|
this.dispatcher,
|
||||||
|
this.deviceStatusBloc, // 🔥 注入
|
||||||
|
) : super(
|
||||||
RemoteControlState(
|
RemoteControlState(
|
||||||
controlEntity: MachineControlStatusEntity(),
|
controlEntity: MachineControlStatusEntity(),
|
||||||
runningStatusModel: RunningStatusModel(),
|
runningStatusModel: RunningStatusModel(),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_initPacketListener();
|
_initPacketListener();
|
||||||
_initStringMessageListener(); // 🔥 新增
|
_initDeviceStatusListener(); // 🔥 改为订阅 DeviceStatusBloc
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initStringMessageListener() {
|
// 🔥 新增:订阅 DeviceStatusBloc 的状态流,而不是直接监听 TCP
|
||||||
//print('>>> [RemoteControl] begin 初始化 0x02 字符串监听器');
|
void _initDeviceStatusListener() {
|
||||||
_logger.logWithLevel('>>> [RemoteControl] begin 初始化 0x02 字符串监听器');
|
_logger.logWithLevel('>>> [RemoteControl] begin 订阅 DeviceStatusBloc 状态流');
|
||||||
_stringMessageSub?.cancel();
|
_deviceStatusSub?.cancel();
|
||||||
|
|
||||||
_stringMessageSub = dispatcher.onStringMessage().listen((message) async {
|
_deviceStatusSub = deviceStatusBloc.stream.listen((deviceState) async {
|
||||||
// print('>>> [RemoteControl] 收到 0x02 字符串推送:$message');
|
if (deviceState is DeviceStatusUpdated) {
|
||||||
_logger.logWithLevel('>>> [RemoteControl] 收到 0x02 字符串推送:$message');
|
final voltage = deviceState.status.voltage;
|
||||||
|
final battery = deviceState.status.battery;
|
||||||
|
final controlMode = deviceState.status.controlMode == '3' ? '远程模式' : '本地模式';
|
||||||
|
|
||||||
if (message.isEmpty) {
|
final c = getNetworkDelay();
|
||||||
//print('⚠️ [RemoteControl] 消息为空,跳过解析');
|
|
||||||
_logger.logWithLevel('⚠️ [RemoteControl] 消息为为空,跳过解析');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 🔥 关键:使用与 DeviceStatusBloc 相同的解析方法
|
|
||||||
// print('>>> [RemoteControl] 🔍 开始解析数据(使用 fromFields)...');
|
|
||||||
_logger.logWithLevel('>>> [RemoteControl] 🔍 开始解析数据(使用 fromFields)...');
|
|
||||||
|
|
||||||
final fields = message.trim().split(',');
|
|
||||||
//print('>>> [RemoteControl] 字段数量:${fields.length}');
|
|
||||||
_logger.logWithLevel('>>> [RemoteControl] 字段数量:${fields.length}');
|
|
||||||
|
|
||||||
if (fields.length < 18) {
|
|
||||||
//print('⚠️ [RemoteControl] 字段不足:${fields.length},期望 ≥18');
|
|
||||||
_logger.logWithLevel('⚠️ [RemoteControl] 字段不足:${fields.length},期望 ≥18');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔥 使用 RunningStatusEntity.fromFields() 解析
|
|
||||||
final status = RunningStatusEntity.fromFields(fields);
|
|
||||||
// 🔥 只提取电压、电量、控制模式
|
|
||||||
final voltage = status.voltage;
|
|
||||||
final battery = status.battery;
|
|
||||||
final controlMode = status.controlMode == '3' ? '远程模式' : '本地模式';
|
|
||||||
|
|
||||||
|
|
||||||
final c = getNetworkDelay(); //
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
runningStatusModel: state.runningStatusModel.copyWith(
|
runningStatusModel: state.runningStatusModel.copyWith(
|
||||||
voltage: voltage.toString(), // 更新电压
|
voltage: voltage.toString(),
|
||||||
battery: battery.toString(), // 更新电量
|
battery: battery.toString(),
|
||||||
controlMode: controlMode, // 更新控制模式
|
controlMode: controlMode,
|
||||||
),
|
),
|
||||||
battery: int.tryParse(battery) ?? 0,
|
battery: int.tryParse(battery) ?? 0,
|
||||||
ping: await c, // 这里直接使用异步返回的数值
|
ping: await c,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
_logger.logWithLevel('✅ [RemoteControl] 从 DeviceStatusBloc 收到更新: 电压=$voltage, 电量=$battery, 模式=$controlMode');
|
||||||
} catch (e, stackTrace) {
|
|
||||||
// print('>>> [RemoteControl] ❌ 解析失败:$e');
|
|
||||||
_logger.logWithLevel('>>> [RemoteControl] ❌ 解析失败:$e');
|
|
||||||
// print('>>> [RemoteControl] ❌ 堆栈跟踪:$stackTrace');
|
|
||||||
// print('>>> [RemoteControl] ❌ 原始消息:$message');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//print('>>> [RemoteControl] ✅ 0x02 字符串监听器已建立完成');
|
_logger.logWithLevel('>>> [RemoteControl] ✅ DeviceStatusBloc 订阅已建立完成');
|
||||||
_logger.logWithLevel('>>> [RemoteControl] ✅ 0x02 字符串监听器已建立完成');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -426,9 +400,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_timer?.cancel(); // 退出页面时务必销毁定时器
|
_timer?.cancel();
|
||||||
_kickOutSub?.cancel();
|
_deviceStatusSub?.cancel(); // 🔥 取消订阅 DeviceStatusBloc
|
||||||
_stringMessageSub?.cancel();
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user