diff --git a/extras/log_file_client/lib/components/custom_time_range_picker.dart b/extras/log_file_client/lib/components/custom_time_range_picker.dart index 2dedcc7a..35fe9a52 100644 --- a/extras/log_file_client/lib/components/custom_time_range_picker.dart +++ b/extras/log_file_client/lib/components/custom_time_range_picker.dart @@ -20,63 +20,73 @@ class CustomTimeRangePicker extends StatefulWidget { } class _CustomTimeRangePickerState extends State { - late DateTime minTime; - late DateTime maxTime; + late DateTimeRange selectedRange; @override void initState() { super.initState(); - minTime = widget.timeRange.start; - maxTime = widget.timeRange.end; + selectedRange = widget.timeRange; } void onDatesSelected(DateTimeRange value) { - minTime = DateTime( - value.start.year, - value.start.month, - value.start.day, - minTime.hour, - minTime.minute, - ); - maxTime = DateTime( - value.end.year, - value.end.month, - value.end.day, - maxTime.hour, - maxTime.minute, - ); + setState(() { + selectedRange = DateTimeRange( + start: DateTime( + value.start.year, + value.start.month, + value.start.day, + selectedRange.start.hour, + selectedRange.start.minute, + ), + end: DateTime( + value.end.year, + value.end.month, + value.end.day, + selectedRange.end.hour, + selectedRange.end.minute, + ), + ); + }); } void onTimeSelected(TimeOfDay time, RangeEndpointType type) { if (type == RangeEndpointType.start) { - minTime = DateTime( - minTime.year, - minTime.month, - minTime.day, - time.hour, - time.minute, + selectedRange = DateTimeRange( + start: DateTime( + selectedRange.start.year, + selectedRange.start.month, + selectedRange.start.day, + time.hour, + time.minute, + ), + end: selectedRange.end, ); } else if (type == RangeEndpointType.end) { - maxTime = DateTime( - maxTime.year, - maxTime.month, - maxTime.day, - time.hour, - time.minute, + selectedRange = DateTimeRange( + start: selectedRange.start, + end: DateTime( + selectedRange.end.year, + selectedRange.end.month, + selectedRange.end.day, + time.hour, + time.minute, + ), ); } } void onApplied() { // Ensure the time range is within the available range - if (minTime.isBefore(widget.timeRange.start)) { - minTime = widget.timeRange.start; - } - if (maxTime.isAfter(widget.timeRange.end)) { - maxTime = widget.timeRange.end; - } + selectedRange = DateTimeRange( + start: selectedRange.start.isAfter(widget.timeRange.start) + ? selectedRange.start + : widget.timeRange.start, + end: selectedRange.end.isBefore(widget.timeRange.end) + ? selectedRange.end + : widget.timeRange.end, + ); - widget.onApplied(DateTimeRange(start: minTime, end: maxTime)); + widget.onApplied(selectedRange); } @override @@ -95,10 +105,7 @@ class _CustomTimeRangePickerState extends State { ), Padding( padding: const EdgeInsets.only(bottom: 8), - child: TextButton( - onPressed: onApplied, - child: const Text('Apply'), - ), + child: TextButton(onPressed: onApplied, child: const Text('Apply')), ), ], ), @@ -141,8 +148,8 @@ class _CustomTimeRangePickerState extends State { Widget _dateDisplay(RangeEndpointType type) { return Text( type == RangeEndpointType.start - ? DateFormat.yMMMd('en_US').format(minTime) - : DateFormat.yMMMd('en_US').format(maxTime), + ? DateFormat.yMMMd('en_US').format(selectedRange.start) + : DateFormat.yMMMd('en_US').format(selectedRange.end), style: const TextStyle(fontSize: 16), ); } @@ -153,8 +160,9 @@ class _CustomTimeRangePickerState extends State { height: 300, child: RangeDatePicker( centerLeadingDate: true, - minDate: minTime, - maxDate: maxTime, + minDate: widget.timeRange.start, + maxDate: widget.timeRange.end, + selectedRange: selectedRange, onRangeSelected: onDatesSelected, splashColor: Colors.transparent, daysOfTheWeekTextStyle: TextStyle( @@ -173,8 +181,14 @@ class _CustomTimeRangePickerState extends State { Widget _timePicker(RangeEndpointType type) { final TimeOfDay selectedTime = type == RangeEndpointType.start - ? TimeOfDay(hour: minTime.hour, minute: minTime.minute) - : TimeOfDay(hour: maxTime.hour, minute: maxTime.minute); + ? TimeOfDay( + hour: selectedRange.start.hour, + minute: selectedRange.start.minute, + ) + : TimeOfDay( + hour: selectedRange.end.hour, + minute: selectedRange.end.minute, + ); return SizedBox( height: 100, diff --git a/extras/log_file_client/lib/components/graph_view.dart b/extras/log_file_client/lib/components/graph_view.dart index 8ce89a33..254e3c07 100644 --- a/extras/log_file_client/lib/components/graph_view.dart +++ b/extras/log_file_client/lib/components/graph_view.dart @@ -30,6 +30,8 @@ class _GraphViewState extends State { late double timeInterval; late DateFormat timeFormat; + int scatterGranularity = 1; + @override void initState() { super.initState(); @@ -79,6 +81,7 @@ class _GraphViewState extends State { displayedTimeRange = timeRange; _displayedTimeRangeIndices = calculateTimeRange(timeRange); calculateTimeAxisFormat(); + calculateGranularity(); }); } @@ -116,6 +119,17 @@ class _GraphViewState extends State { } } + void calculateGranularity() { + final Duration duration = displayedTimeRange.duration; + if (duration <= Duration(days: 5)) { + scatterGranularity = 1; + } else if (duration <= Duration(days: 20)) { + scatterGranularity = 3; + } else { + scatterGranularity = 10; + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -248,11 +262,13 @@ class _GraphViewState extends State { List _chartSeries(List logData) { final MarkerSettings markerSettings = MarkerSettings(height: 2, width: 2); + final subsampledLogData = _subsampleData(logData, scatterGranularity); + return [ ScatterSeries( legendItemText: 'pH', name: 'pH', - dataSource: logData, + dataSource: subsampledLogData, xValueMapper: (LogDataLine log, _) => log.time, yValueMapper: (LogDataLine log, _) => log.phCurrent, color: _showPH ? Colors.green : Colors.transparent, @@ -264,7 +280,7 @@ class _GraphViewState extends State { ScatterSeries( legendItemText: 'pH setpoint', name: 'pH setpoint', - dataSource: logData, + dataSource: subsampledLogData, xValueMapper: (LogDataLine log, _) => log.time, yValueMapper: (LogDataLine log, _) => log.phTarget, color: _showPH ? Colors.green.shade800 : Colors.transparent, @@ -276,7 +292,7 @@ class _GraphViewState extends State { ScatterSeries( legendItemText: 'temp', name: 'temp', - dataSource: logData, + dataSource: subsampledLogData, xValueMapper: (LogDataLine log, _) => log.time, yValueMapper: (LogDataLine log, _) => log.tempMean, color: _showTemp ? Colors.blue : Colors.transparent, @@ -288,7 +304,7 @@ class _GraphViewState extends State { ScatterSeries( legendItemText: 'temp setpoint', name: 'temp setpoint', - dataSource: logData, + dataSource: subsampledLogData, xValueMapper: (LogDataLine log, _) => log.time, yValueMapper: (LogDataLine log, _) => log.tempTarget, color: _showTemp ? Colors.blue.shade800 : Colors.transparent, @@ -299,4 +315,14 @@ class _GraphViewState extends State { ), ]; } + + List _subsampleData(List logData, int granularity) { + final List subSampledData = []; + + for (int i = 0; i < logData.length; i += scatterGranularity) { + subSampledData.add(logData[i]); + } + + return subSampledData; + } }