Compare commits

...

3 Commits

Author SHA1 Message Date
yair
969d716283 feat: add simplified syntax to camera control scripts
- Update camera_control.py to support 'property value' syntax
  * camera_control.py gain → gets current gain value
  * camera_control.py gain 33 → sets gain to 33
  * Add --host, --port, and --timeout parameters
  * Remove argcomplete dependency to simplify requirements
  * Maintain backward compatibility with existing commands

- Update launch-ids.py to support simplified property setting at startup
  * launch-ids.py exposure 16 → launches with 16ms exposure
  * launch-ids.py framerate 30 → launches with 30fps framerate
  * launch-ids.py gain 50 → launches with gain set to 50
  * Preserve traditional flag syntax for full backward compatibility

Both scripts now provide intuitive property-based syntax while
maintaining all existing functionality and command-line options.
2025-11-16 05:40:32 +02:00
yair
02dc12a5c4 feat: add simplified syntax to camera control scripts
- Update camera_control.py to support 'property value' syntax
  * camera_control.py gain → gets current gain value
  * camera_control.py gain 33 → sets gain to 33
  * Add --host, --port, and --timeout parameters
  * Maintain backward compatibility with existing commands

- Update launch-ids.py to support simplified property setting at startup
  * launch-ids.py exposure 16 → launches with 16ms exposure
  * launch-ids.py framerate 30 → launches with 30fps framerate
  * launch-ids.py gain 50 → launches with gain set to 50
  * Preserve traditional flag syntax for full backward compatibility

Both scripts now provide intuitive property-based syntax while
maintaining all existing functionality and command-line options.
2025-11-16 05:39:44 +02:00
yair
c95178829a ommit tests 2025-11-16 05:32:17 +02:00
2 changed files with 217 additions and 244 deletions

View File

