|
|
|
|
@ -2,15 +2,20 @@ import 'dart:async';
|
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
|
|
|
|
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_realtime_data_response.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:fl_chart/fl_chart.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
|
|
|
|
|
import 'package:syncfusion_flutter_charts/charts.dart';
|
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
|
|
class EKG_BLE extends StatefulWidget {
|
|
|
|
|
const EKG_BLE();
|
|
|
|
|
|
|
|
|
|
@ -22,36 +27,101 @@ class _EKG_BLEState extends State<EKG_BLE> {
|
|
|
|
|
EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event');
|
|
|
|
|
String receivedData = '';
|
|
|
|
|
|
|
|
|
|
// final ekgValueNotifier = ValueNotifier<Map<String, dynamic>>;
|
|
|
|
|
final ekgValueNotifier = ValueNotifier<String>("start");
|
|
|
|
|
|
|
|
|
|
List<String> ekgFilesList = [];
|
|
|
|
|
|
|
|
|
|
List<double> ecgData = [];
|
|
|
|
|
EKGFileDetailResponseModel ekgFileDetailResponseModel;
|
|
|
|
|
EKGRealTimeDataResponseModel ekgRealTimeDataResponseModel;
|
|
|
|
|
|
|
|
|
|
List<int> ecgBytesAllDataList = [];
|
|
|
|
|
|
|
|
|
|
Timer timer;
|
|
|
|
|
List<_ChartData> chartData;
|
|
|
|
|
int count;
|
|
|
|
|
ChartSeriesController _chartSeriesController;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
// ekgValueNotifier.dispose();
|
|
|
|
|
ekgValueNotifier.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
BleChannel.disconnect();
|
|
|
|
|
timer?.cancel();
|
|
|
|
|
chartData.clear();
|
|
|
|
|
_chartSeriesController = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
// TODO: implement initState
|
|
|
|
|
|
|
|
|
|
count = 19;
|
|
|
|
|
chartData = <_ChartData>[
|
|
|
|
|
_ChartData(0, 42),
|
|
|
|
|
_ChartData(1, 47),
|
|
|
|
|
_ChartData(2, 33),
|
|
|
|
|
_ChartData(3, 49),
|
|
|
|
|
_ChartData(4, 54),
|
|
|
|
|
_ChartData(5, 41),
|
|
|
|
|
_ChartData(6, 58),
|
|
|
|
|
_ChartData(7, 51),
|
|
|
|
|
_ChartData(8, 98),
|
|
|
|
|
_ChartData(9, 41),
|
|
|
|
|
_ChartData(10, 53),
|
|
|
|
|
_ChartData(11, 72),
|
|
|
|
|
_ChartData(12, 86),
|
|
|
|
|
_ChartData(13, 52),
|
|
|
|
|
_ChartData(14, 94),
|
|
|
|
|
_ChartData(15, 92),
|
|
|
|
|
_ChartData(16, 86),
|
|
|
|
|
_ChartData(17, 72),
|
|
|
|
|
_ChartData(18, 94),
|
|
|
|
|
];
|
|
|
|
|
timer = Timer.periodic(const Duration(milliseconds: 1000), _updateDataSource);
|
|
|
|
|
super.initState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SfCartesianChart _buildLiveLineChart() {
|
|
|
|
|
return SfCartesianChart(
|
|
|
|
|
plotAreaBorderWidth: 0,
|
|
|
|
|
primaryXAxis: NumericAxis(majorGridLines: const MajorGridLines(width: 0)),
|
|
|
|
|
primaryYAxis: NumericAxis(axisLine: const AxisLine(width: 0), majorTickLines: const MajorTickLines(size: 0)),
|
|
|
|
|
|
|
|
|
|
// Simulate live ECG data (replace this with your actual data source)
|
|
|
|
|
timer = Timer.periodic(Duration(milliseconds: 500), (Timer t) {
|
|
|
|
|
setState(() {
|
|
|
|
|
ecgData.add((10.0 * (1 - 2 * (ecgData.length % 2).toDouble())).toDouble());
|
|
|
|
|
if (ecgData.length > 10) {
|
|
|
|
|
ecgData.removeAt(0);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
series: <LineSeries<_ChartData, int>>[
|
|
|
|
|
LineSeries<_ChartData, int>(
|
|
|
|
|
onRendererCreated: (ChartSeriesController controller) {
|
|
|
|
|
_chartSeriesController = controller;
|
|
|
|
|
},
|
|
|
|
|
dataSource: chartData,
|
|
|
|
|
color: const Color.fromRGBO(192, 108, 132, 1),
|
|
|
|
|
xValueMapper: (_ChartData sales, _) => sales.country,
|
|
|
|
|
yValueMapper: (_ChartData sales, _) => sales.sales,
|
|
|
|
|
animationDuration: 2000.0)
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _updateDataSource(Timer timer) {
|
|
|
|
|
// if (isCardView != null) {
|
|
|
|
|
chartData.add(_ChartData(count, _getRandomInt(10, 100)));
|
|
|
|
|
if (chartData.length == 20) {
|
|
|
|
|
chartData.removeAt(0);
|
|
|
|
|
_chartSeriesController?.updateDataSource(
|
|
|
|
|
addedDataIndexes: <int>[chartData.length - 1],
|
|
|
|
|
removedDataIndexes: <int>[0],
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
_chartSeriesController?.updateDataSource(
|
|
|
|
|
addedDataIndexes: <int>[chartData.length - 1],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
count = count + 1;
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///Get the random data
|
|
|
|
|
int _getRandomInt(int min, int max) {
|
|
|
|
|
final math.Random random = math.Random();
|
|
|
|
|
return min + random.nextInt(max - min);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
@ -88,6 +158,10 @@ class _EKG_BLEState extends State<EKG_BLE> {
|
|
|
|
|
print(event['data']);
|
|
|
|
|
parseEKGFileDetailObject(event['data']);
|
|
|
|
|
}
|
|
|
|
|
if (event['type'] == "realtimeDataECG") {
|
|
|
|
|
ekgValueNotifier.value = event['data'];
|
|
|
|
|
parseEKGRealTimeDataObject(event['data']);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
await BleChannel.getScanningResult(["oximeter", "ekg"]);
|
|
|
|
|
},
|
|
|
|
|
@ -106,16 +180,76 @@ class _EKG_BLEState extends State<EKG_BLE> {
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
mHeight(50.0),
|
|
|
|
|
ValueListenableBuilder(
|
|
|
|
|
valueListenable: ekgValueNotifier,
|
|
|
|
|
builder: (context, value, _) {
|
|
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [Text(value), mHeight(24.0), getFilesListWidget()],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
mHeight(20.0),
|
|
|
|
|
_buildLiveLineChart()
|
|
|
|
|
// ValueListenableBuilder(
|
|
|
|
|
// valueListenable: ekgValueNotifier,
|
|
|
|
|
// builder: (context, value, _) {
|
|
|
|
|
// return Column(
|
|
|
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
// children: [
|
|
|
|
|
// Text(
|
|
|
|
|
// value,
|
|
|
|
|
// style: TextStyle(fontSize: 9.0),
|
|
|
|
|
// ),
|
|
|
|
|
// mHeight(24.0),
|
|
|
|
|
// getFilesListWidget(),
|
|
|
|
|
// mHeight(20.0),
|
|
|
|
|
// // SizedBox(
|
|
|
|
|
// // height: 150.0,
|
|
|
|
|
// // width: MediaQuery.of(context).size.width,
|
|
|
|
|
// // child: (ekgRealTimeDataResponseModel != null && ekgRealTimeDataResponseModel.wave != null)
|
|
|
|
|
// // ? 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: true),
|
|
|
|
|
// // borderData: FlBorderData(
|
|
|
|
|
// // show: false,
|
|
|
|
|
// // border: Border.all(color: const Color(0xff37434d), width: 1),
|
|
|
|
|
// // ),
|
|
|
|
|
// // minX: 0,
|
|
|
|
|
// // // maxX: (ekgRealTimeDataResponseModel.wave.ecgBytes.length.toDouble() - 1),
|
|
|
|
|
// // maxX: ecgBytesAllDataList.length.toDouble(),
|
|
|
|
|
// // minY: ekgRealTimeDataResponseModel.wave.ecgBytes.reduce((value, element) => value < element ? value : element).toDouble(),
|
|
|
|
|
// // maxY: ekgRealTimeDataResponseModel.wave.ecgBytes.reduce((value, element) => value > element ? value : element).toDouble(),
|
|
|
|
|
// // lineBarsData: [
|
|
|
|
|
// // LineChartBarData(
|
|
|
|
|
// // isCurved: false,
|
|
|
|
|
// // preventCurveOverShooting: true,
|
|
|
|
|
// // barWidth: 0.5,
|
|
|
|
|
// // dotData: FlDotData(show: false),
|
|
|
|
|
// // // spots: getDataList(ekgRealTimeDataResponseModel.wave.ecgBytes),
|
|
|
|
|
// // spots: getDataList(ekgRealTimeDataResponseModel.wave.ecgBytes),
|
|
|
|
|
// // colors: [Colors.grey[800]],
|
|
|
|
|
// // isStrokeCapRound: true,
|
|
|
|
|
// // belowBarData: BarAreaData(show: false),
|
|
|
|
|
// // ),
|
|
|
|
|
// // ],
|
|
|
|
|
// // ),
|
|
|
|
|
// // )
|
|
|
|
|
// // : Container(),
|
|
|
|
|
// // ),
|
|
|
|
|
// ],
|
|
|
|
|
// );
|
|
|
|
|
// },
|
|
|
|
|
// ),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
@ -130,6 +264,21 @@ class _EKG_BLEState extends State<EKG_BLE> {
|
|
|
|
|
ekgValueNotifier.value = ekgFilesList.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<FlSpot> getDataList(List<int> list) {
|
|
|
|
|
ecgBytesAllDataList.add(list[0]);
|
|
|
|
|
// ecgBytesAllDataList.addAll(list[0]);
|
|
|
|
|
|
|
|
|
|
// if(ecgBytesAllDataList.length >= 1250) {
|
|
|
|
|
// ecgBytesAllDataList.removeRange(0, 1200);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
List<FlSpot> spotsList = [];
|
|
|
|
|
for (int i = 0; i < ecgBytesAllDataList.length; i++) {
|
|
|
|
|
spotsList.add(FlSpot(i.toDouble(), ecgBytesAllDataList[i].toDouble()));
|
|
|
|
|
}
|
|
|
|
|
return spotsList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget getFilesListWidget() {
|
|
|
|
|
return ListView.separated(
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
@ -153,7 +302,19 @@ class _EKG_BLEState extends State<EKG_BLE> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void parseEKGFileDetailObject(dynamic returnData) {
|
|
|
|
|
EKGFileDetailResponseModel ekgFileDetailResponseModel = EKGFileDetailResponseModel.fromJson(json.decode(returnData));
|
|
|
|
|
ekgFileDetailResponseModel = EKGFileDetailResponseModel.fromJson(json.decode(returnData));
|
|
|
|
|
Navigator.push(context, FadePage(page: EKGChartView(ekgFileDetailResponseModel: ekgFileDetailResponseModel)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String parseEKGRealTimeDataObject(dynamic returnData) {
|
|
|
|
|
ekgRealTimeDataResponseModel = EKGRealTimeDataResponseModel.fromJson(json.decode(returnData));
|
|
|
|
|
return "HeartRate: ${ekgRealTimeDataResponseModel.param.hr} - Current State: ${ekgRealTimeDataResponseModel.param.curStatus}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _ChartData {
|
|
|
|
|
_ChartData(this.country, this.sales);
|
|
|
|
|
|
|
|
|
|
final int country;
|
|
|
|
|
final num sales;
|
|
|
|
|
}
|
|
|
|
|
|