"""Nomad Class Handler for Interaction with NomadBLDC Board"""

#  DataContainers.py
#
#  Created on: March 28, 2020
#      Author: Quincy Jones
# 
#  Copyright (c) <2020> <Quincy Jones - quincy@implementedrobotics.com/>
#  Permission is hereby granted, free of charge, to any person obtaining a
#  copy of this software and associated documentation files (the "Software"),
#  to deal in the Software without restriction, including without limitation
#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
#  and/or sell copies of the Software, and to permit persons to whom the Software
#  is furnished to do so, subject to the following conditions:
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


import struct
from dataclasses import dataclass
from typing import List, ClassVar


@dataclass
class LogPacket:
    log_length: int = None
    log_string: str = None

    @classmethod
    def unpack(cls, data):
        return struct.unpack(f"<{data[0]}s", data[1:])[0].decode("utf-8")

@dataclass
class DeviceStats:
    __fmt = "<BBIffff"
    fault: int = None
    control_status: int = None
    uptime: int = None
    voltage_bus: float = None
    current_bus: float = None
    fet_temp: float = None
    motor_temp: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return DeviceStats(*unpacked)

@dataclass
class DeviceInfo:
    fw_major: int = None
    fw_minor: int = None
    device_id: 'typing.Any' = None

@dataclass
class MotorConfig:
    __packet : ClassVar[struct.Struct] = struct.Struct('<I8fiff6I2fi5I')
    # Config Reg 1
    num_pole_pairs: int = None
    K_v: float = None
    K_t: float = None
    K_t_out: float = None
    flux_linkage: float = None
    phase_resistance: float = None
    phase_inductance_d: float = None
    phase_inductance_q: float = None
    gear_ratio: float = None
    phase_order: int = None

    # Thermal Config Reg
    continuous_current_max: float = None
    continuous_current_tau: float = None
    tcr_reserved_1: int = None
    tcr_reserved_2: int = None
    tcr_reserved_3: int = None
    tcr_reserved_4: int = None
    tcr_reserved_5: int = None
    tcr_reserved_6: int = None

    # Cablibration Config Reg
    calib_current: float = None
    calib_voltage: float = None
    calibrated: int = None
    ccr_reserved_1: int = None
    ccr_reserved_2: int = None
    ccr_reserved_3: int = None
    ccr_reserved_4: int = None
    ccr_reserved_5: int = None

    def pack(self):
        return self.__packet.pack(self.num_pole_pairs,
        self.K_v,
        self.K_t,
        self.K_t_out,
        self.flux_linkage,
        self.phase_resistance,
        self.phase_inductance_d,
        self.phase_inductance_q,
        self.gear_ratio,
        self.phase_order,
        self.continuous_current_max,
        self.continuous_current_tau,
        0,
        0,
        0,
        0,
        0,
        0,
        self.calib_current,
        self.calib_voltage,
        self.calibrated,
        0,
        0,
        0,
        0,
        0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorConfig(*unpacked)


@dataclass
class ControllerConfig:
    #__fmt = "<16fI"
    __packet : ClassVar[struct.Struct] = struct.Struct('<8f3I9f2I')
    # Config Reg 1
    k_d: float = None
    k_q: float = None
    k_i_d: float = None
    k_i_q: float = None
    k_i_vel: float = None
    current_bandwidth: float = None
    overmodulation: float = None
    pwm_freq: float = None
    foc_ccl_divider: int = None
    ccr1_reserved_1: int = None
    ccr1_reserver_2: int = None
    
    # Config Reg 2
    K_p_max: float = None
    K_d_max: float = None
    K_p_limit: float = None
    K_d_limit: float = None
    pos_limit_min: float = None
    pos_limit_max: float = None
    velocity_limit: float = None
    torque_limit: float = None
    current_limit: float = None
    ccr2_reserved_1: int = None
    ccr2_reserver_2: int = None



    def pack(self):
        return self.__packet.pack(self.k_d,
        self.k_q,
        self.k_i_d,
        self.k_i_q,
        self.k_i_vel,
        self.current_bandwidth,
        self.overmodulation,
        self.pwm_freq,
        self.foc_ccl_divider,
        0,
        0,
        self.K_p_max,
        self.K_d_max,
        self.K_p_limit,
        self.K_d_limit,
        self.pos_limit_min,
        self.pos_limit_max,
        self.velocity_limit,
        self.torque_limit,
        self.current_limit,
        0,
        0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)

        return ControllerConfig(*unpacked)

@dataclass
class CANConfig:
    __packet : ClassVar[struct.Struct] = struct.Struct('<4I2f')

    # Config Reg 1
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id,
        self.bitrate,
        self.d_bitrate,
        self.mode_fd,
        self.sample_point,
        self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)

        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    #__fmt = "<ffi128b"
    __packet : ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    #direction: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec,
        self.offset_mech,
        self.cpr,
        0,
        0,
        0,
        0,
        *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2], unpacked[3],unpacked[4],unpacked[5],unpacked[6],[*unpacked[7:]])

@dataclass
class MotorState:
    #__fmt = "<16fI"
    __packet : ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    #V_a: float = None
    #V_b: float = None
    #V_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None

    theta_elec: float = None
    theta_elec_dot: float = None
    


    def pack(self):
        return self.__packet.pack(self.I_a,
        self.I_b,
        self.I_c,
        #self.V_a,
        #self.V_b,
        #self.V_c,
        self.windings_temp,
        self.theta_mech,
        self.theta_mech_true,
        self.theta_mech_dot,
        self.theta_elec,
        self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)

@dataclass
class ControllerState:
    __packet : ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None

    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None

    I_rms: float = None
    I_max: float = None

    timeout: int = None

    Vel_int: float = None

    V_d_ref: float = None
    V_q_ref: float = None

    I_d_ref: float = None
    I_q_ref: float = None

    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None

    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d,
        self.I_q,
        self.V_d,
        self.V_q,
        self.d_int,
        self.q_int,
        self.dtc_A,
        self.dtc_B,
        self.dtc_C,
        self.I_rms,
        self.I_max,
        self.timeout,
        self.Vel_int,
        self.V_d_ref,
        self.V_q_ref,
        self.I_d_ref,
        self.I_q_ref,
        self.Pos_ref,
        self.K_p,
        self.Vel_ref,
        self.K_d,
        self.T_ff,
        self.Voltage_bus,
        self.I_bus,
        self.fet_temp,
        )

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)

@dataclass
class EncoderState:
    __packet : ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw,
        self.num_rotations,
        self.position_elec,
        self.position_mech,
        self.velocity_elec,
        self.velocity_elec_filt,
        self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)

@dataclass
class FloatMeasurement:
    __fmt = "<Bf"
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)

@dataclass
class IntMeasurement:
    __fmt = "<BI"
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)

1c0f410470ea94e2db6240cb9569bd0c881bd3c7
<mask token>


@dataclass
class CANConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<4I2f')
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id, self.bitrate, self.d_bitrate,
            self.mode_fd, self.sample_point, self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec, self.offset_mech, self.
            cpr, 0, 0, 0, 0, *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2],
            unpacked[3], unpacked[4], unpacked[5], unpacked[6], [*unpacked[7:]]
            )


@dataclass
class MotorState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None
    theta_elec: float = None
    theta_elec_dot: float = None

    def pack(self):
        return self.__packet.pack(self.I_a, self.I_b, self.I_c, self.
            windings_temp, self.theta_mech, self.theta_mech_true, self.
            theta_mech_dot, self.theta_elec, self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)


@dataclass
class ControllerState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None
    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None
    I_rms: float = None
    I_max: float = None
    timeout: int = None
    Vel_int: float = None
    V_d_ref: float = None
    V_q_ref: float = None
    I_d_ref: float = None
    I_q_ref: float = None
    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None
    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d, self.I_q, self.V_d, self.V_q,
            self.d_int, self.q_int, self.dtc_A, self.dtc_B, self.dtc_C,
            self.I_rms, self.I_max, self.timeout, self.Vel_int, self.
            V_d_ref, self.V_q_ref, self.I_d_ref, self.I_q_ref, self.Pos_ref,
            self.K_p, self.Vel_ref, self.K_d, self.T_ff, self.Voltage_bus,
            self.I_bus, self.fet_temp)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)


@dataclass
class EncoderState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw, self.num_rotations,
            self.position_elec, self.position_mech, self.velocity_elec,
            self.velocity_elec_filt, self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)


@dataclass
class FloatMeasurement:
    __fmt = '<Bf'
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)


@dataclass
class IntMeasurement:
    __fmt = '<BI'
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)

<mask token>


@dataclass
class DeviceStats:
    <mask token>
    fault: int = None
    control_status: int = None
    uptime: int = None
    voltage_bus: float = None
    current_bus: float = None
    fet_temp: float = None
    motor_temp: float = None
    <mask token>


@dataclass
class DeviceInfo:
    fw_major: int = None
    fw_minor: int = None
    device_id: 'typing.Any' = None


@dataclass
class MotorConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<I8fiff6I2fi5I')
    num_pole_pairs: int = None
    K_v: float = None
    K_t: float = None
    K_t_out: float = None
    flux_linkage: float = None
    phase_resistance: float = None
    phase_inductance_d: float = None
    phase_inductance_q: float = None
    gear_ratio: float = None
    phase_order: int = None
    continuous_current_max: float = None
    continuous_current_tau: float = None
    tcr_reserved_1: int = None
    tcr_reserved_2: int = None
    tcr_reserved_3: int = None
    tcr_reserved_4: int = None
    tcr_reserved_5: int = None
    tcr_reserved_6: int = None
    calib_current: float = None
    calib_voltage: float = None
    calibrated: int = None
    ccr_reserved_1: int = None
    ccr_reserved_2: int = None
    ccr_reserved_3: int = None
    ccr_reserved_4: int = None
    ccr_reserved_5: int = None

    def pack(self):
        return self.__packet.pack(self.num_pole_pairs, self.K_v, self.K_t,
            self.K_t_out, self.flux_linkage, self.phase_resistance, self.
            phase_inductance_d, self.phase_inductance_q, self.gear_ratio,
            self.phase_order, self.continuous_current_max, self.
            continuous_current_tau, 0, 0, 0, 0, 0, 0, self.calib_current,
            self.calib_voltage, self.calibrated, 0, 0, 0, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorConfig(*unpacked)


@dataclass
class ControllerConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<8f3I9f2I')
    k_d: float = None
    k_q: float = None
    k_i_d: float = None
    k_i_q: float = None
    k_i_vel: float = None
    current_bandwidth: float = None
    overmodulation: float = None
    pwm_freq: float = None
    foc_ccl_divider: int = None
    ccr1_reserved_1: int = None
    ccr1_reserver_2: int = None
    K_p_max: float = None
    K_d_max: float = None
    K_p_limit: float = None
    K_d_limit: float = None
    pos_limit_min: float = None
    pos_limit_max: float = None
    velocity_limit: float = None
    torque_limit: float = None
    current_limit: float = None
    ccr2_reserved_1: int = None
    ccr2_reserver_2: int = None

    def pack(self):
        return self.__packet.pack(self.k_d, self.k_q, self.k_i_d, self.
            k_i_q, self.k_i_vel, self.current_bandwidth, self.
            overmodulation, self.pwm_freq, self.foc_ccl_divider, 0, 0, self
            .K_p_max, self.K_d_max, self.K_p_limit, self.K_d_limit, self.
            pos_limit_min, self.pos_limit_max, self.velocity_limit, self.
            torque_limit, self.current_limit, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerConfig(*unpacked)


@dataclass
class CANConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<4I2f')
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id, self.bitrate, self.d_bitrate,
            self.mode_fd, self.sample_point, self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec, self.offset_mech, self.
            cpr, 0, 0, 0, 0, *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2],
            unpacked[3], unpacked[4], unpacked[5], unpacked[6], [*unpacked[7:]]
            )


@dataclass
class MotorState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None
    theta_elec: float = None
    theta_elec_dot: float = None

    def pack(self):
        return self.__packet.pack(self.I_a, self.I_b, self.I_c, self.
            windings_temp, self.theta_mech, self.theta_mech_true, self.
            theta_mech_dot, self.theta_elec, self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)


