heart_rate.dart 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'package:flutter/material.dart';
  4. import 'package:get/get.dart';
  5. import 'package:vitalapp/managers/interfaces/models/device.dart';
  6. import 'package:vitalapp/pages/medical/controllers/heart.dart';
  7. import 'package:vitalapp/pages/medical/widgets/device_status.dart';
  8. import 'package:vitalapp/pages/medical/widgets/ecg_view/index.dart';
  9. import 'package:vnote_device_plugin/consts/types.dart';
  10. import 'package:vnote_device_plugin/devices/heart.dart';
  11. import 'package:vitalapp/pages/check/widgets/exam_configurable/exam_card.dart';
  12. import 'package:vitalapp/pages/medical/controller.dart';
  13. import 'package:vitalapp/pages/medical/models/worker.dart';
  14. import 'package:vitalapp/pages/medical/widgets/side_bar.dart';
  15. import 'dart:ui' as ui;
  16. import 'dart:typed_data';
  17. import 'package:vnote_device_plugin/models/exams/heart.dart';
  18. class HeartRate extends StatefulWidget {
  19. const HeartRate({
  20. super.key,
  21. });
  22. @override
  23. State<HeartRate> createState() => _HeartRateState();
  24. }
  25. class _HeartRateState extends State<HeartRate> {
  26. var controller = Get.find<MedicalController>();
  27. late HeartDeviceController heart;
  28. late HeartDeviceWorker worker;
  29. WorkerStatus _connectStatus = WorkerStatus.connecting;
  30. List<int> ecgPoint = [];
  31. late String _heart = controller.diagnosisDataValue['Heart']?['HEART'] ?? '';
  32. late final String _assess =
  33. controller.diagnosisDataValue['Heart']?['ASSESS'] ?? '';
  34. late final String _ecg = controller.diagnosisDataValue['Heart']?['ECG'] ?? '';
  35. late final String _ecgPoint =
  36. controller.diagnosisDataValue['Heart']?['ECG_POINT'] ?? '';
  37. @override
  38. void initState() {
  39. initHeart();
  40. super.initState();
  41. }
  42. Future<void> connect() async {
  43. await worker.connect();
  44. }
  45. Future<void> disconnect() async {
  46. if (!worker.isNull) {
  47. await worker.disconnect();
  48. releaseListeners();
  49. }
  50. }
  51. void releaseListeners() {
  52. worker.connectErrorEvent.removeListener(_onConnectFail);
  53. worker.connectedEvent.removeListener(_onConnectSuccess);
  54. // worker.successEvent.removeListener(_onSuccess);
  55. worker.disconnectedEvent.removeListener(_onDisconnected);
  56. worker.hrValueUpdateEvent.removeListener(_onHrValueUpdate);
  57. worker.ecgValueUpdateEvent.removeListener(_onEcgValueUpdate);
  58. worker.resultReceivedEvent.removeListener(_onRCesultReceived);
  59. }
  60. @override
  61. void dispose() {
  62. disconnect();
  63. super.dispose();
  64. }
  65. void loadListeners() {
  66. // worker.successEvent.addListener(_onSuccess);
  67. worker.connectErrorEvent.addListener(_onConnectFail);
  68. worker.connectedEvent.addListener(_onConnectSuccess);
  69. worker.disconnectedEvent.addListener(_onDisconnected);
  70. worker.hrValueUpdateEvent.addListener(_onHrValueUpdate);
  71. worker.ecgValueUpdateEvent.addListener(_onEcgValueUpdate);
  72. worker.resultReceivedEvent.addListener(_onRCesultReceived);
  73. worker.connect();
  74. }
  75. Future<void> currentDevice() async {
  76. DeviceModel? device = await controller.getDevice(DeviceTypes.HEART);
  77. if (device.isNull) {
  78. _connectStatus = WorkerStatus.unboundDevice;
  79. setState(() {});
  80. return;
  81. }
  82. heart = HeartDeviceController(device?.model ?? '', device?.mac ?? '');
  83. worker = heart.worker;
  84. _connectStatus = heart.connectStatus;
  85. loadListeners();
  86. }
  87. Future<void> initHeart() async {
  88. currentDevice();
  89. await initData();
  90. }
  91. Future<void> initData() async {
  92. await controller.readCachedRecord();
  93. if (controller.diagnosisDataValue['Heart'] == null) {
  94. controller.diagnosisDataValue['Heart'] = {};
  95. }
  96. setState(() {});
  97. }
  98. void _onSuccess(_, double e) {
  99. setState(() {
  100. // _weight = e.toString();
  101. _connectStatus = WorkerStatus.connected;
  102. });
  103. }
  104. void _onHrValueUpdate(_, int e) {
  105. setState(() {
  106. _heart = e.toString();
  107. });
  108. }
  109. void _onRCesultReceived(_, HeartExamResult e) {
  110. setState(() async {
  111. print(ecgPoint.toString());
  112. // _heart = e.toString();
  113. controller.diagnosisDataValue['Heart']?['HEART'] = e.heartRate;
  114. controller.diagnosisDataValue['Heart']?['ASSESS'] = e.analysis.first;
  115. controller.diagnosisDataValue['Heart']?['ECG'] =
  116. await createEcgImageBase64(ecgPoint);
  117. controller.diagnosisDataValue['Heart']?['ECG_POINT'] =
  118. ecgPoint.toString();
  119. print(controller.diagnosisDataValue);
  120. // late final String _assess =
  121. // controller.diagnosisDataValue['Heart']?['ASSESS'] ?? '';
  122. // late final String _ecg =
  123. // controller.diagnosisDataValue['Heart']?['ECG'] ?? '';
  124. // late final String _ecgPoint =
  125. // controller.diagnosisDataValue['Heart']?['ECG_POINT'] ?? '';
  126. });
  127. }
  128. void _onEcgValueUpdate(_, List<int> e) {
  129. try {
  130. EcgViewController ecgViewController = Get.find<EcgViewController>();
  131. ecgPoint.addAll(e);
  132. if (ecgPoint.length > 125 * 3) {
  133. ecgViewController.addData(e);
  134. }
  135. } catch (e) {
  136. print(e);
  137. }
  138. }
  139. void _onDeviceCloseSuccess(sender, e) {
  140. if (e) {
  141. connect();
  142. }
  143. }
  144. void _onConnectFail(sender, e) {
  145. print('连接设备失败');
  146. _connectStatus = WorkerStatus.connectionFailed;
  147. setState(() {});
  148. }
  149. void _onConnectSuccess(sender, e) {
  150. _connectStatus = WorkerStatus.connected;
  151. setState(() {});
  152. }
  153. void _onDisconnected(sender, e) {
  154. print('设备连接中断');
  155. _connectStatus = WorkerStatus.disconnected;
  156. setState(() {});
  157. }
  158. Future<String> createEcgImageBase64(List<int> points) async {
  159. final int width = points.length ~/ 2;
  160. const int height = 240;
  161. final ui.PictureRecorder recorder = ui.PictureRecorder();
  162. final Canvas canvas = Canvas(recorder);
  163. final Path path = Path();
  164. final Paint paint = Paint();
  165. const double strokeWidth = 1.5;
  166. paint.color = Colors.green;
  167. paint.style = PaintingStyle.stroke;
  168. paint.isAntiAlias = true;
  169. paint.strokeWidth = strokeWidth;
  170. path.reset();
  171. int pointCount = points.length;
  172. if (pointCount > 0) {
  173. path.moveTo(0, points[0].toDouble());
  174. int i = 0;
  175. int j = pointCount ~/ 2;
  176. while (i < j) {
  177. path.lineTo(i.toDouble(), points[i].toDouble());
  178. path.lineTo((i + 1).toDouble(), points[i + 1].toDouble());
  179. i += 2;
  180. }
  181. canvas.drawPath(path, paint);
  182. }
  183. final ui.Picture picture = recorder.endRecording();
  184. final ui.Image image = await picture.toImage(width, height);
  185. final ByteData? byteData =
  186. await image.toByteData(format: ui.ImageByteFormat.png);
  187. final Uint8List pngBytes = byteData!.buffer.asUint8List();
  188. final String base64Image = base64Encode(pngBytes);
  189. return base64Image;
  190. }
  191. @override
  192. Widget build(BuildContext context) {
  193. return ExamCard(
  194. titleText: const SizedBox(),
  195. // clickCard: () {},
  196. content: Column(
  197. mainAxisAlignment: MainAxisAlignment.start,
  198. children: [
  199. Padding(
  200. padding: const EdgeInsets.all(10),
  201. child: DeviceStatus(connectStatus: _connectStatus)),
  202. SideBar(
  203. title: '心率',
  204. value: _heart.isEmpty ? '--' : _heart,
  205. unit: '',
  206. ),
  207. SizedBox(
  208. height: 240,
  209. child: LayoutBuilder(builder: (context, constraints) {
  210. print(constraints.maxWidth);
  211. print(constraints.maxHeight);
  212. return EcgView(
  213. width: constraints.maxWidth,
  214. height: constraints.maxHeight,
  215. );
  216. }),
  217. )
  218. // CustomPaint(
  219. // painter: GridCanvasPainter(),
  220. // child: EcgPage(
  221. // points: ecgPoint,
  222. // ),
  223. // ),
  224. // const Divider(indent: 30),
  225. // Stack(
  226. // children: [
  227. // SideBar(
  228. // title: '心率评估',
  229. // value: _assess.isEmpty ? '--' : _assess,
  230. // hasDevice: true,
  231. // unit: '',
  232. // ),
  233. // // DeviceStatus(connectStatus: _connectStatus),
  234. // ],
  235. // ),
  236. // const Divider(indent: 30),
  237. // MyWidget()
  238. // const EcgPage(),
  239. // SideBar(
  240. // title: '心电图',
  241. // value: _assess.isEmpty ? '--' : _assess,
  242. // unit: '',
  243. // ),
  244. ],
  245. ));
  246. }
  247. }