Chore: rename api->camera-management-api and web->management-dashboard-web-app; update compose, ignore, README references

This commit is contained in:
Alireza Vaezi
2025-08-07 22:07:25 -04:00
parent 28dab3a366
commit fc2da16728
281 changed files with 19 additions and 19 deletions

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""
Test script to verify the API changes for camera settings and filename handling.
"""
import requests
import json
import time
from datetime import datetime
# API base URL
BASE_URL = "http://localhost:8000"
def test_api_endpoint(endpoint, method="GET", data=None):
"""Test an API endpoint and return the response"""
url = f"{BASE_URL}{endpoint}"
try:
if method == "GET":
response = requests.get(url)
elif method == "POST":
response = requests.post(url, json=data, headers={"Content-Type": "application/json"})
print(f"\n{method} {endpoint}")
print(f"Status: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"Response: {json.dumps(result, indent=2)}")
return result
else:
print(f"Error: {response.text}")
return None
except requests.exceptions.ConnectionError:
print(f"Error: Could not connect to {url}")
print("Make sure the API server is running with: python main.py")
return None
except Exception as e:
print(f"Error: {e}")
return None
def test_camera_recording_with_settings():
"""Test camera recording with new settings parameters"""
print("=" * 60)
print("Testing Camera Recording API with New Settings")
print("=" * 60)
# Test 1: Basic recording without settings
print("\n1. Testing basic recording (no settings)")
basic_request = {
"camera_name": "camera1",
"filename": "test_basic.avi"
}
result = test_api_endpoint("/cameras/camera1/start-recording", "POST", basic_request)
if result and result.get("success"):
print("✅ Basic recording started successfully")
print(f" Filename: {result.get('filename')}")
# Stop recording
time.sleep(2)
test_api_endpoint("/cameras/camera1/stop-recording", "POST")
else:
print("❌ Basic recording failed")
# Test 2: Recording with camera settings
print("\n2. Testing recording with camera settings")
settings_request = {
"camera_name": "camera1",
"filename": "test_with_settings.avi",
"exposure_ms": 2.0,
"gain": 4.0,
"fps": 5.0
}
result = test_api_endpoint("/cameras/camera1/start-recording", "POST", settings_request)
if result and result.get("success"):
print("✅ Recording with settings started successfully")
print(f" Filename: {result.get('filename')}")
# Stop recording
time.sleep(2)
test_api_endpoint("/cameras/camera1/stop-recording", "POST")
else:
print("❌ Recording with settings failed")
# Test 3: Recording with only settings (no filename)
print("\n3. Testing recording with settings only (no filename)")
settings_only_request = {
"camera_name": "camera1",
"exposure_ms": 1.5,
"gain": 3.0,
"fps": 7.0
}
result = test_api_endpoint("/cameras/camera1/start-recording", "POST", settings_only_request)
if result and result.get("success"):
print("✅ Recording with settings only started successfully")
print(f" Filename: {result.get('filename')}")
# Stop recording
time.sleep(2)
test_api_endpoint("/cameras/camera1/stop-recording", "POST")
else:
print("❌ Recording with settings only failed")
# Test 4: Test filename datetime prefix
print("\n4. Testing filename datetime prefix")
timestamp_before = datetime.now().strftime("%Y%m%d_%H%M")
filename_test_request = {
"camera_name": "camera1",
"filename": "my_custom_name.avi"
}
result = test_api_endpoint("/cameras/camera1/start-recording", "POST", filename_test_request)
if result and result.get("success"):
returned_filename = result.get('filename', '')
print(f" Original filename: my_custom_name.avi")
print(f" Returned filename: {returned_filename}")
# Check if datetime prefix was added
if timestamp_before in returned_filename and "my_custom_name.avi" in returned_filename:
print("✅ Datetime prefix correctly added to filename")
else:
print("❌ Datetime prefix not properly added")
# Stop recording
time.sleep(2)
test_api_endpoint("/cameras/camera1/stop-recording", "POST")
else:
print("❌ Filename test failed")
def test_system_status():
"""Test basic system status to ensure API is working"""
print("\n" + "=" * 60)
print("Testing System Status")
print("=" * 60)
# Test system status
result = test_api_endpoint("/system/status")
if result:
print("✅ System status API working")
print(f" System started: {result.get('system_started')}")
print(f" MQTT connected: {result.get('mqtt_connected')}")
else:
print("❌ System status API failed")
# Test camera status
result = test_api_endpoint("/cameras")
if result:
print("✅ Camera status API working")
for camera_name, camera_info in result.items():
print(f" {camera_name}: {camera_info.get('status')}")
else:
print("❌ Camera status API failed")
if __name__ == "__main__":
print("USDA Vision Camera System - API Changes Test")
print("This script tests the new camera settings parameters and filename handling")
print("\nMake sure the system is running with: python main.py")
# Test system status first
test_system_status()
# Test camera recording with new features
test_camera_recording_with_settings()
print("\n" + "=" * 60)
print("Test completed!")
print("=" * 60)

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""
Test script for camera recovery API endpoints.
This script tests the new camera recovery functionality without requiring actual cameras.
"""
import requests
import json
import time
from typing import Dict, Any
# API base URL
BASE_URL = "http://localhost:8000"
def test_endpoint(method: str, endpoint: str, data: Dict[Any, Any] = None) -> Dict[Any, Any]:
"""Test an API endpoint and return the response"""
url = f"{BASE_URL}{endpoint}"
try:
if method.upper() == "GET":
response = requests.get(url, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, json=data or {}, timeout=10)
else:
raise ValueError(f"Unsupported method: {method}")
print(f"\n{method} {endpoint}")
print(f"Status: {response.status_code}")
if response.headers.get('content-type', '').startswith('application/json'):
result = response.json()
print(f"Response: {json.dumps(result, indent=2)}")
return result
else:
print(f"Response: {response.text}")
return {"text": response.text}
except requests.exceptions.ConnectionError:
print(f"❌ Connection failed - API server not running at {BASE_URL}")
return {"error": "connection_failed"}
except requests.exceptions.Timeout:
print(f"❌ Request timeout")
return {"error": "timeout"}
except Exception as e:
print(f"❌ Error: {e}")
return {"error": str(e)}
def main():
"""Test camera recovery API endpoints"""
print("🔧 Testing Camera Recovery API Endpoints")
print("=" * 50)
# Test basic endpoints first
print("\n📋 BASIC API TESTS")
test_endpoint("GET", "/health")
test_endpoint("GET", "/cameras")
# Test camera recovery endpoints
print("\n🔧 CAMERA RECOVERY TESTS")
camera_names = ["camera1", "camera2"]
for camera_name in camera_names:
print(f"\n--- Testing {camera_name} ---")
# Test connection
test_endpoint("POST", f"/cameras/{camera_name}/test-connection")
# Test reconnect
test_endpoint("POST", f"/cameras/{camera_name}/reconnect")
# Test restart grab
test_endpoint("POST", f"/cameras/{camera_name}/restart-grab")
# Test reset timestamp
test_endpoint("POST", f"/cameras/{camera_name}/reset-timestamp")
# Test full reset
test_endpoint("POST", f"/cameras/{camera_name}/full-reset")
# Test reinitialize
test_endpoint("POST", f"/cameras/{camera_name}/reinitialize")
time.sleep(0.5) # Small delay between tests
print("\n✅ Camera recovery API tests completed!")
print("\nNote: Some operations may fail if cameras are not connected,")
print("but the API endpoints should respond with proper error messages.")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,168 @@
#!/usr/bin/env python3
"""
Test script for MQTT events API endpoint
This script tests the new MQTT events history functionality by:
1. Starting the system components
2. Simulating MQTT messages
3. Testing the API endpoint to retrieve events
"""
import asyncio
import time
import requests
import json
from datetime import datetime
# Test configuration
API_BASE_URL = "http://localhost:8000"
MQTT_EVENTS_ENDPOINT = f"{API_BASE_URL}/mqtt/events"
def test_api_endpoint():
"""Test the MQTT events API endpoint"""
print("🧪 Testing MQTT Events API Endpoint")
print("=" * 50)
try:
# Test basic endpoint
print("📡 Testing GET /mqtt/events (default limit=5)")
response = requests.get(MQTT_EVENTS_ENDPOINT)
if response.status_code == 200:
data = response.json()
print(f"✅ API Response successful")
print(f"📊 Total events: {data.get('total_events', 0)}")
print(f"📋 Events returned: {len(data.get('events', []))}")
if data.get('events'):
print(f"🕐 Last updated: {data.get('last_updated')}")
print("\n📝 Recent events:")
for i, event in enumerate(data['events'], 1):
timestamp = datetime.fromisoformat(event['timestamp']).strftime('%H:%M:%S')
print(f" {i}. [{timestamp}] {event['machine_name']}: {event['payload']} -> {event['normalized_state']}")
else:
print("📭 No events found")
else:
print(f"❌ API Error: {response.status_code}")
print(f" Response: {response.text}")
except requests.exceptions.ConnectionError:
print("❌ Connection Error: API server not running")
print(" Start the system first: python -m usda_vision_system.main")
except Exception as e:
print(f"❌ Error: {e}")
print()
# Test with custom limit
try:
print("📡 Testing GET /mqtt/events?limit=10")
response = requests.get(f"{MQTT_EVENTS_ENDPOINT}?limit=10")
if response.status_code == 200:
data = response.json()
print(f"✅ API Response successful")
print(f"📋 Events returned: {len(data.get('events', []))}")
else:
print(f"❌ API Error: {response.status_code}")
except Exception as e:
print(f"❌ Error: {e}")
def test_system_status():
"""Test system status to verify API is running"""
print("🔍 Checking System Status")
print("=" * 50)
try:
response = requests.get(f"{API_BASE_URL}/system/status")
if response.status_code == 200:
data = response.json()
print(f"✅ System Status: {'Running' if data.get('system_started') else 'Not Started'}")
print(f"🔗 MQTT Connected: {'Yes' if data.get('mqtt_connected') else 'No'}")
print(f"📡 Last MQTT Message: {data.get('last_mqtt_message', 'None')}")
print(f"⏱️ Uptime: {data.get('uptime_seconds', 0):.1f} seconds")
return True
else:
print(f"❌ System Status Error: {response.status_code}")
return False
except requests.exceptions.ConnectionError:
print("❌ Connection Error: API server not running")
print(" Start the system first: python -m usda_vision_system.main")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def test_mqtt_status():
"""Test MQTT status"""
print("📡 Checking MQTT Status")
print("=" * 50)
try:
response = requests.get(f"{API_BASE_URL}/mqtt/status")
if response.status_code == 200:
data = response.json()
print(f"🔗 MQTT Connected: {'Yes' if data.get('connected') else 'No'}")
print(f"🏠 Broker: {data.get('broker_host')}:{data.get('broker_port')}")
print(f"📋 Subscribed Topics: {len(data.get('subscribed_topics', []))}")
print(f"📊 Message Count: {data.get('message_count', 0)}")
print(f"❌ Error Count: {data.get('error_count', 0)}")
if data.get('subscribed_topics'):
print("📍 Topics:")
for topic in data['subscribed_topics']:
print(f" - {topic}")
return True
else:
print(f"❌ MQTT Status Error: {response.status_code}")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def main():
"""Main test function"""
print("🧪 MQTT Events API Test")
print("=" * 60)
print(f"🎯 API Base URL: {API_BASE_URL}")
print(f"📡 Events Endpoint: {MQTT_EVENTS_ENDPOINT}")
print()
# Test system status first
if not test_system_status():
print("\n❌ System not running. Please start the system first:")
print(" python -m usda_vision_system.main")
return
print()
# Test MQTT status
if not test_mqtt_status():
print("\n❌ MQTT not available")
return
print()
# Test the events API
test_api_endpoint()
print("\n" + "=" * 60)
print("🎯 Test Instructions:")
print("1. Make sure the system is running")
print("2. Turn machines on/off to generate MQTT events")
print("3. Run this test again to see the events")
print("4. Check the admin dashboard to see events displayed")
print()
print("📋 API Usage:")
print(f" GET {MQTT_EVENTS_ENDPOINT}")
print(f" GET {MQTT_EVENTS_ENDPOINT}?limit=10")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""
Test script to verify the frame conversion fix works correctly.
"""
import sys
import os
import numpy as np
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Add camera SDK to path
sys.path.append(os.path.join(os.path.dirname(__file__), "camera_sdk"))
try:
import mvsdk
print("✅ mvsdk imported successfully")
except ImportError as e:
print(f"❌ Failed to import mvsdk: {e}")
sys.exit(1)
def test_frame_conversion():
"""Test the frame conversion logic"""
print("🧪 Testing frame conversion logic...")
# Simulate frame data
width, height = 640, 480
frame_size = width * height * 3 # RGB
# Create mock frame data
mock_frame_data = np.random.randint(0, 255, frame_size, dtype=np.uint8)
# Create a mock frame buffer (simulate memory address)
frame_buffer = mock_frame_data.ctypes.data
# Create mock FrameHead
class MockFrameHead:
def __init__(self):
self.iWidth = width
self.iHeight = height
self.uBytes = frame_size
frame_head = MockFrameHead()
try:
# Test the conversion logic (similar to what's in streamer.py)
frame_data_buffer = (mvsdk.c_ubyte * frame_head.uBytes).from_address(frame_buffer)
frame_data = np.frombuffer(frame_data_buffer, dtype=np.uint8)
frame = frame_data.reshape((frame_head.iHeight, frame_head.iWidth, 3))
print(f"✅ Frame conversion successful!")
print(f" Frame shape: {frame.shape}")
print(f" Frame dtype: {frame.dtype}")
print(f" Frame size: {frame.size} bytes")
return True
except Exception as e:
print(f"❌ Frame conversion failed: {e}")
return False
def main():
print("🔧 Frame Conversion Test")
print("=" * 40)
success = test_frame_conversion()
if success:
print("\n✅ Frame conversion fix is working correctly!")
print("📋 The streaming issue should be resolved after system restart.")
else:
print("\n❌ Frame conversion fix needs more work.")
print("\n💡 To apply the fix:")
print("1. Restart the USDA vision system")
print("2. Test streaming again")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""
Test script to demonstrate maximum FPS capture functionality.
"""
import requests
import json
import time
from datetime import datetime
BASE_URL = "http://localhost:8000"
def test_fps_modes():
"""Test different FPS modes to demonstrate the functionality"""
print("=" * 60)
print("Testing Maximum FPS Capture Functionality")
print("=" * 60)
# Test configurations
test_configs = [
{
"name": "Normal FPS (3.0)",
"data": {
"filename": "normal_fps_test.avi",
"exposure_ms": 1.0,
"gain": 3.0,
"fps": 3.0
}
},
{
"name": "High FPS (10.0)",
"data": {
"filename": "high_fps_test.avi",
"exposure_ms": 0.5,
"gain": 2.0,
"fps": 10.0
}
},
{
"name": "Maximum FPS (fps=0)",
"data": {
"filename": "max_fps_test.avi",
"exposure_ms": 0.1, # Very short exposure for max speed
"gain": 1.0, # Low gain to avoid overexposure
"fps": 0 # Maximum speed - no delay
}
},
{
"name": "Default FPS (omitted)",
"data": {
"filename": "default_fps_test.avi",
"exposure_ms": 1.0,
"gain": 3.0
# fps omitted - uses camera config default
}
}
]
for i, config in enumerate(test_configs, 1):
print(f"\n{i}. Testing {config['name']}")
print("-" * 40)
# Start recording
try:
response = requests.post(
f"{BASE_URL}/cameras/camera1/start-recording",
json=config['data'],
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
result = response.json()
if result.get('success'):
print(f"✅ Recording started successfully")
print(f" Filename: {result.get('filename')}")
print(f" Settings: {json.dumps(config['data'], indent=6)}")
# Record for a short time
print(f" Recording for 3 seconds...")
time.sleep(3)
# Stop recording
stop_response = requests.post(f"{BASE_URL}/cameras/camera1/stop-recording")
if stop_response.status_code == 200:
stop_result = stop_response.json()
if stop_result.get('success'):
print(f"✅ Recording stopped successfully")
if 'duration_seconds' in stop_result:
print(f" Duration: {stop_result['duration_seconds']:.1f}s")
else:
print(f"❌ Failed to stop recording: {stop_result.get('message')}")
else:
print(f"❌ Stop request failed: {stop_response.status_code}")
else:
print(f"❌ Recording failed: {result.get('message')}")
else:
print(f"❌ Request failed: {response.status_code} - {response.text}")
except requests.exceptions.ConnectionError:
print(f"❌ Could not connect to {BASE_URL}")
print("Make sure the API server is running with: python main.py")
break
except Exception as e:
print(f"❌ Error: {e}")
# Wait between tests
if i < len(test_configs):
print(" Waiting 2 seconds before next test...")
time.sleep(2)
print("\n" + "=" * 60)
print("FPS Test Summary:")
print("=" * 60)
print("• fps > 0: Controlled frame rate with sleep delay")
print("• fps = 0: MAXIMUM speed capture (no delay between frames)")
print("• fps omitted: Uses camera config default")
print("• Video files with fps=0 are saved with 30 FPS metadata")
print("• Actual capture rate with fps=0 depends on:")
print(" - Camera hardware capabilities")
print(" - Exposure time (shorter = faster)")
print(" - Processing overhead")
print("=" * 60)
if __name__ == "__main__":
print("USDA Vision Camera System - Maximum FPS Test")
print("This script demonstrates fps=0 for maximum capture speed")
print("\nMake sure the system is running with: python main.py")
test_fps_modes()

View File

@@ -0,0 +1,199 @@
#!/usr/bin/env python3
"""
Test script for camera streaming functionality.
This script tests the new streaming capabilities without interfering with recording.
"""
import sys
import os
import time
import requests
import threading
from datetime import datetime
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_api_endpoints():
"""Test the streaming API endpoints"""
base_url = "http://localhost:8000"
print("🧪 Testing Camera Streaming API Endpoints")
print("=" * 50)
# Test system status
try:
response = requests.get(f"{base_url}/system/status", timeout=5)
if response.status_code == 200:
print("✅ System status endpoint working")
data = response.json()
print(f" System: {data.get('status', 'Unknown')}")
print(f" Camera Manager: {'Running' if data.get('camera_manager_running') else 'Stopped'}")
else:
print(f"❌ System status endpoint failed: {response.status_code}")
except Exception as e:
print(f"❌ System status endpoint error: {e}")
# Test camera list
try:
response = requests.get(f"{base_url}/cameras", timeout=5)
if response.status_code == 200:
print("✅ Camera list endpoint working")
cameras = response.json()
print(f" Found {len(cameras)} cameras: {list(cameras.keys())}")
# Test streaming for each camera
for camera_name in cameras.keys():
test_camera_streaming(base_url, camera_name)
else:
print(f"❌ Camera list endpoint failed: {response.status_code}")
except Exception as e:
print(f"❌ Camera list endpoint error: {e}")
def test_camera_streaming(base_url, camera_name):
"""Test streaming for a specific camera"""
print(f"\n🎥 Testing streaming for {camera_name}")
print("-" * 30)
# Test start streaming
try:
response = requests.post(f"{base_url}/cameras/{camera_name}/start-stream", timeout=10)
if response.status_code == 200:
print(f"✅ Start stream endpoint working for {camera_name}")
data = response.json()
print(f" Response: {data.get('message', 'No message')}")
else:
print(f"❌ Start stream failed for {camera_name}: {response.status_code}")
print(f" Error: {response.text}")
return
except Exception as e:
print(f"❌ Start stream error for {camera_name}: {e}")
return
# Wait a moment for stream to initialize
time.sleep(2)
# Test stream endpoint (just check if it responds)
try:
response = requests.get(f"{base_url}/cameras/{camera_name}/stream", timeout=5, stream=True)
if response.status_code == 200:
print(f"✅ Stream endpoint responding for {camera_name}")
print(f" Content-Type: {response.headers.get('content-type', 'Unknown')}")
# Read a small amount of data to verify it's working
chunk_count = 0
for chunk in response.iter_content(chunk_size=1024):
chunk_count += 1
if chunk_count >= 3: # Read a few chunks then stop
break
print(f" Received {chunk_count} data chunks")
else:
print(f"❌ Stream endpoint failed for {camera_name}: {response.status_code}")
except Exception as e:
print(f"❌ Stream endpoint error for {camera_name}: {e}")
# Test stop streaming
try:
response = requests.post(f"{base_url}/cameras/{camera_name}/stop-stream", timeout=5)
if response.status_code == 200:
print(f"✅ Stop stream endpoint working for {camera_name}")
data = response.json()
print(f" Response: {data.get('message', 'No message')}")
else:
print(f"❌ Stop stream failed for {camera_name}: {response.status_code}")
except Exception as e:
print(f"❌ Stop stream error for {camera_name}: {e}")
def test_concurrent_recording_and_streaming():
"""Test that streaming doesn't interfere with recording"""
base_url = "http://localhost:8000"
print("\n🔄 Testing Concurrent Recording and Streaming")
print("=" * 50)
try:
# Get available cameras
response = requests.get(f"{base_url}/cameras", timeout=5)
if response.status_code != 200:
print("❌ Cannot get camera list for concurrent test")
return
cameras = response.json()
if not cameras:
print("❌ No cameras available for concurrent test")
return
camera_name = list(cameras.keys())[0] # Use first camera
print(f"Using camera: {camera_name}")
# Start streaming
print("1. Starting streaming...")
response = requests.post(f"{base_url}/cameras/{camera_name}/start-stream", timeout=10)
if response.status_code != 200:
print(f"❌ Failed to start streaming: {response.text}")
return
time.sleep(2)
# Start recording
print("2. Starting recording...")
response = requests.post(f"{base_url}/cameras/{camera_name}/start-recording",
json={"filename": "test_concurrent_recording.avi"}, timeout=10)
if response.status_code == 200:
print("✅ Recording started successfully while streaming")
else:
print(f"❌ Failed to start recording while streaming: {response.text}")
# Let both run for a few seconds
print("3. Running both streaming and recording for 5 seconds...")
time.sleep(5)
# Stop recording
print("4. Stopping recording...")
response = requests.post(f"{base_url}/cameras/{camera_name}/stop-recording", timeout=5)
if response.status_code == 200:
print("✅ Recording stopped successfully")
else:
print(f"❌ Failed to stop recording: {response.text}")
# Stop streaming
print("5. Stopping streaming...")
response = requests.post(f"{base_url}/cameras/{camera_name}/stop-stream", timeout=5)
if response.status_code == 200:
print("✅ Streaming stopped successfully")
else:
print(f"❌ Failed to stop streaming: {response.text}")
print("✅ Concurrent test completed successfully!")
except Exception as e:
print(f"❌ Concurrent test error: {e}")
def main():
"""Main test function"""
print("🚀 USDA Vision Camera Streaming Test")
print("=" * 50)
print(f"Test started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# Wait for system to be ready
print("⏳ Waiting for system to be ready...")
time.sleep(3)
# Run tests
test_api_endpoints()
test_concurrent_recording_and_streaming()
print("\n" + "=" * 50)
print("🏁 Test completed!")
print("\n📋 Next Steps:")
print("1. Open camera_preview.html in your browser")
print("2. Click 'Start Stream' for any camera")
print("3. Verify live preview works without blocking recording")
print("4. Test concurrent recording and streaming")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
Time verification script for USDA Vision Camera System
Checks if system time is properly synchronized
"""
import datetime
import pytz
import requests
import json
def check_system_time():
"""Check system time against multiple sources"""
print("🕐 USDA Vision Camera System - Time Verification")
print("=" * 50)
# Get local time
local_time = datetime.datetime.now()
utc_time = datetime.datetime.utcnow()
# Get Atlanta timezone
atlanta_tz = pytz.timezone('America/New_York')
atlanta_time = datetime.datetime.now(atlanta_tz)
print(f"Local system time: {local_time}")
print(f"UTC time: {utc_time}")
print(f"Atlanta time: {atlanta_time}")
print(f"Timezone: {atlanta_time.tzname()}")
# Check against multiple time APIs for reliability
time_apis = [
{
"name": "WorldTimeAPI",
"url": "http://worldtimeapi.org/api/timezone/America/New_York",
"parser": lambda data: datetime.datetime.fromisoformat(data['datetime'].replace('Z', '+00:00'))
},
{
"name": "WorldClockAPI",
"url": "http://worldclockapi.com/api/json/est/now",
"parser": lambda data: datetime.datetime.fromisoformat(data['currentDateTime'])
}
]
for api in time_apis:
try:
print(f"\n🌐 Checking against {api['name']}...")
response = requests.get(api['url'], timeout=5)
if response.status_code == 200:
data = response.json()
api_time = api['parser'](data)
# Compare times (allow 5 second difference)
time_diff = abs((atlanta_time.replace(tzinfo=None) - api_time.replace(tzinfo=None)).total_seconds())
print(f"API time: {api_time}")
print(f"Time difference: {time_diff:.2f} seconds")
if time_diff < 5:
print("✅ Time is synchronized (within 5 seconds)")
return True
else:
print("❌ Time is NOT synchronized (difference > 5 seconds)")
return False
else:
print(f"⚠️ {api['name']} returned status {response.status_code}")
continue
except Exception as e:
print(f"⚠️ Error checking {api['name']}: {e}")
continue
print("⚠️ Could not reach any time API services")
print("⚠️ This may be due to network connectivity issues")
print("⚠️ System will continue but time synchronization cannot be verified")
return None
if __name__ == "__main__":
check_system_time()

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Test timezone functionality for the USDA Vision Camera System.
"""
import sys
import os
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from usda_vision_system.core.timezone_utils import (
now_atlanta, format_atlanta_timestamp, format_filename_timestamp,
check_time_sync, log_time_info
)
import logging
def test_timezone_functions():
"""Test timezone utility functions"""
print("🕐 Testing USDA Vision Camera System Timezone Functions")
print("=" * 60)
# Test current time functions
atlanta_time = now_atlanta()
print(f"Current Atlanta time: {atlanta_time}")
print(f"Timezone: {atlanta_time.tzname()}")
print(f"UTC offset: {atlanta_time.strftime('%z')}")
# Test timestamp formatting
timestamp_str = format_atlanta_timestamp()
filename_str = format_filename_timestamp()
print(f"\nTimestamp formats:")
print(f" Display format: {timestamp_str}")
print(f" Filename format: {filename_str}")
# Test time sync
print(f"\n🔄 Testing time synchronization...")
sync_info = check_time_sync()
print(f"Sync status: {sync_info['sync_status']}")
if sync_info.get('time_diff_seconds') is not None:
print(f"Time difference: {sync_info['time_diff_seconds']:.2f} seconds")
# Test logging
print(f"\n📝 Testing time logging...")
logging.basicConfig(level=logging.INFO)
log_time_info()
print(f"\n✅ All timezone tests completed successfully!")
# Show example filename that would be generated
example_filename = f"camera1_recording_{filename_str}.avi"
print(f"\nExample recording filename: {example_filename}")
if __name__ == "__main__":
test_timezone_functions()

View File

@@ -0,0 +1,225 @@
#!/usr/bin/env python3
"""
Test script for the USDA Vision Camera System.
This script performs basic tests to verify system components are working correctly.
"""
import sys
import os
import time
import json
import requests
from datetime import datetime
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_imports():
"""Test that all modules can be imported"""
print("Testing imports...")
try:
from usda_vision_system.core.config import Config
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.core.events import EventSystem
from usda_vision_system.mqtt.client import MQTTClient
from usda_vision_system.camera.manager import CameraManager
from usda_vision_system.storage.manager import StorageManager
from usda_vision_system.api.server import APIServer
from usda_vision_system.main import USDAVisionSystem
print("✅ All imports successful")
return True
except Exception as e:
print(f"❌ Import failed: {e}")
return False
def test_configuration():
"""Test configuration loading"""
print("\nTesting configuration...")
try:
from usda_vision_system.core.config import Config
# Test default config
config = Config()
print(f"✅ Default config loaded")
print(f" MQTT broker: {config.mqtt.broker_host}:{config.mqtt.broker_port}")
print(f" Storage path: {config.storage.base_path}")
print(f" Cameras configured: {len(config.cameras)}")
# Test config file if it exists
if os.path.exists("config.json"):
config_file = Config("config.json")
print(f"✅ Config file loaded")
return True
except Exception as e:
print(f"❌ Configuration test failed: {e}")
return False
def test_camera_discovery():
"""Test camera discovery"""
print("\nTesting camera discovery...")
try:
sys.path.append("./camera_sdk")
import mvsdk
devices = mvsdk.CameraEnumerateDevice()
print(f"✅ Camera discovery successful")
print(f" Found {len(devices)} camera(s)")
for i, device in enumerate(devices):
try:
name = device.GetFriendlyName()
port_type = device.GetPortType()
print(f" Camera {i}: {name} ({port_type})")
except Exception as e:
print(f" Camera {i}: Error getting info - {e}")
return True
except Exception as e:
print(f"❌ Camera discovery failed: {e}")
print(" Make sure GigE cameras are connected and camera SDK library is available")
return False
def test_storage_setup():
"""Test storage directory setup"""
print("\nTesting storage setup...")
try:
from usda_vision_system.core.config import Config
from usda_vision_system.storage.manager import StorageManager
from usda_vision_system.core.state_manager import StateManager
config = Config()
state_manager = StateManager()
storage_manager = StorageManager(config, state_manager)
# Test storage statistics
stats = storage_manager.get_storage_statistics()
print(f"✅ Storage manager initialized")
print(f" Base path: {stats.get('base_path', 'Unknown')}")
print(f" Total files: {stats.get('total_files', 0)}")
return True
except Exception as e:
print(f"❌ Storage setup failed: {e}")
return False
def test_mqtt_config():
"""Test MQTT configuration (without connecting)"""
print("\nTesting MQTT configuration...")
try:
from usda_vision_system.core.config import Config
from usda_vision_system.mqtt.client import MQTTClient
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.core.events import EventSystem
config = Config()
state_manager = StateManager()
event_system = EventSystem()
mqtt_client = MQTTClient(config, state_manager, event_system)
status = mqtt_client.get_status()
print(f"✅ MQTT client initialized")
print(f" Broker: {status['broker_host']}:{status['broker_port']}")
print(f" Topics: {len(status['subscribed_topics'])}")
for topic in status["subscribed_topics"]:
print(f" - {topic}")
return True
except Exception as e:
print(f"❌ MQTT configuration test failed: {e}")
return False
def test_system_initialization():
"""Test full system initialization (without starting)"""
print("\nTesting system initialization...")
try:
from usda_vision_system.main import USDAVisionSystem
# Create system instance
system = USDAVisionSystem()
# Check system status
status = system.get_system_status()
print(f"✅ System initialized successfully")
print(f" Running: {status['running']}")
print(f" Components initialized: {len(status['components'])}")
return True
except Exception as e:
print(f"❌ System initialization failed: {e}")
return False
def test_api_endpoints():
"""Test API endpoints if server is running"""
print("\nTesting API endpoints...")
try:
# Test health endpoint
response = requests.get("http://localhost:8000/health", timeout=5)
if response.status_code == 200:
print("✅ API server is running")
# Test system status endpoint
try:
response = requests.get("http://localhost:8000/system/status", timeout=5)
if response.status_code == 200:
data = response.json()
print(f" System started: {data.get('system_started', False)}")
print(f" MQTT connected: {data.get('mqtt_connected', False)}")
print(f" Active recordings: {data.get('active_recordings', 0)}")
else:
print(f"⚠️ System status endpoint returned {response.status_code}")
except Exception as e:
print(f"⚠️ System status test failed: {e}")
return True
else:
print(f"⚠️ API server returned status {response.status_code}")
return False
except requests.exceptions.ConnectionError:
print("⚠️ API server not running (this is OK if system is not started)")
return True
except Exception as e:
print(f"❌ API test failed: {e}")
return False
def main():
"""Run all tests"""
print("USDA Vision Camera System - Test Suite")
print("=" * 50)
tests = [test_imports, test_configuration, test_camera_discovery, test_storage_setup, test_mqtt_config, test_system_initialization, test_api_endpoints]
passed = 0
total = len(tests)
for test in tests:
try:
if test():
passed += 1
except Exception as e:
print(f"❌ Test {test.__name__} crashed: {e}")
print("\n" + "=" * 50)
print(f"Test Results: {passed}/{total} tests passed")
if passed == total:
print("🎉 All tests passed! System appears to be working correctly.")
return 0
else:
print("⚠️ Some tests failed. Check the output above for details.")
return 1
if __name__ == "__main__":
sys.exit(main())

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

@@ -0,0 +1,291 @@
# coding=utf-8
"""
Simple GigE Camera Capture Script
Captures 10 images every 200 milliseconds and saves them to the images directory.
"""
import os
import time
import numpy as np
import cv2
import platform
from datetime import datetime
import sys
sys.path.append("./python demo")
import mvsdk
def is_camera_ready_for_capture():
"""
Check if camera is ready for capture.
Returns: (ready: bool, message: str, camera_info: object or None)
"""
try:
# Initialize SDK
mvsdk.CameraSdkInit(1)
# Enumerate cameras
DevList = mvsdk.CameraEnumerateDevice()
if len(DevList) < 1:
return False, "No cameras found", None
DevInfo = DevList[0]
# Check if already opened
try:
if mvsdk.CameraIsOpened(DevInfo):
return False, f"Camera '{DevInfo.GetFriendlyName()}' is already opened by another process", DevInfo
except:
pass # Some cameras might not support this check
# Try to initialize
try:
hCamera = mvsdk.CameraInit(DevInfo, -1, -1)
# Quick capture test
try:
# Basic setup
mvsdk.CameraSetTriggerMode(hCamera, 0)
mvsdk.CameraPlay(hCamera)
# Try to get one frame with short timeout
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 500) # 0.5 second timeout
mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)
# Success - close and return
mvsdk.CameraUnInit(hCamera)
return True, f"Camera '{DevInfo.GetFriendlyName()}' is ready for capture", DevInfo
except mvsdk.CameraException as e:
mvsdk.CameraUnInit(hCamera)
if e.error_code == mvsdk.CAMERA_STATUS_TIME_OUT:
return False, "Camera timeout - may be busy or not streaming properly", DevInfo
else:
return False, f"Camera capture test failed: {e.message}", DevInfo
except mvsdk.CameraException as e:
if e.error_code == mvsdk.CAMERA_STATUS_DEVICE_IS_OPENED:
return False, f"Camera '{DevInfo.GetFriendlyName()}' is already in use", DevInfo
elif e.error_code == mvsdk.CAMERA_STATUS_ACCESS_DENY:
return False, f"Access denied to camera '{DevInfo.GetFriendlyName()}'", DevInfo
else:
return False, f"Camera initialization failed: {e.message}", DevInfo
except Exception as e:
return False, f"Camera check failed: {str(e)}", None
def get_camera_ranges(hCamera):
"""
Get the available ranges for camera settings
"""
try:
# Get exposure time range
exp_min, exp_max, exp_step = mvsdk.CameraGetExposureTimeRange(hCamera)
print(f"Exposure time range: {exp_min:.1f} - {exp_max:.1f} μs (step: {exp_step:.1f})")
# Get analog gain range
gain_min, gain_max, gain_step = mvsdk.CameraGetAnalogGainXRange(hCamera)
print(f"Analog gain range: {gain_min:.2f} - {gain_max:.2f}x (step: {gain_step:.3f})")
return (exp_min, exp_max, exp_step), (gain_min, gain_max, gain_step)
except Exception as e:
print(f"Could not get camera ranges: {e}")
return None, None
def capture_images(exposure_time_us=2000, analog_gain=1.0):
"""
Main function to capture images from GigE camera
Parameters:
- exposure_time_us: Exposure time in microseconds (default: 2000 = 2ms)
- analog_gain: Analog gain multiplier (default: 1.0)
"""
# Check if camera is ready for capture
print("Checking camera availability...")
ready, message, camera_info = is_camera_ready_for_capture()
if not ready:
print(f"❌ Camera not ready: {message}")
print("\nPossible solutions:")
print("- Close any other camera applications (preview software, etc.)")
print("- Check camera connection and power")
print("- Wait a moment and try again")
return False
print(f"{message}")
# Initialize SDK (already done in status check, but ensure it's ready)
try:
mvsdk.CameraSdkInit(1) # Initialize SDK with English language
except Exception as e:
print(f"SDK initialization failed: {e}")
return False
# Enumerate cameras
DevList = mvsdk.CameraEnumerateDevice()
nDev = len(DevList)
if nDev < 1:
print("No camera was found!")
return False
print(f"Found {nDev} camera(s):")
for i, DevInfo in enumerate(DevList):
print(f"{i}: {DevInfo.GetFriendlyName()} {DevInfo.GetPortType()}")
# Select camera (use first one if only one available)
camera_index = 0 if nDev == 1 else int(input("Select camera index: "))
DevInfo = DevList[camera_index]
print(f"Selected camera: {DevInfo.GetFriendlyName()}")
# Initialize camera
hCamera = 0
try:
hCamera = mvsdk.CameraInit(DevInfo, -1, -1)
print("Camera initialized successfully")
except mvsdk.CameraException as e:
print(f"CameraInit Failed({e.error_code}): {e.message}")
return False
try:
# Get camera capabilities
cap = mvsdk.CameraGetCapability(hCamera)
# Check if it's a mono or color camera
monoCamera = cap.sIspCapacity.bMonoSensor != 0
print(f"Camera type: {'Monochrome' if monoCamera else 'Color'}")
# Get camera ranges
exp_range, gain_range = get_camera_ranges(hCamera)
# Set output format
if monoCamera:
mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)
else:
mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)
# Set camera to continuous capture mode
mvsdk.CameraSetTriggerMode(hCamera, 0)
# Set manual exposure with improved control
mvsdk.CameraSetAeState(hCamera, 0) # Disable auto exposure
# Clamp exposure time to valid range
if exp_range:
exp_min, exp_max, exp_step = exp_range
exposure_time_us = max(exp_min, min(exp_max, exposure_time_us))
mvsdk.CameraSetExposureTime(hCamera, exposure_time_us)
print(f"Set exposure time: {exposure_time_us/1000:.1f}ms")
# Set analog gain
if gain_range:
gain_min, gain_max, gain_step = gain_range
analog_gain = max(gain_min, min(gain_max, analog_gain))
try:
mvsdk.CameraSetAnalogGainX(hCamera, analog_gain)
print(f"Set analog gain: {analog_gain:.2f}x")
except Exception as e:
print(f"Could not set analog gain: {e}")
# Start camera
mvsdk.CameraPlay(hCamera)
print("Camera started")
# Calculate frame buffer size
FrameBufferSize = cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * (1 if monoCamera else 3)
# Allocate frame buffer
pFrameBuffer = mvsdk.CameraAlignMalloc(FrameBufferSize, 16)
# Create images directory if it doesn't exist
if not os.path.exists("images"):
os.makedirs("images")
print("Starting image capture...")
print("Capturing 10 images with 200ms intervals...")
# Capture 10 images
for i in range(10):
try:
# Get image from camera (timeout: 2000ms)
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 2000)
# Process the raw image data
mvsdk.CameraImageProcess(hCamera, pRawData, pFrameBuffer, FrameHead)
# Release the raw data buffer
mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)
# Handle Windows image flip (images are upside down on Windows)
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(pFrameBuffer, FrameHead, 1)
# Convert to numpy array for OpenCV
frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(pFrameBuffer)
frame = np.frombuffer(frame_data, dtype=np.uint8)
# Reshape based on camera type
if FrameHead.uiMediaType == mvsdk.CAMERA_MEDIA_TYPE_MONO8:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))
else:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))
# Generate filename with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3] # milliseconds
filename = f"images/image_{i+1:02d}_{timestamp}.jpg"
# Save image using OpenCV
success = cv2.imwrite(filename, frame)
if success:
print(f"Image {i+1}/10 saved: {filename} ({FrameHead.iWidth}x{FrameHead.iHeight})")
else:
print(f"Failed to save image {i+1}/10")
# Wait 200ms before next capture (except for the last image)
if i < 9:
time.sleep(0.2)
except mvsdk.CameraException as e:
print(f"Failed to capture image {i+1}/10 ({e.error_code}): {e.message}")
continue
print("Image capture completed!")
# Cleanup
mvsdk.CameraAlignFree(pFrameBuffer)
finally:
# Close camera
mvsdk.CameraUnInit(hCamera)
print("Camera closed")
return True
if __name__ == "__main__":
print("GigE Camera Image Capture Script")
print("=" * 40)
print("Note: If images are overexposed, you can adjust the exposure settings:")
print("- Lower exposure_time_us for darker images (e.g., 1000-5000)")
print("- Lower analog_gain for less amplification (e.g., 0.5-2.0)")
print()
# for cracker
# You can adjust these values to fix overexposure:
success = capture_images(exposure_time_us=6000, analog_gain=16.0) # 2ms exposure (much lower than default 30ms) # 1x gain (no amplification)
# for blower
success = capture_images(exposure_time_us=1000, analog_gain=3.5) # 2ms exposure (much lower than default 30ms) # 1x gain (no amplification)
if success:
print("\nCapture completed successfully!")
print("Images saved in the 'images' directory")
else:
print("\nCapture failed!")
input("Press Enter to exit...")