@dataclass
class ControllerState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None
    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None
    I_rms: float = None
    I_max: float = None
    timeout: int = None
    Vel_int: float = None
    V_d_ref: float = None
    V_q_ref: float = None
    I_d_ref: float = None
    I_q_ref: float = None
    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None
    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d, self.I_q, self.V_d, self.V_q,
            self.d_int, self.q_int, self.dtc_A, self.dtc_B, self.dtc_C,
            self.I_rms, self.I_max, self.timeout, self.Vel_int, self.
            V_d_ref, self.V_q_ref, self.I_d_ref, self.I_q_ref, self.Pos_ref,
            self.K_p, self.Vel_ref, self.K_d, self.T_ff, self.Voltage_bus,
            self.I_bus, self.fet_temp)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)


@dataclass
class EncoderState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw, self.num_rotations,
            self.position_elec, self.position_mech, self.velocity_elec,
            self.velocity_elec_filt, self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)


@dataclass
class FloatMeasurement:
    __fmt = '<Bf'
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)


@dataclass
class IntMeasurement:
    __fmt = '<BI'
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)

<mask token>


@dataclass
class LogPacket:
    log_length: int = None
    log_string: str = None

    @classmethod
    def unpack(cls, data):
        return struct.unpack(f'<{data[0]}s', data[1:])[0].decode('utf-8')


@dataclass
class DeviceStats:
    __fmt = '<BBIffff'
    fault: int = None
    control_status: int = None
    uptime: int = None
    voltage_bus: float = None
    current_bus: float = None
    fet_temp: float = None
    motor_temp: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return DeviceStats(*unpacked)


@dataclass
class DeviceInfo:
    fw_major: int = None
    fw_minor: int = None
    device_id: 'typing.Any' = None


@dataclass
class MotorConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<I8fiff6I2fi5I')
    num_pole_pairs: int = None
    K_v: float = None
    K_t: float = None
    K_t_out: float = None
    flux_linkage: float = None
    phase_resistance: float = None
    phase_inductance_d: float = None
    phase_inductance_q: float = None
    gear_ratio: float = None
    phase_order: int = None
    continuous_current_max: float = None
    continuous_current_tau: float = None
    tcr_reserved_1: int = None
    tcr_reserved_2: int = None
    tcr_reserved_3: int = None
    tcr_reserved_4: int = None
    tcr_reserved_5: int = None
    tcr_reserved_6: int = None
    calib_current: float = None
    calib_voltage: float = None
    calibrated: int = None
    ccr_reserved_1: int = None
    ccr_reserved_2: int = None
    ccr_reserved_3: int = None
    ccr_reserved_4: int = None
    ccr_reserved_5: int = None

    def pack(self):
        return self.__packet.pack(self.num_pole_pairs, self.K_v, self.K_t,
            self.K_t_out, self.flux_linkage, self.phase_resistance, self.
            phase_inductance_d, self.phase_inductance_q, self.gear_ratio,
            self.phase_order, self.continuous_current_max, self.
            continuous_current_tau, 0, 0, 0, 0, 0, 0, self.calib_current,
            self.calib_voltage, self.calibrated, 0, 0, 0, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorConfig(*unpacked)


@dataclass
class ControllerConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<8f3I9f2I')
    k_d: float = None
    k_q: float = None
    k_i_d: float = None
    k_i_q: float = None
    k_i_vel: float = None
    current_bandwidth: float = None
    overmodulation: float = None
    pwm_freq: float = None
    foc_ccl_divider: int = None
    ccr1_reserved_1: int = None
    ccr1_reserver_2: int = None
    K_p_max: float = None
    K_d_max: float = None
    K_p_limit: float = None
    K_d_limit: float = None
    pos_limit_min: float = None
    pos_limit_max: float = None
    velocity_limit: float = None
    torque_limit: float = None
    current_limit: float = None
    ccr2_reserved_1: int = None
    ccr2_reserver_2: int = None

    def pack(self):
        return self.__packet.pack(self.k_d, self.k_q, self.k_i_d, self.
            k_i_q, self.k_i_vel, self.current_bandwidth, self.
            overmodulation, self.pwm_freq, self.foc_ccl_divider, 0, 0, self
            .K_p_max, self.K_d_max, self.K_p_limit, self.K_d_limit, self.
            pos_limit_min, self.pos_limit_max, self.velocity_limit, self.
            torque_limit, self.current_limit, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerConfig(*unpacked)


@dataclass
class CANConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<4I2f')
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id, self.bitrate, self.d_bitrate,
            self.mode_fd, self.sample_point, self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec, self.offset_mech, self.
            cpr, 0, 0, 0, 0, *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2],
            unpacked[3], unpacked[4], unpacked[5], unpacked[6], [*unpacked[7:]]
            )


