Skip to content

Drivers: structure of the package and drivers for DC power source #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
62d57f6
drivers: add structure of the driver package.
MatthieuDartiailh Mar 28, 2018
a953c6b
drivers: add standards for identity subsystem and dc sources
MatthieuDartiailh Apr 14, 2018
48d0ab6
drivers: add common functionality for IEEE and some scpi commands
MatthieuDartiailh Apr 14, 2018
cd31388
drivers.itest: wip on BILT system basic support
MatthieuDartiailh Apr 14, 2018
a404fc2
drivers: fixes to ieee and scpi base commands
MatthieuDartiailh Apr 16, 2018
68ea660
drivers.itest: get all tests for be2101 to pass
MatthieuDartiailh Apr 16, 2018
cfbb3cf
drivers.itest: wip
MatthieuDartiailh Apr 16, 2018
9e09f69
drivers.itest: wip
MatthieuDartiailh Apr 18, 2018
5c3e810
drivers.itest: wip
MatthieuDartiailh Apr 18, 2018
439bbaa
drivers.base: update DC sources standard
MatthieuDartiailh May 10, 2018
6ea0d66
drivers.itest: complete Bilt drivers
MatthieuDartiailh May 10, 2018
31e4065
drivers.yokogawa: add GS200 and 7651 drivers
MatthieuDartiailh May 10, 2018
3d4e830
drivers.keysight: add E3631A, E3633A, E3634A drivers
MatthieuDartiailh May 10, 2018
bcd2bde
drivers.keysight: fix MRO of E363XA
MatthieuDartiailh May 10, 2018
57c437d
tests: add basic tests for all DC voltage sources
MatthieuDartiailh May 11, 2018
34d3a9c
drivers.keysight: E3631A use lock=True in action rather than manual l…
MatthieuDartiailh May 12, 2018
c3717db
drivers.base: fix issue in SCPI error reading
MatthieuDartiailh May 14, 2018
ba00e85
drivers.itest: final fixes to the BE drivers
MatthieuDartiailh May 14, 2018
ee10a05
drivers.yokogawa: fix GS200 driver
MatthieuDartiailh May 14, 2018
fcfbace
drivers.ieee488: meaningful default format for idn
MatthieuDartiailh May 15, 2018
1f09a03
drivers.yokogawa: fix 7651 driver
MatthieuDartiailh May 15, 2018
7f7bbb7
drivers: output -> outputs to mark it is a channel
MatthieuDartiailh May 16, 2018
d2afda8
drivers.keysight: basic debugging of the E3631A
MatthieuDartiailh May 16, 2018
cc8790c
drivers: add some additional comments
MatthieuDartiailh May 16, 2018
363916d
drivers: address @lcontami review comments
MatthieuDartiailh May 23, 2018
7752a64
Core:
galactikvoyager Jun 13, 2018
3204aae
drivers.itest: let the superclass clean the error queue
MatthieuDartiailh Jul 4, 2018
d3c468b
coverage: omit coverage in drivers
MatthieuDartiailh Jul 4, 2018
b730745
coverage: omit coverage in drivers
MatthieuDartiailh Jul 4, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
omit =
*i3py/version.py
*/__init__.py
*i3py/drivers/*

[report]
# Regexes for lines to exclude from consideration
Expand Down
6 changes: 3 additions & 3 deletions i3py/core/base_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from .has_features import HasFeatures


class InstrumentSigleton(type):
class InstrumentSingleton(type):
"""Metaclass ensuring that a single driver is created per instrument.

"""
Expand Down Expand Up @@ -54,7 +54,7 @@ def __call__(cls, *args, **kwargs) -> 'BaseDriver':
cache = cls._instances_cache[cls]
driver_id = cls.compute_id(args, kwargs) # type: ignore
if driver_id not in cache:
dr = super(InstrumentSigleton, cls).__call__(*args, **kwargs)
dr = super(InstrumentSingleton, cls).__call__(*args, **kwargs)

cache[driver_id] = dr
else:
Expand All @@ -64,7 +64,7 @@ def __call__(cls, *args, **kwargs) -> 'BaseDriver':
return dr


class BaseDriver(HasFeatures, metaclass=InstrumentSigleton):
class BaseDriver(HasFeatures, metaclass=InstrumentSingleton):
""" Base class of all instrument drivers in I3py.

This class defines the common interface drivers are expected to implement
Expand Down
2 changes: 1 addition & 1 deletion i3py/core/base_subsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


class SubSystem(HasFeatures):
"""SubSystem allow to split the implementation of a driver into multiple
"""SubSystem allows one to split the implementation of a driver into multiple
parts.

This mechanism allow to avoid crowding the instrument namespace with very
Expand Down
14 changes: 14 additions & 0 deletions i3py/drivers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Package storing all the implemented drivers by manufacturer.

The package contains also the standards definitions and common utilities such
as support for IEEE488 and SCPI commands.

"""
17 changes: 17 additions & 0 deletions i3py/drivers/agilent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Alias package for the Keysight package.

"""
import sys
from i3py.core.lazy_package import LazyPackage

from .. import keysight

sys.modules[__name__] = LazyPackage({}, __name__, __doc__, locals())
17 changes: 17 additions & 0 deletions i3py/drivers/alazar_tech/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Package for the drivers of Alazar Tech instruments.

"""
import sys
from i3py.core.lazy_package import LazyPackage

DRIVERS = {}

sys.modules[__name__] = LazyPackage(DRIVERS, __name__, __doc__, locals())
17 changes: 17 additions & 0 deletions i3py/drivers/anritsu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Package for the drivers of Anritsu instruments.

"""
import sys
from i3py.core.lazy_package import LazyPackage

DRIVERS = {}

sys.modules[__name__] = LazyPackage(DRIVERS, __name__, __doc__, locals())
193 changes: 193 additions & 0 deletions i3py/drivers/base/dc_sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""I3py standard for DC sources.

"""
from i3py.core import HasFeatures, SubSystem, channel
from i3py.core.unit import FLOAT_QUANTITY
from i3py.core.actions import Action
from i3py.core.features import Bool, Float, Str, constant


class DCPowerSource(HasFeatures):
"""Standard interface expected from all DC Power sources.

"""

#: Outputs of the source. By default we declare a single output on index 0.
outputs = channel((0,))

with outputs as o:

#: Is the output on or off.
#: Care should be taken that this value may not be up to date if a
#: failure occurred. To know the current status of the output use
#: read_output_status, this feature only store the target setting.
o.enabled = Bool(aliases={True: ['On', 'ON', 'on'],
False: ['Off', 'OFF', 'off']})

#: Target voltage for the output. If the source is a "current" source
#: this will likely be a fixed value.
o.voltage = Float(unit='V')

#: Range in which the voltage can be set.
o.voltage_range = Float(unit='V')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a float sufficient ? Don't you need a tuple ?
(certain sources can have a range - lim -> + lim, others 0 ->lim.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the one I add access to so far it is sufficient. But a source can add stricter limitation like the Bilt does. The fact is that there is never a direct mapping between the range and the associated limits.


#: How does the source behave if it cannot reach the target voltage
#: because it reached the target current first.
#: - regulate: we stop at the reached voltage when the target current
#: is reached.
#: - trip: the output is disabled if the current reaches or gets
#: greater than the specified current.
o.current_limit_behavior = Str(constant('regulate'),
values=('regulate', 'trip'))

#: Target voltage for the output. If the source is a "voltage" source
#: this will likely be a fixed value.
o.current = Float(unit='A')

#: Range in which the current can be set.
o.current_range = Float(unit='A')

#: How does the source behave if it cannot reach the target current
#: because it reached the target voltage first.
#: - regulate: we stop at the reached voltage when the target current
#: is reached.
#: - trip: the output is disabled if the voltage reaches or gets
#: greater than the specified voltage.
o.voltage_limit_behavior = Str(constant('regulate'),
values=('regulate', 'trip'))

@o
@Action()
def read_output_status(self) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waou what is this syntax ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it the type annatotation that bothers you or the double decoration ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the type annotation. I saw the python page about it. I didn't know it existed !

"""Determine the status of the output.

The generic format of the status is status:reason, if the reason is
not known used 'unknown'. The following values correspond to usual
situations.

Returns
-------
status : {'disabled',
'enabled:constant-voltage',
'enabled:constant-current',
'tripped:over-voltage',
'tripped:over-current',
'unregulated'}
The possible values for the output status are the following.
- 'disabled': the output is currently disabled
- 'enabled:constant-voltage': the target voltage was reached
before the target current and voltage_limit_behavior is
'regulate'.
- 'enabled:constant-current': the target current was reached
before the target voltage and current_limit_behavior is
'regulate'.
- 'tripped:over-voltage': the output tripped after reaching the
voltage limit.
- 'tripped:over-current': the output tripped after reaching the
current limit.
- 'unregulated': The output of the instrument is not stable.

"""
raise NotImplementedError()


class DCPowerSourceWithMeasure(DCPowerSource):
"""DC power source supporting to measure the output current/voltage.

"""
#: Outputs of the source. By default we declare a single output on index 0.
outputs = channel((0,))

with outputs as o:

@o
@Action()
def measure(self, quantity: str, **kwargs) -> FLOAT_QUANTITY:
"""Measure the output voltage/current.

Parameters
----------
quantity : str, {'voltage', 'current'}
Quantity to measure.

**kwargs :
Optional kwargs to specify the conditions of the measure
(integration time, averages, etc) if applicable.

Returns
-------
value : float or pint.Quantity
Measured value. If units are supported the value is a Quantity
object.

"""
raise NotImplementedError()


class DCSourceTriggerSubsystem(SubSystem):
"""Subsystem handing the usual triggering mechanism of DC sources.

It should be added to a DCPowerSource subclass under the name trigger.

"""
#: Working mode for the trigger. This usually defines how the instrument
#: will answer to a trigger event.
mode = Str(values=('disabled',))

#: Possible origin for trigger events.
source = Str(values=('immediate', 'software')) # Will extend later

#: Delay between the trigger and the time at which the instrument start to
#: modify its output.
delay = Float(unit='s')

@Action()
def arm(self):
"""Make the system ready to receive a trigger event.

"""
pass


class DCSourceProtectionSubsystem(SubSystem):
"""Interface for DC source protection.

"""
#: Is the protection enabled.
enabled = Bool(aliases={True: ['On', 'ON', 'On'],
False: ['Off', 'OFF', 'off']})

#: How the output behaves when the low/limit is reached.
behavior = Str(constant('trip'))

#: Lower limit below which the setting is not allowed to go.
low_level = Float()

#: Higher limit above which the setting is not allowed to go.
high_level = Float()

@Action()
def read_status(self) -> str:
"""Read the current status of the protection.

Returns
-------
status : {'working', 'tripped'}

"""
pass

@Action()
def reset(self) -> None:
"""Reset the protection after an issue.

"""
pass
38 changes: 38 additions & 0 deletions i3py/drivers/base/identity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Standard interface of the identity subsystem.

"""
from i3py.core.base_subsystem import SubSystem
from i3py.core.features import Str


class Identity(SubSystem):
"""Standard subsystem defining the expected identity info.

This should be used as a base class for the identity subsystem of
instruments providing identity information.

Notes
-----
Some of those info might not be available for a given instrument. In such
a case the Feature should return ''.

"""
#: Manufacturer as returned by the instrument.
manufacturer = Str(True)

#: Model name as returned by the instrument.
model = Str(True)

#: Instrument serial number.
serial = Str(True)

#: Version of the installed firmware.
firmware = Str(True)
13 changes: 13 additions & 0 deletions i3py/drivers/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2018 by I3py Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Common utility to deal with drivers implementation.

"""

# This package is always needed so there is no point making it lazy
Loading