import { useState, useEffect } from 'react' import { visionApi, type CameraConfig, type CameraConfigUpdate } from '../lib/visionApi' interface CameraConfigModalProps { cameraName: string isOpen: boolean onClose: () => void onSuccess?: (message: string) => void onError?: (error: string) => void } export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onError }: CameraConfigModalProps) { const [config, setConfig] = useState(null) const [loading, setLoading] = useState(false) const [saving, setSaving] = useState(false) const [error, setError] = useState(null) const [hasChanges, setHasChanges] = useState(false) const [originalConfig, setOriginalConfig] = useState(null) useEffect(() => { if (isOpen && cameraName) { loadConfig() } }, [isOpen, cameraName]) const loadConfig = async () => { try { setLoading(true) setError(null) const configData = await visionApi.getCameraConfig(cameraName) // The API should now include all fields including video format settings const configWithDefaults = configData setConfig(configWithDefaults) setOriginalConfig(configWithDefaults) setHasChanges(false) } catch (err) { let errorMessage = 'Failed to load camera configuration' if (err instanceof Error) { errorMessage = err.message // Handle specific API validation errors for missing video format fields if (err.message.includes('video_format') || err.message.includes('video_codec') || err.message.includes('video_quality')) { errorMessage = 'Camera configuration is missing video format settings. This may indicate the backend needs to be updated to support MP4 format. Using default values.' // Create a default configuration for display const defaultConfig = { name: cameraName, machine_topic: '', storage_path: '', enabled: true, auto_record_on_machine_start: false, auto_start_recording_enabled: false, auto_recording_max_retries: 3, auto_recording_retry_delay_seconds: 2, exposure_ms: 1.0, gain: 3.5, target_fps: 0, video_format: 'mp4', video_codec: 'mp4v', video_quality: 95, sharpness: 120, contrast: 110, saturation: 100, gamma: 100, noise_filter_enabled: true, denoise_3d_enabled: false, auto_white_balance: true, color_temperature_preset: 0, anti_flicker_enabled: true, light_frequency: 1, bit_depth: 8, hdr_enabled: false, hdr_gain_mode: 0, } setConfig(defaultConfig) setOriginalConfig(defaultConfig) setHasChanges(false) setError(errorMessage) return } } setError(errorMessage) onError?.(errorMessage) } finally { setLoading(false) } } const updateSetting = (key: keyof CameraConfigUpdate, value: number | boolean | string) => { if (!config) return const newConfig = { ...config, [key]: value } setConfig(newConfig) // Check if there are changes from original const hasChanges = originalConfig && Object.keys(newConfig).some(k => { const configKey = k as keyof CameraConfig return newConfig[configKey] !== originalConfig[configKey] }) setHasChanges(!!hasChanges) // Video format settings are read-only, no validation needed } const saveConfig = async () => { if (!config || !originalConfig) return try { setSaving(true) setError(null) // Build update object with only changed values const updates: CameraConfigUpdate = {} const configKeys: (keyof CameraConfigUpdate)[] = [ 'exposure_ms', 'gain', 'target_fps', 'sharpness', 'contrast', 'saturation', 'gamma', 'noise_filter_enabled', 'denoise_3d_enabled', 'auto_white_balance', 'color_temperature_preset', 'anti_flicker_enabled', 'light_frequency', 'hdr_enabled', 'hdr_gain_mode', 'auto_record_on_machine_start', 'auto_start_recording_enabled', 'auto_recording_max_retries', 'auto_recording_retry_delay_seconds' ] configKeys.forEach(key => { if (config[key] !== originalConfig[key]) { updates[key] = config[key] as any } }) if (Object.keys(updates).length === 0) { onSuccess?.('No changes to save') return } const result = await visionApi.updateCameraConfig(cameraName, updates) if (result.success) { setOriginalConfig(config) setHasChanges(false) onSuccess?.(`Configuration updated: ${result.updated_settings.join(', ')}`) } else { throw new Error(result.message) } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to save configuration' setError(errorMessage) onError?.(errorMessage) } finally { setSaving(false) } } const resetChanges = () => { if (originalConfig) { setConfig(originalConfig) setHasChanges(false) } } if (!isOpen) return null return (
e.stopPropagation()}> {/* Close Button */} {/* Header */}

Camera Configuration - {cameraName}

{/* Content */}
{loading && (
Loading configuration...
)} {error && (

Configuration Error

{error}

{error.includes('video_format') && (

Note: The video format settings are displayed with default values. You can still modify and save the configuration, but the backend may need to be updated to fully support MP4 format settings.

)}
)} {config && !loading && (
{/* System Information (Read-Only) */}

System Information

{config.name}
{config.machine_topic}
{config.storage_path}
{config.enabled ? 'Enabled' : 'Disabled'}
{/* Auto-Recording Settings (Read-Only) */}

Auto-Recording Settings

{config.auto_start_recording_enabled ? 'Enabled' : 'Disabled'}
{config.auto_recording_max_retries}
{config.auto_recording_retry_delay_seconds}s

Auto-recording settings are configured in the system configuration file

{/* Basic Settings */}

Basic Settings

updateSetting('exposure_ms', parseFloat(e.target.value))} className="w-full" />
0.1ms 10ms
updateSetting('gain', parseFloat(e.target.value))} className="w-full" />
0 10
updateSetting('target_fps', parseInt(e.target.value))} className="w-full" />
0 (Max) 30
{/* Image Quality Settings */}

Image Quality

updateSetting('sharpness', parseInt(e.target.value))} className="w-full" />
0 200
updateSetting('contrast', parseInt(e.target.value))} className="w-full" />
0 200
updateSetting('saturation', parseInt(e.target.value))} className="w-full" />
0 200
updateSetting('gamma', parseInt(e.target.value))} className="w-full" />
0 300
{/* Color Settings */}

Color Settings

updateSetting('color_temperature_preset', parseInt(e.target.value))} className="w-full" />
0 (Auto) 10
{/* White Balance RGB Gains */}

White Balance RGB Gains

updateSetting('wb_red_gain', parseFloat(e.target.value))} className="w-full" />
0.00 3.99
updateSetting('wb_green_gain', parseFloat(e.target.value))} className="w-full" />
0.00 3.99
updateSetting('wb_blue_gain', parseFloat(e.target.value))} className="w-full" />
0.00 3.99

Manual white balance gains (only effective when Auto White Balance is disabled)

{/* Advanced Settings */}

Advanced Settings

Requires restart to apply

Requires restart to apply

{/* HDR Settings */}

HDR Settings

updateSetting('hdr_gain_mode', parseInt(e.target.value))} className="w-full" disabled={!config.hdr_enabled} />
0 3
{/* Video Recording Settings (Read-Only) */}

Video Recording Settings

{config.video_format?.toUpperCase() || 'MP4'}

Current recording format

{config.video_codec?.toUpperCase() || 'MP4V'}

Compression codec

{config.video_quality || 95}%

Recording quality

Video Format Information

Video recording settings are configured in the system configuration file and require a service restart to modify.

Current benefits: MP4 format provides ~40% smaller file sizes and better web compatibility than AVI.

{/* Information */}

Configuration Notes

  • Real-time settings: Exposure, gain, image quality, white balance - apply immediately
  • System settings: Video format, noise reduction, auto-recording - configured in system files
  • Performance: HDR mode may impact frame rate when enabled
  • White balance: RGB gains only effective when auto white balance is disabled
)}
{/* Footer */} {config && !loading && (
{hasChanges && ( You have unsaved changes )}
{hasChanges && ( )}
)}
) }