@dataclass
class MotorState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None
    theta_elec: float = None
    theta_elec_dot: float = None

    def pack(self):
        return self.__packet.pack(self.I_a, self.I_b, self.I_c, self.
            windings_temp, self.theta_mech, self.theta_mech_true, self.
            theta_mech_dot, self.theta_elec, self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)


@dataclass
class ControllerState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None
    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None
    I_rms: float = None
    I_max: float = None
    timeout: int = None
    Vel_int: float = None
    V_d_ref: float = None
    V_q_ref: float = None
    I_d_ref: float = None
    I_q_ref: float = None
    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None
    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d, self.I_q, self.V_d, self.V_q,
            self.d_int, self.q_int, self.dtc_A, self.dtc_B, self.dtc_C,
            self.I_rms, self.I_max, self.timeout, self.Vel_int, self.
            V_d_ref, self.V_q_ref, self.I_d_ref, self.I_q_ref, self.Pos_ref,
            self.K_p, self.Vel_ref, self.K_d, self.T_ff, self.Voltage_bus,
            self.I_bus, self.fet_temp)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)


@dataclass
class EncoderState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw, self.num_rotations,
            self.position_elec, self.position_mech, self.velocity_elec,
            self.velocity_elec_filt, self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)


@dataclass
class FloatMeasurement:
    __fmt = '<Bf'
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)


@dataclass
class IntMeasurement:
    __fmt = '<BI'
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)

<mask token>
import struct
from dataclasses import dataclass
from typing import List, ClassVar


@dataclass
class LogPacket:
    log_length: int = None
    log_string: str = None

    @classmethod
    def unpack(cls, data):
        return struct.unpack(f'<{data[0]}s', data[1:])[0].decode('utf-8')


@dataclass
class DeviceStats:
    __fmt = '<BBIffff'
    fault: int = None
    control_status: int = None
    uptime: int = None
    voltage_bus: float = None
    current_bus: float = None
    fet_temp: float = None
    motor_temp: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return DeviceStats(*unpacked)


@dataclass
class DeviceInfo:
    fw_major: int = None
    fw_minor: int = None
    device_id: 'typing.Any' = None


