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
d6fc2a2b
Commit
d6fc2a2b
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
d2db426e
2160220c
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1168 additions
and
119 deletions
+1168
-119
device_control_bloc.dart
lib/blocs/device_control/device_control_bloc.dart
+151
-25
bluetooth_manager.dart
lib/utils/bluetooth/bluetooth_manager.dart
+12
-1
numeric_keyboard.dart
lib/utils/numeric_keyboard.dart
+356
-0
device_control_index_view.dart
.../monitoring/device_control/device_control_index_view.dart
+40
-15
device_control_care_panel.dart
...ing/device_control/widgets/device_control_care_panel.dart
+155
-19
device_control_info_panel.dart
...ing/device_control/widgets/device_control_info_panel.dart
+17
-3
device_control_metric_card.dart
...ng/device_control/widgets/device_control_metric_card.dart
+63
-1
device_control_mode_panel.dart
...ing/device_control/widgets/device_control_mode_panel.dart
+107
-22
device_control_time_panel.dart
...ing/device_control/widgets/device_control_time_panel.dart
+81
-7
device_control_wind_panel.dart
...ing/device_control/widgets/device_control_wind_panel.dart
+54
-8
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+130
-17
monitoring_index_view.dart
lib/views/monitoring/index/monitoring_index_view.dart
+2
-1
No files found.
lib/blocs/device_control/device_control_bloc.dart
View file @
d6fc2a2b
...
@@ -5,6 +5,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
...
@@ -5,6 +5,7 @@ import 'package:hydrated_bloc/hydrated_bloc.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/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'
;
...
@@ -181,6 +182,11 @@ class DeviceControlBloc
...
@@ -181,6 +182,11 @@ class DeviceControlBloc
);
);
if
(
reports
.
isEmpty
||
isClosed
)
return
;
if
(
reports
.
isEmpty
||
isClosed
)
return
;
final
latestReport
=
reports
.
last
;
final
latestReport
=
reports
.
last
;
eventBus
.
emit
(
BluetoothReadInfoChangedEvent
(
BluetoothReadModel
.
fromMcuReport
(
latestReport
),
),
);
eventBus
.
emit
(
McuReportChangedEvent
(
latestReport
));
eventBus
.
emit
(
McuReportChangedEvent
(
latestReport
));
}
catch
(
e
)
{
}
catch
(
e
)
{
Log
.
warn
(
'🔥🔥🔥 BLE_READ_DEBUG device_control_parse_error error=
$e
'
);
Log
.
warn
(
'🔥🔥🔥 BLE_READ_DEBUG device_control_parse_error error=
$e
'
);
...
@@ -210,19 +216,31 @@ class DeviceControlBloc
...
@@ -210,19 +216,31 @@ class DeviceControlBloc
sn:
report
.
sn
.
isEmpty
?
state
.
data
.
sn
:
report
.
sn
,
sn:
report
.
sn
.
isEmpty
?
state
.
data
.
sn
:
report
.
sn
,
cabinTemp:
state
.
data
.
cabinTemp
.
copyWith
(
cabinTemp:
state
.
data
.
cabinTemp
.
copyWith
(
currentValue:
_formatTemperature
(
report
.
mainTemperature
),
currentValue:
_formatTemperature
(
report
.
mainTemperature
),
setValue:
_formatTemperature
(
report
.
temperatureSetting
),
setValue:
_formatTemperatureOrKeep
(
report
.
temperatureSetting
,
state
.
data
.
cabinTemp
.
setValue
,
),
),
),
cabinHumidity:
state
.
data
.
cabinHumidity
.
copyWith
(
cabinHumidity:
state
.
data
.
cabinHumidity
.
copyWith
(
currentValue:
_formatInt
(
report
.
humidity
),
currentValue:
_formatInt
(
report
.
humidity
),
setValue:
_formatInt
(
report
.
humiditySetting
),
setValue:
_formatIntOrKeep
(
report
.
humiditySetting
,
state
.
data
.
cabinHumidity
.
setValue
,
),
),
),
oxygenConcentration:
state
.
data
.
oxygenConcentration
.
copyWith
(
oxygenConcentration:
state
.
data
.
oxygenConcentration
.
copyWith
(
currentValue:
_formatInt
(
report
.
oxygenConcentration
),
currentValue:
_formatInt
(
report
.
oxygenConcentration
),
setValue:
_formatInt
(
report
.
oxygenSetting
),
setValue:
_formatIntOrKeep
(
report
.
oxygenSetting
,
state
.
data
.
oxygenConcentration
.
setValue
,
),
),
),
co2Concentration:
state
.
data
.
co2Concentration
.
copyWith
(
co2Concentration:
state
.
data
.
co2Concentration
.
copyWith
(
currentValue:
_formatInt
(
report
.
co2Measurement
),
currentValue:
_formatInt
(
report
.
co2Measurement
),
alarmThreshold:
_formatInt
(
report
.
co2Setting
),
alarmThreshold:
_formatIntOrKeep
(
report
.
co2Setting
,
state
.
data
.
co2Concentration
.
alarmThreshold
,
),
),
),
);
);
emit
(
state
.
copyWith
(
data:
newData
,
clearError:
true
));
emit
(
state
.
copyWith
(
data:
newData
,
clearError:
true
));
...
@@ -287,6 +305,10 @@ class DeviceControlBloc
...
@@ -287,6 +305,10 @@ class DeviceControlBloc
)
async
{
)
async
{
final
currentValue
=
int
.
tryParse
(
state
.
data
.
cabinTemp
.
setValue
)
??
0
;
final
currentValue
=
int
.
tryParse
(
state
.
data
.
cabinTemp
.
setValue
)
??
0
;
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
99
);
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
99
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG cabin_temp_adjust '
'current=
$currentValue
step=
${event.step}
next=
$nextValue
'
,
);
await
_setCabinTempSetValue
(
'
$nextValue
'
,
emit
);
await
_setCabinTempSetValue
(
'
$nextValue
'
,
emit
);
}
}
...
@@ -304,6 +326,9 @@ class DeviceControlBloc
...
@@ -304,6 +326,9 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
cabinTempSetValue:
setValue
),
userSettings:
state
.
userSettings
.
copyWith
(
cabinTempSetValue:
setValue
),
clearError:
true
,
clearError:
true
,
));
));
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG cabin_temp_set_value value=
$setValue
'
,
);
await
_writeControlValueToBluetooth
(
await
_writeControlValueToBluetooth
(
identifier:
0x02
,
identifier:
0x02
,
setValue:
setValue
,
setValue:
setValue
,
...
@@ -361,6 +386,10 @@ class DeviceControlBloc
...
@@ -361,6 +386,10 @@ class DeviceControlBloc
)
async
{
)
async
{
final
currentValue
=
int
.
tryParse
(
state
.
data
.
cabinHumidity
.
setValue
)
??
0
;
final
currentValue
=
int
.
tryParse
(
state
.
data
.
cabinHumidity
.
setValue
)
??
0
;
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
100
);
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
100
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG cabin_humidity_adjust '
'current=
$currentValue
step=
${event.step}
next=
$nextValue
'
,
);
await
_setCabinHumiditySetValue
(
'
$nextValue
'
,
emit
);
await
_setCabinHumiditySetValue
(
'
$nextValue
'
,
emit
);
}
}
...
@@ -379,6 +408,9 @@ class DeviceControlBloc
...
@@ -379,6 +408,9 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
cabinHumiditySetValue:
setValue
),
state
.
userSettings
.
copyWith
(
cabinHumiditySetValue:
setValue
),
clearError:
true
,
clearError:
true
,
));
));
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG cabin_humidity_set_value value=
$setValue
'
,
);
await
_writeControlValueToBluetooth
(
await
_writeControlValueToBluetooth
(
identifier:
0x04
,
identifier:
0x04
,
setValue:
setValue
,
setValue:
setValue
,
...
@@ -436,6 +468,10 @@ class DeviceControlBloc
...
@@ -436,6 +468,10 @@ class DeviceControlBloc
final
currentValue
=
final
currentValue
=
int
.
tryParse
(
state
.
data
.
oxygenConcentration
.
setValue
)
??
0
;
int
.
tryParse
(
state
.
data
.
oxygenConcentration
.
setValue
)
??
0
;
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
100
);
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
100
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG oxygen_concentration_adjust '
'current=
$currentValue
step=
${event.step}
next=
$nextValue
'
,
);
await
_setOxygenConcentrationSetValue
(
'
$nextValue
'
,
emit
);
await
_setOxygenConcentrationSetValue
(
'
$nextValue
'
,
emit
);
}
}
...
@@ -454,6 +490,9 @@ class DeviceControlBloc
...
@@ -454,6 +490,9 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
oxygenConcentrationSetValue:
setValue
),
state
.
userSettings
.
copyWith
(
oxygenConcentrationSetValue:
setValue
),
clearError:
true
,
clearError:
true
,
));
));
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG oxygen_concentration_set_value value=
$setValue
'
,
);
await
_writeControlValueToBluetooth
(
await
_writeControlValueToBluetooth
(
identifier:
0x05
,
identifier:
0x05
,
setValue:
setValue
,
setValue:
setValue
,
...
@@ -512,6 +551,10 @@ class DeviceControlBloc
...
@@ -512,6 +551,10 @@ class DeviceControlBloc
final
currentValue
=
final
currentValue
=
int
.
tryParse
(
state
.
data
.
co2Concentration
.
alarmThreshold
)
??
0
;
int
.
tryParse
(
state
.
data
.
co2Concentration
.
alarmThreshold
)
??
0
;
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
9999
);
final
nextValue
=
(
currentValue
+
event
.
step
).
clamp
(
0
,
9999
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG co2_alarm_threshold_adjust '
'current=
$currentValue
step=
${event.step}
next=
$nextValue
'
,
);
await
_setCO2ConcentrationAlarmThreshold
(
'
$nextValue
'
,
emit
);
await
_setCO2ConcentrationAlarmThreshold
(
'
$nextValue
'
,
emit
);
}
}
...
@@ -530,6 +573,10 @@ class DeviceControlBloc
...
@@ -530,6 +573,10 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
co2AlarmThreshold:
alarmThreshold
),
state
.
userSettings
.
copyWith
(
co2AlarmThreshold:
alarmThreshold
),
clearError:
true
,
clearError:
true
,
));
));
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG co2_alarm_threshold_set_value '
'value=
$alarmThreshold
'
,
);
await
_writeControlValueToBluetooth
(
await
_writeControlValueToBluetooth
(
identifier:
0x31
,
identifier:
0x31
,
setValue:
alarmThreshold
,
setValue:
alarmThreshold
,
...
@@ -581,10 +628,10 @@ class DeviceControlBloc
...
@@ -581,10 +628,10 @@ class DeviceControlBloc
// ==================== 风速调节控制事件处理 ====================
// ==================== 风速调节控制事件处理 ====================
/// [用户操作] 设置风速模式(发送给MCU)—— 持久化
/// [用户操作] 设置风速模式(发送给MCU)—— 持久化
void
_onWindSpeedModeChanged
(
Future
<
void
>
_onWindSpeedModeChanged
(
DeviceControlWindSpeedModeChanged
event
,
DeviceControlWindSpeedModeChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
windSpeed:
state
.
data
.
windSpeed
.
copyWith
(
windSpeed:
state
.
data
.
windSpeed
.
copyWith
(
currentMode:
event
.
mode
,
currentMode:
event
.
mode
,
...
@@ -595,7 +642,12 @@ class DeviceControlBloc
...
@@ -595,7 +642,12 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
windSpeedModeValue:
event
.
mode
),
userSettings:
state
.
userSettings
.
copyWith
(
windSpeedModeValue:
event
.
mode
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeControlValueToBluetooth
(
identifier:
0xB3
,
setValue:
'
${_windSpeedProtocolValue(event.mode)}
'
,
errorPrefix:
'风速调节'
,
emit:
emit
,
);
}
}
// ==================== 雾化时间设置事件处理 ====================
// ==================== 雾化时间设置事件处理 ====================
...
@@ -643,10 +695,10 @@ class DeviceControlBloc
...
@@ -643,10 +695,10 @@ class DeviceControlBloc
// ==================== 开放供氧控制事件处理 ====================
// ==================== 开放供氧控制事件处理 ====================
/// [用户操作] 设置开放供氧开关(发送给MCU)—— 持久化
/// [用户操作] 设置开放供氧开关(发送给MCU)—— 持久化
void
_onOpenOxygenSwitchChanged
(
Future
<
void
>
_onOpenOxygenSwitchChanged
(
DeviceControlOpenOxygenSwitchChanged
event
,
DeviceControlOpenOxygenSwitchChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
openOxygen:
state
.
data
.
openOxygen
.
copyWith
(
openOxygen:
state
.
data
.
openOxygen
.
copyWith
(
isOn:
event
.
isOn
,
isOn:
event
.
isOn
,
...
@@ -657,7 +709,12 @@ class DeviceControlBloc
...
@@ -657,7 +709,12 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
openOxygenIsOn:
event
.
isOn
),
userSettings:
state
.
userSettings
.
copyWith
(
openOxygenIsOn:
event
.
isOn
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x26
,
isOn:
event
.
isOn
,
errorPrefix:
'开放供氧'
,
emit:
emit
,
);
}
}
// ==================== 供氧计时事件处理 ====================
// ==================== 供氧计时事件处理 ====================
...
@@ -696,10 +753,10 @@ class DeviceControlBloc
...
@@ -696,10 +753,10 @@ class DeviceControlBloc
// ==================== 循环模式控制事件处理 ====================
// ==================== 循环模式控制事件处理 ====================
/// [用户操作] 设置循环模式(发送给MCU)—— 持久化
/// [用户操作] 设置循环模式(发送给MCU)—— 持久化
void
_onCirculationModeChanged
(
Future
<
void
>
_onCirculationModeChanged
(
DeviceControlCirculationModeChanged
event
,
DeviceControlCirculationModeChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
circulationMode:
state
.
data
.
circulationMode
.
copyWith
(
circulationMode:
state
.
data
.
circulationMode
.
copyWith
(
currentMode:
event
.
mode
,
currentMode:
event
.
mode
,
...
@@ -711,16 +768,21 @@ class DeviceControlBloc
...
@@ -711,16 +768,21 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
circulationModeValue:
event
.
mode
),
state
.
userSettings
.
copyWith
(
circulationModeValue:
event
.
mode
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x06
,
isOn:
event
.
mode
==
CirculationMode
.
external
,
errorPrefix:
event
.
mode
==
CirculationMode
.
external
?
'外循环'
:
'内循环'
,
emit:
emit
,
);
}
}
// ==================== 灯光控制事件处理 ====================
// ==================== 灯光控制事件处理 ====================
/// [用户操作] 设置检查灯开关(发送给MCU)—— 持久化
/// [用户操作] 设置检查灯开关(发送给MCU)—— 持久化
void
_onCheckLightSwitchChanged
(
Future
<
void
>
_onCheckLightSwitchChanged
(
DeviceControlCheckLightSwitchChanged
event
,
DeviceControlCheckLightSwitchChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
checkLightOn:
event
.
isOn
,
checkLightOn:
event
.
isOn
,
...
@@ -731,14 +793,19 @@ class DeviceControlBloc
...
@@ -731,14 +793,19 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
checkLightOn:
event
.
isOn
),
userSettings:
state
.
userSettings
.
copyWith
(
checkLightOn:
event
.
isOn
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x0A
,
isOn:
event
.
isOn
,
errorPrefix:
'检查灯'
,
emit:
emit
,
);
}
}
/// [用户操作] 设置照明灯开关(发送给MCU)—— 持久化
/// [用户操作] 设置照明灯开关(发送给MCU)—— 持久化
void
_onIlluminationLightSwitchChanged
(
Future
<
void
>
_onIlluminationLightSwitchChanged
(
DeviceControlIlluminationLightSwitchChanged
event
,
DeviceControlIlluminationLightSwitchChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
illuminationOn:
event
.
isOn
,
illuminationOn:
event
.
isOn
,
...
@@ -749,14 +816,19 @@ class DeviceControlBloc
...
@@ -749,14 +816,19 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
illuminationOn:
event
.
isOn
),
userSettings:
state
.
userSettings
.
copyWith
(
illuminationOn:
event
.
isOn
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x09
,
isOn:
event
.
isOn
,
errorPrefix:
'照明灯'
,
emit:
emit
,
);
}
}
/// [用户操作] 设置蓝光灯开关(发送给MCU)—— 持久化
/// [用户操作] 设置蓝光灯开关(发送给MCU)—— 持久化
void
_onBlueLightSwitchChanged
(
Future
<
void
>
_onBlueLightSwitchChanged
(
DeviceControlBlueLightSwitchChanged
event
,
DeviceControlBlueLightSwitchChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
blueLightOn:
event
.
isOn
,
blueLightOn:
event
.
isOn
,
...
@@ -767,14 +839,19 @@ class DeviceControlBloc
...
@@ -767,14 +839,19 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
blueLightOn:
event
.
isOn
),
userSettings:
state
.
userSettings
.
copyWith
(
blueLightOn:
event
.
isOn
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x1E
,
isOn:
event
.
isOn
,
errorPrefix:
'蓝光'
,
emit:
emit
,
);
}
}
/// [用户操作] 设置红光灯开关(发送给MCU)—— 持久化
/// [用户操作] 设置红光灯开关(发送给MCU)—— 持久化
void
_onRedLightSwitchChanged
(
Future
<
void
>
_onRedLightSwitchChanged
(
DeviceControlRedLightSwitchChanged
event
,
DeviceControlRedLightSwitchChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
final
newData
=
state
.
data
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
lightControl:
state
.
data
.
lightControl
.
copyWith
(
redLightOn:
event
.
isOn
,
redLightOn:
event
.
isOn
,
...
@@ -785,7 +862,12 @@ class DeviceControlBloc
...
@@ -785,7 +862,12 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
redLightOn:
event
.
isOn
),
userSettings:
state
.
userSettings
.
copyWith
(
redLightOn:
event
.
isOn
),
clearError:
true
,
clearError:
true
,
));
));
// TODO: 发送设置到MCU
await
_writeSwitchValueToBluetooth
(
identifier:
0x1F
,
isOn:
event
.
isOn
,
errorPrefix:
'红光'
,
emit:
emit
,
);
}
}
// ==================== 全局统计数据事件处理 ====================
// ==================== 全局统计数据事件处理 ====================
...
@@ -860,12 +942,48 @@ class DeviceControlBloc
...
@@ -860,12 +942,48 @@ class DeviceControlBloc
await
_bluetoothManager
.
connectToDeviceId
(
deviceId
);
await
_bluetoothManager
.
connectToDeviceId
(
deviceId
);
}
}
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG send_control '
'identifier=0x
${identifier.toRadixString(16).padLeft(2, '0').toUpperCase()}
'
'value=
$setValue
scale=
$scale
'
'hex=
${BleBluetoothManager.bytesToHex(command)}
'
'isConnected=
${_bluetoothManager.isConnected}
'
,
);
await
_bluetoothManager
.
writeData
(
command
);
await
_bluetoothManager
.
writeData
(
command
);
}
catch
(
e
)
{
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
error:
'
$errorPrefix
发送失败:
$e
'
));
emit
(
state
.
copyWith
(
error:
'
$errorPrefix
发送失败:
$e
'
));
}
}
}
}
Future
<
void
>
_writeSwitchValueToBluetooth
({
required
int
identifier
,
required
bool
isOn
,
required
String
errorPrefix
,
required
Emitter
<
DeviceControlState
>
emit
,
})
async
{
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG switch_set_value '
'name=
$errorPrefix
isOn=
$isOn
'
,
);
await
_writeControlValueToBluetooth
(
identifier:
identifier
,
setValue:
isOn
?
'1'
:
'0'
,
errorPrefix:
errorPrefix
,
emit:
emit
,
);
}
int
_windSpeedProtocolValue
(
WindSpeedMode
mode
)
{
switch
(
mode
)
{
case
WindSpeedMode
.
comfort
:
return
0
;
case
WindSpeedMode
.
strong
:
return
1
;
case
WindSpeedMode
.
sleep
:
return
2
;
}
}
List
<
int
>?
_buildControlValueCommand
({
List
<
int
>?
_buildControlValueCommand
({
required
int
identifier
,
required
int
identifier
,
required
String
setValue
,
required
String
setValue
,
...
@@ -893,10 +1011,18 @@ class DeviceControlBloc
...
@@ -893,10 +1011,18 @@ class DeviceControlBloc
return
fixed
.
endsWith
(
'.0'
)
?
fixed
.
substring
(
0
,
fixed
.
length
-
2
)
:
fixed
;
return
fixed
.
endsWith
(
'.0'
)
?
fixed
.
substring
(
0
,
fixed
.
length
-
2
)
:
fixed
;
}
}
String
_formatTemperatureOrKeep
(
double
value
,
String
fallback
)
{
return
value
.
isNaN
?
fallback
:
_formatTemperature
(
value
);
}
String
_formatInt
(
int
value
)
{
String
_formatInt
(
int
value
)
{
return
value
<
0
?
'--'
:
'
$value
'
;
return
value
<
0
?
'--'
:
'
$value
'
;
}
}
String
_formatIntOrKeep
(
int
value
,
String
fallback
)
{
return
value
<
0
?
fallback
:
'
$value
'
;
}
@override
@override
Future
<
void
>
close
()
async
{
Future
<
void
>
close
()
async
{
await
_mcuReportSub
.
cancel
();
await
_mcuReportSub
.
cancel
();
...
...
lib/utils/bluetooth/bluetooth_manager.dart
View file @
d6fc2a2b
...
@@ -376,6 +376,15 @@ class BleBluetoothManager<T> {
...
@@ -376,6 +376,15 @@ class BleBluetoothManager<T> {
return
;
return
;
}
}
final
hex
=
bytesToHex
(
data
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG write_data '
'deviceId=
${characteristic.deviceId}
'
'service=
${characteristic.serviceId}
'
'characteristic=
${characteristic.characteristicId}
'
'withoutResponse=
$withoutResponse
'
'hex=
$hex
'
,
);
try
{
try
{
if
(
withoutResponse
)
{
if
(
withoutResponse
)
{
await
_ble
.
writeCharacteristicWithoutResponse
(
await
_ble
.
writeCharacteristicWithoutResponse
(
...
@@ -385,9 +394,11 @@ class BleBluetoothManager<T> {
...
@@ -385,9 +394,11 @@ class BleBluetoothManager<T> {
}
else
{
}
else
{
await
_ble
.
writeCharacteristicWithResponse
(
characteristic
,
value:
data
);
await
_ble
.
writeCharacteristicWithResponse
(
characteristic
,
value:
data
);
}
}
_notifyMessage
(
'蓝牙数据发送成功:
${bytesToHex(data)}
'
);
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG write_success hex=
$hex
'
);
_notifyMessage
(
'蓝牙数据发送成功:
$hex
'
);
_sendResultController
.
add
(
true
);
_sendResultController
.
add
(
true
);
}
catch
(
error
)
{
}
catch
(
error
)
{
Log
.
warn
(
'🔥🔥🔥 BLE_WRITE_DEBUG write_error hex=
$hex
error=
$error
'
);
_notifyMessage
(
'蓝牙数据发送失败:
$error
'
);
_notifyMessage
(
'蓝牙数据发送失败:
$error
'
);
_sendResultController
.
add
(
false
);
_sendResultController
.
add
(
false
);
}
}
...
...
lib/utils/numeric_keyboard.dart
0 → 100644
View file @
d6fc2a2b
import
'package:flutter/material.dart'
;
/// 自定义数字键盘
///
/// 使用示例:
/// ```dart
/// final result = await NumericKeyboard.show(
/// context,
/// initialValue: '25',
/// decimalEnabled: true, // 温度带小数点,其他设置为 false
/// );
/// if (result != null) {
/// // 使用 result 更新设定值
/// }
/// ```
class
NumericKeyboard
extends
StatefulWidget
{
const
NumericKeyboard
({
super
.
key
,
required
this
.
initialValue
,
this
.
decimalEnabled
=
false
,
this
.
onChanged
,
this
.
onConfirm
,
});
/// 初始显示值
final
String
initialValue
;
/// 是否允许输入小数点(温度模式为 true)
final
bool
decimalEnabled
;
/// 输入变化回调(可选)
final
ValueChanged
<
String
>?
onChanged
;
/// 确认按钮回调(可选,配合页面内嵌使用)
final
ValueChanged
<
String
>?
onConfirm
;
/// 弹出数字键盘并返回用户确认的值
///
/// [context] 构建上下文
/// [initialValue] 初始值,默认为空字符串
/// [decimalEnabled] 是否启用小数点输入,默认为 false
///
/// 返回用户点击确认时的字符串,或 null(如退回/关闭)
static
Future
<
String
?>
show
(
BuildContext
context
,
{
String
initialValue
=
''
,
bool
decimalEnabled
=
false
,
})
{
return
showModalBottomSheet
<
String
>(
context:
context
,
isScrollControlled:
true
,
backgroundColor:
Colors
.
transparent
,
barrierColor:
Colors
.
black54
,
builder:
(
_
)
=>
NumericKeyboard
(
initialValue:
initialValue
,
decimalEnabled:
decimalEnabled
,
),
);
}
@override
State
<
NumericKeyboard
>
createState
()
=>
_NumericKeyboardState
();
}
class
_NumericKeyboardState
extends
State
<
NumericKeyboard
>
{
late
String
_text
;
bool
get
_hasDecimal
=>
_text
.
contains
(
'.'
);
@override
void
initState
()
{
super
.
initState
();
_text
=
widget
.
initialValue
;
}
void
_onKeyTap
(
String
key
)
{
setState
(()
{
switch
(
key
)
{
case
'⌫'
:
if
(
_text
.
isNotEmpty
)
{
_text
=
_text
.
substring
(
0
,
_text
.
length
-
1
);
}
break
;
case
'C'
:
_text
=
''
;
break
;
case
'OK'
:
// 确认时调用回调或关闭弹窗
if
(
widget
.
onConfirm
!=
null
)
{
widget
.
onConfirm
!(
_text
);
}
else
{
Navigator
.
of
(
context
).
pop
(
_text
);
}
return
;
// 不触发 onChanged 和后续 setState 中的 pop
case
'.'
:
if
(!
widget
.
decimalEnabled
||
_hasDecimal
)
return
;
if
(
_text
.
isEmpty
)
{
_text
=
'0.'
;
}
else
{
_text
=
'
$_text
.'
;
}
break
;
default
:
// 数字 0-9
// 避免前导零(除非是 "0." 开头)
if
(
_text
==
'0'
&&
key
!=
'.'
)
{
_text
=
key
;
}
else
{
_text
=
'
$_text$key
'
;
}
break
;
}
});
if
(
key
!=
'OK'
)
{
widget
.
onChanged
?.
call
(
_text
);
}
}
@override
Widget
build
(
BuildContext
context
)
{
final
bottomPadding
=
MediaQuery
.
of
(
context
).
padding
.
bottom
;
return
Container
(
padding:
EdgeInsets
.
fromLTRB
(
18
,
18
,
18
,
bottomPadding
+
14
),
decoration:
const
BoxDecoration
(
gradient:
LinearGradient
(
begin:
Alignment
.
topCenter
,
end:
Alignment
.
bottomCenter
,
colors:
[
Color
(
0xFF0D3558
),
Color
(
0xFF06192F
),
],
),
border:
Border
(
top:
BorderSide
(
color:
Color
(
0xFF7BD5FF
),
width:
1.5
),
),
boxShadow:
[
BoxShadow
(
color:
Color
(
0x8000A8FF
),
blurRadius:
22
,
offset:
Offset
(
0
,
-
6
),
),
],
),
child:
SafeArea
(
top:
false
,
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
_buildHandle
(),
const
SizedBox
(
height:
14
),
_buildDisplayArea
(),
const
SizedBox
(
height:
10
),
_buildKeypad
(),
],
),
),
);
}
Widget
_buildHandle
()
{
return
Container
(
width:
78
,
height:
4
,
decoration:
BoxDecoration
(
color:
const
Color
(
0xFF79D7FF
).
withValues
(
alpha:
0.72
),
borderRadius:
BorderRadius
.
circular
(
2
),
),
);
}
Widget
_buildDisplayArea
()
{
return
Row
(
children:
[
Expanded
(
child:
Container
(
height:
58
,
alignment:
Alignment
.
centerRight
,
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
18
),
decoration:
BoxDecoration
(
color:
const
Color
(
0xFF041126
),
borderRadius:
BorderRadius
.
circular
(
8
),
border:
Border
.
all
(
color:
const
Color
(
0xFF58BCEB
),
width:
1.2
),
boxShadow:
const
[
BoxShadow
(
color:
Color
(
0x4000A8FF
),
blurRadius:
14
,
offset:
Offset
(
0
,
0
),
),
],
),
child:
Text
(
_text
.
isEmpty
?
'0'
:
_text
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
const
TextStyle
(
fontSize:
30
,
height:
1
,
fontWeight:
FontWeight
.
w700
,
color:
Color
(
0xFFEAF9FF
),
),
),
),
),
const
SizedBox
(
width:
12
),
SizedBox
(
width:
104
,
height:
58
,
child:
Material
(
color:
Colors
.
transparent
,
borderRadius:
BorderRadius
.
circular
(
8
),
child:
InkWell
(
borderRadius:
BorderRadius
.
circular
(
8
),
onTap:
()
=>
_onKeyTap
(
'OK'
),
child:
Ink
(
decoration:
BoxDecoration
(
gradient:
const
LinearGradient
(
colors:
[
Color
(
0xFF25D8FF
),
Color
(
0xFF1577D8
),
],
),
borderRadius:
BorderRadius
.
circular
(
8
),
border:
Border
.
all
(
color:
const
Color
(
0xFFB6F4FF
)),
boxShadow:
const
[
BoxShadow
(
color:
Color
(
0x6625D8FF
),
blurRadius:
16
,
),
],
),
child:
const
Center
(
child:
Text
(
'确定'
,
style:
TextStyle
(
fontSize:
18
,
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w700
,
),
),
),
),
),
),
),
],
);
}
Widget
_buildKeypad
()
{
// 按键定义:行列表,每行包含(标签, 宽度比例, 颜色类型)
final
keys
=
<
List
<
_KeyDef
>>[
[
const
_KeyDef
(
'1'
,
1
),
const
_KeyDef
(
'2'
,
1
),
const
_KeyDef
(
'3'
,
1
),
const
_KeyDef
(
'⌫'
,
1
,
isAction:
true
),
],
[
const
_KeyDef
(
'4'
,
1
),
const
_KeyDef
(
'5'
,
1
),
const
_KeyDef
(
'6'
,
1
),
const
_KeyDef
(
'C'
,
1
,
isAction:
true
),
],
[
const
_KeyDef
(
'7'
,
1
),
const
_KeyDef
(
'8'
,
1
),
const
_KeyDef
(
'9'
,
1
),
const
_KeyDef
(
''
,
1
),
],
[
if
(
widget
.
decimalEnabled
)
const
_KeyDef
(
'.'
,
1
),
_KeyDef
(
'0'
,
widget
.
decimalEnabled
?
2
:
3
),
const
_KeyDef
(
''
,
1
),
],
];
return
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
2
),
child:
Column
(
children:
keys
.
map
((
row
)
{
return
Row
(
children:
row
.
map
((
keyDef
)
{
if
(
keyDef
.
label
.
isEmpty
)
{
return
const
Spacer
(
flex:
1
);
}
return
Expanded
(
flex:
keyDef
.
flex
,
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
5
),
child:
_buildKeyButton
(
keyDef
),
),
);
}).
toList
(),
);
}).
toList
(),
),
);
}
Widget
_buildKeyButton
(
_KeyDef
keyDef
)
{
final
isAction
=
keyDef
.
isAction
;
return
SizedBox
(
height:
60
,
child:
Material
(
color:
Colors
.
transparent
,
borderRadius:
BorderRadius
.
circular
(
8
),
child:
InkWell
(
borderRadius:
BorderRadius
.
circular
(
8
),
onTap:
()
=>
_onKeyTap
(
keyDef
.
label
),
child:
Ink
(
decoration:
BoxDecoration
(
color:
isAction
?
const
Color
(
0xFF10385D
)
:
const
Color
(
0xFF092849
),
borderRadius:
BorderRadius
.
circular
(
8
),
border:
Border
.
all
(
color:
isAction
?
const
Color
(
0xFF5D9EC5
)
:
const
Color
(
0xFF3F9ED2
),
width:
1
,
),
boxShadow:
const
[
BoxShadow
(
color:
Color
(
0x2600A8FF
),
blurRadius:
10
,
offset:
Offset
(
0
,
2
),
),
],
),
child:
Center
(
child:
Text
(
keyDef
.
label
,
style:
TextStyle
(
fontSize:
isAction
?
20
:
25
,
fontWeight:
isAction
?
FontWeight
.
w600
:
FontWeight
.
w700
,
color:
isAction
?
const
Color
(
0xFFB8E7FF
)
:
const
Color
(
0xFFEAF9FF
),
),
),
),
),
),
),
);
}
}
/// 键盘按键定义
class
_KeyDef
{
final
String
label
;
final
int
flex
;
final
bool
isAction
;
const
_KeyDef
(
this
.
label
,
this
.
flex
,
{
this
.
isAction
=
false
});
}
lib/views/monitoring/device_control/device_control_index_view.dart
View file @
d6fc2a2b
...
@@ -2,6 +2,9 @@ import 'package:auto_route/auto_route.dart';
...
@@ -2,6 +2,9 @@ import 'package:auto_route/auto_route.dart';
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.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
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'cubit/device_control_index_cubit.dart'
;
import
'cubit/device_control_index_cubit.dart'
;
...
@@ -130,23 +133,32 @@ class _DeviceControlLayout extends StatelessWidget {
...
@@ -130,23 +133,32 @@ class _DeviceControlLayout extends StatelessWidget {
SizedBox
(
height:
24
.
h
),
SizedBox
(
height:
24
.
h
),
Expanded
(
Expanded
(
flex:
58
,
flex:
58
,
child:
Row
(
child:
LayoutBuilder
(
builder:
(
context
,
constraints
)
{
final
gap
=
24
.
w
;
final
metricCardWidth
=
(
constraints
.
maxWidth
-
gap
*
3
)
/
4
;
final
modePanelWidth
=
metricCardWidth
*
2
+
gap
;
return
Row
(
children:
[
children:
[
const
Expanded
(
SizedBox
(
flex:
48
,
width:
modePanelWidth
,
child:
DeviceControlModePanel
(),
child:
const
DeviceControlModePanel
(),
),
),
SizedBox
(
width:
24
.
w
),
SizedBox
(
width:
gap
),
const
Expanded
(
SizedBox
(
flex:
23
,
width:
metricCardWidth
,
child:
DeviceControlWindPanel
(),
child:
const
DeviceControlWindPanel
(),
),
),
SizedBox
(
width:
24
.
w
),
SizedBox
(
width:
gap
),
const
Expanded
(
SizedBox
(
flex:
29
,
width:
metricCardWidth
,
child:
DeviceControlTimePanel
(),
child:
const
DeviceControlTimePanel
(),
),
),
],
],
);
},
),
),
),
),
],
],
...
@@ -169,14 +181,27 @@ class _DeviceControlLayout extends StatelessWidget {
...
@@ -169,14 +181,27 @@ class _DeviceControlLayout extends StatelessWidget {
),
),
),
),
SizedBox
(
height:
24
.
h
),
SizedBox
(
height:
24
.
h
),
const
Expanded
(
Expanded
(
flex:
20
,
flex:
20
,
child:
DeviceControlInfoPanel
(
child:
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
buildWhen:
(
previous
,
current
)
=>
previous
.
data
.
openOxygen
!=
current
.
data
.
openOxygen
,
builder:
(
context
,
state
)
{
final
isOn
=
state
.
data
.
openOxygen
.
isOn
;
return
DeviceControlInfoPanel
(
title:
'开放供氧'
,
title:
'开放供氧'
,
actionText:
'Off'
,
actionText:
isOn
?
'ON'
:
'Off'
,
active:
isOn
,
icon:
Icons
.
air
,
icon:
Icons
.
air
,
body:
'开启后风扇打开、制冷、加热和
\n
循环等设备暂停。'
,
body:
'开启后风扇打开、制冷、加热和
\n
循环等设备暂停。'
,
warning:
'*保持设备周围空气相对禁止,严禁烟火'
,
warning:
'*保持设备周围空气相对禁止,严禁烟火'
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
DeviceControlOpenOxygenSwitchChanged
(!
isOn
),
),
);
},
),
),
),
),
SizedBox
(
height:
24
.
h
),
SizedBox
(
height:
24
.
h
),
...
...
lib/views/monitoring/device_control/widgets/device_control_care_panel.dart
View file @
d6fc2a2b
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.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/device_control_bo.dart'
;
import
'device_control_common.dart'
;
import
'device_control_common.dart'
;
...
@@ -9,6 +14,12 @@ class DeviceControlCarePanel extends StatelessWidget {
...
@@ -9,6 +14,12 @@ class DeviceControlCarePanel extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
buildWhen:
(
previous
,
current
)
=>
previous
.
data
.
careLevel
!=
current
.
data
.
careLevel
,
builder:
(
context
,
state
)
{
final
currentLevel
=
state
.
data
.
careLevel
.
currentLevel
;
return
DeviceControlPanelFrame
(
return
DeviceControlPanelFrame
(
backgroundAsset:
assetPanelWide
,
backgroundAsset:
assetPanelWide
,
padding:
EdgeInsets
.
fromLTRB
(
30
.
w
,
26
.
h
,
30
.
w
,
24
.
h
),
padding:
EdgeInsets
.
fromLTRB
(
30
.
w
,
26
.
h
,
30
.
w
,
24
.
h
),
...
@@ -17,35 +28,160 @@ class DeviceControlCarePanel extends StatelessWidget {
...
@@ -17,35 +28,160 @@ class DeviceControlCarePanel extends StatelessWidget {
children:
[
children:
[
const
DeviceControlPanelTitle
(
'护理等级'
),
const
DeviceControlPanelTitle
(
'护理等级'
),
const
Spacer
(),
const
Spacer
(),
Container
(
_CareLevelBar
(
currentLevel:
currentLevel
),
SizedBox
(
height:
24
.
h
),
Row
(
children:
[
for
(
final
item
in
_careLevelItems
)
Expanded
(
child:
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
DeviceControlCareLevelChanged
(
item
.
level
),
),
child:
Text
(
item
.
label
,
textAlign:
TextAlign
.
center
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
.
sp
,
),
),
),
),
],
),
],
),
);
},
);
}
}
class
_CareLevelBar
extends
StatelessWidget
{
const
_CareLevelBar
({
required
this
.
currentLevel
});
final
CareLevel
currentLevel
;
@override
Widget
build
(
BuildContext
context
)
{
final
activeIndex
=
_careLevelItems
.
indexWhere
(
(
item
)
=>
item
.
level
==
currentLevel
,
);
return
LayoutBuilder
(
builder:
(
context
,
constraints
)
{
final
segmentGap
=
4
.
w
;
final
knobSize
=
54
.
w
;
final
segmentWidth
=
(
constraints
.
maxWidth
-
segmentGap
*
(
_careLevelItems
.
length
-
1
))
/
_careLevelItems
.
length
;
final
knobLeft
=
(
segmentWidth
+
segmentGap
)
*
activeIndex
+
segmentWidth
-
knobSize
/
2
;
return
SizedBox
(
height:
knobSize
,
child:
Stack
(
clipBehavior:
Clip
.
none
,
alignment:
Alignment
.
centerLeft
,
children:
[
Positioned
(
left:
0
,
right:
0
,
top:
(
knobSize
-
20
.
h
)
/
2
,
child:
Row
(
children:
[
for
(
var
index
=
0
;
index
<
_careLevelItems
.
length
;
index
++)
...[
Expanded
(
child:
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
DeviceControlCareLevelChanged
(
_careLevelItems
[
index
].
level
,
),
),
child:
Container
(
height:
20
.
h
,
height:
20
.
h
,
decoration:
BoxDecoration
(
decoration:
BoxDecoration
(
borderRadius:
BorderRadius
.
circular
(
10
.
r
),
color:
index
<=
activeIndex
gradient:
const
LinearGradient
(
?
_careLevelItems
[
index
].
color
colors:
[
:
const
Color
(
0xFF6A7481
),
Color
(
0xFFDCEFFC
),
borderRadius:
_segmentRadius
(
index
),
Color
(
0xFF71FFCF
),
boxShadow:
index
<=
activeIndex
Color
(
0xFFE4CF1D
),
?
[
Color
(
0xFFFFA11B
),
BoxShadow
(
Color
(
0xFFFF5945
),
color:
_careLevelItems
[
index
]
Color
(
0xFFFFFFFF
),
.
color
],
.
withValues
(
alpha:
0.35
),
blurRadius:
8
.
r
,
),
),
]
:
null
,
),
),
),
),
SizedBox
(
height:
24
.
h
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
for
(
final
item
in
const
[
'关闭'
,
'三级'
,
'二级'
,
'一级'
,
'特级'
])
Text
(
item
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
.
sp
),
),
),
),
if
(
index
<
_careLevelItems
.
length
-
1
)
SizedBox
(
width:
segmentGap
),
],
],
],
),
),
),
Positioned
(
left:
knobLeft
.
clamp
(
0
,
constraints
.
maxWidth
-
knobSize
),
child:
Container
(
width:
knobSize
,
height:
knobSize
,
decoration:
BoxDecoration
(
color:
const
Color
(
0xFFF7F7F7
),
shape:
BoxShape
.
circle
,
boxShadow:
[
BoxShadow
(
color:
Colors
.
white
.
withValues
(
alpha:
0.72
),
blurRadius:
10
.
r
,
),
],
),
),
),
],
],
),
),
);
);
},
);
}
BorderRadius
_segmentRadius
(
int
index
)
{
final
radius
=
Radius
.
circular
(
12
.
r
);
if
(
index
==
0
)
{
return
BorderRadius
.
horizontal
(
left:
radius
);
}
}
if
(
index
==
_careLevelItems
.
length
-
1
)
{
return
BorderRadius
.
horizontal
(
right:
radius
);
}
return
BorderRadius
.
zero
;
}
}
const
_careLevelItems
=
[
_CareLevelItem
(
'关闭'
,
CareLevel
.
off
,
Color
(
0xFFE5F7FF
)),
_CareLevelItem
(
'三级'
,
CareLevel
.
level3
,
Color
(
0xFF60F2B6
)),
_CareLevelItem
(
'二级'
,
CareLevel
.
level2
,
Color
(
0xFFFFCE1B
)),
_CareLevelItem
(
'一级'
,
CareLevel
.
level1
,
Color
(
0xFFFF8D2E
)),
_CareLevelItem
(
'特级'
,
CareLevel
.
special
,
Color
(
0xFFFF554C
)),
];
class
_CareLevelItem
{
const
_CareLevelItem
(
this
.
label
,
this
.
level
,
this
.
color
);
final
String
label
;
final
CareLevel
level
;
final
Color
color
;
}
}
lib/views/monitoring/device_control/widgets/device_control_info_panel.dart
View file @
d6fc2a2b
...
@@ -12,6 +12,8 @@ class DeviceControlInfoPanel extends StatelessWidget {
...
@@ -12,6 +12,8 @@ class DeviceControlInfoPanel extends StatelessWidget {
required
this
.
icon
,
required
this
.
icon
,
required
this
.
body
,
required
this
.
body
,
this
.
warning
,
this
.
warning
,
this
.
active
=
false
,
this
.
onTap
,
});
});
final
String
title
;
final
String
title
;
...
@@ -19,10 +21,15 @@ class DeviceControlInfoPanel extends StatelessWidget {
...
@@ -19,10 +21,15 @@ class DeviceControlInfoPanel extends StatelessWidget {
final
IconData
icon
;
final
IconData
icon
;
final
String
body
;
final
String
body
;
final
String
?
warning
;
final
String
?
warning
;
final
bool
active
;
final
VoidCallback
?
onTap
;
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
DeviceControlPanelFrame
(
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
DeviceControlPanelFrame
(
backgroundAsset:
assetPanelWide
,
backgroundAsset:
assetPanelWide
,
padding:
EdgeInsets
.
fromLTRB
(
30
.
w
,
22
.
h
,
30
.
w
,
24
.
h
),
padding:
EdgeInsets
.
fromLTRB
(
30
.
w
,
22
.
h
,
30
.
w
,
24
.
h
),
child:
Column
(
child:
Column
(
...
@@ -31,13 +38,19 @@ class DeviceControlInfoPanel extends StatelessWidget {
...
@@ -31,13 +38,19 @@ class DeviceControlInfoPanel extends StatelessWidget {
Row
(
Row
(
children:
[
children:
[
Expanded
(
child:
DeviceControlPanelTitle
(
title
)),
Expanded
(
child:
DeviceControlPanelTitle
(
title
)),
DeviceControlPill
(
text:
actionText
),
DeviceControlPill
(
text:
actionText
,
active:
active
),
],
],
),
),
const
Spacer
(),
const
Spacer
(),
Row
(
Row
(
children:
[
children:
[
Icon
(
icon
,
color:
const
Color
(
0xFF6AC6F2
),
size:
54
.
w
),
Icon
(
icon
,
color:
active
?
const
Color
(
0xFF45F36F
)
:
const
Color
(
0xFF6AC6F2
),
size:
54
.
w
,
),
SizedBox
(
width:
28
.
w
),
SizedBox
(
width:
28
.
w
),
Expanded
(
Expanded
(
child:
Text
(
child:
Text
(
...
@@ -63,6 +76,7 @@ class DeviceControlInfoPanel extends StatelessWidget {
...
@@ -63,6 +76,7 @@ class DeviceControlInfoPanel extends StatelessWidget {
],
],
],
],
),
),
),
);
);
}
}
}
}
lib/views/monitoring/device_control/widgets/device_control_metric_card.dart
View file @
d6fc2a2b
...
@@ -5,6 +5,7 @@ import 'package:laki_icu_app/blocs/device_control/device_control_bloc.dart';
...
@@ -5,6 +5,7 @@ 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_event.dart'
;
import
'package:laki_icu_app/blocs/device_control/device_control_state.dart'
;
import
'package:laki_icu_app/blocs/device_control/device_control_state.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/utils/numeric_keyboard.dart'
;
import
'device_control_common.dart'
;
import
'device_control_common.dart'
;
...
@@ -64,6 +65,12 @@ class DeviceControlMetricCard extends StatelessWidget {
...
@@ -64,6 +65,12 @@ class DeviceControlMetricCard extends StatelessWidget {
isOxygen:
isOxygen
,
isOxygen:
isOxygen
,
isCo2:
isCo2
,
isCo2:
isCo2
,
);
);
final
setValueEventBuilder
=
_setValueEventBuilder
(
isTemperature:
isTemperature
,
isHumidity:
isHumidity
,
isOxygen:
isOxygen
,
isCo2:
isCo2
,
);
return
DeviceControlPanelFrame
(
return
DeviceControlPanelFrame
(
active:
isOxygen
,
active:
isOxygen
,
...
@@ -147,7 +154,20 @@ class DeviceControlMetricCard extends StatelessWidget {
...
@@ -147,7 +154,20 @@ class DeviceControlMetricCard extends StatelessWidget {
),
),
),
),
const
Spacer
(),
const
Spacer
(),
Text
(
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
setValueEventBuilder
==
null
?
null
:
()
=>
_showSetValueKeyboard
(
context
,
initialValue:
setValue
,
decimalEnabled:
isTemperature
,
eventBuilder:
setValueEventBuilder
,
),
child:
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
18
.
w
,
vertical:
8
.
h
),
child:
Text
(
setValue
,
setValue
,
style:
TextStyle
(
style:
TextStyle
(
color:
isCo2
color:
isCo2
...
@@ -158,6 +178,8 @@ class DeviceControlMetricCard extends StatelessWidget {
...
@@ -158,6 +178,8 @@ class DeviceControlMetricCard extends StatelessWidget {
fontWeight:
FontWeight
.
bold
,
fontWeight:
FontWeight
.
bold
,
),
),
),
),
),
),
const
Spacer
(),
const
Spacer
(),
_MetricAdjustButton
(
_MetricAdjustButton
(
text:
'+'
,
text:
'+'
,
...
@@ -206,6 +228,46 @@ class DeviceControlMetricCard extends StatelessWidget {
...
@@ -206,6 +228,46 @@ class DeviceControlMetricCard extends StatelessWidget {
}
}
return
null
;
return
null
;
}
}
static
DeviceControlEvent
Function
(
String
value
)?
_setValueEventBuilder
({
required
bool
isTemperature
,
required
bool
isHumidity
,
required
bool
isOxygen
,
required
bool
isCo2
,
})
{
if
(
isTemperature
)
{
return
(
value
)
=>
DeviceControlCabinTempSetValueChanged
(
value
);
}
if
(
isHumidity
)
{
return
(
value
)
=>
DeviceControlCabinHumiditySetValueChanged
(
value
);
}
if
(
isOxygen
)
{
return
(
value
)
=>
DeviceControlOxygenConcentrationSetValueChanged
(
value
);
}
if
(
isCo2
)
{
return
(
value
)
=>
DeviceControlCO2ConcentrationAlarmThresholdChanged
(
value
);
}
return
null
;
}
static
Future
<
void
>
_showSetValueKeyboard
(
BuildContext
context
,
{
required
String
initialValue
,
required
bool
decimalEnabled
,
required
DeviceControlEvent
Function
(
String
value
)
eventBuilder
,
})
async
{
final
result
=
await
NumericKeyboard
.
show
(
context
,
initialValue:
initialValue
==
'--'
?
''
:
initialValue
,
decimalEnabled:
decimalEnabled
,
);
if
(!
context
.
mounted
||
result
==
null
)
return
;
final
value
=
result
.
trim
();
if
(
value
.
isEmpty
||
value
==
'.'
)
return
;
context
.
read
<
DeviceControlBloc
>().
add
(
eventBuilder
(
value
));
}
}
}
class
_MetricAdjustButton
extends
StatelessWidget
{
class
_MetricAdjustButton
extends
StatelessWidget
{
...
...
lib/views/monitoring/device_control/widgets/device_control_mode_panel.dart
View file @
d6fc2a2b
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.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/device_control_bo.dart'
;
import
'device_control_common.dart'
;
import
'device_control_common.dart'
;
...
@@ -9,34 +14,98 @@ class DeviceControlModePanel extends StatelessWidget {
...
@@ -9,34 +14,98 @@ class DeviceControlModePanel extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
const
items
=
[
return
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
_ModeItem
(
label:
'外循环'
,
icon:
Icons
.
sync
,
active:
true
),
buildWhen:
(
previous
,
current
)
=>
_ModeItem
(
label:
'检查灯'
,
icon:
Icons
.
light
),
previous
.
data
.
circulationMode
!=
current
.
data
.
circulationMode
||
_ModeItem
(
label:
'照明灯'
,
icon:
Icons
.
lightbulb
),
previous
.
data
.
lightControl
!=
current
.
data
.
lightControl
,
_ModeItem
(
label:
'内循环'
,
icon:
Icons
.
autorenew
),
builder:
(
context
,
state
)
{
_ModeItem
(
label:
'蓝光'
,
icon:
Icons
.
wb_sunny_outlined
),
final
circulationMode
=
state
.
data
.
circulationMode
.
currentMode
;
_ModeItem
(
label:
'红光'
,
icon:
Icons
.
wb_sunny
),
final
lightControl
=
state
.
data
.
lightControl
;
final
items
=
[
_ModeItem
(
label:
'外循环'
,
icon:
Icons
.
sync
,
active:
circulationMode
==
CirculationMode
.
external
,
event:
const
DeviceControlCirculationModeChanged
(
CirculationMode
.
external
,
),
),
_ModeItem
(
label:
'检查灯'
,
icon:
Icons
.
light
,
active:
lightControl
.
checkLightOn
,
event:
DeviceControlCheckLightSwitchChanged
(
!
lightControl
.
checkLightOn
,
),
),
_ModeItem
(
label:
'照明灯'
,
icon:
Icons
.
lightbulb
,
active:
lightControl
.
illuminationOn
,
event:
DeviceControlIlluminationLightSwitchChanged
(
!
lightControl
.
illuminationOn
,
),
),
_ModeItem
(
label:
'内循环'
,
icon:
Icons
.
autorenew
,
active:
circulationMode
==
CirculationMode
.
internal
,
event:
const
DeviceControlCirculationModeChanged
(
CirculationMode
.
internal
,
),
),
_ModeItem
(
label:
'蓝光'
,
icon:
Icons
.
wb_sunny_outlined
,
active:
lightControl
.
blueLightOn
,
event:
DeviceControlBlueLightSwitchChanged
(
!
lightControl
.
blueLightOn
,
),
),
_ModeItem
(
label:
'红光'
,
icon:
Icons
.
wb_sunny
,
active:
lightControl
.
redLightOn
,
event:
DeviceControlRedLightSwitchChanged
(
!
lightControl
.
redLightOn
,
),
),
];
];
return
DeviceControlPanelFrame
(
return
DeviceControlPanelFrame
(
backgroundAsset:
assetPanelLarge
,
backgroundAsset:
assetPanelLarge
,
padding:
EdgeInsets
.
all
(
28
.
w
),
padding:
EdgeInsets
.
fromLTRB
(
30
.
w
,
30
.
h
,
30
.
w
,
30
.
h
),
child:
GridView
.
count
(
child:
LayoutBuilder
(
crossAxisCount:
3
,
builder:
(
context
,
constraints
)
{
crossAxisSpacing:
24
.
w
,
final
horizontalGap
=
22
.
w
;
mainAxisSpacing:
24
.
h
,
final
verticalGap
=
22
.
h
;
physics:
const
NeverScrollableScrollPhysics
(),
final
itemWidth
=
(
constraints
.
maxWidth
-
horizontalGap
*
2
)
/
3
;
childAspectRatio:
1.1
,
final
itemHeight
=
(
constraints
.
maxHeight
-
verticalGap
)
/
2
;
return
Wrap
(
spacing:
horizontalGap
,
runSpacing:
verticalGap
,
children:
[
children:
[
for
(
final
item
in
items
)
for
(
final
item
in
items
)
_ModeButton
(
SizedBox
(
width:
itemWidth
,
height:
itemHeight
,
child:
_ModeButton
(
label:
item
.
label
,
label:
item
.
label
,
icon:
item
.
icon
,
icon:
item
.
icon
,
active:
item
.
active
,
active:
item
.
active
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
item
.
event
,
),
),
),
),
],
],
);
},
),
),
);
);
},
);
}
}
}
}
...
@@ -45,29 +114,41 @@ class _ModeButton extends StatelessWidget {
...
@@ -45,29 +114,41 @@ class _ModeButton extends StatelessWidget {
required
this
.
label
,
required
this
.
label
,
required
this
.
icon
,
required
this
.
icon
,
this
.
active
=
false
,
this
.
active
=
false
,
this
.
onTap
,
});
});
final
String
label
;
final
String
label
;
final
IconData
icon
;
final
IconData
icon
;
final
bool
active
;
final
bool
active
;
final
VoidCallback
?
onTap
;
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
color
=
active
?
const
Color
(
0xFF45F36F
)
:
const
Color
(
0xFF7DC4EA
);
final
color
=
active
?
const
Color
(
0xFF45F36F
)
:
const
Color
(
0xFF7DC4EA
);
return
Container
(
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
Container
(
decoration:
deviceControlButtonDecoration
(
decoration:
deviceControlButtonDecoration
(
active:
active
,
active:
active
,
backgroundAsset:
assetArrowButton
,
backgroundAsset:
assetArrowButton
,
),
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
16
.
w
,
vertical:
18
.
h
),
padding:
EdgeInsets
.
fromLTRB
(
12
.
w
,
6
.
h
,
12
.
w
,
16
.
h
),
child:
Column
(
child:
Stack
(
mainAxisAlignment:
MainAxis
Alignment
.
center
,
alignment:
Alignment
.
center
,
children:
[
children:
[
Icon
(
icon
,
color:
color
,
size:
56
.
w
),
Positioned
(
SizedBox
(
height:
20
.
h
),
top:
28
.
h
,
Text
(
child:
Icon
(
icon
,
color:
color
,
size:
100
.
w
),
),
Positioned
(
left:
0
,
right:
0
,
bottom:
0
,
child:
Text
(
label
,
label
,
maxLines:
1
,
maxLines:
1
,
textAlign:
TextAlign
.
center
,
overflow:
TextOverflow
.
ellipsis
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
style:
TextStyle
(
color:
color
,
color:
color
,
...
@@ -75,8 +156,10 @@ class _ModeButton extends StatelessWidget {
...
@@ -75,8 +156,10 @@ class _ModeButton extends StatelessWidget {
fontWeight:
FontWeight
.
w700
,
fontWeight:
FontWeight
.
w700
,
),
),
),
),
),
],
],
),
),
),
);
);
}
}
}
}
...
@@ -85,10 +168,12 @@ class _ModeItem {
...
@@ -85,10 +168,12 @@ class _ModeItem {
final
String
label
;
final
String
label
;
final
IconData
icon
;
final
IconData
icon
;
final
bool
active
;
final
bool
active
;
final
DeviceControlEvent
event
;
const
_ModeItem
({
const
_ModeItem
({
required
this
.
label
,
required
this
.
label
,
required
this
.
icon
,
required
this
.
icon
,
required
this
.
event
,
this
.
active
=
false
,
this
.
active
=
false
,
});
});
}
}
lib/views/monitoring/device_control/widgets/device_control_time_panel.dart
View file @
d6fc2a2b
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.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/utils/numeric_keyboard.dart'
;
import
'device_control_common.dart'
;
import
'device_control_common.dart'
;
...
@@ -9,6 +14,14 @@ class DeviceControlTimePanel extends StatelessWidget {
...
@@ -9,6 +14,14 @@ class DeviceControlTimePanel extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
buildWhen:
(
previous
,
current
)
=>
previous
.
data
.
nebulizationTime
!=
current
.
data
.
nebulizationTime
||
previous
.
data
.
disinfectionTime
!=
current
.
data
.
disinfectionTime
,
builder:
(
context
,
state
)
{
final
nebulizationMinutes
=
state
.
data
.
nebulizationTime
.
setMinutes
;
final
disinfectionMinutes
=
state
.
data
.
disinfectionTime
.
setMinutes
;
return
DeviceControlPanelFrame
(
return
DeviceControlPanelFrame
(
backgroundAsset:
assetPanelTall
,
backgroundAsset:
assetPanelTall
,
padding:
EdgeInsets
.
fromLTRB
(
34
.
w
,
28
.
h
,
34
.
w
,
28
.
h
),
padding:
EdgeInsets
.
fromLTRB
(
34
.
w
,
28
.
h
,
34
.
w
,
28
.
h
),
...
@@ -17,21 +30,82 @@ class DeviceControlTimePanel extends StatelessWidget {
...
@@ -17,21 +30,82 @@ class DeviceControlTimePanel extends StatelessWidget {
children:
[
children:
[
const
DeviceControlPanelTitle
(
'设置雾化时间'
),
const
DeviceControlPanelTitle
(
'设置雾化时间'
),
SizedBox
(
height:
18
.
h
),
SizedBox
(
height:
18
.
h
),
const
Expanded
(
Expanded
(
child:
DeviceControlDigitalValueBox
(
value:
'00'
,
unit:
'分钟'
),
child:
_TimeValueButton
(
value:
nebulizationMinutes
,
active:
false
,
onTap:
()
=>
_showTimeKeyboard
(
context
,
initialValue:
nebulizationMinutes
,
eventBuilder:
(
value
)
=>
DeviceControlNebulizationTimeChanged
(
value
),
),
),
),
Divider
(
color:
Colors
.
white
.
withValues
(
alpha:
0.16
),
height:
36
.
h
,
),
),
Divider
(
color:
Colors
.
white
.
withValues
(
alpha:
0.16
),
height:
36
.
h
),
const
DeviceControlPanelTitle
(
'设置消毒时间'
),
const
DeviceControlPanelTitle
(
'设置消毒时间'
),
SizedBox
(
height:
18
.
h
),
SizedBox
(
height:
18
.
h
),
const
Expanded
(
Expanded
(
child:
DeviceControlDigitalValueBox
(
child:
_TimeValueButton
(
value:
'14'
,
value:
disinfectionMinutes
,
unit:
'分钟'
,
active:
true
,
active:
true
,
onTap:
()
=>
_showTimeKeyboard
(
context
,
initialValue:
disinfectionMinutes
,
eventBuilder:
(
value
)
=>
DeviceControlDisinfectionTimeChanged
(
value
),
),
),
),
),
),
],
],
),
),
);
);
},
);
}
static
Future
<
void
>
_showTimeKeyboard
(
BuildContext
context
,
{
required
String
initialValue
,
required
DeviceControlEvent
Function
(
String
value
)
eventBuilder
,
})
async
{
final
result
=
await
NumericKeyboard
.
show
(
context
,
initialValue:
initialValue
==
'--'
?
''
:
initialValue
,
decimalEnabled:
false
,
);
if
(!
context
.
mounted
||
result
==
null
)
return
;
final
value
=
result
.
trim
();
if
(
value
.
isEmpty
)
return
;
context
.
read
<
DeviceControlBloc
>().
add
(
eventBuilder
(
value
));
}
}
class
_TimeValueButton
extends
StatelessWidget
{
const
_TimeValueButton
({
required
this
.
value
,
required
this
.
active
,
required
this
.
onTap
,
});
final
String
value
;
final
bool
active
;
final
VoidCallback
onTap
;
@override
Widget
build
(
BuildContext
context
)
{
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
DeviceControlDigitalValueBox
(
value:
value
,
unit:
'分钟'
,
active:
active
,
),
);
}
}
}
}
lib/views/monitoring/device_control/widgets/device_control_wind_panel.dart
View file @
d6fc2a2b
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.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/device_control_bo.dart'
;
import
'device_control_common.dart'
;
import
'device_control_common.dart'
;
...
@@ -9,6 +14,12 @@ class DeviceControlWindPanel extends StatelessWidget {
...
@@ -9,6 +14,12 @@ class DeviceControlWindPanel extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
BlocBuilder
<
DeviceControlBloc
,
DeviceControlState
>(
buildWhen:
(
previous
,
current
)
=>
previous
.
data
.
windSpeed
!=
current
.
data
.
windSpeed
,
builder:
(
context
,
state
)
{
final
currentMode
=
state
.
data
.
windSpeed
.
currentMode
;
return
DeviceControlPanelFrame
(
return
DeviceControlPanelFrame
(
backgroundAsset:
assetPanelTall
,
backgroundAsset:
assetPanelTall
,
padding:
EdgeInsets
.
fromLTRB
(
34
.
w
,
28
.
h
,
34
.
w
,
28
.
h
),
padding:
EdgeInsets
.
fromLTRB
(
34
.
w
,
28
.
h
,
34
.
w
,
28
.
h
),
...
@@ -17,26 +28,49 @@ class DeviceControlWindPanel extends StatelessWidget {
...
@@ -17,26 +28,49 @@ class DeviceControlWindPanel extends StatelessWidget {
children:
[
children:
[
const
DeviceControlPanelTitle
(
'风速调节'
),
const
DeviceControlPanelTitle
(
'风速调节'
),
SizedBox
(
height:
24
.
h
),
SizedBox
(
height:
24
.
h
),
const
Expanded
(
Expanded
(
child:
_WideModeButton
(
child:
_WideModeButton
(
label:
'睡眠模式'
,
label:
'睡眠模式'
,
icon:
Icons
.
nightlight_round
,
icon:
Icons
.
nightlight_round
,
active:
currentMode
==
WindSpeedMode
.
sleep
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
const
DeviceControlWindSpeedModeChanged
(
WindSpeedMode
.
sleep
,
),
),
),
),
),
),
SizedBox
(
height:
18
.
h
),
SizedBox
(
height:
18
.
h
),
const
Expanded
(
Expanded
(
child:
_WideModeButton
(
child:
_WideModeButton
(
label:
'舒适模式'
,
label:
'舒适模式'
,
icon:
Icons
.
favorite_border
,
icon:
Icons
.
favorite_border
,
active:
currentMode
==
WindSpeedMode
.
comfort
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
const
DeviceControlWindSpeedModeChanged
(
WindSpeedMode
.
comfort
,
),
),
),
),
),
),
SizedBox
(
height:
18
.
h
),
SizedBox
(
height:
18
.
h
),
const
Expanded
(
Expanded
(
child:
_WideModeButton
(
label:
'强劲模式'
,
icon:
Icons
.
air
),
child:
_WideModeButton
(
label:
'强劲模式'
,
icon:
Icons
.
air
,
active:
currentMode
==
WindSpeedMode
.
strong
,
onTap:
()
=>
context
.
read
<
DeviceControlBloc
>().
add
(
const
DeviceControlWindSpeedModeChanged
(
WindSpeedMode
.
strong
,
),
),
),
),
),
],
],
),
),
);
);
},
);
}
}
}
}
...
@@ -44,20 +78,31 @@ class _WideModeButton extends StatelessWidget {
...
@@ -44,20 +78,31 @@ class _WideModeButton extends StatelessWidget {
const
_WideModeButton
({
const
_WideModeButton
({
required
this
.
label
,
required
this
.
label
,
required
this
.
icon
,
required
this
.
icon
,
this
.
active
=
false
,
this
.
onTap
,
});
});
final
String
label
;
final
String
label
;
final
IconData
icon
;
final
IconData
icon
;
final
bool
active
;
final
VoidCallback
?
onTap
;
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
Container
(
final
color
=
active
?
const
Color
(
0xFF45F36F
)
:
const
Color
(
0xFF7DC4EA
);
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
Container
(
width:
double
.
infinity
,
width:
double
.
infinity
,
decoration:
deviceControlButtonDecoration
(
backgroundAsset:
assetButton
),
decoration:
deviceControlButtonDecoration
(
active:
active
,
backgroundAsset:
assetButton
,
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
),
child:
Row
(
child:
Row
(
children:
[
children:
[
Icon
(
icon
,
color:
const
Color
(
0xFF7DC4EA
)
,
size:
52
.
w
),
Icon
(
icon
,
color:
color
,
size:
52
.
w
),
SizedBox
(
width:
34
.
w
),
SizedBox
(
width:
34
.
w
),
Expanded
(
Expanded
(
child:
Text
(
child:
Text
(
...
@@ -65,7 +110,7 @@ class _WideModeButton extends StatelessWidget {
...
@@ -65,7 +110,7 @@ class _WideModeButton extends StatelessWidget {
maxLines:
1
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
style:
TextStyle
(
color:
Colors
.
white
,
color:
color
,
fontSize:
26
.
sp
,
fontSize:
26
.
sp
,
fontWeight:
FontWeight
.
w600
,
fontWeight:
FontWeight
.
w600
,
),
),
...
@@ -73,6 +118,7 @@ class _WideModeButton extends StatelessWidget {
...
@@ -73,6 +118,7 @@ class _WideModeButton extends StatelessWidget {
),
),
],
],
),
),
),
);
);
}
}
}
}
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
d6fc2a2b
...
@@ -36,6 +36,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -36,6 +36,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
StreamSubscription
<
BluetoothConnectionStatus
>?
_bluetoothStateSub
;
StreamSubscription
<
BluetoothConnectionStatus
>?
_bluetoothStateSub
;
StreamSubscription
<
String
>?
_bluetoothMessageSub
;
StreamSubscription
<
String
>?
_bluetoothMessageSub
;
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
StreamSubscription
<
BluetoothDataPacket
>?
_bluetoothDataSub
;
StreamSubscription
<
BluetoothReadInfoChangedEvent
>?
_bluetoothReadInfoSub
;
StreamSubscription
<
McuReportChangedEvent
>?
_mcuReportChangedSub
;
StreamSubscription
<
McuReportChangedEvent
>?
_mcuReportChangedSub
;
StreamSubscription
<
LakiMqttConnectionState
>?
_mqttStateSub
;
StreamSubscription
<
LakiMqttConnectionState
>?
_mqttStateSub
;
StreamSubscription
<
LakiMqttMessage
>?
_mqttMessageSub
;
StreamSubscription
<
LakiMqttMessage
>?
_mqttMessageSub
;
...
@@ -69,6 +70,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -69,6 +70,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_listenWebrtcState
();
_listenWebrtcState
();
_listenP2pState
();
_listenP2pState
();
_listenBluetoothState
();
_listenBluetoothState
();
_listenBluetoothReadInfo
();
_listenMcuReportChanged
();
_listenMcuReportChanged
();
_loadBoundBluetoothDevice
();
_loadBoundBluetoothDevice
();
loadData
();
loadData
();
...
@@ -178,13 +180,40 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -178,13 +180,40 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
void
_listenMcuReportChanged
()
{
void
_listenMcuReportChanged
()
{
_mcuReportChangedSub
=
eventBus
.
on
<
McuReportChangedEvent
>().
listen
((
event
)
{
_mcuReportChangedSub
=
eventBus
.
on
<
McuReportChangedEvent
>().
listen
((
event
)
{
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
final
metrics
=
_metricsFromBluetoothReport
(
event
.
report
);
Log
.
warn
(
'🔥🔥🔥 BLE_READ_DEBUG index_metrics_from_report '
'
${_metricsLogText(metrics)}
'
,
);
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
latestMcuReport:
event
.
report
,
latestMcuReport:
event
.
report
,
metrics:
_metricsFromBluetoothReport
(
event
.
report
)
,
metrics:
metrics
,
));
));
});
});
}
}
void
_listenBluetoothReadInfo
()
{
_bluetoothReadInfoSub
=
eventBus
.
on
<
BluetoothReadInfoChangedEvent
>().
listen
((
event
)
{
if
(
isClosed
)
return
;
final
metrics
=
_metricsFromBluetoothReadInfo
(
event
.
info
);
if
(
metrics
==
null
)
{
Log
.
warn
(
'🔥🔥🔥 BLE_READ_DEBUG index_read_info_no_metrics '
'sn=
${event.info.sn}
temp=
${event.info.mainTemperature}
'
'humidity=
${event.info.humidity}
o2=
${event.info.oxygenConcentration}
'
'co2=
${event.info.co2Measurement}
'
,
);
return
;
}
Log
.
warn
(
'🔥🔥🔥 BLE_READ_DEBUG index_metrics_from_read_info '
'
${_metricsLogText(metrics)}
'
,
);
emit
(
state
.
copyWith
(
metrics:
metrics
));
});
}
String
_appendBluetoothSideToSn
(
String
sn
)
{
String
_appendBluetoothSideToSn
(
String
sn
)
{
final
cleanSn
=
sn
.
trim
();
final
cleanSn
=
sn
.
trim
();
if
(
cleanSn
.
isEmpty
)
return
''
;
if
(
cleanSn
.
isEmpty
)
return
''
;
...
@@ -241,8 +270,8 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -241,8 +270,8 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
));
));
try
{
try
{
final
metrics
=
await
_monitoringService
.
getMetrics
();
//
final metrics = await _monitoringService.getMetrics();
if
(
isClosed
)
return
;
//
if (isClosed) return;
final
patientInfo
=
await
_monitoringService
.
getPatientInfo
();
final
patientInfo
=
await
_monitoringService
.
getPatientInfo
();
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
final
alerts
=
await
_monitoringService
.
getAlerts
();
final
alerts
=
await
_monitoringService
.
getAlerts
();
...
@@ -251,7 +280,6 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -251,7 +280,6 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
status:
MonitoringIndexStatus
.
success
,
status:
MonitoringIndexStatus
.
success
,
isLoading:
false
,
isLoading:
false
,
metrics:
metrics
,
patientInfo:
patientInfo
,
patientInfo:
patientInfo
,
alerts:
alerts
,
alerts:
alerts
,
error:
null
,
error:
null
,
...
@@ -430,8 +458,8 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -430,8 +458,8 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
Future
<
void
>
refreshData
()
async
{
Future
<
void
>
refreshData
()
async
{
try
{
try
{
final
metrics
=
await
_monitoringService
.
getMetrics
();
//
final metrics = await _monitoringService.getMetrics();
if
(
isClosed
)
return
;
//
if (isClosed) return;
final
patientInfo
=
await
_monitoringService
.
getPatientInfo
();
final
patientInfo
=
await
_monitoringService
.
getPatientInfo
();
if
(
isClosed
)
return
;
if
(
isClosed
)
return
;
final
alerts
=
await
_monitoringService
.
getAlerts
();
final
alerts
=
await
_monitoringService
.
getAlerts
();
...
@@ -439,7 +467,6 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -439,7 +467,6 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
emit
(
state
.
copyWith
(
emit
(
state
.
copyWith
(
status:
MonitoringIndexStatus
.
success
,
status:
MonitoringIndexStatus
.
success
,
metrics:
metrics
,
patientInfo:
patientInfo
,
patientInfo:
patientInfo
,
alerts:
alerts
,
alerts:
alerts
,
error:
null
,
error:
null
,
...
@@ -750,36 +777,98 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -750,36 +777,98 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_mqttClient
=
null
;
_mqttClient
=
null
;
}
}
List
<
MonitoringMetricBO
>
_metricsFromBluetoothReport
(
McuReport
report
)
{
List
<
MonitoringMetricBO
>
_metricsFromBluetoothReport
(
McuReport
report
,
{
List
<
MonitoringMetricBO
>?
baseMetrics
,
})
{
final
metrics
=
baseMetrics
??
state
.
metrics
;
final
replacements
=
<
String
,
MonitoringMetricBO
>{
final
replacements
=
<
String
,
MonitoringMetricBO
>{
'舱内温度'
:
_replaceMetricValue
(
'舱内温度'
:
_replaceMetricValue
(
'舱内温度'
,
'舱内温度'
,
_formatReportTemperature
(
report
.
mainTemperature
),
_formatReportTemperature
OrKeep
(
report
.
mainTemperature
,
'舱内温度'
),
fallbackUnit:
'℃'
,
fallbackUnit:
'℃'
,
baseMetrics:
metrics
,
),
),
'舱内湿度'
:
_replaceMetricValue
(
'舱内湿度'
:
_replaceMetricValue
(
'舱内湿度'
,
'舱内湿度'
,
_formatReportInt
(
report
.
humidity
),
_formatReportInt
OrKeep
(
report
.
humidity
,
'舱内湿度'
),
fallbackUnit:
'RH'
,
fallbackUnit:
'RH'
,
baseMetrics:
metrics
,
),
),
'氧气浓度'
:
_replaceMetricValue
(
'氧气浓度'
:
_replaceMetricValue
(
'氧气浓度'
,
'氧气浓度'
,
_formatReportInt
(
report
.
oxygenConcentration
),
_formatReportInt
OrKeep
(
report
.
oxygenConcentration
,
'氧气浓度'
),
fallbackUnit:
'%'
,
fallbackUnit:
'%'
,
baseMetrics:
metrics
,
),
),
'二氧化碳'
:
_replaceMetricValue
(
'二氧化碳'
:
_replaceMetricValue
(
'二氧化碳'
,
'二氧化碳'
,
_formatReportInt
(
report
.
co2Measurement
),
_formatReportInt
OrKeep
(
report
.
co2Measurement
,
'二氧化碳'
),
fallbackUnit:
'ppm'
,
fallbackUnit:
'ppm'
,
baseMetrics:
metrics
,
),
),
};
};
if
(
state
.
metrics
.
isEmpty
)
{
if
(
metrics
.
isEmpty
)
{
return
replacements
.
values
.
toList
();
return
replacements
.
values
.
toList
();
}
}
return
[
return
[
for
(
final
metric
in
state
.
metrics
)
replacements
[
metric
.
label
]
??
metric
,
for
(
final
metric
in
metrics
)
replacements
[
metric
.
label
]
??
metric
,
];
}
List
<
MonitoringMetricBO
>?
_metricsFromBluetoothReadInfo
(
BluetoothReadModel
info
,
)
{
final
hasMetric
=
info
.
mainTemperature
!=
null
||
info
.
humidity
!=
null
||
info
.
oxygenConcentration
!=
null
||
info
.
co2Measurement
!=
null
;
if
(!
hasMetric
)
return
null
;
final
metrics
=
state
.
metrics
;
final
replacements
=
<
String
,
MonitoringMetricBO
>{
'舱内温度'
:
_replaceMetricValue
(
'舱内温度'
,
info
.
mainTemperature
==
null
?
_existingMetricValue
(
'舱内温度'
)
??
'--'
:
_formatReportTemperature
(
info
.
mainTemperature
!),
fallbackUnit:
'℃'
,
baseMetrics:
metrics
,
),
'舱内湿度'
:
_replaceMetricValue
(
'舱内湿度'
,
info
.
humidity
==
null
?
_existingMetricValue
(
'舱内湿度'
)
??
'--'
:
'
${info.humidity}
'
,
fallbackUnit:
'RH'
,
baseMetrics:
metrics
,
),
'氧气浓度'
:
_replaceMetricValue
(
'氧气浓度'
,
info
.
oxygenConcentration
==
null
?
_existingMetricValue
(
'氧气浓度'
)
??
'--'
:
'
${info.oxygenConcentration}
'
,
fallbackUnit:
'%'
,
baseMetrics:
metrics
,
),
'二氧化碳'
:
_replaceMetricValue
(
'二氧化碳'
,
info
.
co2Measurement
==
null
?
_existingMetricValue
(
'二氧化碳'
)
??
'--'
:
'
${info.co2Measurement}
'
,
fallbackUnit:
'ppm'
,
baseMetrics:
metrics
,
),
};
if
(
metrics
.
isEmpty
)
{
return
replacements
.
values
.
toList
();
}
return
[
for
(
final
metric
in
metrics
)
replacements
[
metric
.
label
]
??
metric
,
];
];
}
}
...
@@ -787,9 +876,11 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -787,9 +876,11 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
String
label
,
String
label
,
String
value
,
{
String
value
,
{
required
String
fallbackUnit
,
required
String
fallbackUnit
,
List
<
MonitoringMetricBO
>?
baseMetrics
,
})
{
})
{
final
metrics
=
baseMetrics
??
state
.
metrics
;
MonitoringMetricBO
?
existing
;
MonitoringMetricBO
?
existing
;
for
(
final
metric
in
state
.
metrics
)
{
for
(
final
metric
in
metrics
)
{
if
(
metric
.
label
==
label
)
{
if
(
metric
.
label
==
label
)
{
existing
=
metric
;
existing
=
metric
;
break
;
break
;
...
@@ -817,8 +908,29 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -817,8 +908,29 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
return
fixed
.
endsWith
(
'.0'
)
?
fixed
.
substring
(
0
,
fixed
.
length
-
2
)
:
fixed
;
return
fixed
.
endsWith
(
'.0'
)
?
fixed
.
substring
(
0
,
fixed
.
length
-
2
)
:
fixed
;
}
}
String
_formatReportInt
(
int
value
)
{
String
_formatReportTemperatureOrKeep
(
double
value
,
String
label
)
{
return
value
<
0
?
'--'
:
'
$value
'
;
if
(!
value
.
isNaN
)
return
_formatReportTemperature
(
value
);
return
_existingMetricValue
(
label
)
??
'--'
;
}
String
_formatReportIntOrKeep
(
int
value
,
String
label
)
{
if
(
value
>=
0
)
return
'
$value
'
;
return
_existingMetricValue
(
label
)
??
'--'
;
}
String
?
_existingMetricValue
(
String
label
)
{
for
(
final
metric
in
state
.
metrics
)
{
if
(
metric
.
label
==
label
&&
metric
.
value
!=
'--'
)
{
return
metric
.
value
;
}
}
return
null
;
}
String
_metricsLogText
(
List
<
MonitoringMetricBO
>
metrics
)
{
return
metrics
.
map
((
item
)
=>
'
${item.label}
=
${item.value}${item.unit}
'
)
.
join
(
' '
);
}
}
/// 清空宠物信息(出舱)
/// 清空宠物信息(出舱)
...
@@ -907,6 +1019,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
...
@@ -907,6 +1019,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_bluetoothStateSub
?.
cancel
();
_bluetoothStateSub
?.
cancel
();
_bluetoothMessageSub
?.
cancel
();
_bluetoothMessageSub
?.
cancel
();
_bluetoothDataSub
?.
cancel
();
_bluetoothDataSub
?.
cancel
();
_bluetoothReadInfoSub
?.
cancel
();
_mcuReportChangedSub
?.
cancel
();
_mcuReportChangedSub
?.
cancel
();
_webrtcService
.
dispose
();
_webrtcService
.
dispose
();
_p2pVideoService
.
dispose
();
_p2pVideoService
.
dispose
();
...
...
lib/views/monitoring/index/monitoring_index_view.dart
View file @
d6fc2a2b
...
@@ -120,7 +120,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
...
@@ -120,7 +120,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
children:
[
children:
[
Expanded
(
Expanded
(
flex:
32
,
flex:
32
,
child:
state
.
metrics
.
isEmpty
?
_buildMetricsEmpty
()
:
_buildMetrics
(
state
.
metrics
),
child:
_buildMetrics
(
state
.
metrics
),
),
),
SizedBox
(
height:
24
.
h
),
SizedBox
(
height:
24
.
h
),
Expanded
(
Expanded
(
...
@@ -157,6 +157,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
...
@@ -157,6 +157,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
);
);
}
}
// ignore: unused_element
Widget
_buildMetricsEmpty
()
{
Widget
_buildMetricsEmpty
()
{
return
Container
(
return
Container
(
width:
double
.
infinity
,
width:
double
.
infinity
,
...
...
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