Files
votianlt/MQTT_QUICK_REFERENCE.md
2025-10-23 12:18:42 +02:00

8.5 KiB

MQTT Quick Reference - Client Migration

🚨 Kritische Änderungen

Port-Änderung (WICHTIG!)

Alt: mqtt-2.assecutor.de:1883
Neu: mqtt-2.assecutor.de:42099

Authentifizierung (NEU!)

Username: app
Password: apppwd

Timeout-Erhöhung

Connection Timeout: 60 Sekunden (vorher 30s)
Keep-Alive: 60 Sekunden (vorher 30s)

📋 Flutter/Dart Code-Snippets

1. Verbindung herstellen

import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

Future<MqttServerClient> connectToMqtt(String clientId) async {
  final client = MqttServerClient.withPort(
    'mqtt-2.assecutor.de',
    clientId,
    42099,  // NEUER PORT!
  );
  
  client.keepAlivePeriod = 60;
  client.connectTimeoutPeriod = 60000;
  client.autoReconnect = true;
  client.setProtocolV311();
  
  final connMessage = MqttConnectMessage()
      .withClientIdentifier(clientId)
      .authenticateAs('app', 'apppwd')  // NEU: Authentifizierung
      .withWillQos(MqttQos.atLeastOnce)
      .startClean()
      .keepAliveFor(60);
  
  client.connectionMessage = connMessage;
  
  await client.connect();
  return client;
}

2. Message Envelope (NEU!)

class MessageEnvelope {
  final String messageId;
  final DateTime timestamp;
  final String topic;
  final Map<String, dynamic> payload;
  final bool requiresAck;
  final int retryCount;
  final DateTime? expiresAt;
  
  MessageEnvelope({
    required this.messageId,
    required this.timestamp,
    required this.topic,
    required this.payload,
    this.requiresAck = true,
    this.retryCount = 0,
    this.expiresAt,
  });
  
  factory MessageEnvelope.fromJson(Map<String, dynamic> json) {
    return MessageEnvelope(
      messageId: json['messageId'],
      timestamp: DateTime.parse(json['timestamp']),
      topic: json['topic'],
      payload: json['payload'],
      requiresAck: json['requiresAck'] ?? true,
      retryCount: json['retryCount'] ?? 0,
      expiresAt: json['expiresAt'] != null 
          ? DateTime.parse(json['expiresAt']) 
          : null,
    );
  }
  
  Map<String, dynamic> toJson() => {
    'messageId': messageId,
    'timestamp': timestamp.toIso8601String(),
    'topic': topic,
    'payload': payload,
    'requiresAck': requiresAck,
    'retryCount': retryCount,
    if (expiresAt != null) 'expiresAt': expiresAt!.toIso8601String(),
  };
}

3. Nachricht senden

import 'package:uuid/uuid.dart';
import 'dart:convert';

Future<void> sendMessage(
  MqttServerClient client,
  String clientId,
  String messageType,
  Map<String, dynamic> payload,
) async {
  final envelope = MessageEnvelope(
    messageId: Uuid().v4(),
    timestamp: DateTime.now(),
    topic: '/server/$clientId/$messageType',
    payload: payload,
    requiresAck: true,
  );
  
  final builder = MqttClientPayloadBuilder();
  builder.addString(jsonEncode(envelope.toJson()));
  
  client.publishMessage(
    envelope.topic,
    MqttQos.exactlyOnce,
    builder.payload!,
  );
}

// Beispiel-Verwendung:
await sendMessage(
  client,
  'app-user-123',
  'task_completed',
  {
    'taskId': '456',
    'status': 'completed',
    'completedAt': DateTime.now().toIso8601String(),
  },
);

4. Nachricht empfangen

void setupMessageListener(MqttServerClient client, String clientId) {
  // Subscribe zu allen relevanten Topics
  client.subscribe('/client/$clientId/#', MqttQos.exactlyOnce);
  client.subscribe('/ack/client/$clientId/#', MqttQos.exactlyOnce);
  
  client.updates!.listen((List<MqttReceivedMessage<MqttMessage>> messages) {
    for (var message in messages) {
      final topic = message.topic;
      final payloadString = MqttPublishPayload.bytesToStringAsString(
        (message.payload as MqttPublishMessage).payload.message,
      );
      
      try {
        final json = jsonDecode(payloadString);
        
        // Prüfen ob Envelope-Format
        if (json.containsKey('messageId') && json.containsKey('payload')) {
          final envelope = MessageEnvelope.fromJson(json);
          
          // Nachricht verarbeiten
          handleEnvelopeMessage(envelope);
          
          // ACK senden wenn erforderlich
          if (envelope.requiresAck) {
            sendAck(client, envelope.messageId, true);
          }
        } else {
          // Legacy-Format
          handleLegacyMessage(topic, json);
        }
      } catch (e) {
        print('Error processing message: $e');
      }
    }
  });
}

