mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00: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. * Fix trace route bytes generation logic in Contact model * Ignore advertisements from self in MeshCoreConnector * Refactor PathTraceData to use List<double> for snrData and adjust data mapping in PathTraceMapScreen * Add SNRIndicator to AppBar and refactor BatteryIndicator layout * Enhance path management dialog to display direct repeaters with color coding based on signal strength * Remove unused import from SNR indicator widget * Update lib/models/contact.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update lib/connector/meshcore_connector.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update lib/connector/meshcore_connector.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update lib/screens/path_trace_map.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update lib/widgets/battery_indicator.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update lib/helpers/cayenne_lpp.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Refactor packet handling to skip only the RSSI byte for improved reliability * Add SNR indicator localization and update UI references for nearby repeaters * Handle loading state and error parsing in PathTraceMapScreen; update SNR indicator dialog content layout * Throw an exception for unsupported LPP types in CayenneLpp class * Refactor AppBarTitle widget to remove unused style parameter; update related screens to reflect changes Improve SNR handling by adding validation for spreading factor range in snrUiFromSNR function Update contact handling in MeshCoreConnector to fix variable naming and improve readability Stop parsing unsupported LPP types in CayenneLpp to avoid misalignment * Sort direct repeaters by last updated time and SNR; limit to top three for improved path management dialog * Prevent notifications for chat and sensor adverts without a valid path * Implement ranking system for direct repeaters based on SNR and recency; update related UI components to reflect changes * Refactor localization keys for "neighbors" terminology across multiple languages - Updated localization keys from "neighbours" to "neighbors" in the following files: - app_localizations_bg.dart - app_localizations_de.dart - app_localizations_en.dart - app_localizations_es.dart - app_localizations_fr.dart - app_localizations_it.dart - app_localizations_nl.dart - app_localizations_pl.dart - app_localizations_pt.dart - app_localizations_ru.dart - app_localizations_sk.dart - app_localizations_sl.dart - app_localizations_sv.dart - app_localizations_uk.dart - app_localizations_zh.dart - Updated corresponding ARB files to reflect the changes in keys. - Renamed the NeighboursScreen to NeighborsScreen in the chat and repeater hub screens for consistency. * Adjust ranking calculation for direct repeaters by adding offset to SNR for improved accuracy * Fix typo in variable name for second direct repeater in path management dialog * Refactor ranking calculation for direct repeaters and update path handling in channel message screens * Refactor path handling in ChannelMessagePathScreen to improve logic for outgoing messages and channel messages * Fix AppBarTitle horizontal overflow with long titles (#187) * Initial plan * Wrap title Column in Expanded to prevent horizontal overflow in AppBarTitle Co-authored-by: wel97459 <12990640+wel97459@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: wel97459 <12990640+wel97459@users.noreply.github.com> * Refactor AppBarTitle widget to simplify Text widget initialization * Add "Show All Paths" feature to chat path management - Implemented localization for "Show All Paths" in multiple languages (DE, EN, ES, FR, IT, NL, PL, PT, RU, SK, SL, SV, UK, ZH). - Updated path management dialog to include a toggle for showing all paths. - Refactored path history display logic to conditionally show paths based on the toggle state. - Cleaned up unused print statements and improved code readability in path tracing and chat screens. * Refactor FeatureToggleRow visibility in chat and path management dialogs based on repeaters list * Remove unused import of 'dart:ffi' in path_trace_map.dart * Refactor repeater management logic and update UI state handling in chat and path management dialogs * Refactor RX data handling and improve repeater management logic in chat and path management dialogs --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: wel97459 <12990640+wel97459@users.noreply.github.com>
277 lines
9.2 KiB
Dart
277 lines
9.2 KiB
Dart
import 'dart:typed_data';
|
|
import 'package:meshcore_open/utils/app_logger.dart';
|
|
|
|
import '../connector/meshcore_protocol.dart';
|
|
|
|
class CayenneLpp {
|
|
static const int lppDigitalInput = 0; // 1 byte
|
|
static const int lppDigitalOutput = 1; // 1 byte
|
|
static const int lppAnalogInput = 2; // 2 bytes, 0.01 signed
|
|
static const int lppAnalogOutput = 3; // 2 bytes, 0.01 signed
|
|
static const int lppGenericSensor = 100; // 4 bytes, unsigned
|
|
static const int lppLuminosity = 101; // 2 bytes, 1 lux unsigned
|
|
static const int lppPresence = 102; // 1 byte, bool
|
|
static const int lppTemperature = 103; // 2 bytes, 0.1°C signed
|
|
static const int lppRelativeHumidity = 104; // 1 byte, 0.5% unsigned
|
|
static const int lppAccelerometer = 113; // 2 bytes per axis, 0.001G
|
|
static const int lppBarometricPressure = 115; // 2 bytes 0.1hPa unsigned
|
|
static const int lppVoltage = 116; // 2 bytes 0.01V unsigned
|
|
static const int lppCurrent = 117; // 2 bytes 0.001A unsigned
|
|
static const int lppFrequency = 118; // 4 bytes 1Hz unsigned
|
|
static const int lppPercentage = 120; // 1 byte 1-100% unsigned
|
|
static const int lppAltitude = 121; // 2 byte 1m signed
|
|
static const int lppConcentration = 125; // 2 bytes, 1 ppm unsigned
|
|
static const int lppPower = 128; // 2 byte, 1W, unsigned
|
|
static const int lppDistance = 130; // 4 byte, 0.001m, unsigned
|
|
static const int lppEnergy = 131; // 4 byte, 0.001kWh, unsigned
|
|
static const int lppDirection = 132; // 2 bytes, 1deg, unsigned
|
|
static const int lppUnixTime = 133; // 4 bytes, unsigned
|
|
static const int lppGyrometer = 134; // 2 bytes per axis, 0.01 °/s
|
|
static const int lppColour = 135; // 1 byte per RGB Color
|
|
static const int lppGps =
|
|
136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
|
|
static const int lppSwitch = 142; // 1 byte, 0/1
|
|
static const int lppPolyline =
|
|
240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
|
|
|
|
final BufferWriter _writer = BufferWriter();
|
|
|
|
Uint8List toBytes() {
|
|
return _writer.toBytes();
|
|
}
|
|
|
|
void addDigitalInput(int channel, int value) {
|
|
_writer.writeByte(channel);
|
|
_writer.writeByte(lppDigitalInput);
|
|
_writer.writeByte(value);
|
|
}
|
|
|
|
void addTemperature(int channel, double value) {
|
|
_writer.writeByte(channel);
|
|
_writer.writeByte(lppTemperature);
|
|
final val = (value * 10).toInt();
|
|
_writer.writeBytes(_int16ToBE(val));
|
|
}
|
|
|
|
void addVoltage(int channel, double value) {
|
|
_writer.writeByte(channel);
|
|
_writer.writeByte(lppVoltage);
|
|
final val = (value * 100).toInt();
|
|
_writer.writeBytes(_int16ToBE(val));
|
|
}
|
|
|
|
void addGps(int channel, double lat, double lon, double alt) {
|
|
_writer.writeByte(channel);
|
|
_writer.writeByte(lppGps);
|
|
_writer.writeBytes(_int24ToBE((lat * 10000).toInt()));
|
|
_writer.writeBytes(_int24ToBE((lon * 10000).toInt()));
|
|
_writer.writeBytes(_int24ToBE((alt * 100).toInt()));
|
|
}
|
|
|
|
Uint8List _int16ToBE(int value) {
|
|
final bytes = Uint8List(2);
|
|
final data = ByteData.view(bytes.buffer);
|
|
data.setInt16(0, value, Endian.big);
|
|
return bytes;
|
|
}
|
|
|
|
Uint8List _int24ToBE(int value) {
|
|
final bytes = Uint8List(3);
|
|
bytes[0] = (value >> 16) & 0xFF;
|
|
bytes[1] = (value >> 8) & 0xFF;
|
|
bytes[2] = value & 0xFF;
|
|
return bytes;
|
|
}
|
|
|
|
static List<Map<String, dynamic>> parse(Uint8List bytes) {
|
|
final buffer = BufferReader(bytes);
|
|
final telemetry = <Map<String, dynamic>>[];
|
|
try {
|
|
while (buffer.remaining >= 2) {
|
|
final channel = buffer.readUInt8();
|
|
final type = buffer.readUInt8();
|
|
|
|
if (channel == 0 && type == 0) {
|
|
break;
|
|
}
|
|
|
|
switch (type) {
|
|
case lppGenericSensor:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt32BE(),
|
|
});
|
|
break;
|
|
case lppLuminosity:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt16BE(),
|
|
});
|
|
break;
|
|
case lppPresence:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt8(),
|
|
});
|
|
break;
|
|
case lppTemperature:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readInt16BE() / 10,
|
|
});
|
|
break;
|
|
case lppRelativeHumidity:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt8() / 2,
|
|
});
|
|
break;
|
|
case lppBarometricPressure:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt16BE() / 10,
|
|
});
|
|
break;
|
|
case lppVoltage:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readInt16BE() / 100,
|
|
});
|
|
break;
|
|
case lppCurrent:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readInt16BE() / 1000,
|
|
});
|
|
break;
|
|
case lppPercentage:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt8(),
|
|
});
|
|
break;
|
|
case lppConcentration:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt16BE(),
|
|
});
|
|
break;
|
|
case lppPower:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': buffer.readUInt16BE(),
|
|
});
|
|
break;
|
|
case lppGps:
|
|
telemetry.add({
|
|
'channel': channel,
|
|
'type': type,
|
|
'value': {
|
|
'latitude': buffer.readInt24BE() / 10000,
|
|
'longitude': buffer.readInt24BE() / 10000,
|
|
'altitude': buffer.readInt24BE() / 100,
|
|
},
|
|
});
|
|
break;
|
|
default:
|
|
return telemetry;
|
|
}
|
|
}
|
|
return telemetry;
|
|
} catch (e) {
|
|
// Handle parsing errors, possibly due to malformed data
|
|
appLogger.error('Error parsing Cayenne LPP data: $e');
|
|
// Return any telemetry parsed so far to preserve partial data
|
|
return telemetry;
|
|
}
|
|
}
|
|
|
|
static List<Map<String, dynamic>> parseByChannel(Uint8List bytes) {
|
|
final buffer = BufferReader(bytes);
|
|
final Map<int, Map<String, dynamic>> channels = {};
|
|
try {
|
|
while (buffer.remaining >= 2) {
|
|
final channel = buffer.readUInt8();
|
|
final type = buffer.readUInt8();
|
|
|
|
// Optional: stop on padding (00 00)
|
|
if (channel == 0 && type == 0) {
|
|
break;
|
|
}
|
|
|
|
final channelData = channels.putIfAbsent(
|
|
channel,
|
|
() => {'channel': channel, 'values': <String, dynamic>{}},
|
|
);
|
|
|
|
switch (type) {
|
|
case lppGenericSensor:
|
|
channelData['values']['generic'] = buffer.readUInt32BE();
|
|
break;
|
|
case lppLuminosity:
|
|
channelData['values']['luminosity'] = buffer.readUInt16BE();
|
|
break;
|
|
case lppPresence:
|
|
channelData['values']['presence'] = buffer.readUInt8() != 0;
|
|
break;
|
|
case lppTemperature:
|
|
channelData['values']['temperature'] = buffer.readInt16BE() / 10.0;
|
|
break;
|
|
case lppRelativeHumidity:
|
|
channelData['values']['humidity'] = buffer.readUInt8() / 2.0;
|
|
break;
|
|
case lppBarometricPressure:
|
|
channelData['values']['pressure'] = buffer.readUInt16BE() / 10.0;
|
|
break;
|
|
case lppVoltage:
|
|
channelData['values']['voltage'] = buffer.readInt16BE() / 100.0;
|
|
break;
|
|
case lppCurrent:
|
|
channelData['values']['current'] = buffer.readInt16BE() / 1000.0;
|
|
break;
|
|
case lppPercentage:
|
|
channelData['values']['percentage'] = buffer.readUInt8();
|
|
break;
|
|
case lppConcentration:
|
|
channelData['values']['concentration'] = buffer.readUInt16BE();
|
|
break;
|
|
case lppPower:
|
|
channelData['values']['power'] = buffer.readUInt16BE();
|
|
break;
|
|
case lppGps:
|
|
channelData['values']['gps'] = {
|
|
'latitude': buffer.readInt24BE() / 10000.0,
|
|
'longitude': buffer.readInt24BE() / 10000.0,
|
|
'altitude': buffer.readInt24BE() / 100.0,
|
|
};
|
|
break;
|
|
// Add more types as needed...
|
|
default:
|
|
//Stopped parsing to avoid misalignment
|
|
return channels.values.toList();
|
|
}
|
|
}
|
|
|
|
final List<Map<String, dynamic>> channelsOut = channels.values.toList();
|
|
channelsOut.sort((a, b) => a['channel'].compareTo(b['channel']));
|
|
return channelsOut;
|
|
} catch (e) {
|
|
// Handle parsing errors, possibly due to malformed data
|
|
appLogger.error('Error parsing Cayenne LPP data: $e');
|
|
return <
|
|
Map<String, dynamic>
|
|
>[]; // Return an empty list on error to avoid crashing the app
|
|
}
|
|
}
|
|
}
|