import atexit from enum import Enum from compLib.LogstashLogging import Logging from compLib.Spi import Spi, Register MOTOR_COUNT = 4 encoder_start_values = [0] * (MOTOR_COUNT + 1) class Encoder(object): """Class used to read the encoders """ @staticmethod def handle_wrap(raw_value, port): """Handle overflow and underflow of int for encoders. :param raw_value: Raw value which was read on port :param port: Port, which the motor is connected to. Between 1 and 4 :raises: IndexError :return: Current encoder position """ if port <= 0 or port > MOTOR_COUNT: raise IndexError("Invalid encoder port specified!") diff = raw_value - encoder_start_values[port] if diff > 2 ** 31: diff -= 2 ** 32 elif diff < -2 ** 31: diff += 2 ** 32 return diff @staticmethod def read_raw(port: int) -> int: """Read raw encoder from a specified port. Will not be reset to 0 at start. :param port: Port, which the motor is connected to. Between 1 and 4 :raises: IndexError :return: Current encoder position """ if port <= 0 or port > MOTOR_COUNT: raise IndexError("Invalid encoder port specified!") raw_value = 0 if port == 1: raw_value = Spi.read(Register.MOTOR_1_POS_B3, 4) elif port == 2: raw_value = Spi.read(Register.MOTOR_2_POS_B3, 4) elif port == 3: raw_value = Spi.read(Register.MOTOR_3_POS_B3, 4) elif port == 4: raw_value = Spi.read(Register.MOTOR_4_POS_B3, 4) return raw_value @staticmethod def read_all_raw(): """Read all encoders at once. This is faster than read_raw as it only uses one SPI call for all encoders! :return: Tuple of all current raw encoder positions """ encoders = Spi.read_array(Register.MOTOR_1_POS_B3, 4 * 4) encoder_1 = int.from_bytes( encoders[0:4], byteorder='big', signed=False) encoder_2 = int.from_bytes( encoders[4:8], byteorder='big', signed=False) encoder_3 = int.from_bytes( encoders[8:12], byteorder='big', signed=False) encoder_4 = int.from_bytes( encoders[12:16], byteorder='big', signed=False) return (encoder_1, encoder_2, encoder_3, encoder_4) @staticmethod def read_all(): """Read all encoders at once. This is faster than read as it only uses one SPI call for all encoders! :return: Tuple of all current encoder positions """ encoders = Encoder.read_all_raw() return (Encoder.handle_wrap(encoders[0], 1), Encoder.handle_wrap(encoders[1], 2), Encoder.handle_wrap(encoders[2], 3), Encoder.handle_wrap(encoders[3], 4)) @staticmethod def read(port: int) -> int: """Read encoder from a specified port :param port: Port, which the motor is connected to. Between 1 and 4 :raises: IndexError :return: Current encoder position """ if port <= 0 or port > MOTOR_COUNT: raise IndexError("Invalid encoder port specified!") return Encoder.handle_wrap(Encoder.read_raw(port), port) @staticmethod def clear(port: int): """Reset encoder position to 0 :param port: Port, which the motor is connected to. Between 1 and 4 :raises: IndexError """ if port <= 0 or port > MOTOR_COUNT: raise IndexError("Invalid encoder port specified!") encoder_start_values[port] = Encoder.read_raw(port) @staticmethod def clear_all(): """Reset all encoder positions to 0 """ for i in range(1, MOTOR_COUNT + 1): encoder_start_values[i] = Encoder.read_raw(i)