Errors API Reference
This page documents the exception classes used in XClock for error handling.
Overview
XClock defines a hierarchy of exception classes for handling different types of errors that may occur during device operation. All XClock exceptions inherit from a common base class, making it easy to catch all XClock-related errors.
from xclock.errors import XClockException, XClockValueError
Exception Hierarchy
Exception (built-in)
└── XClockException
├── XClockValueError
├── XClockDeviceError
└── XClockTimeoutError
Exception Classes
XClockException
Base exception class for all XClock errors.
class XClockException(Exception):
"""
Base exception for all XClock errors.
This is the parent class for all XClock-specific exceptions.
Catching this exception will catch all XClock errors.
"""
Usage:
Use this to catch any XClock-related error:
from xclock.devices import LabJackT4
from xclock.errors import XClockException
try:
device = LabJackT4()
device.add_clock_channel(clock_tick_rate_hz=100)
device.start_clocks()
except XClockException as e:
print(f"XClock error occurred: {e}")
finally:
device.close()
XClockValueError
Raised when invalid values or parameters are provided.
class XClockValueError(XClockException):
"""
Exception raised for invalid parameter values.
This exception is raised when a parameter value is invalid,
out of range, or conflicts with other parameters.
"""
Common Causes:
Invalid frequency values (negative, zero, or out of range)
Invalid channel names
Conflicting parameters (e.g., both
duration_sandnumber_of_pulsesspecified)Invalid configuration combinations
Examples:
from xclock.devices import LabJackT4
from xclock.errors import XClockValueError
device = LabJackT4()
# Example 1: Invalid frequency
try:
device.add_clock_channel(clock_tick_rate_hz=-100) # Negative frequency
except XClockValueError as e:
print(f"Invalid value: {e}")
# Example 2: Invalid channel name
try:
device.add_clock_channel(
clock_tick_rate_hz=100,
channel_name="INVALID_CHANNEL"
)
except XClockValueError as e:
print(f"Invalid channel: {e}")
# Example 3: Conflicting parameters
try:
device.add_clock_channel(
clock_tick_rate_hz=100,
duration_s=10.0,
number_of_pulses=500 # Can't specify both
)
except XClockValueError as e:
print(f"Parameter conflict: {e}")
device.close()
XClockDeviceError
Raised when device-level errors occur.
class XClockDeviceError(XClockException):
"""
Exception raised for device-level errors.
This exception is raised when there are problems communicating
with or controlling the hardware device.
"""
Common Causes:
Device not found or not connected
USB communication errors
Device initialization failures
Hardware malfunction
Driver/SDK errors
Examples:
from xclock.devices import LabJackT4
from xclock.errors import XClockDeviceError
# Example 1: Device not connected
try:
device = LabJackT4() # No device connected
except XClockDeviceError as e:
print(f"Device error: {e}")
print("Please check USB connection")
# Example 2: Communication error during operation
try:
device = LabJackT4()
device.add_clock_channel(100, "FIO0")
device.start_clocks()
# ... device unplugged during operation ...
except XClockDeviceError as e:
print(f"Device communication error: {e}")
XClockTimeoutError
Raised when an operation times out.
class XClockTimeoutError(XClockException):
"""
Exception raised when an operation times out.
This exception is raised when a time-limited operation
does not complete within the specified timeout period.
"""
Common Causes:
Waiting for trigger that never arrives
Device not responding
Long-running operations exceeding timeout
Examples:
from xclock.devices import LabJackT4, EdgeType
from xclock.errors import XClockTimeoutError
device = LabJackT4()
# Example 1: Trigger timeout
try:
triggered = device.wait_for_trigger_edge(
channel_name="DIO4",
timeout_s=10.0,
edge_type=EdgeType.RISING,
)
if not triggered:
print("No trigger received within 10 seconds")
except XClockTimeoutError as e:
print(f"Timeout error: {e}")
device.close()
Error Handling Patterns
Pattern 1: Catch All XClock Errors
from xclock.devices import LabJackT4
from xclock.errors import XClockException
try:
device = LabJackT4()
device.add_clock_channel(100, "FIO0", duration_s=10)
device.start_clocks(wait_for_pulsed_clocks_to_finish=True)
except XClockException as e:
print(f"XClock error: {e}")
# Handle any XClock error
finally:
try:
device.close()
except:
pass
Pattern 2: Handle Specific Errors Differently
from xclock.devices import LabJackT4
from xclock.errors import XClockValueError, XClockDeviceError, XClockTimeoutError
try:
device = LabJackT4()
device.add_clock_channel(100, "FIO0")
# Wait for trigger
triggered = device.wait_for_trigger_edge("DIO4", timeout_s=30.0)
if triggered:
device.start_clocks(wait_for_pulsed_clocks_to_finish=True)
except XClockValueError as e:
print(f"Configuration error: {e}")
print("Please check your parameters")
except XClockDeviceError as e:
print(f"Device error: {e}")
print("Please check device connection and drivers")
except XClockTimeoutError as e:
print(f"Timeout: {e}")
print("Operation took too long")
finally:
device.close()
Pattern 3: Retry on Specific Errors
from xclock.devices import LabJackT4
from xclock.errors import XClockDeviceError
import time
MAX_RETRIES = 3
RETRY_DELAY = 2.0
for attempt in range(MAX_RETRIES):
try:
device = LabJackT4()
print("Device connected successfully")
break
except XClockDeviceError as e:
if attempt < MAX_RETRIES - 1:
print(f"Connection failed (attempt {attempt + 1}/{MAX_RETRIES}): {e}")
print(f"Retrying in {RETRY_DELAY} seconds...")
time.sleep(RETRY_DELAY)
else:
print(f"Failed to connect after {MAX_RETRIES} attempts")
raise
Pattern 4: Graceful Degradation
from xclock.devices import LabJackT4, DummyDaqDevice
from xclock.errors import XClockDeviceError
# Try real hardware, fall back to dummy device
try:
device = LabJackT4()
print("Using LabJack T4 hardware")
except XClockDeviceError:
print("Hardware not available, using dummy device")
device = DummyDaqDevice()
# Rest of code works the same
device.add_clock_channel(100, duration_s=5)
device.start_clocks(wait_for_pulsed_clocks_to_finish=True)
device.close()
Pattern 5: Context Manager (if implemented)
from xclock.devices import LabJackT4
from xclock.errors import XClockException
try:
# Assuming context manager support
with LabJackT4() as device:
device.add_clock_channel(100, "FIO0", duration_s=10)
device.start_clocks(wait_for_pulsed_clocks_to_finish=True)
except XClockException as e:
print(f"Error: {e}")
# Device automatically closed
Best Practices
1. Always Close Devices
Even when errors occur, ensure devices are properly closed:
device = None
try:
device = LabJackT4()
# ... operations ...
except XClockException as e:
print(f"Error: {e}")
finally:
if device is not None:
try:
device.close()
except:
pass # Ignore errors during cleanup
2. Provide Context in Error Messages
When re-raising or wrapping exceptions:
from xclock.devices import LabJackT4
from xclock.errors import XClockException
def setup_experiment(frequencies):
"""Setup experiment with multiple clocks."""
try:
device = LabJackT4()
for i, freq in enumerate(frequencies):
device.add_clock_channel(freq)
return device
except XClockException as e:
raise XClockException(
f"Failed to setup experiment with frequencies {frequencies}: {e}"
) from e
3. Log Errors Appropriately
import logging
from xclock.devices import LabJackT4
from xclock.errors import XClockException
logger = logging.getLogger(__name__)
try:
device = LabJackT4()
device.add_clock_channel(100, "FIO0")
device.start_clocks()
except XClockException as e:
logger.error(f"XClock operation failed: {e}", exc_info=True)
raise
finally:
device.close()
4. Validate Early
Validate parameters early to provide clear error messages:
from xclock.errors import XClockValueError
def add_validated_clock(device, frequency, channel=None):
"""Add clock with validation."""
# Validate frequency
if frequency <= 0:
raise XClockValueError(f"Frequency must be positive, got {frequency}")
if frequency > 1_000_000:
raise XClockValueError(f"Frequency too high: {frequency} Hz (max: 1 MHz)")
# Validate channel if specified
if channel is not None:
available = device.get_available_output_clock_channels()
if channel not in available:
raise XClockValueError(
f"Invalid channel '{channel}'. Available: {available}"
)
# Add clock
return device.add_clock_channel(frequency, channel)
Error Messages
XClock aims to provide clear, actionable error messages:
Good error messages include:
What went wrong
Why it happened (if known)
How to fix it (if applicable)
Examples:
❌ Bad: "Invalid parameter"
✅ Good: "Invalid frequency: -100 Hz. Frequency must be positive."
❌ Bad: "Device error"
✅ Good: "Failed to connect to LabJack T4. Check USB connection and ensure drivers are installed."
❌ Bad: "Can't add clock"
✅ Good: "Cannot add clock: channel 'FIO0' is already in use. Available channels: FIO1, FIO2, FIO3"
Common Error Scenarios
Scenario 1: Device Not Found
from xclock.devices import LabJackT4
from xclock.errors import XClockDeviceError
try:
device = LabJackT4()
except XClockDeviceError as e:
print("Troubleshooting steps:")
print("1. Check USB cable is connected")
print("2. Verify device power LED is on")
print("3. Ensure LabJack LJM drivers are installed")
print("4. Try a different USB port")
print("5. Test device with Kipling software")
Scenario 2: Invalid Configuration
from xclock.devices import LabJackT4
from xclock.errors import XClockValueError
device = LabJackT4()
try:
# Trying to use more channels than available
for i in range(20): # Device only has 12 channels
device.add_clock_channel(100)
except XClockValueError as e:
print(f"Configuration error: {e}")
print(f"Device supports up to {len(device.get_available_output_clock_channels())} channels")
Scenario 3: Resource Cleanup Errors
from xclock.devices import LabJackT4
from xclock.errors import XClockException
device = LabJackT4()
device.add_clock_channel(100, "FIO0")
device.start_clocks()
# Don't forget to stop and close!
try:
device.stop_clocks()
device.close()
except XClockException as e:
print(f"Cleanup error: {e}")
# May need to power cycle device
See Also
Devices API Reference - Device API reference
Edge Detection API Reference - Edge detection API
Quick Start Guide - Usage examples