hitl_tester.modules.bms.korad
Provides controls for the Rigol DM3068 DMM.
(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 the Rigol DM3068 DMM. 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 32import atexit 33from enum import IntFlag 34 35from hitl_tester.modules.bms_types import SafeResource 36from hitl_tester.modules.logger import logger 37 38 39class Status(IntFlag): 40 """Reset flags from the CSR.""" 41 42 # 0x80 = N/A or OVP 43 OUTPUT_ON = 0x40 44 OVERVOLTAGE_OVERCURRENT_PROTECTION = 0x20 # or OCP or Lock 0=Lock, 1=Unlock 45 # 0x10 = Beep 0=Off, 1=On 46 # 0x08+0x04 = Tracking 00=Independent, 01=Tracking series,11=Tracking parallel 47 # 0x02 = CH2 0=CC mode, 1=CV mode 48 CONSTANT_VOLTAGE = 0x01 # Constant current if off 49 50 def __str__(self) -> str: 51 flags = [str(flag.name).title() for flag in Status if self.value & flag] 52 if not self.value & self.OUTPUT_ON: 53 flags.append("Output Off") 54 if not self.value & self.CONSTANT_VOLTAGE: 55 flags.append("Constant Current") 56 return " | ".join(flags) 57 58 59class Korad: 60 """Korad KA6003P power supply command wrapper.""" 61 62 VOLTAGE_MAX = 60 63 VOLTAGE_MIN = 1.2 64 CURRENT_MAX = 3 65 66 def __init__(self, korad_id: int, resource: SafeResource): 67 """ 68 Initialize the KA6003P wrapper with a specific PyVISA resource. 69 This class does NOT open the resource, you have to open it for yourself! 70 """ 71 self.id: int = korad_id 72 self.resource = resource 73 self._overvoltage_protection = False 74 self._overcurrent_protection = False 75 self._beep = False 76 self.disable() 77 78 @atexit.register 79 def __atexit__(): 80 """Configure a safe shut down for when the class instance is destroyed.""" 81 self.disable() 82 83 @property 84 def status(self) -> Status: 85 """Get the power supply status.""" 86 self.resource.write("STATUS?") 87 response = self.resource.read_raw()[1:] 88 return Status(int.from_bytes(response, byteorder="big") if len(response) == 1 else 0) 89 90 def set_profile(self, volts: float, amps: float): 91 """Sets charging profile""" 92 self.volts = volts 93 self.amps = amps 94 95 @property 96 def overcurrent_protection(self) -> float: 97 """Get the last set overcurrent protection state.""" 98 return self._overcurrent_protection 99 100 @overcurrent_protection.setter 101 def overcurrent_protection(self, enabled: bool): 102 """Enable or disable overcurrent protection.""" 103 self._overcurrent_protection = enabled 104 self.resource.write(f"OCP{int(enabled)}") 105 106 @property 107 def overvoltage_protection(self) -> float: 108 """Get the last set overvoltage protection state.""" 109 return self._overvoltage_protection 110 111 @overvoltage_protection.setter 112 def overvoltage_protection(self, enabled: bool): 113 """Enable or disable overvoltage protection.""" 114 self._overvoltage_protection = enabled 115 self.resource.write(f"OVP{int(enabled)}") 116 117 @property 118 def measured_amps(self) -> float: 119 """Measures current.""" 120 return float(self.resource.query("IOUT1?")) 121 122 @property 123 def amps(self) -> float: 124 """Get the target current.""" 125 return float(self.resource.query("ISET1?")) 126 127 @amps.setter 128 def amps(self, new_amps: float): 129 """Set the target current.""" 130 if new_amps > Korad.CURRENT_MAX: 131 raise RuntimeError(f"Current of {new_amps}A exceeds maximum of {Korad.CURRENT_MAX}A.") 132 self.resource.write(f"ISET1:{new_amps}") 133 134 @property 135 def measured_volts(self) -> float: 136 """Measures voltage.""" 137 return float(self.resource.query("VOUT1?")) 138 139 @property 140 def volts(self) -> float: 141 """Get the target voltage.""" 142 return float(self.resource.query("VSET1?")) 143 144 @volts.setter 145 def volts(self, new_volts: float): 146 """Set the target voltage""" 147 if not Korad.VOLTAGE_MIN <= new_volts <= Korad.VOLTAGE_MAX: 148 raise RuntimeError(f"{new_volts}V is out of range {Korad.VOLTAGE_MIN}V to {Korad.VOLTAGE_MAX}V.") 149 self.resource.write(f"VSET1:{new_volts}") 150 151 @property 152 def beep(self) -> bool: 153 """Get the beep setting.""" 154 return self._beep 155 156 @beep.setter 157 def beep(self, enable_beep: bool): 158 """Turns on or off the beep.""" 159 self.resource.write(f"BEEP{int(enable_beep)}") 160 161 def recall(self, memory_number: int): 162 """Recalls a panel setting from memory 1 to 5.""" 163 self.resource.write(f"RCL{memory_number}") 164 165 def store(self, memory_number: int): 166 """Stores a panel setting to memory 1 to 5.""" 167 self.resource.write(f"SAV{memory_number}") 168 169 def enable(self): 170 """Enable the output.""" 171 logger.write_info_to_report("Enabling Korad charger") 172 self.resource.write("OUT1") 173 174 def disable(self): 175 """Disable the output.""" 176 logger.write_info_to_report("Disabling Korad charger") 177 self.resource.write("OUT0")
class
Status(enum.IntFlag):
40class Status(IntFlag): 41 """Reset flags from the CSR.""" 42 43 # 0x80 = N/A or OVP 44 OUTPUT_ON = 0x40 45 OVERVOLTAGE_OVERCURRENT_PROTECTION = 0x20 # or OCP or Lock 0=Lock, 1=Unlock 46 # 0x10 = Beep 0=Off, 1=On 47 # 0x08+0x04 = Tracking 00=Independent, 01=Tracking series,11=Tracking parallel 48 # 0x02 = CH2 0=CC mode, 1=CV mode 49 CONSTANT_VOLTAGE = 0x01 # Constant current if off 50 51 def __str__(self) -> str: 52 flags = [str(flag.name).title() for flag in Status if self.value & flag] 53 if not self.value & self.OUTPUT_ON: 54 flags.append("Output Off") 55 if not self.value & self.CONSTANT_VOLTAGE: 56 flags.append("Constant Current") 57 return " | ".join(flags)
Reset flags from the CSR.
OUTPUT_ON =
<Status.OUTPUT_ON: 64>
OVERVOLTAGE_OVERCURRENT_PROTECTION =
<Status.OVERVOLTAGE_OVERCURRENT_PROTECTION: 32>
CONSTANT_VOLTAGE =
<Status.CONSTANT_VOLTAGE: 1>
Inherited Members
- builtins.int
- conjugate
- bit_length
- bit_count
- to_bytes
- from_bytes
- as_integer_ratio
- is_integer
- real
- imag
- numerator
- denominator
- enum.Enum
- name
- value
class
Korad:
60class Korad: 61 """Korad KA6003P power supply command wrapper.""" 62 63 VOLTAGE_MAX = 60 64 VOLTAGE_MIN = 1.2 65 CURRENT_MAX = 3 66 67 def __init__(self, korad_id: int, resource: SafeResource): 68 """ 69 Initialize the KA6003P wrapper with a specific PyVISA resource. 70 This class does NOT open the resource, you have to open it for yourself! 71 """ 72 self.id: int = korad_id 73 self.resource = resource 74 self._overvoltage_protection = False 75 self._overcurrent_protection = False 76 self._beep = False 77 self.disable() 78 79 @atexit.register 80 def __atexit__(): 81 """Configure a safe shut down for when the class instance is destroyed.""" 82 self.disable() 83 84 @property 85 def status(self) -> Status: 86 """Get the power supply status.""" 87 self.resource.write("STATUS?") 88 response = self.resource.read_raw()[1:] 89 return Status(int.from_bytes(response, byteorder="big") if len(response) == 1 else 0) 90 91 def set_profile(self, volts: float, amps: float): 92 """Sets charging profile""" 93 self.volts = volts 94 self.amps = amps 95 96 @property 97 def overcurrent_protection(self) -> float: 98 """Get the last set overcurrent protection state.""" 99 return self._overcurrent_protection 100 101 @overcurrent_protection.setter 102 def overcurrent_protection(self, enabled: bool): 103 """Enable or disable overcurrent protection.""" 104 self._overcurrent_protection = enabled 105 self.resource.write(f"OCP{int(enabled)}") 106 107 @property 108 def overvoltage_protection(self) -> float: 109 """Get the last set overvoltage protection state.""" 110 return self._overvoltage_protection 111 112 @overvoltage_protection.setter 113 def overvoltage_protection(self, enabled: bool): 114 """Enable or disable overvoltage protection.""" 115 self._overvoltage_protection = enabled 116 self.resource.write(f"OVP{int(enabled)}") 117 118 @property 119 def measured_amps(self) -> float: 120 """Measures current.""" 121 return float(self.resource.query("IOUT1?")) 122 123 @property 124 def amps(self) -> float: 125 """Get the target current.""" 126 return float(self.resource.query("ISET1?")) 127 128 @amps.setter 129 def amps(self, new_amps: float): 130 """Set the target current.""" 131 if new_amps > Korad.CURRENT_MAX: 132 raise RuntimeError(f"Current of {new_amps}A exceeds maximum of {Korad.CURRENT_MAX}A.") 133 self.resource.write(f"ISET1:{new_amps}") 134 135 @property 136 def measured_volts(self) -> float: 137 """Measures voltage.""" 138 return float(self.resource.query("VOUT1?")) 139 140 @property 141 def volts(self) -> float: 142 """Get the target voltage.""" 143 return float(self.resource.query("VSET1?")) 144 145 @volts.setter 146 def volts(self, new_volts: float): 147 """Set the target voltage""" 148 if not Korad.VOLTAGE_MIN <= new_volts <= Korad.VOLTAGE_MAX: 149 raise RuntimeError(f"{new_volts}V is out of range {Korad.VOLTAGE_MIN}V to {Korad.VOLTAGE_MAX}V.") 150 self.resource.write(f"VSET1:{new_volts}") 151 152 @property 153 def beep(self) -> bool: 154 """Get the beep setting.""" 155 return self._beep 156 157 @beep.setter 158 def beep(self, enable_beep: bool): 159 """Turns on or off the beep.""" 160 self.resource.write(f"BEEP{int(enable_beep)}") 161 162 def recall(self, memory_number: int): 163 """Recalls a panel setting from memory 1 to 5.""" 164 self.resource.write(f"RCL{memory_number}") 165 166 def store(self, memory_number: int): 167 """Stores a panel setting to memory 1 to 5.""" 168 self.resource.write(f"SAV{memory_number}") 169 170 def enable(self): 171 """Enable the output.""" 172 logger.write_info_to_report("Enabling Korad charger") 173 self.resource.write("OUT1") 174 175 def disable(self): 176 """Disable the output.""" 177 logger.write_info_to_report("Disabling Korad charger") 178 self.resource.write("OUT0")
Korad KA6003P power supply command wrapper.
Korad(korad_id: int, resource: hitl_tester.modules.bms_types.SafeResource)
67 def __init__(self, korad_id: int, resource: SafeResource): 68 """ 69 Initialize the KA6003P wrapper with a specific PyVISA resource. 70 This class does NOT open the resource, you have to open it for yourself! 71 """ 72 self.id: int = korad_id 73 self.resource = resource 74 self._overvoltage_protection = False 75 self._overcurrent_protection = False 76 self._beep = False 77 self.disable() 78 79 @atexit.register 80 def __atexit__(): 81 """Configure a safe shut down for when the class instance is destroyed.""" 82 self.disable()
Initialize the KA6003P wrapper with a specific PyVISA resource. This class does NOT open the resource, you have to open it for yourself!
status: Status
84 @property 85 def status(self) -> Status: 86 """Get the power supply status.""" 87 self.resource.write("STATUS?") 88 response = self.resource.read_raw()[1:] 89 return Status(int.from_bytes(response, byteorder="big") if len(response) == 1 else 0)
Get the power supply status.
def
set_profile(self, volts: float, amps: float):
91 def set_profile(self, volts: float, amps: float): 92 """Sets charging profile""" 93 self.volts = volts 94 self.amps = amps
Sets charging profile
overcurrent_protection: float
96 @property 97 def overcurrent_protection(self) -> float: 98 """Get the last set overcurrent protection state.""" 99 return self._overcurrent_protection
Get the last set overcurrent protection state.
overvoltage_protection: float
107 @property 108 def overvoltage_protection(self) -> float: 109 """Get the last set overvoltage protection state.""" 110 return self._overvoltage_protection
Get the last set overvoltage protection state.
measured_amps: float
118 @property 119 def measured_amps(self) -> float: 120 """Measures current.""" 121 return float(self.resource.query("IOUT1?"))
Measures current.
amps: float
123 @property 124 def amps(self) -> float: 125 """Get the target current.""" 126 return float(self.resource.query("ISET1?"))
Get the target current.
measured_volts: float
135 @property 136 def measured_volts(self) -> float: 137 """Measures voltage.""" 138 return float(self.resource.query("VOUT1?"))
Measures voltage.
volts: float
140 @property 141 def volts(self) -> float: 142 """Get the target voltage.""" 143 return float(self.resource.query("VSET1?"))
Get the target voltage.
def
recall(self, memory_number: int):
162 def recall(self, memory_number: int): 163 """Recalls a panel setting from memory 1 to 5.""" 164 self.resource.write(f"RCL{memory_number}")
Recalls a panel setting from memory 1 to 5.
def
store(self, memory_number: int):
166 def store(self, memory_number: int): 167 """Stores a panel setting to memory 1 to 5.""" 168 self.resource.write(f"SAV{memory_number}")
Stores a panel setting to memory 1 to 5.