import 'dart:async'; import 'dart:convert'; import 'package:fis_common/logger/logger.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:vitalapp/managers/interfaces/data_convert.dart'; import 'package:vitalapp/managers/interfaces/models/device.dart'; import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_card.dart'; import 'package:vitalapp/pages/medical/controller.dart'; import 'package:vitalapp/pages/medical/controllers/heart.dart'; import 'package:vitalapp/pages/medical/models/worker.dart'; import 'package:vitalapp/pages/medical/widgets/device_status.dart'; import 'package:vitalapp/pages/medical/widgets/device_status_position.dart'; import 'package:vitalapp/pages/medical/widgets/ecg_view/index.dart'; import 'package:vitalapp/pages/medical/widgets/side_bar.dart'; import 'package:vnote_device_plugin/consts/types.dart'; import 'package:vnote_device_plugin/devices/heart.dart'; import 'package:vnote_device_plugin/models/exams/heart.dart'; class HeartRate extends StatefulWidget { const HeartRate({ super.key, }); @override State createState() => _HeartRateState(); } class _HeartRateState extends State { final MedicalController medicalController = Get.find(); final IDataConvertManager dataConvertManager = Get.find(); late HeartDeviceController heartDeviceController; late HeartDeviceWorker worker; WorkerStatus connectStatus = WorkerStatus.connecting; List ecgPoint = []; int errorCount = 0; bool isConnectFail = false; late String _heart = medicalController.diagnosisDataValue['Heart']?['HEART']?.toString() ?? ''; late String _assess = medicalController.diagnosisDataValue['Heart']?['ASSESS'] ?? ''; late final heartRate = medicalController.diagnosisDataValue['Heart']?['HEART'] ?? 0; /// 是否显示重试按钮 bool showResetButton = false; /// 初始时的心电初始数据 List? initEcgData; String _deviceError = ''; @override Widget build(BuildContext context) { return Stack( children: [ ExamCard( titleText: const SizedBox(), bottomPadding: 10, content: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const SideBar( title: '心率', value: '', unit: '', ), const Expanded(child: SizedBox()), Text( _heart.isEmpty ? '--' : _heart, style: const TextStyle( fontSize: 60, color: Colors.black, ), ), const Text( " bpm", style: TextStyle(fontSize: 25), ), const SizedBox( width: 15, ) ], ), if (_deviceError.isNotEmpty) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 30, ), child: const Text( '心率测量出错', style: TextStyle( fontSize: 25, ), ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 50, ), child: Text( _deviceError, style: const TextStyle( fontSize: 24, ), ), ), ], ), if (_assess.isNotEmpty) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 30, ), child: const Text( '心率评估', style: TextStyle( fontSize: 25, ), ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 50, ), child: Text( dataConvertManager.heartRateConversion( int.parse(heartRate.toString())), style: const TextStyle( fontSize: 24, ), ), ), ], ), const SizedBox( height: 10, ), SizedBox( height: 240, child: LayoutBuilder(builder: (context, constraints) { if (initEcgData == null) { return Container(); } else { return EcgView( width: constraints.maxWidth, height: constraints.maxHeight, initData: initEcgData!, ); } }), ), if (showResetButton) _buildResetButton() else // _buildDebugButton() const SizedBox( height: 48, ), ], ), ), if (!isConnectFail) DeviceStatusPosition( deviceStatus: DeviceStatus(connectStatus: connectStatus), ) else _buildErrorButton(), ], ); } Widget _buildDebugButton() { return Container( child: Row( children: [ const Text( '测试============?', style: TextStyle(color: Colors.grey, fontSize: 24), ), TextButton( onPressed: () { debugTest(); }, child: const Text( '重试', style: TextStyle(color: Colors.blue, fontSize: 24), ), ), ], ), ); } Widget _buildResetButton() { return Container( child: Row( children: [ const Text( '测量数据不完整,是否需要重试?', style: TextStyle(color: Colors.grey, fontSize: 24), ), TextButton( onPressed: () { ecgPoint = []; resetEcgView(); setEcgData(); setState(() { showResetButton = false; }); }, child: const Text( '重试', style: TextStyle(color: Colors.blue, fontSize: 24), ), ), ], ), ); } // 设备初始化 Future initDevice() async { DeviceModel? device = await medicalController.getDevice(DeviceTypes.HEART); if (device == null) { connectStatus = WorkerStatus.unboundDevice; setState(() {}); return; } heartDeviceController = HeartDeviceController(device.model, device.mac); worker = heartDeviceController.worker; connectStatus = heartDeviceController.connectStatus; loadListeners(); worker.connect(); } /// 尝试重连 Future tryReconnect() async { await worker.disconnect(); await worker.connect(); } @override void dispose() async { super.dispose(); releaseListeners(); await worker.disconnect(); } /// 重置数据以及心电图 void resetEcgView() { try { EcgViewController ecgViewController = Get.find(); ecgPoint = []; ecgViewController.reset(); } catch (e) { logger.i('心率图测量失败:$e'); } } /// 数据初始化 Future initData() async { await medicalController.readCachedRecord(); if (medicalController.diagnosisDataValue['Heart'] == null) { medicalController.diagnosisDataValue['Heart'] = {}; } if (medicalController.diagnosisDataValue['Heart']?['ECG_POINT'] != null) { initEcgData = jsonDecode( medicalController.diagnosisDataValue['Heart']?['ECG_POINT']) .cast(); if (initEcgData != null && initEcgData!.isNotEmpty && initEcgData!.length < 125 * 30) { print("数据不完整 ⭐⭐⭐⭐⭐⭐⭐"); showResetButton = true; } } else { initEcgData = []; } setState(() {}); } Future initHeart() async { await initDevice(); await initData(); } @override void initState() { initHeart(); super.initState(); } void loadListeners() { worker.connectErrorEvent.addListener(_onConnectFail); worker.connectedEvent.addListener(_onConnectSuccess); worker.disconnectedEvent.addListener(_onDisconnected); worker.hrValueUpdateEvent.addListener(_onHrValueUpdate); worker.ecgValueUpdateEvent.addListener(_onEcgValueUpdate); worker.resultReceivedEvent.addListener(_onRCesultReceived); worker.errorEvent.addListener(_onError); } void releaseListeners() { worker.connectErrorEvent.removeListener(_onConnectFail); worker.connectedEvent.removeListener(_onConnectSuccess); worker.disconnectedEvent.removeListener(_onDisconnected); worker.hrValueUpdateEvent.removeListener(_onHrValueUpdate); worker.ecgValueUpdateEvent.removeListener(_onEcgValueUpdate); worker.resultReceivedEvent.removeListener(_onRCesultReceived); worker.errorEvent.removeListener(_onError); } /// 需要封装一下 Widget _buildErrorButton() { return DeviceStatusPosition( deviceStatus: Row( children: [ const Text( '请确认设备是否启动', style: TextStyle(fontSize: 24, color: Colors.red), ), IconButton( onPressed: () { worker.connect(); setState(() { connectStatus = WorkerStatus.connecting; isConnectFail = false; }); }, icon: const Icon(Icons.refresh), iconSize: 32, ), ], ), ); } void _onConnectFail(sender, e) async { logger.i("设备连接失败:${worker.mac}"); if (errorCount < 3) { errorCount++; await tryReconnect(); } else { isConnectFail = true; } connectStatus = WorkerStatus.connectionFailed; setState(() {}); } void _onConnectSuccess(sender, e) { logger.e('_HeartRateState ${worker.mac}, 设备连接成功'); isConnectFail = false; errorCount = 0; connectStatus = WorkerStatus.connected; setState(() {}); } /// 设备连接中断 void _onDisconnected(sender, e) async { print('设备连接中断'); logger.i("设备连接中断:${worker.mac}"); if (errorCount < 3) { errorCount++; await tryReconnect(); } else { isConnectFail = true; } connectStatus = WorkerStatus.disconnected; setState(() {}); } // 心电图数据更新 void _onEcgValueUpdate(_, List e) { try { EcgViewController ecgViewController = Get.find(); ecgPoint.addAll(e); if (ecgPoint.length > 125 * 3) { // 3s 后开始塞数据 ecgViewController.addData(e); } } catch (e) { print(e); } } /// 更新心率 void _onHrValueUpdate(_, int e) { logger.i('心率更新:$e'); if (_deviceError.isNotEmpty || _assess.isNotEmpty) { // 如果上次因错误而停止了,这里需要先重置 _deviceError = ''; _assess = ''; resetEcgView(); } setState(() { _heart = e.toString(); }); } void _onError(_, String e) { logger.i('心率更新失败:$e'); _deviceError = e; resetEcgView(); setState(() {}); } /// 接收到最终结果 void _onRCesultReceived(_, HeartExamResult e) async { logger.i('心率更新接收结果:${e.heartRate}'); // print(ecgPoint.toString()); // _heart = e.toString(); _assess = e.analysis.first; _heart = e.heartRate.toString(); medicalController.diagnosisDataValue['Heart']?['HEART'] = e.heartRate.toString(); medicalController.diagnosisDataValue['Heart']?['ASSESS'] = dataConvertManager.heartRateConversion(int.parse(heartRate)); await setEcgData(); /// 判断数据是否完整 if (ecgPoint.isNotEmpty && ecgPoint.length < 125 * 30) { print("数据不完整 ⭐⭐⭐⭐⭐⭐⭐"); showResetButton = true; } medicalController.saveCachedRecord(); print(medicalController.diagnosisDataValue); setState(() {}); } void debugTest() async { logger.i('心率更新接收结果 测试'); // print(ecgPoint.toString()); // _heart = e.toString(); _assess = "ss"; _heart = "90"; medicalController.diagnosisDataValue['Heart']?['HEART'] = "90"; medicalController.diagnosisDataValue['Heart']?['ASSESS'] = dataConvertManager.heartRateConversion(int.parse(heartRate)); await setEcgData(); /// 判断数据是否完整 // if (ecgPoint.isNotEmpty && ecgPoint.length < 125 * 30) { // } print("数据不完整 ⭐⭐⭐⭐⭐⭐⭐"); showResetButton = true; medicalController.saveCachedRecord(); print(medicalController.diagnosisDataValue); setState(() {}); } /// 设置最终数据 Future setEcgData() async { EcgViewController ecgViewController = Get.find(); medicalController.diagnosisDataValue['Heart']?['ECG'] = await ecgViewController.getFullDataImageBase64(); medicalController.diagnosisDataValue['Heart']?['ECG_POINT'] = jsonEncode(ecgViewController.allPoints); } }