Commit 96e4c909 authored by 张宏's avatar 张宏

服务器下发mqtt指令控制设备

parent 6d1704c6
......@@ -1348,3 +1348,10 @@ class DeviceControlBloc
/// [用户调用] 重置设备设置
void resetSettings() => add(const DeviceControlResetSettings());
}
/// 全局 DeviceControlBloc 引用。
///
/// 由 main.dart 在创建时写入。
/// [MonitoringIndexCubit._handleIcuCommandMessage] 通过此引用获取 Bloc 实例,
/// 将 MQTT 下行指令映射为 Bloc Event 并通过蓝牙写入设备。
DeviceControlBloc? globalDeviceControlBloc;
......@@ -87,8 +87,12 @@ class _MyAppState extends State<MyApp> {
storageService: _storageService,
);
DeviceControlBloc get _deviceControlBlocInstance =>
_deviceControlBloc ??= DeviceControlBloc();
DeviceControlBloc get _deviceControlBlocInstance {
if (_deviceControlBloc != null) return _deviceControlBloc!;
_deviceControlBloc = DeviceControlBloc();
globalDeviceControlBloc = _deviceControlBloc;
return _deviceControlBloc!;
}
DeviceThresholdConfigBloc get _deviceThresholdConfigBlocInstance {
if (_deviceThresholdConfigBloc != null) return _deviceThresholdConfigBloc!;
......
......@@ -75,7 +75,7 @@ class LakiMqttTopics {
static String icuAlarm(String deviceSn) => 'icu/$deviceSn/alarm'; // stephen 上行 传感器(设备)异常告警 icu/{SN}/alarm → DeviceAlarmHandler
static String icuTo(String deviceSn) => 'icu/$deviceSn/to';
static String icuTo(String deviceSn) => 'icu/$deviceSn/to'; // stephen 下行,服务端下发指令控制设备
static String icuPetBind(String deviceSn) => 'icu/$deviceSn/pet/bind'; // stephen 下行 宠物 绑定、出舱
......
......@@ -6,8 +6,10 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/cabin_detail_response.dart';
import 'package:laki_icu_app/utils/date_utils.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_bloc.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/models/bo/camera_alarm_bo.dart';
import 'package:laki_icu_app/models/bo/device_control_bo.dart';
import 'package:laki_icu_app/services/cabin_service.dart';
import 'package:laki_icu_app/services/p2p_video_service.dart';
import 'package:laki_icu_app/services/webrtc_service.dart';
......@@ -820,6 +822,7 @@ class MonitoringIndexCubit extends HydratedCubit<MonitoringIndexState> {
}
await _mqttClient!.connect();
// 订阅
_mqttClient!
..subscribeIcuCommands(deviceSn)
..subscribePetBind(deviceSn)
......@@ -849,6 +852,7 @@ class MonitoringIndexCubit extends HydratedCubit<MonitoringIndexState> {
}
}
// 解析mqtt订阅消息
void _listenMqttState(LakiMqttClient client) {
_mqttStateSub = client.stateStream.listen((status) {
if (isClosed) return;
......@@ -872,6 +876,12 @@ class MonitoringIndexCubit extends HydratedCubit<MonitoringIndexState> {
_handlePetBindMessage(json);
return;
}
if (json != null &&
message.topic == LakiMqttTopics.icuTo(_mqttDeviceSn ?? '')) {
_handleIcuCommandMessage(json);
return;
}
emit(state.copyWith(
mqttMessage: 'MQTT 收到 ${message.topic}: ${message.payload}',
));
......@@ -971,6 +981,188 @@ class MonitoringIndexCubit extends HydratedCubit<MonitoringIndexState> {
emit(state.copyWith(patientInfo: patientInfo));
}
/// 处理 MQTT icu/{SN}/to 服务端下发控制指令
///
/// 服务端下发格式:
/// ```json
/// {
/// "eventId": "...",
/// "deviceId": "CNA75875A",
/// "deviceType": "ICU",
/// "timestamp": 1782365796552,
/// "type": "setting",
/// "data": { "temperatureSetting": 260 }
/// }
/// ```
/// [data] 中每次只带一个维度字段。
void _handleIcuCommandMessage(Map<String, dynamic> json) {
if (json['type'] != 'setting') return;
final data = json['data'];
if (data is! Map<String, dynamic> || data.isEmpty) return;
final bloc = globalDeviceControlBloc;
if (bloc == null) {
debugPrint('[IcuCommand] globalDeviceControlBloc 未初始化,无法处理指令');
return;
}
// data 每次只带一个维度,取第一个 key
final key = data.keys.first;
final rawValue = data[key];
if (rawValue is! num) {
debugPrint('[IcuCommand] 未知指令字段值类型: $key = $rawValue');
return;
}
final value = rawValue.toInt();
debugPrint('[IcuCommand] 收到指令: $key = $value');
switch (key) {
// ========== 已有 BLE identifier ==========
// 温度设定值 (MQTT ×10,如 260 → 26.0℃)
case 'temperatureSetting':
final temp = (value / 10).toStringAsFixed(1);
bloc.setCabinTempSetValue(temp);
break;
// 湿度设定值
case 'humiditySetting':
bloc.setCabinHumiditySetValue('$value');
break;
// 氧气设定值
case 'oxygenSetting':
bloc.setOxygenConcentrationSetValue('$value');
break;
// CO2 设定值
case 'co2Setting':
bloc.setCO2ConcentrationAlarmThreshold('$value');
break;
// 风速:0=舒适 / 1=强风 / 2=睡眠
case 'windSet':
final mode = _windSpeedModeFromInt(value);
bloc.setWindSpeedMode(mode);
break;
// 护理等级:0=关 / 1=三级 / 2=二级 / 3=一级 / 4=特级
case 'levelLight':
final level = _careLevelFromInt(value);
bloc.setCareLevel(level);
break;
// 开放供氧:0=关 / 1=开
case 'openO2':
bloc.setOpenOxygenSwitch(value == 1);
break;
// 内外循环:0=外 / 1=内
case 'internalExternalCycleState':
bloc.setCirculationMode(
value == 0 ? CirculationMode.external : CirculationMode.internal,
);
break;
// 照明灯:0=关 / 1=开
case 'floodLight':
bloc.setIlluminationLightSwitch(value == 1);
break;
// 泛光灯/检查灯:0=关 / 1=开
case 'inspectionLamp':
bloc.setCheckLightSwitch(value == 1);
break;
// 蓝光灯:0=关 / 1=开
case 'blueLight':
bloc.setBlueLightSwitch(value == 1);
break;
// 红光灯:0=关 / 1=开
case 'redLight':
bloc.setRedLightSwitch(value == 1);
break;
// ========== 待确认 BLE identifier 的字段 ==========
// TODO: 待确认 BLE identifier 后补充
case 'openO2SettingMax':
debugPrint('[IcuCommand] openO2SettingMax=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'noOpenO2SettingMax':
debugPrint(
'[IcuCommand] noOpenO2SettingMax=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'co2FastClearSwitch':
debugPrint(
'[IcuCommand] co2FastClearSwitch=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'wifible':
debugPrint('[IcuCommand] wifible=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'redLightDuration':
debugPrint(
'[IcuCommand] redLightDuration=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'redLightInterval':
debugPrint(
'[IcuCommand] redLightInterval=$value — 待确认 BLE identifier');
break;
// TODO: 待确认 BLE identifier 后补充
case 'airConditioner':
debugPrint('[IcuCommand] airConditioner=$value — 待确认 BLE identifier');
break;
default:
debugPrint('[IcuCommand] 未知指令字段: $key = $value');
break;
}
}
WindSpeedMode _windSpeedModeFromInt(int value) {
switch (value) {
case 0:
return WindSpeedMode.comfort;
case 1:
return WindSpeedMode.strong;
case 2:
return WindSpeedMode.sleep;
default:
return WindSpeedMode.sleep;
}
}
CareLevel _careLevelFromInt(int value) {
switch (value) {
case 0:
return CareLevel.off;
case 1:
return CareLevel.level3;
case 2:
return CareLevel.level2;
case 3:
return CareLevel.level1;
case 4:
return CareLevel.special;
default:
return CareLevel.off;
}
}
/// 毫秒时间戳 → HH:mm:ss 格式(与 Android 一致)
String _formatTimestamp(int milliseconds) {
final dateTime =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment