Add new PyQt UI

This commit is contained in:
Ayzen
2026-04-26 18:39:55 +03:00
parent c92745d2bc
commit 0ec504ffa9
33 changed files with 3284 additions and 3789 deletions

View File

@ -1,20 +1,14 @@
"""
Parameter validation for laser control module.
Validates all input parameters against physical constraints
and protocol limits before sending to device.
"""
"""Validation helpers for controller inputs."""
import math
from typing import Dict, Any, Tuple
import re
from typing import Any
from .constants import (
TEMP_MIN_C, TEMP_MAX_C,
CURRENT_MIN_MA, CURRENT_MAX_MA,
CURRENT_STEP_MIN_MA, CURRENT_STEP_MAX_MA,
TEMP_STEP_MIN_C, TEMP_STEP_MAX_C,
TIME_STEP_MIN_US, TIME_STEP_MAX_US,
DELAY_TIME_MIN_MS, DELAY_TIME_MAX_MS,
PROFILE_NAME_ALLOWED_PATTERN,
PROFILE_NAME_MAX_LENGTH,
)
from .exceptions import (
ValidationError,
@ -22,7 +16,6 @@ from .exceptions import (
CurrentOutOfRangeError,
InvalidParameterError,
)
from .models import VariationType
class ParameterValidator:
@ -87,152 +80,13 @@ class ParameterValidator:
)
return value
@staticmethod
def validate_time_params(time_step: Any, delay_time: Any) -> Tuple[int, int]:
"""
Validate time parameters for variation mode.
Args:
time_step: Discretisation time step in microseconds.
delay_time: Delay between pulses in milliseconds.
Returns:
Tuple (time_step, delay_time) as integers.
Raises:
InvalidParameterError: If values are not numeric.
ValidationError: If values are outside allowed ranges.
"""
if not isinstance(time_step, (int, float)):
raise InvalidParameterError("time_step", "Value must be a number")
if not isinstance(delay_time, (int, float)):
raise InvalidParameterError("delay_time", "Value must be a number")
time_step_int = int(time_step)
delay_time_int = int(delay_time)
if time_step_int < TIME_STEP_MIN_US or time_step_int > TIME_STEP_MAX_US:
raise ValidationError(
f"time step {time_step_int} µs is out of range "
f"[{TIME_STEP_MIN_US} - {TIME_STEP_MAX_US}] µs"
)
if delay_time_int < DELAY_TIME_MIN_MS or delay_time_int > DELAY_TIME_MAX_MS:
raise ValidationError(
f"delay time {delay_time_int} ms is out of range "
f"[{DELAY_TIME_MIN_MS} - {DELAY_TIME_MAX_MS}] ms"
)
return time_step_int, delay_time_int
@staticmethod
def validate_variation_params(
params: Dict[str, Any],
variation_type: Any
) -> Dict[str, Any]:
"""
Validate parameters for variation mode.
Args:
params: Dictionary with keys:
min_value, max_value, step, time_step, delay_time.
variation_type: A VariationType enum value.
Returns:
Dictionary with validated and type-coerced values.
Raises:
ValidationError: For any constraint violation.
InvalidParameterError: For wrong types.
"""
# Validate variation type
if not isinstance(variation_type, VariationType):
try:
variation_type = VariationType(variation_type)
except (ValueError, KeyError):
raise ValidationError(
f"Invalid variation type '{variation_type}'. "
f"Must be one of {[e.name for e in VariationType]}"
)
# Check required keys
required_keys = {'min_value', 'max_value', 'step', 'time_step', 'delay_time'}
missing = required_keys - params.keys()
if missing:
raise ValidationError(
f"Missing required parameters: {sorted(missing)}"
)
# Validate min/max
min_val = ParameterValidator._check_numeric(params['min_value'], 'min_value')
max_val = ParameterValidator._check_numeric(params['max_value'], 'max_value')
if min_val >= max_val:
raise ValidationError(
f"min_value ({min_val}) must be less than max_value ({max_val})"
)
# Validate step based on variation type
step = ParameterValidator._check_numeric(params['step'], 'step')
is_current_variation = variation_type in (
VariationType.CHANGE_CURRENT_LD1,
VariationType.CHANGE_CURRENT_LD2
)
is_temp_variation = variation_type in (
VariationType.CHANGE_TEMPERATURE_LD1,
VariationType.CHANGE_TEMPERATURE_LD2
)
if is_current_variation:
step_min, step_max = CURRENT_STEP_MIN_MA, CURRENT_STEP_MAX_MA
unit = "mA"
# Also validate range against current limits
ParameterValidator.validate_current(min_val, 'min_value')
ParameterValidator.validate_current(max_val, 'max_value')
elif is_temp_variation:
step_min, step_max = TEMP_STEP_MIN_C, TEMP_STEP_MAX_C
unit = "°C"
# Also validate range against temperature limits
ParameterValidator.validate_temperature(min_val, 'min_value')
ParameterValidator.validate_temperature(max_val, 'max_value')
else:
raise ValidationError(
f"Variation type {variation_type.name} cannot be used in variation mode"
)
if step <= 0:
raise ValidationError(
f"step must be positive, got {step} {unit}"
)
if step < step_min:
raise ValidationError(
f"step {step} {unit} is too small (minimum {step_min} {unit})"
)
if step > step_max:
raise ValidationError(
f"step {step} {unit} is too large (maximum {step_max} {unit})"
)
# Validate time parameters
time_step, delay_time = ParameterValidator.validate_time_params(
params['time_step'], params['delay_time']
)
return {
'variation_type': variation_type,
'min_value': min_val,
'max_value': max_val,
'step': step,
'time_step': time_step,
'delay_time': delay_time,
}
@staticmethod
def validate_manual_mode_params(
temp1: Any,
temp2: Any,
current1: Any,
current2: Any,
) -> Dict[str, float]:
) -> dict[str, float]:
"""
Validate all four manual mode parameters.
@ -254,4 +108,26 @@ class ParameterValidator:
'temp2': ParameterValidator.validate_temperature(temp2, 'temp2'),
'current1': ParameterValidator.validate_current(current1, 'current1'),
'current2': ParameterValidator.validate_current(current2, 'current2'),
}
}
@staticmethod
def validate_profile_name(value: Any) -> str:
"""Validate a short ASCII profile name suitable for the device LCD."""
if not isinstance(value, str):
raise InvalidParameterError("profile_name", "Value must be a string")
normalized = value.strip()
if not normalized:
raise InvalidParameterError("profile_name", "Value must not be empty")
if len(normalized) > PROFILE_NAME_MAX_LENGTH:
raise InvalidParameterError(
"profile_name",
f"Value must be at most {PROFILE_NAME_MAX_LENGTH} characters long",
)
if re.fullmatch(PROFILE_NAME_ALLOWED_PATTERN, normalized) is None:
raise InvalidParameterError(
"profile_name",
"Only ASCII letters, digits, spaces, '-' and '_' are allowed",
)
return normalized