@dataclass
class MotorConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<I8fiff6I2fi5I')
    num_pole_pairs: int = None
    K_v: float = None
    K_t: float = None
    K_t_out: float = None
    flux_linkage: float = None
    phase_resistance: float = None
    phase_inductance_d: float = None
    phase_inductance_q: float = None
    gear_ratio: float = None
    phase_order: int = None
    continuous_current_max: float = None
    continuous_current_tau: float = None
    tcr_reserved_1: int = None
    tcr_reserved_2: int = None
    tcr_reserved_3: int = None
    tcr_reserved_4: int = None
    tcr_reserved_5: int = None
    tcr_reserved_6: int = None
    calib_current: float = None
    calib_voltage: float = None
    calibrated: int = None
    ccr_reserved_1: int = None
    ccr_reserved_2: int = None
    ccr_reserved_3: int = None
    ccr_reserved_4: int = None
    ccr_reserved_5: int = None

    def pack(self):
        return self.__packet.pack(self.num_pole_pairs, self.K_v, self.K_t,
            self.K_t_out, self.flux_linkage, self.phase_resistance, self.
            phase_inductance_d, self.phase_inductance_q, self.gear_ratio,
            self.phase_order, self.continuous_current_max, self.
            continuous_current_tau, 0, 0, 0, 0, 0, 0, self.calib_current,
            self.calib_voltage, self.calibrated, 0, 0, 0, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorConfig(*unpacked)


@dataclass
class ControllerConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<8f3I9f2I')
    k_d: float = None
    k_q: float = None
    k_i_d: float = None
    k_i_q: float = None
    k_i_vel: float = None
    current_bandwidth: float = None
    overmodulation: float = None
    pwm_freq: float = None
    foc_ccl_divider: int = None
    ccr1_reserved_1: int = None
    ccr1_reserver_2: int = None
    K_p_max: float = None
    K_d_max: float = None
    K_p_limit: float = None
    K_d_limit: float = None
    pos_limit_min: float = None
    pos_limit_max: float = None
    velocity_limit: float = None
    torque_limit: float = None
    current_limit: float = None
    ccr2_reserved_1: int = None
    ccr2_reserver_2: int = None

    def pack(self):
        return self.__packet.pack(self.k_d, self.k_q, self.k_i_d, self.
            k_i_q, self.k_i_vel, self.current_bandwidth, self.
            overmodulation, self.pwm_freq, self.foc_ccl_divider, 0, 0, self
            .K_p_max, self.K_d_max, self.K_p_limit, self.K_d_limit, self.
            pos_limit_min, self.pos_limit_max, self.velocity_limit, self.
            torque_limit, self.current_limit, 0, 0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerConfig(*unpacked)


@dataclass
class CANConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<4I2f')
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id, self.bitrate, self.d_bitrate,
            self.mode_fd, self.sample_point, self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    __packet: ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec, self.offset_mech, self.
            cpr, 0, 0, 0, 0, *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2],
            unpacked[3], unpacked[4], unpacked[5], unpacked[6], [*unpacked[7:]]
            )


@dataclass
class MotorState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None
    theta_elec: float = None
    theta_elec_dot: float = None

    def pack(self):
        return self.__packet.pack(self.I_a, self.I_b, self.I_c, self.
            windings_temp, self.theta_mech, self.theta_mech_true, self.
            theta_mech_dot, self.theta_elec, self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)


@dataclass
class ControllerState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None
    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None
    I_rms: float = None
    I_max: float = None
    timeout: int = None
    Vel_int: float = None
    V_d_ref: float = None
    V_q_ref: float = None
    I_d_ref: float = None
    I_q_ref: float = None
    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None
    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d, self.I_q, self.V_d, self.V_q,
            self.d_int, self.q_int, self.dtc_A, self.dtc_B, self.dtc_C,
            self.I_rms, self.I_max, self.timeout, self.Vel_int, self.
            V_d_ref, self.V_q_ref, self.I_d_ref, self.I_q_ref, self.Pos_ref,
            self.K_p, self.Vel_ref, self.K_d, self.T_ff, self.Voltage_bus,
            self.I_bus, self.fet_temp)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)


@dataclass
class EncoderState:
    __packet: ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw, self.num_rotations,
            self.position_elec, self.position_mech, self.velocity_elec,
            self.velocity_elec_filt, self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)


@dataclass
class FloatMeasurement:
    __fmt = '<Bf'
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)


@dataclass
class IntMeasurement:
    __fmt = '<BI'
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)

"""Nomad Class Handler for Interaction with NomadBLDC Board"""

#  DataContainers.py
#
#  Created on: March 28, 2020
#      Author: Quincy Jones
# 
#  Copyright (c) <2020> <Quincy Jones - quincy@implementedrobotics.com/>
#  Permission is hereby granted, free of charge, to any person obtaining a
#  copy of this software and associated documentation files (the "Software"),
#  to deal in the Software without restriction, including without limitation
#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
#  and/or sell copies of the Software, and to permit persons to whom the Software
#  is furnished to do so, subject to the following conditions:
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


import struct
from dataclasses import dataclass
from typing import List, ClassVar


@dataclass
class LogPacket:
    log_length: int = None
    log_string: str = None

    @classmethod
    def unpack(cls, data):
        return struct.unpack(f"<{data[0]}s", data[1:])[0].decode("utf-8")

