123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:ui';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:get/get.dart';
- import 'package:vitalapp/pages/medical/controller.dart';
- import 'package:vitalapp/pages/medical/widgets/twelve_ecg_view/state.dart';
- import 'package:vitalapp/pages/medical/widgets/twelve_ecg_view/widgets/grid_background_printer_for_five.dart';
- import 'dart:ui' as ui;
- import 'index.dart';
- class TwelveEcgViewController extends GetxController {
- TwelveEcgViewController({
- required this.initPoints,
- });
- final state = TwelveEcgState();
- final MedicalController medicalController = Get.find<MedicalController>();
- List<int> initPoints;
- /// 每秒的数据量
- static int get dataPerSecond => 200;
- /// 更新帧率
- static int updateFrameRate = 15;
- /// 更新周期
- static int updatePeriod = 1000 ~/ updateFrameRate;
- /// 画布时间跨度(秒)
- int get timeSpan => 5;
- /// 横坐标数据量
- int get xDataCount => (dataPerSecond * timeSpan).toInt();
- /// 所有心电数据
- List<int> allPoints = [];
- /// 需绘制的新数据
- List<List<int>> newPointsToDraw = [
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- []
- ];
- /// 需绘制的历史数据
- List<List<int>> oldPointsToDraw = [
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- [],
- []
- ];
- List<double> yMaxList = [
- 1000,
- 5000,
- 2000,
- 8000,
- 8000,
- 8000,
- 1000,
- 8000,
- 8000,
- 8000,
- 8000,
- 8000,
- ];
- /// 启动时时间戳
- int startTime = DateTime.now().millisecondsSinceEpoch;
- /// 当前数据位(根据时间戳计算)
- int currentDataIndex = 0;
- /// 数据刷新定时器
- Timer timer = Timer(Duration.zero, () {});
- /// 发生错误时暂停了
- bool isPaused = false;
- /// 是否有初始值
- bool isInitPoints = false;
- /// 读取到数据的回调
- void addData(List<int> data) {
- if (allPoints.isEmpty) {
- startTime = DateTime.now().millisecondsSinceEpoch;
- _startTimer();
- }
- allPoints.addAll(data);
- if (isPaused) {
- isPaused = false;
- _startTimer();
- }
- }
- /// 打开全屏心电图弹窗
- void openFullScreenDialog() {
- var startDate =
- medicalController.diagnosisDataValue['TwelveHeart']?['StartDate'] ?? "";
- var endDate =
- medicalController.diagnosisDataValue['TwelveHeart']?['EndDate'] ?? '';
- if (startDate.toString() != "" && endDate.toString() != "")
- state.rangeValues = RangeValues(
- double.parse(startDate.toString()), double.parse(endDate.toString()));
- Get.dialog(
- const FullScreenEcgDataDialog(),
- );
- }
- // 重置
- void reset() {
- timer.cancel();
- isPaused = false;
- allPoints = [];
- newPointsToDraw = [[], [], [], [], [], [], [], [], [], [], [], []];
- oldPointsToDraw = [[], [], [], [], [], [], [], [], [], [], [], []];
- startTime = DateTime.now().millisecondsSinceEpoch;
- currentDataIndex = 0;
- update(['twelve_ecg_view']);
- }
- /// 开始采集
- void startSave() {
- timer.cancel();
- allPoints = [];
- startTime = DateTime.now().millisecondsSinceEpoch;
- currentDataIndex = 0;
- }
- /// 因为实时数据请求量太大,就是延迟俩秒数据
- void updateTwelveEcgView() {
- initPoints = allPoints;
- update(['twelve_ecg_view']);
- }
- /// 获取完整心电图的base64(带base64头)
- Future<String> getFullDataImageBase64() async {
- final painter = EcgPainterForAll(
- allPoints: allPoints,
- yMaxList: yMaxList,
- );
- final bgPainter =
- GridBackgroundPainterForAll(rangeValues: RangeValues(0, 0));
- const size = Size(5000, 650);
- // 使用离屏Canvas绘制
- final Uint8List? bytes =
- await _capturePainterToImage(painter, bgPainter, size);
- if (bytes == null) {
- return "";
- } else {
- return _convertToBase64Url(bytes);
- }
- }
- List<List<int>> splitArrayIntoChunks(List<int> array, int chunkSize) {
- List<List<int>> chunks = [];
- for (int i = 0; i < array.length; i += chunkSize) {
- chunks.add(array.sublist(
- i, i + chunkSize > array.length ? array.length : i + chunkSize));
- }
- return chunks;
- }
- List<List<int>> splitArrayIntoChunks2() {
- List<List<int>> chunks = [];
- var satrtPoint = (allPoints.length * (state.rangeValues.start / 30)).ceil();
- var dataLength = (allPoints.length *
- ((state.rangeValues.end - state.rangeValues.start) / 30))
- .ceil();
- chunks.add(allPoints.sublist(satrtPoint, satrtPoint + dataLength));
- return chunks;
- }
- updateEcgImage() async {
- medicalController.diagnosisDataValue['TwelveHeart']?['ECG12'] =
- await getFiveImageBase64();
- medicalController.diagnosisDataValue['TwelveHeart']?['StartDate'] =
- state.rangeValues.start.toString();
- medicalController.diagnosisDataValue['TwelveHeart']?['EndDate'] =
- state.rangeValues.end.toString();
- }
- /// 获取五秒图心电图的base64(带base64头)
- Future<String> getFiveImageBase64() async {
- List<List<int>> chunks = splitArrayIntoChunks2();
- List<int> lastChunk = chunks.last;
- final painter = EcgPainterForAll(
- allPoints: lastChunk,
- yMaxList: yMaxList,
- );
- final bgPainter = GridBackgroundPainterForFive();
- // double width =
- // ((state.rangeValues.end - state.rangeValues.start) / 30) * 3200;
- Size size = Size(833, 650);
- // 使用离屏Canvas绘制
- final Uint8List? bytes =
- await _capturePainterToImage(painter, bgPainter, size);
- if (bytes == null) {
- return "";
- } else {
- return _convertToBase64Url(bytes);
- }
- }
- /// 开启定时器,每隔一定时间添加一次数据,并且更新UI
- void _startTimer() {
- timer = Timer.periodic(
- Duration(milliseconds: updatePeriod),
- (timer) {
- // print("timer: ${timer.tick}");
- _updateData();
- },
- );
- }
- List<List<int>> separateLines(List<int> array) {
- // 将数据拆分成12条线的数据
- List<List<int>> lines = List.generate(12, (_) => []);
- for (int i = 0; i < array.length; i++) {
- int lineIndex = i % 12; // 计算当前数据所属的线的索引
- lines[lineIndex].add(array[i]); // 将数据添加到对应的线中
- }
- return lines;
- }
- /// 每帧更新数据
- void _updateData() {
- // 计算当前数据位
- currentDataIndex = (DateTime.now().millisecondsSinceEpoch - startTime) ~/
- (1000 ~/ dataPerSecond);
- /// 需显示的数据量 不能为0
- int needDataCount = (currentDataIndex % xDataCount) == 0
- ? 25
- : currentDataIndex % xDataCount;
- /// 当前周期数
- int currentPeriod = currentDataIndex ~/ xDataCount;
- // 计算新数据
- try {
- for (int i = 0; i < 12; i++) {
- newPointsToDraw[i] = separateLines(allPoints)[i].sublist(
- currentDataIndex - needDataCount,
- currentDataIndex,
- );
- if (currentPeriod > 0) {
- oldPointsToDraw[i] = separateLines(allPoints)[i].sublist(
- (currentPeriod - 1) * xDataCount,
- currentPeriod * xDataCount,
- );
- }
- }
- if (currentDataIndex > dataPerSecond * 30) {
- startSave();
- }
- print(
- "update newPointsToDraw: $currentDataIndex $needDataCount ${newPointsToDraw[0].length} --currentPeriod $currentPeriod");
- } catch (e) {
- print("allPoints.length${allPoints.length}");
- timer.cancel();
- isPaused = true;
- }
- update(['twelve_ecg_view']);
- }
- /// 将字节数组转换为base64
- String _convertToBase64Url(Uint8List imageData) {
- String base64Image = base64Encode(imageData);
- String base64Url = base64Image;
- return base64Url;
- }
- /// 将CustomPainter绘制的内容转换为图片
- Future<Uint8List?> _capturePainterToImage(
- CustomPainter painter, CustomPainter bgPainter, Size size) async {
- final bounds = Offset.zero & size;
- final picture = PictureRecorder();
- final pictureCanvas = Canvas(picture);
- // 给Canvas设置绘制范围
- pictureCanvas.clipRect(bounds);
- /// 绘制背景(纯白)
- pictureCanvas.drawColor(Colors.white, BlendMode.color);
- // 在Canvas上进行绘制
- bgPainter.paint(pictureCanvas, size);
- painter.paint(pictureCanvas, size);
- /// 绘制一圈边框
- final borderPaint = Paint()
- ..color = Colors.black
- ..style = PaintingStyle.stroke
- ..strokeWidth = 1.0;
- pictureCanvas.drawRect(bounds, borderPaint);
- // 结束绘制
- final recordedPicture = picture.endRecording();
- final image =
- await recordedPicture.toImage(size.width.toInt(), size.height.toInt());
- // 转换为字节数组
- final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
- final bytes = byteData?.buffer.asUint8List();
- return bytes;
- }
- // @override
- // void onInit() {
- // super.onInit();
- // }
- @override
- void onReady() {
- super.onReady();
- if (initPoints.isNotEmpty) {
- isInitPoints = true;
- List<List<int>> initPointsLists = separateLines(initPoints);
- for (int i = 0; i < 12; i++) {
- newPointsToDraw[i] = initPointsLists[i];
- }
- update(['twelve_ecg_view']);
- allPoints = initPoints;
- }
- // if (initPoints.length >= 3750) {
- // newPointsToDraw = initPoints.sublist(3375, 3750);
- // update(['twelve_ecg_view']);
- // allPoints = initPoints;
- // } else {
- // newPointsToDraw = initPoints;
- // update(['twelve_ecg_view']);
- // allPoints = initPoints;
- // }
- }
- @override
- void onClose() {
- super.onClose();
- timer.cancel();
- }
- }
|