Script hang: jsdrv_publish timed out

I’m running a Python script using my JS110. At some point, the script seems to lockup after a “jsdrv_publish timed out” is printed.
I think this usually happens when I’m doing other things with the same computer (i.e. possibly a spike in CPU/memory usage).

What I want is to periodically sample current and voltage once every ~second. My needs do not require no dropped samples, etc. I do need this to last over a period of 2-3hrs.
Is there anything that could be suggested to change in my script to make this robust?

py -m pyjoulescope_driver info

driver info
SYSTEM INFORMATION
------------------
python               3.12.2 (tags/v3.12.2:6abddd9, Feb  6 2024, 21:26:36) [MSC v.1937 64 bit (AMD64)]
python impl          CPython
platform             Windows-10-10.0.19045-SP0
processor            Intel64 Family 6 Model 142 Stepping 12, GenuineIntel
CPU cores            4 physical, 8 total
CPU frequency        801 MHz (0 MHz min to 2304 MHz max)
RAM                  4.2 GB available, 15.8 GB total (26.5%)

PYTHON PACKAGE INFORMATION
--------------------------
jls                  0.9.2
joulescope           1.1.12
numpy                1.26.4
pyjoulescope_driver  1.4.7


JOULESCOPE INFORMATION
----------------------
u/js110/000650

sample extract of my .py script:

.py code
js_device = joulescope.scan_require_one(config="auto")
with js_device:
 while:

    data = js_device.read(duration=0.1)
    current, voltage = np.mean(data, axis=0, dtype=np.float64)

    # sleep so we only loop at 1 second intervals
    time.sleep(x)

Hi @lmcv and welcome to the Joulescope forum.

Calling read sets up sample streaming, receives data, then stops sample streaming. The start and stop can take some time, so this ends up being a pretty good stress test! It’s much better to leave sample streaming running and just extract the data you need. One approach is shown in read_by_callback.py.

However, since you only need data once per second, you can use the statistics data. With the JS220, the statistics are always computed on the instrument. With the JS110, they are computed on instrument at 2 Hz only, but with no standard deviation, which sounds good for your application.

Here is some example code:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright 2019-2024 Jetperch LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Display sensor-side statistics from the first connected Joulescope."""

from joulescope import scan
import time


def statistics_callback(stats):
    """The function called for each statistics.

    :param stats: The statistics data structure.
    """
    t = stats['time']['range']['value'][0]
    i = stats['signals']['current']['µ']
    v = stats['signals']['voltage']['µ']
    p = stats['signals']['power']['µ']
    c = stats['accumulators']['charge']
    e = stats['accumulators']['energy']

    fmts = ['{x:.9f}', '{x:.3f}', '{x:.9f}', '{x:.9f}', '{x:.9f}']
    values = []
    for k, fmt in zip([i, v, p, c, e], fmts):
        value = fmt.format(x=k['value'])
        value = f'{value} {k["units"]}'
        values.append(value)
    ', '.join(values)
    print(f"{t:.1f}: " + ', '.join(values))


def run():
    devices = scan(config='off')
    if not len(devices):
        print('No Joulescope device found')
        return 1
    device = devices[0]
    device.statistics_callback_register(statistics_callback, 'sensor')
    device.open()
    try:
        device.parameter_set('i_range', 'auto')
        device.parameter_set('v_range', '15V')
        time.sleep(10)   # replace with your code
    finally:
        device.close()


if __name__ == '__main__':
    run()

Does this work for your application?

Thanks for such a fast response, Matt.

Thanks for the direction - makes sense. Honestly, this was just the first thing I tried…
I’ll be able to try it your suggestion when the work-week starts. I’ll report back how I go :slight_smile:

1 Like

Hi @mliberty,

Thanks for your guidance - it seems to have been just what I needed.

For completeness, here’s roughly what I ended up with:

new .py extract
import time
import joulescope

def main():
    js_data = JsData()
    js_device = joulescope.scan_require_one(config="auto")
    js_device.statistics_callback_register(js_data.statistics_callback, "sensor")

    while True:
        with js_device:
            print(f"{js_data}")
            time.sleep(1.0)

class JsData:
    def __init__(self):
        self.voltage: float = 0.0
        self.current: float = 0.0

    def statistics_callback(self, stats_dict):
        """get sample; not average or other stats"""
        voltage = stats_dict["signals"]["voltage"]["µ"]
        current = stats_dict["signals"]["current"]["µ"]

        self.voltage = voltage["value"]
        # want milliamps
        self.current = current["value"] * 1000.0

    def __str__(self):
        return f"voltage={self.voltage:.3f}, current={self.current:.3f}"

if __name__ == "__main__":
    main()
1 Like