Module pycti.channel_interface
Expand source code
import logging
import os
from pydantic import BaseModel
from pydantic import validator
from .messages import Msg
from .cycler_interface import CyclerInterface
logger = logging.getLogger(__name__)
class ChannelInterface(CyclerInterface):
"""
Class for interfacing with Arbin battery cycler at a channel level.
"""
def __init__(self, config: dict, env_path: str = os.path.join(os.getcwd(), '.env')):
"""
Creates a class instance for interfacing with Arbin battery cycler at a channel level.
Parameters
----------
config : dict
A configuration dictionary. Must contain the following keys:
channel : int
The channel to target with the ChannelInterface class instance.
test_name : *optional* : str
The test name to use if using the ChannelInterface to start a test.
schedule_name : *optional* : str
The name of the schedule file to use if using the ChannelInterface to start a test.
ip_address : str
The IP address of the Arbin host computer.
port : int
The TCP port to communicate through.
timeout_s : *optional* : float
How long to wait before timing out on TCP communication. Defaults to 3 seconds.
msg_buffer_size : *optional* : int
How big of a message buffer to use for sending/receiving messages.
A minimum of 1024 bytes is recommended. Defaults to 4096 bytes.
env_path : *optional* : str
The path to the `.env` file containing the Arbin CTI username,`ARBIN_CTI_USERNAME`, and password, `ARBIN_CTI_PASSWORD`.
Defaults to looking in the working directory.
"""
self.__config = ChannelInterfaceConfig(**config)
super().__init__(self.__config.dict(), env_path)
def read_channel_status(self) -> dict:
"""
Method to read the status of the channel defined in the config.
Returns
-------
status : dict
A dictionary detailing the status of the channel. Returns None if there is an issue.
"""
# Add to channel value to account for zero indexing subtraction in parent method.
return super().read_channel_status(channel=(self.__config.channel+1))
def assign_schedule(self) -> bool:
"""
Method to assign a schedule to the channel defined in the config.
Returns
-------
success : bool
True/False based on whether the schedule was assigned without issue.
"""
success = False
if not self.__config.schedule_name:
logger.error("Schedule name undefined!")
return success
assign_schedule_msg_tx_bin = Msg.AssignSchedule.Client.pack(
{'channel': self.__config.channel, 'schedule': self.__config.schedule_name})
response_msg_bin = self._send_receive_msg(
assign_schedule_msg_tx_bin)
if response_msg_bin:
assign_schedule_msg_rx_dict = Msg.AssignSchedule.Server.unpack(
response_msg_bin)
if assign_schedule_msg_rx_dict['result'] == 'success':
success = True
logger.info(
f'Successfully assigned schedule {self.__config.schedule_name} to channel {self.__config.channel}')
logger.debug(assign_schedule_msg_rx_dict)
else:
logger.error(
f'Failed to assign schedule {self.__config.schedule_name}! Issue: {assign_schedule_msg_rx_dict["result"]}')
return success
def start_test(self) -> bool:
"""
Starts channel on method specified in config.
Returns
-------
success : bool
True/False based on whether the test was started without issue.
"""
success = False
if not self.__config.test_name:
logger.error("Test name undefined!")
return success
# Make sure the schedule is assigned before starting the test to avoid any funny business
if self.assign_schedule():
start_test_msg_tx_bin = Msg.StartSchedule.Client.pack(
{'channel': self.__config.channel, 'test_name': self.__config.test_name})
response_msg_bin = self._send_receive_msg(start_test_msg_tx_bin)
if response_msg_bin:
start_test_msg_rx_dict = Msg.StartSchedule.Server.unpack(
response_msg_bin)
if start_test_msg_rx_dict['result'] == 'success':
success = True
logger.info(
f'Successfully started test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}')
logger.debug(start_test_msg_rx_dict)
else:
logger.error(
f'Failed to start test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}. Issue: {start_test_msg_rx_dict["result"]}')
return success
def stop_test(self) -> bool:
"""
Stops the test running on the channel specified in the config.
Returns
-------
success : bool
True/False based on whether the test stopped without issue.
Also returns True if no test was running on the channel.
"""
success = False
stop_test_msg_tx_bin = Msg.StopSchedule.Client.pack(
{'channel': self.__config.channel})
response_msg_bin = self._send_receive_msg(
stop_test_msg_tx_bin)
if response_msg_bin:
stop_test_msg_rx_dict = Msg.StopSchedule.Server.unpack(
response_msg_bin)
if stop_test_msg_rx_dict['result'] == 'success':
success = True
logger.info(
f'Successfully stopped test on channel {self.__config.channel}')
logger.debug(stop_test_msg_rx_dict)
else:
logger.error(
f'Failed to stop test on channel {self.__config.channel}! Issue: {stop_test_msg_rx_dict["result"]}')
return success
def set_meta_variable(self, mv_num: int, mv_value: float) -> bool:
"""
Sets the passed meta variable number `mv_num` to the passed value `mv_value`
on the channel specified in the config. Note the test must be running.
Parameters
----------
mv_num : int
The meta variable number to set. Must be between 1 and 16 (inclusive)
mv_value : float
The meta variable value to set.
Returns
-------
success : bool
True/False based on whether the meta variable was set.
"""
success = False
updated_msg_vals = {}
updated_msg_vals['channel'] = self.__config.channel
updated_msg_vals['mv_meta_code'] = Msg.SetMetaVariable.Client.mv_channel_codes[mv_num]
updated_msg_vals['mv_data'] = mv_value
set_mv_msg_tx_bin = Msg.SetMetaVariable.Client.pack(updated_msg_vals)
response_msg_bin = self._send_receive_msg(
set_mv_msg_tx_bin)
if response_msg_bin:
set_mv_msg_rx_dict = Msg.SetMetaVariable.Server.unpack(
response_msg_bin)
if set_mv_msg_rx_dict['result'] == 'success':
success = True
logger.info(
f'Successfully set meta variable {mv_num} to a value of {mv_value}')
logger.debug(set_mv_msg_rx_dict)
else:
logger.error(
f'Failed to set meta variable {mv_num} to a value of {mv_value}! Issue: {set_mv_msg_rx_dict["result"]}')
return success
class ChannelInterfaceConfig(BaseModel):
'''
Holds channel config information for the CyclerInterface class.
Parameters
----------
channel : int
The channel to target with the ChannelInterface class instance.
test_name : *optional*
The test name to use if using the ChannelInterface to start a test.
schedule_name : str
The name of the schedule file to use if using the ChannelInterface to start a test.
ip_address : str
The IP address of the Arbin host computer.
port : int
The TCP port to communicate through.
timeout_s : float
How long to wait before timing out on TCP communication. Defaults to 3 seconds.
msg_buffer_size : int
How big of a message buffer to use for sending/receiving messages.
A minimum of 1024 bytes is recommended. Defaults to 4096 bytes.
'''
channel: int
test_name: str = None
schedule_name: str = None
ip_address: str
port: int
timeout_s: float = 3.0
msg_buffer_size: int = 4096
@validator('channel')
def username_alphanumeric(cls, v):
if v < 1:
raise ValueError('Channel must be greater than zero!')
return v-1
Classes
class ChannelInterface (config: dict, env_path: str = '/Users/zander/Documents/Work/BattGenie/pycti/.env')
-
Class for interfacing with Arbin battery cycler at a channel level.
Creates a class instance for interfacing with Arbin battery cycler at a channel level.
Parameters
config
:dict
- A configuration dictionary. Must contain the following keys: channel : int The channel to target with the ChannelInterface class instance. test_name : optional : str The test name to use if using the ChannelInterface to start a test. schedule_name : optional : str The name of the schedule file to use if using the ChannelInterface to start a test. ip_address : str The IP address of the Arbin host computer. port : int The TCP port to communicate through. timeout_s : optional : float How long to wait before timing out on TCP communication. Defaults to 3 seconds. msg_buffer_size : optional : int How big of a message buffer to use for sending/receiving messages. A minimum of 1024 bytes is recommended. Defaults to 4096 bytes.
env_path
:*optional* : str
- The path to the
.env
file containing the Arbin CTI username,ARBIN_CTI_USERNAME
, and password,ARBIN_CTI_PASSWORD
. Defaults to looking in the working directory.
Expand source code
class ChannelInterface(CyclerInterface): """ Class for interfacing with Arbin battery cycler at a channel level. """ def __init__(self, config: dict, env_path: str = os.path.join(os.getcwd(), '.env')): """ Creates a class instance for interfacing with Arbin battery cycler at a channel level. Parameters ---------- config : dict A configuration dictionary. Must contain the following keys: channel : int The channel to target with the ChannelInterface class instance. test_name : *optional* : str The test name to use if using the ChannelInterface to start a test. schedule_name : *optional* : str The name of the schedule file to use if using the ChannelInterface to start a test. ip_address : str The IP address of the Arbin host computer. port : int The TCP port to communicate through. timeout_s : *optional* : float How long to wait before timing out on TCP communication. Defaults to 3 seconds. msg_buffer_size : *optional* : int How big of a message buffer to use for sending/receiving messages. A minimum of 1024 bytes is recommended. Defaults to 4096 bytes. env_path : *optional* : str The path to the `.env` file containing the Arbin CTI username,`ARBIN_CTI_USERNAME`, and password, `ARBIN_CTI_PASSWORD`. Defaults to looking in the working directory. """ self.__config = ChannelInterfaceConfig(**config) super().__init__(self.__config.dict(), env_path) def read_channel_status(self) -> dict: """ Method to read the status of the channel defined in the config. Returns ------- status : dict A dictionary detailing the status of the channel. Returns None if there is an issue. """ # Add to channel value to account for zero indexing subtraction in parent method. return super().read_channel_status(channel=(self.__config.channel+1)) def assign_schedule(self) -> bool: """ Method to assign a schedule to the channel defined in the config. Returns ------- success : bool True/False based on whether the schedule was assigned without issue. """ success = False if not self.__config.schedule_name: logger.error("Schedule name undefined!") return success assign_schedule_msg_tx_bin = Msg.AssignSchedule.Client.pack( {'channel': self.__config.channel, 'schedule': self.__config.schedule_name}) response_msg_bin = self._send_receive_msg( assign_schedule_msg_tx_bin) if response_msg_bin: assign_schedule_msg_rx_dict = Msg.AssignSchedule.Server.unpack( response_msg_bin) if assign_schedule_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully assigned schedule {self.__config.schedule_name} to channel {self.__config.channel}') logger.debug(assign_schedule_msg_rx_dict) else: logger.error( f'Failed to assign schedule {self.__config.schedule_name}! Issue: {assign_schedule_msg_rx_dict["result"]}') return success def start_test(self) -> bool: """ Starts channel on method specified in config. Returns ------- success : bool True/False based on whether the test was started without issue. """ success = False if not self.__config.test_name: logger.error("Test name undefined!") return success # Make sure the schedule is assigned before starting the test to avoid any funny business if self.assign_schedule(): start_test_msg_tx_bin = Msg.StartSchedule.Client.pack( {'channel': self.__config.channel, 'test_name': self.__config.test_name}) response_msg_bin = self._send_receive_msg(start_test_msg_tx_bin) if response_msg_bin: start_test_msg_rx_dict = Msg.StartSchedule.Server.unpack( response_msg_bin) if start_test_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully started test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}') logger.debug(start_test_msg_rx_dict) else: logger.error( f'Failed to start test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}. Issue: {start_test_msg_rx_dict["result"]}') return success def stop_test(self) -> bool: """ Stops the test running on the channel specified in the config. Returns ------- success : bool True/False based on whether the test stopped without issue. Also returns True if no test was running on the channel. """ success = False stop_test_msg_tx_bin = Msg.StopSchedule.Client.pack( {'channel': self.__config.channel}) response_msg_bin = self._send_receive_msg( stop_test_msg_tx_bin) if response_msg_bin: stop_test_msg_rx_dict = Msg.StopSchedule.Server.unpack( response_msg_bin) if stop_test_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully stopped test on channel {self.__config.channel}') logger.debug(stop_test_msg_rx_dict) else: logger.error( f'Failed to stop test on channel {self.__config.channel}! Issue: {stop_test_msg_rx_dict["result"]}') return success def set_meta_variable(self, mv_num: int, mv_value: float) -> bool: """ Sets the passed meta variable number `mv_num` to the passed value `mv_value` on the channel specified in the config. Note the test must be running. Parameters ---------- mv_num : int The meta variable number to set. Must be between 1 and 16 (inclusive) mv_value : float The meta variable value to set. Returns ------- success : bool True/False based on whether the meta variable was set. """ success = False updated_msg_vals = {} updated_msg_vals['channel'] = self.__config.channel updated_msg_vals['mv_meta_code'] = Msg.SetMetaVariable.Client.mv_channel_codes[mv_num] updated_msg_vals['mv_data'] = mv_value set_mv_msg_tx_bin = Msg.SetMetaVariable.Client.pack(updated_msg_vals) response_msg_bin = self._send_receive_msg( set_mv_msg_tx_bin) if response_msg_bin: set_mv_msg_rx_dict = Msg.SetMetaVariable.Server.unpack( response_msg_bin) if set_mv_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully set meta variable {mv_num} to a value of {mv_value}') logger.debug(set_mv_msg_rx_dict) else: logger.error( f'Failed to set meta variable {mv_num} to a value of {mv_value}! Issue: {set_mv_msg_rx_dict["result"]}') return success
Ancestors
Methods
def assign_schedule(self) ‑> bool
-
Method to assign a schedule to the channel defined in the config.
Returns
success
:bool
- True/False based on whether the schedule was assigned without issue.
Expand source code
def assign_schedule(self) -> bool: """ Method to assign a schedule to the channel defined in the config. Returns ------- success : bool True/False based on whether the schedule was assigned without issue. """ success = False if not self.__config.schedule_name: logger.error("Schedule name undefined!") return success assign_schedule_msg_tx_bin = Msg.AssignSchedule.Client.pack( {'channel': self.__config.channel, 'schedule': self.__config.schedule_name}) response_msg_bin = self._send_receive_msg( assign_schedule_msg_tx_bin) if response_msg_bin: assign_schedule_msg_rx_dict = Msg.AssignSchedule.Server.unpack( response_msg_bin) if assign_schedule_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully assigned schedule {self.__config.schedule_name} to channel {self.__config.channel}') logger.debug(assign_schedule_msg_rx_dict) else: logger.error( f'Failed to assign schedule {self.__config.schedule_name}! Issue: {assign_schedule_msg_rx_dict["result"]}') return success
def read_channel_status(self) ‑> dict
-
Method to read the status of the channel defined in the config.
Returns
status
:dict
- A dictionary detailing the status of the channel. Returns None if there is an issue.
Expand source code
def read_channel_status(self) -> dict: """ Method to read the status of the channel defined in the config. Returns ------- status : dict A dictionary detailing the status of the channel. Returns None if there is an issue. """ # Add to channel value to account for zero indexing subtraction in parent method. return super().read_channel_status(channel=(self.__config.channel+1))
def set_meta_variable(self, mv_num: int, mv_value: float) ‑> bool
-
Sets the passed meta variable number
mv_num
to the passed valuemv_value
on the channel specified in the config. Note the test must be running.Parameters
mv_num
:int
- The meta variable number to set. Must be between 1 and 16 (inclusive)
mv_value
:float
- The meta variable value to set.
Returns
success
:bool
- True/False based on whether the meta variable was set.
Expand source code
def set_meta_variable(self, mv_num: int, mv_value: float) -> bool: """ Sets the passed meta variable number `mv_num` to the passed value `mv_value` on the channel specified in the config. Note the test must be running. Parameters ---------- mv_num : int The meta variable number to set. Must be between 1 and 16 (inclusive) mv_value : float The meta variable value to set. Returns ------- success : bool True/False based on whether the meta variable was set. """ success = False updated_msg_vals = {} updated_msg_vals['channel'] = self.__config.channel updated_msg_vals['mv_meta_code'] = Msg.SetMetaVariable.Client.mv_channel_codes[mv_num] updated_msg_vals['mv_data'] = mv_value set_mv_msg_tx_bin = Msg.SetMetaVariable.Client.pack(updated_msg_vals) response_msg_bin = self._send_receive_msg( set_mv_msg_tx_bin) if response_msg_bin: set_mv_msg_rx_dict = Msg.SetMetaVariable.Server.unpack( response_msg_bin) if set_mv_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully set meta variable {mv_num} to a value of {mv_value}') logger.debug(set_mv_msg_rx_dict) else: logger.error( f'Failed to set meta variable {mv_num} to a value of {mv_value}! Issue: {set_mv_msg_rx_dict["result"]}') return success
def start_test(self) ‑> bool
-
Starts channel on method specified in config.
Returns
success
:bool
- True/False based on whether the test was started without issue.
Expand source code
def start_test(self) -> bool: """ Starts channel on method specified in config. Returns ------- success : bool True/False based on whether the test was started without issue. """ success = False if not self.__config.test_name: logger.error("Test name undefined!") return success # Make sure the schedule is assigned before starting the test to avoid any funny business if self.assign_schedule(): start_test_msg_tx_bin = Msg.StartSchedule.Client.pack( {'channel': self.__config.channel, 'test_name': self.__config.test_name}) response_msg_bin = self._send_receive_msg(start_test_msg_tx_bin) if response_msg_bin: start_test_msg_rx_dict = Msg.StartSchedule.Server.unpack( response_msg_bin) if start_test_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully started test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}') logger.debug(start_test_msg_rx_dict) else: logger.error( f'Failed to start test {self.__config.test_name} with schedule {self.__config.schedule_name} on channel {self.__config.channel}. Issue: {start_test_msg_rx_dict["result"]}') return success
def stop_test(self) ‑> bool
-
Stops the test running on the channel specified in the config.
Returns
success
:bool
- True/False based on whether the test stopped without issue. Also returns True if no test was running on the channel.
Expand source code
def stop_test(self) -> bool: """ Stops the test running on the channel specified in the config. Returns ------- success : bool True/False based on whether the test stopped without issue. Also returns True if no test was running on the channel. """ success = False stop_test_msg_tx_bin = Msg.StopSchedule.Client.pack( {'channel': self.__config.channel}) response_msg_bin = self._send_receive_msg( stop_test_msg_tx_bin) if response_msg_bin: stop_test_msg_rx_dict = Msg.StopSchedule.Server.unpack( response_msg_bin) if stop_test_msg_rx_dict['result'] == 'success': success = True logger.info( f'Successfully stopped test on channel {self.__config.channel}') logger.debug(stop_test_msg_rx_dict) else: logger.error( f'Failed to stop test on channel {self.__config.channel}! Issue: {stop_test_msg_rx_dict["result"]}') return success
Inherited members
class ChannelInterfaceConfig (**data: Any)
-
Holds channel config information for the CyclerInterface class.
Parameters
channel : int The channel to target with the ChannelInterface class instance. test_name : *optional* The test name to use if using the ChannelInterface to start a test. schedule_name : str The name of the schedule file to use if using the ChannelInterface to start a test. ip_address : str The IP address of the Arbin host computer. port : int The TCP port to communicate through. timeout_s : float How long to wait before timing out on TCP communication. Defaults to 3 seconds. msg_buffer_size : int How big of a message buffer to use for sending/receiving messages. A minimum of 1024 bytes is recommended. Defaults to 4096 bytes.
Create a new model by parsing and validating input data from keyword arguments.
Raises ValidationError if the input data cannot be parsed to form a valid model.
Expand source code
class ChannelInterfaceConfig(BaseModel): ''' Holds channel config information for the CyclerInterface class. Parameters ---------- channel : int The channel to target with the ChannelInterface class instance. test_name : *optional* The test name to use if using the ChannelInterface to start a test. schedule_name : str The name of the schedule file to use if using the ChannelInterface to start a test. ip_address : str The IP address of the Arbin host computer. port : int The TCP port to communicate through. timeout_s : float How long to wait before timing out on TCP communication. Defaults to 3 seconds. msg_buffer_size : int How big of a message buffer to use for sending/receiving messages. A minimum of 1024 bytes is recommended. Defaults to 4096 bytes. ''' channel: int test_name: str = None schedule_name: str = None ip_address: str port: int timeout_s: float = 3.0 msg_buffer_size: int = 4096 @validator('channel') def username_alphanumeric(cls, v): if v < 1: raise ValueError('Channel must be greater than zero!') return v-1
Ancestors
- pydantic.main.BaseModel
- pydantic.utils.Representation
Class variables
var channel : int
var ip_address : str
var msg_buffer_size : int
var port : int
var schedule_name : str
var test_name : str
var timeout_s : float
Static methods
def username_alphanumeric(v)
-
Expand source code
@validator('channel') def username_alphanumeric(cls, v): if v < 1: raise ValueError('Channel must be greater than zero!') return v-1