View File

@@ -0,0 +1,439 @@
# coding=utf-8
import cv2
import numpy as np
import platform
import time
import threading
from datetime import datetime
import os
import sys
# Add the python demo directory to path to import mvsdk
sys.path.append("python demo")
import mvsdk
class CameraVideoRecorder:
def __init__(self):
self.hCamera = 0
self.pFrameBuffer = 0
self.cap = None
self.monoCamera = False
self.recording = False
self.video_writer = None
self.frame_count = 0
self.start_time = None
def list_cameras(self):
"""List all available cameras"""
try:
# Initialize SDK
mvsdk.CameraSdkInit(1)
except Exception as e:
print(f"SDK initialization failed: {e}")
return []
# Enumerate cameras
DevList = mvsdk.CameraEnumerateDevice()
nDev = len(DevList)
if nDev < 1:
print("No cameras found!")
return []
print(f"\nFound {nDev} camera(s):")
cameras = []
for i, DevInfo in enumerate(DevList):
camera_info = {"index": i, "name": DevInfo.GetFriendlyName(), "port_type": DevInfo.GetPortType(), "serial": DevInfo.GetSn(), "dev_info": DevInfo}
cameras.append(camera_info)
print(f"{i}: {camera_info['name']} ({camera_info['port_type']}) - SN: {camera_info['serial']}")
return cameras
def initialize_camera(self, dev_info, exposure_ms=1.0, gain=3.5, target_fps=3.0):
"""Initialize camera with specified settings"""
self.target_fps = target_fps
try:
# Initialize camera
self.hCamera = mvsdk.CameraInit(dev_info, -1, -1)
print(f"Camera initialized successfully")
# Get camera capabilities
self.cap = mvsdk.CameraGetCapability(self.hCamera)
self.monoCamera = self.cap.sIspCapacity.bMonoSensor != 0
print(f"Camera type: {'Monochrome' if self.monoCamera else 'Color'}")
# Set output format
if self.monoCamera:
mvsdk.CameraSetIspOutFormat(self.hCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)
else:
mvsdk.CameraSetIspOutFormat(self.hCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)
# Calculate RGB buffer size
FrameBufferSize = self.cap.sResolutionRange.iWidthMax * self.cap.sResolutionRange.iHeightMax * (1 if self.monoCamera else 3)
# Allocate RGB buffer
self.pFrameBuffer = mvsdk.CameraAlignMalloc(FrameBufferSize, 16)
# Set camera to continuous capture mode
mvsdk.CameraSetTriggerMode(self.hCamera, 0)
# Set manual exposure
mvsdk.CameraSetAeState(self.hCamera, 0) # Disable auto exposure
exposure_time_us = exposure_ms * 1000 # Convert ms to microseconds
# Get exposure range and clamp value
try:
exp_min, exp_max, exp_step = mvsdk.CameraGetExposureTimeRange(self.hCamera)
exposure_time_us = max(exp_min, min(exp_max, exposure_time_us))
print(f"Exposure range: {exp_min:.1f} - {exp_max:.1f} μs")
except Exception as e:
print(f"Could not get exposure range: {e}")
mvsdk.CameraSetExposureTime(self.hCamera, exposure_time_us)
print(f"Set exposure time: {exposure_time_us/1000:.1f}ms")
# Set analog gain
try:
gain_min, gain_max, gain_step = mvsdk.CameraGetAnalogGainXRange(self.hCamera)
gain = max(gain_min, min(gain_max, gain))
mvsdk.CameraSetAnalogGainX(self.hCamera, gain)
print(f"Set analog gain: {gain:.2f}x (range: {gain_min:.2f} - {gain_max:.2f})")
except Exception as e:
print(f"Could not set analog gain: {e}")
# Start camera
mvsdk.CameraPlay(self.hCamera)
print("Camera started successfully")
return True
except mvsdk.CameraException as e:
print(f"Camera initialization failed({e.error_code}): {e.message}")
return False
def start_recording(self, output_filename=None):
"""Start video recording"""
if self.recording:
print("Already recording!")
return False
if not output_filename:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"video_{timestamp}.avi"
# Create output directory if it doesn't exist
os.makedirs(os.path.dirname(output_filename) if os.path.dirname(output_filename) else ".", exist_ok=True)
# Get first frame to determine video properties
try:
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.hCamera, 2000)
mvsdk.CameraImageProcess(self.hCamera, pRawData, self.pFrameBuffer, FrameHead)
mvsdk.CameraReleaseImageBuffer(self.hCamera, pRawData)
# Handle Windows frame flipping
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(self.pFrameBuffer, FrameHead, 1)
# Convert to numpy array
frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(self.pFrameBuffer)
frame = np.frombuffer(frame_data, dtype=np.uint8)
if self.monoCamera:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))
# Convert mono to BGR for video writer
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
else:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))
except mvsdk.CameraException as e:
print(f"Failed to get initial frame: {e.message}")
return False
# Initialize video writer
fourcc = cv2.VideoWriter_fourcc(*"XVID")
fps = getattr(self, "target_fps", 3.0) # Use configured FPS or default to 3.0
frame_size = (FrameHead.iWidth, FrameHead.iHeight)
self.video_writer = cv2.VideoWriter(output_filename, fourcc, fps, frame_size)
if not self.video_writer.isOpened():
print(f"Failed to open video writer for {output_filename}")
return False
self.recording = True
self.frame_count = 0
self.start_time = time.time()
self.output_filename = output_filename
print(f"Started recording to: {output_filename}")
print(f"Frame size: {frame_size}, FPS: {fps}")
print("Press 'q' to stop recording...")
return True
def stop_recording(self):
"""Stop video recording"""
if not self.recording:
print("Not currently recording!")
return False
self.recording = False
if self.video_writer:
self.video_writer.release()
self.video_writer = None
duration = time.time() - self.start_time if self.start_time else 0
avg_fps = self.frame_count / duration if duration > 0 else 0
print(f"\nRecording stopped!")
print(f"Saved: {self.output_filename}")
print(f"Frames recorded: {self.frame_count}")
print(f"Duration: {duration:.1f} seconds")
print(f"Average FPS: {avg_fps:.1f}")
return True
def record_loop(self):
"""Main recording loop"""
if not self.recording:
return
print("Recording... Press 'q' in the preview window to stop")
while self.recording:
try:
# Get frame from camera
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.hCamera, 200)
mvsdk.CameraImageProcess(self.hCamera, pRawData, self.pFrameBuffer, FrameHead)
mvsdk.CameraReleaseImageBuffer(self.hCamera, pRawData)
# Handle Windows frame flipping
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(self.pFrameBuffer, FrameHead, 1)
# Convert to numpy array
frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(self.pFrameBuffer)
frame = np.frombuffer(frame_data, dtype=np.uint8)
if self.monoCamera:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))
frame_bgr = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
else:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))
frame_bgr = frame
# Write every frame to video (FPS is controlled by video file playback rate)
if self.video_writer and self.recording:
self.video_writer.write(frame_bgr)
self.frame_count += 1
# Show preview (resized for display)
display_frame = cv2.resize(frame_bgr, (640, 480), interpolation=cv2.INTER_LINEAR)
# Add small delay to control capture rate based on target FPS
target_fps = getattr(self, "target_fps", 3.0)
time.sleep(1.0 / target_fps)
# Add recording indicator
cv2.putText(display_frame, f"REC - Frame: {self.frame_count}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.imshow("Camera Recording - Press 'q' to stop", display_frame)
# Check for quit key
if cv2.waitKey(1) & 0xFF == ord("q"):
self.stop_recording()
break
except mvsdk.CameraException as e:
if e.error_code != mvsdk.CAMERA_STATUS_TIME_OUT:
print(f"Camera error: {e.message}")
break
def cleanup(self):
"""Clean up resources"""
if self.recording:
self.stop_recording()
if self.video_writer:
self.video_writer.release()
if self.hCamera > 0:
mvsdk.CameraUnInit(self.hCamera)
self.hCamera = 0
if self.pFrameBuffer:
mvsdk.CameraAlignFree(self.pFrameBuffer)
self.pFrameBuffer = 0
cv2.destroyAllWindows()
def interactive_menu():
"""Interactive menu for camera operations"""
recorder = CameraVideoRecorder()
try:
# List available cameras
cameras = recorder.list_cameras()
if not cameras:
return
# Select camera
if len(cameras) == 1:
selected_camera = cameras[0]
print(f"\nUsing camera: {selected_camera['name']}")
else:
while True:
try:
choice = int(input(f"\nSelect camera (0-{len(cameras)-1}): "))
if 0 <= choice < len(cameras):
selected_camera = cameras[choice]
break
else:
print("Invalid selection!")
except ValueError:
print("Please enter a valid number!")
# Get camera settings from user
print(f"\nCamera Settings:")
try:
exposure = float(input("Enter exposure time in ms (default 1.0): ") or "1.0")
gain = float(input("Enter gain value (default 3.5): ") or "3.5")
fps = float(input("Enter target FPS (default 3.0): ") or "3.0")
except ValueError:
print("Using default values: exposure=1.0ms, gain=3.5x, fps=3.0")
exposure, gain, fps = 1.0, 3.5, 3.0
# Initialize camera with specified settings
print(f"\nInitializing camera with:")
print(f"- Exposure: {exposure}ms")
print(f"- Gain: {gain}x")
print(f"- Target FPS: {fps}")
if not recorder.initialize_camera(selected_camera["dev_info"], exposure_ms=exposure, gain=gain, target_fps=fps):
return
# Menu loop
while True:
print(f"\n{'='*50}")
print("Camera Video Recorder Menu")
print(f"{'='*50}")
print("1. Start Recording")
print("2. List Camera Info")
print("3. Test Camera (Live Preview)")
print("4. Exit")
try:
choice = input("\nSelect option (1-4): ").strip()
if choice == "1":
# Start recording
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"videos/camera_recording_{timestamp}.avi"
# Create videos directory
os.makedirs("videos", exist_ok=True)
if recorder.start_recording(output_file):
recorder.record_loop()
elif choice == "2":
# Show camera info
print(f"\nCamera Information:")
print(f"Name: {selected_camera['name']}")
print(f"Port Type: {selected_camera['port_type']}")
print(f"Serial Number: {selected_camera['serial']}")
print(f"Type: {'Monochrome' if recorder.monoCamera else 'Color'}")
elif choice == "3":
# Live preview
print("\nLive Preview - Press 'q' to stop")
preview_loop(recorder)
elif choice == "4":
print("Exiting...")
break
else:
print("Invalid option! Please select 1-4.")
except KeyboardInterrupt:
print("\nReturning to menu...")
continue
except KeyboardInterrupt:
print("\nInterrupted by user")
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
finally:
recorder.cleanup()
print("Cleanup completed")
def preview_loop(recorder):
"""Live preview without recording"""
print("Live preview mode - Press 'q' to return to menu")
while True:
try:
# Get frame from camera
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(recorder.hCamera, 200)
mvsdk.CameraImageProcess(recorder.hCamera, pRawData, recorder.pFrameBuffer, FrameHead)
mvsdk.CameraReleaseImageBuffer(recorder.hCamera, pRawData)
# Handle Windows frame flipping
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(recorder.pFrameBuffer, FrameHead, 1)
# Convert to numpy array
frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(recorder.pFrameBuffer)
frame = np.frombuffer(frame_data, dtype=np.uint8)
if recorder.monoCamera:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))
frame_bgr = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
else:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))
frame_bgr = frame
# Show preview (resized for display)
display_frame = cv2.resize(frame_bgr, (640, 480), interpolation=cv2.INTER_LINEAR)
# Add info overlay
cv2.putText(display_frame, f"PREVIEW - {FrameHead.iWidth}x{FrameHead.iHeight}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(display_frame, "Press 'q' to return to menu", (10, display_frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
cv2.imshow("Camera Preview", display_frame)
# Check for quit key
if cv2.waitKey(1) & 0xFF == ord("q"):
cv2.destroyWindow("Camera Preview")
break
except mvsdk.CameraException as e:
if e.error_code != mvsdk.CAMERA_STATUS_TIME_OUT:
print(f"Camera error: {e.message}")
break
def main():
print("Camera Video Recorder")
print("====================")
print("This script allows you to:")
print("- List all available cameras")
print("- Record videos with custom exposure (1ms), gain (3.5x), and FPS (3.0) settings")
print("- Save videos with timestamps")
print("- Stop recording anytime with 'q' key")
print()
interactive_menu()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,6 @@
def main():
print("Hello from usda-vision-cameras!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,197 @@
#coding=utf-8
"""
Test script to help find optimal exposure settings for your GigE camera.
This script captures a single test image with different exposure settings.
"""
import os
import sys
import mvsdk
import numpy as np
import cv2
import platform
from datetime import datetime
# Add the python demo directory to path
sys.path.append('./python demo')
def test_exposure_settings():
"""
Test different exposure settings to find optimal values
"""
# Initialize SDK
try:
mvsdk.CameraSdkInit(1)
print("SDK initialized successfully")
except Exception as e:
print(f"SDK initialization failed: {e}")
return False
# Enumerate cameras
DevList = mvsdk.CameraEnumerateDevice()
nDev = len(DevList)
if nDev < 1:
print("No camera was found!")
return False
print(f"Found {nDev} camera(s):")
for i, DevInfo in enumerate(DevList):
print(f" {i}: {DevInfo.GetFriendlyName()} ({DevInfo.GetPortType()})")
# Use first camera
DevInfo = DevList[0]
print(f"\nSelected camera: {DevInfo.GetFriendlyName()}")
# Initialize camera
try:
hCamera = mvsdk.CameraInit(DevInfo, -1, -1)
print("Camera initialized successfully")
except mvsdk.CameraException as e:
print(f"CameraInit Failed({e.error_code}): {e.message}")
return False
try:
# Get camera capabilities
cap = mvsdk.CameraGetCapability(hCamera)
monoCamera = (cap.sIspCapacity.bMonoSensor != 0)
print(f"Camera type: {'Monochrome' if monoCamera else 'Color'}")
# Get camera ranges
try:
exp_min, exp_max, exp_step = mvsdk.CameraGetExposureTimeRange(hCamera)
print(f"Exposure time range: {exp_min:.1f} - {exp_max:.1f} μs")
gain_min, gain_max, gain_step = mvsdk.CameraGetAnalogGainXRange(hCamera)
print(f"Analog gain range: {gain_min:.2f} - {gain_max:.2f}x")
except Exception as e:
print(f"Could not get camera ranges: {e}")
exp_min, exp_max = 100, 100000
gain_min, gain_max = 1.0, 4.0
# Set output format
if monoCamera:
mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)
else:
mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)
# Set camera to continuous capture mode
mvsdk.CameraSetTriggerMode(hCamera, 0)
mvsdk.CameraSetAeState(hCamera, 0) # Disable auto exposure
# Start camera
mvsdk.CameraPlay(hCamera)
# Allocate frame buffer
FrameBufferSize = cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * (1 if monoCamera else 3)
pFrameBuffer = mvsdk.CameraAlignMalloc(FrameBufferSize, 16)
# Create test directory
if not os.path.exists("exposure_tests"):
os.makedirs("exposure_tests")
print("\nTesting different exposure settings...")
print("=" * 50)
# Test different exposure times (in microseconds)
exposure_times = [500, 1000, 2000, 5000, 10000, 20000] # 0.5ms to 20ms
analog_gains = [1.0] # Start with 1x gain
test_count = 0
for exp_time in exposure_times:
for gain in analog_gains:
# Clamp values to valid ranges
exp_time = max(exp_min, min(exp_max, exp_time))
gain = max(gain_min, min(gain_max, gain))
print(f"\nTest {test_count + 1}: Exposure={exp_time/1000:.1f}ms, Gain={gain:.1f}x")
# Set camera parameters
mvsdk.CameraSetExposureTime(hCamera, exp_time)
try:
mvsdk.CameraSetAnalogGainX(hCamera, gain)
except:
pass # Some cameras might not support this
# Wait a moment for settings to take effect
import time
time.sleep(0.1)
# Capture image
try:
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 2000)
mvsdk.CameraImageProcess(hCamera, pRawData, pFrameBuffer, FrameHead)
mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)
# Handle Windows image flip
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(pFrameBuffer, FrameHead, 1)
# Convert to numpy array
frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(pFrameBuffer)
frame = np.frombuffer(frame_data, dtype=np.uint8)
if FrameHead.uiMediaType == mvsdk.CAMERA_MEDIA_TYPE_MONO8:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))
else:
frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))
# Calculate image statistics
mean_brightness = np.mean(frame)
max_brightness = np.max(frame)
# Save image
filename = f"exposure_tests/test_{test_count+1:02d}_exp{exp_time/1000:.1f}ms_gain{gain:.1f}x.jpg"
cv2.imwrite(filename, frame)
# Provide feedback
status = ""
if mean_brightness < 50:
status = "TOO DARK"
elif mean_brightness > 200:
status = "TOO BRIGHT"
elif max_brightness >= 255:
status = "OVEREXPOSED"
else:
status = "GOOD"
print(f" → Saved: {filename}")
print(f" → Brightness: mean={mean_brightness:.1f}, max={max_brightness:.1f} [{status}]")
test_count += 1
except mvsdk.CameraException as e:
print(f" → Failed to capture: {e.message}")
print(f"\nCompleted {test_count} test captures!")
print("Check the 'exposure_tests' directory to see the results.")
print("\nRecommendations:")
print("- Look for images marked as 'GOOD' - these have optimal exposure")
print("- If all images are 'TOO BRIGHT', try lower exposure times or gains")
print("- If all images are 'TOO DARK', try higher exposure times or gains")
print("- Avoid 'OVEREXPOSED' images as they have clipped highlights")
# Cleanup
mvsdk.CameraAlignFree(pFrameBuffer)
finally:
# Close camera
mvsdk.CameraUnInit(hCamera)
print("\nCamera closed")
return True
if __name__ == "__main__":
print("GigE Camera Exposure Test Script")
print("=" * 40)
print("This script will test different exposure settings and save sample images.")
print("Use this to find the optimal settings for your lighting conditions.")
print()
success = test_exposure_settings()
if success:
print("\nTesting completed successfully!")
else:
print("\nTesting failed!")
input("Press Enter to exit...")

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""
Test script to demonstrate enhanced MQTT logging and API endpoints.
This script shows:
1. Enhanced console logging for MQTT events
2. New MQTT status API endpoint
3. Machine status API endpoint
"""
import sys
import os
import time
import requests
import json
from datetime import datetime
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_api_endpoints():
"""Test the API endpoints for MQTT and machine status"""
base_url = "http://localhost:8000"
print("🧪 Testing API Endpoints...")
print("=" * 50)
# Test system status
try:
print("\n📊 System Status:")
response = requests.get(f"{base_url}/system/status", timeout=5)
if response.status_code == 200:
data = response.json()
print(f" System Started: {data.get('system_started')}")
print(f" MQTT Connected: {data.get('mqtt_connected')}")
print(f" Last MQTT Message: {data.get('last_mqtt_message')}")
print(f" Active Recordings: {data.get('active_recordings')}")
print(f" Total Recordings: {data.get('total_recordings')}")
else:
print(f" ❌ Error: {response.status_code}")
except Exception as e:
print(f" ❌ Connection Error: {e}")
# Test MQTT status
try:
print("\n📡 MQTT Status:")
response = requests.get(f"{base_url}/mqtt/status", timeout=5)
if response.status_code == 200:
data = response.json()
print(f" Connected: {data.get('connected')}")
print(f" Broker: {data.get('broker_host')}:{data.get('broker_port')}")
print(f" Message Count: {data.get('message_count')}")
print(f" Error Count: {data.get('error_count')}")
print(f" Last Message: {data.get('last_message_time')}")
print(f" Uptime: {data.get('uptime_seconds'):.1f}s" if data.get('uptime_seconds') else " Uptime: N/A")
print(f" Subscribed Topics:")
for topic in data.get('subscribed_topics', []):
print(f" - {topic}")
else:
print(f" ❌ Error: {response.status_code}")
except Exception as e:
print(f" ❌ Connection Error: {e}")
# Test machine status
try:
print("\n🏭 Machine Status:")
response = requests.get(f"{base_url}/machines", timeout=5)
if response.status_code == 200:
data = response.json()
if data:
for machine_name, machine_info in data.items():
print(f" {machine_name}:")
print(f" State: {machine_info.get('state')}")
print(f" Last Updated: {machine_info.get('last_updated')}")
print(f" Last Message: {machine_info.get('last_message')}")
print(f" MQTT Topic: {machine_info.get('mqtt_topic')}")
else:
print(" No machines found")
else:
print(f" ❌ Error: {response.status_code}")
except Exception as e:
print(f" ❌ Connection Error: {e}")
def main():
"""Main test function"""
print("🔍 MQTT Logging and API Test")
print("=" * 50)
print()
print("This script tests the enhanced MQTT logging and new API endpoints.")
print("Make sure the USDA Vision System is running before testing.")
print()
# Wait a moment
time.sleep(1)
# Test API endpoints
test_api_endpoints()
print("\n" + "=" * 50)
print("✅ Test completed!")
print()
print("📝 What to expect when running the system:")
print(" 🔗 MQTT CONNECTED: [broker_host:port]")
print(" 📋 MQTT SUBSCRIBED: [machine] → [topic]")
print(" 📡 MQTT MESSAGE: [machine] → [payload]")
print(" ⚠️ MQTT DISCONNECTED: [reason]")
print()
print("🌐 API Endpoints available:")
print(" GET /system/status - Overall system status")
print(" GET /mqtt/status - MQTT client status and statistics")
print(" GET /machines - All machine states from MQTT")
print(" GET /cameras - Camera statuses")
print()
print("💡 To see live MQTT logs, run: python main.py")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,267 @@
#!/usr/bin/env python3
"""
Test script for auto-recording functionality.
This script tests the auto-recording feature by simulating MQTT state changes
and verifying that cameras start and stop recording automatically.
"""
import sys
import os
import time
import json
import requests
from datetime import datetime
# Add the parent directory to Python path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from usda_vision_system.core.config import Config
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.core.events import EventSystem, publish_machine_state_changed
class AutoRecordingTester:
"""Test class for auto-recording functionality"""
def __init__(self):
self.api_base_url = "http://localhost:8000"
self.config = Config("config.json")
self.state_manager = StateManager()
self.event_system = EventSystem()
# Test results
self.test_results = []
def log_test(self, test_name: str, success: bool, message: str = ""):
"""Log a test result"""
status = "✅ PASS" if success else "❌ FAIL"
timestamp = datetime.now().strftime("%H:%M:%S")
result = f"[{timestamp}] {status} {test_name}"
if message:
result += f" - {message}"
print(result)
self.test_results.append({
"test_name": test_name,
"success": success,
"message": message,
"timestamp": timestamp
})
def check_api_available(self) -> bool:
"""Check if the API server is available"""
try:
response = requests.get(f"{self.api_base_url}/cameras", timeout=5)
return response.status_code == 200
except Exception:
return False
def get_camera_status(self, camera_name: str) -> dict:
"""Get camera status from API"""
try:
response = requests.get(f"{self.api_base_url}/cameras", timeout=5)
if response.status_code == 200:
cameras = response.json()
return cameras.get(camera_name, {})
except Exception as e:
print(f"Error getting camera status: {e}")
return {}
def get_auto_recording_status(self) -> dict:
"""Get auto-recording manager status"""
try:
response = requests.get(f"{self.api_base_url}/auto-recording/status", timeout=5)
if response.status_code == 200:
return response.json()
except Exception as e:
print(f"Error getting auto-recording status: {e}")
return {}
def enable_auto_recording(self, camera_name: str) -> bool:
"""Enable auto-recording for a camera"""
try:
response = requests.post(f"{self.api_base_url}/cameras/{camera_name}/auto-recording/enable", timeout=5)
return response.status_code == 200
except Exception as e:
print(f"Error enabling auto-recording: {e}")
return False
def disable_auto_recording(self, camera_name: str) -> bool:
"""Disable auto-recording for a camera"""
try:
response = requests.post(f"{self.api_base_url}/cameras/{camera_name}/auto-recording/disable", timeout=5)
return response.status_code == 200
except Exception as e:
print(f"Error disabling auto-recording: {e}")
return False
def simulate_machine_state_change(self, machine_name: str, state: str):
"""Simulate a machine state change via event system"""
print(f"🔄 Simulating machine state change: {machine_name} -> {state}")
publish_machine_state_changed(machine_name, state, "test_script")
def test_api_connectivity(self) -> bool:
"""Test API connectivity"""
available = self.check_api_available()
self.log_test("API Connectivity", available,
"API server is reachable" if available else "API server is not reachable")
return available
def test_auto_recording_status(self) -> bool:
"""Test auto-recording status endpoint"""
status = self.get_auto_recording_status()
success = bool(status and "running" in status)
self.log_test("Auto-Recording Status API", success,
f"Status: {status}" if success else "Failed to get status")
return success
def test_camera_auto_recording_config(self) -> bool:
"""Test camera auto-recording configuration"""
success = True
# Test enabling auto-recording for camera1
enabled = self.enable_auto_recording("camera1")
if enabled:
self.log_test("Enable Auto-Recording (camera1)", True, "Successfully enabled")
else:
self.log_test("Enable Auto-Recording (camera1)", False, "Failed to enable")
success = False
# Check camera status
time.sleep(1)
camera_status = self.get_camera_status("camera1")
auto_enabled = camera_status.get("auto_recording_enabled", False)
self.log_test("Auto-Recording Status Check", auto_enabled,
f"Camera1 auto-recording enabled: {auto_enabled}")
if not auto_enabled:
success = False
return success
def test_machine_state_simulation(self) -> bool:
"""Test machine state change simulation"""
try:
# Test vibratory conveyor (camera1)
self.simulate_machine_state_change("vibratory_conveyor", "on")
time.sleep(2)
camera_status = self.get_camera_status("camera1")
is_recording = camera_status.get("is_recording", False)
auto_active = camera_status.get("auto_recording_active", False)
self.log_test("Machine ON -> Recording Start", is_recording,
f"Camera1 recording: {is_recording}, auto-active: {auto_active}")
# Test turning machine off
time.sleep(3)
self.simulate_machine_state_change("vibratory_conveyor", "off")
time.sleep(2)
camera_status = self.get_camera_status("camera1")
is_recording_after = camera_status.get("is_recording", False)
auto_active_after = camera_status.get("auto_recording_active", False)
self.log_test("Machine OFF -> Recording Stop", not is_recording_after,
f"Camera1 recording: {is_recording_after}, auto-active: {auto_active_after}")
return is_recording and not is_recording_after
except Exception as e:
self.log_test("Machine State Simulation", False, f"Error: {e}")
return False
def test_retry_mechanism(self) -> bool:
"""Test retry mechanism for failed recording attempts"""
# This test would require simulating camera failures
# For now, we'll just check if the retry queue is accessible
try:
status = self.get_auto_recording_status()
retry_queue = status.get("retry_queue", {})
self.log_test("Retry Queue Access", True,
f"Retry queue accessible, current items: {len(retry_queue)}")
return True
except Exception as e:
self.log_test("Retry Queue Access", False, f"Error: {e}")
return False
def run_all_tests(self):
"""Run all auto-recording tests"""
print("🧪 Starting Auto-Recording Tests")
print("=" * 50)
# Check if system is running
if not self.test_api_connectivity():
print("\n❌ Cannot run tests - API server is not available")
print("Please start the USDA Vision System first:")
print(" python main.py")
return False
# Run tests
tests = [
self.test_auto_recording_status,
self.test_camera_auto_recording_config,
self.test_machine_state_simulation,
self.test_retry_mechanism,
]
passed = 0
total = len(tests)
for test in tests:
try:
if test():
passed += 1
time.sleep(1) # Brief pause between tests
except Exception as e:
self.log_test(test.__name__, False, f"Exception: {e}")
# Print summary
print("\n" + "=" * 50)
print(f"📊 Test Summary: {passed}/{total} tests passed")
if passed == total:
print("🎉 All auto-recording tests passed!")
return True
else:
print(f"⚠️ {total - passed} test(s) failed")
return False
def cleanup(self):
"""Cleanup after tests"""
print("\n🧹 Cleaning up...")
# Disable auto-recording for test cameras
self.disable_auto_recording("camera1")
self.disable_auto_recording("camera2")
# Turn off machines
self.simulate_machine_state_change("vibratory_conveyor", "off")
self.simulate_machine_state_change("blower_separator", "off")
print("✅ Cleanup completed")
def main():
"""Main test function"""
tester = AutoRecordingTester()
try:
success = tester.run_all_tests()
return 0 if success else 1
except KeyboardInterrupt:
print("\n⚠️ Tests interrupted by user")
return 1
except Exception as e:
print(f"\n❌ Test execution failed: {e}")
return 1
finally:
tester.cleanup()
if __name__ == "__main__":
exit_code = main()
sys.exit(exit_code)

View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python3
"""
Test script to verify auto-recording functionality with simulated MQTT messages.
This script tests that:
1. Auto recording manager properly handles machine state changes
2. Recording starts when machine turns "on"
3. Recording stops when machine turns "off"
4. Camera configuration from config.json is used
"""
import sys
import os
import time
import logging
from datetime import datetime
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
def setup_logging():
"""Setup logging for the test"""
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
def test_auto_recording_with_mqtt():
"""Test auto recording functionality with simulated MQTT messages"""
print("🧪 Testing Auto Recording with MQTT Messages")
print("=" * 50)
try:
# Import required modules
from usda_vision_system.core.config import Config
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.core.events import EventSystem, EventType
from usda_vision_system.recording.auto_manager import AutoRecordingManager
print("✅ Modules imported successfully")
# Create system components
config = Config("config.json")
state_manager = StateManager()
event_system = EventSystem()
# Create a mock camera manager for testing
class MockCameraManager:
def __init__(self):
self.recording_calls = []
self.stop_calls = []
def manual_start_recording(self, camera_name, filename, exposure_ms=None, gain=None, fps=None):
call_info = {"camera_name": camera_name, "filename": filename, "exposure_ms": exposure_ms, "gain": gain, "fps": fps, "timestamp": datetime.now()}
self.recording_calls.append(call_info)
print(f"📹 MOCK: Starting recording for {camera_name}")
print(f" - Filename: {filename}")
print(f" - Settings: exposure={exposure_ms}ms, gain={gain}, fps={fps}")
return True
def manual_stop_recording(self, camera_name):
call_info = {"camera_name": camera_name, "timestamp": datetime.now()}
self.stop_calls.append(call_info)
print(f"⏹️ MOCK: Stopping recording for {camera_name}")
return True
mock_camera_manager = MockCameraManager()
# Create auto recording manager
auto_manager = AutoRecordingManager(config, state_manager, event_system, mock_camera_manager)
print("✅ Auto recording manager created")
# Start the auto recording manager
if not auto_manager.start():
print("❌ Failed to start auto recording manager")
return False
print("✅ Auto recording manager started")
# Test 1: Simulate blower_separator turning ON (should trigger camera1)
print("\n🔄 Test 1: Blower separator turns ON")
print("📡 Publishing machine state change event...")
# Use the same event system instance that the auto manager is subscribed to
event_system.publish(EventType.MACHINE_STATE_CHANGED, "test_script", {"machine_name": "blower_separator", "state": "on", "previous_state": None})
time.sleep(1.0) # Give more time for event processing
print(f"📊 Total recording calls so far: {len(mock_camera_manager.recording_calls)}")
for call in mock_camera_manager.recording_calls:
print(f" - {call['camera_name']}: {call['filename']}")
# Check if recording was started for camera1
camera1_calls = [call for call in mock_camera_manager.recording_calls if call["camera_name"] == "camera1"]
if camera1_calls:
call = camera1_calls[-1]
print(f"✅ Camera1 recording started with config:")
print(f" - Exposure: {call['exposure_ms']}ms (expected: 0.3ms)")
print(f" - Gain: {call['gain']} (expected: 4.0)")
print(f" - FPS: {call['fps']} (expected: 0)")
# Verify settings match config.json
if call["exposure_ms"] == 0.3 and call["gain"] == 4.0 and call["fps"] == 0:
print("✅ Camera settings match config.json")
else:
print("❌ Camera settings don't match config.json")
return False
else:
print("❌ Camera1 recording was not started")
return False
# Test 2: Simulate vibratory_conveyor turning ON (should trigger camera2)
print("\n🔄 Test 2: Vibratory conveyor turns ON")
event_system.publish(EventType.MACHINE_STATE_CHANGED, "test_script", {"machine_name": "vibratory_conveyor", "state": "on", "previous_state": None})
time.sleep(0.5)
# Check if recording was started for camera2
camera2_calls = [call for call in mock_camera_manager.recording_calls if call["camera_name"] == "camera2"]
if camera2_calls:
call = camera2_calls[-1]
print(f"✅ Camera2 recording started with config:")
print(f" - Exposure: {call['exposure_ms']}ms (expected: 0.2ms)")
print(f" - Gain: {call['gain']} (expected: 2.0)")
print(f" - FPS: {call['fps']} (expected: 0)")
# Verify settings match config.json
if call["exposure_ms"] == 0.2 and call["gain"] == 2.0 and call["fps"] == 0:
print("✅ Camera settings match config.json")
else:
print("❌ Camera settings don't match config.json")
return False
else:
print("❌ Camera2 recording was not started")
return False
# Test 3: Simulate machines turning OFF
print("\n🔄 Test 3: Machines turn OFF")
event_system.publish(EventType.MACHINE_STATE_CHANGED, "test_script", {"machine_name": "blower_separator", "state": "off", "previous_state": None})
event_system.publish(EventType.MACHINE_STATE_CHANGED, "test_script", {"machine_name": "vibratory_conveyor", "state": "off", "previous_state": None})
time.sleep(0.5)
# Check if recordings were stopped
camera1_stops = [call for call in mock_camera_manager.stop_calls if call["camera_name"] == "camera1"]
camera2_stops = [call for call in mock_camera_manager.stop_calls if call["camera_name"] == "camera2"]
if camera1_stops and camera2_stops:
print("✅ Both cameras stopped recording when machines turned OFF")
else:
print(f"❌ Recording stop failed - Camera1 stops: {len(camera1_stops)}, Camera2 stops: {len(camera2_stops)}")
return False
# Stop the auto recording manager
auto_manager.stop()
print("✅ Auto recording manager stopped")
print("\n🎉 All auto recording tests passed!")
print("\n📊 Summary:")
print(f" - Total recording starts: {len(mock_camera_manager.recording_calls)}")
print(f" - Total recording stops: {len(mock_camera_manager.stop_calls)}")
print(f" - Camera1 starts: {len([c for c in mock_camera_manager.recording_calls if c['camera_name'] == 'camera1'])}")
print(f" - Camera2 starts: {len([c for c in mock_camera_manager.recording_calls if c['camera_name'] == 'camera2'])}")
return True
except Exception as e:
print(f"❌ Test failed with error: {e}")
import traceback
traceback.print_exc()
return False
def main():
"""Run the auto recording test"""
setup_logging()
success = test_auto_recording_with_mqtt()
if success:
print("\n✅ Auto recording functionality is working correctly!")
print("\n📝 The system should now properly:")
print(" 1. Start recording when machines turn ON")
print(" 2. Stop recording when machines turn OFF")
print(" 3. Use camera settings from config.json")
print(" 4. Generate appropriate filenames with timestamps")
else:
print("\n❌ Auto recording test failed!")
print("Please check the implementation and try again.")
return success
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)

View File

@@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""
Simple test script for auto-recording functionality.
This script performs basic checks to verify that the auto-recording feature
is properly integrated and configured.
"""
import sys
import os
import json
import time
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_config_structure():
"""Test that config.json has the required auto-recording fields"""
print("🔍 Testing configuration structure...")
try:
with open("config.json", "r") as f:
config = json.load(f)
# Check system-level auto-recording setting
system_config = config.get("system", {})
if "auto_recording_enabled" not in system_config:
print("❌ Missing 'auto_recording_enabled' in system config")
return False
print(f"✅ System auto-recording enabled: {system_config['auto_recording_enabled']}")
# Check camera-level auto-recording settings
cameras = config.get("cameras", [])
if not cameras:
print("❌ No cameras found in config")
return False
for camera in cameras:
camera_name = camera.get("name", "unknown")
required_fields = ["auto_start_recording_enabled", "auto_recording_max_retries", "auto_recording_retry_delay_seconds"]
missing_fields = [field for field in required_fields if field not in camera]
if missing_fields:
print(f"❌ Camera {camera_name} missing fields: {missing_fields}")
return False
print(f"✅ Camera {camera_name} auto-recording config:")
print(f" - Enabled: {camera['auto_start_recording_enabled']}")
print(f" - Max retries: {camera['auto_recording_max_retries']}")
print(f" - Retry delay: {camera['auto_recording_retry_delay_seconds']}s")
print(f" - Machine topic: {camera.get('machine_topic', 'unknown')}")
return True
except Exception as e:
print(f"❌ Error reading config: {e}")
return False
def test_module_imports():
"""Test that all required modules can be imported"""
print("\n🔍 Testing module imports...")
try:
from usda_vision_system.recording.auto_manager import AutoRecordingManager
print("✅ AutoRecordingManager imported successfully")
from usda_vision_system.core.config import Config
config = Config("config.json")
print("✅ Config loaded successfully")
from usda_vision_system.core.state_manager import StateManager
state_manager = StateManager()
print("✅ StateManager created successfully")
from usda_vision_system.core.events import EventSystem
event_system = EventSystem()
print("✅ EventSystem created successfully")
# Test creating AutoRecordingManager (without camera_manager for now)
auto_manager = AutoRecordingManager(config, state_manager, event_system, None)
print("✅ AutoRecordingManager created successfully")
return True
except Exception as e:
print(f"❌ Import error: {e}")
return False
def test_camera_mapping():
"""Test camera to machine topic mapping"""
print("\n🔍 Testing camera to machine mapping...")
try:
with open("config.json", "r") as f:
config = json.load(f)
cameras = config.get("cameras", [])
expected_mappings = {"camera1": "blower_separator", "camera2": "vibratory_conveyor"} # Blower separator # Conveyor/cracker cam
for camera in cameras:
camera_name = camera.get("name")
machine_topic = camera.get("machine_topic")
if camera_name in expected_mappings:
expected_topic = expected_mappings[camera_name]
if machine_topic == expected_topic:
print(f"{camera_name} correctly mapped to {machine_topic}")
else:
print(f"{camera_name} mapped to {machine_topic}, expected {expected_topic}")
return False
else:
print(f"⚠️ Unknown camera: {camera_name}")
return True
except Exception as e:
print(f"❌ Error checking mappings: {e}")
return False
def test_api_models():
"""Test that API models include auto-recording fields"""
print("\n🔍 Testing API models...")
try:
from usda_vision_system.api.models import CameraStatusResponse, CameraConfigResponse, AutoRecordingConfigRequest, AutoRecordingConfigResponse, AutoRecordingStatusResponse
# Check CameraStatusResponse has auto-recording fields
camera_response = CameraStatusResponse(name="test", status="available", is_recording=False, last_checked="2024-01-01T00:00:00", auto_recording_enabled=True, auto_recording_active=False, auto_recording_failure_count=0)
print("✅ CameraStatusResponse includes auto-recording fields")
# Check CameraConfigResponse has auto-recording fields
config_response = CameraConfigResponse(
name="test",
machine_topic="test_topic",
storage_path="/test",
enabled=True,
auto_start_recording_enabled=True,
auto_recording_max_retries=3,
auto_recording_retry_delay_seconds=5,
exposure_ms=1.0,
gain=1.0,
target_fps=30.0,
sharpness=100,
contrast=100,
saturation=100,
gamma=100,
noise_filter_enabled=False,
denoise_3d_enabled=False,
auto_white_balance=True,
color_temperature_preset=0,
wb_red_gain=1.0,
wb_green_gain=1.0,
wb_blue_gain=1.0,
anti_flicker_enabled=False,
light_frequency=1,
bit_depth=8,
hdr_enabled=False,
hdr_gain_mode=0,
)
print("✅ CameraConfigResponse includes auto-recording fields")
print("✅ All auto-recording API models available")
return True
except Exception as e:
print(f"❌ API model error: {e}")
return False
def main():
"""Run all basic tests"""
print("🧪 Auto-Recording Integration Test")
print("=" * 40)
tests = [test_config_structure, test_module_imports, test_camera_mapping, test_api_models]
passed = 0
total = len(tests)
for test in tests:
try:
if test():
passed += 1
except Exception as e:
print(f"❌ Test {test.__name__} failed with exception: {e}")
print("\n" + "=" * 40)
print(f"📊 Results: {passed}/{total} tests passed")
if passed == total:
print("🎉 All integration tests passed!")
print("\n📝 Next steps:")
print("1. Start the system: python main.py")
print("2. Run full tests: python tests/test_auto_recording.py")
print("3. Test with MQTT messages to trigger auto-recording")
return True
else:
print(f"⚠️ {total - passed} test(s) failed")
print("Please fix the issues before running the full system")
return False
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)

