Refactor camera management to conditionally import SDK and handle mock mode; update API base URL references to localhost in documentation and code.

This commit is contained in:
salirezav
2025-08-08 13:20:31 -04:00
parent fc2da16728
commit 20907509b1
17 changed files with 404 additions and 121 deletions

View File

@@ -7,14 +7,14 @@ This guide is specifically designed for AI assistants to understand and implemen
The USDA Vision Camera system provides live video streaming through REST API endpoints. The streaming uses MJPEG format which is natively supported by HTML `<img>` tags and can be easily integrated into React components.
### Key Characteristics:
- **Base URL**: `http://vision:8000` (production) or `http://localhost:8000` (development)
- **Base URL**: `http://localhost:8000` (production) or `http://localhost:8000` (development)
- **Stream Format**: MJPEG (Motion JPEG)
- **Content-Type**: `multipart/x-mixed-replace; boundary=frame`
- **Authentication**: None (add if needed for production)
- **CORS**: Enabled for all origins (configure for production)
### Base URL Configuration:
- **Production**: `http://vision:8000` (requires hostname setup)
- **Production**: `http://localhost:8000` (requires hostname setup)
- **Development**: `http://localhost:8000` (local testing)
- **Custom IP**: `http://192.168.1.100:8000` (replace with actual IP)
- **Custom hostname**: Configure DNS or /etc/hosts as needed
@@ -77,7 +77,7 @@ GET /cameras/{camera_name}/stream
```jsx
import React, { useState, useEffect } from 'react';
const CameraStream = ({ cameraName, apiBaseUrl = 'http://vision:8000' }) => {
const CameraStream = ({ cameraName, apiBaseUrl = 'http://localhost:8000' }) => {
const [isStreaming, setIsStreaming] = useState(false);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
@@ -227,7 +227,7 @@ export default CameraStream;
import React, { useState, useEffect } from 'react';
import CameraStream from './CameraStream';
const CameraDashboard = ({ apiBaseUrl = 'http://vision:8000' }) => {
const CameraDashboard = ({ apiBaseUrl = 'http://localhost:8000' }) => {
const [cameras, setCameras] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -315,7 +315,7 @@ export default CameraDashboard;
```jsx
import { useState, useEffect, useCallback } from 'react';
const useCameraStream = (cameraName, apiBaseUrl = 'http://vision:8000') => {
const useCameraStream = (cameraName, apiBaseUrl = 'http://localhost:8000') => {
const [isStreaming, setIsStreaming] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
@@ -451,7 +451,7 @@ const CameraStreamTailwind = ({ cameraName }) => {
### Environment Variables (.env)
```env
# Production configuration (using 'vision' hostname)
REACT_APP_CAMERA_API_URL=http://vision:8000
REACT_APP_CAMERA_API_URL=http://localhost:8000
REACT_APP_STREAM_REFRESH_INTERVAL=30000
REACT_APP_STREAM_TIMEOUT=10000
@@ -465,7 +465,7 @@ REACT_APP_STREAM_TIMEOUT=10000
### API Configuration
```javascript
const apiConfig = {
baseUrl: process.env.REACT_APP_CAMERA_API_URL || 'http://vision:8000',
baseUrl: process.env.REACT_APP_CAMERA_API_URL || 'http://localhost:8000',
timeout: parseInt(process.env.REACT_APP_STREAM_TIMEOUT) || 10000,
refreshInterval: parseInt(process.env.REACT_APP_STREAM_REFRESH_INTERVAL) || 30000,
};

View File

@@ -3,18 +3,18 @@
#
# CONFIGURATION:
# - Default Base URL: http://localhost:8000 (local development)
# - Production Base URL: http://vision:8000 (when using hostname 'vision')
# - Production Base URL: http://localhost:8000 (when using hostname 'vision')
# - Custom hostname: Update @baseUrl variable below
#
# HOSTNAME SETUP:
# To use 'vision' hostname instead of 'localhost':
# 1. Add to /etc/hosts: 127.0.0.1 vision
# 2. Or configure DNS to point 'vision' to the server IP
# 3. Update camera_preview.html: API_BASE = 'http://vision:8000'
# 3. Update camera_preview.html: API_BASE = 'http://localhost:8000'
###############################################################################
# Base URL Configuration - Change this to match your setup
@baseUrl = http://vision:8000
@baseUrl = http://localhost:8000
# Alternative configurations:
# @baseUrl = http://localhost:8000 # Local development
# @baseUrl = http://192.168.1.100:8000 # Specific IP address
@@ -30,8 +30,8 @@
# - Requires hostname resolution setup
# - Add to /etc/hosts: 127.0.0.1 vision
# - Or configure DNS: vision -> server IP address
# - Update camera_preview.html: API_BASE = 'http://vision:8000'
# - Set @baseUrl = http://vision:8000
# - Update camera_preview.html: API_BASE = 'http://localhost:8000'
# - Set @baseUrl = http://localhost:8000
# Option 2: Using localhost (development)
# - Works immediately on local machine

View File

@@ -16,7 +16,7 @@ export interface ApiConfig {
}
export const defaultApiConfig: ApiConfig = {
baseUrl: 'http://vision:8000', // Production default, change to 'http://localhost:8000' for development
baseUrl: 'http://localhost:8000', // Production default, change to 'http://localhost:8000' for development
timeout: 10000,
refreshInterval: 30000,
};

View File

@@ -1,7 +1,7 @@
### USDA Vision Camera Streaming API
###
### CONFIGURATION:
### - Production: http://vision:8000 (requires hostname setup)
### - Production: http://localhost:8000 (requires hostname setup)
### - Development: http://localhost:8000
### - Custom: Update @baseUrl below to match your setup
###
@@ -9,7 +9,7 @@
### Use with VS Code REST Client extension or similar tools.
# Base URL - Update to match your configuration
@baseUrl = http://vision:8000
@baseUrl = http://localhost:8000
# Alternative: @baseUrl = http://localhost:8000
### =============================================================================

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -11,43 +12,43 @@
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}
.camera-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.camera-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
background-color: #fafafa;
}
.camera-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.camera-stream {
width: 100%;
max-width: 100%;
@@ -58,14 +59,14 @@
min-height: 200px;
display: block;
}
.camera-controls {
margin-top: 10px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
padding: 8px 16px;
border: none;
@@ -74,80 +75,80 @@
font-size: 14px;
transition: background-color 0.3s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
.btn-success {
background-color: #28a745;
color: white;
}
.btn-success:hover {
background-color: #1e7e34;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
.btn-danger:hover {
background-color: #c82333;
}
.status {
margin-top: 10px;
padding: 8px;
border-radius: 4px;
font-size: 14px;
}
.status-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status-info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.system-info {
margin-top: 30px;
padding: 15px;
background-color: #e9ecef;
border-radius: 4px;
}
.system-info h3 {
margin-top: 0;
color: #495057;
}
.api-info {
font-family: monospace;
font-size: 12px;
@@ -155,18 +156,19 @@
}
</style>
</head>
<body>
<div class="container">
<h1>🎥 USDA Vision Camera Live Preview</h1>
<div class="camera-grid" id="cameraGrid">
<!-- Camera cards will be dynamically generated -->
</div>
<div class="system-info">
<h3>📡 System Information</h3>
<div id="systemStatus">Loading system status...</div>
<h3>🔗 API Endpoints</h3>
<div class="api-info">
<p><strong>Live Stream:</strong> GET /cameras/{camera_name}/stream</p>
@@ -178,18 +180,18 @@
</div>
<script>
const API_BASE = 'http://vision:8000';
const API_BASE = 'http://localhost:8000';
let cameras = {};
// Initialize the page
async function init() {
await loadCameras();
await loadSystemStatus();
// Refresh status every 5 seconds
setInterval(loadSystemStatus, 5000);
}
// Load camera information
async function loadCameras() {
try {
@@ -202,13 +204,13 @@
showError('Failed to load camera information');
}
}
// Load system status
async function loadSystemStatus() {
try {
const response = await fetch(`${API_BASE}/system/status`);
const data = await response.json();
const statusDiv = document.getElementById('systemStatus');
statusDiv.innerHTML = `
<p><strong>System:</strong> ${data.status}</p>
@@ -222,18 +224,18 @@
document.getElementById('systemStatus').innerHTML = '<p style="color: red;">Failed to load system status</p>';
}
}
// Render camera cards
function renderCameras() {
const grid = document.getElementById('cameraGrid');
grid.innerHTML = '';
for (const [cameraName, cameraInfo] of Object.entries(cameras)) {
const card = createCameraCard(cameraName, cameraInfo);
grid.appendChild(card);
}
}
// Create a camera card
function createCameraCard(cameraName, cameraInfo) {
const card = document.createElement('div');
@@ -254,22 +256,22 @@
`;
return card;
}
// Start streaming for a camera
async function startStream(cameraName) {
try {
updateStatus(cameraName, 'Starting stream...', 'info');
// Start the stream
const response = await fetch(`${API_BASE}/cameras/${cameraName}/start-stream`, {
method: 'POST'
});
if (response.ok) {
// Set the stream source
const streamImg = document.getElementById(`stream-${cameraName}`);
streamImg.src = `${API_BASE}/cameras/${cameraName}/stream?t=${Date.now()}`;
updateStatus(cameraName, 'Stream started successfully', 'success');
} else {
const error = await response.text();
@@ -280,21 +282,21 @@
updateStatus(cameraName, `Error starting stream: ${error.message}`, 'error');
}
}
// Stop streaming for a camera
async function stopStream(cameraName) {
try {
updateStatus(cameraName, 'Stopping stream...', 'info');
const response = await fetch(`${API_BASE}/cameras/${cameraName}/stop-stream`, {
method: 'POST'
});
if (response.ok) {
// Clear the stream source
const streamImg = document.getElementById(`stream-${cameraName}`);
streamImg.src = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPk5vIFN0cmVhbTwvdGV4dD48L3N2Zz4=";
updateStatus(cameraName, 'Stream stopped successfully', 'success');
} else {
const error = await response.text();
@@ -305,7 +307,7 @@
updateStatus(cameraName, `Error stopping stream: ${error.message}`, 'error');
}
}
// Refresh stream for a camera
function refreshStream(cameraName) {
const streamImg = document.getElementById(`stream-${cameraName}`);
@@ -316,21 +318,22 @@
updateStatus(cameraName, 'No active stream to refresh', 'error');
}
}
// Update status message
function updateStatus(cameraName, message, type) {
const statusDiv = document.getElementById(`status-${cameraName}`);
statusDiv.className = `status status-${type}`;
statusDiv.textContent = message;
}
// Show error message
function showError(message) {
alert(`Error: ${message}`);
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>
</html>

View File

@@ -1,5 +1,9 @@
# This file was autogenerated by uv via the following command:
# uv export --format requirements-txt
aiofiles==24.1.0 \
--hash=sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c \
--hash=sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5
# via usda-vision-cameras
annotated-types==0.7.0 \
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
@@ -7,11 +11,61 @@ annotated-types==0.7.0 \
anyio==4.9.0 \
--hash=sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028 \
--hash=sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c
# via starlette
# via
# httpx
# starlette
appnope==0.1.4 ; sys_platform == 'darwin' \
--hash=sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee \
--hash=sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c
# via ipykernel
asttokens==3.0.0 \
--hash=sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7 \
--hash=sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2
# via stack-data
certifi==2025.7.14 \
--hash=sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2 \
--hash=sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995
# via requests
# via
# httpcore
# httpx
# requests
cffi==1.17.1 ; implementation_name == 'pypy' \
--hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
--hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \
--hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \
--hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \
--hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \
--hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \
--hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \
--hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \
--hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \
--hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \
--hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \
--hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \
--hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \
--hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \
--hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \
--hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \
--hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \
--hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \
--hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \
--hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \
--hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \
--hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \
--hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \
--hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \
--hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \
--hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \
--hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \
--hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \
--hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \
--hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \
--hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \
--hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \
--hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \
--hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \
--hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b
# via pyzmq
charset-normalizer==3.4.2 \
--hash=sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7 \
--hash=sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0 \
@@ -64,7 +118,12 @@ colorama==0.4.6 ; sys_platform == 'win32' \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
# via
# click
# ipython
# tqdm
comm==0.2.3 \
--hash=sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971 \
--hash=sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417
# via ipykernel
contourpy==1.3.2 \
--hash=sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f \
--hash=sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92 \
@@ -115,6 +174,30 @@ cycler==0.12.1 \
--hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \
--hash=sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c
# via matplotlib
debugpy==1.8.15 \
--hash=sha256:047a493ca93c85ccede1dbbaf4e66816794bdc214213dde41a9a61e42d27f8fc \
--hash=sha256:3dcc7225cb317469721ab5136cda9ff9c8b6e6fb43e87c9e15d5b108b99d01ba \
--hash=sha256:58d7a20b7773ab5ee6bdfb2e6cf622fdf1e40c9d5aef2857d85391526719ac00 \
--hash=sha256:62954fb904bec463e2b5a415777f6d1926c97febb08ef1694da0e5d1463c5c3b \
--hash=sha256:73c943776cb83e36baf95e8f7f8da765896fd94b05991e7bc162456d25500683 \
--hash=sha256:94dc0f0d00e528d915e0ce1c78e771475b2335b376c49afcc7382ee0b146bab6 \
--hash=sha256:b08e9b0bc260cf324c890626961dad4ffd973f7568fbf57feb3c3a65ab6b6327 \
--hash=sha256:babc4fb1962dd6a37e94d611280e3d0d11a1f5e6c72ac9b3d87a08212c4b6dd3 \
--hash=sha256:bce2e6c5ff4f2e00b98d45e7e01a49c7b489ff6df5f12d881c67d2f1ac635f3d \
--hash=sha256:e2a4fe357c92334272eb2845fcfcdbec3ef9f22c16cf613c388ac0887aed15fa \
--hash=sha256:f5e01291ad7d6649aed5773256c5bba7a1a556196300232de1474c3c372592bf \
--hash=sha256:f778e68f2986a58479d0ac4f643e0b8c82fdd97c2e200d4d61e7c2d13838eb53 \
--hash=sha256:f9d1b5abd75cd965e2deabb1a06b0e93a1546f31f9f621d2705e78104377c702 \
--hash=sha256:fcf0748d4f6e25f89dc5e013d1129ca6f26ad4da405e0723a4f704583896a709
# via ipykernel
decorator==5.2.1 \
--hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \
--hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a
# via ipython
executing==2.2.0 \
--hash=sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa \
--hash=sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755
# via stack-data
fastapi==0.116.1 \
--hash=sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565 \
--hash=sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143
@@ -150,17 +233,54 @@ fonttools==4.59.0 \
h11==0.16.0 \
--hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
--hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
# via uvicorn
# via
# httpcore
# uvicorn
httpcore==1.0.9 \
--hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
--hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
# via httpx
httpx==0.28.1 \
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
# via usda-vision-cameras
idna==3.10 \
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
# via
# anyio
# httpx
# requests
imageio==2.37.0 \
--hash=sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed \
--hash=sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996
# via usda-vision-cameras
ipykernel==6.30.0 \
--hash=sha256:b7b808ddb2d261aae2df3a26ff3ff810046e6de3dfbc6f7de8c98ea0a6cb632c \
--hash=sha256:fd2936e55c4a1c2ee8b1e5fa6a372b8eecc0ab1338750dee76f48fa5cca1301e
# via usda-vision-cameras
ipython==9.4.0 \
--hash=sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066 \
--hash=sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270
# via ipykernel
ipython-pygments-lexers==1.1.1 \
--hash=sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81 \
--hash=sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c
# via ipython
jedi==0.19.2 \
--hash=sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0 \
--hash=sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9
# via ipython
jupyter-client==8.6.3 \
--hash=sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419 \
--hash=sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f
# via ipykernel
jupyter-core==5.8.1 \
--hash=sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941 \
--hash=sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0
# via
# ipykernel
# jupyter-client
kiwisolver==1.4.8 \
--hash=sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50 \
--hash=sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8 \
@@ -249,6 +369,16 @@ matplotlib==3.10.3 \
--hash=sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158 \
--hash=sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b
# via usda-vision-cameras
matplotlib-inline==0.1.7 \
--hash=sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90 \
--hash=sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca
# via
# ipykernel
# ipython
nest-asyncio==1.6.0 \
--hash=sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe \
--hash=sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c
# via ipykernel
numpy==2.3.2 \
--hash=sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5 \
--hash=sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b \
@@ -342,11 +472,21 @@ opencv-python==4.11.0.86 \
packaging==25.0 \
--hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \
--hash=sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f
# via matplotlib
# via
# ipykernel
# matplotlib
paho-mqtt==2.1.0 \
--hash=sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834 \
--hash=sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee
# via usda-vision-cameras
parso==0.8.4 \
--hash=sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18 \
--hash=sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d
# via jedi
pexpect==4.9.0 ; sys_platform != 'emscripten' and sys_platform != 'win32' \
--hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \
--hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f
# via ipython
pillow==11.3.0 \
--hash=sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2 \
--hash=sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214 \
@@ -429,6 +569,36 @@ pillow==11.3.0 \
# imageio
# matplotlib
# usda-vision-cameras
platformdirs==4.3.8 \
--hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \
--hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4
# via jupyter-core
prompt-toolkit==3.0.51 \
--hash=sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07 \
--hash=sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed
# via ipython
psutil==7.0.0 \
--hash=sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25 \
--hash=sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91 \
--hash=sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da \
--hash=sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34 \
--hash=sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553 \
--hash=sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456 \
--hash=sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993 \
--hash=sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99
# via ipykernel
ptyprocess==0.7.0 ; sys_platform != 'emscripten' and sys_platform != 'win32' \
--hash=sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35 \
--hash=sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220
# via pexpect
pure-eval==0.2.3 \
--hash=sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0 \
--hash=sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42
# via stack-data
pycparser==2.22 ; implementation_name == 'pypy' \
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
# via cffi
pydantic==2.11.7 \
--hash=sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db \
--hash=sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b
@@ -490,6 +660,12 @@ pydantic-core==2.33.2 \
--hash=sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6 \
--hash=sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d
# via pydantic
pygments==2.19.2 \
--hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
--hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
# via
# ipython
# ipython-pygments-lexers
pyparsing==3.2.3 \
--hash=sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf \
--hash=sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be
@@ -497,11 +673,66 @@ pyparsing==3.2.3 \
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
# via matplotlib
# via
# jupyter-client
# matplotlib
pytz==2025.2 \
--hash=sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3 \
--hash=sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00
# via usda-vision-cameras
pywin32==311 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32' \
--hash=sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151 \
--hash=sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87 \
--hash=sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 \
--hash=sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d \
--hash=sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31 \
--hash=sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a \
--hash=sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42 \
--hash=sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2 \
--hash=sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee \
--hash=sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067 \
--hash=sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852 \
--hash=sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d
# via jupyter-core
pyzmq==27.0.0 \
--hash=sha256:0546a720c1f407b2172cb04b6b094a78773491497e3644863cf5c96c42df8cff \
--hash=sha256:111db5f395e09f7e775f759d598f43cb815fc58e0147623c4816486e1a39dc22 \
--hash=sha256:14fe7aaac86e4e93ea779a821967360c781d7ac5115b3f1a171ced77065a0174 \
--hash=sha256:15f39d50bd6c9091c67315ceb878a4f531957b121d2a05ebd077eb35ddc5efed \
--hash=sha256:1958947983fef513e6e98eff9cb487b60bf14f588dc0e6bf35fa13751d2c8251 \
--hash=sha256:20d5cb29e8c5f76a127c75b6e7a77e846bc4b655c373baa098c26a61b7ecd0ef \
--hash=sha256:21457825249b2a53834fa969c69713f8b5a79583689387a5e7aed880963ac564 \
--hash=sha256:2c386339d7e3f064213aede5d03d054b237937fbca6dd2197ac8cf3b25a6b14e \
--hash=sha256:53a48f0228eab6cbf69fde3aa3c03cbe04e50e623ef92ae395fce47ef8a76152 \
--hash=sha256:56e46bbb85d52c1072b3f809cc1ce77251d560bc036d3a312b96db1afe76db2e \
--hash=sha256:5cd11d46d7b7e5958121b3eaf4cd8638eff3a720ec527692132f05a57f14341d \
--hash=sha256:60e8cc82d968174650c1860d7b716366caab9973787a1c060cf8043130f7d0f7 \
--hash=sha256:67855c14173aec36395d7777aaba3cc527b393821f30143fd20b98e1ff31fd38 \
--hash=sha256:67bfbcbd0a04c575e8103a6061d03e393d9f80ffdb9beb3189261e9e9bc5d5e9 \
--hash=sha256:6ad0562d4e6abb785be3e4dd68599c41be821b521da38c402bc9ab2a8e7ebc7e \
--hash=sha256:7011ade88c8e535cf140f8d1a59428676fbbce7c6e54fefce58bf117aefb6667 \
--hash=sha256:8617c7d43cd8ccdb62aebe984bfed77ca8f036e6c3e46dd3dddda64b10f0ab7a \
--hash=sha256:88b4e43cab04c3c0f0d55df3b1eef62df2b629a1a369b5289a58f6fa8b07c4f4 \
--hash=sha256:9df43a2459cd3a3563404c1456b2c4c69564daa7dbaf15724c09821a3329ce46 \
--hash=sha256:a20528da85c7ac7a19b7384e8c3f8fa707841fd85afc4ed56eda59d93e3d98ad \
--hash=sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf \
--hash=sha256:b801c2e40c5aa6072c2f4876de8dccd100af6d9918d4d0d7aa54a1d982fd4f44 \
--hash=sha256:c0dc628b5493f9a8cd9844b8bee9732ef587ab00002157c9329e4fc0ef4d3afa \
--hash=sha256:c0ed2c1f335ba55b5fdc964622254917d6b782311c50e138863eda409fbb3b6d \
--hash=sha256:c36ad534c0c29b4afa088dc53543c525b23c0797e01b69fef59b1a9c0e38b688 \
--hash=sha256:c5817641eebb391a2268c27fecd4162448e03538387093cdbd8bf3510c316b38 \
--hash=sha256:c8878011653dcdc27cc2c57e04ff96f0471e797f5c19ac3d7813a245bcb24371 \
--hash=sha256:cb0ac5179cba4b2f94f1aa208fbb77b62c4c9bf24dd446278b8b602cf85fcda3 \
--hash=sha256:cbabc59dcfaac66655c040dfcb8118f133fb5dde185e5fc152628354c1598e52 \
--hash=sha256:cd1dc59763effd1576f8368047c9c31468fce0af89d76b5067641137506792ae \
--hash=sha256:d8229f2efece6a660ee211d74d91dbc2a76b95544d46c74c615e491900dc107f \
--hash=sha256:dc1091f59143b471d19eb64f54bae4f54bcf2a466ffb66fe45d94d8d734eb495 \
--hash=sha256:dce4199bf5f648a902ce37e7b3afa286f305cd2ef7a8b6ec907470ccb6c8b371 \
--hash=sha256:e918d70862d4cfd4b1c187310015646a14e1f5917922ab45b29f28f345eeb6be \
--hash=sha256:f7bbe9e1ed2c8d3da736a15694d87c12493e54cc9dc9790796f0321794bbc91f
# via
# ipykernel
# jupyter-client
requests==2.32.4 \
--hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \
--hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422
@@ -514,20 +745,50 @@ sniffio==1.3.1 \
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
# via anyio
stack-data==0.6.3 \
--hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
--hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via ipython
starlette==0.47.2 \
--hash=sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8 \
--hash=sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b
# via fastapi
tornado==6.5.1 \
--hash=sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7 \
--hash=sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692 \
--hash=sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331 \
--hash=sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e \
--hash=sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a \
--hash=sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c \
--hash=sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b \
--hash=sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6 \
--hash=sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888 \
--hash=sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401 \
--hash=sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7 \
--hash=sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365
# via
# ipykernel
# jupyter-client
tqdm==4.67.1 \
--hash=sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2 \
--hash=sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2
# via usda-vision-cameras
traitlets==5.14.3 \
--hash=sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7 \
--hash=sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f
# via
# ipykernel
# ipython
# jupyter-client
# jupyter-core
# matplotlib-inline
typing-extensions==4.14.1 \
--hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \
--hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76
# via
# anyio
# fastapi
# ipython
# pydantic
# pydantic-core
# starlette
@@ -544,6 +805,10 @@ uvicorn==0.35.0 \
--hash=sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a \
--hash=sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01
# via usda-vision-cameras
wcwidth==0.2.13 \
--hash=sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859 \
--hash=sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5
# via prompt-toolkit
websockets==15.0.1 \
--hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \
--hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \

View File

@@ -12,9 +12,15 @@ import logging
from typing import Dict, List, Optional, Tuple, Any
from datetime import datetime
# Add camera SDK to path
# Add camera SDK to path and import conditionally
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "camera_sdk"))
import mvsdk
try:
import mvsdk
CAMERA_SDK_AVAILABLE = True
except (ImportError, OSError) as e:
# Camera SDK not available - system will run in mock mode
mvsdk = None
CAMERA_SDK_AVAILABLE = False
from ..core.config import Config, CameraConfig
from ..core.state_manager import StateManager, CameraStatus
@@ -35,8 +41,11 @@ class CameraManager:
self.event_system = event_system
self.logger = logging.getLogger(__name__)
# Initialize SDK early to suppress error messages
initialize_sdk_with_suppression()
# Initialize SDK early to suppress error messages (if available)
if CAMERA_SDK_AVAILABLE:
initialize_sdk_with_suppression()
else:
self.logger.warning("Camera SDK not available - running in mock mode")
# Camera management
self.available_cameras: List[Any] = [] # mvsdk camera device info
@@ -111,6 +120,11 @@ class CameraManager:
try:
self.logger.info("Discovering GigE cameras...")
if not CAMERA_SDK_AVAILABLE:
self.logger.warning("Camera SDK not available - no cameras will be discovered")
self.available_cameras = []
return
# Enumerate cameras using mvsdk
device_list = mvsdk.CameraEnumerateDevice()
self.available_cameras = device_list