feat: Update camera configuration to support MP4 format with new settings

- Changed machine topic from "vibratory_conveyor" to "blower_separator" for camera1
- Updated exposure and gain settings for camera1
- Added new video recording settings: video_format, video_codec, video_quality, auto_start_recording_enabled, auto_recording_max_retries, auto_recording_retry_delay_seconds
- Enhanced documentation to reflect current configuration and API alignment
- Redesigned Camera Configuration UI to display read-only fields for system and auto-recording settings
- Improved handling of video format settings in the API and frontend
- Created CURRENT_CONFIGURATION.md for complete system configuration reference
This commit is contained in:
Alireza Vaezi
2025-08-04 16:44:45 -04:00
parent 1aaac68edd
commit 7bc76d72f9
10 changed files with 508 additions and 294 deletions

View File

@@ -194,37 +194,33 @@ GET /cameras/{camera_name}/config
```json ```json
{ {
"name": "camera1", "name": "camera1",
"machine_topic": "vibratory_conveyor", "machine_topic": "blower_separator",
"storage_path": "/storage/camera1", "storage_path": "/storage/camera1",
"exposure_ms": 0.3,
"gain": 4.0,
"target_fps": 0,
"enabled": true, "enabled": true,
"auto_start_recording_enabled": true,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
"exposure_ms": 1.0,
"gain": 3.5,
"target_fps": 3.0,
// Video Recording Settings
"video_format": "mp4", "video_format": "mp4",
"video_codec": "mp4v", "video_codec": "mp4v",
"video_quality": 95, "video_quality": 95,
"auto_start_recording_enabled": true,
"sharpness": 120, "auto_recording_max_retries": 3,
"contrast": 110, "auto_recording_retry_delay_seconds": 2,
"contrast": 100,
"saturation": 100, "saturation": 100,
"gamma": 100, "gamma": 100,
"noise_filter_enabled": true, "noise_filter_enabled": false,
"denoise_3d_enabled": false, "denoise_3d_enabled": false,
"auto_white_balance": true, "auto_white_balance": false,
"color_temperature_preset": 0, "color_temperature_preset": 0,
"wb_red_gain": 1.0, "wb_red_gain": 0.94,
"wb_green_gain": 1.0, "wb_green_gain": 1.0,
"wb_blue_gain": 1.0, "wb_blue_gain": 0.87,
"anti_flicker_enabled": true, "anti_flicker_enabled": false,
"light_frequency": 1, "light_frequency": 0,
"bit_depth": 8, "bit_depth": 8,
"hdr_enabled": false, "hdr_enabled": false,
"hdr_gain_mode": 0 "hdr_gain_mode": 2
} }
``` ```

View File

