getPrinterStatus method

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

Implementation

Future<Map<String, dynamic>> getPrinterStatus(
  int vendorId,
  int productId,
  List<int> command, {
  int interfaceNumber = 0,
  int endpointAddress = 0x01,
  int readEndpointAddress = 0x81,
  int timeout = 10000,
  bool expectResponse = false,
  int maxResponseLength = 542,
}) 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',
      'isConnected': false,
      'statusType': command.length >= 3 ? command[2] : 0
    };
  }

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

  Map<String, dynamic> statusInfo = {
    'success': false,
    'isConnected': false,
    'rawData': null,
    'statusType': command.length >= 3 ? command[2] : 0
  };
  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',
        'isConnected': false,
        'statusType': command.length >= 3 ? command[2] : 0
      };
    }

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

    final transferredPtr = calloc<Int>();

    log("Enviando comando $command...");
    int transferResult = _bindings.libusb_bulk_transfer(
        handle,
        endpointAddress,
        buffer.cast<UnsignedChar>(),
        command.length,
        transferredPtr,
        timeout);

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

    //final bytesSent = transferredPtr.value;

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

    if (transferResult < 0) {
      String errorDescription = _getUsbErrorDescription(transferResult);
      return {
        'success': false,
        'error':
            'Error al enviar comando: $command, detalle: $errorDescription',
        'isConnected': false,
        'statusType': command.length >= 3 ? command[2] : 0
      };
    }

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

    Uint8List buffer2 = Uint8List(512);
    final Pointer<UnsignedChar> dataPointer =
        malloc.allocate<UnsignedChar>(buffer2.length);
    for (var i = 0; i < buffer2.length; i++) {
      dataPointer[i] = buffer[i];
    }

    final Pointer<Int> transferredPointer = malloc.allocate<Int>(1);
    transferredPointer.value = 0;

    // Llamar a la función correctamente
    int readResult = _bindings.libusb_bulk_transfer(
      handle, // libusb_device_handle*
      0x81, // unsigned char endpoint
      dataPointer, // unsigned char* data
      buffer2.length, // int length
      transferredPointer, // int* transferred
      timeout, // unsigned int timeout
    );

    // Leer cuántos bytes se transfirieron
    int bytesReceived = transferredPointer.value;

    if (readResult == 0 && bytesReceived > 0) {
      // Copiar los datos recibidos de vuelta a un Uint8List
      Uint8List receivedData = Uint8List(bytesReceived);
      for (var i = 0; i < bytesReceived; i++) {
        receivedData[i] = dataPointer[i];
      }

      // Determinar qué tipo de comando es según el tercer byte
      int statusType = command.length >= 3 ? command[2] : 0;

      statusInfo['success'] = true;
      statusInfo['isConnected'] = true;
      statusInfo['rawData'] = receivedData;
      statusInfo['binaryResponse'] =
          receivedData[0].toRadixString(2).padLeft(8, '0');
      statusInfo['statusType'] = statusType;

      // Interpretar los datos según el tipo de estado
      if (bytesReceived > 0) {
        //interpretPrinterStatus(receivedData[0]);
        //bool isOnline = (receivedData[0] & (1 << 3)) == 0;
        //log('Impresora en línea: $isOnline');
        //statusInfo['status'] = 'Impresora en línea: $isOnline';

        // Interpretar la respuesta según el tipo de comando
        switch (statusType) {
          case 1: // Estado de la impresora [0x10, 0x04, 0x01]
            statusInfo['isOnline'] = (receivedData[0] & (1 << 3)) == 0;
            statusInfo['cashDrawerOpen'] = (receivedData[0] & (1 << 2)) != 0;
            break;

          case 2: // Estado offline [0x10, 0x04, 0x02]
            statusInfo['isCoverOpen'] = (receivedData[0] & (1 << 2)) != 0;
            statusInfo['isPaperFeedByButton'] =
                (receivedData[0] & (1 << 3)) != 0;
            break;

          case 4: // Estado del sensor de papel [0x10, 0x04, 0x04]
            // Evaluamos los bits 2-3 (estado del papel cerca del final)
            bool bit2 = (receivedData[0] & (1 << 2)) != 0;
            bool bit3 = (receivedData[0] & (1 << 3)) != 0;

            // Evaluamos los bits 5-6 (estado del sensor de fin de papel)
            bool bit5 = (receivedData[0] & (1 << 5)) != 0;
            bool bit6 = (receivedData[0] & (1 << 6)) != 0;

            statusInfo['paperStatus'] = {
              'paperNearEnd': bit2 ||
                  bit3, // Si cualquiera de estos bits está activado, el papel está cerca del final
              'paperEnd': bit5 ||
                  bit6, // Si cualquiera de estos bits está activado, se ha detectado el fin del papel
              'paperPresent': !(bit5 ||
                  bit6), // Si los bits 5-6 están desactivados, hay papel presente
              'paperAdequate': !(bit2 ||
                  bit3), // Si los bits 2-3 están desactivados, el papel es adecuado
            };
            break;

          default:
            statusInfo['error'] = 'Tipo de comando no reconocido';
        }
      }
    } else {
      log("Error: ${_bindings.libusb_error_name(readResult)}");
      log("Description: ${_getUsbErrorDescription(readResult)}");
      statusInfo['error'] =
          'Error al leer respuesta: ${_bindings.libusb_error_name(readResult)}';
    }

    // Liberar la memoria
    malloc.free(dataPointer);
    malloc.free(transferredPointer);

    // 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);
    }
  } catch (e) {
    log('Exception: $e');
    return {
      'success': false,
      'error': 'Error al comunicarse con la impresora: ${e.toString()}',
      'isConnected': false,
      'statusType': command.length >= 3 ? command[2] : 0
    };
  } finally {
    closeDevice(handle);
  }
  return statusInfo;
}