Source code for sub8_thrust_and_kill_board.packets

import struct
from collections import namedtuple

from mil_usb_to_can.sub8 import ApplicationPacket

# CAN channel to send thrust messages to
THRUST_SEND_ID = 0x21
# CAN channel to send kill messages to
KILL_SEND_ID = 0x11


[docs]class KillMessage(ApplicationPacket): """ Represents a packet sent to kill/thrust board which contains a command or response related to kill status. Inherits from :class:`~mil_usb_to_can.ApplicationPacket`. .. container:: operations .. describe:: str(x) Pretty-prints the class name and each of the three packet attributes. Attributes: IDENTIFIER (int): The packet's identifier. Set equal to the ordinal value of "K", or 75. COMMAND (int): Identifier representing the packet as a command packet. Equal to 67. This identifier should only be found in the first byte of the payload. RESPONSE (int): Identifier representing the packet as a response packet. Equal to 82. This identifier should only be found in the first byte of the payload. HARD (int): Identifier representing the type of kill as a hard kill. Equal to 72. This identifier is designed to be used in the second byte of the payload. SOFT (int): Identifier representing the type of kill as a soft kill. Equal to 83. This identifier is designed to be used in the second byte of the payload. ASSERTED (int): Identifier equal to 65. This identifier is meant to be used in the third byte of the payload. UNASSERTED (int): Identifier equal to 85. This identifier is meant to be used in the third byte of the payload. """ IDENTIFIER = ord("K") COMMAND = 0x43 RESPONSE = 0x52 HARD = 0x48 SOFT = 0x53 ASSERTED = 0x41 UNASSERTED = 0x55 PADDING = 0x00
[docs] @classmethod def create_kill_message( cls, command: bool = False, hard: bool = False, asserted: bool = False, ): """ Creates a kill message containing three bytes of information, specified as parameters. Args: command (bool): Whether to make the kill message a command. If ``False``, then the packet will represent a response. hard (bool): Whether to make the packet kill type hard. If ``False``, then the packet will represent a soft kill. asserted (bool): Whether to make the packet asserted. If ``False``, then an unasserted packet is generated. """ command_byte = cls.COMMAND if command else cls.RESPONSE hard_soft_byte = cls.HARD if hard else cls.SOFT assert_unassert_byte = cls.ASSERTED if asserted else cls.UNASSERTED payload = struct.pack( "BBBB", command_byte, hard_soft_byte, assert_unassert_byte, cls.PADDING, ) return cls(cls.IDENTIFIER, payload)
@property def is_command(self) -> bool: """ Whether the packet represents a command. Returns: bool: The status of the packet's command/response type. """ return self.payload[0] == self.COMMAND @property def is_response(self) -> bool: """ Whether the packet represents a response. Returns: bool: The status of the packet's command/response type. """ return self.payload[0] == self.RESPONSE @property def is_hard(self) -> bool: """ Whether the packet represents a hard kill. Returns: bool: The status of the packet's hard/soft kill type. """ return self.payload[1] == self.HARD @property def is_soft(self) -> bool: """ Whether the packet represents a soft kill. Returns: bool: The status of the packet's hard/soft kill type. """ return self.payload[1] == self.SOFT @property def is_asserted(self): """ Whether the packet represents an asserted packet. Returns: bool: The status of the packet's asserteed/unasserted type. """ return self.payload[2] == self.ASSERTED @property def is_unasserted(self): """ Whether the packet represents an unasserted packet. Returns: bool: The status of the packet's asserteed/unasserted type. """ return self.payload[2] == self.UNASSERTED def __str__(self): return f"KillMessage(command={self.is_command}, hard={self.is_hard}, asserted={self.is_asserted})"
KillStatus = namedtuple( "KillStatus", [ "heartbeat_lost", "mobo_soft_kill", "switch_soft_kill", "soft_killed", "hard_killed", "thrusters_initializing", "go_switch", "soft_kill_switch", "hard_kill_switch", ], ) class StatusMessage(KillStatus): BIT_MASK = KillStatus( 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 1 << 11, 1 << 12, 1 << 13, 1 << 14, ) STRUCT_FORMAT = "=h" def __new__(cls, *args): """ Constructs a new namedtuple to derive the class from. This can't be done in __init__ because namedtuples are immutable. """ return super().__new__(cls, *args) @classmethod def from_bytes(cls, data): unpacked = struct.unpack(cls.STRUCT_FORMAT, data)[0] args = [] for field in KillStatus._fields: args.append(bool(unpacked & getattr(cls.BIT_MASK, field))) return cls(*args) def __bytes__(self): out = 0 for field in KillStatus._fields: if getattr(self, field): out = out | getattr(self.BIT_MASK, field) return struct.pack(self.STRUCT_FORMAT, out)
[docs]class HeartbeatMessage(ApplicationPacket): """ Represents the special heartbeat packet send to kill and thruster board. Inherits from :class:`~mil_usb_to_can.ApplicationPacket`. .. container:: operations .. describe:: str(x) Pretty-prints the class name. Attributes: IDENTIFIER (int): The packet identifier. Set equal to the ordinal value of "H," or 72. """ IDENTIFIER = ord("H")
[docs] @classmethod def create(cls): """ Creates a new heartbeat message to be sent. """ return cls(cls.IDENTIFIER, struct.pack("BB", ord("M"), 0))
def __str__(self): return "HeartbeatMessage()"
[docs]class ThrustPacket(ApplicationPacket): """ Represents a command send to the thrust/kill board to set the PWM of a thruster. Inherits from :class:`~mil_usb_to_can.ApplicationPacket`. .. container:: operations .. describe:: str(x) Pretty-prints the class name and each of the two packet attributes, the thruster ID and the command. Attributes: IDENTIFIER (int): The packet identifier, equal to the ordinal value of "T," or 84. """ IDENTIFIER = ord("T")
[docs] @classmethod def create_thrust_packet(cls, thruster_id: int, command: float): """ Creates a thruster packet given an ID and command. Args: thruster_id (int): The ID of the thruster to create a packet for. command (float): The command to associate with the packet. """ payload = struct.pack("=Bf", thruster_id, command) return cls(cls.IDENTIFIER, payload)
@property def thruster_id(self) -> int: """ The thruster ID associated with the packet. Returns: int: The ID. """ return struct.unpack("B", self.payload[0])[0] @property def command(self) -> float: """ The command associated with the packet. Returns: float: The associated command. """ return struct.unpack("f", self.payload[1:])[0] def __str__(self): return f"ThrustPacket(thruster_id={self.thruster_id}, command={self.command})"