Commit 266ba06d authored by akari's avatar akari

feat: 控制温湿度

parent dc8a1afb
import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/models/bo/device_control_bo.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
/// 用途:设备控制事件基类
abstract class DeviceControlEvent extends Equatable {
......@@ -26,6 +27,16 @@ class DeviceControlDataUpdated extends DeviceControlEvent {
List<Object?> get props => [data];
}
/// MCU上报数据同步到设备控制
class DeviceControlMcuReportFetched extends DeviceControlEvent {
final McuReport report;
const DeviceControlMcuReportFetched(this.report);
@override
List<Object?> get props => [report];
}
// ==================== 设备SN事件 ====================
/// 获取设备SN(从MCU获取)
......@@ -70,6 +81,16 @@ class DeviceControlCabinTempSetValueChanged extends DeviceControlEvent {
List<Object?> get props => [setValue];
}
/// 调整舱内温度设定值(用户点击 -/+,发送给MCU)
class DeviceControlCabinTempSetValueAdjusted extends DeviceControlEvent {
final int step;
const DeviceControlCabinTempSetValueAdjusted(this.step);
@override
List<Object?> get props => [step];
}
/// 设置舱内温度开关
class DeviceControlCabinTempSwitchChanged extends DeviceControlEvent {
final bool isOn;
......@@ -102,6 +123,16 @@ class DeviceControlCabinHumiditySetValueChanged extends DeviceControlEvent {
List<Object?> get props => [setValue];
}
/// 调整舱内湿度设定值(用户点击 -/+,发送给MCU)
class DeviceControlCabinHumiditySetValueAdjusted extends DeviceControlEvent {
final int step;
const DeviceControlCabinHumiditySetValueAdjusted(this.step);
@override
List<Object?> get props => [step];
}
/// 设置舱内湿度开关
class DeviceControlCabinHumiditySwitchChanged extends DeviceControlEvent {
final bool isOn;
......@@ -115,7 +146,8 @@ class DeviceControlCabinHumiditySwitchChanged extends DeviceControlEvent {
// ==================== 氧气浓度控制事件 ====================
/// 获取氧气浓度当前值(从MCU获取)
class DeviceControlOxygenConcentrationCurrentValueFetched extends DeviceControlEvent {
class DeviceControlOxygenConcentrationCurrentValueFetched
extends DeviceControlEvent {
final String currentValue;
const DeviceControlOxygenConcentrationCurrentValueFetched(this.currentValue);
......@@ -125,7 +157,8 @@ class DeviceControlOxygenConcentrationCurrentValueFetched extends DeviceControlE
}
/// 设置氧气浓度设定值(用户配置,发送给MCU)
class DeviceControlOxygenConcentrationSetValueChanged extends DeviceControlEvent {
class DeviceControlOxygenConcentrationSetValueChanged
extends DeviceControlEvent {
final String setValue;
const DeviceControlOxygenConcentrationSetValueChanged(this.setValue);
......@@ -134,6 +167,17 @@ class DeviceControlOxygenConcentrationSetValueChanged extends DeviceControlEvent
List<Object?> get props => [setValue];
}
/// 调整氧气浓度设定值(用户点击 -/+,发送给MCU)
class DeviceControlOxygenConcentrationSetValueAdjusted
extends DeviceControlEvent {
final int step;
const DeviceControlOxygenConcentrationSetValueAdjusted(this.step);
@override
List<Object?> get props => [step];
}
/// 设置氧气浓度开关
class DeviceControlOxygenConcentrationSwitchChanged extends DeviceControlEvent {
final bool isOn;
......@@ -147,7 +191,8 @@ class DeviceControlOxygenConcentrationSwitchChanged extends DeviceControlEvent {
// ==================== 二氧化碳浓度控制事件 ====================
/// 获取二氧化碳浓度当前值(从MCU获取)
class DeviceControlCO2ConcentrationCurrentValueFetched extends DeviceControlEvent {
class DeviceControlCO2ConcentrationCurrentValueFetched
extends DeviceControlEvent {
final String currentValue;
const DeviceControlCO2ConcentrationCurrentValueFetched(this.currentValue);
......@@ -157,7 +202,8 @@ class DeviceControlCO2ConcentrationCurrentValueFetched extends DeviceControlEven
}
/// 设置二氧化碳浓度告警阈值(用户配置,发送给MCU)
class DeviceControlCO2ConcentrationAlarmThresholdChanged extends DeviceControlEvent {
class DeviceControlCO2ConcentrationAlarmThresholdChanged
extends DeviceControlEvent {
final String alarmThreshold;
const DeviceControlCO2ConcentrationAlarmThresholdChanged(this.alarmThreshold);
......@@ -166,6 +212,17 @@ class DeviceControlCO2ConcentrationAlarmThresholdChanged extends DeviceControlEv
List<Object?> get props => [alarmThreshold];
}
/// 调整二氧化碳浓度设定值(用户点击 -/+,发送给MCU)
class DeviceControlCO2ConcentrationAlarmThresholdAdjusted
extends DeviceControlEvent {
final int step;
const DeviceControlCO2ConcentrationAlarmThresholdAdjusted(this.step);
@override
List<Object?> get props => [step];
}
// ==================== 护理等级控制事件 ====================
/// 设置护理等级(用户配置,发送给MCU)
......
......@@ -8,25 +8,25 @@ const DeviceControlBO _defaultDeviceControlBO = DeviceControlBO(
environmentTemp: '25.0℃',
totalOxygenTime: '0h',
cabinTemp: TemperatureControlBO(
currentValue: '24',
currentValue: '--',
setValue: '24',
unit: '℃',
isOn: false,
),
cabinHumidity: HumidityControlBO(
currentValue: '24',
currentValue: '--',
setValue: '50',
unit: '%',
isOn: false,
),
oxygenConcentration: OxygenConcentrationBO(
currentValue: '24',
currentValue: '--',
setValue: '90',
unit: '%',
isOn: true,
),
co2Concentration: CO2ConcentrationBO(
currentValue: '2400',
currentValue: '--',
alarmThreshold: '4000',
unit: 'PPM',
),
......@@ -106,7 +106,15 @@ class DeviceControlState extends Equatable {
/// 从持久化存储中恢复状态(仅恢复用户设置,MCU值使用默认值)
factory DeviceControlState.fromJson(Map<String, dynamic> json) {
final userSettings = DeviceControlUserSettings.fromJson(json);
final data = userSettings.applyTo(_defaultDeviceControlBO);
final restoredData = userSettings.applyTo(_defaultDeviceControlBO);
final data = restoredData.copyWith(
cabinTemp: restoredData.cabinTemp.copyWith(currentValue: '--'),
cabinHumidity: restoredData.cabinHumidity.copyWith(currentValue: '--'),
oxygenConcentration:
restoredData.oxygenConcentration.copyWith(currentValue: '--'),
co2Concentration:
restoredData.co2Concentration.copyWith(currentValue: '--'),
);
return DeviceControlState(
data: data,
userSettings: userSettings,
......
......@@ -23,10 +23,12 @@ class BluetoothReadModel extends Equatable {
factory BluetoothReadModel.fromMcuReport(McuReport report) {
return BluetoothReadModel(
sn: report.sn.trim().isEmpty ? null : report.sn.trim(),
mainTemperature: report.mainTemperature,
humidity: report.humidity,
oxygenConcentration: report.oxygenConcentration,
co2Measurement: report.co2Measurement,
mainTemperature:
report.mainTemperature.isNaN ? null : report.mainTemperature,
humidity: report.humidity < 0 ? null : report.humidity,
oxygenConcentration:
report.oxygenConcentration < 0 ? null : report.oxygenConcentration,
co2Measurement: report.co2Measurement < 0 ? null : report.co2Measurement,
updatedAt: DateTime.now(),
);
}
......
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:laki_icu_app/utils/logger.dart';
typedef BluetoothPayloadParser<T> = T? Function(List<int> bytes, String rawHex);
final BleBluetoothManager appBluetoothManager = BleBluetoothManager();
enum BluetoothConnectionStatus {
disconnected,
connecting,
......@@ -125,6 +129,7 @@ class BleBluetoothManager<T> {
await _ble.initialize();
final status = _ble.status;
debugPrint('🔥🔥🔥 BLE_CONNECT_DEBUG ensure_ready status=$status');
if (status == BleStatus.ready) {
return true;
}
......@@ -135,10 +140,14 @@ class BleBluetoothManager<T> {
.timeout(const Duration(seconds: 2), onTimeout: () => status);
if (nextStatus != BleStatus.ready) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG ensure_ready_failed nextStatus=$nextStatus');
_notifyMessage('蓝牙未就绪或未授权: $nextStatus');
return false;
}
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG ensure_ready_success nextStatus=$nextStatus');
return true;
}
......@@ -243,12 +252,18 @@ class BleBluetoothManager<T> {
}
Future<void> connectToDeviceId(String deviceId) async {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_to_device_id start deviceId=$deviceId');
if (deviceId.isEmpty) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_to_device_id empty_device_id');
_notifyMessage('无效的蓝牙设备地址');
return;
}
if (!await ensureBluetoothReady()) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_to_device_id bluetooth_not_ready');
return;
}
......@@ -267,6 +282,7 @@ class BleBluetoothManager<T> {
_setStatus(BluetoothConnectionStatus.connecting);
_notifyMessage('正在连接蓝牙设备: $deviceId');
debugPrint('🔥🔥🔥 BLE_CONNECT_DEBUG connecting deviceId=$deviceId');
final completer = Completer<void>();
_connectionSubscription = _ble
......@@ -282,10 +298,14 @@ class BleBluetoothManager<T> {
switch (update.connectionState) {
case DeviceConnectionState.connecting:
_setStatus(BluetoothConnectionStatus.connecting);
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG state_connecting deviceId=$deviceId');
break;
case DeviceConnectionState.connected:
_setStatus(BluetoothConnectionStatus.connectedReady);
_notifyMessage('蓝牙连接成功: $deviceId');
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG state_connected deviceId=$deviceId');
await _enableNotifications(deviceId);
activateDataSource();
if (!completer.isCompleted) {
......@@ -294,10 +314,16 @@ class BleBluetoothManager<T> {
break;
case DeviceConnectionState.disconnecting:
_setStatus(BluetoothConnectionStatus.disconnected);
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG state_disconnecting deviceId=$deviceId');
break;
case DeviceConnectionState.disconnected:
_setStatus(BluetoothConnectionStatus.disconnected);
_notifyMessage('蓝牙连接断开: $deviceId');
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG state_disconnected deviceId=$deviceId '
'failure=${update.failure}',
);
_clearCharacteristics();
if (!completer.isCompleted && update.failure != null) {
completer.completeError(update.failure!);
......@@ -308,6 +334,8 @@ class BleBluetoothManager<T> {
onError: (Object error) {
_setStatus(BluetoothConnectionStatus.disconnected);
_notifyMessage('蓝牙连接失败: $error');
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_error deviceId=$deviceId error=$error');
if (!completer.isCompleted) {
completer.completeError(error);
}
......@@ -317,6 +345,8 @@ class BleBluetoothManager<T> {
try {
await completer.future.timeout(connectionTimeout);
} catch (_) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_timeout_or_error deviceId=$deviceId');
await disconnect();
rethrow;
}
......@@ -327,6 +357,8 @@ class BleBluetoothManager<T> {
_status == BluetoothConnectionStatus.active) {
_setStatus(BluetoothConnectionStatus.active);
_notifyMessage('蓝牙数据源已激活');
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG data_source_active deviceId=$_connectedDeviceId');
}
}
......@@ -395,10 +427,16 @@ class BleBluetoothManager<T> {
Future<void> _enableNotifications(String deviceId) async {
final characteristic = _notifyCharacteristic;
if (characteristic == null) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_characteristic_null deviceId=$deviceId');
throw StateError('接收特征值为空');
}
await _notifySubscription?.cancel();
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_subscribe_start deviceId=$deviceId '
'service=$serviceUuid characteristic=$notifyCharacteristicUuid',
);
_notifySubscription = _ble.subscribeToCharacteristic(characteristic).listen(
(bytes) {
if (!isActive || bytes.isEmpty) {
......@@ -406,6 +444,10 @@ class BleBluetoothManager<T> {
}
final rawHex = bytesToHex(bytes);
debugPrint(
'🔥🔥🔥 BLE_READ_DEBUG manager_notify deviceId=$deviceId len=${bytes.length} hex=$rawHex');
Log.warn('🔥🔥🔥 BLE_READ_DEBUG manager_notify deviceId=$deviceId '
'len=${bytes.length} hex=$rawHex');
final parsedData = _parser?.call(bytes, rawHex);
_dataController.add(
BluetoothDataPacket<T>(
......@@ -416,8 +458,14 @@ class BleBluetoothManager<T> {
),
);
},
onError: (Object error) => _notifyMessage('接收蓝牙数据失败: $error'),
onError: (Object error) {
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_error deviceId=$deviceId error=$error');
_notifyMessage('接收蓝牙数据失败: $error');
},
);
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_subscribe_done deviceId=$deviceId');
}
void _clearCharacteristics() {
......@@ -427,6 +475,10 @@ class BleBluetoothManager<T> {
void _setStatus(BluetoothConnectionStatus status) {
_status = status;
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG manager_status status=$status '
'deviceId=$_connectedDeviceId',
);
if (!_stateController.isClosed) {
_stateController.add(status);
}
......
import 'dart:convert';
const int mcuReportBytes = 145;
const int mcuReportHeaderBytes = 5;
const int mcuReportTailBytes = 3;
class HexReader {
HexReader(String hexData) {
......@@ -37,10 +39,20 @@ class HexReader {
return int.parse(readHex(2), radix: 16);
}
int readUInt16LE() {
final low = readUInt8();
final high = readUInt8();
return low | (high << 8);
}
double readTenths() {
return readUInt16() / 10;
}
double readTenthsLE() {
return readUInt16LE() / 10;
}
int readHighByte() {
return readUInt16() >> 8;
}
......@@ -330,85 +342,104 @@ class McuReport {
class McuReportParser {
static McuReport parseHex(String hexData) {
final data = HexReader(hexData);
if (data.remainingBytes < mcuReportBytes) {
if (data.remainingBytes < mcuReportHeaderBytes) {
throw RangeError(
'完整 MCU 上报需要 $mcuReportBytes 字节,当前只有 ${data.remainingBytes} 字节',
'MCU 上报至少需要 $mcuReportHeaderBytes 字节头,当前只有 ${data.remainingBytes} 字节',
);
}
final head = data.readHex(2);
final direct = data.readHex(1);
final serviceNum = data.readHex(1);
final length = data.readUInt8();
final payloadBytes = data.remainingBytes >= length + mcuReportTailBytes
? length
: data.remainingBytes;
final payload =
payloadBytes > 0 ? HexReader(data.readHex(payloadBytes)) : null;
int remainingPayloadBytes() => payload?.remainingBytes ?? 0;
int u16() => remainingPayloadBytes() >= 2 ? payload!.readUInt16LE() : -1;
double tenths() =>
remainingPayloadBytes() >= 2 ? payload!.readTenthsLE() : double.nan;
int highByte() =>
remainingPayloadBytes() >= 2 ? payload!.readUInt16LE() >> 8 : -1;
String hex2() => remainingPayloadBytes() >= 2 ? payload!.readHex(2) : '';
String ascii(int bytes) =>
remainingPayloadBytes() >= bytes ? payload!.readAscii(bytes) : '';
return McuReport(
head: data.readHex(2),
direct: data.readHex(1),
serviceNum: data.readHex(1),
length: data.readUInt8(),
mainTemperature: data.readTenths(),
versionNumber: data.readTenths(),
ambientTemperature: data.readTenths(),
upperTemperature: data.readTenths(),
humidity: data.readUInt16(),
oxygenConcentration: data.readUInt16(),
temperatureSetting: data.readTenths(),
lowerTemperature: data.readTenths(),
humiditySetting: data.readUInt16(),
oxygenSetting: data.readUInt16(),
internalExternalCycleState: data.readUInt16(),
negativeIonSwitch: data.readUInt16(),
uvLamp: data.readUInt16(),
inspectionLamp: data.readUInt16(),
floodLight: data.readUInt16(),
o2ChangeSlope: data.readUInt16(),
rightAtomizerTreatmentTime: data.readUInt16(),
rightInfraredPhysiotherapyTime: data.readUInt16(),
tempS1: data.readHighByte() / 10,
tempH1: data.readHighByte(),
atomizerTreatmentTime: data.readUInt16(),
infraredPhysiotherapyTime: data.readHighByte(),
mainTempCorrect: data.readHighByte(),
auxiliaryTemperatureCorrect: data.readHighByte(),
o2CorrectStart: data.readHighByte(),
o2CorrectDiffer: data.readHighByte(),
humidityCorrect: data.readHighByte(),
oxygenCorrect: data.readHighByte(),
eTempCorrect: data.readHighByte(),
xyyModel: data.readHex(2),
unknown: data.readHex(2),
wifible: data.readHighByte(),
levelLight: data.readHighByte(),
openO2: data.readHighByte(),
airConditioner: data.readHighByte(),
co2Correct: data.readHighByte(),
co2Measurement: data.readUInt16(),
co2Setting: data.readUInt16(),
co2FastClearSwitch: data.readHighByte(),
openO2SettingMax: data.readHighByte(),
noOpenO2SettingMax: data.readHighByte(),
windSet: data.readHighByte(),
rangeData1: data.readHex(2),
rangeData2: data.readHex(2),
rangeData3: data.readHex(2),
co2Safety: data.readUInt16(),
co2SettingMin: data.readUInt16(),
blueLight: data.readHighByte(),
redLight: data.readHighByte(),
fcSwitch: data.readHighByte(),
o2SupplyTime: data.readUInt16(),
totalTime: data.readUInt16(),
ratioTemp2: data.readUInt8(),
ratioTemp1: data.readUInt8(),
sp: data.readHighByte(),
dp: data.readHighByte(),
mean: data.readHighByte(),
pulseRate: data.readHighByte(),
xyyStatus: data.readHighByte(),
xyyError1: data.readHighByte(),
sn: data.readAscii(10),
topTempCorrect: data.readHighByte(),
midTempCorrect: data.readHighByte(),
o2OpenCorrectStart: data.readHighByte(),
o2OpenCorrectStep: data.readHighByte(),
o2CorrectMax: data.readHighByte(),
o2OpenCorrectMax: data.readHighByte(),
parsedBytes: data.byteIndex,
head: head,
direct: direct,
serviceNum: serviceNum,
length: length,
mainTemperature: tenths(),
versionNumber: tenths(),
ambientTemperature: tenths(),
upperTemperature: tenths(),
humidity: u16(),
oxygenConcentration: u16(),
temperatureSetting: tenths(),
lowerTemperature: tenths(),
humiditySetting: u16(),
oxygenSetting: u16(),
internalExternalCycleState: u16(),
negativeIonSwitch: u16(),
uvLamp: u16(),
inspectionLamp: u16(),
floodLight: u16(),
o2ChangeSlope: u16(),
rightAtomizerTreatmentTime: u16(),
rightInfraredPhysiotherapyTime: u16(),
tempS1: highByte() / 10,
tempH1: highByte(),
atomizerTreatmentTime: u16(),
infraredPhysiotherapyTime: highByte(),
mainTempCorrect: highByte(),
auxiliaryTemperatureCorrect: highByte(),
o2CorrectStart: highByte(),
o2CorrectDiffer: highByte(),
humidityCorrect: highByte(),
oxygenCorrect: highByte(),
eTempCorrect: highByte(),
xyyModel: hex2(),
unknown: hex2(),
wifible: highByte(),
levelLight: highByte(),
openO2: highByte(),
airConditioner: highByte(),
co2Correct: highByte(),
co2Measurement: u16(),
co2Setting: u16(),
co2FastClearSwitch: highByte(),
openO2SettingMax: highByte(),
noOpenO2SettingMax: highByte(),
windSet: highByte(),
rangeData1: hex2(),
rangeData2: hex2(),
rangeData3: hex2(),
co2Safety: u16(),
co2SettingMin: u16(),
blueLight: highByte(),
redLight: highByte(),
fcSwitch: highByte(),
o2SupplyTime: u16(),
totalTime: u16(),
ratioTemp2: remainingPayloadBytes() >= 1 ? payload!.readUInt8() : -1,
ratioTemp1: remainingPayloadBytes() >= 1 ? payload!.readUInt8() : -1,
sp: highByte(),
dp: highByte(),
mean: highByte(),
pulseRate: highByte(),
xyyStatus: highByte(),
xyyError1: highByte(),
sn: ascii(10),
topTempCorrect: highByte(),
midTempCorrect: highByte(),
o2OpenCorrectStart: highByte(),
o2OpenCorrectStep: highByte(),
o2CorrectMax: highByte(),
o2OpenCorrectMax: highByte(),
parsedBytes: mcuReportHeaderBytes + (payload?.byteIndex ?? 0),
);
}
......@@ -427,6 +458,14 @@ class McuReportParser {
class McuReportFrameDecoder {
final List<int> _buffer = [];
int get bufferedBytes => _buffer.length;
String get bufferedHexPreview => _buffer
.take(24)
.map((item) => item.toRadixString(16).padLeft(2, '0'))
.join()
.toUpperCase();
List<McuReport> addBytes(List<int> bytes) {
if (bytes.isEmpty) {
return const [];
......@@ -435,10 +474,12 @@ class McuReportFrameDecoder {
_buffer.addAll(bytes);
final reports = <McuReport>[];
while (_buffer.length >= mcuReportBytes) {
final headIndex = _buffer.indexOf(0x68);
while (_buffer.length >= mcuReportHeaderBytes) {
final headIndex = _findReportHeadIndex();
if (headIndex < 0) {
_buffer.clear();
if (_buffer.length > 3) {
_buffer.removeRange(0, _buffer.length - 3);
}
break;
}
......@@ -446,23 +487,43 @@ class McuReportFrameDecoder {
_buffer.removeRange(0, headIndex);
}
if (_buffer.length < mcuReportBytes) {
if (_buffer.length < mcuReportHeaderBytes) {
break;
}
final frame = List<int>.from(_buffer.take(mcuReportBytes));
final payloadLength = _buffer[4];
final frameBytes =
mcuReportHeaderBytes + payloadLength + mcuReportTailBytes;
if (_buffer.length < frameBytes) {
reports.add(McuReportParser.parseBytes(List<int>.from(_buffer)));
break;
}
final frame = List<int>.from(_buffer.take(frameBytes));
if (frame.last != 0x16) {
_buffer.removeAt(0);
continue;
}
reports.add(McuReportParser.parseBytes(frame));
_buffer.removeRange(0, mcuReportBytes);
_buffer.removeRange(0, frameBytes);
}
return reports;
}
int _findReportHeadIndex() {
for (var index = 0; index <= _buffer.length - 4; index++) {
if (_buffer[index] == 0x68 &&
_buffer[index + 1] == 0x02 &&
_buffer[index + 2] == 0x01 &&
_buffer[index + 3] == 0x01) {
return index;
}
}
return -1;
}
void clear() {
_buffer.clear();
}
......
import 'dart:async';
import 'package:laki_icu_app/models/bo/bluetooth_read_model.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
class EventBus {
final StreamController<dynamic> _controller = StreamController.broadcast();
......@@ -41,3 +42,10 @@ class BluetoothReadInfoChangedEvent {
class BluetoothReadInfoClearedEvent {
const BluetoothReadInfoClearedEvent();
}
// MCU上报数据更新事件
class McuReportChangedEvent {
final McuReport report;
const McuReportChangedEvent(this.report);
}
......@@ -3,6 +3,7 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/services/webrtc_service.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
import 'package:laki_icu_app/utils/mqtt/index.dart';
enum MonitoringIndexStatus {
initial,
......@@ -61,6 +62,15 @@ class MonitoringIndexState extends Equatable {
/// 最近解析出的 MCU 上报数据
final McuReport? latestMcuReport;
/// MQTT 连接状态
final LakiMqttConnectionState mqttConnectionState;
/// MQTT 状态提示
final String? mqttMessage;
/// 当前 MQTT 连接使用的设备 SN
final String? mqttDeviceSn;
const MonitoringIndexState({
this.status = MonitoringIndexStatus.initial,
this.isLoading = false,
......@@ -82,6 +92,9 @@ class MonitoringIndexState extends Equatable {
this.bluetoothMessage,
this.latestBluetoothRawHex,
this.latestMcuReport,
this.mqttConnectionState = LakiMqttConnectionState.disconnected,
this.mqttMessage,
this.mqttDeviceSn,
});
MonitoringIndexState copyWith({
......@@ -105,9 +118,13 @@ class MonitoringIndexState extends Equatable {
String? bluetoothMessage,
String? latestBluetoothRawHex,
McuReport? latestMcuReport,
LakiMqttConnectionState? mqttConnectionState,
String? mqttMessage,
String? mqttDeviceSn,
bool clearBoundBluetoothDevice = false,
bool clearLatestMcuReport = false,
bool clearPatientInfo = false,
bool clearMqttDeviceSn = false,
}) {
return MonitoringIndexState(
status: status ?? this.status,
......@@ -138,6 +155,10 @@ class MonitoringIndexState extends Equatable {
latestBluetoothRawHex ?? this.latestBluetoothRawHex,
latestMcuReport:
clearLatestMcuReport ? null : latestMcuReport ?? this.latestMcuReport,
mqttConnectionState: mqttConnectionState ?? this.mqttConnectionState,
mqttMessage: mqttMessage ?? this.mqttMessage,
mqttDeviceSn:
clearMqttDeviceSn ? null : mqttDeviceSn ?? this.mqttDeviceSn,
);
}
......@@ -163,5 +184,8 @@ class MonitoringIndexState extends Equatable {
bluetoothMessage,
latestBluetoothRawHex,
latestMcuReport,
mqttConnectionState,
mqttMessage,
mqttDeviceSn,
];
}
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