From 433616c7b627fbbb859ab1d632b24d9a752b44bb Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 12 Dec 2023 17:03:49 +0300 Subject: [PATCH] ECG Graph implemented --- .../diplomaticquarterapp/ble/BleBridge.kt | 37 +++++- .../ekg_file_detail_response_model.dart | 25 ++++ .../viatom_devices/ekg_chart_view.dart | 110 ++++++++++++++++++ .../viatom_devices/ekg_tracker_ble.dart | 77 ++++++------ lib/viatom_ble/ble_connect.dart | 24 ++-- 5 files changed, 222 insertions(+), 51 deletions(-) create mode 100644 lib/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart create mode 100644 lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt index 221ab5f4..89d1728d 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt @@ -18,6 +18,8 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.EventChannel import android.util.SparseArray import androidx.annotation.RequiresApi +import com.cloud.diplomaticquarterapp.ble.utils.EcgData +import com.google.gson.Gson //Ble @@ -28,10 +30,14 @@ import com.lepu.blepro.event.EventMsgConst.Ble.* import com.lepu.blepro.event.InterfaceEvent import com.lepu.blepro.ext.BleServiceHelper import com.lepu.blepro.ext.er2.DeviceInfo +import com.lepu.blepro.ext.er2.Er2EcgFile +import com.lepu.blepro.ext.er2.Er2File import com.lepu.blepro.ext.pc60fw.RtParam import com.lepu.blepro.objs.Bluetooth import com.lepu.blepro.objs.BluetoothController import com.lepu.blepro.observer.BleChangeObserver +import com.lepu.blepro.utils.DateUtil +import com.lepu.blepro.utils.Er1Decompress import io.flutter.plugin.common.EventChannel.EventSink import no.nordicsemi.android.ble.observer.ConnectionObserver @@ -49,6 +55,7 @@ class BleBridge( private const val SCAN_DEVICE = "scan" private const val SCAN_DEVICE_EKG = "scan_ekg" private const val EKG_FILES_LIST = "ekg_files_list" + private const val EKG_FILE_DETAIL = "ekg_file_detail" private const val DISCONNECT_DEVICE = "disconnect_device" } @@ -144,6 +151,9 @@ class BleBridge( // scanDeviceEKG(methodCall, result) } else if (methodCall.method == EKG_FILES_LIST) { getEKGFilesList() + } else if (methodCall.method == EKG_FILE_DETAIL) { + val fileName = methodCall.arguments; + getEKGFileDetail(fileName.toString()) } else { result.notImplemented() } @@ -211,6 +221,7 @@ class BleBridge( } if (deviceName.contains("DuoEK", ignoreCase = true)) { + val gson = Gson() LiveEventBus.get(InterfaceEvent.ER2.EventEr2Info) .observe(this.mainActivity) { val data = it.data as DeviceInfo @@ -233,9 +244,23 @@ class BleBridge( println("EventEr2SetTime") // Get Device Info BleServiceHelper.BleServiceHelper.er2GetInfo(model) - - //Get EKG File List -// BleServiceHelper.BleServiceHelper.er2GetFileList(model) + } + LiveEventBus.get(InterfaceEvent.ER2.EventEr2ReadFileComplete) + .observe(this.mainActivity) { + println("EventEr2ReadFileComplete") + val data = it.data as Er2File + val file = Er2EcgFile(data.content) + val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData) + val ecgData = EcgData() + val startTime = DateUtil.getSecondTimestamp(data.fileName.replace("R", "")) + ecgData.fileName = data.fileName + ecgData.duration = file.recordingTime + ecgData.shortData = ecgShorts + ecgData.startTime = startTime + val returnData = + mapOf("type" to "fileDetail", "data" to gson.toJson(ecgData)) +// val output = gson.toJson(returnData) + eventSink?.success(returnData) } } @@ -271,9 +296,13 @@ class BleBridge( } - fun getEKGFilesList() { + private fun getEKGFilesList() { //Get EKG File List BleServiceHelper.BleServiceHelper.er2GetFileList(model) } + private fun getEKGFileDetail(fileName: String) { + BleServiceHelper.BleServiceHelper.er2ReadFile(model, fileName) + } + } \ No newline at end of file diff --git a/lib/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart b/lib/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart new file mode 100644 index 00000000..18b5420f --- /dev/null +++ b/lib/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart @@ -0,0 +1,25 @@ +class EKGFileDetailResponseModel { + int duration; + String fileName; + List shortData; + int startTime; + + EKGFileDetailResponseModel( + {this.duration, this.fileName, this.shortData, this.startTime}); + + EKGFileDetailResponseModel.fromJson(Map json) { + duration = json['duration']; + fileName = json['fileName']; + shortData = json['shortData'].cast(); + startTime = json['startTime']; + } + + Map toJson() { + final Map data = new Map(); + data['duration'] = this.duration; + data['fileName'] = this.fileName; + data['shortData'] = this.shortData; + data['startTime'] = this.startTime; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart b/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart new file mode 100644 index 00000000..15e92163 --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart @@ -0,0 +1,110 @@ +import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class EKGChartView extends StatefulWidget { + EKGFileDetailResponseModel ekgFileDetailResponseModel; + + EKGChartView({@required this.ekgFileDetailResponseModel}); + + @override + State createState() => _EKGChartViewState(); +} + +class _EKGChartViewState extends State { + @override + void initState() { + super.initState(); + + print(widget.ekgFileDetailResponseModel.fileName); + } + + @override + Widget build(BuildContext context) { + List list = widget.ekgFileDetailResponseModel.shortData; + List> mainList = chunkIntList(list, 1250); + + return AppScaffold( + appBarTitle: "ECG", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: ListView.separated( + shrinkWrap: true, + physics: ScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.only(right: MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width * (mainList[index].length / 1250))), + child: SizedBox( + height: 120.0, + child: LineChart( + LineChartData( + lineTouchData: LineTouchData(handleBuiltInTouches: false), + gridData: FlGridData( + show: true, + verticalInterval: 30, + horizontalInterval: 30, + getDrawingVerticalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + getDrawingHorizontalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + ), + titlesData: FlTitlesData(show: false), + borderData: FlBorderData( + show: false, + border: Border.all(color: const Color(0xff37434d), width: 1), + ), + minX: 0, + maxX: (mainList[index].length.toDouble() - 1), + minY: list.reduce((value, element) => value < element ? value : element).toDouble(), + maxY: list.reduce((value, element) => value > element ? value : element).toDouble(), + lineBarsData: [ + LineChartBarData( + isCurved: false, + preventCurveOverShooting: true, + barWidth: 0.5, + dotData: FlDotData(show: false), + spots: getDataList(mainList[index]), + colors: [Colors.grey[800]], + isStrokeCapRound: true, + belowBarData: BarAreaData(show: false), + ), + ], + ), + ), + ), + ); + }, + itemCount: mainList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + ), + ); + } + + List> chunkIntList(List list, int chunkSize) { + List> chunks = []; + for (int i = 0; i < list.length; i += chunkSize) { + int end = i + chunkSize; + chunks.add(list.sublist(i, end > list.length ? list.length : end)); + } + return chunks; + } + + List getDataList(List list) { + List spotsList = []; + for (int i = 0; i < list.length; i++) { + spotsList.add(FlSpot(i.toDouble(), list[i].toDouble())); + } + return spotsList; + } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart b/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart index b33cac73..c01da287 100644 --- a/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart +++ b/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart @@ -1,7 +1,12 @@ +import 'dart:convert'; + +import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -37,7 +42,7 @@ class _EKG_BLEState extends State { @override Widget build(BuildContext context) { return AppScaffold( - appBarTitle: "EKG Tracker", + appBarTitle: "ECG Tracker", showNewAppBar: true, isShowDecPage: false, showNewAppBarTitle: true, @@ -63,6 +68,11 @@ class _EKG_BLEState extends State { if (event['type'] == "fileList") { parseEKGFilesList(event['data']); } + if (event['type'] == 'fileDetail') { + print("Received file data ---:"); + print(event['data']); + parseEKGFileDetailObject(event['data']); + } }); await BleChannel.getScanningResult(["oximeter", "ekg"]); }, @@ -85,38 +95,12 @@ class _EKG_BLEState extends State { ValueListenableBuilder( valueListenable: ekgValueNotifier, builder: (context, value, _) { - return Text(value); - // ? Platform.isAndroid - // ? Column( - // children: [ - // Text( - // getSPO2( - // value.toString().replaceAll("RtParam", ""), - // ), - // ), - // Text( - // getPR( - // value.toString().replaceAll("RtParam", ""), - // ), - // ), - // // Text( - // // getPI( - // // value.toString().replaceAll("RtParam", ""), - // // ), - // // ), - // ], - // ) - // : Column( - // children: [ - // Text(getSPO2iOS(value.toString())), - // Text(getPRiOS(value.toString())), - // ], - // ) - // : Text(value); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text(value), mHeight(24.0), getFilesListWidget()], + ); }, ), - mHeight(24.0), - getFilesListWidget() ], ), ), @@ -125,13 +109,36 @@ class _EKG_BLEState extends State { } void parseEKGFilesList(List filesListArray) { - List filesList = filesListArray.toString().replaceAll("[", "").replaceAll("]", "").split(","); - print("received files list: $filesListArray"); - ekgValueNotifier.value = filesListArray.toString().replaceAll("[", "").replaceAll("]", ""); + ekgFilesList.clear(); + ekgFilesList = filesListArray.toString().replaceAll("[", "").replaceAll("]", "").split(","); + ekgFilesList.removeWhere((element) => element.contains("a")); // removing analysis files from the list + ekgValueNotifier.value = ekgFilesList.toString(); } Widget getFilesListWidget() { - return Container(); + return ListView.separated( + shrinkWrap: true, + physics: ScrollPhysics(), + reverse: true, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + getEKGFileDetails(ekgFilesList[index]); + }, + child: Text(ekgFilesList[index])); + }, + itemCount: ekgFilesList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + ); } + void getEKGFileDetails(String fileName) async { + print("received file name: $fileName"); + await BleChannel.getEKGFileDetails(fileName); + } + + void parseEKGFileDetailObject(dynamic returnData) { + EKGFileDetailResponseModel ekgFileDetailResponseModel = EKGFileDetailResponseModel.fromJson(json.decode(returnData)); + Navigator.push(context, FadePage(page: EKGChartView(ekgFileDetailResponseModel: ekgFileDetailResponseModel))); + } } diff --git a/lib/viatom_ble/ble_connect.dart b/lib/viatom_ble/ble_connect.dart index 0ad00179..35a264a1 100644 --- a/lib/viatom_ble/ble_connect.dart +++ b/lib/viatom_ble/ble_connect.dart @@ -17,18 +17,6 @@ class BleChannel { } } - // static Future getScanningResultEKG(List deviceType) async { - // try { - // print("----------Flutter Init -------"); - // final String result = await platform.invokeMethod('scan_ekg', deviceType); - // print("----------Flutter Result -------"); - // print(result); - // return result; - // } catch (e) { - // return "Error: $e"; - // } - // } - static Future getEKGFilesList(List deviceType) async { try { print("----------Flutter Init -------"); @@ -41,6 +29,18 @@ class BleChannel { } } + static Future getEKGFileDetails(String fileName) async { + try { + print("----------Flutter Init -------"); + final String result = await platform.invokeMethod('ekg_file_detail', fileName); + print("----------Flutter Result -------"); + print(result); + return result; + } catch (e) { + return "Error: $e"; + } + } + static Future disconnect() async { try { print("----------Flutter Init -------");