| .. | .. |
|---|
| 1 | +import 'dart:convert'; |
|---|
| 2 | + |
|---|
| 1 | 3 | import 'package:flutter/foundation.dart'; |
|---|
| 4 | +import 'package:mqtt_client/mqtt_client.dart'; |
|---|
| 5 | +import 'package:mqtt_client/mqtt_server_client.dart'; |
|---|
| 6 | + |
|---|
| 7 | +import 'mqtt_service.dart'; |
|---|
| 2 | 8 | |
|---|
| 3 | 9 | /// A single trace entry capturing a message-handling event. |
|---|
| 4 | 10 | class TraceEntry { |
|---|
| .. | .. |
|---|
| 22 | 28 | /// Captures message-handling events from MQTT, chat screen, and other |
|---|
| 23 | 29 | /// components. The buffer is capped at [maxEntries] (default 200). |
|---|
| 24 | 30 | /// Works in both debug and release builds. |
|---|
| 31 | +/// |
|---|
| 32 | +/// When an MqttService is attached via [attachMqtt], trace entries are |
|---|
| 33 | +/// automatically published to the server on `pailot/control/in` so they |
|---|
| 34 | +/// can be read from the daemon log. |
|---|
| 25 | 35 | class TraceService { |
|---|
| 26 | 36 | TraceService._(); |
|---|
| 27 | 37 | static final TraceService instance = TraceService._(); |
|---|
| 28 | 38 | |
|---|
| 29 | 39 | static const int maxEntries = 200; |
|---|
| 30 | 40 | final List<TraceEntry> _entries = []; |
|---|
| 41 | + MqttService? _mqtt; |
|---|
| 42 | + |
|---|
| 43 | + /// Attach an MQTT service for auto-publishing traces to the server. |
|---|
| 44 | + void attachMqtt(MqttService mqtt) { |
|---|
| 45 | + _mqtt = mqtt; |
|---|
| 46 | + } |
|---|
| 31 | 47 | |
|---|
| 32 | 48 | /// All entries, oldest first. |
|---|
| 33 | 49 | List<TraceEntry> get entries => List.unmodifiable(_entries); |
|---|
| 34 | 50 | |
|---|
| 35 | 51 | /// Add a trace entry. Oldest entry is evicted once the buffer is full. |
|---|
| 52 | + /// If MQTT is attached and connected, the entry is also published to the server. |
|---|
| 36 | 53 | void addTrace(String event, String details) { |
|---|
| 37 | 54 | _entries.add(TraceEntry( |
|---|
| 38 | 55 | timestamp: DateTime.now(), |
|---|
| .. | .. |
|---|
| 43 | 60 | _entries.removeAt(0); |
|---|
| 44 | 61 | } |
|---|
| 45 | 62 | debugPrint('[TRACE] $event — $details'); |
|---|
| 63 | + |
|---|
| 64 | + // Auto-publish to server if MQTT is connected |
|---|
| 65 | + _publishTrace(event, details); |
|---|
| 66 | + } |
|---|
| 67 | + |
|---|
| 68 | + void _publishTrace(String event, String details) { |
|---|
| 69 | + final mqtt = _mqtt; |
|---|
| 70 | + if (mqtt == null || !mqtt.isConnected) return; |
|---|
| 71 | + try { |
|---|
| 72 | + final payload = jsonEncode({ |
|---|
| 73 | + 'type': 'command', |
|---|
| 74 | + 'command': 'app_trace', |
|---|
| 75 | + 'event': event, |
|---|
| 76 | + 'details': details, |
|---|
| 77 | + 'ts': DateTime.now().millisecondsSinceEpoch, |
|---|
| 78 | + }); |
|---|
| 79 | + final builder = MqttClientPayloadBuilder(); |
|---|
| 80 | + builder.addString(payload); |
|---|
| 81 | + mqtt.publishRaw('pailot/control/in', builder.payload!, MqttQos.atMostOnce); |
|---|
| 82 | + } catch (_) { |
|---|
| 83 | + // Non-fatal — don't let trace logging break the app |
|---|
| 84 | + } |
|---|
| 46 | 85 | } |
|---|
| 47 | 86 | |
|---|
| 48 | 87 | /// Clear all entries. |
|---|