getPrinterStatus method
Implementation
Future<Map<String, dynamic>> getPrinterStatus(
int vendorId,
int productId,
List<int> command, {
int interfaceNumber = 0,
int timeout = 10000,
bool expectResponse = false,
int maxResponseLength = 542,
}) async {
// 🔹 1️⃣ Open the device
final Pointer<libusb_device_handle>? handleNullable =
openDevice(vendorId, productId);
if (handleNullable == nullptr || handleNullable == null) {
return {
'success': false,
'error': 'Could not open device',
'isConnected': false,
'statusType': command.length >= 3 ? command[2] : 0
};
}
// 🔹 2️⃣ Obtener los endpoints correctos
final device = getDevice(vendorId, productId);
if (device == null) {
//log("❌ The USB device could not be found");
return {'success': false, 'error': 'The device could not be found.'};
}
final endpoints = getDeviceEndpoints(device);
if (endpoints.isEmpty) {
return {'success': false, 'error': 'No valid endpoints found'};
}
// Detect OUT (write) eN (read)
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)} - Endpoint IN: ${endpointIn.toRadixString(16)}");
final handle = handleNullable;
Map<String, dynamic> statusInfo = {
'success': false,
'isConnected': false,
'rawData': null,
'statusType': command.length >= 3 ? command[2] : 0
};
try {
// Check if there is an active kernel driver and detach if necessary
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");
}
}
// Configure the device if necessary
final configResult = _bindings.libusb_set_configuration(handle, 1);
if (configResult < 0) {
//log("Warning: Could not set configuration: $configResult");
}
// 🔹 3️⃣ Reclamar la interfaz antes de transferir datos
// Claim the interface with multiple attempts
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',
'isConnected': false,
'statusType': command.length >= 3 ? command[2] : 0
};
}
// 🔹 4️⃣ Enviar el comando a la impresora
final buffer = calloc<Uint8>(command.length);
final bufferList = buffer.asTypedList(command.length);
bufferList.setAll(0, command);
final transferredPtr = calloc<Int>();
log("📤 Sending command $command to endpoint ${endpointOut.toRadixString(16)}...");
int transferResult = _bindings.libusb_bulk_transfer(handle, endpointOut,
buffer.cast<UnsignedChar>(), command.length, transferredPtr, timeout);
await Future.delayed(Duration(milliseconds: 100));
calloc.free(buffer);
calloc.free(transferredPtr);
if (transferResult < 0) {
String errorDescription = _getUsbErrorDescription(transferResult);
log("❌ Error sending command: $errorDescription");
return {
'success': false,
'error': 'Error sending command: $command, detail: $errorDescription',
'isConnected': false,
'statusType': command.length >= 3 ? command[2] : 0
};
}
log("✔️ Command sent successfully");
// 🔹 5️⃣ Leer la respuesta si aplica
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];
//dataPointer[i] = 0; // Inicializar con ceros
}
final Pointer<Int> transferredPointer = malloc.allocate<Int>(1);
transferredPointer.value = 0;
log("📥 Waiting for response on endpointIn ${endpointIn.toRadixString(16)}...");
// Call the function correctly
int readResult = _bindings.libusb_bulk_transfer(
handle, // libusb_device_handle*
endpointIn, // unsigned char endpoint
dataPointer, // unsigned char* data
buffer2.length, // int length
transferredPointer, // int* transferred
timeout, // unsigned int timeout
);
// Read how many bytes were transferred
int bytesReceived = transferredPointer.value;
if (readResult == 0 && bytesReceived > 0) {
// Copy the received data back to a Uint8List
Uint8List receivedData = Uint8List(bytesReceived);
for (var i = 0; i < bytesReceived; i++) {
receivedData[i] = dataPointer[i];
}
// Determine what type of command it is based on the third 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;
// Interpret the data based on the status type
if (bytesReceived > 0) {
switch (statusType) {
case 1: // Printer status [0x10, 0x04, 0x01]
statusInfo['isOnline'] = (receivedData[0] & (1 << 3)) == 0;
statusInfo['cashDrawerOpen'] = (receivedData[0] & (1 << 2)) != 0;
statusInfo['waitingForRecovery'] =
(receivedData[0] & (1 << 5)) != 0;
statusInfo['paperFeedButtonPressed'] =
(receivedData[0] & (1 << 6)) != 0;
// Si está offline y esperando recuperación, podemos inferir posibles problemas
if (!statusInfo['isOnline'] && statusInfo['waitingForRecovery']) {
statusInfo['possibleErrorConditions'] = [
'cover_open',
'paper_end',
'error_state'
];
// No es necesario enviar el segundo comando si ya sabemos que hay un error
}
break;
case 2: // Offline status [0x10, 0x04, 0x02]
statusInfo['isCoverOpen'] = (receivedData[0] & (1 << 2)) != 0;
statusInfo['isPaperFeedByButton'] =
(receivedData[0] & (1 << 3)) != 0;
break;
case 4: // Paper sensor status [0x10, 0x04, 0x04]
bool bit2 = (receivedData[0] & (1 << 2)) != 0;
bool bit3 = (receivedData[0] & (1 << 3)) != 0;
bool bit5 = (receivedData[0] & (1 << 5)) != 0;
bool bit6 = (receivedData[0] & (1 << 6)) != 0;
statusInfo['paperStatus'] = {
'paperNearEnd': bit2 || bit3,
'paperEnd': bit5 || bit6,
'paperPresent': !(bit5 || bit6),
'paperAdequate': !(bit2 || bit3),
};
break;
default:
statusInfo['error'] = 'Unrecognized command type';
}
}
} else {
log("Error: ${_bindings.libusb_error_name(readResult)}");
log("Description: ${_getUsbErrorDescription(readResult)}");
statusInfo['error'] =
'Error reading response: ${_bindings.libusb_error_name(readResult)}';
}
// Free memory
malloc.free(dataPointer);
malloc.free(transferredPointer);
// Release the interface
_bindings.libusb_release_interface(handle, interfaceNumber);
// Reattach the kernel driver if it was detached
if (hasKernelDriver == 1 && (Platform.isLinux || Platform.isMacOS)) {
_bindings.libusb_attach_kernel_driver(handle, interfaceNumber);
}
} catch (e) {
log('Exception: $e');
return {
'success': false,
'error': 'Error communicating with the printer: ${e.toString()}',
'isConnected': false,
'statusType': command.length >= 3 ? command[2] : 0
};
} finally {
closeDevice(handle);
}
return statusInfo;
}