"""
TrGEN Configuration Management for Python
This module provides classes and utilities for exporting and importing TrGEN configurations
to/from JSON files with .trgen extension. It supports full trigger port configurations
including memory instructions, network settings, and metadata.
Classes:
TrgenConfiguration: Main configuration container
ConfigurationMetadata: Configuration file metadata
DefaultSettings: Default trigger settings
TriggerPortConfig: Individual trigger port configuration
NetworkSettings: Network connection settings
PortProgrammingState: Enumeration for port programming states
"""
import json
import os
from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Any
from .trgen_pin import TrgenPin
[docs]
class PortProgrammingState(Enum):
"""
Represents the programming state of a trigger port.
"""
NOT_PROGRAMMED = "not_programmed"
PROGRAMMED = "programmed"
RESET = "reset"
UNKNOWN = "unknown"
[docs]
class DefaultSettings:
"""
Default settings for all triggers in the configuration.
Contains global default values that apply to all trigger ports.
"""
def __init__(self):
self.default_trigger_duration_us = 20 # Changed from 40 to 20 as per request
self.default_log_level = "warn"
self.default_timeout_ms = 2000
self.auto_reset_enabled = True
self.auto_reset_delay_us = 100
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert default settings to dictionary for JSON serialization."""
return {
"default_trigger_duration_us": self.default_trigger_duration_us,
"default_log_level": self.default_log_level,
"default_timeout_ms": self.default_timeout_ms,
"auto_reset_enabled": self.auto_reset_enabled,
"auto_reset_delay_us": self.auto_reset_delay_us
}
[docs]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'DefaultSettings':
"""Create default settings instance from dictionary."""
settings = cls()
settings.default_trigger_duration_us = data.get("default_trigger_duration_us", 20)
settings.default_log_level = data.get("default_log_level", "warn")
settings.default_timeout_ms = data.get("default_timeout_ms", 2000)
settings.auto_reset_enabled = data.get("auto_reset_enabled", True)
settings.auto_reset_delay_us = data.get("auto_reset_delay_us", 100)
return settings
[docs]
class TriggerPortConfig:
"""
Configuration for a specific trigger port.
Contains all the information needed to configure and program a single trigger port,
including its memory instructions and settings.
"""
def __init__(self, memory_length: int = 32):
self.id = 0
self.name = ""
self.type = ""
self.enabled = True
self.custom_duration_us = None
self.memory_instructions = [0] * memory_length
self.memory_length = memory_length
self.last_instruction_index = -1
self.programming_state = PortProgrammingState.NOT_PROGRAMMED
self.last_programmed_at = None
self.notes = ""
self.custom_settings = {}
[docs]
def set_memory_from_trgen_port(self, trgen_port):
"""
Set the memory content from a TrgenPort object.
Args:
trgen_port: TrgenPort instance to copy memory from
"""
if trgen_port is None:
raise ValueError("trgen_port cannot be None")
self.id = trgen_port.id
self.type = trgen_port.type
self.memory_length = len(trgen_port.memory)
self.memory_instructions = trgen_port.memory.copy()
# Find last valid instruction
self._find_last_instruction_index()
# Update programming state
self.programming_state = (PortProgrammingState.PROGRAMMED
if self.last_instruction_index >= 0
else PortProgrammingState.NOT_PROGRAMMED)
self.last_programmed_at = datetime.now()
[docs]
def apply_to_trgen_port(self, trgen_port):
"""
Apply this configuration to a TrgenPort object.
Args:
trgen_port: TrgenPort instance to apply configuration to
"""
if trgen_port is None:
raise ValueError("trgen_port cannot be None")
if trgen_port.id != self.id:
raise ValueError(f"ID mismatch: TrgenPort has ID {trgen_port.id}, configuration has ID {self.id}")
# Apply memory instructions
copy_length = min(len(self.memory_instructions), len(trgen_port.memory))
for i in range(copy_length):
trgen_port.setInstruction(i, self.memory_instructions[i])
def _find_last_instruction_index(self):
"""Find the index of the last valid instruction in memory."""
self.last_instruction_index = -1
for i in range(len(self.memory_instructions) - 1, -1, -1):
if self.memory_instructions[i] != 0:
self.last_instruction_index = i
break
[docs]
def has_programmed_instructions(self) -> bool:
"""Check if the port has programmed instructions."""
return self.last_instruction_index >= 0
[docs]
def get_instructions_string(self) -> str:
"""Get a string representation of the programmed instructions."""
if not self.has_programmed_instructions():
return "No instructions programmed"
instructions = []
for i in range(self.last_instruction_index + 1):
instructions.append(f"[{i}]: 0x{self.memory_instructions[i]:08X}")
return ", ".join(instructions)
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert trigger port config to dictionary for JSON serialization."""
return {
"id": self.id,
"name": self.name,
"type": self.type,
"enabled": self.enabled,
"custom_duration_us": self.custom_duration_us,
"memory_instructions": self.memory_instructions,
"memory_length": self.memory_length,
"last_instruction_index": self.last_instruction_index,
"programming_state": self.programming_state.value,
"last_programmed_at": self.last_programmed_at.isoformat() if self.last_programmed_at else None,
"notes": self.notes,
"custom_settings": self.custom_settings
}
[docs]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'TriggerPortConfig':
"""Create trigger port config instance from dictionary."""
memory_length = data.get("memory_length", 32)
config = cls(memory_length)
config.id = data.get("id", 0)
config.name = data.get("name", "")
config.type = data.get("type", "")
config.enabled = data.get("enabled", True)
config.custom_duration_us = data.get("custom_duration_us")
config.memory_instructions = data.get("memory_instructions", [0] * memory_length)
config.last_instruction_index = data.get("last_instruction_index", -1)
config.programming_state = PortProgrammingState(data.get("programming_state", "not_programmed"))
config.notes = data.get("notes", "")
config.custom_settings = data.get("custom_settings", {})
last_programmed_str = data.get("last_programmed_at")
if last_programmed_str:
config.last_programmed_at = datetime.fromisoformat(last_programmed_str)
return config
[docs]
class NetworkSettings:
"""
Network configuration settings for TrGEN device connection.
Contains IP address, port, and timeout settings.
"""
def __init__(self):
self.ip_address = "192.168.123.1"
self.port = 4242
self.timeout_ms = 2000
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert network settings to dictionary for JSON serialization."""
return {
"ip_address": self.ip_address,
"port": self.port,
"timeout_ms": self.timeout_ms
}
[docs]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'NetworkSettings':
"""Create network settings instance from dictionary."""
settings = cls()
settings.ip_address = data.get("ip_address", "192.168.123.1")
settings.port = data.get("port", 4242)
settings.timeout_ms = data.get("timeout_ms", 2000)
return settings
[docs]
class TrgenConfiguration:
"""
Main configuration class that contains all TrGEN settings.
This is the root configuration object that gets serialized to/from JSON files.
"""
def __init__(self):
self.metadata = ConfigurationMetadata()
self.defaults = DefaultSettings()
self.trigger_ports = {} # Dict[str, TriggerPortConfig]
self.network = NetworkSettings()
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to dictionary for JSON serialization."""
return {
"metadata": self.metadata.to_dict(),
"defaults": self.defaults.to_dict(),
"trigger_ports": {key: port.to_dict() for key, port in self.trigger_ports.items()},
"network": self.network.to_dict()
}
[docs]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'TrgenConfiguration':
"""Create configuration instance from dictionary."""
config = cls()
config.metadata = ConfigurationMetadata.from_dict(data.get("metadata", {}))
config.defaults = DefaultSettings.from_dict(data.get("defaults", {}))
config.network = NetworkSettings.from_dict(data.get("network", {}))
# Load trigger ports
trigger_ports_data = data.get("trigger_ports", {})
for key, port_data in trigger_ports_data.items():
config.trigger_ports[key] = TriggerPortConfig.from_dict(port_data)
return config