initial commit
This commit is contained in:
219
laser_control/models.py
Normal file
219
laser_control/models.py
Normal file
@ -0,0 +1,219 @@
|
||||
"""
|
||||
Data models for laser control module.
|
||||
|
||||
Provides dataclasses and enums for structured data representation
|
||||
throughout the laser control system.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class VariationType(IntEnum):
|
||||
"""Types of parameter variation modes."""
|
||||
MANUAL = 0x00
|
||||
CHANGE_CURRENT_LD1 = 0x01
|
||||
CHANGE_CURRENT_LD2 = 0x02
|
||||
CHANGE_TEMPERATURE_LD1 = 0x03
|
||||
CHANGE_TEMPERATURE_LD2 = 0x04
|
||||
|
||||
|
||||
class DeviceState(IntEnum):
|
||||
"""Device operational states."""
|
||||
IDLE = 0x0000
|
||||
RUNNING = 0x0001
|
||||
BUSY = 0x0002
|
||||
ERROR = 0x00FF
|
||||
ERROR_OVERHEAT = 0x0100
|
||||
ERROR_POWER = 0x0200
|
||||
ERROR_COMMUNICATION = 0x0400
|
||||
ERROR_INVALID_COMMAND = 0x0800
|
||||
|
||||
|
||||
@dataclass
|
||||
class ManualModeParams:
|
||||
"""Parameters for manual control mode."""
|
||||
temp1: float # Temperature for laser 1 (°C)
|
||||
temp2: float # Temperature for laser 2 (°C)
|
||||
current1: float # Current for laser 1 (mA)
|
||||
current2: float # Current for laser 2 (mA)
|
||||
pi_coeff1_p: float = 1.0 # PI controller proportional coefficient for laser 1
|
||||
pi_coeff1_i: float = 0.5 # PI controller integral coefficient for laser 1
|
||||
pi_coeff2_p: float = 1.0 # PI controller proportional coefficient for laser 2
|
||||
pi_coeff2_i: float = 0.5 # PI controller integral coefficient for laser 2
|
||||
|
||||
def to_dict(self) -> Dict[str, float]:
|
||||
"""Convert to dictionary representation."""
|
||||
return {
|
||||
'temp1': self.temp1,
|
||||
'temp2': self.temp2,
|
||||
'current1': self.current1,
|
||||
'current2': self.current2,
|
||||
'pi_coeff1_p': self.pi_coeff1_p,
|
||||
'pi_coeff1_i': self.pi_coeff1_i,
|
||||
'pi_coeff2_p': self.pi_coeff2_p,
|
||||
'pi_coeff2_i': self.pi_coeff2_i
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class VariationParams:
|
||||
"""Parameters for variation mode."""
|
||||
variation_type: VariationType
|
||||
# Static parameters (fixed during variation)
|
||||
static_temp1: float
|
||||
static_temp2: float
|
||||
static_current1: float
|
||||
static_current2: float
|
||||
# Variation range
|
||||
min_value: float # Minimum value for varied parameter
|
||||
max_value: float # Maximum value for varied parameter
|
||||
step: float # Step size for variation
|
||||
# Time parameters
|
||||
time_step: int # Time step in microseconds (20-100)
|
||||
delay_time: int # Delay between measurements in milliseconds (3-10)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary representation."""
|
||||
return {
|
||||
'variation_type': self.variation_type.value,
|
||||
'static_temp1': self.static_temp1,
|
||||
'static_temp2': self.static_temp2,
|
||||
'static_current1': self.static_current1,
|
||||
'static_current2': self.static_current2,
|
||||
'min_value': self.min_value,
|
||||
'max_value': self.max_value,
|
||||
'step': self.step,
|
||||
'time_step': self.time_step,
|
||||
'delay_time': self.delay_time
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Measurements:
|
||||
"""Real-time measurements from the device."""
|
||||
# Photodiode currents
|
||||
current1: float # Photodiode current for laser 1 (mA)
|
||||
current2: float # Photodiode current for laser 2 (mA)
|
||||
# Temperatures
|
||||
temp1: float # Temperature of laser 1 (°C)
|
||||
temp2: float # Temperature of laser 2 (°C)
|
||||
temp_ext1: Optional[float] = None # External thermistor 1 temperature (°C)
|
||||
temp_ext2: Optional[float] = None # External thermistor 2 temperature (°C)
|
||||
# Power supply voltages
|
||||
voltage_3v3: float = 0.0 # 3.3V rail voltage
|
||||
voltage_5v1: float = 0.0 # 5V rail 1 voltage
|
||||
voltage_5v2: float = 0.0 # 5V rail 2 voltage
|
||||
voltage_7v0: float = 0.0 # 7V rail voltage
|
||||
# Metadata
|
||||
timestamp: Optional[datetime] = None
|
||||
message_id: Optional[int] = None
|
||||
to6_counter_lsb: Optional[int] = None
|
||||
to6_counter_msb: Optional[int] = None
|
||||
|
||||
def __post_init__(self):
|
||||
"""Set timestamp if not provided."""
|
||||
if self.timestamp is None:
|
||||
self.timestamp = datetime.now()
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary representation."""
|
||||
return {
|
||||
'current1': self.current1,
|
||||
'current2': self.current2,
|
||||
'temp1': self.temp1,
|
||||
'temp2': self.temp2,
|
||||
'temp_ext1': self.temp_ext1,
|
||||
'temp_ext2': self.temp_ext2,
|
||||
'voltage_3v3': self.voltage_3v3,
|
||||
'voltage_5v1': self.voltage_5v1,
|
||||
'voltage_5v2': self.voltage_5v2,
|
||||
'voltage_7v0': self.voltage_7v0,
|
||||
'timestamp': self.timestamp.isoformat() if self.timestamp else None,
|
||||
'message_id': self.message_id
|
||||
}
|
||||
|
||||
def check_power_rails(self) -> Dict[str, bool]:
|
||||
"""Check if power supply voltages are within acceptable range."""
|
||||
return {
|
||||
'3v3': 3.1 <= self.voltage_3v3 <= 3.5,
|
||||
'5v1': 4.8 <= self.voltage_5v1 <= 5.3,
|
||||
'5v2': 4.8 <= self.voltage_5v2 <= 5.3,
|
||||
'7v0': 6.5 <= self.voltage_7v0 <= 7.5
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeviceStatus:
|
||||
"""Complete device status information."""
|
||||
state: DeviceState
|
||||
measurements: Optional[Measurements] = None
|
||||
is_connected: bool = False
|
||||
last_command_id: Optional[int] = None
|
||||
error_message: Optional[str] = None
|
||||
|
||||
@property
|
||||
def is_idle(self) -> bool:
|
||||
"""Check if device is idle."""
|
||||
return self.state == DeviceState.IDLE
|
||||
|
||||
@property
|
||||
def is_running(self) -> bool:
|
||||
"""Check if device is running a task."""
|
||||
return self.state == DeviceState.RUNNING
|
||||
|
||||
@property
|
||||
def has_error(self) -> bool:
|
||||
"""Check if device has any error."""
|
||||
return self.state >= DeviceState.ERROR
|
||||
|
||||
@property
|
||||
def error_type(self) -> Optional[str]:
|
||||
"""Get human-readable error type."""
|
||||
if not self.has_error:
|
||||
return None
|
||||
|
||||
error_map = {
|
||||
DeviceState.ERROR_OVERHEAT: "Overheating",
|
||||
DeviceState.ERROR_POWER: "Power supply issue",
|
||||
DeviceState.ERROR_COMMUNICATION: "Communication error",
|
||||
DeviceState.ERROR_INVALID_COMMAND: "Invalid command"
|
||||
}
|
||||
return error_map.get(self.state, "Unknown error")
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary representation."""
|
||||
return {
|
||||
'state': self.state.value,
|
||||
'state_name': self.state.name,
|
||||
'measurements': self.measurements.to_dict() if self.measurements else None,
|
||||
'is_connected': self.is_connected,
|
||||
'last_command_id': self.last_command_id,
|
||||
'error_message': self.error_message,
|
||||
'is_idle': self.is_idle,
|
||||
'is_running': self.is_running,
|
||||
'has_error': self.has_error,
|
||||
'error_type': self.error_type
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class CalibrationData:
|
||||
"""Calibration data for device sensors."""
|
||||
# Temperature calibration coefficients
|
||||
temp1_offset: float = 0.0
|
||||
temp1_scale: float = 1.0
|
||||
temp2_offset: float = 0.0
|
||||
temp2_scale: float = 1.0
|
||||
# Current calibration coefficients
|
||||
current1_offset: float = 0.0
|
||||
current1_scale: float = 1.0
|
||||
current2_offset: float = 0.0
|
||||
current2_scale: float = 1.0
|
||||
# Voltage calibration
|
||||
voltage_3v3_scale: float = 1.0
|
||||
voltage_5v1_scale: float = 1.0
|
||||
voltage_5v2_scale: float = 1.0
|
||||
voltage_7v0_scale: float = 1.0
|
||||
Reference in New Issue
Block a user