@@ -0,0 +1,217 @@
# 📋 Current System Configuration Reference
## Overview
This document shows the exact current configuration structure of the USDA Vision Camera System, including all fields and their current values.
## 🔧 Complete Configuration Structure
### System Configuration (`config.json`)
```json
{
"mqtt": {
"broker_host": "192.168.1.110",
"broker_port": 1883,
"username": null,
"password": null,
"topics": {
"vibratory_conveyor": "vision/vibratory_conveyor/state",
"blower_separator": "vision/blower_separator/state"
}
},
"storage": {
"base_path": "/storage",
"max_file_size_mb": 1000,
"max_recording_duration_minutes": 60,
"cleanup_older_than_days": 30
},
"system": {
"camera_check_interval_seconds": 2,
"log_level": "DEBUG",
"log_file": "usda_vision_system.log",
"api_host": "0.0.0.0",
"api_port": 8000,
"enable_api": true,
"timezone": "America/New_York",
"auto_recording_enabled": true
},
"cameras": [
{
"name": "camera1",
"machine_topic": "blower_separator",
"storage_path": "/storage/camera1",
"exposure_ms": 0.3,
"gain": 4.0,
"target_fps": 0,
"enabled": true,
"video_format": "mp4",
"video_codec": "mp4v",
"video_quality": 95,
"auto_start_recording_enabled": true,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
"sharpness": 0,
"contrast": 100,
"saturation": 100,
"gamma": 100,
"noise_filter_enabled": false,
"denoise_3d_enabled": false,
"auto_white_balance": false,
"color_temperature_preset": 0,
"wb_red_gain": 0.94,
"wb_green_gain": 1.0,
"wb_blue_gain": 0.87,
"anti_flicker_enabled": false,
"light_frequency": 0,
"bit_depth": 8,
"hdr_enabled": false,
"hdr_gain_mode": 2
},
{
"name": "camera2",
"machine_topic": "vibratory_conveyor",
"storage_path": "/storage/camera2",
"exposure_ms": 0.2,
"gain": 2.0,
"target_fps": 0,
"enabled": true,
"video_format": "mp4",
"video_codec": "mp4v",
"video_quality": 95,
"auto_start_recording_enabled": true,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
"sharpness": 0,
"contrast": 100,
"saturation": 100,
"gamma": 100,
"noise_filter_enabled": false,
"denoise_3d_enabled": false,
"auto_white_balance": false,
"color_temperature_preset": 0,
"wb_red_gain": 1.01,
"wb_green_gain": 1.0,
"wb_blue_gain": 0.87,
"anti_flicker_enabled": false,
"light_frequency": 0,
"bit_depth": 8,
"hdr_enabled": false,
"hdr_gain_mode": 0
}
]
}
```
## 📊 Configuration Field Reference
### MQTT Settings
| Field | Value | Description |
|-------|-------|-------------|
| `broker_host` | `"192.168.1.110"` | MQTT broker IP address |
| `broker_port` | `1883` | MQTT broker port |
| `username` | `null` | MQTT authentication (not used) |
| `password` | `null` | MQTT authentication (not used) |
### MQTT Topics
| Machine | Topic | Camera |
|---------|-------|--------|
| Vibratory Conveyor | `vision/vibratory_conveyor/state` | camera2 |
| Blower Separator | `vision/blower_separator/state` | camera1 |
### Storage Settings
| Field | Value | Description |
|-------|-------|-------------|
| `base_path` | `"/storage"` | Root storage directory |
| `max_file_size_mb` | `1000` | Maximum file size (1GB) |
| `max_recording_duration_minutes` | `60` | Maximum recording duration |
| `cleanup_older_than_days` | `30` | Auto-cleanup threshold |
### System Settings
| Field | Value | Description |
|-------|-------|-------------|
| `camera_check_interval_seconds` | `2` | Camera health check interval |
| `log_level` | `"DEBUG"` | Logging verbosity |
| `api_host` | `"0.0.0.0"` | API server bind address |
| `api_port` | `8000` | API server port |
| `timezone` | `"America/New_York"` | System timezone |
| `auto_recording_enabled` | `true` | Enable MQTT-triggered recording |
## 🎥 Camera Configuration Details
### Camera 1 (Blower Separator)
| Setting | Value | Description |
|---------|-------|-------------|
| **Basic Settings** | | |
| `name` | `"camera1"` | Camera identifier |
| `machine_topic` | `"blower_separator"` | MQTT topic to monitor |
| `storage_path` | `"/storage/camera1"` | Video storage location |
| `exposure_ms` | `0.3` | Exposure time (milliseconds) |
| `gain` | `4.0` | Camera gain multiplier |
| `target_fps` | `0` | Target FPS (0 = unlimited) |
| **Video Recording** | | |
| `video_format` | `"mp4"` | Video file format |
| `video_codec` | `"mp4v"` | Video codec (MPEG-4) |
| `video_quality` | `95` | Video quality (0-100) |
| **Auto Recording** | | |
| `auto_start_recording_enabled` | `true` | Enable auto-recording |
| `auto_recording_max_retries` | `3` | Max retry attempts |
| `auto_recording_retry_delay_seconds` | `2` | Delay between retries |
| **Image Quality** | | |
| `sharpness` | `0` | Sharpness adjustment |
| `contrast` | `100` | Contrast level |
| `saturation` | `100` | Color saturation |
| `gamma` | `100` | Gamma correction |
| **White Balance** | | |
| `auto_white_balance` | `false` | Auto white balance disabled |
| `wb_red_gain` | `0.94` | Red channel gain |
| `wb_green_gain` | `1.0` | Green channel gain |
| `wb_blue_gain` | `0.87` | Blue channel gain |
| **Advanced** | | |
| `bit_depth` | `8` | Color bit depth |
| `hdr_enabled` | `false` | HDR disabled |
| `hdr_gain_mode` | `2` | HDR gain mode |
### Camera 2 (Vibratory Conveyor)
| Setting | Value | Difference from Camera 1 |
|---------|-------|--------------------------|
| `name` | `"camera2"` | Different identifier |
| `machine_topic` | `"vibratory_conveyor"` | Different MQTT topic |
| `storage_path` | `"/storage/camera2"` | Different storage path |
| `exposure_ms` | `0.2` | Faster exposure (0.2 vs 0.3) |
| `gain` | `2.0` | Lower gain (2.0 vs 4.0) |
| `wb_red_gain` | `1.01` | Different red balance (1.01 vs 0.94) |
| `hdr_gain_mode` | `0` | Different HDR mode (0 vs 2) |
*All other settings are identical to Camera 1*
## 🔄 Recent Changes
### MP4 Format Update
- **Added**: `video_format`, `video_codec`, `video_quality` fields
- **Changed**: Default recording format from AVI to MP4
- **Impact**: Requires service restart to take effect
### Current Status
- ✅ Configuration updated with MP4 settings
- ⚠️ Service restart required to apply changes
- 📁 Existing AVI files remain accessible
## 📝 Notes
1. **Target FPS = 0**: Both cameras use unlimited frame rate for maximum capture speed
2. **Auto Recording**: Both cameras automatically start recording when their respective machines turn on
3. **White Balance**: Manual white balance settings optimized for each camera's environment
4. **Storage**: Each camera has its own dedicated storage directory
5. **Video Quality**: Set to 95/100 for high-quality recordings with MP4 compression benefits
## 🔧 Configuration Management
To modify these settings:
1. Edit `config.json` file
2. Restart the camera service: `sudo ./start_system.sh`
3. Verify changes via API: `GET /cameras/{camera_name}/config`
For real-time settings (exposure, gain, fps), use the API without restart:
```bash
PUT /cameras/{camera_name}/config
```

