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
12e86f2f
Commit
12e86f2f
authored
Jun 23, 2026
by
akari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 蓝牙数据同步到主页
parent
e5f6d0a9
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
388 additions
and
25 deletions
+388
-25
auth_bloc.dart
lib/blocs/auth/auth_bloc.dart
+1
-2
bluetooth_read_bloc.dart
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
+67
-0
bluetooth_read_event.dart
lib/blocs/bluetooth_read/bluetooth_read_event.dart
+26
-0
bluetooth_read_state.dart
lib/blocs/bluetooth_read/bluetooth_read_state.dart
+30
-0
main.dart
lib/main.dart
+13
-2
bluetooth_read_model.dart
lib/models/bo/bluetooth_read_model.dart
+64
-0
user_model.dart
lib/models/bo/user_model.dart
+0
-3
bluetooth_manager.dart
lib/utils/bluetooth/bluetooth_manager.dart
+4
-0
event_bus.dart
lib/utils/event_bus.dart
+14
-0
storage_service.dart
lib/utils/storage/storage_service.dart
+15
-13
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+154
-5
No files found.
lib/blocs/auth/auth_bloc.dart
View file @
12e86f2f
...
@@ -28,8 +28,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
...
@@ -28,8 +28,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
try
{
try
{
// 新接口在登录响应中直接返回用户信息
// 新接口在登录响应中直接返回用户信息
final
loginModel
=
final
loginModel
=
await
_authService
.
login
(
event
.
phone
,
event
.
smsCode
);
await
_authService
.
login
(
event
.
phone
,
event
.
smsCode
);
if
(
loginModel
.
user
!=
null
)
{
if
(
loginModel
.
user
!=
null
)
{
emit
(
AuthSuccess
(
loginModel
.
user
!));
emit
(
AuthSuccess
(
loginModel
.
user
!));
...
...
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
0 → 100644
View file @
12e86f2f
import
'dart:async'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/utils/event_bus.dart'
;
import
'package:laki_icu_app/utils/storage/storage_service.dart'
;
import
'bluetooth_read_event.dart'
;
import
'bluetooth_read_state.dart'
;
class
BluetoothReadBloc
extends
Bloc
<
BluetoothReadEvent
,
BluetoothReadState
>
{
final
StorageService
_storageService
;
late
final
StreamSubscription
<
BluetoothReadInfoChangedEvent
>
_infoChangedSub
;
late
final
StreamSubscription
<
BluetoothReadInfoClearedEvent
>
_infoClearedSub
;
BluetoothReadBloc
({
required
StorageService
storageService
,
})
:
_storageService
=
storageService
,
super
(
const
BluetoothReadState
())
{
on
<
BluetoothReadLoadRequested
>(
_onLoadRequested
);
on
<
BluetoothReadUpdated
>(
_onUpdated
);
on
<
BluetoothReadCleared
>(
_onCleared
);
_infoChangedSub
=
eventBus
.
on
<
BluetoothReadInfoChangedEvent
>().
listen
(
(
event
)
=>
add
(
BluetoothReadUpdated
(
event
.
info
)),
);
_infoClearedSub
=
eventBus
.
on
<
BluetoothReadInfoClearedEvent
>().
listen
(
(
_
)
=>
add
(
const
BluetoothReadCleared
()),
);
add
(
const
BluetoothReadLoadRequested
());
}
Future
<
void
>
_onLoadRequested
(
BluetoothReadLoadRequested
event
,
Emitter
<
BluetoothReadState
>
emit
,
)
async
{
emit
(
state
.
copyWith
(
isLoading:
true
,
clearError:
true
));
try
{
final
info
=
await
_storageService
.
getBluetoothReadInfo
();
emit
(
state
.
copyWith
(
info:
info
,
isLoading:
false
,
clearError:
true
));
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
e
.
toString
()));
}
}
void
_onUpdated
(
BluetoothReadUpdated
event
,
Emitter
<
BluetoothReadState
>
emit
,
)
{
emit
(
state
.
copyWith
(
info:
event
.
info
,
clearError:
true
));
}
void
_onCleared
(
BluetoothReadCleared
event
,
Emitter
<
BluetoothReadState
>
emit
,
)
{
emit
(
state
.
copyWith
(
info:
BluetoothReadModel
.
empty
,
clearError:
true
));
}
@override
Future
<
void
>
close
()
async
{
await
_infoChangedSub
.
cancel
();
await
_infoClearedSub
.
cancel
();
return
super
.
close
();
}
}
lib/blocs/bluetooth_read/bluetooth_read_event.dart
0 → 100644
View file @
12e86f2f
import
'package:equatable/equatable.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
abstract
class
BluetoothReadEvent
extends
Equatable
{
const
BluetoothReadEvent
();
@override
List
<
Object
?>
get
props
=>
[];
}
class
BluetoothReadLoadRequested
extends
BluetoothReadEvent
{
const
BluetoothReadLoadRequested
();
}
class
BluetoothReadUpdated
extends
BluetoothReadEvent
{
final
BluetoothReadModel
info
;
const
BluetoothReadUpdated
(
this
.
info
);
@override
List
<
Object
?>
get
props
=>
[
info
];
}
class
BluetoothReadCleared
extends
BluetoothReadEvent
{
const
BluetoothReadCleared
();
}
lib/blocs/bluetooth_read/bluetooth_read_state.dart
0 → 100644
View file @
12e86f2f
import
'package:equatable/equatable.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
class
BluetoothReadState
extends
Equatable
{
final
BluetoothReadModel
info
;
final
bool
isLoading
;
final
String
?
error
;
const
BluetoothReadState
({
this
.
info
=
BluetoothReadModel
.
empty
,
this
.
isLoading
=
false
,
this
.
error
,
});
BluetoothReadState
copyWith
({
BluetoothReadModel
?
info
,
bool
?
isLoading
,
String
?
error
,
bool
clearError
=
false
,
})
{
return
BluetoothReadState
(
info:
info
??
this
.
info
,
isLoading:
isLoading
??
this
.
isLoading
,
error:
clearError
?
null
:
error
??
this
.
error
,
);
}
@override
List
<
Object
?>
get
props
=>
[
info
,
isLoading
,
error
];
}
lib/main.dart
View file @
12e86f2f
...
@@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
...
@@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import
'package:laki_icu_app/blocs/auth/auth_bloc.dart'
;
import
'package:laki_icu_app/blocs/auth/auth_bloc.dart'
;
import
'package:laki_icu_app/blocs/auth/auth_event.dart'
;
import
'package:laki_icu_app/blocs/auth/auth_event.dart'
;
import
'package:laki_icu_app/blocs/auth/auth_state.dart'
;
import
'package:laki_icu_app/blocs/auth/auth_state.dart'
;
import
'package:laki_icu_app/blocs/bluetooth_read/bluetooth_read_bloc.dart'
;
import
'package:laki_icu_app/routes/app_router.dart'
;
import
'package:laki_icu_app/routes/app_router.dart'
;
import
'package:laki_icu_app/repositories/auth_repository.dart'
;
import
'package:laki_icu_app/repositories/auth_repository.dart'
;
import
'package:laki_icu_app/services/auth_service.dart'
;
import
'package:laki_icu_app/services/auth_service.dart'
;
...
@@ -32,14 +33,14 @@ void main(List<String> args) {
...
@@ -32,14 +33,14 @@ void main(List<String> args) {
// WidgetsFlutterBinding.ensureInitialized();
// WidgetsFlutterBinding.ensureInitialized();
// UME 调试工具(仅 Debug 模式启用)
// UME 调试工具(仅 Debug 模式启用)
if
(
kDebugMode
)
{
if
(
kDebugMode
)
{
MarionetteBinding
.
ensureInitialized
();
MarionetteBinding
.
ensureInitialized
();
PluginManager
.
instance
PluginManager
.
instance
..
register
(
WidgetInfoInspector
())
..
register
(
WidgetInfoInspector
())
..
register
(
WidgetDetailInspector
())
..
register
(
WidgetDetailInspector
())
..
register
(
Console
());
..
register
(
Console
());
// ..register(DioInspector(dio: ApiClient.instance().dio));
// ..register(DioInspector(dio: ApiClient.instance().dio));
runApp
(
UMEWidget
(
enable:
true
,
child:
const
MyApp
()));
runApp
(
UMEWidget
(
enable:
true
,
child:
const
MyApp
()));
}
else
{
}
else
{
WidgetsFlutterBinding
.
ensureInitialized
();
WidgetsFlutterBinding
.
ensureInitialized
();
...
@@ -61,9 +62,15 @@ class _MyAppState extends State<MyApp> {
...
@@ -61,9 +62,15 @@ class _MyAppState extends State<MyApp> {
late
final
AuthService
_authService
;
late
final
AuthService
_authService
;
late
final
AppRouter
_appRouter
;
late
final
AppRouter
_appRouter
;
late
final
AuthBloc
_authBloc
;
late
final
AuthBloc
_authBloc
;
BluetoothReadBloc
?
_bluetoothReadBloc
;
StreamSubscription
?
_tokenExpiredSubscription
;
StreamSubscription
?
_tokenExpiredSubscription
;
bool
_isInitializing
=
true
;
bool
_isInitializing
=
true
;
BluetoothReadBloc
get
_bluetoothReadBlocInstance
=>
_bluetoothReadBloc
??=
BluetoothReadBloc
(
storageService:
_storageService
,
);
@override
@override
void
initState
()
{
void
initState
()
{
super
.
initState
();
super
.
initState
();
...
@@ -146,6 +153,7 @@ class _MyAppState extends State<MyApp> {
...
@@ -146,6 +153,7 @@ class _MyAppState extends State<MyApp> {
void
dispose
()
{
void
dispose
()
{
_tokenExpiredSubscription
?.
cancel
();
_tokenExpiredSubscription
?.
cancel
();
_authBloc
.
close
();
_authBloc
.
close
();
_bluetoothReadBloc
?.
close
();
super
.
dispose
();
super
.
dispose
();
}
}
...
@@ -157,6 +165,9 @@ class _MyAppState extends State<MyApp> {
...
@@ -157,6 +165,9 @@ class _MyAppState extends State<MyApp> {
return
MultiBlocProvider
(
return
MultiBlocProvider
(
providers:
[
providers:
[
BlocProvider
<
AuthBloc
>.
value
(
value:
_authBloc
),
BlocProvider
<
AuthBloc
>.
value
(
value:
_authBloc
),
BlocProvider
<
BluetoothReadBloc
>.
value
(
value:
_bluetoothReadBlocInstance
,
),
// BlocProvider<CounterBloc>(create: (_) => CounterBloc()),
// BlocProvider<CounterBloc>(create: (_) => CounterBloc()),
],
],
child:
MultiBlocListener
(
child:
MultiBlocListener
(
...
...
lib/models/bo/bluetooth_read_model.dart
0 → 100644
View file @
12e86f2f
import
'package:equatable/equatable.dart'
;
import
'package:laki_icu_app/utils/bluetooth/mcu_report.dart'
;
class
BluetoothReadModel
extends
Equatable
{
static
const
empty
=
BluetoothReadModel
();
final
String
?
sn
;
final
double
?
mainTemperature
;
final
int
?
humidity
;
final
int
?
oxygenConcentration
;
final
int
?
co2Measurement
;
final
DateTime
?
updatedAt
;
const
BluetoothReadModel
({
this
.
sn
,
this
.
mainTemperature
,
this
.
humidity
,
this
.
oxygenConcentration
,
this
.
co2Measurement
,
this
.
updatedAt
,
});
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
,
updatedAt:
DateTime
.
now
(),
);
}
bool
get
hasSn
=>
sn
!=
null
&&
sn
!.
isNotEmpty
;
BluetoothReadModel
copyWith
({
String
?
sn
,
double
?
mainTemperature
,
int
?
humidity
,
int
?
oxygenConcentration
,
int
?
co2Measurement
,
DateTime
?
updatedAt
,
bool
clearSn
=
false
,
})
{
return
BluetoothReadModel
(
sn:
clearSn
?
null
:
sn
??
this
.
sn
,
mainTemperature:
mainTemperature
??
this
.
mainTemperature
,
humidity:
humidity
??
this
.
humidity
,
oxygenConcentration:
oxygenConcentration
??
this
.
oxygenConcentration
,
co2Measurement:
co2Measurement
??
this
.
co2Measurement
,
updatedAt:
updatedAt
??
this
.
updatedAt
,
);
}
@override
List
<
Object
?>
get
props
=>
[
sn
,
mainTemperature
,
humidity
,
oxygenConcentration
,
co2Measurement
,
updatedAt
,
];
}
lib/models/bo/user_model.dart
View file @
12e86f2f
...
@@ -11,9 +11,6 @@ class UserModel extends Equatable {
...
@@ -11,9 +11,6 @@ class UserModel extends Equatable {
final
dynamic
hospitalId
;
final
dynamic
hospitalId
;
final
String
?
hospitalName
;
final
String
?
hospitalName
;
// ignore: non_constant_identifier_names
static
String
?
SN
;
const
UserModel
({
const
UserModel
({
required
this
.
id
,
required
this
.
id
,
this
.
nickname
,
this
.
nickname
,
...
...
lib/utils/bluetooth/bluetooth_manager.dart
View file @
12e86f2f
...
@@ -248,6 +248,10 @@ class BleBluetoothManager<T> {
...
@@ -248,6 +248,10 @@ class BleBluetoothManager<T> {
return
;
return
;
}
}
if
(!
await
ensureBluetoothReady
())
{
return
;
}
await
disconnect
();
await
disconnect
();
_connectedDeviceId
=
deviceId
;
_connectedDeviceId
=
deviceId
;
_notifyCharacteristic
=
QualifiedCharacteristic
(
_notifyCharacteristic
=
QualifiedCharacteristic
(
...
...
lib/utils/event_bus.dart
View file @
12e86f2f
import
'dart:async'
;
import
'dart:async'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
class
EventBus
{
class
EventBus
{
final
StreamController
<
dynamic
>
_controller
=
StreamController
.
broadcast
();
final
StreamController
<
dynamic
>
_controller
=
StreamController
.
broadcast
();
...
@@ -27,3 +29,15 @@ class LogoutEvent {}
...
@@ -27,3 +29,15 @@ class LogoutEvent {}
// 打开蓝牙扫描/绑定页面事件
// 打开蓝牙扫描/绑定页面事件
class
OpenBluetoothScanEvent
{}
class
OpenBluetoothScanEvent
{}
// 蓝牙读取信息更新事件
class
BluetoothReadInfoChangedEvent
{
final
BluetoothReadModel
info
;
const
BluetoothReadInfoChangedEvent
(
this
.
info
);
}
// 蓝牙读取信息清空事件
class
BluetoothReadInfoClearedEvent
{
const
BluetoothReadInfoClearedEvent
();
}
lib/utils/storage/storage_service.dart
View file @
12e86f2f
import
'package:flutter_secure_storage/flutter_secure_storage.dart'
;
import
'package:flutter_secure_storage/flutter_secure_storage.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.dart'
;
import
'package:laki_icu_app/models/bo/user_model.dart'
;
import
'package:laki_icu_app/models/bo/user_model.dart'
;
class
StorageService
{
class
StorageService
{
...
@@ -9,7 +10,8 @@ class StorageService {
...
@@ -9,7 +10,8 @@ class StorageService {
static
const
String
_keyRememberSmsCode
=
'remember_sms_code'
;
static
const
String
_keyRememberSmsCode
=
'remember_sms_code'
;
static
const
String
_keyRememberEnabled
=
'remember_enabled'
;
static
const
String
_keyRememberEnabled
=
'remember_enabled'
;
static
const
String
_keyUserInfo
=
'user_info'
;
static
const
String
_keyUserInfo
=
'user_info'
;
static
const
String
_keyUserSn
=
'user_sn'
;
static
const
String
_keyBluetoothReadSn
=
'bluetooth_read_sn'
;
static
const
String
_legacyKeyUserSn
=
'user_sn'
;
static
const
String
_keyClientId
=
'client_id'
;
static
const
String
_keyClientId
=
'client_id'
;
static
const
String
_keyBoundBluetoothDeviceId
=
'bound_bluetooth_device_id'
;
static
const
String
_keyBoundBluetoothDeviceId
=
'bound_bluetooth_device_id'
;
static
const
String
_keyBoundBluetoothDeviceName
=
static
const
String
_keyBoundBluetoothDeviceName
=
...
@@ -76,12 +78,10 @@ class StorageService {
...
@@ -76,12 +78,10 @@ class StorageService {
final
jsonStr
=
final
jsonStr
=
'
${userInfo.id}
|
${userInfo.nickname ?? ''}
|
${userInfo.phone ?? ''}
|
${userInfo.avatar ?? ''}
|
${userInfo.type ?? ''}
|
${userInfo.canControlDevice ?? ''}
|
${userInfo.canViewOperation ?? ''}
|
${userInfo.hospitalId ?? ''}
|
${userInfo.hospitalName ?? ''}
'
;
'
${userInfo.id}
|
${userInfo.nickname ?? ''}
|
${userInfo.phone ?? ''}
|
${userInfo.avatar ?? ''}
|
${userInfo.type ?? ''}
|
${userInfo.canControlDevice ?? ''}
|
${userInfo.canViewOperation ?? ''}
|
${userInfo.hospitalId ?? ''}
|
${userInfo.hospitalName ?? ''}
'
;
await
_storage
.
write
(
key:
_keyUserInfo
,
value:
jsonStr
);
await
_storage
.
write
(
key:
_keyUserInfo
,
value:
jsonStr
);
await
getUserSn
();
}
}
Future
<
UserModel
?>
getUserInfo
()
async
{
Future
<
UserModel
?>
getUserInfo
()
async
{
final
value
=
await
_storage
.
read
(
key:
_keyUserInfo
);
final
value
=
await
_storage
.
read
(
key:
_keyUserInfo
);
UserModel
.
SN
=
await
getUserSn
();
if
(
value
==
null
||
value
.
isEmpty
)
return
null
;
if
(
value
==
null
||
value
.
isEmpty
)
return
null
;
final
parts
=
value
.
split
(
'|'
);
final
parts
=
value
.
split
(
'|'
);
if
(
parts
.
length
<
9
)
return
null
;
if
(
parts
.
length
<
9
)
return
null
;
...
@@ -102,20 +102,22 @@ class StorageService {
...
@@ -102,20 +102,22 @@ class StorageService {
await
_storage
.
delete
(
key:
_keyUserInfo
);
await
_storage
.
delete
(
key:
_keyUserInfo
);
}
}
Future
<
void
>
saveUserSn
(
String
sn
)
async
{
// ==================== 蓝牙读取信息相关 ====================
UserModel
.
SN
=
sn
;
await
_storage
.
write
(
key:
_keyUserSn
,
value:
sn
);
Future
<
void
>
saveBluetoothReadSn
(
String
sn
)
async
{
await
_storage
.
write
(
key:
_keyBluetoothReadSn
,
value:
sn
);
await
_storage
.
delete
(
key:
_legacyKeyUserSn
);
}
}
Future
<
String
?>
getUserSn
()
async
{
Future
<
BluetoothReadModel
>
getBluetoothReadInfo
()
async
{
final
sn
=
await
_storage
.
read
(
key:
_key
UserSn
);
final
sn
=
await
_storage
.
read
(
key:
_key
BluetoothReadSn
)
??
UserModel
.
SN
=
sn
;
await
_storage
.
read
(
key:
_legacyKeyUserSn
)
;
return
sn
;
return
BluetoothReadModel
(
sn:
sn
)
;
}
}
Future
<
void
>
delete
UserSn
()
async
{
Future
<
void
>
delete
BluetoothReadInfo
()
async
{
UserModel
.
SN
=
null
;
await
_storage
.
delete
(
key:
_keyBluetoothReadSn
)
;
await
_storage
.
delete
(
key:
_
k
eyUserSn
);
await
_storage
.
delete
(
key:
_
legacyK
eyUserSn
);
}
}
// ==================== ClientId 相关 ====================
// ==================== ClientId 相关 ====================
...
...
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
12e86f2f
...
@@ -3,10 +3,13 @@ import 'dart:async';
...
@@ -3,10 +3,13 @@ import 'dart:async';
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_bloc/flutter_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/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/services/monitoring_service.dart'
;
import
'package:laki_icu_app/services/monitoring_service.dart'
;
import
'package:laki_icu_app/services/p2p_video_service.dart'
;
import
'package:laki_icu_app/services/p2p_video_service.dart'
;
import
'package:laki_icu_app/services/webrtc_service.dart'
;
import
'package:laki_icu_app/services/webrtc_service.dart'
;
import
'package:laki_icu_app/models/bo/bluetooth_read_model.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/storage/storage_service.dart'
;
import
'package:laki_icu_app/utils/storage/storage_service.dart'
;
import
'monitoring_index_state.dart'
;
import
'monitoring_index_state.dart'
;
...
@@ -28,6 +31,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -28,6 +31,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
StreamSubscription
<
BluetoothConnectionStatus
>?
_bluetoothStateSub
;
StreamSubscription
<
BluetoothConnectionStatus
>?
_bluetoothStateSub
;
StreamSubscription
<
String
>?
_bluetoothMessageSub
;
StreamSubscription
<
String
>?
_bluetoothMessageSub
;
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
Timer
?
_bluetoothReconnectTimer
;
bool
_isAutoReconnectEnabled
=
false
;
bool
_isAutoConnectingBluetooth
=
false
;
bool
_isManualUnbindingBluetooth
=
false
;
/// 连接代际计数器 —— 每次切换模式时 +1,
/// 连接代际计数器 —— 每次切换模式时 +1,
/// 防止旧连接的异步结果污染当前模式的状态。
/// 防止旧连接的异步结果污染当前模式的状态。
...
@@ -76,6 +83,9 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -76,6 +83,9 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_bluetoothStateSub
=
_bluetoothManager
.
stateStream
.
listen
((
status
)
{
_bluetoothStateSub
=
_bluetoothManager
.
stateStream
.
listen
((
status
)
{
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
bluetoothConnectionStatus:
status
));
emit
(
state
.
copyWith
(
bluetoothConnectionStatus:
status
));
if
(
status
==
BluetoothConnectionStatus
.
disconnected
)
{
_scheduleBoundBluetoothReconnect
();
}
});
});
_bluetoothMessageSub
=
_bluetoothManager
.
messageStream
.
listen
((
message
)
{
_bluetoothMessageSub
=
_bluetoothManager
.
messageStream
.
listen
((
message
)
{
...
@@ -88,14 +98,23 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -88,14 +98,23 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
try
{
try
{
final
reports
=
_mcuReportDecoder
.
addBytes
(
packet
.
bytes
);
final
reports
=
_mcuReportDecoder
.
addBytes
(
packet
.
bytes
);
final
latestReport
=
reports
.
isEmpty
?
null
:
reports
.
last
;
final
latestReport
=
reports
.
isEmpty
?
null
:
reports
.
last
;
final
latestSn
=
latestReport
?.
sn
.
trim
();
if
(
latestReport
!=
null
)
{
if
(
latestSn
!=
null
&&
latestSn
.
isNotEmpty
)
{
final
bluetoothReadInfo
=
await
_storageService
.
saveUserSn
(
latestSn
);
BluetoothReadModel
.
fromMcuReport
(
latestReport
);
if
(
bluetoothReadInfo
.
hasSn
)
{
await
_storageService
.
saveBluetoothReadSn
(
bluetoothReadInfo
.
sn
!);
}
eventBus
.
emit
(
BluetoothReadInfoChangedEvent
(
bluetoothReadInfo
),
);
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
}
}
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
latestBluetoothRawHex:
packet
.
rawHex
,
latestBluetoothRawHex:
packet
.
rawHex
,
latestMcuReport:
latestReport
,
latestMcuReport:
latestReport
,
metrics:
latestReport
==
null
?
state
.
metrics
:
_metricsFromBluetoothReport
(
latestReport
),
bluetoothMessage:
latestReport
==
null
bluetoothMessage:
latestReport
==
null
?
state
.
bluetoothMessage
?
state
.
bluetoothMessage
:
'蓝牙数据解析成功:
${latestReport.sn.isEmpty ? latestReport.head : latestReport.sn}
'
,
:
'蓝牙数据解析成功:
${latestReport.sn.isEmpty ? latestReport.head : latestReport.sn}
'
,
...
@@ -113,13 +132,17 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -113,13 +132,17 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final
boundDevice
=
await
_storageService
.
getBoundBluetoothDevice
();
final
boundDevice
=
await
_storageService
.
getBoundBluetoothDevice
();
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
final
deviceId
=
boundDevice
[
'deviceId'
];
final
deviceId
=
boundDevice
[
'deviceId'
];
await
_storageService
.
get
UserSn
();
await
_storageService
.
get
BluetoothReadInfo
();
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
boundBluetoothDeviceId:
boundBluetoothDeviceId:
deviceId
==
null
||
deviceId
.
isEmpty
?
null
:
deviceId
,
deviceId
==
null
||
deviceId
.
isEmpty
?
null
:
deviceId
,
boundBluetoothDeviceName:
boundDevice
[
'deviceName'
],
boundBluetoothDeviceName:
boundDevice
[
'deviceName'
],
));
));
if
(
deviceId
!=
null
&&
deviceId
.
isNotEmpty
)
{
_isAutoReconnectEnabled
=
true
;
_connectBoundBluetoothDevice
();
}
}
}
Future
<
void
>
loadData
()
async
{
Future
<
void
>
loadData
()
async
{
...
@@ -310,6 +333,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -310,6 +333,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
deviceId:
device
.
remoteId
,
deviceId:
device
.
remoteId
,
deviceName:
device
.
name
,
deviceName:
device
.
name
,
);
);
_isAutoReconnectEnabled
=
true
;
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
isBluetoothBinding:
false
,
isBluetoothBinding:
false
,
...
@@ -329,9 +353,13 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -329,9 +353,13 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
}
}
Future
<
void
>
unbindBluetoothDevice
()
async
{
Future
<
void
>
unbindBluetoothDevice
()
async
{
_isManualUnbindingBluetooth
=
true
;
_isAutoReconnectEnabled
=
false
;
_cancelBluetoothReconnect
();
await
_bluetoothManager
.
disconnect
();
await
_bluetoothManager
.
disconnect
();
await
_storageService
.
deleteBoundBluetoothDevice
();
await
_storageService
.
deleteBoundBluetoothDevice
();
await
_storageService
.
deleteUserSn
();
await
_storageService
.
deleteBluetoothReadInfo
();
eventBus
.
emit
(
const
BluetoothReadInfoClearedEvent
());
_mcuReportDecoder
.
clear
();
_mcuReportDecoder
.
clear
();
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
...
@@ -340,6 +368,126 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -340,6 +368,126 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
bluetoothMessage:
'蓝牙设备已解绑'
,
bluetoothMessage:
'蓝牙设备已解绑'
,
latestBluetoothRawHex:
''
,
latestBluetoothRawHex:
''
,
));
));
_isManualUnbindingBluetooth
=
false
;
}
void
_scheduleBoundBluetoothReconnect
()
{
if
(!
_isAutoReconnectEnabled
||
_isManualUnbindingBluetooth
||
_isAutoConnectingBluetooth
||
state
.
boundBluetoothDeviceId
==
null
||
state
.
boundBluetoothDeviceId
!.
isEmpty
||
state
.
isBluetoothBinding
)
{
return
;
}
_bluetoothReconnectTimer
??=
Timer
(
const
Duration
(
seconds:
5
),
()
{
_bluetoothReconnectTimer
=
null
;
_connectBoundBluetoothDevice
();
},
);
}
Future
<
void
>
_connectBoundBluetoothDevice
()
async
{
final
deviceId
=
state
.
boundBluetoothDeviceId
;
if
(!
_isAutoReconnectEnabled
||
_isManualUnbindingBluetooth
||
_isAutoConnectingBluetooth
||
deviceId
==
null
||
deviceId
.
isEmpty
||
_bluetoothManager
.
isConnected
)
{
return
;
}
_cancelBluetoothReconnect
();
_isAutoConnectingBluetooth
=
true
;
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
bluetoothMessage:
'正在自动连接蓝牙设备'
));
}
try
{
await
_bluetoothManager
.
connectToDeviceId
(
deviceId
);
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
bluetoothMessage:
'蓝牙设备已自动连接'
));
}
}
catch
(
e
)
{
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
bluetoothMessage:
'蓝牙自动连接失败:
$e
'
));
}
}
finally
{
_isAutoConnectingBluetooth
=
false
;
if
(!
isClosed
&&
!
_bluetoothManager
.
isConnected
)
{
_scheduleBoundBluetoothReconnect
();
}
}
}
void
_cancelBluetoothReconnect
()
{
_bluetoothReconnectTimer
?.
cancel
();
_bluetoothReconnectTimer
=
null
;
}
List
<
MonitoringMetricBO
>
_metricsFromBluetoothReport
(
McuReport
report
)
{
final
replacements
=
<
String
,
MonitoringMetricBO
>{
'舱内温度'
:
_replaceMetricValue
(
'舱内温度'
,
report
.
mainTemperature
.
toStringAsFixed
(
1
),
fallbackUnit:
'℃'
,
),
'舱内湿度'
:
_replaceMetricValue
(
'舱内湿度'
,
'
${report.humidity}
'
,
fallbackUnit:
'RH'
,
),
'氧气浓度'
:
_replaceMetricValue
(
'氧气浓度'
,
'
${report.oxygenConcentration}
'
,
fallbackUnit:
'%'
,
),
'二氧化碳'
:
_replaceMetricValue
(
'二氧化碳'
,
'
${report.co2Measurement}
'
,
fallbackUnit:
'ppm'
,
),
};
if
(
state
.
metrics
.
isEmpty
)
{
return
replacements
.
values
.
toList
();
}
return
[
for
(
final
metric
in
state
.
metrics
)
replacements
[
metric
.
label
]
??
metric
,
];
}
MonitoringMetricBO
_replaceMetricValue
(
String
label
,
String
value
,
{
required
String
fallbackUnit
,
})
{
MonitoringMetricBO
?
existing
;
for
(
final
metric
in
state
.
metrics
)
{
if
(
metric
.
label
==
label
)
{
existing
=
metric
;
break
;
}
}
if
(
existing
==
null
)
{
return
MonitoringMetricBO
(
value:
value
,
unit:
fallbackUnit
,
label:
label
,
);
}
return
MonitoringMetricBO
(
value:
value
,
unit:
existing
.
unit
.
isEmpty
?
fallbackUnit
:
existing
.
unit
,
label:
existing
.
label
,
borderColorValue:
existing
.
borderColorValue
,
backgroundColorValue:
existing
.
backgroundColorValue
,
);
}
}
/// 获取 WebRTC 渲染器供 UI 层使用
/// 获取 WebRTC 渲染器供 UI 层使用
...
@@ -350,6 +498,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -350,6 +498,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
@override
@override
Future
<
void
>
close
()
{
Future
<
void
>
close
()
{
_cancelBluetoothReconnect
();
_webrtcStateSub
?.
cancel
();
_webrtcStateSub
?.
cancel
();
_p2pStateSub
?.
cancel
();
_p2pStateSub
?.
cancel
();
_bluetoothScanSub
?.
cancel
();
_bluetoothScanSub
?.
cancel
();
...
...
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