sendDataToPrinter method

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

Implementation

Future<Map<String, dynamic>> sendDataToPrinter(
  int vendorId,
  int productId,
  Uint8List data, {
  int interfaceNumber = 0,
  int timeout = 10000,
  bool expectResponse = false,
  int maxResponseLength = 256,
}) async {
  final Pointer<libusb_device_handle>? handleNullable =
      openDevice(vendorId, productId);
  if (handleNullable == nullptr || handleNullable == null) {
    return {'success': false, 'error': 'Could not open device'};
  }

  final handle = handleNullable;
  // 🔹 2️⃣ Obtener los endpoints correctos
  final device = getDevice(vendorId, productId);
  if (device == null) {
    log("❌ No se pudo encontrar el dispositivo USB");
    return {'success': false, 'error': 'No se pudo encontrar el dispositivo'};
  }

  final endpoints = getDeviceEndpoints(device);
  if (endpoints.isEmpty) {
    return {'success': false, 'error': 'No se encontraron endpoints válidos'};
  }

  // Detectar OUT (escritura) e IN (lectura)
  int endpointOut =
      endpoints.firstWhere((e) => e & 0x80 == 0, orElse: () => 0x01);
  int endpointIn =
      endpoints.firstWhere((e) => e & 0x80 != 0, orElse: () => 0x82);

  //log("✔️ Endpoint OUT: ${endpointOut.toRadixString(16)}");
  //log("✔️ Endpoint IN: ${endpointIn.toRadixString(16)}");

  try {
    int hasKernelDriver = 0;
    if (Platform.isLinux || Platform.isMacOS) {
      try {
        hasKernelDriver =
            _bindings.libusb_kernel_driver_active(handle, interfaceNumber);
        if (hasKernelDriver == 1) {
          log("Detaching kernel driver...");
          final detachResult =
              _bindings.libusb_detach_kernel_driver(handle, interfaceNumber);
          if (detachResult < 0) {
            log("Could not detach kernel driver: $detachResult");
          } else {
            log("Kernel driver detached successfully");
          }
        }
      } catch (e) {
        log("Error checking/detaching kernel driver: $e");
      }
    }

    final configResult = _bindings.libusb_set_configuration(handle, 1);
    if (configResult < 0) {
      log("Warning: Could not set configuration: $configResult");
    }

    int claimResult = -1;
    int attempts = 0;
    const maxAttempts = 3;

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

      log("Attempt ${attempts + 1} failed with error $claimResult. Retrying...");
      await Future.delayed(Duration(milliseconds: 500));
      attempts++;
    }

    if (claimResult < 0) {
      return {
        'success': false,
        'error': 'Could not claim interface after $maxAttempts attempts',
        'errorCode': claimResult,
        'errorDescription': _getUsbErrorDescription(claimResult)
      };
    }

    final buffer = calloc<Uint8>(data.length);
    final bufferList = buffer.asTypedList(data.length);
    bufferList.setAll(0, data);

    final transferredPtr = calloc<Int>();

    log("Sending ${data.length} bytes to endpoint $endpointOut...");
    int transferResult = _bindings.libusb_bulk_transfer(handle, endpointOut,
        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 in data transfer',
        'errorCode': transferResult,
        'errorDescription': _getUsbErrorDescription(transferResult)
      };
    }

    log("Transfer successful: $bytesSent bytes sent");

    Map<String, dynamic> result = {
      'success': true,
      'bytesSent': bytesSent,
    };

    if (expectResponse) {
      final responseBuffer = calloc<Uint8>(maxResponseLength);
      final responseTransferredPtr = calloc<Int>();

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

      log("Reading response from endpoint $endpointIn...");
      final responseResult = _bindings.libusb_bulk_transfer(
          handle,
          endpointIn,
          responseBuffer.cast<UnsignedChar>(),
          maxResponseLength,
          responseTransferredPtr,
          timeout);

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

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

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

          result['responseData'] = responseList;
          result['bytesReceived'] = bytesReceived;
        } else {
          result['responseData'] = [];
          result['bytesReceived'] = 0;
        }
      } else {
        log("Error reading response: $responseResult");
        result['responseError'] = _getUsbErrorDescription(responseResult);
      }

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

    _bindings.libusb_release_interface(handle, interfaceNumber);

    if (hasKernelDriver == 1 && (Platform.isLinux || Platform.isMacOS)) {
      _bindings.libusb_attach_kernel_driver(handle, interfaceNumber);
    }

    return result;
  } catch (e) {
    return {
      'success': false,
      'error': 'Error communicating with the printer',
      'exception': e.toString()
    };
  } finally {
    closeDevice(handle);
  }
}