gary-rowe / hid4java

A cross-platform Java Native Access (JNA) wrapper for the libusb/hidapi library. Works out of the box on Windows/Mac/Linux.
MIT License
229 stars 71 forks source link

HidServiceEvent not fired during HidService.start #101

Closed Laivindur closed 3 years ago

Laivindur commented 3 years ago

Hi @gary-rowe

I have realised that, for some reason, when we call hidService.start() by the first time, no HidServiceEvent is handled by the listener.

Here the code I use to start HidService. It's very similar to the one in the project's example

public static void startHIDService() {
        // System info to assist with library detection
        log.info("Platform architecture: " + Platform.ARCH);
        log.info("Resource prefix: " + Platform.RESOURCE_PREFIX);

        // Configure to use custom specification
        HidServicesSpecification hidServicesSpecification = new HidServicesSpecification();
        hidServicesSpecification.setAutoShutdown(true);
        hidServicesSpecification.setScanInterval(500);
        hidServicesSpecification.setPauseInterval(5000);
        hidServicesSpecification.setScanMode(ScanMode.SCAN_AT_FIXED_INTERVAL_WITH_PAUSE_AFTER_WRITE);
        //HidApi.useLibUsbVariant = false;

        // Get HID services using custom specification
        hidServices = HidManager.getHidServices(hidServicesSpecification);
        hidServices.addHidServicesListener(new HidServicesListener() {

            @Override
            public void hidDeviceAttached(HidServicesEvent event) {
                log.info("Device attached: " + event);

            }

            @Override
            public void hidDeviceDetached(HidServicesEvent event) {
                log.error("Device detached: " + event);

            }

            @Override
            public void hidFailure(HidServicesEvent event) {
                log.error("HID failure: " + event);

            }

        });

        // Start the services
        log.info("Starting HID services.");
        hidServices.start();
    }

Note that, the listener, has not been implemented in the class itself. It's defined on-the-fly by an anonymous class

hidServices.addHidServicesListener(new HidServicesListener() {
...
});

In production, it's a first-citizen class with its own file.

I was expecting that, during the HidService initialization, any HID device already attached will cause a HidServiceEvent. Inspecting hid4java classes, I have found that, somehow, I'm right.

HidService.start() calls HidDeviceManager.start() and ...

public class HidDeviceManager {
...
public void start() {

    // Check for previous start
    if (this.isScanning()) {
      return;
    }

    // Perform a one-off scan to populate attached devices
    scan();

    // Ensure we have a scan thread available
    configureScanThread(getScanRunnable());

  }
...
}

The method scan()

 for (HidDevice attachedDevice : attachedHidDeviceList) {

      if (!this.attachedDevices.containsKey(attachedDevice.getId())) {

        // Device has become attached so add it but do not open
        attachedDevices.put(attachedDevice.getId(), attachedDevice);

        // Fire the event on a separate thread
        listenerList.fireHidDeviceAttached(attachedDevice);

      }
    }

The first time any device is mapped into attachedDevices should result in a new call to stenerList.fireHidDeviceAttached(attachedDevice);

Given the code above, I should see something like this in the console

Platform architecture: x86-64
Resource prefix: linux-x86-64
Starting HID services.
Device attached: HidServicesEvent{hidDevice=HidDevice [path=/dev/hidraw0, vendorId=0x483, productId=0x5750, serialNumber=STM3210, releaseNumber=0x200, manufacturer=STMicroelectronics, product=STM32 Custm HID, usagePage=0x0, usage=0x0, interfaceNumber=0]}
Enumerating attached devices...
HidDevice [path=/dev/hidraw0, vendorId=0x483, productId=0x5750, serialNumber=STM3210, releaseNumber=0x200, manufacturer=STMicroelectronics, product=STM32 Custm HID, usagePage=0x0, usage=0x0, interfaceNumber=0]
Waiting 30s to demonstrate attach/detach handling. Watch for a slow response after write if configured.

Note the output after Starting HID services. starting with Device attached: HidServicesEvent{.

At the moment, the output is

Platform architecture: x86-64
Resource prefix: linux-x86-64
Starting HID services.
Enumerating attached devices...
HidDevice [path=/dev/hidraw0, vendorId=0x483, productId=0x5750, serialNumber=STM3210, releaseNumber=0x200, manufacturer=STMicroelectronics, product=STM32 Custm HID, usagePage=0x0, usage=0x0, interfaceNumber=0]
Waiting 30s to demonstrate attach/detach handling. Watch for a slow response after write if configured.

What am I doing wrong? I'm missing something, but I don't know what

Thank you in advance.

gary-rowe commented 3 years ago

Hi @Laivindur

Good spot! The HidService code was starting before any listeners were added leading to missed events on startup. However since this has been the default behaviour for a long time I've decided to keep it in place and have an autoStart flag in HidSpecification to drive a manual start process.

I've updated the examples (and added some ANSI colour) to reflect various customisations with a manual start being one of them.

Can you review the changes and see if it fixes this issue for you?

Laivindur commented 3 years ago
Platform architecture: x86-64
Resource prefix: linux-x86-64
Libusb activation: true
Manually starting HID services.
Enumerating attached devices...
Device attached: HidServicesEvent{hidDevice=HidDevice [path=0001:0014:00, vendorId=0x483, productId=0x5750, serialNumber=STM3210, releaseNumber=0x200, manufacturer=STMicroelectronics, product=STM32 Custm HID, usagePage=0x0, usage=0x0, interfaceNumber=0]}
HidDevice [path=0001:0014:00, vendorId=0x483, productId=0x5750, serialNumber=STM3210, releaseNumber=0x200, manufacturer=STMicroelectronics, product=STM32 Custm HID, usagePage=0x0, usage=0x0, interfaceNumber=0]
Waiting 30s to demonstrate attach/detach handling. Watch for slow response after write if configured.


Worked like a charm!

I will upgrade to the new version as soon as it's released.

Thank you for the quick response and fix. It allows me to get rid of ugly walkaround :-)

gary-rowe commented 3 years ago

Thanks for testing it @Laivindur. I'll merge it into develop which should mean that a 0.7.0 release can be done.