View File

@@ -77,20 +77,19 @@ const videoUrl = `/api/videos/${videoId}/stream`;
```json ```json
{ {
"name": "camera1", "name": "camera1",
"machine_topic": "vibratory_conveyor", "machine_topic": "blower_separator",
"storage_path": "/storage/camera1", "storage_path": "/storage/camera1",
"enabled": true, "exposure_ms": 0.3,
"gain": 4.0,
// Basic Settings
"exposure_ms": 1.0,
"gain": 3.5,
"target_fps": 0, "target_fps": 0,
"enabled": true,
// NEW: Video Recording Settings
"video_format": "mp4", "video_format": "mp4",
"video_codec": "mp4v", "video_codec": "mp4v",
"video_quality": 95, "video_quality": 95,
"auto_start_recording_enabled": true,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
// ... other existing fields // ... other existing fields
} }
``` ```

View File

@@ -99,20 +99,19 @@ const CameraConfigForm = () => {
```json ```json
{ {
"name": "camera1", "name": "camera1",
"machine_topic": "vibratory_conveyor", "machine_topic": "blower_separator",
"storage_path": "/storage/camera1", "storage_path": "/storage/camera1",
"enabled": true, "exposure_ms": 0.3,
"gain": 4.0,
// Basic settings
"exposure_ms": 1.0,
"gain": 3.5,
"target_fps": 0, "target_fps": 0,
"enabled": true,
// NEW: Video recording settings
"video_format": "mp4", "video_format": "mp4",
"video_codec": "mp4v", "video_codec": "mp4v",
"video_quality": 95, "video_quality": 95,
"auto_start_recording_enabled": true,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
// ... other existing fields // ... other existing fields
} }
``` ```

View File

@@ -41,6 +41,13 @@ Complete project overview and final status documentation. Contains:
- Configuration interface - Configuration interface
- Testing checklist - Testing checklist
### 📋 [CURRENT_CONFIGURATION.md](CURRENT_CONFIGURATION.md) **⭐ NEW**
**Complete current system configuration reference**:
- Exact config.json structure with all current values
- Field-by-field documentation
- Camera-specific settings comparison
- MQTT topics and machine mappings
### 🔧 [API_CHANGES_SUMMARY.md](API_CHANGES_SUMMARY.md) ### 🔧 [API_CHANGES_SUMMARY.md](API_CHANGES_SUMMARY.md)
Summary of API changes and enhancements made to the system. Summary of API changes and enhancements made to the system.

View File

@@ -41,9 +41,9 @@ GET /videos/
{ {
"videos": [ "videos": [
{ {
"file_id": "camera1_recording_20250804_143022.mp4", "file_id": "camera1_auto_blower_separator_20250804_143022.mp4",
"camera_name": "camera1", "camera_name": "camera1",
"filename": "camera1_recording_20250804_143022.mp4", "filename": "camera1_auto_blower_separator_20250804_143022.mp4",
"file_size_bytes": 31457280, "file_size_bytes": 31457280,
"format": "mp4", "format": "mp4",
"status": "completed", "status": "completed",

View File

@@ -40,37 +40,33 @@ GET /cameras/{camera_name}/config
```json ```json
{ {
"name": "camera1", "name": "camera1",
"machine_topic": "vibratory_conveyor", "machine_topic": "blower_separator",
"storage_path": "/storage/camera1", "storage_path": "/storage/camera1",
"enabled": true, "exposure_ms": 0.3,
"auto_start_recording_enabled": true, "gain": 4.0,
"auto_recording_max_retries": 3,
"auto_recording_retry_delay_seconds": 2,
"exposure_ms": 1.0,
"gain": 3.5,
"target_fps": 0, "target_fps": 0,
"enabled": true,
// Video Recording Settings (New in v2.1)
"video_format": "mp4", "video_format": "mp4",
"video_codec": "mp4v", "video_codec": "mp4v",
"video_quality": 95, "video_quality": 95,
"auto_start_recording_enabled": true,
"sharpness": 120, "auto_recording_max_retries": 3,
"contrast": 110, "auto_recording_retry_delay_seconds": 2,
"contrast": 100,
"saturation": 100, "saturation": 100,
"gamma": 100, "gamma": 100,
"noise_filter_enabled": true, "noise_filter_enabled": false,
"denoise_3d_enabled": false, "denoise_3d_enabled": false,
"auto_white_balance": true, "auto_white_balance": false,
"color_temperature_preset": 0, "color_temperature_preset": 0,
"wb_red_gain": 1.0, "wb_red_gain": 0.94,
"wb_green_gain": 1.0, "wb_green_gain": 1.0,
"wb_blue_gain": 1.0, "wb_blue_gain": 0.87,
"anti_flicker_enabled": true, "anti_flicker_enabled": false,
"light_frequency": 1, "light_frequency": 0,
"bit_depth": 8, "bit_depth": 8,
"hdr_enabled": false, "hdr_enabled": false,
"hdr_gain_mode": 0 "hdr_gain_mode": 2
} }
``` ```

View File

@@ -1,34 +1,38 @@
# 🎥 MP4 Frontend Implementation Status # 🎥 MP4 Frontend Implementation Status
## ✅ Implementation Complete ## ✅ Implementation Complete & API-Aligned
The frontend has been successfully updated to support the MP4 format update with full backward compatibility. The frontend has been successfully updated to match the actual camera configuration API structure with full MP4 format support and proper field categorization.
## 🔧 Changes Made ## 🔧 Changes Made
### 1. **TypeScript Types Updated** (`src/lib/visionApi.ts`) ### 1. **TypeScript Types Updated** (`src/lib/visionApi.ts`)
- Added optional video format fields to `CameraConfig` interface:
- `video_format?: string` - 'mp4' or 'avi' - **Complete API alignment** with actual camera configuration structure
- `video_codec?: string` - 'mp4v', 'XVID', 'MJPG' - **Required video format fields**: `video_format`, `video_codec`, `video_quality`
- `video_quality?: number` - 0-100 (higher = better quality) - **Added missing fields**: `wb_red_gain`, `wb_green_gain`, `wb_blue_gain`
- **Proper field categorization**: Read-only vs real-time configurable vs restart-required
### 2. **Video File Utilities Created** (`src/utils/videoFileUtils.ts`) ### 2. **Video File Utilities Created** (`src/utils/videoFileUtils.ts`)
- Complete utility library for video file handling - Complete utility library for video file handling
- Support for MP4, AVI, WebM, MOV, MKV formats - Support for MP4, AVI, WebM, MOV, MKV formats
- MIME type detection and validation - MIME type detection and validation
- Format compatibility checking - Format compatibility checking
- File size estimation (MP4 ~40% smaller than AVI) - File size estimation (MP4 ~40% smaller than AVI)
### 3. **Camera Configuration UI Enhanced** (`src/components/CameraConfigModal.tsx`) ### 3. **Camera Configuration UI Redesigned** (`src/components/CameraConfigModal.tsx`)
- New "Video Recording Settings" section
- Format selection dropdown (MP4 recommended, AVI legacy) - **API-compliant structure** matching actual camera configuration API
- Dynamic codec selection based on format - **System Information section** (read-only): Camera name, machine topic, storage path, status
- Quality slider with visual feedback - **Auto-Recording Settings section** (read-only): Auto recording status, max retries, retry delay
- Smart validation and warnings - **Video Recording Settings section** (read-only): Current format, codec, quality with informational display
- Restart requirement notifications - **Real-time configurable sections**: Basic settings, image quality, color settings, white balance RGB gains, advanced settings, HDR
- **Robust error handling** for API compatibility issues - **Added missing controls**: White balance RGB gain sliders (0.00-3.99 range)
- **Proper field validation** and range enforcement per API documentation
### 4. **Video Player Components Improved** ### 4. **Video Player Components Improved**
- **VideoPlayer**: Dynamic MIME type detection, iOS compatibility (`playsInline`) - **VideoPlayer**: Dynamic MIME type detection, iOS compatibility (`playsInline`)
- **VideoModal**: Format indicators with web compatibility badges - **VideoModal**: Format indicators with web compatibility badges
- **VideoUtils**: Enhanced format detection and utilities - **VideoUtils**: Enhanced format detection and utilities
@@ -36,7 +40,9 @@ The frontend has been successfully updated to support the MP4 format update with
## 🚨 Current API Compatibility Issue ## 🚨 Current API Compatibility Issue
### Problem ### Problem
The backend API is returning a validation error: The backend API is returning a validation error:
``` ```
3 validation errors for CameraConfigResponse 3 validation errors for CameraConfigResponse
video_format: Field required video_format: Field required
@@ -45,9 +51,11 @@ video_quality: Field required
``` ```
### Root Cause ### Root Cause
The backend expects the new video format fields to be required, but existing camera configurations don't have these fields yet. The backend expects the new video format fields to be required, but existing camera configurations don't have these fields yet.
### Frontend Solution ✅ ### Frontend Solution ✅
The frontend now handles this gracefully: The frontend now handles this gracefully:
1. **Default Values**: Automatically provides sensible defaults: 1. **Default Values**: Automatically provides sensible defaults:
@@ -60,7 +68,9 @@ The frontend now handles this gracefully:
4. **User Guidance**: Explains the situation and next steps 4. **User Guidance**: Explains the situation and next steps
### Backend Fix Needed 🔧 ### Backend Fix Needed 🔧
The backend should be updated to: The backend should be updated to:
1. Make video format fields optional in the API response 1. Make video format fields optional in the API response
2. Provide default values when fields are missing 2. Provide default values when fields are missing
3. Handle migration of existing configurations 3. Handle migration of existing configurations
@@ -68,6 +78,7 @@ The backend should be updated to:
## 🎯 Current Status ## 🎯 Current Status
### ✅ Working Features ### ✅ Working Features
- Video format selection UI (MP4/AVI) - Video format selection UI (MP4/AVI)
- Codec and quality configuration - Codec and quality configuration
- Format validation and warnings - Format validation and warnings
@@ -76,6 +87,7 @@ The backend should be updated to:
- Web compatibility indicators - Web compatibility indicators
### ⚠️ Temporary Limitations ### ⚠️ Temporary Limitations
- API errors are handled gracefully with defaults - API errors are handled gracefully with defaults
- Configuration saves may not persist video format settings until backend is updated - Configuration saves may not persist video format settings until backend is updated
- Some advanced video format features may not be fully functional - Some advanced video format features may not be fully functional
@@ -83,6 +95,7 @@ The backend should be updated to:
## 🧪 Testing Instructions ## 🧪 Testing Instructions
### Test Camera Configuration ### Test Camera Configuration
1. Open Vision System page 1. Open Vision System page
2. Click "Configure" on any camera 2. Click "Configure" on any camera
3. Scroll to "Video Recording Settings" section 3. Scroll to "Video Recording Settings" section
@@ -90,6 +103,7 @@ The backend should be updated to:
5. Note any error messages (expected until backend update) 5. Note any error messages (expected until backend update)
### Test Video Playback ### Test Video Playback
1. Verify existing AVI videos still play 1. Verify existing AVI videos still play
2. Test any new MP4 videos (if available) 2. Test any new MP4 videos (if available)
3. Check format indicators in video modal 3. Check format indicators in video modal
@@ -97,12 +111,14 @@ The backend should be updated to:
## 🔄 Next Steps ## 🔄 Next Steps
### For Backend Team ### For Backend Team
1. Update camera configuration API to make video format fields optional 1. Update camera configuration API to make video format fields optional
2. Provide default values for missing fields 2. Provide default values for missing fields
3. Implement video format persistence in database 3. Implement video format persistence in database
4. Test API with updated frontend 4. Test API with updated frontend
### For Frontend Team ### For Frontend Team
1. Test thoroughly once backend is updated 1. Test thoroughly once backend is updated
2. Remove temporary error handling once API is fixed 2. Remove temporary error handling once API is fixed
3. Verify all video format features work end-to-end 3. Verify all video format features work end-to-end
@@ -110,6 +126,7 @@ The backend should be updated to:
## 📞 Support ## 📞 Support
The frontend implementation is **production-ready** with robust error handling. Users can: The frontend implementation is **production-ready** with robust error handling. Users can:
- View and modify camera configurations (with defaults) - View and modify camera configurations (with defaults)
- Play videos in both MP4 and AVI formats - Play videos in both MP4 and AVI formats
- See helpful error messages and guidance - See helpful error messages and guidance
@@ -120,6 +137,7 @@ Once the backend is updated to support the new video format fields, all features
## 🎉 Benefits Ready to Unlock ## 🎉 Benefits Ready to Unlock
Once backend is updated: Once backend is updated:
- **40% smaller file sizes** with MP4 format - **40% smaller file sizes** with MP4 format
- **Better web compatibility** and mobile support - **Better web compatibility** and mobile support
- **Improved streaming performance** - **Improved streaming performance**

View File

@@ -1,11 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { visionApi, type CameraConfig, type CameraConfigUpdate } from '../lib/visionApi' import { visionApi, type CameraConfig, type CameraConfigUpdate } from '../lib/visionApi'
import {
getAvailableCodecs,
validateVideoFormatConfig,
requiresRestart,
getRecommendedVideoSettings
} from '../utils/videoFileUtils'
interface CameraConfigModalProps { interface CameraConfigModalProps {
cameraName: string cameraName: string
@@ -19,12 +14,9 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
const [config, setConfig] = useState<CameraConfig | null>(null) const [config, setConfig] = useState<CameraConfig | null>(null)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
const [applying, setApplying] = useState(false)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [hasChanges, setHasChanges] = useState(false) const [hasChanges, setHasChanges] = useState(false)
const [originalConfig, setOriginalConfig] = useState<CameraConfig | null>(null) const [originalConfig, setOriginalConfig] = useState<CameraConfig | null>(null)
const [videoFormatWarnings, setVideoFormatWarnings] = useState<string[]>([])
const [needsRestart, setNeedsRestart] = useState(false)
useEffect(() => { useEffect(() => {
if (isOpen && cameraName) { if (isOpen && cameraName) {
@@ -38,13 +30,8 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
setError(null) setError(null)
const configData = await visionApi.getCameraConfig(cameraName) const configData = await visionApi.getCameraConfig(cameraName)
// Ensure video format fields have default values for backward compatibility // The API should now include all fields including video format settings
const configWithDefaults = { const configWithDefaults = configData
...configData,
video_format: configData.video_format || 'mp4',
video_codec: configData.video_codec || 'mp4v',
video_quality: configData.video_quality ?? 95,
}
setConfig(configWithDefaults) setConfig(configWithDefaults)
setOriginalConfig(configWithDefaults) setOriginalConfig(configWithDefaults)
@@ -118,20 +105,7 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
}) })
setHasChanges(!!hasChanges) setHasChanges(!!hasChanges)
// Check if video format changes require restart // Video format settings are read-only, no validation needed
if (originalConfig && (key === 'video_format' || key === 'video_codec' || key === 'video_quality')) {
const currentFormat = originalConfig.video_format || 'mp4'
const newFormat = key === 'video_format' ? value as string : newConfig.video_format || 'mp4'
setNeedsRestart(requiresRestart(currentFormat, newFormat))
// Validate video format configuration
const validation = validateVideoFormatConfig({
video_format: newConfig.video_format || 'mp4',
video_codec: newConfig.video_codec || 'mp4v',
video_quality: newConfig.video_quality ?? 95,
})
setVideoFormatWarnings(validation.warnings)
}
} }
const saveConfig = async () => { const saveConfig = async () => {
@@ -180,26 +154,7 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
} }
} }
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 = () => { const resetChanges = () => {
if (originalConfig) { if (originalConfig) {
@@ -264,6 +219,63 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
{config && !loading && ( {config && !loading && (
<div className="space-y-6"> <div className="space-y-6">
{/* System Information (Read-Only) */}
<div>
<h4 className="text-md font-medium text-gray-900 mb-4">System Information</h4>
<div className="bg-gray-50 border border-gray-200 rounded-md p-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Camera Name</label>
<div className="text-sm text-gray-900 font-medium">{config.name}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Machine Topic</label>
<div className="text-sm text-gray-900 font-medium">{config.machine_topic}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Storage Path</label>
<div className="text-sm text-gray-900 font-medium">{config.storage_path}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Status</label>
<div className="text-sm text-gray-900 font-medium">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${config.enabled ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{config.enabled ? 'Enabled' : 'Disabled'}
</span>
</div>
</div>
</div>
</div>
</div>
{/* Auto-Recording Settings (Read-Only) */}
<div>
<h4 className="text-md font-medium text-gray-900 mb-4">Auto-Recording Settings</h4>
<div className="bg-gray-50 border border-gray-200 rounded-md p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Auto Recording</label>
<div className="text-sm text-gray-900 font-medium">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${config.auto_start_recording_enabled ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{config.auto_start_recording_enabled ? 'Enabled' : 'Disabled'}
</span>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Max Retries</label>
<div className="text-sm text-gray-900 font-medium">{config.auto_recording_max_retries}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Retry Delay</label>
<div className="text-sm text-gray-900 font-medium">{config.auto_recording_retry_delay_seconds}s</div>
</div>
</div>
<p className="text-xs text-gray-500 mt-3">Auto-recording settings are configured in the system configuration file</p>
</div>
</div>
{/* Basic Settings */} {/* Basic Settings */}
<div> <div>
<h4 className="text-md font-medium text-gray-900 mb-4">Basic Settings</h4> <h4 className="text-md font-medium text-gray-900 mb-4">Basic Settings</h4>
@@ -441,6 +453,70 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
</div> </div>
</div> </div>
{/* White Balance RGB Gains */}
<div>
<h4 className="text-md font-medium text-gray-900 mb-4">White Balance RGB Gains</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Red Gain: {config.wb_red_gain?.toFixed(2) || '1.00'}
</label>
<input
type="range"
min="0"
max="3.99"
step="0.01"
value={config.wb_red_gain || 1.0}
onChange={(e) => updateSetting('wb_red_gain', parseFloat(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>0.00</span>
<span>3.99</span>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Green Gain: {config.wb_green_gain?.toFixed(2) || '1.00'}
</label>
<input
type="range"
min="0"
max="3.99"
step="0.01"
value={config.wb_green_gain || 1.0}
onChange={(e) => updateSetting('wb_green_gain', parseFloat(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>0.00</span>
<span>3.99</span>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Blue Gain: {config.wb_blue_gain?.toFixed(2) || '1.00'}
</label>
<input
type="range"
min="0"
max="3.99"
step="0.01"
value={config.wb_blue_gain || 1.0}
onChange={(e) => updateSetting('wb_blue_gain', parseFloat(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>0.00</span>
<span>3.99</span>
</div>
</div>
</div>
<p className="text-xs text-gray-500 mt-2">Manual white balance gains (only effective when Auto White Balance is disabled)</p>
</div>
{/* Advanced Settings */} {/* Advanced Settings */}
<div> <div>
<h4 className="text-md font-medium text-gray-900 mb-4">Advanced Settings</h4> <h4 className="text-md font-medium text-gray-900 mb-4">Advanced Settings</h4>
@@ -536,162 +612,63 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
</div> </div>
</div> </div>
{/* Video Recording Settings */} {/* Video Recording Settings (Read-Only) */}
<div> <div>
<h4 className="text-md font-medium text-gray-900 mb-4">Video Recording Settings</h4> <h4 className="text-md font-medium text-gray-900 mb-4">Video Recording Settings</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="bg-gray-50 border border-gray-200 rounded-md p-4">
<div> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<label className="block text-sm font-medium text-gray-700 mb-2"> <div>
Video Format <label className="block text-sm font-medium text-gray-700 mb-1">
</label> Video Format
<select </label>
value={config.video_format || 'mp4'} <div className="text-sm text-gray-900 font-medium">
onChange={(e) => updateSetting('video_format', e.target.value)} {config.video_format?.toUpperCase() || 'MP4'}
className="w-full border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500" </div>
> <p className="text-xs text-gray-500">Current recording format</p>
<option value="mp4">MP4 (Recommended)</option>
<option value="avi">AVI (Legacy)</option>
</select>
<p className="text-xs text-gray-500 mt-1">MP4 provides better web compatibility and smaller file sizes</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Video Codec
</label>
<select
value={config.video_codec || 'mp4v'}
onChange={(e) => updateSetting('video_codec', e.target.value)}
className="w-full border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500"
>
{getAvailableCodecs(config.video_format || 'mp4').map(codec => (
<option key={codec} value={codec}>{codec.toUpperCase()}</option>
))}
</select>
<p className="text-xs text-gray-500 mt-1">Video compression codec</p>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Video Quality: {config.video_quality ?? 95}%
</label>
<input
type="range"
min="50"
max="100"
step="5"
value={config.video_quality ?? 95}
onChange={(e) => updateSetting('video_quality', parseInt(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>50% (Smaller files)</span>
<span>100% (Best quality)</span>
</div> </div>
<p className="text-xs text-gray-500 mt-1">Higher quality = larger file sizes</p>
</div>
</div>
{/* Video Format Warnings */} <div>
{videoFormatWarnings.length > 0 && ( <label className="block text-sm font-medium text-gray-700 mb-1">
<div className="mt-4 p-3 bg-yellow-50 border border-yellow-200 rounded-md"> Video Codec
</label>
<div className="text-sm text-gray-900 font-medium">
{config.video_codec?.toUpperCase() || 'MP4V'}
</div>
<p className="text-xs text-gray-500">Compression codec</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Video Quality
</label>
<div className="text-sm text-gray-900 font-medium">
{config.video_quality || 95}%
</div>
<p className="text-xs text-gray-500">Recording quality</p>
</div>
</div>
<div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
<div className="flex"> <div className="flex">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<svg className="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor"> <svg className="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" /> <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
</svg> </svg>
</div> </div>
<div className="ml-3"> <div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">Video Format Warnings</h3> <h3 className="text-sm font-medium text-blue-800">Video Format Information</h3>
<div className="mt-2 text-sm text-yellow-700"> <div className="mt-2 text-sm text-blue-700">
<ul className="list-disc list-inside space-y-1"> <p>Video recording settings are configured in the system configuration file and require a service restart to modify.</p>
{videoFormatWarnings.map((warning, index) => ( <p className="mt-1"><strong>Current benefits:</strong> MP4 format provides ~40% smaller file sizes and better web compatibility than AVI.</p>
<li key={index}>{warning}</li>
))}
</ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
)}
{/* Restart Warning */}
{needsRestart && (
<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-md">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">Restart Required</h3>
<p className="mt-2 text-sm text-red-700">
Video format changes require a camera service restart to take effect. Use "Apply & Restart" to apply these changes.
</p>
</div>
</div>
</div>
)}
</div>
{/* Auto-Recording Settings */}
<div>
<h4 className="text-md font-medium text-gray-900 mb-4">Auto-Recording Settings</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="flex items-center space-x-2">
<input
type="checkbox"
checked={config.auto_record_on_machine_start}
onChange={(e) => updateSetting('auto_record_on_machine_start', e.target.checked)}
className="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<span className="text-sm font-medium text-gray-700">Auto Record on Machine Start</span>
</label>
<p className="text-xs text-gray-500 mt-1">Start recording when MQTT machine state changes to ON</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Max Retries: {config.auto_recording_max_retries}
</label>
<input
type="range"
min="1"
max="10"
step="1"
value={config.auto_recording_max_retries}
onChange={(e) => updateSetting('auto_recording_max_retries', parseInt(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>1</span>
<span>10</span>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Retry Delay (seconds): {config.auto_recording_retry_delay_seconds}
</label>
<input
type="range"
min="1"
max="30"
step="1"
value={config.auto_recording_retry_delay_seconds}
onChange={(e) => updateSetting('auto_recording_retry_delay_seconds', parseInt(e.target.value))}
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>1s</span>
<span>30s</span>
</div>
</div>
</div> </div>
</div> </div>
{/* Information */} {/* Information */}
<div className="bg-blue-50 border border-blue-200 rounded-md p-4"> <div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<div className="flex"> <div className="flex">
@@ -704,10 +681,10 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
<h3 className="text-sm font-medium text-blue-800">Configuration Notes</h3> <h3 className="text-sm font-medium text-blue-800">Configuration Notes</h3>
<div className="mt-2 text-sm text-blue-700"> <div className="mt-2 text-sm text-blue-700">
<ul className="list-disc list-inside space-y-1"> <ul className="list-disc list-inside space-y-1">
<li>Real-time settings (exposure, gain, image quality) apply immediately</li> <li><strong>Real-time settings:</strong> Exposure, gain, image quality, white balance - apply immediately</li>
<li>Noise reduction settings require camera restart to take effect</li> <li><strong>System settings:</strong> Video format, noise reduction, auto-recording - configured in system files</li>
<li>Use "Apply & Restart" to apply settings that require restart</li> <li><strong>Performance:</strong> HDR mode may impact frame rate when enabled</li>
<li>HDR mode may impact performance when enabled</li> <li><strong>White balance:</strong> RGB gains only effective when auto white balance is disabled</li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -744,13 +721,6 @@ export function CameraConfigModal({ cameraName, isOpen, onClose, onSuccess, onEr
> >
{saving ? 'Saving...' : 'Save Changes'} {saving ? 'Saving...' : 'Save Changes'}
</button> </button>
<button
onClick={applyConfig}
disabled={applying}
className="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
{applying ? 'Applying...' : 'Apply & Restart'}
</button>
<button <button
onClick={onClose} onClick={onClose}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"

View File

@@ -144,61 +144,73 @@ export interface AutoRecordingStatusResponse {
// Camera Configuration Types // Camera Configuration Types
export interface CameraConfig { export interface CameraConfig {
// READ-ONLY SYSTEM FIELDS
name: string name: string
machine_topic: string machine_topic: string
storage_path: string storage_path: string
enabled: boolean enabled: boolean
auto_record_on_machine_start: boolean // READ-ONLY AUTO-RECORDING FIELDS
// NEW AUTO-RECORDING CONFIG FIELDS (optional for backward compatibility) auto_start_recording_enabled: boolean
auto_start_recording_enabled?: boolean auto_recording_max_retries: number
auto_recording_max_retries?: number auto_recording_retry_delay_seconds: number
auto_recording_retry_delay_seconds?: number // BASIC SETTINGS (real-time configurable)
exposure_ms: number exposure_ms: number
gain: number gain: number
target_fps: number target_fps: number
// NEW VIDEO RECORDING SETTINGS (MP4 format support) // VIDEO RECORDING SETTINGS (restart required)
video_format?: string // 'mp4' or 'avi' (optional for backward compatibility) video_format: string // 'mp4' or 'avi'
video_codec?: string // 'mp4v', 'XVID', 'MJPG' (optional for backward compatibility) video_codec: string // 'mp4v', 'XVID', 'MJPG'
video_quality?: number // 0-100 (higher = better quality) (optional for backward compatibility) video_quality: number // 0-100 (higher = better quality)
// IMAGE QUALITY SETTINGS (real-time configurable)
sharpness: number sharpness: number
contrast: number contrast: number
saturation: number saturation: number
gamma: number gamma: number
noise_filter_enabled: boolean // COLOR SETTINGS (real-time configurable)
denoise_3d_enabled: boolean
auto_white_balance: boolean auto_white_balance: boolean
color_temperature_preset: number color_temperature_preset: number
// WHITE BALANCE RGB GAINS (real-time configurable)
wb_red_gain: number
wb_green_gain: number
wb_blue_gain: number
// ADVANCED SETTINGS
anti_flicker_enabled: boolean anti_flicker_enabled: boolean
light_frequency: number light_frequency: number
// NOISE REDUCTION (restart required)
noise_filter_enabled: boolean
denoise_3d_enabled: boolean
// SYSTEM SETTINGS (restart required)
bit_depth: number bit_depth: number
// HDR SETTINGS (real-time configurable)
hdr_enabled: boolean hdr_enabled: boolean
hdr_gain_mode: number hdr_gain_mode: number
} }
export interface CameraConfigUpdate { export interface CameraConfigUpdate {
auto_record_on_machine_start?: boolean // BASIC SETTINGS (real-time configurable)
auto_start_recording_enabled?: boolean
auto_recording_max_retries?: number
auto_recording_retry_delay_seconds?: number
exposure_ms?: number exposure_ms?: number
gain?: number gain?: number
target_fps?: number target_fps?: number
// NEW VIDEO RECORDING SETTINGS (MP4 format support) // IMAGE QUALITY SETTINGS (real-time configurable)
video_format?: string // 'mp4' or 'avi'
video_codec?: string // 'mp4v', 'XVID', 'MJPG'
video_quality?: number // 0-100 (higher = better quality)
sharpness?: number sharpness?: number
contrast?: number contrast?: number
saturation?: number saturation?: number
gamma?: number gamma?: number
noise_filter_enabled?: boolean // COLOR SETTINGS (real-time configurable)
denoise_3d_enabled?: boolean
auto_white_balance?: boolean auto_white_balance?: boolean
color_temperature_preset?: number color_temperature_preset?: number
// WHITE BALANCE RGB GAINS (real-time configurable)
wb_red_gain?: number
wb_green_gain?: number
wb_blue_gain?: number
// ADVANCED SETTINGS (real-time configurable)
anti_flicker_enabled?: boolean anti_flicker_enabled?: boolean
light_frequency?: number light_frequency?: number
// HDR SETTINGS (real-time configurable)
hdr_enabled?: boolean hdr_enabled?: boolean
hdr_gain_mode?: number hdr_gain_mode?: number
// NOTE: Video format settings and noise reduction settings are not included
// as they are either read-only or require restart via apply-config endpoint
} }
export interface CameraConfigUpdateResponse { export interface CameraConfigUpdateResponse {