sendDataToPrinter method

Future<Map<String, dynamic>> sendDataToPrinter(
  1. int vendorId,
  2. int productId,
  3. Uint8List data, {
  4. int interfaceNumber = 0,
  5. int endpointAddress = 0x01,
  6. int readEndpointAddress = 0x81,
  7. int timeout = 10000,
  8. bool expectResponse = false,
  9. int maxResponseLength = 256,
})

Implementation

Future<Map<String, dynamic>> sendDataToPrinter(
  int vendorId,
  int productId,
  Uint8List data, {
  int interfaceNumber = 0,
  int endpointAddress = 0x01,
  int readEndpointAddress = 0x81,
  int timeout = 10000,
  bool expectResponse = false,
  int maxResponseLength = 256,
}) async {
  // Abre el dispositivo
  final Pointer<libusb_device_handle>? handleNullable =
      openDevice(vendorId, productId);
  if (handleNullable == nullptr || handleNullable == null) {
    return {'success': false, 'error': 'No se pudo abrir el dispositivo'};
  }

  // Aquí convertimos de Pointer? a Pointer, ahora que sabemos que no es nulo
  final handle = handleNullable;

  try {
    // Verificar si hay un kernel driver activo y desconectarlo si es necesario
    int hasKernelDriver = 0;
    if (Platform.isLinux || Platform.isMacOS) {
      try {
        hasKernelDriver =
            _bindings.libusb_kernel_driver_active(handle, interfaceNumber);
        if (hasKernelDriver == 1) {
          log("Desconectando el driver del kernel...");
          final detachResult =
              _bindings.libusb_detach_kernel_driver(handle, interfaceNumber);
          if (detachResult < 0) {
            log("No se pudo desconectar el driver del kernel: $detachResult");
          } else {
            log("Driver del kernel desconectado con éxito");
          }
        }
      } catch (e) {
        log("Error al verificar/desconectar el driver del kernel: $e");
      }
    }

    // Configurar el dispositivo si es necesario
    final configResult = _bindings.libusb_set_configuration(handle, 1);
    if (configResult < 0) {
      log("Advertencia: No se pudo establecer la configuración: $configResult");
      // Continuamos a pesar del error, ya que algunas impresoras funcionan sin esto
    }

    // Reclamar la interfaz con múltiples intentos
    int claimResult = -1;
    int attempts = 0;
    const maxAttempts = 3;

    while (attempts < maxAttempts) {
      claimResult = _bindings.libusb_claim_interface(handle, interfaceNumber);
      if (claimResult == 0) break;

      log("Intento ${attempts + 1} fallido con error $claimResult. Reintentando...");
      // Esperar un poco antes de reintentar
      await Future.delayed(Duration(milliseconds: 500));

      attempts++;
    }

    if (claimResult < 0) {
      return {
        'success': false,
        'error':
            'No se pudo reclamar la interfaz después de $maxAttempts intentos',
        'errorCode': claimResult,
        'errorDescription': _getUsbErrorDescription(claimResult)
      };
    }

    // Enviar datos a la impresora
    final buffer = calloc<Uint8>(data.length);
    final bufferList = buffer.asTypedList(data.length);
    bufferList.setAll(0, data);

    final transferredPtr = calloc<Int>();

    log("Enviando ${data.length} bytes al endpoint $endpointAddress...");
    int transferResult = _bindings.libusb_bulk_transfer(
        handle,
        endpointAddress,
        buffer.cast<UnsignedChar>(),
        data.length,
        transferredPtr,
        timeout);

    await Future.delayed(Duration(milliseconds: 5000));

    final bytesSent = transferredPtr.value;

    calloc.free(buffer);
    calloc.free(transferredPtr);

    if (transferResult < 0) {
      return {
        'success': false,
        'error': 'Error en la transferencia de datos',
        'errorCode': transferResult,
        'errorDescription': _getUsbErrorDescription(transferResult)
      };
    }

    log("Transferencia exitosa: $bytesSent bytes enviados");

    // Si se espera una respuesta, leer los datos de la impresora
    Map<String, dynamic> result = {
      'success': true,
      'bytesSent': bytesSent,
    };

    if (expectResponse) {
      // Crear buffer para la respuesta
      final responseBuffer = calloc<Uint8>(maxResponseLength);
      final responseTransferredPtr = calloc<Int>();

      // Pequeña espera para dar tiempo a la impresora a procesar y preparar la respuesta
      await Future.delayed(Duration(milliseconds: 800));

      log("Leyendo respuesta desde el endpoint $readEndpointAddress...");
      final responseResult = _bindings.libusb_bulk_transfer(
          handle,
          readEndpointAddress,
          responseBuffer.cast<UnsignedChar>(),
          maxResponseLength,
          responseTransferredPtr,
          timeout);

      await Future.delayed(Duration(milliseconds: 100));

      if (responseResult >= 0) {
        final bytesReceived = responseTransferredPtr.value;
        log("Respuesta recibida: $bytesReceived bytes");

        if (bytesReceived > 0) {
          // Convertir la respuesta a List<int>
          final responseList = List<int>.filled(bytesReceived, 0);
          for (var i = 0; i < bytesReceived; i++) {
            responseList[i] = responseBuffer[i];
          }

          // Añadir la respuesta al resultado
          result['responseData'] = responseList;
          result['bytesReceived'] = bytesReceived;
        } else {
          result['responseData'] = [];
          result['bytesReceived'] = 0;
        }
      } else {
        log("Error al leer la respuesta: $responseResult");
        result['responseError'] = _getUsbErrorDescription(responseResult);
      }

      calloc.free(responseBuffer);
      calloc.free(responseTransferredPtr);
    }

    // Liberar la interfaz
    _bindings.libusb_release_interface(handle, interfaceNumber);

    // Reconectar el driver del kernel si lo desconectamos
    if (hasKernelDriver == 1 && (Platform.isLinux || Platform.isMacOS)) {
      _bindings.libusb_attach_kernel_driver(handle, interfaceNumber);
    }

    return result;
  } catch (e) {
    return {
      'success': false,
      'error': 'Error al comunicarse con la impresora',
      'exception': e.toString()
    };
  } finally {
    closeDevice(handle);
  }
}