Add comprehensive test suite for USDA Vision Camera System

- Implemented main test script to verify system components and functionality.
- Added individual test scripts for camera exposure settings, API changes, camera recovery, maximum FPS, MQTT events, logging, and timezone functionality.
- Created service file for system management and automatic startup.
- Included detailed logging and error handling in test scripts for better diagnostics.
- Ensured compatibility with existing camera SDK and API endpoints.
This commit is contained in:
Alireza Vaezi
2025-07-28 17:33:49 -04:00
parent 9cb043ef5f
commit 7bc8138f24
72 changed files with 419 additions and 118 deletions

4
.gitignore vendored
View File

@@ -59,8 +59,8 @@ config_production.json
.DS_Store
Thumbs.db
# Camera library cache
python demo/__pycache__/
# Camera SDK cache
camera_sdk/__pycache__/
# Test outputs
test_output/

View File

@@ -1,5 +1,5 @@
{
"python.analysis.extraPaths": [
"./python demo"
"./camera_sdk"
]
}

View File

@@ -10,7 +10,7 @@ This system integrates MQTT machine monitoring with automated video recording fr
- **🔄 MQTT Integration**: Listens to multiple machine state topics
- **📹 Automatic Recording**: Starts/stops recording based on machine states
- **📷 GigE Camera Support**: Uses python demo library (mvsdk) for camera control
- **📷 GigE Camera Support**: Uses camera SDK library (mvsdk) for camera control
- **⚡ Multi-threading**: Concurrent MQTT listening, camera monitoring, and recording
- **🌐 REST API**: FastAPI server for dashboard integration
- **📡 WebSocket Support**: Real-time status updates
@@ -19,6 +19,38 @@ This system integrates MQTT machine monitoring with automated video recording fr
- **⚙️ Configuration Management**: JSON-based configuration system
- **🕐 Timezone Sync**: Proper time synchronization for Atlanta, Georgia
## 📁 Project Structure
```
USDA-Vision-Cameras/
├── README.md # Main documentation (this file)
├── main.py # System entry point
├── config.json # System configuration
├── requirements.txt # Python dependencies
├── pyproject.toml # UV package configuration
├── start_system.sh # Startup script
├── setup_timezone.sh # Time sync setup
├── usda_vision_system/ # Main application
│ ├── core/ # Core functionality
│ ├── mqtt/ # MQTT integration
│ ├── camera/ # Camera management
│ ├── storage/ # File management
│ ├── api/ # REST API server
│ └── main.py # Application coordinator
├── camera_sdk/ # GigE camera SDK library
├── demos/ # Demo and example code
│ ├── cv_grab*.py # Camera SDK usage examples
│ └── mqtt_*.py # MQTT demo scripts
├── tests/ # Test files
│ ├── test_*.py # System tests
│ └── legacy_tests/ # Archived development files
├── notebooks/ # Jupyter notebooks
├── docs/ # Documentation files
└── storage/ # Recording storage
├── camera1/ # Camera 1 recordings
└── camera2/ # Camera 2 recordings
```
## 🏗️ Architecture
```
@@ -46,7 +78,7 @@ This system integrates MQTT machine monitoring with automated video recording fr
## 📋 Prerequisites
### Hardware Requirements
- GigE cameras compatible with python demo library
- GigE cameras compatible with camera SDK library
- Network connection to MQTT broker
- Sufficient storage space for video recordings
@@ -90,7 +122,7 @@ pip install -r requirements.txt
```
### 3. Setup GigE Camera Library
Ensure the `python demo` directory contains the mvsdk library for your GigE cameras. This should include:
Ensure the `camera_sdk` directory contains the mvsdk library for your GigE cameras. This should include:
- `mvsdk.py` - Python SDK wrapper
- Camera driver libraries
- Any camera-specific configuration files
@@ -519,13 +551,13 @@ python check_time.py
# Check camera connections
ping 192.168.1.165 # Replace with your camera IP
# Verify python demo library
ls -la "python demo/"
# Verify camera SDK library
ls -la "camera_sdk/"
# Should contain mvsdk.py and related files
# Test camera discovery manually
python -c "
import sys; sys.path.append('./python demo')
import sys; sys.path.append('./camera_sdk')
import mvsdk
devices = mvsdk.CameraEnumerateDevice()
print(f'Found {len(devices)} cameras')
@@ -579,7 +611,7 @@ df -h storage/
# Test camera initialization
python -c "
import sys; sys.path.append('./python demo')
import sys; sys.path.append('./camera_sdk')
import mvsdk
devices = mvsdk.CameraEnumerateDevice()
if devices:

