Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
laki_icu_app
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张宏
laki_icu_app
Commits
eec1450c
Commit
eec1450c
authored
Jun 26, 2026
by
张宏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
设备告警、摄像头告警 上下行对接联调完成
parent
fb9b8d79
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
310 additions
and
32 deletions
+310
-32
device_control_bloc.dart
lib/blocs/device_control/device_control_bloc.dart
+92
-1
device_threshold_config_bloc.dart
...device_threshold_config/device_threshold_config_bloc.dart
+5
-0
main.dart
lib/main.dart
+6
-2
monitoring_bo.dart
lib/models/bo/monitoring_bo.dart
+35
-1
cabin_repository.dart
lib/repositories/cabin_repository.dart
+2
-2
event_bus.dart
lib/utils/event_bus.dart
+9
-0
laki_mqtt_client.dart
lib/utils/mqtt/laki_mqtt_client.dart
+42
-0
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+97
-26
monitoring_index_state.dart
lib/views/monitoring/index/cubit/monitoring_index_state.dart
+22
-0
No files found.
lib/blocs/device_control/device_control_bloc.dart
View file @
eec1450c
import
'dart:async'
;
import
'dart:async'
;
import
'package:hydrated_bloc/hydrated_bloc.dart'
;
import
'package:hydrated_bloc/hydrated_bloc.dart'
;
import
'package:intl/intl.dart'
;
import
'device_control_event.dart'
;
import
'device_control_event.dart'
;
import
'device_control_state.dart'
;
import
'device_control_state.dart'
;
import
'package:laki_icu_app/models/bo/device_control_bo.dart'
;
import
'package:laki_icu_app/models/bo/device_control_bo.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/blocs/device_threshold_config/device_threshold_config.dart'
;
import
'package:laki_icu_app/utils/bluetooth/index.dart'
;
import
'package:laki_icu_app/utils/bluetooth/index.dart'
;
import
'package:laki_icu_app/utils/event_bus.dart'
;
import
'package:laki_icu_app/utils/event_bus.dart'
;
import
'package:laki_icu_app/utils/logger.dart'
;
import
'package:laki_icu_app/utils/logger.dart'
;
import
'package:laki_icu_app/utils/mqtt/index.dart'
;
import
'package:laki_icu_app/utils/storage/storage_service.dart'
;
import
'package:laki_icu_app/utils/storage/storage_service.dart'
;
/// 用途:设备控制Bloc
/// 用途:设备控制Bloc
...
@@ -27,7 +31,7 @@ class DeviceControlBloc
...
@@ -27,7 +31,7 @@ class DeviceControlBloc
// ==================== 基础事件 ====================
// ==================== 基础事件 ====================
on
<
DeviceControlLoadRequested
>(
_onLoadRequested
);
on
<
DeviceControlLoadRequested
>(
_onLoadRequested
);
on
<
DeviceControlDataUpdated
>(
_onDataUpdated
);
on
<
DeviceControlDataUpdated
>(
_onDataUpdated
);
on
<
DeviceControlMcuReportFetched
>(
_onMcuReportFetched
);
on
<
DeviceControlMcuReportFetched
>(
_onMcuReportFetched
);
// 蓝牙上报设备值,更新bloc
// ==================== 设备SN事件 ====================
// ==================== 设备SN事件 ====================
on
<
DeviceControlSnFetched
>(
_onSnFetched
);
on
<
DeviceControlSnFetched
>(
_onSnFetched
);
...
@@ -121,6 +125,10 @@ class DeviceControlBloc
...
@@ -121,6 +125,10 @@ class DeviceControlBloc
late
final
StreamSubscription
<
McuReportChangedEvent
>
_mcuReportSub
;
late
final
StreamSubscription
<
McuReportChangedEvent
>
_mcuReportSub
;
late
final
StreamSubscription
<
BluetoothDataPacket
>
_bluetoothDataSub
;
late
final
StreamSubscription
<
BluetoothDataPacket
>
_bluetoothDataSub
;
/// 上次设备告警分发时间(30s 防抖)
DateTime
?
_lastAlarmDispatchTime
;
static
const
Duration
_alarmDispatchInterval
=
Duration
(
seconds:
30
);
// ==================== HydratedBloc 持久化 ====================
// ==================== HydratedBloc 持久化 ====================
@override
@override
...
@@ -244,6 +252,89 @@ class DeviceControlBloc
...
@@ -244,6 +252,89 @@ class DeviceControlBloc
),
),
);
);
emit
(
state
.
copyWith
(
data:
newData
,
clearError:
true
));
emit
(
state
.
copyWith
(
data:
newData
,
clearError:
true
));
// 异步分发:触发设备异常告警(MQTT 上行 + UI 通知)
_dispatchDeviceAlarm
(
newData
);
}
// ==================== 设备异常告警分发 ====================
/// 每隔 [_alarmDispatchInterval](默认 30s)检查一次传感器值是否超出阈值,
/// 并通过 MQTT 上行告警、EventBus 通知 UI 层插入/更新设备告警。
void
_dispatchDeviceAlarm
(
DeviceControlBO
data
)
{
// 30s 防抖
final
now
=
DateTime
.
now
();
if
(
_lastAlarmDispatchTime
!=
null
&&
now
.
difference
(
_lastAlarmDispatchTime
!)
<
_alarmDispatchInterval
)
{
return
;
}
_lastAlarmDispatchTime
=
now
;
// 获取全局阈值配置
final
thresholdBloc
=
globalThresholdConfigBloc
;
if
(
thresholdBloc
==
null
)
return
;
// 设备 SN 为空时无法上行 MQTT 和通知 UI
final
sn
=
data
.
sn
;
if
(
sn
==
null
||
sn
.
isEmpty
)
return
;
// 解析传感器值(温度 ×10 对齐阈值精度)
final
tempDouble
=
double
.
tryParse
(
data
.
cabinTemp
.
currentValue
);
final
temp
=
tempDouble
!=
null
?
(
tempDouble
*
10
).
round
()
:
null
;
final
humidity
=
int
.
tryParse
(
data
.
cabinHumidity
.
currentValue
);
final
oxygen
=
int
.
tryParse
(
data
.
oxygenConcentration
.
currentValue
);
final
config
=
thresholdBloc
.
state
.
config
;
// 6 个独立阈值判断(温度阈值 ×10 对齐)
final
tempUpper
=
temp
!=
null
&&
temp
>
config
.
tempMax
*
10
;
final
tempLower
=
temp
!=
null
&&
temp
<
config
.
tempMin
*
10
;
final
humiUpper
=
humidity
!=
null
&&
humidity
>
config
.
humidityMax
;
final
humiLower
=
humidity
!=
null
&&
humidity
<
config
.
humidityMin
;
final
oxyUpper
=
oxygen
!=
null
&&
oxygen
>
config
.
oxygenMax
;
final
oxyLower
=
oxygen
!=
null
&&
oxygen
<
config
.
oxygenMin
;
final
hasAnyAlarm
=
tempUpper
||
tempLower
||
humiUpper
||
humiLower
||
oxyUpper
||
oxyLower
;
// MQTT 上行(仅存在告警时才发送)
final
mqttClient
=
globalMqttClient
;
if
(
hasAnyAlarm
&&
mqttClient
!=
null
&&
mqttClient
.
isConnected
)
{
mqttClient
.
publishDeviceAlarm
(
deviceSn:
sn
,
temperatureAlarmUpper:
tempUpper
,
temperatureAlarmLower:
tempLower
,
humidityAlarmUpper:
humiUpper
,
humidityAlarmLower:
humiLower
,
oxygenAlarmUpper:
oxyUpper
,
oxygenAlarmLower:
oxyLower
,
);
}
// 通过 EventBus 通知 UI 层
if
(
hasAnyAlarm
)
{
final
descParts
=
<
String
>[];
if
(
tempUpper
)
descParts
.
add
(
'温度过高'
);
if
(
tempLower
)
descParts
.
add
(
'温度过低'
);
if
(
humiUpper
)
descParts
.
add
(
'湿度过高'
);
if
(
humiLower
)
descParts
.
add
(
'湿度过低'
);
if
(
oxyUpper
)
descParts
.
add
(
'氧浓度过高'
);
if
(
oxyLower
)
descParts
.
add
(
'氧浓度过低'
);
final
alarmInfo
=
AlertInfoBO
(
title:
'设备传感器异常'
,
description:
descParts
.
join
(
'、'
),
time:
DateFormat
(
'yyyy-MM-dd HH:mm:ss'
).
format
(
now
),
status:
'待处理'
,
isDeviceAlarm:
true
,
);
eventBus
.
emit
(
DeviceSensorAlarmEvent
(
sn:
sn
,
alarmInfo:
alarmInfo
),
);
}
else
{
eventBus
.
emit
(
DeviceSensorAlarmEvent
(
sn:
sn
),
);
}
}
}
// ==================== 设备SN事件处理 ====================
// ==================== 设备SN事件处理 ====================
...
...
lib/blocs/device_threshold_config/device_threshold_config_bloc.dart
View file @
eec1450c
...
@@ -278,3 +278,8 @@ class DeviceThresholdConfigBloc extends HydratedBloc<DeviceThresholdConfigEvent,
...
@@ -278,3 +278,8 @@ class DeviceThresholdConfigBloc extends HydratedBloc<DeviceThresholdConfigEvent,
};
};
}
}
}
}
/// 全局阈值配置 Bloc 引用。
///
/// 由 main.dart 在创建时写入,[DeviceControlBloc._dispatchDeviceAlarm] 通过此引用读取阈值。
DeviceThresholdConfigBloc
?
globalThresholdConfigBloc
;
lib/main.dart
View file @
eec1450c
...
@@ -90,8 +90,12 @@ class _MyAppState extends State<MyApp> {
...
@@ -90,8 +90,12 @@ class _MyAppState extends State<MyApp> {
DeviceControlBloc
get
_deviceControlBlocInstance
=>
DeviceControlBloc
get
_deviceControlBlocInstance
=>
_deviceControlBloc
??=
DeviceControlBloc
();
_deviceControlBloc
??=
DeviceControlBloc
();
DeviceThresholdConfigBloc
get
_deviceThresholdConfigBlocInstance
=>
DeviceThresholdConfigBloc
get
_deviceThresholdConfigBlocInstance
{
_deviceThresholdConfigBloc
??=
DeviceThresholdConfigBloc
();
if
(
_deviceThresholdConfigBloc
!=
null
)
return
_deviceThresholdConfigBloc
!;
_deviceThresholdConfigBloc
=
DeviceThresholdConfigBloc
();
globalThresholdConfigBloc
=
_deviceThresholdConfigBloc
;
return
_deviceThresholdConfigBloc
!;
}
@override
@override
void
initState
()
{
void
initState
()
{
...
...
lib/models/bo/monitoring_bo.dart
View file @
eec1450c
...
@@ -125,12 +125,16 @@ class AlertInfoBO extends Equatable {
...
@@ -125,12 +125,16 @@ class AlertInfoBO extends Equatable {
/// MQTT 下发告警记录 ID,上行 camera/{SN}/up 时使用
/// MQTT 下发告警记录 ID,上行 camera/{SN}/up 时使用
final
int
?
alarmId
;
final
int
?
alarmId
;
/// 是否为设备传感器告警(区别于摄像头告警)
final
bool
isDeviceAlarm
;
const
AlertInfoBO
({
const
AlertInfoBO
({
required
this
.
title
,
required
this
.
title
,
required
this
.
description
,
required
this
.
description
,
required
this
.
time
,
required
this
.
time
,
this
.
status
=
'已处理'
,
this
.
status
=
'已处理'
,
this
.
alarmId
,
this
.
alarmId
,
this
.
isDeviceAlarm
=
false
,
});
});
factory
AlertInfoBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
factory
AlertInfoBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
...
@@ -140,11 +144,41 @@ class AlertInfoBO extends Equatable {
...
@@ -140,11 +144,41 @@ class AlertInfoBO extends Equatable {
time:
json
[
'time'
]
as
String
?
??
''
,
time:
json
[
'time'
]
as
String
?
??
''
,
status:
json
[
'status'
]
as
String
?
??
'已处理'
,
status:
json
[
'status'
]
as
String
?
??
'已处理'
,
alarmId:
json
[
'alarmId'
]
as
int
?,
alarmId:
json
[
'alarmId'
]
as
int
?,
isDeviceAlarm:
json
[
'isDeviceAlarm'
]
as
bool
?
??
false
,
);
}
Map
<
String
,
dynamic
>
toJson
()
{
return
{
'title'
:
title
,
'description'
:
description
,
'time'
:
time
,
'status'
:
status
,
'alarmId'
:
alarmId
,
'isDeviceAlarm'
:
isDeviceAlarm
,
};
}
AlertInfoBO
copyWith
({
String
?
title
,
String
?
description
,
String
?
time
,
String
?
status
,
int
?
alarmId
,
bool
?
isDeviceAlarm
,
})
{
return
AlertInfoBO
(
title:
title
??
this
.
title
,
description:
description
??
this
.
description
,
time:
time
??
this
.
time
,
status:
status
??
this
.
status
,
alarmId:
alarmId
??
this
.
alarmId
,
isDeviceAlarm:
isDeviceAlarm
??
this
.
isDeviceAlarm
,
);
);
}
}
@override
@override
List
<
Object
?>
get
props
=>
[
title
,
description
,
time
,
status
,
alarmId
];
List
<
Object
?>
get
props
=>
[
title
,
description
,
time
,
status
,
alarmId
,
isDeviceAlarm
];
}
}
/// 用途:首页监护舱右侧设备菜单数据
/// 用途:首页监护舱右侧设备菜单数据
...
...
lib/repositories/cabin_repository.dart
View file @
eec1450c
...
@@ -20,8 +20,8 @@ class CabinRepository {
...
@@ -20,8 +20,8 @@ class CabinRepository {
// }
// }
return
DioRequest
.
instance
.
get
<
CabinDetailResponse
>(
return
DioRequest
.
instance
.
get
<
CabinDetailResponse
>(
'/icu/detail'
,
'/icu/detail'
,
//
queryParameters: {'carbinSn': carbinSn},
queryParameters:
{
'carbinSn'
:
carbinSn
},
queryParameters:
{
'carbinSn'
:
'CNA07212L'
},
//
queryParameters: {'carbinSn': 'CNA07212L'},
fromJsonT:
(
data
)
=>
fromJsonT:
(
data
)
=>
CabinDetailResponse
.
fromJson
(
data
as
Map
<
String
,
dynamic
>),
CabinDetailResponse
.
fromJson
(
data
as
Map
<
String
,
dynamic
>),
);
);
...
...
lib/utils/event_bus.dart
View file @
eec1450c
import
'dart:async'
;
import
'dart:async'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/utils/bluetooth/index.dart'
;
import
'package:laki_icu_app/utils/bluetooth/index.dart'
;
class
EventBus
{
class
EventBus
{
...
@@ -49,3 +50,11 @@ class McuReportChangedEvent {
...
@@ -49,3 +50,11 @@ class McuReportChangedEvent {
const
McuReportChangedEvent
(
this
.
report
);
const
McuReportChangedEvent
(
this
.
report
);
}
}
// 设备传感器告警事件(温度/湿度/氧浓度阈值异常)
class
DeviceSensorAlarmEvent
{
final
String
sn
;
final
AlertInfoBO
?
alarmInfo
;
// 非 null = 有异常,null = 传感器正常
const
DeviceSensorAlarmEvent
({
required
this
.
sn
,
this
.
alarmInfo
});
}
lib/utils/mqtt/laki_mqtt_client.dart
View file @
eec1450c
...
@@ -301,6 +301,42 @@ class LakiMqttClient {
...
@@ -301,6 +301,42 @@ class LakiMqttClient {
);
);
}
}
/// 上行 传感器(设备)异常告警
///
/// 对应 topic: icu/{SN}/alarm → DeviceAlarmHandler
/// 仅当任一 alarm 字段为 true 时才调用此方法。
int
publishDeviceAlarm
({
required
String
deviceSn
,
required
bool
temperatureAlarmUpper
,
required
bool
temperatureAlarmLower
,
required
bool
humidityAlarmUpper
,
required
bool
humidityAlarmLower
,
required
bool
oxygenAlarmUpper
,
required
bool
oxygenAlarmLower
,
MqttQos
qos
=
MqttQos
.
atLeastOnce
,
})
{
return
publishJson
(
LakiMqttTopics
.
icuAlarm
(
deviceSn
),
{
'eventId'
:
deviceSn
,
'deviceVersionNo'
:
''
,
'deviceType'
:
'ICU'
,
'timestamp'
:
DateTime
.
now
().
millisecondsSinceEpoch
,
'type'
:
'alarm'
,
'data'
:
{
'sn'
:
deviceSn
,
'temperatureAlarmUpper'
:
temperatureAlarmUpper
,
'temperatureAlarmLower'
:
temperatureAlarmLower
,
'humidityAlarmUpper'
:
humidityAlarmUpper
,
'humidityAlarmLower'
:
humidityAlarmLower
,
'oxygenAlarmUpper'
:
oxygenAlarmUpper
,
'oxygenAlarmLower'
:
oxygenAlarmLower
,
},
},
qos:
qos
,
);
}
/// 上行 出舱数据
/// 上行 出舱数据
/// create by stephen
/// create by stephen
/// [deviceSn] 设备 SN,[type] 操作类型(unbindPet),
/// [deviceSn] 设备 SN,[type] 操作类型(unbindPet),
...
@@ -399,3 +435,9 @@ class LakiMqttClient {
...
@@ -399,3 +435,9 @@ class LakiMqttClient {
}
}
}
}
}
}
/// 全局 MQTT 客户端引用。
///
/// 由 [MonitoringIndexCubit] 在连接成功时写入,断开时清空。
/// [DeviceControlBloc._dispatchDeviceAlarm] 通过此引用获取 client 上行告警。
LakiMqttClient
?
globalMqttClient
;
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
eec1450c
import
'dart:async'
;
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:
flutter_bloc/flutter
_bloc.dart'
;
import
'package:
hydrated_bloc/hydrated
_bloc.dart'
;
import
'package:laki_icu_app/enums/video_stream_mode_enum.dart'
;
import
'package:laki_icu_app/enums/video_stream_mode_enum.dart'
;
import
'package:laki_icu_app/utils/date_utils.dart'
;
import
'package:laki_icu_app/utils/date_utils.dart'
;
...
@@ -21,7 +21,7 @@ import 'monitoring_index_state.dart';
...
@@ -21,7 +21,7 @@ import 'monitoring_index_state.dart';
/// 设备凭据 —— 从舱详情接口获取 cameraSn 和 wifiPwd
/// 设备凭据 —— 从舱详情接口获取 cameraSn 和 wifiPwd
class
MonitoringIndexCubit
extends
Cubit
<
MonitoringIndexState
>
{
class
MonitoringIndexCubit
extends
Hydrated
Cubit
<
MonitoringIndexState
>
{
final
CabinService
_cabinService
;
final
CabinService
_cabinService
;
final
WebrtcService
_webrtcService
;
final
WebrtcService
_webrtcService
;
final
P2pVideoService
_p2pVideoService
;
final
P2pVideoService
_p2pVideoService
;
...
@@ -38,6 +38,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -38,6 +38,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
StreamSubscription
<
BluetoothReadInfoChangedEvent
>?
_bluetoothReadInfoSub
;
StreamSubscription
<
BluetoothReadInfoChangedEvent
>?
_bluetoothReadInfoSub
;
StreamSubscription
<
McuReportChangedEvent
>?
_mcuReportChangedSub
;
StreamSubscription
<
McuReportChangedEvent
>?
_mcuReportChangedSub
;
StreamSubscription
<
DeviceSensorAlarmEvent
>?
_deviceAlarmSub
;
StreamSubscription
<
LakiMqttConnectionState
>?
_mqttStateSub
;
StreamSubscription
<
LakiMqttConnectionState
>?
_mqttStateSub
;
StreamSubscription
<
LakiMqttMessage
>?
_mqttMessageSub
;
StreamSubscription
<
LakiMqttMessage
>?
_mqttMessageSub
;
StreamSubscription
<
String
>?
_mqttErrorSub
;
StreamSubscription
<
String
>?
_mqttErrorSub
;
...
@@ -75,11 +76,12 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -75,11 +76,12 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_listenBluetoothState
();
_listenBluetoothState
();
_listenBluetoothReadInfo
();
_listenBluetoothReadInfo
();
_listenMcuReportChanged
();
_listenMcuReportChanged
();
_listenDeviceSensorAlarm
();
_loadBoundBluetoothDevice
();
_loadBoundBluetoothDevice
();
// // todo 测试使用 待删除
// // todo 测试使用 待删除
_fetchCabinDetailAndConnectVideo
(
"CNA07212L"
);
//
_fetchCabinDetailAndConnectVideo("CNA07212L");
_connectMqttForDeviceSn
(
"CNA07212L"
);
//
_connectMqttForDeviceSn("CNA07212L");
}
}
/// 监听 WebRTC 连接状态变化并同步到 State
/// 监听 WebRTC 连接状态变化并同步到 State
...
@@ -198,6 +200,34 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -198,6 +200,34 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
});
});
}
}
void
_listenDeviceSensorAlarm
()
{
_deviceAlarmSub
=
eventBus
.
on
<
DeviceSensorAlarmEvent
>().
listen
(
_onDeviceSensorAlarm
);
}
void
_onDeviceSensorAlarm
(
DeviceSensorAlarmEvent
event
)
{
if
(
isClosed
)
return
;
if
(
event
.
alarmInfo
!=
null
)
{
// 有传感器异常:插入或更新设备告警
final
existingIndex
=
state
.
alerts
.
indexWhere
((
a
)
=>
a
.
isDeviceAlarm
);
if
(
existingIndex
>=
0
)
{
// 已存在 → 替换,保留原有 status
final
existingStatus
=
state
.
alerts
[
existingIndex
].
status
;
final
updatedAlerts
=
List
<
AlertInfoBO
>.
from
(
state
.
alerts
);
updatedAlerts
[
existingIndex
]
=
event
.
alarmInfo
!.
copyWith
(
status:
existingStatus
);
emit
(
state
.
copyWith
(
alerts:
updatedAlerts
));
}
else
{
// 不存在 → 插入头部
emit
(
state
.
copyWith
(
alerts:
[
event
.
alarmInfo
!,
...
state
.
alerts
]));
}
}
else
{
// 传感器恢复正常,不删除已有告警
// TODO: 后续业务需要持久化时在此补充持久化逻辑
}
}
void
_listenBluetoothReadInfo
()
{
void
_listenBluetoothReadInfo
()
{
_bluetoothReadInfoSub
=
_bluetoothReadInfoSub
=
eventBus
.
on
<
BluetoothReadInfoChangedEvent
>().
listen
((
event
)
{
eventBus
.
on
<
BluetoothReadInfoChangedEvent
>().
listen
((
event
)
{
...
@@ -638,6 +668,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -638,6 +668,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
logging:
kDebugMode
,
logging:
kDebugMode
,
),
),
);
);
globalMqttClient
=
_mqttClient
;
_listenMqttState
(
_mqttClient
!);
_listenMqttState
(
_mqttClient
!);
}
}
...
@@ -857,6 +888,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -857,6 +888,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_mqttErrorSub
=
null
;
_mqttErrorSub
=
null
;
await
_mqttClient
?.
dispose
();
await
_mqttClient
?.
dispose
();
_mqttClient
=
null
;
_mqttClient
=
null
;
globalMqttClient
=
null
;
}
}
List
<
MonitoringMetricBO
>
_metricsFromBluetoothReport
(
List
<
MonitoringMetricBO
>
_metricsFromBluetoothReport
(
...
@@ -1084,6 +1116,20 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -1084,6 +1116,20 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
emit
(
state
.
copyWith
(
selectedAlert:
alert
));
emit
(
state
.
copyWith
(
selectedAlert:
alert
));
}
}
// ==================== HydratedCubit 持久化 ====================
@override
MonitoringIndexState
?
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
MonitoringIndexState
.
fromJson
(
json
);
}
@override
Map
<
String
,
dynamic
>?
toJson
(
MonitoringIndexState
state
)
{
return
state
.
toJson
();
}
// ==================== 告警操作 ====================
/// 清除选中的告警(用于关闭弹窗)
/// 清除选中的告警(用于关闭弹窗)
void
clearSelectedAlert
()
{
void
clearSelectedAlert
()
{
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
...
@@ -1092,36 +1138,60 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -1092,36 +1138,60 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
/// 标记告警为已处理
/// 标记告警为已处理
///
///
/// 通过 MQTT camera/{SN}/up 上行处理结果,并从列表中移除。
/// 设备告警:仅从 alerts 中移除,不上行 MQTT camera。
/// 摄像头告警:通过 MQTT camera/{SN}/up 上行处理结果,并从列表中移除。
void
markAlertAsProcessed
(
AlertInfoBO
alert
)
{
void
markAlertAsProcessed
(
AlertInfoBO
alert
)
{
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
_publishCameraAlarmResult
(
alert
,
'handled'
);
if
(
alert
.
isDeviceAlarm
)
{
final
removedCameraAlerts
=
// 设备告警:不上行 MQTT,仅移除
state
.
cameraAlerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
final
removedAlerts
=
final
removedAlerts
=
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
alerts:
removedAlerts
,
cameraAlerts:
removedCameraAlerts
,
clearSelectedAlert:
true
,
alerts:
removedAlerts
,
));
selectedAlert:
null
,
}
else
{
));
// 摄像头告警:保持原有逻辑
_publishCameraAlarmResult
(
alert
,
'handled'
);
final
removedCameraAlerts
=
state
.
cameraAlerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
final
removedAlerts
=
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
emit
(
state
.
copyWith
(
cameraAlerts:
removedCameraAlerts
,
alerts:
removedAlerts
,
clearSelectedAlert:
true
,
));
}
}
}
/// 标记告警为误报
/// 标记告警为误报
///
///
/// 通过 MQTT camera/{SN}/up 上行处理结果,并从列表中移除。
/// 设备告警:仅从 alerts 中移除,不上行 MQTT camera。
/// 摄像头告警:通过 MQTT camera/{SN}/up 上行处理结果,并从列表中移除。
void
markAlertAsFalse
(
AlertInfoBO
alert
)
{
void
markAlertAsFalse
(
AlertInfoBO
alert
)
{
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
_publishCameraAlarmResult
(
alert
,
'false_alarm'
);
if
(
alert
.
isDeviceAlarm
)
{
final
removedCameraAlerts
=
// 设备告警:不上行 MQTT,仅移除
state
.
cameraAlerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
final
removedAlerts
=
final
removedAlerts
=
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
alerts:
removedAlerts
,
cameraAlerts:
removedCameraAlerts
,
clearSelectedAlert:
true
,
alerts:
removedAlerts
,
));
selectedAlert:
null
,
}
else
{
));
// 摄像头告警:保持原有逻辑
_publishCameraAlarmResult
(
alert
,
'false_alarm'
);
final
removedCameraAlerts
=
state
.
cameraAlerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
final
removedAlerts
=
state
.
alerts
.
where
((
item
)
=>
item
!=
alert
).
toList
();
emit
(
state
.
copyWith
(
cameraAlerts:
removedCameraAlerts
,
alerts:
removedAlerts
,
clearSelectedAlert:
true
,
));
}
}
}
/// 上行 MQTT camera/{SN}/up 告警处理结果
/// 上行 MQTT camera/{SN}/up 告警处理结果
...
@@ -1156,6 +1226,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -1156,6 +1226,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_bluetoothDataSub
?.
cancel
();
_bluetoothDataSub
?.
cancel
();
_bluetoothReadInfoSub
?.
cancel
();
_bluetoothReadInfoSub
?.
cancel
();
_mcuReportChangedSub
?.
cancel
();
_mcuReportChangedSub
?.
cancel
();
_deviceAlarmSub
?.
cancel
();
_webrtcService
.
dispose
();
_webrtcService
.
dispose
();
_p2pVideoService
.
dispose
();
_p2pVideoService
.
dispose
();
return
_disposeMqttClient
().
then
((
_
)
=>
super
.
close
());
return
_disposeMqttClient
().
then
((
_
)
=>
super
.
close
());
...
...
lib/views/monitoring/index/cubit/monitoring_index_state.dart
View file @
eec1450c
...
@@ -189,6 +189,28 @@ class MonitoringIndexState extends Equatable {
...
@@ -189,6 +189,28 @@ class MonitoringIndexState extends Equatable {
);
);
}
}
/// 从本地持久化 JSON 反序列化(仅恢复 alerts 和 cameraAlerts)
factory
MonitoringIndexState
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
MonitoringIndexState
(
alerts:
(
json
[
'alerts'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
AlertInfoBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
const
[],
cameraAlerts:
(
json
[
'cameraAlerts'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
AlertInfoBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
const
[],
);
}
/// 序列化为本地持久化 JSON(仅持久化 alerts 和 cameraAlerts)
Map
<
String
,
dynamic
>
toJson
()
{
return
{
'alerts'
:
alerts
.
map
((
e
)
=>
e
.
toJson
()).
toList
(),
'cameraAlerts'
:
cameraAlerts
.
map
((
e
)
=>
e
.
toJson
()).
toList
(),
};
}
@override
@override
List
<
Object
?>
get
props
=>
[
List
<
Object
?>
get
props
=>
[
status
,
status
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment