Add instrument factory
This commit is contained in:
176
src/py_dvt_ate/instruments/factory.py
Normal file
176
src/py_dvt_ate/instruments/factory.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""Instrument factory for creating configured instrument sets.
|
||||
|
||||
This module provides a factory pattern for creating sets of instruments
|
||||
based on configuration. It abstracts away the choice between simulated
|
||||
and real hardware, allowing test code to be written once and run against
|
||||
either backend.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from py_dvt_ate.instruments.interfaces import IMultimeter, IPowerSupply, IThermalChamber
|
||||
|
||||
|
||||
@dataclass
|
||||
class InstrumentSet:
|
||||
"""Container for a complete set of instruments.
|
||||
|
||||
Holds all instruments needed for DVT testing. All instruments implement
|
||||
the interface protocols (IThermalChamber, IPowerSupply, IMultimeter),
|
||||
allowing them to be simulated or real hardware.
|
||||
|
||||
Attributes:
|
||||
chamber: Thermal chamber for temperature control.
|
||||
psu: Programmable power supply for DUT power.
|
||||
dmm: Digital multimeter for precision measurements.
|
||||
"""
|
||||
|
||||
chamber: IThermalChamber
|
||||
psu: IPowerSupply
|
||||
dmm: IMultimeter
|
||||
|
||||
|
||||
@dataclass
|
||||
class InstrumentConfig:
|
||||
"""Configuration for instrument connections.
|
||||
|
||||
Defines how to connect to instruments. The backend determines whether
|
||||
to use simulated instruments (TCP connections to virtual instruments)
|
||||
or real hardware (PyVISA connections).
|
||||
|
||||
Attributes:
|
||||
backend: "simulator" for virtual instruments, "pyvisa" for real hardware.
|
||||
|
||||
Simulator Settings:
|
||||
simulator_host: Hostname/IP of simulation server. Default "localhost".
|
||||
chamber_port: TCP port for thermal chamber simulator. Default 5001.
|
||||
psu_port: TCP port for power supply simulator. Default 5002.
|
||||
dmm_port: TCP port for multimeter simulator. Default 5003.
|
||||
|
||||
PyVISA Settings (for real hardware):
|
||||
chamber_visa: VISA resource string for thermal chamber (e.g., "TCPIP::192.168.1.10::INSTR").
|
||||
psu_visa: VISA resource string for power supply.
|
||||
dmm_visa: VISA resource string for multimeter.
|
||||
"""
|
||||
|
||||
backend: Literal["simulator", "pyvisa"]
|
||||
|
||||
# Simulator settings
|
||||
simulator_host: str = "localhost"
|
||||
chamber_port: int = 5001
|
||||
psu_port: int = 5002
|
||||
dmm_port: int = 5003
|
||||
|
||||
# PyVISA settings (for real hardware)
|
||||
chamber_visa: str | None = None
|
||||
psu_visa: str | None = None
|
||||
dmm_visa: str | None = None
|
||||
|
||||
|
||||
class InstrumentFactory:
|
||||
"""Factory for creating instrument sets from configuration.
|
||||
|
||||
This factory encapsulates the creation logic for instrument sets,
|
||||
hiding the complexity of instantiating transports and drivers based
|
||||
on the chosen backend.
|
||||
|
||||
Example:
|
||||
>>> config = InstrumentConfig(backend="simulator")
|
||||
>>> instruments = InstrumentFactory.create(config)
|
||||
>>> instruments.chamber.set_temperature(85.0)
|
||||
>>> instruments.psu.set_voltage(1, 3.3)
|
||||
>>> voltage = instruments.dmm.measure_dc_voltage()
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def create(config: InstrumentConfig) -> InstrumentSet:
|
||||
"""Create instrument set based on configuration.
|
||||
|
||||
Args:
|
||||
config: Configuration specifying backend and connection details.
|
||||
|
||||
Returns:
|
||||
InstrumentSet containing all configured instruments.
|
||||
|
||||
Raises:
|
||||
ValueError: If backend is unknown or configuration is invalid.
|
||||
ConnectionError: If unable to connect to instruments.
|
||||
"""
|
||||
if config.backend == "simulator":
|
||||
return InstrumentFactory._create_simulated(config)
|
||||
elif config.backend == "pyvisa":
|
||||
return InstrumentFactory._create_pyvisa(config)
|
||||
else:
|
||||
raise ValueError(f"Unknown backend: {config.backend}")
|
||||
|
||||
@staticmethod
|
||||
def _create_simulated(config: InstrumentConfig) -> InstrumentSet:
|
||||
"""Create simulated instruments connected via TCP.
|
||||
|
||||
Creates TCP transports for each virtual instrument and wraps them
|
||||
in SCPI drivers. The simulation server must be running and listening
|
||||
on the configured ports.
|
||||
|
||||
Args:
|
||||
config: Configuration with simulator_host and port settings.
|
||||
|
||||
Returns:
|
||||
InstrumentSet with simulated instruments.
|
||||
|
||||
Raises:
|
||||
ConnectionError: If unable to connect to simulation server.
|
||||
"""
|
||||
from py_dvt_ate.instruments.drivers.chamber import ThermalChamberDriver
|
||||
from py_dvt_ate.instruments.drivers.multimeter import MultimeterDriver
|
||||
from py_dvt_ate.instruments.drivers.power_supply import PowerSupplyDriver
|
||||
from py_dvt_ate.instruments.transport.tcp import TCPTransport
|
||||
|
||||
# Create transports for each instrument
|
||||
chamber_transport = TCPTransport(config.simulator_host, config.chamber_port)
|
||||
psu_transport = TCPTransport(config.simulator_host, config.psu_port)
|
||||
dmm_transport = TCPTransport(config.simulator_host, config.dmm_port)
|
||||
|
||||
# Wrap transports in drivers
|
||||
return InstrumentSet(
|
||||
chamber=ThermalChamberDriver(chamber_transport),
|
||||
psu=PowerSupplyDriver(psu_transport),
|
||||
dmm=MultimeterDriver(dmm_transport),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_pyvisa(config: InstrumentConfig) -> InstrumentSet:
|
||||
"""Create PyVISA instruments for real hardware.
|
||||
|
||||
Creates VISA transports for each real instrument and wraps them
|
||||
in SCPI drivers. Requires PyVISA to be installed and VISA resource
|
||||
strings to be configured.
|
||||
|
||||
Args:
|
||||
config: Configuration with chamber_visa, psu_visa, dmm_visa settings.
|
||||
|
||||
Returns:
|
||||
InstrumentSet with real hardware instruments.
|
||||
|
||||
Raises:
|
||||
NotImplementedError: PyVISA backend not yet implemented.
|
||||
ValueError: If required VISA resource strings are missing.
|
||||
"""
|
||||
# Future implementation would use pyvisa.ResourceManager
|
||||
# to create VISA transports:
|
||||
#
|
||||
# import pyvisa
|
||||
# from py_dvt_ate.instruments.transport.visa import VISATransport
|
||||
#
|
||||
# rm = pyvisa.ResourceManager()
|
||||
# chamber_transport = VISATransport(rm.open_resource(config.chamber_visa))
|
||||
# psu_transport = VISATransport(rm.open_resource(config.psu_visa))
|
||||
# dmm_transport = VISATransport(rm.open_resource(config.dmm_visa))
|
||||
#
|
||||
# return InstrumentSet(
|
||||
# chamber=ThermalChamberDriver(chamber_transport),
|
||||
# psu=PowerSupplyDriver(psu_transport),
|
||||
# dmm=MultimeterDriver(dmm_transport),
|
||||
# )
|
||||
|
||||
raise NotImplementedError("PyVISA backend not yet implemented")
|
||||
Reference in New Issue
Block a user