5. Acknowledgment senden (NEU!)

class AcknowledgmentMessage {
  final String messageId;
  final DateTime timestamp;
  final String status;
  final String? errorMessage;
  
  AcknowledgmentMessage({
    required this.messageId,
    required this.timestamp,
    required this.status,
    this.errorMessage,
  });
  
  Map<String, dynamic> toJson() => {
    'messageId': messageId,
    'timestamp': timestamp.toIso8601String(),
    'status': status,
    if (errorMessage != null) 'errorMessage': errorMessage,
  };
}

void sendAck(
  MqttServerClient client,
  String messageId,
  bool success,
  [String? errorMessage]
) {
  final ack = AcknowledgmentMessage(
    messageId: messageId,
    timestamp: DateTime.now(),
    status: success ? 'SUCCESS' : 'FAILED',
    errorMessage: errorMessage,
  );
  
  final topic = '/ack/server/$messageId';
  final builder = MqttClientPayloadBuilder();
  builder.addString(jsonEncode(ack.toJson()));
  
  client.publishMessage(
    topic,
    MqttQos.exactlyOnce,
    builder.payload!,
  );
}

📊 Topic-Struktur (unverändert)

Client → Server

/server/{clientId}/task_completed
/server/{clientId}/login
/server/{clientId}/message
/server/{clientId}/jobs/assigned

Server → Client

/client/{clientId}/jobs/assigned
/client/{clientId}/message
/client/{clientId}/task_update

Acknowledgments

Client → Server: /ack/server/{messageId}
Server → Client: /ack/client/{clientId}/{messageId}

🔍 Debugging

Verbindung testen

Future<void> testConnection() async {
  try {
    final client = await connectToMqtt('test-${Uuid().v4()}');
    
    if (client.connectionStatus!.state == MqttConnectionState.connected) {
      print('✅ Connection successful!');
      
      // Test-Nachricht senden
      await sendMessage(
        client,
        'test-client',
        'test',
        {'message': 'Hello from client'},
      );
      
      print('✅ Test message sent!');
    } else {
      print('❌ Connection failed: ${client.connectionStatus}');
    }
  } catch (e) {
    print('❌ Error: $e');
  }
}

Häufige Fehler

1. Connection Timeout

Fehler: TimeoutException after 60 seconds
Lösung: 
- Prüfen Sie, ob Port 42099 erreichbar ist
- Firewall-Einstellungen überprüfen
- Netzwerkverbindung testen

2. Authentication Failed

Fehler: Connection refused
Lösung:
- Username: 'app'
- Password: 'apppwd'
- Stellen Sie sicher, dass authenticateAs() aufgerufen wird

3. Message not received

Fehler: Keine Nachrichten empfangen
Lösung:
- Subscription überprüfen: client.subscribe('/client/$clientId/#', ...)
- Listener registriert: client.updates!.listen(...)
- Topic-Format korrekt: /client/{clientId}/{messageType}

📦 Dependencies

pubspec.yaml

dependencies:
  mqtt_client: ^10.2.0
  uuid: ^4.0.0

Migration Checklist

  • Port auf 42099 geändert
  • Authentifizierung hinzugefügt (app/apppwd)
  • Connection-Timeout auf 60s erhöht
  • Keep-Alive auf 60s gesetzt
  • MessageEnvelope-Klasse implementiert
  • AcknowledgmentMessage-Klasse implementiert
  • Envelope-basiertes Senden implementiert
  • Envelope-basiertes Empfangen implementiert
  • ACK-Handling implementiert
  • Fehlerbehandlung für ACKs implementiert
  • Verbindung getestet
  • Nachrichtenversand getestet
  • Nachrichtenempfang getestet

🔗 Weitere Dokumentation

  • Detaillierte Migration: MQTT_MIGRATION_GUIDE.md
  • Changelog: CHANGELOG_MQTT.md
  • Architektur: MESSAGING_LAYER.md
  • API-Referenz: MQTT_README.md

💡 Best Practices

  1. Eindeutige Client-IDs: Verwenden Sie stabile, geräte-spezifische IDs
  2. Fehlerbehandlung: Implementieren Sie Retry-Logik für fehlgeschlagene Verbindungen
  3. ACK-Timeout: Warten Sie maximal 30 Sekunden auf ACKs
  4. Message-Expiry: Prüfen Sie expiresAt vor der Verarbeitung
  5. Logging: Loggen Sie alle Verbindungsereignisse für Debugging

🆘 Support

Bei Problemen:

  1. Logs auf Client- und Server-Seite prüfen
  2. Netzwerk-Konnektivität testen (Port 42099)
  3. MQTT-Client-Tool verwenden (z.B. MQTT Explorer)
  4. Dokumentation konsultieren