mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
formats all dart files using `dart format .` from the root project dir this makes the code style repeatable by new contributors and makes PR review easier
258 lines
7.3 KiB
Dart
258 lines
7.3 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import '../connector/meshcore_protocol.dart';
|
|
|
|
class BleDebugLogEntry {
|
|
final DateTime timestamp;
|
|
final bool outgoing;
|
|
final String description;
|
|
final Uint8List payload;
|
|
|
|
BleDebugLogEntry({
|
|
required this.timestamp,
|
|
required this.outgoing,
|
|
required this.description,
|
|
required this.payload,
|
|
});
|
|
|
|
String get hexPreview {
|
|
const maxBytes = 64;
|
|
final bytes = payload.length > maxBytes
|
|
? payload.sublist(0, maxBytes)
|
|
: payload;
|
|
final hex = bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ');
|
|
return payload.length > maxBytes ? '$hex …' : hex;
|
|
}
|
|
}
|
|
|
|
class BleRawLogRxEntry {
|
|
final DateTime timestamp;
|
|
final Uint8List payload;
|
|
|
|
BleRawLogRxEntry({required this.timestamp, required this.payload});
|
|
|
|
String get hexPreview {
|
|
const maxBytes = 64;
|
|
final bytes = payload.length > maxBytes
|
|
? payload.sublist(0, maxBytes)
|
|
: payload;
|
|
final hex = bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ');
|
|
return payload.length > maxBytes ? '$hex …' : hex;
|
|
}
|
|
}
|
|
|
|
class BleDebugLogService extends ChangeNotifier {
|
|
static const int maxEntries = 500;
|
|
final List<BleDebugLogEntry> _entries = [];
|
|
final List<BleRawLogRxEntry> _rawLogRxEntries = [];
|
|
|
|
List<BleDebugLogEntry> get entries => List.unmodifiable(_entries);
|
|
List<BleRawLogRxEntry> get rawLogRxEntries =>
|
|
List.unmodifiable(_rawLogRxEntries);
|
|
|
|
void logFrame(Uint8List frame, {required bool outgoing, String? note}) {
|
|
if (frame.isEmpty) return;
|
|
final code = frame[0];
|
|
final description = _describeFrame(code, frame, outgoing, note);
|
|
_entries.add(
|
|
BleDebugLogEntry(
|
|
timestamp: DateTime.now(),
|
|
outgoing: outgoing,
|
|
description: description,
|
|
payload: Uint8List.fromList(frame),
|
|
),
|
|
);
|
|
|
|
if (_entries.length > maxEntries) {
|
|
_entries.removeRange(0, _entries.length - maxEntries);
|
|
}
|
|
|
|
if (!outgoing && code == pushCodeLogRxData && frame.length > 3) {
|
|
_rawLogRxEntries.add(
|
|
BleRawLogRxEntry(
|
|
timestamp: DateTime.now(),
|
|
payload: Uint8List.fromList(frame.sublist(3)),
|
|
),
|
|
);
|
|
if (_rawLogRxEntries.length > maxEntries) {
|
|
_rawLogRxEntries.removeRange(0, _rawLogRxEntries.length - maxEntries);
|
|
}
|
|
}
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
void clear() {
|
|
_entries.clear();
|
|
_rawLogRxEntries.clear();
|
|
notifyListeners();
|
|
}
|
|
|
|
String _describeFrame(
|
|
int code,
|
|
Uint8List frame,
|
|
bool outgoing,
|
|
String? note,
|
|
) {
|
|
final label = _codeLabel(code, outgoing: outgoing);
|
|
final prefix = outgoing ? 'TX' : 'RX';
|
|
final extra = _frameDetail(code, frame);
|
|
final noteText = note != null ? ' • $note' : '';
|
|
return '$prefix $label$extra$noteText';
|
|
}
|
|
|
|
String _codeLabel(int code, {required bool outgoing}) {
|
|
if (outgoing) {
|
|
return _commandLabel(code) ?? 'CODE_$code';
|
|
}
|
|
|
|
final pushLabel = _pushLabel(code);
|
|
if (pushLabel != null) return pushLabel;
|
|
final responseLabel = _responseLabel(code);
|
|
if (responseLabel != null) return responseLabel;
|
|
return 'CODE_$code';
|
|
}
|
|
|
|
String? _commandLabel(int code) {
|
|
switch (code) {
|
|
case cmdAppStart:
|
|
return 'CMD_APP_START';
|
|
case cmdSendTxtMsg:
|
|
return 'CMD_SEND_TXT_MSG';
|
|
case cmdSendChannelTxtMsg:
|
|
return 'CMD_SEND_CHANNEL_TXT_MSG';
|
|
case cmdGetContacts:
|
|
return 'CMD_GET_CONTACTS';
|
|
case cmdGetDeviceTime:
|
|
return 'CMD_GET_DEVICE_TIME';
|
|
case cmdSetDeviceTime:
|
|
return 'CMD_SET_DEVICE_TIME';
|
|
case cmdSendSelfAdvert:
|
|
return 'CMD_SEND_SELF_ADVERT';
|
|
case cmdSetAdvertName:
|
|
return 'CMD_SET_ADVERT_NAME';
|
|
case cmdAddUpdateContact:
|
|
return 'CMD_ADD_UPDATE_CONTACT';
|
|
case cmdSyncNextMessage:
|
|
return 'CMD_SYNC_NEXT_MESSAGE';
|
|
case cmdSetRadioParams:
|
|
return 'CMD_SET_RADIO_PARAMS';
|
|
case cmdSetRadioTxPower:
|
|
return 'CMD_SET_RADIO_TX_POWER';
|
|
case cmdResetPath:
|
|
return 'CMD_RESET_PATH';
|
|
case cmdRemoveContact:
|
|
return 'CMD_REMOVE_CONTACT';
|
|
case cmdReboot:
|
|
return 'CMD_REBOOT';
|
|
case cmdGetBattAndStorage:
|
|
return 'CMD_GET_BATT_AND_STORAGE';
|
|
case cmdSendLogin:
|
|
return 'CMD_SEND_LOGIN';
|
|
case cmdGetChannel:
|
|
return 'CMD_GET_CHANNEL';
|
|
case cmdSetChannel:
|
|
return 'CMD_SET_CHANNEL';
|
|
case cmdGetRadioSettings:
|
|
return 'CMD_GET_RADIO_SETTINGS';
|
|
case cmdSetCustomVar:
|
|
return 'CMD_SET_CUSTOM_VAR';
|
|
case cmdSendTracePath:
|
|
return 'CMD_SEND_TRACE_PATH';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String? _responseLabel(int code) {
|
|
switch (code) {
|
|
case respCodeOk:
|
|
return 'RESP_CODE_OK';
|
|
case respCodeErr:
|
|
return 'RESP_CODE_ERR';
|
|
case respCodeContactsStart:
|
|
return 'RESP_CODE_CONTACTS_START';
|
|
case respCodeContact:
|
|
return 'RESP_CODE_CONTACT';
|
|
case respCodeEndOfContacts:
|
|
return 'RESP_CODE_END_OF_CONTACTS';
|
|
case respCodeSelfInfo:
|
|
return 'RESP_CODE_SELF_INFO';
|
|
case respCodeSent:
|
|
return 'RESP_CODE_SENT';
|
|
case respCodeContactMsgRecv:
|
|
return 'RESP_CODE_CONTACT_MSG_RECV';
|
|
case respCodeChannelMsgRecv:
|
|
return 'RESP_CODE_CHANNEL_MSG_RECV';
|
|
case respCodeCurrTime:
|
|
return 'RESP_CODE_CURR_TIME';
|
|
case respCodeNoMoreMessages:
|
|
return 'RESP_CODE_NO_MORE_MESSAGES';
|
|
case respCodeBattAndStorage:
|
|
return 'RESP_CODE_BATT_AND_STORAGE';
|
|
case respCodeContactMsgRecvV3:
|
|
return 'RESP_CODE_CONTACT_MSG_RECV_V3';
|
|
case respCodeChannelMsgRecvV3:
|
|
return 'RESP_CODE_CHANNEL_MSG_RECV_V3';
|
|
case respCodeChannelInfo:
|
|
return 'RESP_CODE_CHANNEL_INFO';
|
|
case respCodeRadioSettings:
|
|
return 'RESP_CODE_RADIO_SETTINGS';
|
|
case pushCodeTraceData:
|
|
return 'PUSH_CODE_TRACE_DATA';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String? _pushLabel(int code) {
|
|
switch (code) {
|
|
case pushCodeAdvert:
|
|
return 'PUSH_CODE_ADVERT';
|
|
case pushCodePathUpdated:
|
|
return 'PUSH_CODE_PATH_UPDATED';
|
|
case pushCodeSendConfirmed:
|
|
return 'PUSH_CODE_SEND_CONFIRMED';
|
|
case pushCodeMsgWaiting:
|
|
return 'PUSH_CODE_MSG_WAITING';
|
|
case pushCodeLoginSuccess:
|
|
return 'PUSH_CODE_LOGIN_SUCCESS';
|
|
case pushCodeLoginFail:
|
|
return 'PUSH_CODE_LOGIN_FAIL';
|
|
case pushCodeLogRxData:
|
|
return 'PUSH_CODE_LOG_RX_DATA';
|
|
case pushCodeNewAdvert:
|
|
return 'PUSH_CODE_NEW_ADVERT';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String _frameDetail(int code, Uint8List frame) {
|
|
switch (code) {
|
|
case respCodeSent:
|
|
if (frame.length >= 10) {
|
|
final timeoutMs = readUint32LE(frame, 6);
|
|
return ' • timeout=${timeoutMs}ms';
|
|
}
|
|
return '';
|
|
case pushCodeSendConfirmed:
|
|
if (frame.length >= 9) {
|
|
final tripMs = readUint32LE(frame, 5);
|
|
return ' • trip=${tripMs}ms';
|
|
}
|
|
return '';
|
|
case pushCodeLoginSuccess:
|
|
return ' • login ok';
|
|
case pushCodeLoginFail:
|
|
return ' • login fail';
|
|
case respCodeBattAndStorage:
|
|
if (frame.length >= 3) {
|
|
final mv = readUint16LE(frame, 1);
|
|
return ' • ${mv}mV';
|
|
}
|
|
return '';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
}
|