@dataclass
class DeviceStats:
    __fmt = "<BBIffff"
    fault: int = None
    control_status: int = None
    uptime: int = None
    voltage_bus: float = None
    current_bus: float = None
    fet_temp: float = None
    motor_temp: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return DeviceStats(*unpacked)

@dataclass
class DeviceInfo:
    fw_major: int = None
    fw_minor: int = None
    device_id: 'typing.Any' = None

@dataclass
class MotorConfig:
    __packet : ClassVar[struct.Struct] = struct.Struct('<I8fiff6I2fi5I')
    # Config Reg 1
    num_pole_pairs: int = None
    K_v: float = None
    K_t: float = None
    K_t_out: float = None
    flux_linkage: float = None
    phase_resistance: float = None
    phase_inductance_d: float = None
    phase_inductance_q: float = None
    gear_ratio: float = None
    phase_order: int = None

    # Thermal Config Reg
    continuous_current_max: float = None
    continuous_current_tau: float = None
    tcr_reserved_1: int = None
    tcr_reserved_2: int = None
    tcr_reserved_3: int = None
    tcr_reserved_4: int = None
    tcr_reserved_5: int = None
    tcr_reserved_6: int = None

    # Cablibration Config Reg
    calib_current: float = None
    calib_voltage: float = None
    calibrated: int = None
    ccr_reserved_1: int = None
    ccr_reserved_2: int = None
    ccr_reserved_3: int = None
    ccr_reserved_4: int = None
    ccr_reserved_5: int = None

    def pack(self):
        return self.__packet.pack(self.num_pole_pairs,
        self.K_v,
        self.K_t,
        self.K_t_out,
        self.flux_linkage,
        self.phase_resistance,
        self.phase_inductance_d,
        self.phase_inductance_q,
        self.gear_ratio,
        self.phase_order,
        self.continuous_current_max,
        self.continuous_current_tau,
        0,
        0,
        0,
        0,
        0,
        0,
        self.calib_current,
        self.calib_voltage,
        self.calibrated,
        0,
        0,
        0,
        0,
        0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorConfig(*unpacked)


@dataclass
class ControllerConfig:
    #__fmt = "<16fI"
    __packet : ClassVar[struct.Struct] = struct.Struct('<8f3I9f2I')
    # Config Reg 1
    k_d: float = None
    k_q: float = None
    k_i_d: float = None
    k_i_q: float = None
    k_i_vel: float = None
    current_bandwidth: float = None
    overmodulation: float = None
    pwm_freq: float = None
    foc_ccl_divider: int = None
    ccr1_reserved_1: int = None
    ccr1_reserver_2: int = None
    
    # Config Reg 2
    K_p_max: float = None
    K_d_max: float = None
    K_p_limit: float = None
    K_d_limit: float = None
    pos_limit_min: float = None
    pos_limit_max: float = None
    velocity_limit: float = None
    torque_limit: float = None
    current_limit: float = None
    ccr2_reserved_1: int = None
    ccr2_reserver_2: int = None



    def pack(self):
        return self.__packet.pack(self.k_d,
        self.k_q,
        self.k_i_d,
        self.k_i_q,
        self.k_i_vel,
        self.current_bandwidth,
        self.overmodulation,
        self.pwm_freq,
        self.foc_ccl_divider,
        0,
        0,
        self.K_p_max,
        self.K_d_max,
        self.K_p_limit,
        self.K_d_limit,
        self.pos_limit_min,
        self.pos_limit_max,
        self.velocity_limit,
        self.torque_limit,
        self.current_limit,
        0,
        0)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)

        return ControllerConfig(*unpacked)

@dataclass
class CANConfig:
    __packet : ClassVar[struct.Struct] = struct.Struct('<4I2f')

    # Config Reg 1
    id: int = None
    bitrate: int = None
    d_bitrate: int = None
    mode_fd: int = None
    sample_point: float = None
    d_sample_point: float = None

    def pack(self):
        return self.__packet.pack(self.id,
        self.bitrate,
        self.d_bitrate,
        self.mode_fd,
        self.sample_point,
        self.d_sample_point)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)

        return CANConfig(*unpacked)


