hitl_tester.modules.bms.adc_plate

Provides controls for various hardware.

(c) 2020-2024 TurnAround Factor, Inc.

#

CUI DISTRIBUTION CONTROL

Controlled by: DLA J68 R&D SBIP

CUI Category: Small Business Research and Technology

Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS

POC: GOV SBIP Program Manager Denise Price, 571-767-0111

Distribution authorized to U.S. Government Agencies only, to protect information not owned by the

U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that

it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests

for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,

Fort Belvoir, VA 22060-6221

#

SBIR DATA RIGHTS

Contract No.:SP4701-23-C-0083

Contractor Name: TurnAround Factor, Inc.

Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005

Expiration of SBIR Data Rights Period: September 24, 2029

The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer

software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights

in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause

contained in the above identified contract. No restrictions apply after the expiration date shown above. Any

reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce

the markings.

  1"""
  2Provides controls for various hardware.
  3
  4# (c) 2020-2024 TurnAround Factor, Inc.
  5#
  6# CUI DISTRIBUTION CONTROL
  7# Controlled by: DLA J68 R&D SBIP
  8# CUI Category: Small Business Research and Technology
  9# Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS
 10# POC: GOV SBIP Program Manager Denise Price, 571-767-0111
 11# Distribution authorized to U.S. Government Agencies only, to protect information not owned by the
 12# U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that
 13# it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests
 14# for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,
 15# Fort Belvoir, VA 22060-6221
 16#
 17# SBIR DATA RIGHTS
 18# Contract No.:SP4701-23-C-0083
 19# Contractor Name: TurnAround Factor, Inc.
 20# Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005
 21# Expiration of SBIR Data Rights Period: September 24, 2029
 22# The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer
 23# software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights
 24# in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause
 25# contained in the above identified contract. No restrictions apply after the expiration date shown above. Any
 26# reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce
 27# the markings.
 28"""
 29
 30from __future__ import annotations
 31
 32from dataclasses import dataclass, fields
 33from enum import Enum
 34from typing_extensions import Self
 35
 36import pytest
 37
 38from hitl_tester.modules.bms_types import BMSFlags, SuppressExceptions
 39from hitl_tester.modules.file_lock import RFileLock
 40
 41if hasattr(pytest, "flags") and isinstance(pytest.flags, BMSFlags) and pytest.flags.dry_run:
 42    from hitl_tester.modules.bms.pseudo_hardware import ADC
 43else:
 44    import piplates.ADCplate as ADC  # type: ignore[no-redef]
 45
 46
 47class ADCInput(str, Enum):
 48    """
 49    The ADC plate can support a combination of eight ground referenced single ended inputs or four,
 50    true differential inputs. A differential input is the combination of two single ended inputs.
 51    """
 52
 53    DIFFERENTIAL = "D"
 54    SINGLE = "S"
 55
 56
 57@dataclass
 58class Pins:
 59    """Make pin management easier."""
 60
 61    address: int
 62    pin: int
 63    pin_type: ADCInput
 64
 65    def __iter__(self):
 66        """Allows unpacking with *."""
 67        return iter(tuple(getattr(self, field.name) for field in fields(self)))
 68
 69
 70class ADCPlate:
 71    """ADC Pi-Plate access."""
 72
 73    _instance: Self | None = None
 74
 75    def __new__(cls):
 76        """Make ADCPlate a singleton."""
 77        cls._instance = cls._instance or super().__new__(cls)
 78        return cls._instance
 79
 80    def __init__(self):
 81        if (
 82            hasattr(pytest, "flags")
 83            and isinstance(pytest.flags, BMSFlags)
 84            and not (pytest.flags.doc_generation or pytest.flags.dry_run)
 85        ):
 86            configs = pytest.flags.config.get("adc_board", {})
 87            if configs:
 88                cells_board_address = configs["cells_board_address"]
 89                misc_board_address = configs["misc_board_address"]
 90
 91                self.cell_pins = Pins(cells_board_address, 0, ADCInput.DIFFERENTIAL)
 92                self.cell_ids = range(1, 5)
 93                self.battery_pins = Pins(misc_board_address, 0, ADCInput.SINGLE)
 94                self.terminal_pins = Pins(misc_board_address, 1, ADCInput.SINGLE)
 95                self.thermister_1_pins = Pins(misc_board_address, 1, ADCInput.DIFFERENTIAL)
 96                self.thermister_2_pins = Pins(misc_board_address, 2, ADCInput.DIFFERENTIAL)
 97                self.heater_pins = Pins(misc_board_address, 6, ADCInput.SINGLE)
 98
 99        self._lock = RFileLock("piplate")
