Add instrument factory

This commit is contained in:
2025-07-26 19:22:27 +00:00
parent fe2271d551
commit 03e364e071
2 changed files with 184 additions and 0 deletions

View File

@@ -8,6 +8,11 @@ This package provides everything needed to communicate with lab instruments:
- Factory for creating configured instrument sets - Factory for creating configured instrument sets
""" """
from py_dvt_ate.instruments.factory import (
InstrumentConfig,
InstrumentFactory,
InstrumentSet,
)
from py_dvt_ate.instruments.interfaces import ( from py_dvt_ate.instruments.interfaces import (
IMultimeter, IMultimeter,
IPowerSupply, IPowerSupply,
@@ -18,4 +23,7 @@ __all__ = [
"IThermalChamber", "IThermalChamber",
"IPowerSupply", "IPowerSupply",
"IMultimeter", "IMultimeter",
"InstrumentSet",
"InstrumentConfig",
"InstrumentFactory",
] ]

View 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")