pre uc480 integration
This commit is contained in:
+142
-23
@@ -126,17 +126,34 @@ class HeliosLaser:
|
||||
Send a query and read response.
|
||||
|
||||
Args:
|
||||
command: ASCII query command (without CR)
|
||||
command: ASCII query command (without CR or ?)
|
||||
|
||||
Returns:
|
||||
Response string or None if error
|
||||
Response value string or None if error
|
||||
"""
|
||||
if not self._send_command(command):
|
||||
return None
|
||||
|
||||
try:
|
||||
response = self.serial.readline().decode('ascii').strip()
|
||||
# Clear any pending data in the buffer
|
||||
self.serial.reset_input_buffer()
|
||||
time.sleep(0.05)
|
||||
|
||||
if not self._send_command(command):
|
||||
return None
|
||||
|
||||
time.sleep(0.2) # Give device time to respond
|
||||
|
||||
response = self.serial.read_until(b'\r').decode('ascii', errors='replace').strip()
|
||||
logger.debug(f"Query '{command}' response: {response}")
|
||||
|
||||
# Helios format: "COMMAND = VALUE UNIT"
|
||||
# Extract just the value part
|
||||
if '=' in response:
|
||||
parts = response.split('=')
|
||||
if len(parts) >= 2:
|
||||
value_part = parts[1].strip()
|
||||
# Remove unit suffix if present (e.g., "ns", "mA", "mW")
|
||||
value = value_part.split()[0]
|
||||
return value
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
@@ -160,7 +177,12 @@ class HeliosLaser:
|
||||
# Convert frequency to period in nanoseconds
|
||||
period_ns = int(1e9 / frequency)
|
||||
|
||||
command = f"FP={period_ns}"
|
||||
# Clamp to valid range (8000-60000 ns)
|
||||
if not (8000 <= period_ns <= 60000):
|
||||
logger.error(f"Period {period_ns} ns out of range (8000-60000)")
|
||||
return False
|
||||
|
||||
command = f"LDF {period_ns}"
|
||||
return self._send_command(command)
|
||||
|
||||
def set_current_ma(self, current: int) -> bool:
|
||||
@@ -168,16 +190,16 @@ class HeliosLaser:
|
||||
Set pump diode current in mA.
|
||||
|
||||
Args:
|
||||
current: Current in mA (0 - 7000)
|
||||
current: Current in mA (0 - 2000 for this model)
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
if not (0 <= current <= 7000):
|
||||
logger.error(f"Current {current} mA out of range (0-7000)")
|
||||
if not (0 <= current <= 2000):
|
||||
logger.error(f"Current {current} mA out of range (0-2000)")
|
||||
return False
|
||||
|
||||
command = f"PC={current}"
|
||||
command = f"LDS {current}"
|
||||
return self._send_command(command)
|
||||
|
||||
def set_pulse_mode(self, mode: PulseMode) -> bool:
|
||||
@@ -190,7 +212,7 @@ class HeliosLaser:
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
command = f"PM={mode.value}"
|
||||
command = f"LDG {mode.value}"
|
||||
return self._send_command(command)
|
||||
|
||||
def set_laser_enable(self, enable: bool) -> bool:
|
||||
@@ -203,7 +225,7 @@ class HeliosLaser:
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
command = f"LE={1 if enable else 0}"
|
||||
command = f"LDO {1 if enable else 0}"
|
||||
success = self._send_command(command)
|
||||
|
||||
if success:
|
||||
@@ -219,12 +241,12 @@ class HeliosLaser:
|
||||
Returns:
|
||||
True if laser is enabled
|
||||
"""
|
||||
response = self._query("LE?")
|
||||
response = self._query("LDO")
|
||||
if response:
|
||||
try:
|
||||
return int(response) == 1
|
||||
except ValueError:
|
||||
logger.error(f"Invalid response for LE?: {response}")
|
||||
logger.error(f"Invalid response for LDO: {response}")
|
||||
return False
|
||||
|
||||
def get_frequency_hz(self) -> Optional[int]:
|
||||
@@ -234,13 +256,13 @@ class HeliosLaser:
|
||||
Returns:
|
||||
Frequency in Hz or None if error
|
||||
"""
|
||||
response = self._query("FP?")
|
||||
response = self._query("LDF")
|
||||
if response:
|
||||
try:
|
||||
period_ns = int(response)
|
||||
return int(1e9 / period_ns)
|
||||
except (ValueError, ZeroDivisionError):
|
||||
logger.error(f"Invalid response for FP?: {response}")
|
||||
logger.error(f"Invalid response for LDF: {response}")
|
||||
return None
|
||||
|
||||
def get_current_ma(self) -> Optional[int]:
|
||||
@@ -250,12 +272,12 @@ class HeliosLaser:
|
||||
Returns:
|
||||
Current in mA or None if error
|
||||
"""
|
||||
response = self._query("PC?")
|
||||
response = self._query("LDS")
|
||||
if response:
|
||||
try:
|
||||
return int(response)
|
||||
except ValueError:
|
||||
logger.error(f"Invalid response for PC?: {response}")
|
||||
logger.error(f"Invalid response for LDS: {response}")
|
||||
return None
|
||||
|
||||
def get_power_mw(self) -> Optional[float]:
|
||||
@@ -265,12 +287,12 @@ class HeliosLaser:
|
||||
Returns:
|
||||
Power in mW or None if error
|
||||
"""
|
||||
response = self._query("PO?")
|
||||
response = self._query("HMP")
|
||||
if response:
|
||||
try:
|
||||
return float(response)
|
||||
except ValueError:
|
||||
logger.error(f"Invalid response for PO?: {response}")
|
||||
logger.error(f"Invalid response for HMP: {response}")
|
||||
return None
|
||||
|
||||
def get_controller_serial(self) -> Optional[str]:
|
||||
@@ -280,7 +302,7 @@ class HeliosLaser:
|
||||
Returns:
|
||||
Serial number string or None if error
|
||||
"""
|
||||
return self._query("SN?")
|
||||
return self._query("CSR")
|
||||
|
||||
def get_head_serial(self) -> Optional[str]:
|
||||
"""
|
||||
@@ -289,7 +311,104 @@ class HeliosLaser:
|
||||
Returns:
|
||||
Serial number string or None if error
|
||||
"""
|
||||
return self._query("HSN?")
|
||||
return self._query("HSR")
|
||||
|
||||
def get_status_registers(self) -> tuple:
|
||||
"""
|
||||
Query LER, LCE, and CCE status registers.
|
||||
|
||||
Each register is a bitmask (sum of flags). Non-zero values indicate
|
||||
active faults. Reset with reset_faults().
|
||||
|
||||
Returns:
|
||||
Tuple of (ler, lce, cce) as ints, or None for each on error.
|
||||
"""
|
||||
def _read_reg(cmd):
|
||||
resp = self._query(cmd)
|
||||
if resp is not None:
|
||||
try:
|
||||
return int(resp)
|
||||
except ValueError:
|
||||
logger.error(f"Invalid response for {cmd}: {resp}")
|
||||
return None
|
||||
|
||||
ler = _read_reg("LER")
|
||||
lce = _read_reg("LCE")
|
||||
cce = _read_reg("CCE")
|
||||
return (ler, lce, cce)
|
||||
|
||||
def reset_faults(self) -> bool:
|
||||
"""
|
||||
Execute the controller reset sequence to clear status registers.
|
||||
|
||||
Protocol-specified sequence: CCE 0 -> LCE 0 -> LER 0
|
||||
|
||||
Returns:
|
||||
True if all three commands sent successfully
|
||||
"""
|
||||
ok = True
|
||||
ok = self._send_command("CCE 0") and ok
|
||||
time.sleep(0.1)
|
||||
ok = self._send_command("LCE 0") and ok
|
||||
time.sleep(0.1)
|
||||
ok = self._send_command("LER 0") and ok
|
||||
if ok:
|
||||
logger.info("Fault reset sequence sent")
|
||||
return ok
|
||||
|
||||
def get_remote_enable(self) -> Optional[bool]:
|
||||
"""
|
||||
Query the remote enable state (LRE - activates utility connector pin 8).
|
||||
|
||||
Returns:
|
||||
True if remote enable is active, False if not, None on error
|
||||
"""
|
||||
response = self._query("LRE")
|
||||
if response is not None:
|
||||
try:
|
||||
return int(response) == 1
|
||||
except ValueError:
|
||||
logger.error(f"Invalid response for LRE: {response}")
|
||||
return None
|
||||
|
||||
def send_raw_command(self, command: str) -> Optional[str]:
|
||||
"""
|
||||
Send a raw command string and return the raw response.
|
||||
|
||||
Useful for diagnostics. Sends *command* + CR, waits briefly,
|
||||
then reads whatever the device returns (up to the first CR or timeout).
|
||||
|
||||
Returns:
|
||||
Raw response string (decoded, stripped) or None on error.
|
||||
"""
|
||||
if not self.is_connected or not self.serial:
|
||||
logger.error("Not connected to laser")
|
||||
return None
|
||||
try:
|
||||
self.serial.reset_input_buffer()
|
||||
time.sleep(0.05)
|
||||
self.serial.write((command + '\r').encode('ascii'))
|
||||
time.sleep(0.3)
|
||||
raw = self.serial.read_until(b'\r')
|
||||
if not raw:
|
||||
raw = self.serial.read(self.serial.in_waiting)
|
||||
return raw.decode('ascii', errors='replace').strip()
|
||||
except Exception as e:
|
||||
logger.error(f"send_raw_command error: {e}")
|
||||
return None
|
||||
|
||||
def set_remote_enable(self, enable: bool) -> bool:
|
||||
"""
|
||||
Set the remote enable state (LRE - utility connector pin 8).
|
||||
|
||||
Args:
|
||||
enable: True to activate remote enable, False to deactivate
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
command = f"LRE {1 if enable else 0}"
|
||||
return self._send_command(command)
|
||||
|
||||
def __del__(self):
|
||||
"""Destructor - ensure cleanup"""
|
||||
|
||||
Reference in New Issue
Block a user