100
101    def measure(self, board_address: int, channel: int, input_type: ADCInput) -> float:
102        """Measure an input on the ADC plate."""
103        with SuppressExceptions(), self._lock:
104            ADC.setMODE(board_address, "ADV")  # Advanced mode
105            ADC.configINPUT(board_address, f"{input_type.value}{channel}", 7, True)
106            return ADC.readSINGLE(board_address, f"{input_type.value}{channel}") or 0.0
107        return 0.0
108
109    def cell_volts(self, cell_id: int) -> float:
110        """Measure cell voltage (ID 0 to 3)."""
111        self.cell_pins.pin = cell_id - 1
112        return self.measure(*self.cell_pins)
113
114    @property
115    def battery_volts(self) -> float:
116        """Measure battery voltage (cells combined)."""
117        return self.measure(*self.battery_pins)
118
119    @property
120    def terminal_volts(self) -> float:
121        """Measure terminal voltage."""
122        return self.measure(*self.terminal_pins)
123
124    @property
125    def thermistor_1_volts(self) -> float:
126        """Measure thermistor 1 voltage."""
127        return self.measure(*self.thermister_1_pins)
128
129    @property
130    def thermistor_2_volts(self) -> float:
131        """Measure thermistor 2 voltage."""
132        return self.measure(*self.thermister_2_pins)
133
134    @property
135    def heater_volts(self) -> float:
136        """Measure heater voltage."""
137        return self.measure(*self.heater_pins)
class ADCInput(builtins.str, enum.Enum):
48class ADCInput(str, Enum):
49    """
50    The ADC plate can support a combination of eight ground referenced single ended inputs or four,
51    true differential inputs. A differential input is the combination of two single ended inputs.
52    """
53
54    DIFFERENTIAL = "D"
55    SINGLE = "S"

The ADC plate can support a combination of eight ground referenced single ended inputs or four, true differential inputs. A differential input is the combination of two single ended inputs.

DIFFERENTIAL = <ADCInput.DIFFERENTIAL: 'D'>
SINGLE = <ADCInput.SINGLE: 'S'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@dataclass
class Pins:
58@dataclass
59class Pins:
60    """Make pin management easier."""
61
62    address: int
63    pin: int
64    pin_type: ADCInput
65
66    def __iter__(self):
67        """Allows unpacking with *."""
68        return iter(tuple(getattr(self, field.name) for field in fields(self)))

Make pin management easier.

Pins( address: int, pin: int, pin_type: ADCInput)
address: int
pin: int
pin_type: ADCInput
class ADCPlate:
 71class ADCPlate:
 72    """ADC Pi-Plate access."""
 73
 74    _instance: Self | None = None
 75
 76    def __new__(cls):
 77        """Make ADCPlate a singleton."""
 78        cls._instance = cls._instance or super().__new__(cls)
 79        return cls._instance
 80
 81    def __init__(self):
 82        if (
 83            hasattr(pytest, "flags")
 84            and isinstance(pytest.flags, BMSFlags)
 85            and not (pytest.flags.doc_generation or pytest.flags.dry_run)
 86        ):
 87            configs = pytest.flags.config.get("adc_board", {})
 88            if configs:
 89                cells_board_address = configs["cells_board_address"]
 90                misc_board_address = configs["misc_board_address"]
 91
 92                self.cell_pins = Pins(cells_board_address, 0, ADCInput.DIFFERENTIAL)
 93                self.cell_ids = range(1, 5)
 94                self.battery_pins = Pins(misc_board_address, 0, ADCInput.SINGLE)
 95                self.terminal_pins = Pins(misc_board_address, 1, ADCInput.SINGLE)
 96                self.thermister_1_pins = Pins(misc_board_address, 1, ADCInput.DIFFERENTIAL)
 97                self.thermister_2_pins = Pins(misc_board_address, 2, ADCInput.DIFFERENTIAL)
 98                self.heater_pins = Pins(misc_board_address, 6, ADCInput.SINGLE)
 99