View File

@@ -0,0 +1,185 @@
"""
Test the modular video streaming functionality.
This test verifies that the video module integrates correctly with the existing system
and provides the expected streaming capabilities.
"""
import asyncio
import logging
from pathlib import Path
# Configure logging for tests
logging.basicConfig(level=logging.INFO)
async def test_video_module_integration():
"""Test video module integration with the existing system"""
print("\n🎬 Testing Video Module Integration...")
try:
# Import the necessary components
from usda_vision_system.core.config import Config
from usda_vision_system.storage.manager import StorageManager
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.video.integration import create_video_module
print("✅ Successfully imported video module components")
# Initialize core components
config = Config()
state_manager = StateManager()
storage_manager = StorageManager(config, state_manager)
print("✅ Core components initialized")
# Create video module
video_module = create_video_module(
config=config,
storage_manager=storage_manager,
enable_caching=True,
enable_conversion=False # Disable conversion for testing
)
print("✅ Video module created successfully")
# Test module status
status = video_module.get_module_status()
print(f"📊 Video module status: {status}")
# Test video service
videos = await video_module.video_service.get_all_videos(limit=5)
print(f"📹 Found {len(videos)} video files")
for video in videos[:3]: # Show first 3 videos
print(f" - {video.file_id} ({video.camera_name}) - {video.file_size_bytes} bytes")
# Test streaming service
if videos:
video_file = videos[0]
streaming_info = await video_module.streaming_service.get_video_info(video_file.file_id)
if streaming_info:
print(f"🎯 Streaming test: {streaming_info.file_id} is streamable: {streaming_info.is_streamable}")
# Test API routes creation
api_routes = video_module.get_api_routes()
admin_routes = video_module.get_admin_routes()
print(f"🛣️ API routes created: {len(api_routes.routes)} routes")
print(f"🔧 Admin routes created: {len(admin_routes.routes)} routes")
# List some of the available routes
print("📋 Available video endpoints:")
for route in api_routes.routes:
if hasattr(route, 'path') and hasattr(route, 'methods'):
methods = ', '.join(route.methods) if route.methods else 'N/A'
print(f" {methods} {route.path}")
# Cleanup
await video_module.cleanup()
print("✅ Video module cleanup completed")
return True
except Exception as e:
print(f"❌ Video module test failed: {e}")
import traceback
traceback.print_exc()
return False
async def test_video_streaming_endpoints():
"""Test video streaming endpoints with a mock FastAPI app"""
print("\n🌐 Testing Video Streaming Endpoints...")
try:
from fastapi import FastAPI
from fastapi.testclient import TestClient
from usda_vision_system.core.config import Config
from usda_vision_system.storage.manager import StorageManager
from usda_vision_system.core.state_manager import StateManager
from usda_vision_system.video.integration import create_video_module
# Create test app
app = FastAPI()
# Initialize components
config = Config()
state_manager = StateManager()
storage_manager = StorageManager(config, state_manager)
# Create video module
video_module = create_video_module(
config=config,
storage_manager=storage_manager,
enable_caching=True,
enable_conversion=False
)
# Add video routes to test app
video_routes = video_module.get_api_routes()
admin_routes = video_module.get_admin_routes()
app.include_router(video_routes)
app.include_router(admin_routes)
print("✅ Test FastAPI app created with video routes")
# Create test client
client = TestClient(app)
# Test video list endpoint
response = client.get("/videos/")
print(f"📋 GET /videos/ - Status: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f" Found {data.get('total_count', 0)} videos")
# Test video module status (if we had added it to the routes)
# This would be available in the main API server
print("✅ Video streaming endpoints test completed")
# Cleanup
await video_module.cleanup()
return True
except Exception as e:
print(f"❌ Video streaming endpoints test failed: {e}")
import traceback
traceback.print_exc()
return False
async def main():
"""Run all video module tests"""
print("🚀 Starting Video Module Tests")
print("=" * 50)
# Test 1: Module Integration
test1_success = await test_video_module_integration()
# Test 2: Streaming Endpoints
test2_success = await test_video_streaming_endpoints()
print("\n" + "=" * 50)
print("📊 Test Results:")
print(f" Module Integration: {'✅ PASS' if test1_success else '❌ FAIL'}")
print(f" Streaming Endpoints: {'✅ PASS' if test2_success else '❌ FAIL'}")
if test1_success and test2_success:
print("\n🎉 All video module tests passed!")
print("\n📖 Next Steps:")
print(" 1. Restart the usda-vision-camera service")
print(" 2. Test video streaming in your React app")
print(" 3. Use endpoints like: GET /videos/ and GET /videos/{file_id}/stream")
else:
print("\n⚠️ Some tests failed. Check the error messages above.")
return test1_success and test2_success
if __name__ == "__main__":
asyncio.run(main())