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
53839dd5
Commit
53839dd5
authored
Jun 24, 2026
by
张宏
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://git.ruanyiit.com/zhanghong/laki_icu_app
parents
08cc8142
65c9e3b0
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
229 additions
and
18 deletions
+229
-18
frame_card_green_325x372.png
lib/assets/monitoring/frame_card_green_325x372.png
+0
-0
bluetooth_manager.dart
lib/utils/bluetooth/bluetooth_manager.dart
+175
-11
monitoring_bottom_menu.dart
lib/views/layout/widgets/monitoring_bottom_menu.dart
+45
-2
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+9
-5
No files found.
lib/assets/monitoring/frame_card_green_325x372.png
View replaced file @
08cc8142
View file @
53839dd5
7.31 KB
|
W:
|
H:
110 KB
|
W:
|
H:
2-up
Swipe
Onion skin
lib/utils/bluetooth/bluetooth_manager.dart
View file @
53839dd5
...
...
@@ -88,10 +88,12 @@ class BleBluetoothManager<T> {
String
?
_connectedDeviceId
;
QualifiedCharacteristic
?
_notifyCharacteristic
;
QualifiedCharacteristic
?
_writeCharacteristic
;
bool
_writeCharacteristicOnlyWithoutResponse
=
false
;
StreamSubscription
<
DiscoveredDevice
>?
_scanSubscription
;
StreamSubscription
<
ConnectionStateUpdate
>?
_connectionSubscription
;
StreamSubscription
<
List
<
int
>>?
_notifySubscription
;
Timer
?
_scanTimer
;
Future
<
void
>
_writeQueue
=
Future
<
void
>.
value
();
final
Map
<
String
,
BluetoothScanDevice
>
_scannedDevices
=
{};
...
...
@@ -137,7 +139,7 @@ class BleBluetoothManager<T> {
final
nextStatus
=
await
_ble
.
statusStream
.
where
((
value
)
=>
value
!=
BleStatus
.
unknown
)
.
first
.
timeout
(
const
Duration
(
seconds:
2
),
onTimeout:
()
=>
status
);
.
timeout
(
const
Duration
(
seconds:
8
),
onTimeout:
()
=>
status
);
if
(
nextStatus
!=
BleStatus
.
ready
)
{
debugPrint
(
...
...
@@ -258,13 +260,14 @@ class BleBluetoothManager<T> {
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_to_device_id empty_device_id'
);
_notifyMessage
(
'无效的蓝牙设备地址'
);
return
;
throw
ArgumentError
(
'无效的蓝牙设备地址'
)
;
}
if
(!
await
ensureBluetoothReady
())
{
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG connect_to_device_id bluetooth_not_ready'
);
return
;
_setStatus
(
BluetoothConnectionStatus
.
disconnected
);
throw
StateError
(
'蓝牙未就绪或未授权'
);
}
await
disconnect
();
...
...
@@ -306,11 +309,31 @@ class BleBluetoothManager<T> {
_notifyMessage
(
'蓝牙连接成功:
$deviceId
'
);
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG state_connected deviceId=
$deviceId
'
);
await
_enableNotifications
(
deviceId
);
try
{
await
_discoverServicesAndResolveWriteCharacteristic
(
deviceId
);
final
notifyReady
=
await
_enableNotifications
(
deviceId
);
if
(
notifyReady
)
{
activateDataSource
();
}
else
{
_notifyMessage
(
'蓝牙已连接,但未找到可订阅的接收特征值'
);
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG connected_without_notify '
'deviceId=
$deviceId
'
,
);
}
if
(!
completer
.
isCompleted
)
{
completer
.
complete
();
}
}
catch
(
error
)
{
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG connected_setup_error '
'deviceId=
$deviceId
error=
$error
'
,
);
_notifyMessage
(
'蓝牙连接初始化失败:
$error
'
);
if
(!
completer
.
isCompleted
)
{
completer
.
completeError
(
error
);
}
}
break
;
case
DeviceConnectionState
.
disconnecting
:
_setStatus
(
BluetoothConnectionStatus
.
disconnected
);
...
...
@@ -362,7 +385,21 @@ class BleBluetoothManager<T> {
}
}
Future
<
void
>
writeData
(
List
<
int
>
data
,
{
bool
withoutResponse
=
false
})
async
{
Future
<
void
>
writeData
(
List
<
int
>
data
,
{
bool
withoutResponse
=
false
})
{
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG enqueue data=
$data
hex=
${bytesToHex(data)}
'
,
);
_writeQueue
=
_writeQueue
.
then
(
(
_
)
=>
_writeDataLocked
(
data
,
withoutResponse:
withoutResponse
),
onError:
(
_
)
=>
_writeDataLocked
(
data
,
withoutResponse:
withoutResponse
),
);
return
_writeQueue
;
}
Future
<
void
>
_writeDataLocked
(
List
<
int
>
data
,
{
bool
withoutResponse
=
false
,
})
async
{
if
(
data
.
isEmpty
)
{
_notifyMessage
(
'发送数据为空'
);
_sendResultController
.
add
(
false
);
...
...
@@ -377,16 +414,19 @@ class BleBluetoothManager<T> {
}
final
hex
=
bytesToHex
(
data
);
final
effectiveWithoutResponse
=
withoutResponse
||
_writeCharacteristicOnlyWithoutResponse
;
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG write_data '
'deviceId=
${characteristic.deviceId}
'
'service=
${characteristic.serviceId}
'
'characteristic=
${characteristic.characteristicId}
'
'withoutResponse=
$withoutResponse
'
'withoutResponse=
$effectiveWithoutResponse
'
'data=
$data
'
'hex=
$hex
'
,
);
try
{
if
(
w
ithoutResponse
)
{
if
(
effectiveW
ithoutResponse
)
{
await
_ble
.
writeCharacteristicWithoutResponse
(
characteristic
,
value:
data
,
...
...
@@ -401,6 +441,8 @@ class BleBluetoothManager<T> {
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG write_error hex=
$hex
error=
$error
'
);
_notifyMessage
(
'蓝牙数据发送失败:
$error
'
);
_sendResultController
.
add
(
false
);
}
finally
{
await
Future
<
void
>.
delayed
(
const
Duration
(
milliseconds:
120
));
}
}
...
...
@@ -435,20 +477,23 @@ class BleBluetoothManager<T> {
await
_sendResultController
.
close
();
}
Future
<
void
>
_enableNotifications
(
String
deviceId
)
async
{
Future
<
bool
>
_enableNotifications
(
String
deviceId
)
async
{
final
characteristic
=
_notifyCharacteristic
;
if
(
characteristic
==
null
)
{
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_characteristic_null deviceId=
$deviceId
'
);
throw
StateError
(
'接收特征值为空'
)
;
return
false
;
}
try
{
await
_notifySubscription
?.
cancel
();
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_subscribe_start deviceId=
$deviceId
'
'service=
$serviceUuid
characteristic=
$notifyCharacteristicUuid
'
,
'service=
${characteristic.serviceId}
'
'characteristic=
${characteristic.characteristicId}
'
,
);
_notifySubscription
=
_ble
.
subscribeToCharacteristic
(
characteristic
).
listen
(
_notifySubscription
=
_ble
.
subscribeToCharacteristic
(
characteristic
).
listen
(
(
bytes
)
{
if
(!
isActive
||
bytes
.
isEmpty
)
{
return
;
...
...
@@ -477,11 +522,126 @@ class BleBluetoothManager<T> {
);
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_subscribe_done deviceId=
$deviceId
'
);
return
true
;
}
catch
(
error
)
{
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG notify_subscribe_error deviceId=
$deviceId
error=
$error
'
);
_notifyMessage
(
'订阅蓝牙数据失败:
$error
'
);
return
false
;
}
}
Future
<
void
>
_discoverServicesAndResolveWriteCharacteristic
(
String
deviceId
,
)
async
{
try
{
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG start deviceId=
$deviceId
'
);
await
_ble
.
discoverAllServices
(
deviceId
);
final
services
=
await
_ble
.
getDiscoveredServices
(
deviceId
);
QualifiedCharacteristic
?
fallbackWriteCharacteristic
;
QualifiedCharacteristic
?
fallbackNotifyCharacteristic
;
var
fallbackOnlyWithoutResponse
=
false
;
var
preferredWriteFound
=
false
;
var
preferredNotifyFound
=
false
;
for
(
final
service
in
services
)
{
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG service=
${service.id}
'
);
for
(
final
characteristic
in
service
.
characteristics
)
{
final
isPreferredWrite
=
_isSameUuid
(
characteristic
.
id
,
writeCharacteristicUuid
);
final
isPreferredNotify
=
_isSameUuid
(
characteristic
.
id
,
notifyCharacteristicUuid
);
final
canWrite
=
characteristic
.
isWritableWithResponse
||
characteristic
.
isWritableWithoutResponse
;
final
canNotify
=
characteristic
.
isNotifiable
||
characteristic
.
isIndicatable
;
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG characteristic=
${characteristic.id}
'
'service=
${service.id}
'
'read=
${characteristic.isReadable}
'
'write=
${characteristic.isWritableWithResponse}
'
'writeNoResp=
${characteristic.isWritableWithoutResponse}
'
'notify=
${characteristic.isNotifiable}
'
'indicate=
${characteristic.isIndicatable}
'
'
${isPreferredWrite ? ' preferredWrite=true' : ''}
'
'
${isPreferredNotify ? ' preferredNotify=true' : ''}
'
,
);
final
resolved
=
QualifiedCharacteristic
(
deviceId:
deviceId
,
serviceId:
service
.
id
,
characteristicId:
characteristic
.
id
,
);
if
(
canNotify
)
{
fallbackNotifyCharacteristic
??=
resolved
;
if
(
isPreferredNotify
)
{
_notifyCharacteristic
=
resolved
;
preferredNotifyFound
=
true
;
}
}
if
(!
canWrite
)
continue
;
final
onlyWithoutResponse
=
!
characteristic
.
isWritableWithResponse
&&
characteristic
.
isWritableWithoutResponse
;
fallbackWriteCharacteristic
??=
resolved
;
fallbackOnlyWithoutResponse
=
onlyWithoutResponse
;
if
(
isPreferredWrite
)
{
_writeCharacteristic
=
resolved
;
_writeCharacteristicOnlyWithoutResponse
=
onlyWithoutResponse
;
preferredWriteFound
=
true
;
}
}
}
if
(!
preferredNotifyFound
&&
fallbackNotifyCharacteristic
!=
null
)
{
_notifyCharacteristic
=
fallbackNotifyCharacteristic
;
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG preferred_notify_not_available '
'fallback=
${_notifyCharacteristic!.characteristicId}
'
'service=
${_notifyCharacteristic!.serviceId}
'
,
);
}
if
(!
preferredWriteFound
&&
fallbackWriteCharacteristic
!=
null
)
{
_writeCharacteristic
=
fallbackWriteCharacteristic
;
_writeCharacteristicOnlyWithoutResponse
=
fallbackOnlyWithoutResponse
;
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG preferred_write_not_writable '
'fallback=
${_writeCharacteristic!.characteristicId}
'
'service=
${_writeCharacteristic!.serviceId}
'
'onlyWithoutResponse=
$_writeCharacteristicOnlyWithoutResponse
'
,
);
}
final
resolvedNotify
=
_notifyCharacteristic
;
final
resolvedWrite
=
_writeCharacteristic
;
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG resolved_notify '
'characteristic=
${resolvedNotify?.characteristicId}
'
'service=
${resolvedNotify?.serviceId}
'
,
);
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG resolved_write '
'characteristic=
${resolvedWrite?.characteristicId}
'
'service=
${resolvedWrite?.serviceId}
'
'onlyWithoutResponse=
$_writeCharacteristicOnlyWithoutResponse
'
,
);
}
catch
(
error
)
{
Log
.
warn
(
'🔥🔥🔥 BLE_DISCOVER_DEBUG error deviceId=
$deviceId
error=
$error
'
,
);
}
}
void
_clearCharacteristics
()
{
_notifyCharacteristic
=
null
;
_writeCharacteristic
=
null
;
_writeCharacteristicOnlyWithoutResponse
=
false
;
}
void
_setStatus
(
BluetoothConnectionStatus
status
)
{
...
...
@@ -524,6 +684,10 @@ class BleBluetoothManager<T> {
return
prefixes
.
any
(
value
.
startsWith
);
}
static
bool
_isSameUuid
(
Uuid
left
,
Uuid
right
)
{
return
left
.
toString
().
toLowerCase
()
==
right
.
toString
().
toLowerCase
();
}
static
String
_normalizeRemoteIdForSort
(
String
remoteId
)
{
return
remoteId
.
replaceAll
(
RegExp
(
r'[^0-9a-zA-Z]'
),
''
).
toUpperCase
();
}
...
...
lib/views/layout/widgets/monitoring_bottom_menu.dart
View file @
53839dd5
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/utils/bluetooth/index.dart'
;
typedef
OnMenuItemTap
=
Function
(
String
menuId
);
...
...
@@ -77,15 +78,57 @@ class MonitoringBottomMenu extends StatelessWidget {
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
// 图标
Container
(
StreamBuilder
<
BluetoothConnectionStatus
>(
stream:
appBluetoothManager
.
stateStream
,
initialData:
appBluetoothManager
.
status
,
builder:
(
context
,
snapshot
)
{
final
status
=
snapshot
.
data
;
final
isActive
=
status
==
BluetoothConnectionStatus
.
connectedReady
||
status
==
BluetoothConnectionStatus
.
active
;
return
SizedBox
(
width:
167
.
w
,
height:
62
.
h
,
child:
AnimatedContainer
(
duration:
const
Duration
(
milliseconds:
180
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
isActive
?
7
.
w
:
0
,
vertical:
isActive
?
4
.
h
:
0
,
),
decoration:
isActive
?
BoxDecoration
(
color:
const
Color
(
0xFF063D36
)
.
withValues
(
alpha:
0.22
),
borderRadius:
BorderRadius
.
circular
(
32
.
r
),
boxShadow:
[
BoxShadow
(
color:
const
Color
(
0xFF39FF88
)
.
withValues
(
alpha:
0.18
),
blurRadius:
12
.
w
,
spreadRadius:
1
.
w
,
),
],
)
:
null
,
child:
ColorFiltered
(
colorFilter:
isActive
?
const
ColorFilter
.
mode
(
Color
(
0xFF8DFFCB
),
BlendMode
.
srcATop
,
)
:
const
ColorFilter
.
mode
(
Colors
.
transparent
,
BlendMode
.
dst
,
),
child:
Image
.
asset
(
item
.
iconPath
,
fit:
BoxFit
.
contain
,
),
),
),
);
},
),
],
),
...
...
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
53839dd5
...
...
@@ -522,6 +522,9 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
try
{
await
_bluetoothManager
.
connectToDevice
(
device
);
if
(!
_bluetoothManager
.
isConnected
)
{
throw
StateError
(
'蓝牙连接未就绪'
);
}
await
_storageService
.
saveBoundBluetoothDevice
(
deviceId:
device
.
remoteId
,
deviceName:
device
.
name
,
...
...
@@ -620,7 +623,11 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
'deviceId=
$deviceId
isConnected=
${_bluetoothManager.isConnected}
'
,
);
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
bluetoothMessage:
'蓝牙设备已自动连接'
));
emit
(
state
.
copyWith
(
bluetoothMessage:
_bluetoothManager
.
isConnected
?
'蓝牙设备已自动连接'
:
'蓝牙自动连接未就绪,等待重试'
,
));
}
}
catch
(
e
)
{
debugPrint
(
...
...
@@ -1074,9 +1081,6 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_mcuReportChangedSub
?.
cancel
();
_webrtcService
.
dispose
();
_p2pVideoService
.
dispose
();
return
Future
.
wait
([
_disposeMqttClient
(),
_bluetoothManager
.
release
(),
]).
then
((
_
)
=>
super
.
close
());
return
_disposeMqttClient
().
then
((
_
)
=>
super
.
close
());
}
}
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