hitl_tester.modules.cyber_6t.gpio

Controls the USB GPIO

(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"""
  2Controls the USB GPIO
  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
 30import atexit
 31from dataclasses import dataclass
 32from enum import IntEnum
 33
 34import serial
 35from serial import SerialException
 36
 37from hitl_tester.modules.logger import logger
 38
 39
 40class NoDataException(Exception):
 41    """Exception raised when no data is received"""
 42
 43    def __init__(self):
 44        super().__init__("No data received")
 45
 46
 47class Baud(IntEnum):
 48    """The 6T baud rate in kb/s."""
 49
 50    RATE_1000 = 0
 51    RATE_500 = 1
 52    RATE_250 = 2
 53    RATE_250_LIMITED = 3
 54
 55
 56@dataclass
 57class Pinout6T:
 58    """Represents teh 6T pinout."""
 59
 60    dormant_1: bool = True
 61    dormant_2: bool = True
 62    reset: bool = True
 63    baud_rate: Baud = Baud.RATE_250_LIMITED
 64    position_id: int = 0xF  # ID 254 (NULL address)
 65
 66    def __int__(self) -> int:
 67        return (
 68            (self.position_id & 0xF) << 5
 69            | (self.baud_rate & 0x3) << 3
 70            | self.reset << 2
 71            | self.dormant_2 << 1
 72            | self.dormant_1
 73        )
 74
 75
 76class GPIOController:
 77    """GPIO controller"""
 78
 79    battery_1 = Pinout6T()
 80    battery_2 = Pinout6T()
 81    battery_3 = Pinout6T()
 82
 83    @staticmethod
 84    def get_index(num: int) -> str:
 85        """Gets correct index"""
 86        if num < 10:
 87            return str(num)
 88        return chr(55 + num)
 89
 90    @staticmethod
 91    def on(num: int):
 92        """Turn GPIO on"""
 93        GPIOController.call(f"gpio set {GPIOController.get_index(num)}")
 94
 95    @staticmethod
 96    def off(num: int):
 97        """Turn GPIO off"""
 98        GPIOController.call(f"gpio clear {GPIOController.get_index(num)}")
 99
100    @staticmethod
101    def status() -> str:
102        """Shows GPIO status"""
103        return GPIOController.call("gpio readall")
104
105    @staticmethod
106    def call(cmd: str) -> str:
107        """Talk to GPIO"""
108        response = None
109        try:
110            # 9600,8,N,1
111            with serial.Serial("/dev/taf-gpio", timeout=1) as ser:
112                ser.write(bytes(cmd + "\r", "utf-8"))
113                response = ser.read(25)
114                if not response:
115                    raise NoDataException
116                response = response[len(cmd) : -1]
117                if not response:
118                    raise NoDataException
119                response = response.decode().strip("\n\r")
120        except (TimeoutError, NoDataException) as ex:
121            logger.write_debug_to_report(ex)
122        except (FileNotFoundError, SerialException):
123            pass
124        return response or ""
125
126    @classmethod
127    def reset(cls):
128        """Reset GPIO to floating."""
129        cls.call("gpio writeall 00000000")
130        cls.call("gpio iodir ffffffff")
131
132    @classmethod
133    def set_battery_state(cls):
134        """Set the battery pins according to the pinouts for battery 1, 2, 3."""
135        cls.call("gpio writeall 00000000")
136        cls.call(f"gpio iodir {int(cls.battery_3) << 18 | int(cls.battery_2) << 9 | int(cls.battery_1):08x}")
137
138
139@atexit.register
140def __atexit__():
141    """Float all pins when shutting down."""
142    logger.write_debug_to_report("Shutting down GPIO.")
143    GPIOController.battery_1 = Pinout6T()
144    GPIOController.battery_2 = Pinout6T()
145    GPIOController.battery_3 = Pinout6T()
146    GPIOController.set_battery_state()
147
148
149if __name__ == "__main__":
150    logger.write_debug_to_report("Power cycling...")
151    GPIOController.reset()
152    logger.write_debug_to_report(f"Status: {GPIOController.status()}")
class NoDataException(builtins.Exception):
41class NoDataException(Exception):
42    """Exception raised when no data is received"""
43
44    def __init__(self):
45        super().__init__("No data received")

Exception raised when no data is received

Inherited Members
builtins.BaseException
with_traceback
add_note
args
class Baud(enum.IntEnum):
48class Baud(IntEnum):
49    """The 6T baud rate in kb/s."""
50
51    RATE_1000 = 0
52    RATE_500 = 1
53    RATE_250 = 2
54    RATE_250_LIMITED = 3

The 6T baud rate in kb/s.

RATE_1000 = <Baud.RATE_1000: 0>
RATE_500 = <Baud.RATE_500: 1>
RATE_250 = <Baud.RATE_250: 2>
RATE_250_LIMITED = <Baud.RATE_250_LIMITED: 3>
Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
@dataclass
class Pinout6T:
57@dataclass
58class Pinout6T:
59    """Represents teh 6T pinout."""
60
61    dormant_1: bool = True
62    dormant_2: bool = True
63    reset: bool = True
64    baud_rate: Baud = Baud.RATE_250_LIMITED
65    position_id: int = 0xF  # ID 254 (NULL address)
66
67    def __int__(self) -> int:
68        return (
69            (self.position_id & 0xF) << 5
70            | (self.baud_rate & 0x3) << 3
71            | self.reset << 2
72            | self.dormant_2 << 1
73            | self.dormant_1
74        )

Represents teh 6T pinout.

Pinout6T( dormant_1: bool = True, dormant_2: bool = True, reset: bool = True, baud_rate: Baud = <Baud.RATE_250_LIMITED: 3>, position_id: int = 15)
dormant_1: bool = True
dormant_2: bool = True
reset: bool = True
baud_rate: Baud = <Baud.RATE_250_LIMITED: 3>
position_id: int = 15
class GPIOController:
 77class GPIOController:
 78    """GPIO controller"""
 79
 80    battery_1 = Pinout6T()
 81    battery_2 = Pinout6T()
 82    battery_3 = Pinout6T()
 83
 84    @staticmethod
 85    def get_index(num: int) -> str:
 86        """Gets correct index"""
 87        if num < 10:
 88            return str(num)
 89        return chr(55 + num)
 90
 91    @staticmethod
 92    def on(num: int):
 93        """Turn GPIO on"""
 94        GPIOController.call(f"gpio set {GPIOController.get_index(num)}")
 95
 96    @staticmethod
 97    def off(num: int):
 98        """Turn GPIO off"""
 99        GPIOController.call(f"gpio clear {GPIOController.get_index(num)}")
100
101    @staticmethod
102    def status() -> str:
103        """Shows GPIO status"""
104        return GPIOController.call("gpio readall")
105
106    @staticmethod
107    def call(cmd: str) -> str:
108        """Talk to GPIO"""
109        response = None
110        try:
111            # 9600,8,N,1
112            with serial.Serial("/dev/taf-gpio", timeout=1) as ser:
113                ser.write(bytes(cmd + "\r", "utf-8"))
114                response = ser.read(25)
115                if not response:
116                    raise NoDataException
117                response = response[len(cmd) : -1]
118                if not response:
119                    raise NoDataException
120                response = response.decode().strip("\n\r")
121        except (TimeoutError, NoDataException) as ex:
122            logger.write_debug_to_report(ex)
123        except (FileNotFoundError, SerialException):
124            pass
125        return response or ""
126
127    @classmethod
128    def reset(cls):
129        """Reset GPIO to floating."""
130        cls.call("gpio writeall 00000000")
131        cls.call("gpio iodir ffffffff")
132
133    @classmethod
134    def set_battery_state(cls):
135        """Set the battery pins according to the pinouts for battery 1, 2, 3."""
136        cls.call("gpio writeall 00000000")
137        cls.call(f"gpio iodir {int(cls.battery_3) << 18 | int(cls.battery_2) << 9 | int(cls.battery_1):08x}")

GPIO controller

battery_1 = Pinout6T(dormant_1=True, dormant_2=True, reset=True, baud_rate=<Baud.RATE_250_LIMITED: 3>, position_id=15)
battery_2 = Pinout6T(dormant_1=True, dormant_2=True, reset=True, baud_rate=<Baud.RATE_250_LIMITED: 3>, position_id=15)
battery_3 = Pinout6T(dormant_1=True, dormant_2=True, reset=True, baud_rate=<Baud.RATE_250_LIMITED: 3>, position_id=15)
@staticmethod
def get_index(num: int) -> str:
84    @staticmethod
85    def get_index(num: int) -> str:
86        """Gets correct index"""
87        if num < 10:
88            return str(num)
89        return chr(55 + num)

Gets correct index

@staticmethod
def on(num: int):
91    @staticmethod
92    def on(num: int):
93        """Turn GPIO on"""
94        GPIOController.call(f"gpio set {GPIOController.get_index(num)}")

Turn GPIO on

@staticmethod
def off(num: int):
96    @staticmethod
97    def off(num: int):
98        """Turn GPIO off"""
99        GPIOController.call(f"gpio clear {GPIOController.get_index(num)}")

Turn GPIO off

@staticmethod
def status() -> str:
101    @staticmethod
102    def status() -> str:
103        """Shows GPIO status"""
104        return GPIOController.call("gpio readall")

Shows GPIO status

@staticmethod
def call(cmd: str) -> str:
106    @staticmethod
107    def call(cmd: str) -> str:
108        """Talk to GPIO"""
109        response = None
110        try:
111            # 9600,8,N,1
112            with serial.Serial("/dev/taf-gpio", timeout=1) as ser:
113                ser.write(bytes(cmd + "\r", "utf-8"))
114                response = ser.read(25)
115                if not response:
116                    raise NoDataException
117                response = response[len(cmd) : -1]
118                if not response:
119                    raise NoDataException
120                response = response.decode().strip("\n\r")
121        except (TimeoutError, NoDataException) as ex:
122            logger.write_debug_to_report(ex)
123        except (FileNotFoundError, SerialException):
124            pass
125        return response or ""

Talk to GPIO

@classmethod
def reset(cls):
127    @classmethod
128    def reset(cls):
129        """Reset GPIO to floating."""
130        cls.call("gpio writeall 00000000")
131        cls.call("gpio iodir ffffffff")

Reset GPIO to floating.

@classmethod
def set_battery_state(cls):
133    @classmethod
134    def set_battery_state(cls):
135        """Set the battery pins according to the pinouts for battery 1, 2, 3."""
136        cls.call("gpio writeall 00000000")
137        cls.call(f"gpio iodir {int(cls.battery_3) << 18 | int(cls.battery_2) << 9 | int(cls.battery_1):08x}")

Set the battery pins according to the pinouts for battery 1, 2, 3.