Pyjoulescope_driver power on does not release the device

Hi,
I have migrated my project to make use of the pyjoulescope_driver from the former example pyjoulescope_examples/bin at main · jetperch/pyjoulescope_examples · GitHub (from Updated dut_power.py to use pyjoulescope_driver directly. · jetperch/pyjoulescope_examples@09148e2 · GitHub)
I would like to not changing my interface of my package. That’s why I have migrated the dut_power.py.
I have written 2 pytest unittests which both fail with:

c:\code\project\
src\dut_power.py:48: in dut_power
    with Driver() as jsdrv:
pyjoulescope_driver/binding.pyx:614: in pyjoulescope_driver.binding.Driver.__init__
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   RuntimeError: jsdrv_initialize failed 21 IN_USE | The requested resource is currently in use.

pyjoulescope_driver/binding.pyx:595: RuntimeError

The unit test:

    def doPowerDUT(self, value="on"):
        if isinstance(value, str):
            value = value.lower()
        sys.argv.clear()
        sys.argv.append("dut_power.py")
        if value == "off" or value == 0:
            sys.argv.append("0")
            dut_power("0") # if does not work take _power_off from trigger.py
            return True, "OK", {"doPowerDUT set to {}".format(value)}
        elif value == "on" or value == 1:
            sys.argv.append("1")
            dut_power("1")
            self.log.info("DO_POWER_DUT(1)")
            return True, "OK", {"do power DUT": str(value)}
        else:
            self.log.info("DO_POWER_DUT(0)")
            return False, "ERROR", {"do power DUT": str(value)}
        return True, "OK", {"do power DUT": str(value)}

installed packages:

> python -m pyjoulescope_driver info

    SYSTEM INFORMATION
    ------------------
    python               3.9.12 (tags/v3.9.12:b28265d, Mar 23 2022, 23:52:46) [MSC v.1929 64 bit (AMD64)]
    python impl          CPython
    platform             Windows-10-10.0.19045-SP0
    processor            Intel64 Family 6 Model 69 Stepping 1, GenuineIntel
    CPU cores            2 physical, 4 total
    CPU frequency        2701 MHz (0 MHz min to 2701 MHz max)
    RAM                  2.9 GB available, 7.9 GB total (36.7%)

    PYTHON PACKAGE INFORMATION
    --------------------------
    jls                  0.9.4
    joulescope           1.1.9
    numpy                1.24.4
    pyjoulescope_driver  1.4.10


    JOULESCOPE INFORMATION
    ----------------------
    u/js110/xxxxxx

Assumption:
That when using with Driver() as jsdrv: does close all handles but it doesn’t.

Hi @lukGWF - I am pretty sure that this works correctly. First, can you update to the latest pyjoulescope_driver 1.5.2:

python -m pip install -U pyjoulescope_driver joulescope pyjls

The code snippets your provided are not enough to duplicate. I copied from dut_power.py and put together a simple script to try to duplicate the problem:

from pyjoulescope_driver import Driver
import time


def _topic(device_path):
    if 'js110' in device_path:
        return device_path + '/s/i/range/select'
    else:
        return device_path + '/s/i/range/mode'


def dut_power_set(jsdrv, device_path, power_on):
    i_range = 'auto' if power_on else 'off'
    jsdrv.publish(_topic(device_path), i_range)


def doPowerDUT(value="on"):
    if value in ['off', 0, False, None]:
        value = False
    elif value in ['on', 1, True]:
        value = True
    else:
        return False, 'Error', {'do power DUT': str(value)}
    with Driver() as jsdrv:
        device_paths = sorted(jsdrv.device_paths())
        for device_path in device_paths:
            jsdrv.open(device_path)
            dut_power_set(jsdrv, device_path, value)
            jsdrv.close(device_path)
    return True, 'OK', {'do power DUT': str(value)}


def run():
    for idx in range(10):
        print(f'Iteration {idx}')
        doPowerDUT(idx & 1)


if __name__ == '__main__':
    run()

which works great on my Windows 11 machine with both a JS110 and JS220:

PS C:\project\Joulescope\support\2024\05\22> python -m pyjoulescope_driver info

    SYSTEM INFORMATION
    ------------------
    python               3.12.3 (tags/v3.12.3:f6650f9, Apr  9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]
    python impl          CPython
    platform             Windows-11-10.0.22631-SP0
    processor            Intel64 Family 6 Model 183 Stepping 1, GenuineIntel
    CPU cores            24 physical, 32 total
    CPU frequency        3000 MHz (0 MHz min to 3000 MHz max)
    RAM                  46.4 GB available, 63.7 GB total (72.7%)

    PYTHON PACKAGE INFORMATION
    --------------------------
    jls                  0.9.4
    joulescope           1.1.13
    numpy                1.26.4
    pyjoulescope_driver  1.5.2


    JOULESCOPE INFORMATION
    ----------------------
    u/js110/001612
    u/js220/002122: hw=1.0.0, fw=1.2.2, fpga=1.2.1

Does this script work for you?

Note that some unit test runners will run tests in parallel, which is definitely not going to work.

Hi @mliberty,
Thanks for the prompt reply.
I have successfully updated pyjoulescope_driver to 1.5.2.
Joulescope to 1.1.13

But still having the same issue.
How can I investigate who/what is blocking? I also did a reboot which not solved the issue.

Did the script I provided work for you?

Hi @mliberty,
No, updating and changing to your solution did not work yet.

Hi @lukGWF - Can you run the script exactly as is and post the output? I have attached it as a complete file to make it easy:
main.py (1.0 KB)

Here is the output on my computer:

Joulescopes can only be open by one program at a time. Please be sure to close all other programs that are accessing your Joulescopes, including the Joulescope UI.

Ensure that you never try to create a pyjoulescope_driver.Driver() instance in the same Python program as anything that uses joulescope.scan() or any other joulescope API call.

For JS110 and JS220

Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9

Ok, so the script is working and correctly sets the dut power. I am confused. Why did you say:

What is still not working for you?

Hi @mliberty ,
When I call directly:
PS1> python src\agent_cpm\dut_power.py “1”

Traceback (most recent call last):
  File "c:\code\21.0150-agent-cpm-slim\src\agent_cpm\dut_power.py", line 80, in <module>
    sys.exit(main())
  File "c:\code\21.0150-agent-cpm-slim\src\agent_cpm\dut_power.py", line 77, in main
    dut_power(power_on)
  File "c:\code\21.0150-agent-cpm-slim\src\agent_cpm\dut_power.py", line 56, in dut_power
    with Driver() as jsdrv:
  File "pyjoulescope_driver/binding.pyx", line 614, in pyjoulescope_driver.binding.Driver.__init__
  File "pyjoulescope_driver/binding.pyx", line 595, in pyjoulescope_driver.binding._handle_rc
RuntimeError: jsdrv_initialize failed 21 IN_USE | The requested resource is currently in use.

dut_power.py (2.4 KB)

Well, lines 55 and 56 of dut_power.py are:

    with Driver() as jsdrv:
        with Driver() as jsdrv:

If you remove line 56 (the second Driver instance), the script works.

With the current pyjoulescope_driver implementation, you can only have one Driver instance active at a time. We did not enforce the singleton design pattern since this may change in the future.