Update Docker configuration, enhance error handling, and improve logging

- Added health check to the camera management API service in docker-compose.yml for better container reliability.
- Updated installation scripts in Dockerfile to check for existing dependencies before installation, improving efficiency.
- Enhanced error handling in the USDAVisionSystem class to allow partial operation if some components fail to start, preventing immediate shutdown.
- Improved logging throughout the application, including more detailed error messages and critical error handling in the main loop.
- Refactored WebSocketManager and CameraMonitor classes to use debug logging for connection events, reducing log noise.
This commit is contained in:
salirezav
2025-12-03 17:23:31 -05:00
parent b0f84811bd
commit 5d52183d8e
30 changed files with 4314 additions and 220 deletions

View File

@@ -80,8 +80,7 @@ export interface MachineType {
// Phase-specific interfaces
export interface Soaking {
id: string
experiment_id: string
repetition_id?: string | null
repetition_id: string
scheduled_start_time: string
actual_start_time?: string | null
soaking_duration_minutes: number
@@ -94,8 +93,7 @@ export interface Soaking {
export interface Airdrying {
id: string
experiment_id: string
repetition_id?: string | null
repetition_id: string
scheduled_start_time: string
actual_start_time?: string | null
duration_minutes: number
@@ -307,8 +305,7 @@ export interface UpdatePhaseDataRequest {
// Phase creation request interfaces
export interface CreateSoakingRequest {
experiment_id: string
repetition_id?: string
repetition_id: string
scheduled_start_time: string
soaking_duration_minutes: number
actual_start_time?: string
@@ -316,19 +313,17 @@ export interface CreateSoakingRequest {
}
export interface CreateAirdryingRequest {
experiment_id: string
repetition_id?: string
scheduled_start_time?: string // Will be auto-calculated from soaking if not provided
repetition_id: string
scheduled_start_time: string
duration_minutes: number
actual_start_time?: string
actual_end_time?: string
}
export interface CreateCrackingRequest {
experiment_id: string
repetition_id?: string
repetition_id: string
machine_type_id: string
scheduled_start_time?: string // Will be auto-calculated from airdrying if not provided
scheduled_start_time: string
actual_start_time?: string
actual_end_time?: string
}
@@ -798,11 +793,22 @@ export const phaseManagement = {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
if (!request.repetition_id) {
throw new Error('repetition_id is required')
}
const scheduledEndTime = new Date(new Date(request.scheduled_start_time).getTime() + request.soaking_duration_minutes * 60000).toISOString()
const { data, error } = await supabase
.from('soaking')
.insert({
...request,
.upsert({
repetition_id: request.repetition_id,
scheduled_start_time: request.scheduled_start_time,
soaking_duration_minutes: request.soaking_duration_minutes,
scheduled_end_time: scheduledEndTime,
created_by: user.id
}, {
onConflict: 'repetition_id'
})
.select()
.single()
@@ -824,10 +830,23 @@ export const phaseManagement = {
},
async getSoakingByExperimentId(experimentId: string): Promise<Soaking | null> {
// Get the first repetition for this experiment
const { data: repetitions, error: repsError } = await supabase
.from('experiment_repetitions')
.select('id')
.eq('experiment_id', experimentId)
.order('repetition_number', { ascending: true })
.limit(1)
if (repsError || !repetitions || repetitions.length === 0) {
return null
}
// Get soaking for the first repetition
const { data, error } = await supabase
.from('soaking')
.select('*')
.eq('experiment_id', experimentId)
.eq('repetition_id', repetitions[0].id)
.single()
if (error) {
@@ -856,11 +875,26 @@ export const phaseManagement = {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
if (!request.repetition_id) {
throw new Error('repetition_id is required')
}
if (!request.scheduled_start_time) {
throw new Error('scheduled_start_time is required')
}
const scheduledEndTime = new Date(new Date(request.scheduled_start_time).getTime() + request.duration_minutes * 60000).toISOString()
const { data, error } = await supabase
.from('airdrying')
.insert({
...request,
.upsert({
repetition_id: request.repetition_id,
scheduled_start_time: request.scheduled_start_time,
duration_minutes: request.duration_minutes,
scheduled_end_time: scheduledEndTime,
created_by: user.id
}, {
onConflict: 'repetition_id'
})
.select()
.single()
@@ -882,10 +916,23 @@ export const phaseManagement = {
},
async getAirdryingByExperimentId(experimentId: string): Promise<Airdrying | null> {
// Get the first repetition for this experiment
const { data: repetitions, error: repsError } = await supabase
.from('experiment_repetitions')
.select('id')
.eq('experiment_id', experimentId)
.order('repetition_number', { ascending: true })
.limit(1)
if (repsError || !repetitions || repetitions.length === 0) {
return null
}
// Get airdrying for the first repetition
const { data, error } = await supabase
.from('airdrying')
.select('*')
.eq('experiment_id', experimentId)
.eq('repetition_id', repetitions[0].id)
.single()
if (error) {
@@ -914,11 +961,23 @@ export const phaseManagement = {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
if (!request.repetition_id) {
throw new Error('repetition_id is required')
}
if (!request.scheduled_start_time) {
throw new Error('scheduled_start_time is required')
}
const { data, error } = await supabase
.from('cracking')
.insert({
...request,
.upsert({
repetition_id: request.repetition_id,
machine_type_id: request.machine_type_id,
scheduled_start_time: request.scheduled_start_time,
created_by: user.id
}, {
onConflict: 'repetition_id'
})
.select()
.single()