connect method

Future<String?> connect({
  1. String? authToken,
})

Connect to Socket.IO server

Implementation

Future<String?> connect({String? authToken}) async {
  try {
    print('🔌 [SOCKET] Connecting to Socket.IO server...');

    // Disconnect existing connection if any
    disconnect();

    // Create socket connection.
    //
    // IMPORTANT: onairos-npm (web SDK) authenticates via `auth: { token }` and then emits
    // `start-training` after connect. We send auth both ways (handshake auth + header) for
    // maximum backend compatibility.
    final opts = <String, dynamic>{
      'transports': ['websocket', 'polling'],
      'reconnection': false,
      'timeout': 60000,
      if (authToken != null) 'auth': {'token': authToken},
      if (authToken != null) 'extraHeaders': {'Authorization': 'Bearer $authToken'},
    };
    _socket = IO.io('https://api2.onairos.uk', opts);

    // Create completer to wait for connection
    _connectionCompleter = Completer<String?>();

    // Set up event listeners
    _socket!.onConnect((_) {
      print('✅ [SOCKET] Connected to training server');
      _isConnected = true;
      _socketId = _socket!.id;
      print('🔑 [SOCKET] Socket ID: $_socketId');

      // Complete the connection completer
      if (!_connectionCompleter!.isCompleted) {
        _connectionCompleter!.complete(_socketId);
      }
    });

    _socket!.onDisconnect((_) {
      print('❌ [SOCKET] Disconnected from training server');
      _isConnected = false;
      _socketId = null;
    });

    _socket!.onConnectError((error) {
      print('❌ [SOCKET] Connection error: $error');
      _errorController.add('Connection failed: $error');

      // Complete with null on error
      if (!_connectionCompleter!.isCompleted) {
        _connectionCompleter!.complete(null);
      }
    });

    // Listen for training updates
    _socket!.on('trainingUpdate', (data) {
      print('📡 [SOCKET] Training update received: $data');
      _progressController.add(Map<String, dynamic>.from(data));

      if (data['status'] != null) {
        _statusController.add(data['status'].toString());
      }

      if (data['error'] != null) {
        _errorController.add(data['error'].toString());
      }
    });

    // Listen for training completion
    _socket!.on('trainingCompleted', (data) {
      print('✅ [SOCKET] Training completed: $data');
      _completionController.add({
        ...Map<String, dynamic>.from(data),
        'completed': true,
        'sourceEvent': 'trainingCompleted',
      });
    });

    // onairos-npm uses these event names.
    _socket!.on('training-progress', (data) {
      print('📡 [SOCKET] training-progress: $data');
      _progressController.add(Map<String, dynamic>.from(data));
    });
    _socket!.on('training-complete', (data) {
      print('✅ [SOCKET] training-complete: $data');
      _completionController.add({
        ...Map<String, dynamic>.from(data),
        'completed': true,
        'sourceEvent': 'training-complete',
      });
    });

    // Back-compat: legacy Enoch mobile backend events (we no longer depend on them for flow).
    _socket!.on('inferenceCompleted', (data) {
      print('🧠 [SOCKET] Inference completed (legacy): $data');
      _completionController.add({
        ...Map<String, dynamic>.from(data),
        'completed': true,
        'sourceEvent': 'inferenceCompleted',
      });
    });
    _socket!.on('modelStandby', (data) {
      print('✅ [SOCKET] Model standby (legacy): $data');
      _completionController.add({
        ...Map<String, dynamic>.from(data),
        'completed': true,
        'fullyComplete': true,
        'sourceEvent': 'modelStandby',
      });
    });

    // Connect to server
    _socket!.connect();

    // Wait for connection with timeout
    try {
      final socketId = await _connectionCompleter!.future.timeout(
        const Duration(seconds: 10),
        onTimeout: () {
          print('⚠️ [SOCKET] Connection timeout after 10 seconds');
          if (!_connectionCompleter!.isCompleted) {
            _connectionCompleter!.complete(null);
          }
          return null;
        },
      );

      if (socketId != null) {
        print('✅ [SOCKET] Successfully connected with ID: $socketId');
        return socketId;
      } else {
        print('⚠️ [SOCKET] Connection failed or timed out');
        return null;
      }
    } catch (e) {
      print('❌ [SOCKET] Error waiting for connection: $e');
      return null;
    }
  } catch (e) {
    print('❌ [SOCKET] Error connecting: $e');
    _errorController.add('Connection error: $e');
    return null;
  }
}