Skip to content
Permalink
Browse files

driver/sigrok: add support for PSU control via sigrok

This is currently only tested via a Manson HCS-2302, which uses a
USB-serial connection for control. This required adding the
SigrokUSBSerialDevice to match correctly via udev.

Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
  • Loading branch information...
jluebbe committed Oct 11, 2019
1 parent eb5408d commit 263f2e73e7ceb8c5d00cd3636e95bfd4ef633b63
@@ -438,6 +438,24 @@ host which is exported over the network. The SigrokDriver will access it via SSH
Used by:
- `SigrokDriver`_

SigrokUSBSerialDevice
~~~~~~~~~~~~~~~~~~~~~
A SigrokUSBSerialDevice resource describes a sigrok device which communicates
of a USB serial port instead of being a USB device itself (see
`SigrokUSBDevice` for that case).

.. code-block:: yaml
SigrokUSBSerialDevice:
driver: manson-hcs-3xxx
match:
'@ID_SERIAL_SHORT': P-00-02389
- driver (str): name of the sigrok driver to use

Used by:
- `SigrokPowerDriver`_

USBSDMuxDevice
~~~~~~~~~~~~~~
A :any:`USBSDMuxDevice` resource describes a Pengutronix
@@ -1478,6 +1496,28 @@ Implements:
The driver can be used in test cases by calling the `capture`, `stop` and
`analyze` functions.

SigrokPowerDriver
~~~~~~~~~~~~~~~~~
The SigrokPowerDriver uses a `SigrokUSBSerialDevice`_ Resource to control a
programmable power supply.

Binds to:
sigrok:
- `SigrokUSBSerialDevice`_
- NetworkSigrokUSBSerialDevice

Implements:
- :any:`PowerProtocol`

.. code-block:: yaml
SigrokPowerDriver:
delay: 3.0
Arguments:
- delay (float): optional delay in seconds between off and on, defaults to
3.0

USBSDMuxDriver
~~~~~~~~~~~~~~
The :any:`USBSDMuxDriver` uses a USBSDMuxDevice resource to control a
@@ -6,6 +6,7 @@
import shutil
import signal
import tempfile
import time
import uuid
import csv

@@ -14,11 +15,15 @@
import attr

from ..factory import target_factory
from ..resource.remote import NetworkSigrokUSBDevice
from ..resource.udev import SigrokUSBDevice
from ..protocol import PowerProtocol
from ..resource.remote import NetworkSigrokUSBDevice, NetworkSigrokUSBSerialDevice
from ..resource.udev import SigrokUSBDevice, SigrokUSBSerialDevice
from ..resource.sigrok import SigrokDevice
from ..step import step
from ..util.helper import processwrapper
from .common import Driver, check_file
from .exception import ExecutionError
from .powerdriver import PowerResetMixin


@attr.s(eq=False)
@@ -83,6 +88,10 @@ def _get_sigrok_prefix(self):
prefix += ["-d", "{}:conn={}.{}".format(
self.sigrok.driver, self.sigrok.busnum, self.sigrok.devnum
)]
elif isinstance(self.sigrok, (NetworkSigrokUSBSerialDevice, SigrokUSBSerialDevice)):
prefix += ["-d", "{}:conn={}".format(
self.sigrok.driver, self.sigrok.path
)]
else:
prefix += ["-d", self.sigrok.driver]
if self.sigrok.channels:
@@ -231,3 +240,86 @@ def analyze(self, args, filename=None):
match.groupdict()
for match in re.finditer(annotation_regex, output.decode("utf-8"))
]


@target_factory.reg_driver
@attr.s(eq=False)
class SigrokPowerDriver(SigrokCommon, PowerResetMixin, PowerProtocol):
"""The SigrokPowerDriverDriver uses sigrok-cli to control a PSU and collect
measurements.
Args:
bindings (dict): driver to use with sigrok
"""
bindings = {
"sigrok": {SigrokUSBSerialDevice, NetworkSigrokUSBSerialDevice},
}
delay = attr.ib(default=3.0, validator=attr.validators.instance_of(float))

@Driver.check_active
@step()
def on(self):
processwrapper.check_output(
self._get_sigrok_prefix() + ["--config", "enabled=yes", "--set"]
)

@Driver.check_active
@step()
def off(self):
processwrapper.check_output(
self._get_sigrok_prefix() + ["--config", "enabled=no", "--set"]
)

@Driver.check_active
@step()
def cycle(self):
self.off()
time.sleep(self.delay)
self.on()

@Driver.check_active
@step(args=["value"])
def set_voltage_target(self, value):
processwrapper.check_output(
self._get_sigrok_prefix() + ["--config", "voltage_target={:f}".format(value), "--set"]
)

