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 [applying, setApplying] = 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) setConfig(configData) setOriginalConfig(configData) setHasChanges(false) } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to load camera configuration' setError(errorMessage) onError?.(errorMessage) } finally { setLoading(false) } } const updateSetting = (key: keyof CameraConfigUpdate, value: number | boolean) => { 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) } 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 applyConfig = async () => { try { setApplying(true) setError(null) const result = await visionApi.applyCameraConfig(cameraName) if (result.success) { onSuccess?.('Configuration applied successfully. Camera restarted.') } else { throw new Error(result.message) } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to apply configuration' setError(errorMessage) onError?.(errorMessage) } finally { setApplying(false) } } const resetChanges = () => { if (originalConfig) { setConfig(originalConfig) setHasChanges(false) } } if (!isOpen) return null return (
{/* Header */}

Camera Configuration - {cameraName}

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

{error}

)} {config && !loading && (
{/* 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
{/* 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
{/* Auto-Recording Settings */}

Auto-Recording Settings

Start recording when MQTT machine state changes to ON

updateSetting('auto_recording_max_retries', parseInt(e.target.value))} className="w-full" />
1 10
updateSetting('auto_recording_retry_delay_seconds', parseInt(e.target.value))} className="w-full" />
1s 30s
{/* Information */}

Configuration Notes

  • Real-time settings (exposure, gain, image quality) apply immediately
  • Noise reduction settings require camera restart to take effect
  • Use "Apply & Restart" to apply settings that require restart
  • HDR mode may impact performance when enabled
)}
{/* Footer */} {config && !loading && (
{hasChanges && ( You have unsaved changes )}
{hasChanges && ( )}
)}
) }