heart_rate.dart 9.0 KB

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