@Driver.check_active
@step(args=["value"])
def set_current_limit(self, value):
processwrapper.check_output(
self._get_sigrok_prefix() + ["--config", "current_limit={:f}".format(value), "--set"]
)

@Driver.check_active
@step(result=True)
def get(self):
out = processwrapper.check_output(
self._get_sigrok_prefix() + ["--get", "enabled"]
)
if out == b'true':
return True
elif out == b'false':
return False
else:
raise ExecutionError("Unkown enable status {}".format(out))

@Driver.check_active
@step(result=True)
def measure(self):
out = processwrapper.check_output(
self._get_sigrok_prefix() + ["--show"]
)
res = {}
for line in out.splitlines():
line = line.strip()
if b':' not in line:
continue
k, v = line.split(b':', 1)
if k == b'voltage':
res['voltage'] = float(v)
elif k == b'current':
res['current'] = float(v)
if len(res) != 2:
raise ExecutionError("Cannot parse --show output {}".format(out))
return res
@@ -307,6 +307,7 @@ def _get_params(self):
exports["RKUSBLoader"] = USBGenericExport
exports["AlteraUSBBlaster"] = USBGenericExport
exports["SigrokUSBDevice"] = USBSigrokExport
exports["SigrokUSBSerialDevice"] = USBSigrokExport
exports["USBSDMuxDevice"] = USBSDMuxExport

exports["USBMassStorage"] = USBGenericExport
@@ -162,6 +162,17 @@ def __attrs_post_init__(self):
super().__attrs_post_init__()


@target_factory.reg_resource
@attr.s(eq=False)
class NetworkSigrokUSBSerialDevice(RemoteUSBResource):
"""The NetworkSigrokUSBSerialDevice describes a remotely accessible sigrok USB device"""
driver = attr.ib(default=None, validator=attr.validators.instance_of(str))
channels = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))
def __attrs_post_init__(self):
self.timeout = 10.0
super().__attrs_post_init__()


@target_factory.reg_resource
@attr.s(eq=False)
class NetworkUSBMassStorage(RemoteUSBResource):
@@ -346,6 +346,8 @@ class SigrokUSBDevice(USBResource):
"""The SigrokUSBDevice describes an attached sigrok device with driver and
optional channel mapping, it is identified via usb using udev.
This is used for devices which communicate over a custom USB protocol.
Args:
driver (str): driver to use with sigrok
channels (str): a sigrok channel mapping as desribed in the sigrok-cli man page
@@ -357,6 +359,33 @@ def __attrs_post_init__(self):
self.match['@SUBSYSTEM'] = 'usb'
super().__attrs_post_init__()

@target_factory.reg_resource
@attr.s(eq=False)
class SigrokUSBSerialDevice(USBResource):
"""The SigrokUSBSerialDevice describes an attached sigrok device with driver and
optional channel mapping, it is identified via usb using udev.
This is used for devices which communicate over an emulated serial device.
Args:
driver (str): driver to use with sigrok
channels (str): a sigrok channel mapping as desribed in the sigrok-cli man page
"""
driver = attr.ib(default=None, validator=attr.validators.instance_of(str))
channels = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))

def __attrs_post_init__(self):
self.match['SUBSYSTEM'] = 'tty'
self.match['@SUBSYSTEM'] = 'usb'
super().__attrs_post_init__()

@property
def path(self):
if self.device is not None:
return self.device.device_node

return None

@target_factory.reg_resource
@attr.s(eq=False)
class USBSDMuxDevice(USBResource):
@@ -3,9 +3,9 @@
from time import sleep
from shutil import which

from labgrid.resource.udev import SigrokUSBDevice
from labgrid.resource.udev import SigrokUSBDevice, SigrokUSBSerialDevice
from labgrid.resource.sigrok import SigrokDevice
from labgrid.driver.sigrokdriver import SigrokDriver
from labgrid.driver.sigrokdriver import SigrokDriver, SigrokPowerDriver


pytestmark = pytest.mark.skipif(not which("sigrok-cli"),
@@ -35,3 +35,8 @@ def test_sigrok_usb_driver(target, tmpdir):
assert samples != None
assert list(samples[0].keys()) == ['time', 'D0', 'D1']
assert list(samples[-1].keys()) == ['time', 'D0', 'D1']

def test_sigrok_power_driver(target):
r = SigrokUSBSerialDevice(target, name=None, driver='manson-hcs-3xxx')
d = SigrokPowerDriver(target, name=None)
target.activate(d)

0 comments on commit 263f2e7

Please sign in to comment.
You can’t perform that action at this time.