import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:vitalapp/architecture/app_parameters.dart'; import 'package:vitalapp/managers/device_controller_manager.dart'; import 'package:vitalapp/managers/interfaces/organization.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_blood_pressure.dart'; import 'package:vitalapp/pages/medical/widgets/device_status_position.dart'; import 'package:vitalapp/pages/medical/widgets/exam_card.dart'; import 'package:vnote_device_plugin/consts/types.dart'; import 'package:vnote_device_plugin/devices/nibp.dart'; import 'package:vnote_device_plugin/models/exams/nibp.dart'; import 'package:fis_common/logger/logger.dart'; import 'package:vitalapp/managers/interfaces/models/device.dart'; import 'package:vitalapp/pages/medical/controller.dart'; import 'package:vitalapp/pages/medical/models/item.dart'; import 'package:vitalapp/pages/medical/models/worker.dart'; import 'package:vitalapp/pages/medical/widgets/device_status.dart'; // ignore: must_be_immutable class BloodPressure extends StatefulWidget { const BloodPressure({ super.key, }); @override State createState() => _BloodPressureState(); } class _BloodPressureState extends State { var controller = Get.find(); bool get isPureSoftwareMode => AppParameters.data.isPureSoftwareMode; PressureDeviceStatus pressureDeviceStatus = PressureDeviceStatus.start; bool isConnectFail = false; DeviceControllerManager? nibp; NibpDeviceWorker? worker; int liveValue = 0; int errorCount = 0; bool isShowError = false; String errorMessage = ''; WorkerStatus _connectStatus = WorkerStatus.connecting; @override void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { currentDevice(); }); super.initState(); } @override void didUpdateWidget(BloodPressure oldWidget) { super.didUpdateWidget(oldWidget); } Future currentDevice() async { DeviceModel? device = await controller.getDevice(DeviceTypes.NIBP); if (device == null) { _connectStatus = WorkerStatus.unboundDevice; worker = null; setState(() {}); return; } nibp = DeviceControllerManager(DeviceTypes.NIBP, device.model, device.mac); worker = nibp!.worker as NibpDeviceWorker; _connectStatus = nibp!.connectStatus; loadListeners(); connect(); } void loadListeners() { worker!.liveUpdateEvent.addListener(_onLiveUpdate); worker!.errorEvent.addListener(_onError); worker!.resultUpdateEvent.addListener(_onSuccess); worker!.connectErrorEvent.addListener(_onConnectFail); worker!.connectedEvent.addListener(_onConnectSuccess); worker!.disconnectedEvent.addListener(_onDisconnected); } void releaseListeners() { if (worker != null) { worker!.connectErrorEvent.removeListener(_onConnectFail); worker!.connectedEvent.removeListener(_onConnectSuccess); worker!.liveUpdateEvent.removeListener(_onLiveUpdate); worker!.errorEvent.removeListener(_onError); worker!.resultUpdateEvent.removeListener(_onSuccess); worker!.disconnectedEvent.removeListener(_onDisconnected); } } Future connect() async { try { if (worker != null) { await worker!.connect(); } } catch (err) { print(err); } } Future disconnect() async { try { if (worker != null) { await worker!.disconnect(); } } catch (err) { print(err); } } /// 尝试重连 Future tryReconnect() async { if (worker != null) { await disconnect(); await connect(); } } @override void dispose() { nibp?.dispose(); nibp = null; releaseListeners(); disconnect(); worker?.dispose(); super.dispose(); } void _onLiveUpdate(_, int e) { if (controller.diagnosisDataValue['NIBP'] != null) { controller.diagnosisDataValue['NIBP'] = null; } setState(() { liveValue = e; isShowError = false; }); } /// 检测报错 void _onError(_, String errorMessage) async { logger.i('errorMessage:$errorMessage ${DateTime.now().toString()}'); final message = errorMessage.replaceAll("1506|Error|", ""); setState(() { isShowError = true; this.errorMessage = message; liveValue = 0; pressureDeviceStatus = PressureDeviceStatus.end; }); } /// TODO 需求不清,检测的数据需要传给体检,但是检测又不区分左右侧血压 void _onSuccess(_, NibpExamValue e) async { logger.i( '检测完成,高压:${e.systolicPressure},低压:${e.diastolicPressure},脉率:${e.pulse}'); int sbp = e.systolicPressure; int dbp = e.diastolicPressure; final orgManager = Get.find(); final paramSbp = await orgManager.getDynamicParamByKey("Sbp"); if (paramSbp != null) { sbp = nibp!.handleExamValueSpecially(sbp, paramSbp); logger.i('NIBP Value handle specially - Sbp: $sbp.'); } final paramDbp = await orgManager.getDynamicParamByKey("Sbp"); if (paramDbp != null) { dbp = nibp!.handleExamValueSpecially(dbp, paramDbp); logger.i('NIBP Value handle specially - Dbp:$dbp.'); } setState(() { pressureDeviceStatus = PressureDeviceStatus.end; /// 这是第三方需要的数据 controller.diagnosisDataValue['NIBP'] = { 'Sbp': sbp.toString(), 'Dbp': dbp.toString(), 'Pulse_Beat': e.pulse.toString(), }; controller.saveCachedRecord(); }); } void _onConnectFail(sender, e) { logger.i("连接设备失败:${worker!.mac}"); print('连接设备失败:${worker!.mac},errorCount:$errorCount,${DateTime.now()}'); if (errorCount < 3) { print('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}'); logger.i('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}'); errorCount++; tryReconnect(); } else { print('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}'); logger.i('连接设备失败:${worker!.mac},$errorCount,${DateTime.now()}'); isConnectFail = true; } _connectStatus = WorkerStatus.connectionFailed; setState(() {}); } void _onDisconnected(sender, e) { logger.i("设备连接中断:${worker!.mac}"); print('设备连接中断:${worker!.mac}'); tryReconnect(); errorCount = 0; _connectStatus = WorkerStatus.disconnected; setState(() {}); } void _onConnectSuccess(sender, e) { logger.i("设备连接成功:$e"); print("设备连接成功:$e"); setState(() { _connectStatus = WorkerStatus.connected; errorCount = 0; isConnectFail = false; isShowError = false; }); } Widget _buildValue() { if (controller.diagnosisDataValue['NIBP'] == null) { return _buildLiveWidget(); } //收缩压(高压) var hightValue = controller.diagnosisDataValue['NIBP']?['Sbp']?.toString() ?? ''; //舒张压(低压) var lowValue = controller.diagnosisDataValue['NIBP']?['Dbp']?.toString() ?? ''; if (hightValue.isNotEmpty || lowValue.isNotEmpty) { return _buildResultWidget(); } return _buildLiveWidget(); } @override Widget build(BuildContext context) { return Stack( children: [ ExamCard( title: '', clickCard: _onClickBloodPressure, content: Column( children: [ Row( children: [ const SizedBox( width: 25, ), const Text( '血压', style: TextStyle(fontSize: 25), ), const Expanded(child: SizedBox()), InkWell( child: _SideBar( value: _buildValue(), unit: 'mmHg', ), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox( width: 25, ), const Text( '脉率', style: TextStyle(fontSize: 25), ), const Expanded(child: SizedBox()), Text( controller.diagnosisDataValue['NIBP']?['Pulse_Beat'] .toString() ?? '', style: const TextStyle( fontSize: 45, color: Colors.black, ), ), const Text( ' bpm', style: TextStyle(fontSize: 25), ), const SizedBox( width: 30, ), ], ), Container( height: 30, margin: const EdgeInsets.only(bottom: 10), child: isShowError ? Row( children: [ const SizedBox( width: 25, ), Text( '检测失败:', style: TextStyle( fontSize: 20, ), ), Text( '${errorMessage}', style: TextStyle(fontSize: 20, color: Colors.grey), ), const Expanded(child: SizedBox()), ], ) : SizedBox(), ), ], ), ), if (!isPureSoftwareMode) ...[ if (isConnectFail) ...[ _buildErrorButton(), ] else ...[ Positioned( right: 10, top: 10, child: DeviceStatus(connectStatus: _connectStatus), ), ], ], ], ); } /// 需要封装一下 Widget _buildErrorButton() { return DeviceStatusPosition( deviceStatus: Row( children: [ const Text( '请确认设备是否启动', style: TextStyle(fontSize: 24, color: Colors.red), ), IconButton( onPressed: () { controller.diagnosisDataValue['NIBP'] = null; liveValue = 0; tryReconnect(); setState(() { _connectStatus = WorkerStatus.connecting; isConnectFail = false; }); }, icon: const Icon(Icons.refresh), iconSize: 32, ), ], ), ); } Widget _buildLiveWidget() { return SizedBox( height: 130, width: 180, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( alignment: Alignment.center, child: Text( liveValue.toString() == '0' ? '--' : liveValue.toString(), style: const TextStyle( fontSize: 60, color: Colors.black, ), ), ), const Text( ' mmHg', style: TextStyle(fontSize: 25), ), const SizedBox( width: 30, ) ], ), ); } Widget _buildResultWidget() { const textStyle = TextStyle( fontSize: 45, color: Colors.black, ); var normalStyle = const TextStyle( fontSize: 25, color: Colors.black, ); var uuitText = const Text( ' mmHg', style: TextStyle( fontSize: 25, color: Colors.black, ), ); return Stack( children: [ Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( children: [ Text( '收缩压', style: normalStyle, ), Container( alignment: Alignment.centerRight, width: 200, child: Text( controller.diagnosisDataValue['NIBP']['Sbp'], style: textStyle, ), ), uuitText, const SizedBox( width: 30, ), ], ), Row( children: [ Text( '舒张压', style: normalStyle, ), Container( alignment: Alignment.centerRight, width: 200, child: Text( controller.diagnosisDataValue['NIBP']['Dbp'], style: textStyle, ), ), uuitText, const SizedBox( width: 30, ), ], ), ], ), ], ); } Future _onClickBloodPressure() async { String sbp = ""; String dbp = ""; String heart = ""; if (controller.diagnosisDataValue.containsKey("NIBP")) { var nibp = controller.diagnosisDataValue["NIBP"]; if (nibp is Map) { if (nibp.containsKey("Sbp")) { sbp = nibp["Sbp"]; } if (nibp.containsKey("Dbp")) { dbp = nibp["Dbp"]; } if (nibp.containsKey("Pulse_Beat")) { heart = nibp["Pulse_Beat"]; } } } String? result = await VDialogBloodPressure( title: '血压', initialValue: [ sbp, dbp, ], heartRate: heart, isDisplayHeartRate: true, ).show(); if (result != null) { try { String sbp = jsonDecode(result).first; String dbp = jsonDecode(result)[1]; String heart = jsonDecode(result)[2]; if (sbp.isNotEmpty && dbp.isNotEmpty) { //收缩压(高压) controller.diagnosisDataValue['NIBP'] = { 'Sbp': sbp, 'Dbp': dbp, 'Pulse_Beat': heart, }; setState(() {}); } } catch (e) { logger.e("BloodPressure _onClickBloodPressure ex:", e); } } } } class _SideBar extends StatelessWidget { final Widget value; final String unit; const _SideBar({ required this.value, required this.unit, }); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( alignment: Alignment.bottomRight, child: FittedBox( child: Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ value, ], ), ), ), ], ); } }