100        self._lock = RFileLock("piplate")
101
102    def measure(self, board_address: int, channel: int, input_type: ADCInput) -> float:
103        """Measure an input on the ADC plate."""
104        with SuppressExceptions(), self._lock:
105            ADC.setMODE(board_address, "ADV")  # Advanced mode
106            ADC.configINPUT(board_address, f"{input_type.value}{channel}", 7, True)
107            return ADC.readSINGLE(board_address, f"{input_type.value}{channel}") or 0.0
108        return 0.0
109
110    def cell_volts(self, cell_id: int) -> float:
111        """Measure cell voltage (ID 0 to 3)."""
112        self.cell_pins.pin = cell_id - 1
113        return self.measure(*self.cell_pins)
114
115    @property
116    def battery_volts(self) -> float:
117        """Measure battery voltage (cells combined)."""
118        return self.measure(*self.battery_pins)
119
120    @property
121    def terminal_volts(self) -> float:
122        """Measure terminal voltage."""
123        return self.measure(*self.terminal_pins)
124
125    @property
126    def thermistor_1_volts(self) -> float:
127        """Measure thermistor 1 voltage."""
128        return self.measure(*self.thermister_1_pins)
129
130    @property
131    def thermistor_2_volts(self) -> float:
132        """Measure thermistor 2 voltage."""
133        return self.measure(*self.thermister_2_pins)
134
135    @property
136    def heater_volts(self) -> float:
137        """Measure heater voltage."""
138        return self.measure(*self.heater_pins)

ADC Pi-Plate access.

ADCPlate()
76    def __new__(cls):
77        """Make ADCPlate a singleton."""
78        cls._instance = cls._instance or super().__new__(cls)
79        return cls._instance

Make ADCPlate a singleton.

def measure( self, board_address: int, channel: int, input_type: ADCInput) -> float:
102    def measure(self, board_address: int, channel: int, input_type: ADCInput) -> float:
103        """Measure an input on the ADC plate."""
104        with SuppressExceptions(), self._lock:
105            ADC.setMODE(board_address, "ADV")  # Advanced mode
106            ADC.configINPUT(board_address, f"{input_type.value}{channel}", 7, True)
107            return ADC.readSINGLE(board_address, f"{input_type.value}{channel}") or 0.0
108        return 0.0

Measure an input on the ADC plate.

def cell_volts(self, cell_id: int) -> float:
110    def cell_volts(self, cell_id: int) -> float:
111        """Measure cell voltage (ID 0 to 3)."""
112        self.cell_pins.pin = cell_id - 1
113        return self.measure(*self.cell_pins)

Measure cell voltage (ID 0 to 3).

battery_volts: float
115    @property
116    def battery_volts(self) -> float:
117        """Measure battery voltage (cells combined)."""
118        return self.measure(*self.battery_pins)

Measure battery voltage (cells combined).

terminal_volts: float
120    @property
121    def terminal_volts(self) -> float:
122        """Measure terminal voltage."""
123        return self.measure(*self.terminal_pins)

Measure terminal voltage.

thermistor_1_volts: float
125    @property
126    def thermistor_1_volts(self) -> float:
127        """Measure thermistor 1 voltage."""
128        return self.measure(*self.thermister_1_pins)

Measure thermistor 1 voltage.

thermistor_2_volts: float
130    @property
131    def thermistor_2_volts(self) -> float:
132        """Measure thermistor 2 voltage."""
133        return self.measure(*self.thermister_2_pins)

Measure thermistor 2 voltage.

heater_volts: float
135    @property
136    def heater_volts(self) -> float:
137        """Measure heater voltage."""
138        return self.measure(*self.heater_pins)

Measure heater voltage.