@dataclass
class EncoderConfig:
    #__fmt = "<ffi128b"
    __packet : ClassVar[struct.Struct] = struct.Struct('<ffi4I128b')
    offset_elec: float = None
    offset_mech: float = None
    cpr: int = None
    ecr_reserved_1: int = None
    ecr_reserved_2: int = None
    ecr_reserved_3: int = None
    ecr_reserved_4: int = None
    #direction: int = None
    offset_lut: List[int] = None

    def pack(self):
        return self.__packet.pack(self.offset_elec,
        self.offset_mech,
        self.cpr,
        0,
        0,
        0,
        0,
        *self.offset_lut)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderConfig(unpacked[0], unpacked[1], unpacked[2], unpacked[3],unpacked[4],unpacked[5],unpacked[6],[*unpacked[7:]])

@dataclass
class MotorState:
    #__fmt = "<16fI"
    __packet : ClassVar[struct.Struct] = struct.Struct('<9f')
    I_a: float = None
    I_b: float = None
    I_c: float = None
    #V_a: float = None
    #V_b: float = None
    #V_c: float = None
    windings_temp: float = None
    theta_mech: float = None
    theta_mech_true: float = None
    theta_mech_dot: float = None

    theta_elec: float = None
    theta_elec_dot: float = None
    


    def pack(self):
        return self.__packet.pack(self.I_a,
        self.I_b,
        self.I_c,
        #self.V_a,
        #self.V_b,
        #self.V_c,
        self.windings_temp,
        self.theta_mech,
        self.theta_mech_true,
        self.theta_mech_dot,
        self.theta_elec,
        self.theta_elec_dot)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return MotorState(*unpacked)

@dataclass
class ControllerState:
    __packet : ClassVar[struct.Struct] = struct.Struct('<11fI13f')
    I_d: float = None
    I_q: float = None
    V_d: float = None
    V_q: float = None
    d_int: float = None
    q_int: float = None

    dtc_A: float = None
    dtc_B: float = None
    dtc_C: float = None

    I_rms: float = None
    I_max: float = None

    timeout: int = None

    Vel_int: float = None

    V_d_ref: float = None
    V_q_ref: float = None

    I_d_ref: float = None
    I_q_ref: float = None

    Pos_ref: float = None
    K_p: float = None
    Vel_ref: float = None
    K_d: float = None
    T_ff: float = None

    Voltage_bus: float = None
    I_bus: float = None
    fet_temp: float = None

    def pack(self):
        return self.__packet.pack(self.I_d,
        self.I_q,
        self.V_d,
        self.V_q,
        self.d_int,
        self.q_int,
        self.dtc_A,
        self.dtc_B,
        self.dtc_C,
        self.I_rms,
        self.I_max,
        self.timeout,
        self.Vel_int,
        self.V_d_ref,
        self.V_q_ref,
        self.I_d_ref,
        self.I_q_ref,
        self.Pos_ref,
        self.K_p,
        self.Vel_ref,
        self.K_d,
        self.T_ff,
        self.Voltage_bus,
        self.I_bus,
        self.fet_temp,
        )

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return ControllerState(*unpacked)

@dataclass
class EncoderState:
    __packet : ClassVar[struct.Struct] = struct.Struct('<2i5f')
    position_raw: int = None
    num_rotations: int = None
    position_elec: float = None
    position_mech: float = None
    velocity_elec: float = None
    velocity_elec_filt: float = None
    velocity_mech: float = None

    def pack(self):
        return self.__packet.pack(self.position_raw,
        self.num_rotations,
        self.position_elec,
        self.position_mech,
        self.velocity_elec,
        self.velocity_elec_filt,
        self.velocity_mech)

    @classmethod
    def unpack(cls, data):
        unpacked = cls.__packet.unpack(data)
        return EncoderState(*unpacked)

@dataclass
class FloatMeasurement:
    __fmt = "<Bf"
    status: int = None
    measurement: float = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return FloatMeasurement(*unpacked)

@dataclass
class IntMeasurement:
    __fmt = "<BI"
    status: int = None
    measurement: int = None

    @classmethod
    def unpack(cls, data):
        unpacked = struct.unpack(cls.__fmt, data)
        return IntMeasurement(*unpacked)