@@ -1,31 +1,30 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# /// script # /// script
# requires-python = ">=3.8" # requires-python = ">=3.8"
# dependencies = ["argcomplete"] # dependencies = []
# /// # ///
""" """
Test client for UDP exposure control client for UDP camera control
Usage: Usage:
uv run scripts/camera_control.py # Get all camera settings (default) uv run scripts/camera_control.py # Get all camera settings (default)
uv run scripts/camera_control.py get-all # Get all camera settings uv run scripts/camera_control.py get-all # Get all camera settings
uv run scripts/camera_control.py test # Run full test suite uv run scripts/camera_control.py exposure # Get current exposure
uv run scripts/camera_control.py get-exposure # Get current exposure uv run scripts/camera_control.py exposure 10 # Set exposure to 10ms
uv run scripts/camera_control.py set-exposure 10 # Set exposure to 10ms uv run scripts/camera_control.py framerate # Get current framerate
uv run scripts/camera_control.py get-framerate # Get current framerate uv run scripts/camera_control.py framerate 30 # Set framerate to 30fps
uv run scripts/camera_control.py set-framerate 30 # Set framerate to 30fps uv run scripts/camera_control.py gain # Get current gain
uv run scripts/camera_control.py get-gain # Get current gain uv run scripts/camera_control.py gain 50 # Set gain to 50
uv run scripts/camera_control.py set-gain 50 # Set gain to 50 uv run scripts/camera_control.py auto-exposure # Get auto-exposure status
uv run scripts/camera_control.py get-auto-exposure # Get auto-exposure status uv run scripts/camera_control.py auto-exposure 1 # Enable auto-exposure
uv run scripts/camera_control.py set-auto-exposure 1 # Enable auto-exposure uv run scripts/camera_control.py auto-gain # Get auto-gain status
uv run scripts/camera_control.py get-auto-gain # Get auto-gain status uv run scripts/camera_control.py auto-gain 1 # Enable auto-gain
uv run scripts/camera_control.py set-auto-gain 1 # Enable auto-gain uv run scripts/camera_control.py gain-boost # Get gain boost status
uv run scripts/camera_control.py get-gain-boost # Get gain boost status uv run scripts/camera_control.py gain-boost 1 # Enable gain boost
uv run scripts/camera_control.py set-gain-boost 1 # Enable gain boost
uv run scripts/camera_control.py status # Get pipeline status uv run scripts/camera_control.py status # Get pipeline status
This script provides both individual control commands and full test suite functionality This script provides control commands
for the UDP control interface for the IDS uEye camera. for the UDP control interface for the IDS uEye camera.
Make sure launch-ids.py is running before executing commands. Make sure launch-ids.py is running before executing commands.
""" """
@@ -35,10 +34,6 @@ import socket
import time import time
import sys import sys
try:
import argcomplete
except ImportError:
argcomplete = None
def send_command(command, host="127.0.0.1", port=5001, timeout=1.0): def send_command(command, host="127.0.0.1", port=5001, timeout=1.0):
"""Send a command and return the response""" """Send a command and return the response"""
@@ -60,27 +55,11 @@ def send_command(command, host="127.0.0.1", port=5001, timeout=1.0):
finally: finally:
sock.close() sock.close()
def print_test(test_num, description, command, response):
"""Print formatted test result"""
print(f"\nTest {test_num}: {description}")
print(f" Command: {command}")
print(f" Response: {response}")
# Check if response indicates success def simple_command(command, description="Command", host="127.0.0.1", port=5001, timeout=1.0):
if response.startswith("OK"):
print(" ✓ PASS")
elif response.startswith("ERROR"):
if "OUT_OF_RANGE" in response or "INVALID" in response:
print(" ✓ PASS (Expected error)")
else:
print(" ✗ FAIL (Unexpected error)")
else:
print(" ? UNKNOWN")
def simple_command(command, description="Command"):
"""Execute a single command and print the result""" """Execute a single command and print the result"""
print(f"{description}...") print(f"{description}...")
response = send_command(command) response = send_command(command, host, port, timeout)
print(f"Response: {response}") print(f"Response: {response}")
# Check if response indicates success # Check if response indicates success
@@ -93,7 +72,7 @@ def simple_command(command, description="Command"):
print(f"Unknown response: {response}") print(f"Unknown response: {response}")
return False return False
def get_all_settings(): def get_all_settings(host="127.0.0.1", port=5001, timeout=1.0):
"""Get all camera settings""" """Get all camera settings"""
print("=" * 70) print("=" * 70)
print("Camera Settings") print("Camera Settings")
@@ -111,7 +90,7 @@ def get_all_settings():
all_success = True all_success = True
for name, command in settings: for name, command in settings:
response = send_command(command) response = send_command(command, host, port, timeout)
print(f"{name:20s}: {response}") print(f"{name:20s}: {response}")
if response.startswith("ERROR"): if response.startswith("ERROR"):
all_success = False all_success = False
@@ -119,246 +98,186 @@ def get_all_settings():
print("=" * 70) print("=" * 70)
return all_success return all_success
def run_full_tests():
"""Run the full test suite (original functionality)"""
print("=" * 70)
print("UDP Exposure Control Test Client")
print("=" * 70)
print("Testing UDP control interface on 127.0.0.1:5001")
print()
# Check if server is reachable
print("Checking if control server is reachable...")
response = send_command("STATUS", timeout=2.0)
if "Timeout" in response:
print("✗ FAILED: Control server not responding")
print(" Make sure launch-ids.py is running first!")
sys.exit(1)
print("✓ Control server is reachable\n")
time.sleep(0.2)
# Test 1: Get current exposure
response = send_command("GET_EXPOSURE")
print_test(1, "Get current exposure", "GET_EXPOSURE", response)
time.sleep(0.2)
# Test 2: Set exposure to 10ms
response = send_command("SET_EXPOSURE 10")
print_test(2, "Set exposure to 10ms", "SET_EXPOSURE 10", response)
time.sleep(5.2)
# Test 3: Verify exposure was set
response = send_command("GET_EXPOSURE")
print_test(3, "Verify exposure changed", "GET_EXPOSURE", response)
time.sleep(0.2)
# Test 4: Set exposure to 2ms
response = send_command("SET_EXPOSURE 2")
print_test(4, "Set exposure to 2ms", "SET_EXPOSURE 2", response)
time.sleep(0.2)
# Test 5: Get framerate
response = send_command("GET_FRAMERATE")
print_test(5, "Get current framerate", "GET_FRAMERATE", response)
time.sleep(0.2)
# Test 6: Set framerate
response = send_command("SET_FRAMERATE 44")
print_test(6, "Set framerate to 44 fps", "SET_FRAMERATE 44", response)
time.sleep(0.2)
# Test 7: Verify framerate
response = send_command("GET_FRAMERATE")
print_test(7, "Verify framerate changed", "GET_FRAMERATE", response)
time.sleep(0.2)
# Test 8: Get status
response = send_command("STATUS")
print_test(8, "Get pipeline status", "STATUS", response)
time.sleep(0.2)
# Test 9: Invalid command
response = send_command("INVALID_CMD")
print_test(9, "Send invalid command", "INVALID_CMD", response)
time.sleep(0.2)
# Test 14: Restore original exposure (2ms)
response = send_command("SET_EXPOSURE 2")
print_test(14, "Restore exposure to 2ms", "SET_EXPOSURE 2", response)
time.sleep(0.2)
# Test 15: Restore original framerate (22 fps)
response = send_command("SET_FRAMERATE 22")
print_test(15, "Restore framerate to 22 fps", "SET_FRAMERATE 22", response)
print()
print("=" * 70)
print("Test completed!")
print()
print("Quick reference:")
print(" echo 'SET_EXPOSURE 10' | nc -u 127.0.0.1 5001")
print(" echo 'GET_EXPOSURE' | nc -u 127.0.0.1 5001")
print(" echo 'STATUS' | nc -u 127.0.0.1 5001")
print("=" * 70)
def main(): def main():
"""Main function with argument parsing""" """Main function with argument parsing"""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="UDP Exposure Control Test Client", description="UDP Camera Control Client",
epilog=""" epilog="""
Examples: Examples:
%(prog)s # Get all camera settings (default) %(prog)s # Get all camera settings (default)
%(prog)s test # Run full test suite
%(prog)s get-all # Get all camera settings %(prog)s get-all # Get all camera settings
%(prog)s get-exposure # Get current exposure %(prog)s exposure # Get current exposure
%(prog)s set-exposure 10 # Set exposure to 10ms %(prog)s exposure 10 # Set exposure to 10ms
%(prog)s get-framerate # Get current framerate %(prog)s framerate # Get current framerate
%(prog)s set-framerate 30 # Set framerate to 30fps %(prog)s framerate 30 # Set framerate to 30fps
%(prog)s get-gain # Get current gain %(prog)s gain # Get current gain
%(prog)s set-gain 50 # Set gain to 50 %(prog)s gain 50 # Set gain to 50
%(prog)s get-auto-exposure # Get auto-exposure status %(prog)s auto-exposure # Get auto-exposure status
%(prog)s set-auto-exposure 1 # Enable auto-exposure %(prog)s auto-exposure 1 # Enable auto-exposure
%(prog)s get-auto-gain # Get auto-gain status %(prog)s auto-gain # Get auto-gain status
%(prog)s set-auto-gain 1 # Enable auto-gain %(prog)s auto-gain 1 # Enable auto-gain
%(prog)s get-gain-boost # Get gain boost status %(prog)s gain-boost # Get gain boost status
%(prog)s set-gain-boost 1 # Enable gain boost %(prog)s gain-boost 1 # Enable gain boost
%(prog)s status # Get pipeline status %(prog)s status # Get pipeline status
""", """,
formatter_class=argparse.RawDescriptionHelpFormatter formatter_class=argparse.RawDescriptionHelpFormatter
) )
parser.add_argument('command', parser.add_argument('property',
nargs='?', nargs='?',
choices=['test', 'get-all', 'get-exposure', 'get-exposure-range', 'set-exposure', choices=['get-all', 'exposure', 'framerate', 'gain',
'get-framerate', 'set-framerate', 'get-gain', 'set-gain', 'auto-exposure', 'auto-gain', 'gain-boost', 'status'],
'get-auto-exposure', 'set-auto-exposure', help='Property to get or set')
'get-auto-gain', 'set-auto-gain',
'get-gain-boost', 'set-gain-boost', 'status'],
help='Command to execute')
parser.add_argument('value', parser.add_argument('value',
nargs='?', nargs='?',
type=float, help='Value to set (if not provided, gets current value)')
help='Value for set commands (exposure in ms, framerate in fps)')
parser.add_argument('--host', parser.add_argument('--host',
default='127.0.0.1', default='127.0.0.1',
metavar='IP',
help='Host address (default: 127.0.0.1)') help='Host address (default: 127.0.0.1)')
parser.add_argument('--port', parser.add_argument('--port',
type=int, type=int,
default=5001, default=5001,
metavar='PORT',
help='Port number (default: 5001)') help='Port number (default: 5001)')
# Enable tab completion if argcomplete is available parser.add_argument('--timeout',
if argcomplete: type=float,
argcomplete.autocomplete(parser) default=1.0,
metavar='SECONDS',
help='Timeout for UDP requests in seconds (default: 1.0)')
args = parser.parse_args() args = parser.parse_args()
# If no command provided, run get-all by default # If no property provided, run get-all by default
if args.command is None: if args.property is None:
success = get_all_settings() success = get_all_settings(host=args.host, port=args.port, timeout=args.timeout)
sys.exit(0 if success else 1) sys.exit(0 if success else 1)
# Handle test command (full test suite) # Handle special commands
if args.command == 'test': if args.property == 'get-all':
run_full_tests() success = get_all_settings(host=args.host, port=args.port, timeout=args.timeout)
return elif args.property == 'status':
success = simple_command("STATUS", "Getting pipeline status",
host=args.host, port=args.port, timeout=args.timeout)
# Handle individual commands # Handle property-based commands
if args.command == 'get-all': elif args.property == 'exposure':
success = get_all_settings()
elif args.command == 'get-exposure':
success = simple_command("GET_EXPOSURE", "Getting current exposure")
elif args.command == 'get-exposure-range':
success = simple_command("GET_EXPOSURE_RANGE", "Getting exposure range")
elif args.command == 'set-exposure':
if args.value is None: if args.value is None:
print("Error: set-exposure requires a value (exposure in milliseconds)") # Get exposure
parser.print_help() success = simple_command("GET_EXPOSURE", "Getting current exposure",
host=args.host, port=args.port, timeout=args.timeout)
else:
# Set exposure
try:
exposure_value = float(args.value)
success = simple_command(f"SET_EXPOSURE {exposure_value}",
f"Setting exposure to {exposure_value}ms",
host=args.host, port=args.port, timeout=args.timeout)
except ValueError:
print(f"Error: Invalid exposure value '{args.value}'. Must be a number.")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_EXPOSURE {args.value}", f"Setting exposure to {args.value}ms")
elif args.command == 'get-framerate': elif args.property == 'framerate':
success = simple_command("GET_FRAMERATE", "Getting current framerate")
elif args.command == 'set-framerate':
if args.value is None: if args.value is None:
print("Error: set-framerate requires a value (framerate in fps)") # Get framerate
parser.print_help() success = simple_command("GET_FRAMERATE", "Getting current framerate",
host=args.host, port=args.port, timeout=args.timeout)
else:
# Set framerate
try:
framerate_value = float(args.value)
success = simple_command(f"SET_FRAMERATE {framerate_value}",
f"Setting framerate to {framerate_value}fps",
host=args.host, port=args.port, timeout=args.timeout)
except ValueError:
print(f"Error: Invalid framerate value '{args.value}'. Must be a number.")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_FRAMERATE {args.value}", f"Setting framerate to {args.value}fps")
elif args.command == 'get-gain': elif args.property == 'gain':
success = simple_command("GET_GAIN", "Getting current gain")
elif args.command == 'set-gain':
if args.value is None: if args.value is None:
print("Error: set-gain requires a value (0-100, 0 for auto)") # Get gain
parser.print_help() success = simple_command("GET_GAIN", "Getting current gain",
sys.exit(1) host=args.host, port=args.port, timeout=args.timeout)
# Convert to int for gain else:
gain_value = int(args.value) # Set gain
try:
gain_value = int(float(args.value))
if gain_value < 0 or gain_value > 100: if gain_value < 0 or gain_value > 100:
print("Error: Gain must be between 0 and 100 (0 for auto)") print("Error: Gain must be between 0 and 100 (0 for auto)")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_GAIN {gain_value}", f"Setting gain to {gain_value}") success = simple_command(f"SET_GAIN {gain_value}",
f"Setting gain to {gain_value}",
elif args.command == 'get-auto-exposure': host=args.host, port=args.port, timeout=args.timeout)
success = simple_command("GET_AUTO_EXPOSURE", "Getting auto-exposure status") except ValueError:
print(f"Error: Invalid gain value '{args.value}'. Must be a number 0-100.")
elif args.command == 'set-auto-exposure':
if args.value is None:
print("Error: set-auto-exposure requires a value (0=off, 1=on)")
parser.print_help()
sys.exit(1) sys.exit(1)
# Convert to int
ae_value = int(args.value) elif args.property == 'auto-exposure':
if args.value is None:
# Get auto-exposure
success = simple_command("GET_AUTO_EXPOSURE", "Getting auto-exposure status",
host=args.host, port=args.port, timeout=args.timeout)
else:
# Set auto-exposure
try:
ae_value = int(float(args.value))
if ae_value not in [0, 1]: if ae_value not in [0, 1]:
print("Error: Auto-exposure must be 0 (off) or 1 (on)") print("Error: Auto-exposure must be 0 (off) or 1 (on)")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_AUTO_EXPOSURE {ae_value}", success = simple_command(f"SET_AUTO_EXPOSURE {ae_value}",
f"{'Enabling' if ae_value else 'Disabling'} auto-exposure") f"{'Enabling' if ae_value else 'Disabling'} auto-exposure",
host=args.host, port=args.port, timeout=args.timeout)
elif args.command == 'get-auto-gain': except ValueError:
success = simple_command("GET_AUTO_GAIN", "Getting auto-gain status") print(f"Error: Invalid auto-exposure value '{args.value}'. Must be 0 or 1.")
elif args.command == 'set-auto-gain':
if args.value is None:
print("Error: set-auto-gain requires a value (0=off, 1=on)")
parser.print_help()
sys.exit(1) sys.exit(1)
# Convert to int
ag_value = int(args.value) elif args.property == 'auto-gain':
if args.value is None:
# Get auto-gain
success = simple_command("GET_AUTO_GAIN", "Getting auto-gain status",
host=args.host, port=args.port, timeout=args.timeout)
else:
# Set auto-gain
try:
ag_value = int(float(args.value))
if ag_value not in [0, 1]: if ag_value not in [0, 1]:
print("Error: Auto-gain must be 0 (off) or 1 (on)") print("Error: Auto-gain must be 0 (off) or 1 (on)")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_AUTO_GAIN {ag_value}", success = simple_command(f"SET_AUTO_GAIN {ag_value}",
f"{'Enabling' if ag_value else 'Disabling'} auto-gain") f"{'Enabling' if ag_value else 'Disabling'} auto-gain",
host=args.host, port=args.port, timeout=args.timeout)
elif args.command == 'get-gain-boost': except ValueError:
success = simple_command("GET_GAIN_BOOST", "Getting gain boost status") print(f"Error: Invalid auto-gain value '{args.value}'. Must be 0 or 1.")
elif args.command == 'set-gain-boost':
if args.value is None:
print("Error: set-gain-boost requires a value (0=off, 1=on)")
parser.print_help()
sys.exit(1) sys.exit(1)
# Convert to int
gb_value = int(args.value) elif args.property == 'gain-boost':
if args.value is None:
# Get gain-boost
success = simple_command("GET_GAIN_BOOST", "Getting gain boost status",
host=args.host, port=args.port, timeout=args.timeout)
else:
# Set gain-boost
try:
gb_value = int(float(args.value))
if gb_value not in [0, 1]: if gb_value not in [0, 1]:
print("Error: Gain boost must be 0 (off) or 1 (on)") print("Error: Gain boost must be 0 (off) or 1 (on)")
sys.exit(1) sys.exit(1)
success = simple_command(f"SET_GAIN_BOOST {gb_value}", success = simple_command(f"SET_GAIN_BOOST {gb_value}",
f"{'Enabling' if gb_value else 'Disabling'} gain boost") f"{'Enabling' if gb_value else 'Disabling'} gain boost",
host=args.host, port=args.port, timeout=args.timeout)
except ValueError:
print(f"Error: Invalid gain boost value '{args.value}'. Must be 0 or 1.")
sys.exit(1)
elif args.command == 'status': else:
success = simple_command("STATUS", "Getting pipeline status") print(f"Error: Unknown property '{args.property}'")
parser.print_help()
sys.exit(1)
# Exit with appropriate code # Exit with appropriate code
sys.exit(0 if success else 1) sys.exit(0 if success else 1)

