mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-22 02:14:28 +10:00
Refactor Cayenne LPP parsing with error handling and logging
- Added error handling and logging to the Cayenne LPP parsing methods to manage malformed data gracefully. - Improved the structure of the parsing logic for better readability and maintainability. - Updated the Contact model to include error handling during frame parsing. - Refactored Channels, Contacts, Map, and Neighbours screens to utilize a new AppBarTitle widget for consistent app bar design. - Enhanced the BatteryIndicator widget to display SNR information for direct repeaters. - Introduced SNRUi class for better management of SNR icon and text representation. - Improved error handling in PathTraceMap and Neighbours screens to log errors appropriately.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:meshcore_open/connector/meshcore_connector.dart';
|
||||
import 'package:meshcore_open/widgets/battery_indicator.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AppBarTitle extends StatelessWidget {
|
||||
final String title;
|
||||
final TextStyle? style;
|
||||
final Widget? leading;
|
||||
final Widget? trailing;
|
||||
const AppBarTitle(
|
||||
this.title,
|
||||
this.style, {
|
||||
this.leading,
|
||||
this.trailing,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final connector = context.watch<MeshCoreConnector>();
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (leading != null) leading!,
|
||||
Text(title),
|
||||
if (connector.isConnected && connector.selfName != null)
|
||||
Center(
|
||||
child: Text(
|
||||
'(${connector.selfName})',
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
BatteryIndicator(connector: connector),
|
||||
if (trailing != null) trailing!,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:meshcore_open/widgets/snr_indicator.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
|
||||
@@ -42,6 +43,7 @@ class _BatteryIndicatorState extends State<BatteryIndicator> {
|
||||
Widget build(BuildContext context) {
|
||||
final percent = widget.connector.batteryPercent;
|
||||
final millivolts = widget.connector.batteryMillivolts;
|
||||
final directRepeaters = widget.connector.directRepeaters;
|
||||
|
||||
if (millivolts == null) {
|
||||
return const SizedBox.shrink();
|
||||
@@ -55,6 +57,20 @@ class _BatteryIndicatorState extends State<BatteryIndicator> {
|
||||
}
|
||||
|
||||
final batteryUi = batteryUiForPercent(percent);
|
||||
final directBestRepeaters = List.of(directRepeaters)
|
||||
..sort((a, b) {
|
||||
final dateCompare = b.lastUpdated.compareTo(a.lastUpdated);
|
||||
if (dateCompare != 0) return dateCompare;
|
||||
return (b.snr).compareTo(a.snr);
|
||||
});
|
||||
final directRepeater = directBestRepeaters.isEmpty
|
||||
? null
|
||||
: directBestRepeaters.first;
|
||||
|
||||
final snrUi = snrUiFromSNR(
|
||||
directBestRepeaters.isNotEmpty ? directRepeater!.snr : null,
|
||||
widget.connector.currentSf,
|
||||
);
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
@@ -68,19 +84,53 @@ class _BatteryIndicatorState extends State<BatteryIndicator> {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(batteryUi.icon, size: 18, color: batteryUi.color),
|
||||
const SizedBox(width: 2),
|
||||
Flexible(
|
||||
child: Text(
|
||||
displayText,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: batteryUi.color,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(batteryUi.icon, size: 18, color: batteryUi.color),
|
||||
const SizedBox(width: 2),
|
||||
Flexible(
|
||||
child: Text(
|
||||
displayText,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: batteryUi.color,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
overflow: TextOverflow.visible,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(snrUi.icon, size: 18, color: snrUi.color),
|
||||
Text(
|
||||
snrUi.text,
|
||||
style: TextStyle(fontSize: 12, color: snrUi.color),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (directRepeater != null)
|
||||
Text(
|
||||
'${directRepeaters.length}: ${directRepeater.pubkeyFirstByte.toRadixString(16).padLeft(2, '0')}: ${_formatLastUpdated(directRepeater.lastUpdated)}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -88,4 +138,22 @@ class _BatteryIndicatorState extends State<BatteryIndicator> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatLastUpdated(DateTime lastSeen) {
|
||||
final now = DateTime.now();
|
||||
final diff = now.difference(lastSeen);
|
||||
|
||||
if (diff.isNegative || diff.inMinutes < 1) {
|
||||
return "${diff.inSeconds}s";
|
||||
}
|
||||
if (diff.inMinutes < 60) {
|
||||
return "${diff.inMinutes}m";
|
||||
}
|
||||
if (diff.inHours < 24) {
|
||||
final hours = diff.inHours;
|
||||
return hours == 1 ? "1h" : "${hours}hs";
|
||||
}
|
||||
final days = diff.inDays;
|
||||
return days == 1 ? "1d" : "${days}ds";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SNRUi {
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
final String text;
|
||||
const SNRUi(this.icon, this.color, this.text);
|
||||
}
|
||||
|
||||
List<double> getSNRfromSF(int spreadingFactor) {
|
||||
switch (spreadingFactor) {
|
||||
case 7:
|
||||
@@ -19,44 +26,33 @@ List<double> getSNRfromSF(int spreadingFactor) {
|
||||
}
|
||||
}
|
||||
|
||||
class SNRIcon extends StatelessWidget {
|
||||
final double snr;
|
||||
final List<double> snrLevels;
|
||||
|
||||
const SNRIcon({
|
||||
super.key,
|
||||
required this.snr,
|
||||
this.snrLevels = const [4.0, -2.0, -4.0, -6.0],
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
IconData icon;
|
||||
Color color;
|
||||
|
||||
if (snr >= snrLevels[0]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.green;
|
||||
} else if (snr >= snrLevels[1]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.lightGreen;
|
||||
} else if (snr >= snrLevels[2]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.yellow;
|
||||
} else if (snr >= snrLevels[3]) {
|
||||
icon = Icons.signal_cellular_alt_2_bar;
|
||||
color = Colors.orange;
|
||||
} else {
|
||||
icon = Icons.signal_cellular_alt_1_bar;
|
||||
color = Colors.red;
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: color),
|
||||
Text('$snr dB', style: TextStyle(fontSize: 10, color: color)),
|
||||
],
|
||||
);
|
||||
SNRUi snrUiFromSNR(double? snr, int? spreadingFactor) {
|
||||
if (snr == null || spreadingFactor == null) {
|
||||
return const SNRUi(Icons.signal_cellular_off, Colors.grey, '—');
|
||||
}
|
||||
|
||||
final snrLevels = getSNRfromSF(spreadingFactor);
|
||||
|
||||
IconData icon;
|
||||
Color color;
|
||||
String text = '${snr.toStringAsFixed(1)} dB';
|
||||
|
||||
if (snr >= snrLevels[0]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.green;
|
||||
} else if (snr >= snrLevels[1]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.lightGreen;
|
||||
} else if (snr >= snrLevels[2]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.yellow;
|
||||
} else if (snr >= snrLevels[3]) {
|
||||
icon = Icons.signal_cellular_alt_2_bar;
|
||||
color = Colors.orange;
|
||||
} else {
|
||||
icon = Icons.signal_cellular_alt_1_bar;
|
||||
color = Colors.red;
|
||||
}
|
||||
|
||||
return SNRUi(icon, color, text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user