66
camera_sdk/README.md Normal file
View File

@@ -0,0 +1,66 @@
# Camera SDK Library
This directory contains the core GigE camera SDK library required for the USDA Vision Camera System.
## Contents
### Core SDK Library
- **`mvsdk.py`** - Python wrapper for the GigE camera SDK
- Provides Python bindings for camera control functions
- Handles camera initialization, configuration, and image capture
- **Critical dependency** - Required for all camera operations
## Important Notes
⚠️ **This is NOT demo code** - This directory contains the core SDK library that the entire system depends on for camera functionality.
### SDK Library Details
- The `mvsdk.py` file is a Python wrapper around the native camera SDK
- It provides ctypes bindings to the underlying C/C++ camera library
- Contains all camera control functions, constants, and data structures
- Used by all camera modules in `usda_vision_system/camera/`
### Dependencies
- Requires the native camera SDK library (`libMVSDK.so` on Linux)
- The native library should be installed system-wide or available in the library path
## Usage
This SDK is automatically imported by the camera modules:
```python
# Imported by camera modules
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
```
## Demo Code
For camera usage examples and demo code, see the `../demos/` directory:
- `cv_grab.py` - Basic camera capture example
- `cv_grab2.py` - Multi-camera capture example
- `cv_grab_callback.py` - Callback-based capture example
- `grab.py` - Simple image capture example
## Troubleshooting
If you encounter camera SDK issues:
1. **Check SDK Installation**:
```bash
ls -la camera_sdk/mvsdk.py
```
2. **Test SDK Import**:
```bash
python -c "import sys; sys.path.append('./camera_sdk'); import mvsdk; print('SDK imported successfully')"
```
3. **Check Native Library**:
```bash
# On Linux
ldconfig -p | grep MVSDK
```
For more troubleshooting, see the main [README.md](../README.md#troubleshooting).

29
container_init.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Container initialization script for USDA Vision Camera System
# This script sets up and starts the systemd service in a container environment
echo "🐳 Container Init - USDA Vision Camera System"
echo "============================================="
# Start systemd if not already running (for containers)
if ! pgrep systemd > /dev/null; then
echo "🔧 Starting systemd..."
exec /sbin/init &
sleep 5
fi
# Setup the service if not already installed
if [ ! -f "/etc/systemd/system/usda-vision-camera.service" ]; then
echo "📦 Setting up USDA Vision Camera service..."
cd /home/alireza/USDA-vision-cameras
sudo ./setup_service.sh
fi
# Start the service
echo "🚀 Starting USDA Vision Camera service..."
sudo systemctl start usda-vision-camera
# Follow the logs
echo "📋 Following service logs (Ctrl+C to exit)..."
sudo journalctl -u usda-vision-camera -f

View File

@@ -9,7 +9,7 @@ The USDA Vision Camera System has been successfully implemented, tested, and doc
### ✅ Core Functionality
- **MQTT Integration**: Dual topic listening for machine states
- **Automatic Recording**: Camera recording triggered by machine on/off states
- **GigE Camera Support**: Full integration with python demo library
- **GigE Camera Support**: Full integration with camera SDK library
- **Multi-threading**: Concurrent MQTT + camera monitoring + recording
- **File Management**: Timestamp-based naming in organized directories
@@ -50,11 +50,16 @@ USDA-Vision-Cameras/
│ ├── storage/ # File management
│ ├── api/ # REST API server
│ └── main.py # Application coordinator
├── python demo/ # GigE camera library
├── camera_sdk/ # GigE camera SDK library
├── demos/ # Demo and example code
│ ├── cv_grab*.py # Camera SDK usage examples
│ └── mqtt_*.py # MQTT demo scripts
├── storage/ # Recording storage
│ ├── camera1/ # Camera 1 recordings
│ └── camera2/ # Camera 2 recordings
── old tests/ # Archived development files
── tests/ # Test files and legacy tests
├── notebooks/ # Jupyter notebooks
└── docs/ # Documentation files
```
## 🚀 How to Deploy

49
docs/README.md Normal file
View File

@@ -0,0 +1,49 @@
# USDA Vision Camera System - Documentation
This directory contains detailed documentation for the USDA Vision Camera System.
## Documentation Files
### 📋 [PROJECT_COMPLETE.md](PROJECT_COMPLETE.md)
Complete project overview and final status documentation. Contains:
- Project completion status
- Final system architecture
- Deployment instructions
- Production readiness checklist
### 🔧 [API_CHANGES_SUMMARY.md](API_CHANGES_SUMMARY.md)
Summary of API changes and enhancements made to the system.
### 📷 [CAMERA_RECOVERY_GUIDE.md](CAMERA_RECOVERY_GUIDE.md)
Guide for camera recovery procedures and troubleshooting camera-related issues.
### 📡 [MQTT_LOGGING_GUIDE.md](MQTT_LOGGING_GUIDE.md)
Comprehensive guide for MQTT logging configuration and troubleshooting.
## Main Documentation
The main system documentation is located in the root directory:
- **[../README.md](../README.md)** - Primary system documentation with installation, configuration, and usage instructions
## Additional Resources
### Demo Code
- **[../demos/](../demos/)** - Demo scripts and camera SDK examples
### Test Files
- **[../tests/](../tests/)** - Test scripts and legacy test files
### Jupyter Notebooks
- **[../notebooks/](../notebooks/)** - Interactive notebooks for system exploration and testing
## Quick Links
- [System Installation](../README.md#installation)
- [Configuration Guide](../README.md#configuration)
- [API Documentation](../README.md#api-reference)
- [Troubleshooting](../README.md#troubleshooting)
- [Camera SDK Examples](../demos/camera_sdk_examples/)
## Support
For technical support and questions, refer to the main [README.md](../README.md) troubleshooting section or check the system logs.

61
setup_service.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/bin/bash
# USDA Vision Camera System Service Setup Script
echo "USDA Vision Camera System - Service Setup"
echo "========================================"
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "❌ This script must be run as root (use sudo)"
exit 1
fi
# Get the current directory (where the script is located)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVICE_FILE="$SCRIPT_DIR/usda-vision-camera.service"
echo "📁 Working directory: $SCRIPT_DIR"
# Check if service file exists
if [ ! -f "$SERVICE_FILE" ]; then
echo "❌ Service file not found: $SERVICE_FILE"
exit 1
fi
# Make start_system.sh executable
echo "🔧 Making start_system.sh executable..."
chmod +x "$SCRIPT_DIR/start_system.sh"
# Update the service file with the correct path
echo "📝 Updating service file with correct paths..."
sed -i "s|WorkingDirectory=.*|WorkingDirectory=$SCRIPT_DIR|g" "$SERVICE_FILE"
sed -i "s|ExecStart=.*|ExecStart=/bin/bash $SCRIPT_DIR/start_system.sh|g" "$SERVICE_FILE"
# Copy service file to systemd directory
echo "📋 Installing service file..."
cp "$SERVICE_FILE" /etc/systemd/system/
# Reload systemd daemon
echo "🔄 Reloading systemd daemon..."
systemctl daemon-reload
# Enable the service
echo "✅ Enabling USDA Vision Camera service..."
systemctl enable usda-vision-camera.service
# Check service status
echo "📊 Service status:"
systemctl status usda-vision-camera.service --no-pager
echo ""
echo "🎉 Service setup complete!"
echo ""
echo "Available commands:"
echo " sudo systemctl start usda-vision-camera # Start the service"
echo " sudo systemctl stop usda-vision-camera # Stop the service"
echo " sudo systemctl restart usda-vision-camera # Restart the service"
echo " sudo systemctl status usda-vision-camera # Check service status"
echo " sudo journalctl -u usda-vision-camera -f # View live logs"
echo ""
echo "The service will automatically start when the container/system boots."

View File

@@ -38,10 +38,18 @@ python test_system.py
if [ $? -ne 0 ]; then
echo "❌ System tests failed. Please check the configuration."
read -p "Do you want to continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
# When running as a service, don't prompt for user input
if [ -t 0 ]; then
# Interactive mode - prompt user
read -p "Do you want to continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
else
# Non-interactive mode (service) - continue with warning
echo "⚠️ Running in non-interactive mode. Continuing despite test failures..."
sleep 2
fi
fi

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:32:15
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:32:15.057651 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:32:33
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:32:33.490923 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:32:34
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:32:34.649940 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:32:39
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:32:39.753448 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:32:45
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:32:45.492905 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:33:40
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:33:40.702630 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:34:18
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:34:18.442386 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:34:28
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:34:28.207051 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:34:53
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:34:53.315912 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:35:00
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:35:00.929268 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:35:32
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:35:32.169682 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -0,0 +1,4 @@
Log file created at: 2025/07/28 15:35:34
Running on machine: vision
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
E0728 15:35:34.519351 191852 MVCAMAPI.cpp:369] CameraInit Failed, err:32774,Version:2.1.0.49,FriendlyName:Blower-Yield-Cam,SN:054012620023

View File

@@ -64,7 +64,7 @@ def test_camera_discovery():
"""Test camera discovery"""
print("\nTesting camera discovery...")
try:
sys.path.append("./python demo")
sys.path.append("./camera_sdk")
import mvsdk
devices = mvsdk.CameraEnumerateDevice()
@@ -82,7 +82,7 @@ def test_camera_discovery():
return True
except Exception as e:
print(f"❌ Camera discovery failed: {e}")
print(" Make sure GigE cameras are connected and python demo library is available")
print(" Make sure GigE cameras are connected and camera SDK library is available")
return False

View File

@@ -0,0 +1,26 @@
[Unit]
Description=USDA Vision Camera System
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/home/alireza/USDA-vision-cameras
ExecStart=/bin/bash /home/alireza/USDA-vision-cameras/start_system.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Environment variables
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=HOME=/root
# Security settings (adjust as needed)
NoNewPrivileges=false
PrivateTmp=false
[Install]
WantedBy=multi-user.target

View File

@@ -2,7 +2,7 @@
Camera module for the USDA Vision Camera System.
This module handles GigE camera discovery, management, monitoring, and recording
using the python demo library (mvsdk).
using the camera SDK library (mvsdk).
"""
from .manager import CameraManager

View File

@@ -12,8 +12,8 @@ import logging
from typing import Dict, List, Optional, Tuple, Any
from datetime import datetime
# Add python demo to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "python demo"))
# Add camera SDK to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
from ..core.config import Config, CameraConfig

View File

@@ -12,8 +12,8 @@ import logging
import contextlib
from typing import Dict, List, Optional, Any
# Add python demo to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "python demo"))
# Add camera SDK to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
from ..core.config import Config

View File

@@ -1,7 +1,7 @@
"""
Camera Recorder for the USDA Vision Camera System.
This module handles video recording from GigE cameras using the python demo library (mvsdk).
This module handles video recording from GigE cameras using the camera SDK library (mvsdk).
"""
import sys
@@ -16,8 +16,8 @@ from typing import Optional, Dict, Any
from datetime import datetime
from pathlib import Path
# Add python demo to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "python demo"))
# Add camera SDK to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
from ..core.config import CameraConfig

View File

@@ -8,8 +8,8 @@ import sys
import os
import logging
# Add python demo to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "python demo"))
# Add camera SDK to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
logger = logging.getLogger(__name__)
@@ -21,62 +21,62 @@ _sdk_initialized = False
def initialize_sdk_with_suppression():
"""Initialize the camera SDK with error suppression"""
global _sdk_initialized
if _sdk_initialized:
return True
try:
# Initialize SDK with English language
result = mvsdk.CameraSdkInit(1)
if result == 0:
logger.info("Camera SDK initialized successfully")
# Try to set system options to suppress logging
try:
# These are common options that might control logging
# We'll try them and ignore failures since they might not be supported
# Try to disable debug output
try:
mvsdk.CameraSetSysOption("DebugLevel", "0")
except:
pass
# Try to disable console output
try:
mvsdk.CameraSetSysOption("ConsoleOutput", "0")
except:
pass
# Try to disable error logging
try:
mvsdk.CameraSetSysOption("ErrorLog", "0")
except:
pass
# Try to set log level to none
try:
mvsdk.CameraSetSysOption("LogLevel", "0")
except:
pass
# Try to disable verbose mode
try:
mvsdk.CameraSetSysOption("Verbose", "0")
except:
pass
logger.debug("Attempted to configure SDK logging options")
except Exception as e:
logger.debug(f"Could not configure SDK logging options: {e}")
_sdk_initialized = True
return True
else:
logger.error(f"SDK initialization failed with code: {result}")
return False
except Exception as e:
logger.error(f"SDK initialization failed: {e}")
return False

View File

@@ -16,23 +16,22 @@ from pathlib import Path
@dataclass
class MQTTConfig:
"""MQTT broker configuration"""
broker_host: str = "192.168.1.110"
broker_port: int = 1883
username: Optional[str] = None
password: Optional[str] = None
topics: Dict[str, str] = None
topics: Optional[Dict[str, str]] = None
def __post_init__(self):
if self.topics is None:
self.topics = {
"vibratory_conveyor": "vision/vibratory_conveyor/state",
"blower_separator": "vision/blower_separator/state"
}
self.topics = {"vibratory_conveyor": "vision/vibratory_conveyor/state", "blower_separator": "vision/blower_separator/state"}
@dataclass
class CameraConfig:
"""Individual camera configuration"""
name: str
machine_topic: str # Which MQTT topic triggers this camera
storage_path: str
@@ -43,42 +42,44 @@ class CameraConfig:
# Image Quality Settings
sharpness: int = 100 # 0-200, default 100 (no sharpening)
contrast: int = 100 # 0-200, default 100 (normal contrast)
saturation: int = 100 # 0-200, default 100 (normal saturation, color cameras only)
gamma: int = 100 # 0-300, default 100 (normal gamma)
contrast: int = 100 # 0-200, default 100 (normal contrast)
saturation: int = 100 # 0-200, default 100 (normal saturation, color cameras only)
gamma: int = 100 # 0-300, default 100 (normal gamma)
# Noise Reduction
noise_filter_enabled: bool = True # Enable basic noise filtering
denoise_3d_enabled: bool = False # Enable advanced 3D denoising (may reduce FPS)
denoise_3d_enabled: bool = False # Enable advanced 3D denoising (may reduce FPS)
# Color Settings (for color cameras)
auto_white_balance: bool = True # Enable automatic white balance
auto_white_balance: bool = True # Enable automatic white balance
color_temperature_preset: int = 0 # 0=auto, 1=daylight, 2=fluorescent, etc.
# Advanced Settings
anti_flicker_enabled: bool = True # Reduce artificial lighting flicker
light_frequency: int = 1 # 0=50Hz, 1=60Hz (match local power frequency)
light_frequency: int = 1 # 0=50Hz, 1=60Hz (match local power frequency)
# Bit Depth & Format
bit_depth: int = 8 # 8, 10, 12, or 16 bits per channel
bit_depth: int = 8 # 8, 10, 12, or 16 bits per channel
# HDR Settings
hdr_enabled: bool = False # Enable High Dynamic Range
hdr_gain_mode: int = 0 # HDR processing mode
hdr_enabled: bool = False # Enable High Dynamic Range
hdr_gain_mode: int = 0 # HDR processing mode
@dataclass
class StorageConfig:
"""Storage configuration"""
base_path: str = "/storage"
max_file_size_mb: int = 1000 # Max size per video file
max_recording_duration_minutes: int = 60 # Max recording duration
cleanup_older_than_days: int = 30 # Auto cleanup old files
@dataclass
class SystemConfig:
"""System-wide configuration"""
camera_check_interval_seconds: int = 2
log_level: str = "INFO"
log_file: str = "usda_vision_system.log"
@@ -90,60 +91,57 @@ class SystemConfig:
class Config:
"""Main configuration manager"""
def __init__(self, config_file: Optional[str] = None):
self.config_file = config_file or "config.json"
self.logger = logging.getLogger(__name__)
# Default configurations
self.mqtt = MQTTConfig()
self.storage = StorageConfig()
self.system = SystemConfig()
# Camera configurations - will be populated from config file or defaults
self.cameras: List[CameraConfig] = []
# Load configuration
self.load_config()
# Ensure storage directories exist
self._ensure_storage_directories()
def load_config(self) -> None:
"""Load configuration from file"""
config_path = Path(self.config_file)
if config_path.exists():
try:
with open(config_path, 'r') as f:
with open(config_path, "r") as f:
config_data = json.load(f)
# Load MQTT config
if 'mqtt' in config_data:
mqtt_data = config_data['mqtt']
if "mqtt" in config_data:
mqtt_data = config_data["mqtt"]
self.mqtt = MQTTConfig(**mqtt_data)
# Load storage config
if 'storage' in config_data:
storage_data = config_data['storage']
if "storage" in config_data:
storage_data = config_data["storage"]
self.storage = StorageConfig(**storage_data)
# Load system config
if 'system' in config_data:
system_data = config_data['system']
if "system" in config_data:
system_data = config_data["system"]
self.system = SystemConfig(**system_data)
# Load camera configs
if 'cameras' in config_data:
self.cameras = [
CameraConfig(**cam_data)
for cam_data in config_data['cameras']
]
if "cameras" in config_data:
self.cameras = [CameraConfig(**cam_data) for cam_data in config_data["cameras"]]
else:
self._create_default_camera_configs()
self.logger.info(f"Configuration loaded from {config_path}")
except Exception as e:
self.logger.error(f"Error loading config from {config_path}: {e}")
self._create_default_camera_configs()
@@ -151,66 +149,50 @@ class Config:
self.logger.info(f"Config file {config_path} not found, using defaults")
self._create_default_camera_configs()
self.save_config() # Save default config
def _create_default_camera_configs(self) -> None:
"""Create default camera configurations"""
self.cameras = [
CameraConfig(
name="camera1",
machine_topic="vibratory_conveyor",
storage_path=os.path.join(self.storage.base_path, "camera1")
),
CameraConfig(
name="camera2",
machine_topic="blower_separator",
storage_path=os.path.join(self.storage.base_path, "camera2")
)
]
self.cameras = [CameraConfig(name="camera1", machine_topic="vibratory_conveyor", storage_path=os.path.join(self.storage.base_path, "camera1")), CameraConfig(name="camera2", machine_topic="blower_separator", storage_path=os.path.join(self.storage.base_path, "camera2"))]
def save_config(self) -> None:
"""Save current configuration to file"""
config_data = {
'mqtt': asdict(self.mqtt),
'storage': asdict(self.storage),
'system': asdict(self.system),
'cameras': [asdict(cam) for cam in self.cameras]
}
config_data = {"mqtt": asdict(self.mqtt), "storage": asdict(self.storage), "system": asdict(self.system), "cameras": [asdict(cam) for cam in self.cameras]}
try:
with open(self.config_file, 'w') as f:
with open(self.config_file, "w") as f:
json.dump(config_data, f, indent=2)
self.logger.info(f"Configuration saved to {self.config_file}")
except Exception as e:
self.logger.error(f"Error saving config to {self.config_file}: {e}")
def _ensure_storage_directories(self) -> None:
"""Ensure all storage directories exist"""
try:
# Create base storage directory
Path(self.storage.base_path).mkdir(parents=True, exist_ok=True)
# Create camera-specific directories
for camera in self.cameras:
Path(camera.storage_path).mkdir(parents=True, exist_ok=True)
self.logger.info("Storage directories verified/created")
except Exception as e:
self.logger.error(f"Error creating storage directories: {e}")
def get_camera_by_topic(self, topic: str) -> Optional[CameraConfig]:
"""Get camera configuration by MQTT topic"""
for camera in self.cameras:
if camera.machine_topic == topic:
return camera
return None
def get_camera_by_name(self, name: str) -> Optional[CameraConfig]:
"""Get camera configuration by name"""
for camera in self.cameras:
if camera.name == name:
return camera
return None
def update_camera_config(self, name: str, **kwargs) -> bool:
"""Update camera configuration"""
camera = self.get_camera_by_name(name)
@@ -221,12 +203,7 @@ class Config:
self.save_config()
return True
return False
def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to dictionary"""
return {
'mqtt': asdict(self.mqtt),
'storage': asdict(self.storage),
'system': asdict(self.system),
'cameras': [asdict(cam) for cam in self.cameras]
}
return {"mqtt": asdict(self.mqtt), "storage": asdict(self.storage), "system": asdict(self.system), "cameras": [asdict(cam) for cam in self.cameras]}