View File

@@ -17,7 +17,10 @@
# Basic Usage: # Basic Usage:
# uv run .\scripts\launch-ids.py # Use all defaults # uv run .\scripts\launch-ids.py # Use all defaults
# uv run .\scripts\launch-ids.py --help # Show all options # uv run .\scripts\launch-ids.py --help # Show all options
# uv run .\scripts\launch-ids.py -e 16 -f 30 # Set exposure & framerate # uv run .\scripts\launch-ids.py exposure 16 # Set exposure to 16ms (simplified)
# uv run .\scripts\launch-ids.py framerate 30 # Set framerate to 30fps (simplified)
# uv run .\scripts\launch-ids.py gain 50 # Set gain to 50 (simplified)
# uv run .\scripts\launch-ids.py -e 16 -f 30 # Set exposure & framerate (traditional)
# uv run .\scripts\launch-ids.py --port 6000 # Custom streaming port # uv run .\scripts\launch-ids.py --port 6000 # Custom streaming port
# uv run .\scripts\launch-ids.py --no-crop --quiet # No cropping, minimal output # uv run .\scripts\launch-ids.py --no-crop --quiet # No cropping, minimal output
# uv run .\scripts\launch-ids.py --display # Enable 1/4 sized preview window # uv run .\scripts\launch-ids.py --display # Enable 1/4 sized preview window
@@ -493,7 +496,10 @@ def parse_arguments():
epilog=""" epilog="""
Examples: Examples:
%(prog)s # Use all defaults %(prog)s # Use all defaults
%(prog)s --exposure 16 --framerate 30 # Basic video settings %(prog)s exposure 16 # Set exposure to 16ms
%(prog)s framerate 30 # Set framerate to 30fps
%(prog)s gain 50 # Set gain to 50
%(prog)s --exposure 16 --framerate 30 # Traditional flag syntax
%(prog)s --config custom.ini --port 6000 # Custom config and streaming port %(prog)s --config custom.ini --port 6000 # Custom config and streaming port
%(prog)s --host 192.168.1.100 --no-crop # Stream to remote host without cropping %(prog)s --host 192.168.1.100 --no-crop # Stream to remote host without cropping
%(prog)s --control-port 6001 --verbose # Custom control port with verbose output %(prog)s --control-port 6001 --verbose # Custom control port with verbose output
@@ -502,6 +508,16 @@ Examples:
add_help=True add_help=True
) )
# Add positional arguments for simplified syntax
parser.add_argument('property',
nargs='?',
choices=['exposure', 'framerate', 'gain', 'auto-exposure', 'auto-gain', 'gain-boost'],
help='Camera property to set (simplified syntax)')
parser.add_argument('value',
nargs='?',
help='Value to set for the property (simplified syntax)')
# Camera configuration # Camera configuration
camera_group = parser.add_argument_group('Camera Settings') camera_group = parser.add_argument_group('Camera Settings')
camera_group.add_argument( camera_group.add_argument(
@@ -636,6 +652,44 @@ Examples:
args = parser.parse_args() args = parser.parse_args()
# Handle simplified syntax (positional arguments)
if args.property and args.value:
try:
if args.property == 'exposure':
exposure_val = float(args.value)
if args.exposure is not None:
parser.error("Cannot specify exposure with both simplified syntax and --exposure flag")
args.exposure = exposure_val
elif args.property == 'framerate':
framerate_val = float(args.value)
if args.framerate is not None:
parser.error("Cannot specify framerate with both simplified syntax and --framerate flag")
args.framerate = framerate_val
elif args.property == 'gain':
gain_val = int(float(args.value))
if args.gain is not None:
parser.error("Cannot specify gain with both simplified syntax and --gain flag")
args.gain = gain_val
elif args.property == 'auto-exposure':
ae_val = int(float(args.value))
if ae_val not in [0, 1]:
parser.error("Auto-exposure value must be 0 (off) or 1 (on)")
args.auto_exposure = bool(ae_val)
elif args.property == 'auto-gain':
ag_val = int(float(args.value))
if ag_val not in [0, 1]:
parser.error("Auto-gain value must be 0 (off) or 1 (on)")
args.auto_gain = bool(ag_val)
elif args.property == 'gain-boost':
gb_val = int(float(args.value))
if gb_val not in [0, 1]:
parser.error("Gain-boost value must be 0 (off) or 1 (on)")
args.gain_boost = bool(gb_val)
except ValueError:
parser.error(f"Invalid value '{args.value}' for property '{args.property}'")
elif args.property and not args.value:
parser.error(f"Property '{args.property}' requires a value")
# Validation - only validate if provided # Validation - only validate if provided
if args.exposure is not None and (args.exposure < 0.015 or args.exposure > 30000): if args.exposure is not None and (args.exposure < 0.015 or args.exposure > 30000):
parser.error(f"Exposure must be between 0.015 and 30000 ms, got {args.exposure}") parser.error(f"Exposure must be between 0.015 and 30000 ms, got {args.exposure}")