mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Fix telemetry merge bug
This commit is contained in:
@@ -878,7 +878,16 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
' ⚠️ Retaining last valid GPS. Incoming telemetry GPS is invalid/missing: $incomingGps',
|
' ⚠️ Retaining last valid GPS. Incoming telemetry GPS is invalid/missing: $incomingGps',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
telemetry = mergedTelemetry;
|
telemetry = ContactTelemetry(
|
||||||
|
gpsLocation: telemetry.gpsLocation,
|
||||||
|
batteryPercentage: mergedTelemetry.batteryPercentage,
|
||||||
|
batteryMilliVolts: mergedTelemetry.batteryMilliVolts,
|
||||||
|
temperature: mergedTelemetry.temperature,
|
||||||
|
timestamp: mergedTelemetry.timestamp,
|
||||||
|
humidity: mergedTelemetry.humidity,
|
||||||
|
pressure: mergedTelemetry.pressure,
|
||||||
|
extraSensorData: telemetry.extraSensorData,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update contact with new telemetry AND last seen time
|
// Update contact with new telemetry AND last seen time
|
||||||
@@ -993,10 +1002,10 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
// last known reading for any field that is omitted in the incoming update.
|
// last known reading for any field that is omitted in the incoming update.
|
||||||
final incomingGps = _getValidGpsOrNull(incomingTelemetry.gpsLocation);
|
final incomingGps = _getValidGpsOrNull(incomingTelemetry.gpsLocation);
|
||||||
final previousGps = _getValidGpsOrNull(existingTelemetry?.gpsLocation);
|
final previousGps = _getValidGpsOrNull(existingTelemetry?.gpsLocation);
|
||||||
final mergedExtraSensorData = <String, dynamic>{
|
final mergedExtraSensorData = _mergeExtraSensorData(
|
||||||
...?existingTelemetry?.extraSensorData,
|
existingTelemetry?.extraSensorData,
|
||||||
...?incomingTelemetry.extraSensorData,
|
incomingTelemetry.extraSensorData,
|
||||||
};
|
);
|
||||||
|
|
||||||
return ContactTelemetry(
|
return ContactTelemetry(
|
||||||
gpsLocation: incomingGps ?? previousGps,
|
gpsLocation: incomingGps ?? previousGps,
|
||||||
@@ -1017,6 +1026,49 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _mergeExtraSensorData(
|
||||||
|
Map<String, dynamic>? existingExtraSensorData,
|
||||||
|
Map<String, dynamic>? incomingExtraSensorData,
|
||||||
|
) {
|
||||||
|
final merged = <String, dynamic>{...?existingExtraSensorData};
|
||||||
|
if (incomingExtraSensorData == null || incomingExtraSensorData.isEmpty) {
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
final incomingMetricFamilies = incomingExtraSensorData.keys
|
||||||
|
.map(_telemetryMetricFamilyForKey)
|
||||||
|
.whereType<String>()
|
||||||
|
.toSet();
|
||||||
|
if (incomingMetricFamilies.isNotEmpty) {
|
||||||
|
merged.removeWhere((key, _) {
|
||||||
|
final family = _telemetryMetricFamilyForKey(key);
|
||||||
|
return family != null && incomingMetricFamilies.contains(family);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.addAll(incomingExtraSensorData);
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _telemetryMetricFamilyForKey(String key) {
|
||||||
|
const sourcePrefix = '__source_channel:';
|
||||||
|
if (key.startsWith(sourcePrefix)) {
|
||||||
|
return key.substring(sourcePrefix.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
final separatorIndex = key.lastIndexOf('_');
|
||||||
|
if (separatorIndex <= 0 || separatorIndex == key.length - 1) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
final suffix = key.substring(separatorIndex + 1);
|
||||||
|
if (int.tryParse(suffix) == null) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.substring(0, separatorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
int _coordinateToAdvertMicrodegrees(double coordinate) {
|
int _coordinateToAdvertMicrodegrees(double coordinate) {
|
||||||
return (coordinate * 1e6).round();
|
return (coordinate * 1e6).round();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ void main() {
|
|||||||
expect(updated.telemetry!.extraSensorData, containsPair('co2', 415.0));
|
expect(updated.telemetry!.extraSensorData, containsPair('co2', 415.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('retains prior telemetry fields across sparse telemetry updates', () {
|
test('retains scalar telemetry but wipes stale extra sensor fields on refresh', () {
|
||||||
final fullTelemetry = ContactTelemetry(
|
final fullTelemetry = ContactTelemetry(
|
||||||
gpsLocation: const LatLng(46.0569, 14.5058),
|
gpsLocation: const LatLng(46.0569, 14.5058),
|
||||||
batteryPercentage: 54.0,
|
batteryPercentage: 54.0,
|
||||||
@@ -366,7 +366,55 @@ void main() {
|
|||||||
expect(updated.telemetry!.temperature, equals(19.5));
|
expect(updated.telemetry!.temperature, equals(19.5));
|
||||||
expect(updated.telemetry!.humidity, equals(58.0));
|
expect(updated.telemetry!.humidity, equals(58.0));
|
||||||
expect(updated.telemetry!.pressure, equals(1011.2));
|
expect(updated.telemetry!.pressure, equals(1011.2));
|
||||||
expect(updated.telemetry!.extraSensorData, containsPair('pm25', 8.0));
|
expect(updated.telemetry!.extraSensorData, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('replaces old source-channel mappings when a metric moves channels', () {
|
||||||
|
final initialTelemetry = ContactTelemetry(
|
||||||
|
gpsLocation: null,
|
||||||
|
batteryPercentage: null,
|
||||||
|
batteryMilliVolts: null,
|
||||||
|
temperature: 21.5,
|
||||||
|
timestamp: DateTime.now().subtract(const Duration(minutes: 2)),
|
||||||
|
humidity: null,
|
||||||
|
pressure: null,
|
||||||
|
extraSensorData: const {
|
||||||
|
'__source_channel:temperature': 2,
|
||||||
|
'temperature_2': 21.5,
|
||||||
|
'humidity_4': 66.0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
provider.addOrUpdateContact(
|
||||||
|
createContact(
|
||||||
|
key: publicKey,
|
||||||
|
type: ContactType.chat,
|
||||||
|
).copyWith(telemetry: initialTelemetry),
|
||||||
|
);
|
||||||
|
|
||||||
|
final movedChannelTelemetry = CayenneLppParser.createTemperatureData(
|
||||||
|
23.5,
|
||||||
|
channel: 3,
|
||||||
|
);
|
||||||
|
|
||||||
|
provider.updateTelemetry(publicKey.sublist(0, 6), movedChannelTelemetry);
|
||||||
|
|
||||||
|
final updated = provider.findContactByKey(publicKey)!;
|
||||||
|
expect(updated.telemetry, isNotNull);
|
||||||
|
expect(updated.telemetry!.temperature, closeTo(23.5, 0.1));
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.extraSensorData,
|
||||||
|
containsPair('__source_channel:temperature', 3),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.extraSensorData,
|
||||||
|
containsPair('temperature_3', closeTo(23.5, 0.1)),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.extraSensorData,
|
||||||
|
isNot(contains('temperature_2')),
|
||||||
|
);
|
||||||
|
expect(updated.telemetry!.extraSensorData, isNot(contains('humidity_4')));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('builds message snapshot from latest valid telemetry', () {
|
test('builds message snapshot from latest valid telemetry', () {
|
||||||
|
|||||||
Reference in New Issue
Block a user