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
266ba06d
Commit
266ba06d
authored
Jun 24, 2026
by
akari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 控制温湿度
parent
dc8a1afb
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
428 additions
and
105 deletions
+428
-105
device_control_bloc.dart
lib/blocs/device_control/device_control_bloc.dart
+0
-0
device_control_event.dart
lib/blocs/device_control/device_control_event.dart
+61
-4
device_control_state.dart
lib/blocs/device_control/device_control_state.dart
+13
-5
bluetooth_read_model.dart
lib/models/bo/bluetooth_read_model.dart
+6
-4
bluetooth_manager.dart
lib/utils/bluetooth/bluetooth_manager.dart
+53
-1
mcu_report.dart
lib/utils/bluetooth/mcu_report.dart
+141
-80
event_bus.dart
lib/utils/event_bus.dart
+8
-0
device_control_metric_card.dart
...ng/device_control/widgets/device_control_metric_card.dart
+122
-11
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+0
-0
monitoring_index_state.dart
lib/views/monitoring/index/cubit/monitoring_index_state.dart
+24
-0
No files found.
lib/blocs/device_control/device_control_bloc.dart
View file @
266ba06d
This diff is collapsed.
Click to expand it.
lib/blocs/device_control/device_control_event.dart
View file @
266ba06d
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)
...
...
lib/blocs/device_control/device_control_state.dart
View file @
266ba06d
...
...
@@ -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
,
...
...
lib/models/bo/bluetooth_read_model.dart
View file @
266ba06d
...
...
@@ -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
(),
);
}
...
...
lib/utils/bluetooth/bluetooth_manager.dart
View file @
266ba06d
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
);
}
...
...
lib/utils/bluetooth/mcu_report.dart
View file @
266ba06d
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
<
mcuReport
Header
Bytes
)
{
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:
d
ata
.
readHex
(
1
)
,
serviceNum:
data
.
readHex
(
1
)
,
length:
data
.
readUInt8
()
,
mainTemperature:
data
.
readT
enths
(),
versionNumber:
data
.
readT
enths
(),
ambientTemperature:
data
.
readT
enths
(),
upperTemperature:
data
.
readT
enths
(),
humidity:
data
.
readUInt
16
(),
oxygenConcentration:
data
.
readUInt
16
(),
temperatureSetting:
data
.
readT
enths
(),
lowerTemperature:
data
.
readT
enths
(),
humiditySetting:
data
.
readUInt
16
(),
oxygenSetting:
data
.
readUInt
16
(),
internalExternalCycleState:
data
.
readUInt
16
(),
negativeIonSwitch:
data
.
readUInt
16
(),
uvLamp:
data
.
readUInt
16
(),
inspectionLamp:
data
.
readUInt
16
(),
floodLight:
data
.
readUInt
16
(),
o2ChangeSlope:
data
.
readUInt
16
(),
rightAtomizerTreatmentTime:
data
.
readUInt
16
(),
rightInfraredPhysiotherapyTime:
data
.
readUInt
16
(),
tempS1:
data
.
readH
ighByte
()
/
10
,
tempH1:
data
.
readH
ighByte
(),
atomizerTreatmentTime:
data
.
readUInt
16
(),
infraredPhysiotherapyTime:
data
.
readH
ighByte
(),
mainTempCorrect:
data
.
readH
ighByte
(),
auxiliaryTemperatureCorrect:
data
.
readH
ighByte
(),
o2CorrectStart:
data
.
readH
ighByte
(),
o2CorrectDiffer:
data
.
readH
ighByte
(),
humidityCorrect:
data
.
readH
ighByte
(),
oxygenCorrect:
data
.
readH
ighByte
(),
eTempCorrect:
data
.
readH
ighByte
(),
xyyModel:
data
.
readHex
(
2
),
unknown:
data
.
readHex
(
2
),
wifible:
data
.
readH
ighByte
(),
levelLight:
data
.
readH
ighByte
(),
openO2:
data
.
readH
ighByte
(),
airConditioner:
data
.
readH
ighByte
(),
co2Correct:
data
.
readH
ighByte
(),
co2Measurement:
data
.
readUInt
16
(),
co2Setting:
data
.
readUInt
16
(),
co2FastClearSwitch:
data
.
readH
ighByte
(),
openO2SettingMax:
data
.
readH
ighByte
(),
noOpenO2SettingMax:
data
.
readH
ighByte
(),
windSet:
data
.
readH
ighByte
(),
rangeData1:
data
.
readHex
(
2
),
rangeData2:
data
.
readHex
(
2
),
rangeData3:
data
.
readHex
(
2
),
co2Safety:
data
.
readUInt
16
(),
co2SettingMin:
data
.
readUInt
16
(),
blueLight:
data
.
readH
ighByte
(),
redLight:
data
.
readH
ighByte
(),
fcSwitch:
data
.
readH
ighByte
(),
o2SupplyTime:
data
.
readUInt
16
(),
totalTime:
data
.
readUInt
16
(),
ratioTemp2:
data
.
readUInt8
()
,
ratioTemp1:
data
.
readUInt8
()
,
sp:
data
.
readH
ighByte
(),
dp:
data
.
readH
ighByte
(),
mean:
data
.
readH
ighByte
(),
pulseRate:
data
.
readH
ighByte
(),
xyyStatus:
data
.
readH
ighByte
(),
xyyError1:
data
.
readH
ighByte
(),
sn:
data
.
readA
scii
(
10
),
topTempCorrect:
data
.
readH
ighByte
(),
midTempCorrect:
data
.
readH
ighByte
(),
o2OpenCorrectStart:
data
.
readH
ighByte
(),
o2OpenCorrectStep:
data
.
readH
ighByte
(),
o2CorrectMax:
data
.
readH
ighByte
(),
o2OpenCorrectMax:
data
.
readH
ighByte
(),
parsedBytes:
data
.
byteIndex
,
head:
head
,
direct:
d
irect
,
serviceNum:
serviceNum
,
length:
length
,
mainTemperature:
t
enths
(),
versionNumber:
t
enths
(),
ambientTemperature:
t
enths
(),
upperTemperature:
t
enths
(),
humidity:
u
16
(),
oxygenConcentration:
u
16
(),
temperatureSetting:
t
enths
(),
lowerTemperature:
t
enths
(),
humiditySetting:
u
16
(),
oxygenSetting:
u
16
(),
internalExternalCycleState:
u
16
(),
negativeIonSwitch:
u
16
(),
uvLamp:
u
16
(),
inspectionLamp:
u
16
(),
floodLight:
u
16
(),
o2ChangeSlope:
u
16
(),
rightAtomizerTreatmentTime:
u
16
(),
rightInfraredPhysiotherapyTime:
u
16
(),
tempS1:
h
ighByte
()
/
10
,
tempH1:
h
ighByte
(),
atomizerTreatmentTime:
u
16
(),
infraredPhysiotherapyTime:
h
ighByte
(),
mainTempCorrect:
h
ighByte
(),
auxiliaryTemperatureCorrect:
h
ighByte
(),
o2CorrectStart:
h
ighByte
(),
o2CorrectDiffer:
h
ighByte
(),
humidityCorrect:
h
ighByte
(),
oxygenCorrect:
h
ighByte
(),
eTempCorrect:
h
ighByte
(),
xyyModel:
hex2
(
),
unknown:
hex2
(
),
wifible:
h
ighByte
(),
levelLight:
h
ighByte
(),
openO2:
h
ighByte
(),
airConditioner:
h
ighByte
(),
co2Correct:
h
ighByte
(),
co2Measurement:
u
16
(),
co2Setting:
u
16
(),
co2FastClearSwitch:
h
ighByte
(),
openO2SettingMax:
h
ighByte
(),
noOpenO2SettingMax:
h
ighByte
(),
windSet:
h
ighByte
(),
rangeData1:
hex2
(
),
rangeData2:
hex2
(
),
rangeData3:
hex2
(
),
co2Safety:
u
16
(),
co2SettingMin:
u
16
(),
blueLight:
h
ighByte
(),
redLight:
h
ighByte
(),
fcSwitch:
h
ighByte
(),
o2SupplyTime:
u
16
(),
totalTime:
u
16
(),
ratioTemp2:
remainingPayloadBytes
()
>=
1
?
payload
!.
readUInt8
()
:
-
1
,
ratioTemp1:
remainingPayloadBytes
()
>=
1
?
payload
!.
readUInt8
()
:
-
1
,
sp:
h
ighByte
(),
dp:
h
ighByte
(),
mean:
h
ighByte
(),
pulseRate:
h
ighByte
(),
xyyStatus:
h
ighByte
(),
xyyError1:
h
ighByte
(),
sn:
a
scii
(
10
),
topTempCorrect:
h
ighByte
(),
midTempCorrect:
h
ighByte
(),
o2OpenCorrectStart:
h
ighByte
(),
o2OpenCorrectStep:
h
ighByte
(),
o2CorrectMax:
h
ighByte
(),
o2OpenCorrectMax:
h
ighByte
(),
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
>=
mcuReport
Header
Bytes
)
{
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
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
(
mcuReport
Bytes
));
final
frame
=
List
<
int
>.
from
(
_buffer
.
take
(
frame
Bytes
));
if
(
frame
.
last
!=
0x16
)
{
_buffer
.
removeAt
(
0
);
continue
;
}
reports
.
add
(
McuReportParser
.
parseBytes
(
frame
));
_buffer
.
removeRange
(
0
,
mcuReport
Bytes
);
_buffer
.
removeRange
(
0
,
frame
Bytes
);
}
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
();
}
...
...
lib/utils/event_bus.dart
View file @
266ba06d
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
);
}
lib/views/monitoring/device_control/widgets/device_control_metric_card.dart
View file @
266ba06d
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:laki_icu_app/blocs/device_control/device_control_bloc.dart'
;
import
'package:laki_icu_app/blocs/device_control/device_control_event.dart'
;
import
'package:laki_icu_app/blocs/device_control/device_control_state.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'device_control_common.dart'
;
...
...
@@ -12,10 +16,55 @@ class DeviceControlMetricCard extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
final
isTemperature
=
metric
.
label
.
contains
(
'温度'
);
final
isHumidity
=
metric
.
label
.
contains
(
'湿度'
);
final
isOxygen
=
metric
.
label
.
contains
(
'氧气'
);
final
isCo2
=
metric
.
label
.
contains
(
'二氧化碳'
)
||
metric
.
label
.
toUpperCase
().
contains
(
'CO'
);
return
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
buildWhen:
(
previous
,
current
)
=>
previous
.
data
.
cabinTemp
.
setValue
!=
current
.
data
.
cabinTemp
.
setValue
||
previous
.
data
.
cabinHumidity
.
setValue
!=
current
.
data
.
cabinHumidity
.
setValue
||
previous
.
data
.
oxygenConcentration
.
setValue
!=
current
.
data
.
oxygenConcentration
.
setValue
||
previous
.
data
.
co2Concentration
.
alarmThreshold
!=
current
.
data
.
co2Concentration
.
alarmThreshold
||
previous
.
data
.
cabinTemp
.
currentValue
!=
current
.
data
.
cabinTemp
.
currentValue
||
previous
.
data
.
cabinHumidity
.
currentValue
!=
current
.
data
.
cabinHumidity
.
currentValue
||
previous
.
data
.
oxygenConcentration
.
currentValue
!=
current
.
data
.
oxygenConcentration
.
currentValue
||
previous
.
data
.
co2Concentration
.
currentValue
!=
current
.
data
.
co2Concentration
.
currentValue
,
builder:
(
context
,
state
)
{
final
currentValue
=
isTemperature
?
state
.
data
.
cabinTemp
.
currentValue
:
isCo2
?
state
.
data
.
co2Concentration
.
currentValue
:
isOxygen
?
state
.
data
.
oxygenConcentration
.
currentValue
:
isHumidity
?
state
.
data
.
cabinHumidity
.
currentValue
:
metric
.
value
;
final
setValue
=
isTemperature
?
state
.
data
.
cabinTemp
.
setValue
:
isCo2
?
state
.
data
.
co2Concentration
.
alarmThreshold
:
isOxygen
?
state
.
data
.
oxygenConcentration
.
setValue
:
isHumidity
?
state
.
data
.
cabinHumidity
.
setValue
:
'24'
;
final
adjustEventBuilder
=
_adjustEventBuilder
(
isTemperature:
isTemperature
,
isHumidity:
isHumidity
,
isOxygen:
isOxygen
,
isCo2:
isCo2
,
);
return
DeviceControlPanelFrame
(
active:
isOxygen
,
backgroundAsset:
isOxygen
?
assetMetricGreen
:
assetMetricBlue
,
...
...
@@ -43,7 +92,7 @@ class DeviceControlMetricCard extends StatelessWidget {
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
Text
(
metric
.
v
alue
,
currentV
alue
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
isCo2
?
60
.
sp
:
82
.
sp
,
...
...
@@ -72,7 +121,8 @@ class DeviceControlMetricCard extends StatelessWidget {
),
],
),
Divider
(
color:
Colors
.
white
.
withValues
(
alpha:
0.18
),
height:
28
.
h
),
Divider
(
color:
Colors
.
white
.
withValues
(
alpha:
0.18
),
height:
28
.
h
),
Text
(
isCo2
?
'告警阈值设定'
...
...
@@ -87,16 +137,18 @@ class DeviceControlMetricCard extends StatelessWidget {
const
Spacer
(),
Row
(
children:
[
DeviceControlAdjustText
(
'-'
,
active:
isOxygen
||
!
isCo2
),
_MetricAdjustButton
(
text:
'-'
,
active:
adjustEventBuilder
!=
null
,
onTap:
adjustEventBuilder
==
null
?
null
:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
adjustEventBuilder
(
false
),
),
),
const
Spacer
(),
Text
(
isCo2
?
'4000'
:
isOxygen
?
'90'
:
metric
.
label
.
contains
(
'湿度'
)
?
'10'
:
'24'
,
setValue
,
style:
TextStyle
(
color:
isCo2
?
const
Color
(
0xFFFFC400
)
...
...
@@ -107,16 +159,75 @@ class DeviceControlMetricCard extends StatelessWidget {
),
),
const
Spacer
(),
const
DeviceControlAdjustText
(
'+'
,
active:
true
),
_MetricAdjustButton
(
text:
'+'
,
active:
adjustEventBuilder
!=
null
,
onTap:
adjustEventBuilder
==
null
?
null
:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
adjustEventBuilder
(
true
),
),
),
],
),
],
),
);
},
);
}
static
String
_title
(
String
label
)
{
if
(
label
.
contains
(
'二氧化碳'
))
return
'二氧化碳浓度'
;
return
label
;
}
static
DeviceControlEvent
Function
(
bool
increase
)?
_adjustEventBuilder
({
required
bool
isTemperature
,
required
bool
isHumidity
,
required
bool
isOxygen
,
required
bool
isCo2
,
})
{
if
(
isTemperature
)
{
return
(
increase
)
=>
DeviceControlCabinTempSetValueAdjusted
(
increase
?
1
:
-
1
);
}
if
(
isHumidity
)
{
return
(
increase
)
=>
DeviceControlCabinHumiditySetValueAdjusted
(
increase
?
1
:
-
1
);
}
if
(
isOxygen
)
{
return
(
increase
)
=>
DeviceControlOxygenConcentrationSetValueAdjusted
(
increase
?
1
:
-
1
);
}
if
(
isCo2
)
{
return
(
increase
)
=>
DeviceControlCO2ConcentrationAlarmThresholdAdjusted
(
increase
?
100
:
-
100
);
}
return
null
;
}
}
class
_MetricAdjustButton
extends
StatelessWidget
{
const
_MetricAdjustButton
({
required
this
.
text
,
required
this
.
active
,
this
.
onTap
,
});
final
String
text
;
final
bool
active
;
final
VoidCallback
?
onTap
;
@override
Widget
build
(
BuildContext
context
)
{
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
12
.
w
,
vertical:
8
.
h
),
child:
DeviceControlAdjustText
(
text
,
active:
active
),
),
);
}
}
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
266ba06d
This diff is collapsed.
Click to expand it.
lib/views/monitoring/index/cubit/monitoring_index_state.dart
View file @
266ba06d
...
...
@@ -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
,
];
}
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