blood_pressure.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:vitalapp/managers/device_controller_manager.dart';
  4. import 'package:vitalapp/managers/interfaces/organization.dart';
  5. import 'package:vitalapp/pages/medical/widgets/device_status_position.dart';
  6. import 'package:vitalapp/pages/medical/widgets/exam_card.dart';
  7. import 'package:vnote_device_plugin/consts/types.dart';
  8. import 'package:vnote_device_plugin/devices/nibp.dart';
  9. import 'package:vnote_device_plugin/models/exams/nibp.dart';
  10. import 'package:fis_common/logger/logger.dart';
  11. import 'package:vitalapp/managers/interfaces/models/device.dart';
  12. import 'package:vitalapp/pages/medical/controller.dart';
  13. import 'package:vitalapp/pages/medical/models/item.dart';
  14. import 'package:vitalapp/pages/medical/models/worker.dart';
  15. import 'package:vitalapp/pages/medical/widgets/device_status.dart';
  16. // ignore: must_be_immutable
  17. class BloodPressure extends StatefulWidget {
  18. const BloodPressure({
  19. super.key,
  20. });
  21. @override
  22. State<BloodPressure> createState() => _BloodPressureState();
  23. }
  24. class _BloodPressureState extends State<BloodPressure> {
  25. var controller = Get.find<MedicalController>();
  26. PressureDeviceStatus pressureDeviceStatus = PressureDeviceStatus.start;
  27. bool isConnectFail = false;
  28. DeviceControllerManager? nibp;
  29. NibpDeviceWorker? worker;
  30. int liveValue = 0;
  31. int errorCount = 0;
  32. WorkerStatus _connectStatus = WorkerStatus.connecting;
  33. @override
  34. void initState() {
  35. WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
  36. currentDevice();
  37. });
  38. super.initState();
  39. }
  40. @override
  41. void didUpdateWidget(BloodPressure oldWidget) {
  42. super.didUpdateWidget(oldWidget);
  43. }
  44. Future<void> currentDevice() async {
  45. DeviceModel? device = await controller.getDevice(DeviceTypes.NIBP);
  46. if (device == null) {
  47. _connectStatus = WorkerStatus.unboundDevice;
  48. worker = null;
  49. setState(() {});
  50. return;
  51. }
  52. nibp = DeviceControllerManager(DeviceTypes.NIBP, device.model, device.mac);
  53. worker = nibp!.worker as NibpDeviceWorker;
  54. _connectStatus = nibp!.connectStatus;
  55. loadListeners();
  56. connect();
  57. }
  58. void loadListeners() {
  59. worker!.liveUpdateEvent.addListener(_onLiveUpdate);
  60. worker!.resultUpdateEvent.addListener(_onSuccess);
  61. worker!.connectErrorEvent.addListener(_onConnectFail);
  62. worker!.connectedEvent.addListener(_onConnectSuccess);
  63. worker!.disconnectedEvent.addListener(_onDisconnected);
  64. }
  65. void releaseListeners() {
  66. if (worker != null) {
  67. worker!.connectErrorEvent.removeListener(_onConnectFail);
  68. worker!.connectedEvent.removeListener(_onConnectSuccess);
  69. worker!.liveUpdateEvent.removeListener(_onLiveUpdate);
  70. worker!.resultUpdateEvent.removeListener(_onSuccess);
  71. worker!.disconnectedEvent.removeListener(_onDisconnected);
  72. }
  73. }
  74. Future<void> connect() async {
  75. try {
  76. if (worker != null) {
  77. await worker!.connect();
  78. }
  79. } catch (err) {
  80. print(err);
  81. }
  82. }
  83. Future<void> disconnect() async {
  84. try {
  85. if (worker != null) {
  86. await worker!.disconnect();
  87. }
  88. } catch (err) {
  89. print(err);
  90. }
  91. }
  92. /// 尝试重连
  93. Future<void> tryReconnect() async {
  94. if (worker != null) {
  95. await disconnect();
  96. await connect();
  97. }
  98. }
  99. @override
  100. void dispose() {
  101. nibp?.dispose();
  102. nibp = null;
  103. releaseListeners();
  104. disconnect();
  105. worker?.dispose();
  106. super.dispose();
  107. }
  108. void _onLiveUpdate(_, int e) {
  109. if (controller.diagnosisDataValue['NIBP'] != null) {
  110. controller.diagnosisDataValue['NIBP'] = null;
  111. }
  112. setState(() {
  113. liveValue = e;
  114. });
  115. }
  116. /// TODO 需求不清,检测的数据需要传给体检,但是检测又不区分左右侧血压
  117. void _onSuccess(_, NibpExamValue e) async {
  118. logger.i(
  119. '检测完成,高压:${e.systolicPressure},低压:${e.diastolicPressure},脉率:${e.pulse}');
  120. int sbp = e.systolicPressure;
  121. int dbp = e.diastolicPressure;
  122. final orgManager = Get.find<IOrganizationManager>();
  123. final paramSbp = await orgManager.getDynamicParamByKey("Sbp");
  124. if (paramSbp != null) {
  125. sbp = nibp!.handleExamValueSpecially(sbp, paramSbp);
  126. logger.i('NIBP Value handle specially - Sbp: $sbp.');
  127. }
  128. final paramDbp = await orgManager.getDynamicParamByKey("Sbp");
  129. if (paramDbp != null) {
  130. dbp = nibp!.handleExamValueSpecially(dbp, paramDbp);
  131. logger.i('NIBP Value handle specially - Dbp:$dbp.');
  132. }
  133. setState(() {
  134. pressureDeviceStatus = PressureDeviceStatus.end;
  135. /// 这是第三方需要的数据
  136. controller.diagnosisDataValue['NIBP'] = {
  137. 'Sbp': sbp.toString(),
  138. 'Dbp': dbp.toString(),
  139. 'Pulse_Beat': e.pulse.toString(),
  140. };
  141. controller.saveCachedRecord();
  142. });
  143. }
  144. void _onConnectFail(sender, e) {
  145. logger.i("连接设备失败:${worker!.mac}");
  146. print('连接设备失败:${worker!.mac},errorCount:$errorCount,${DateTime.now()}');
  147. if (errorCount < 3) {
  148. print('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}');
  149. logger.i('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}');
  150. errorCount++;
  151. tryReconnect();
  152. } else {
  153. print('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}');
  154. logger.i('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}');
  155. isConnectFail = true;
  156. }
  157. _connectStatus = WorkerStatus.connectionFailed;
  158. setState(() {});
  159. }
  160. void _onDisconnected(sender, e) {
  161. logger.i("设备连接中断:${worker!.mac}");
  162. print('设备连接中断:${worker!.mac}');
  163. tryReconnect();
  164. errorCount = 0;
  165. _connectStatus = WorkerStatus.disconnected;
  166. setState(() {});
  167. }
  168. void _onConnectSuccess(sender, e) {
  169. logger.i("设备连接成功:$e");
  170. print("设备连接成功:$e");
  171. setState(() {
  172. _connectStatus = WorkerStatus.connected;
  173. errorCount = 0;
  174. isConnectFail = false;
  175. });
  176. }
  177. Widget _buildValue() {
  178. if (controller.diagnosisDataValue['NIBP'] == null) {
  179. return _buildLiveWidget();
  180. }
  181. //收缩压(高压)
  182. var hightValue =
  183. controller.diagnosisDataValue['NIBP']?['Sbp']?.toString() ?? '';
  184. //舒张压(低压)
  185. var lowValue =
  186. controller.diagnosisDataValue['NIBP']?['Dbp']?.toString() ?? '';
  187. if (hightValue.isNotEmpty || lowValue.isNotEmpty) {
  188. return _buildResultWidget();
  189. }
  190. return _buildLiveWidget();
  191. }
  192. @override
  193. Widget build(BuildContext context) {
  194. return Stack(
  195. children: [
  196. ExamCard(
  197. title: '',
  198. content: Column(
  199. children: [
  200. Row(
  201. children: [
  202. const SizedBox(
  203. width: 25,
  204. ),
  205. const Text(
  206. '血压',
  207. style: TextStyle(fontSize: 25),
  208. ),
  209. const Expanded(child: SizedBox()),
  210. InkWell(
  211. child: _SideBar(
  212. value: _buildValue(),
  213. unit: 'mmHg',
  214. ),
  215. ),
  216. ],
  217. ),
  218. Row(
  219. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  220. children: [
  221. const SizedBox(
  222. width: 25,
  223. ),
  224. const Text(
  225. '脉率',
  226. style: TextStyle(fontSize: 25),
  227. ),
  228. const Expanded(child: SizedBox()),
  229. Text(
  230. controller.diagnosisDataValue['NIBP']?['Pulse_Beat']
  231. .toString() ??
  232. '',
  233. style: const TextStyle(
  234. fontSize: 45,
  235. color: Colors.black,
  236. ),
  237. ),
  238. const Text(
  239. ' bpm',
  240. style: TextStyle(fontSize: 25),
  241. ),
  242. const SizedBox(
  243. width: 30,
  244. ),
  245. ],
  246. ),
  247. ],
  248. ),
  249. ),
  250. if (isConnectFail) ...[
  251. _buildErrorButton(),
  252. ] else ...[
  253. Positioned(
  254. right: 10,
  255. top: 10,
  256. child: DeviceStatus(connectStatus: _connectStatus),
  257. ),
  258. ],
  259. ],
  260. );
  261. }
  262. /// 需要封装一下
  263. Widget _buildErrorButton() {
  264. return DeviceStatusPosition(
  265. deviceStatus: Row(
  266. children: [
  267. const Text(
  268. '请确认设备是否启动',
  269. style: TextStyle(fontSize: 24, color: Colors.red),
  270. ),
  271. IconButton(
  272. onPressed: () {
  273. controller.diagnosisDataValue['NIBP'] = null;
  274. liveValue = 0;
  275. tryReconnect();
  276. setState(() {
  277. _connectStatus = WorkerStatus.connecting;
  278. isConnectFail = false;
  279. });
  280. },
  281. icon: const Icon(Icons.refresh),
  282. iconSize: 32,
  283. ),
  284. ],
  285. ),
  286. );
  287. }
  288. Widget _buildLiveWidget() {
  289. return SizedBox(
  290. height: 130,
  291. child: Row(
  292. crossAxisAlignment: CrossAxisAlignment.center,
  293. children: [
  294. Container(
  295. alignment: Alignment.center,
  296. child: Text(
  297. liveValue.toString() == '0' ? '--' : liveValue.toString(),
  298. style: const TextStyle(
  299. fontSize: 60,
  300. color: Colors.black,
  301. ),
  302. ),
  303. ),
  304. const Text(
  305. ' mmHg',
  306. style: TextStyle(fontSize: 25),
  307. ),
  308. const SizedBox(
  309. width: 30,
  310. )
  311. ],
  312. ),
  313. );
  314. }
  315. Widget _buildResultWidget() {
  316. const textStyle = TextStyle(
  317. fontSize: 45,
  318. color: Colors.black,
  319. );
  320. var normalStyle = const TextStyle(
  321. fontSize: 25,
  322. color: Colors.black,
  323. );
  324. var uuitText = const Text(
  325. ' mmHg',
  326. style: TextStyle(
  327. fontSize: 25,
  328. color: Colors.black,
  329. ),
  330. );
  331. return Stack(
  332. children: [
  333. Column(
  334. mainAxisAlignment: MainAxisAlignment.center,
  335. mainAxisSize: MainAxisSize.min,
  336. crossAxisAlignment: CrossAxisAlignment.end,
  337. children: [
  338. Row(
  339. children: [
  340. Text(
  341. '收缩压',
  342. style: normalStyle,
  343. ),
  344. Container(
  345. alignment: Alignment.centerRight,
  346. width: 200,
  347. child: Text(
  348. controller.diagnosisDataValue['NIBP']['Sbp'],
  349. style: textStyle,
  350. ),
  351. ),
  352. uuitText,
  353. const SizedBox(
  354. width: 30,
  355. ),
  356. ],
  357. ),
  358. Row(
  359. children: [
  360. Text(
  361. '舒张压',
  362. style: normalStyle,
  363. ),
  364. Container(
  365. alignment: Alignment.centerRight,
  366. width: 200,
  367. child: Text(
  368. controller.diagnosisDataValue['NIBP']['Dbp'],
  369. style: textStyle,
  370. ),
  371. ),
  372. uuitText,
  373. const SizedBox(
  374. width: 30,
  375. ),
  376. ],
  377. ),
  378. ],
  379. ),
  380. ],
  381. );
  382. }
  383. }
  384. class _SideBar extends StatelessWidget {
  385. final Widget value;
  386. final String unit;
  387. const _SideBar({
  388. required this.value,
  389. required this.unit,
  390. });
  391. @override
  392. Widget build(BuildContext context) {
  393. return Row(
  394. mainAxisAlignment: MainAxisAlignment.end,
  395. crossAxisAlignment: CrossAxisAlignment.start,
  396. children: [
  397. Container(
  398. alignment: Alignment.bottomRight,
  399. child: FittedBox(
  400. child: Row(
  401. mainAxisAlignment: MainAxisAlignment.end,
  402. crossAxisAlignment: CrossAxisAlignment.end,
  403. children: [
  404. value,
  405. ],
  406. ),
  407. ),
  408. ),
  409. ],
  410. );
  411. }
  412. }