Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
R
RecopilacionParametrosVideoTransferenciaArchivos
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Antonio Benítez Cortés
RecopilacionParametrosVideoTransferenciaArchivos
Commits
76994f56
Commit
76994f56
authored
May 28, 2026
by
Antonio Benítez Cortés
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new file
parents
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
571 additions
and
0 deletions
+571
-0
CaptureMoonrakerTransfer.py
+571
-0
No files found.
CaptureMoonrakerTransfer.py
0 → 100644
View file @
76994f56
# Captura video de 2 camaras + JSON con parametros de Moonraker
# - Guarda frames temporalmente, genera video y los borra al finalizar
# Uso: ~/klipper_capture/venv/bin/python3 capture_session.py
import
urllib.request
import
requests
import
json
import
os
import
time
import
threading
import
subprocess
import
shutil
from
datetime
import
datetime
,
timezone
# ---------------------------------------------------------------
# CONFIGURACION
# ---------------------------------------------------------------
PI_IP
=
"127.0.0.1"
CAM1_PORT
=
8080
CAM2_PORT
=
8081
MOONRAKER_PORT
=
7125
INTERVAL_S
=
0.5
OUTPUT_DIR
=
"/home/pi/printer_data/captures"
# Resolucion: configurar en ~/printer_data/config/crowsnest.conf
# linea "resolution: 640x480" de cada camara y reiniciar crowsnest
# ---------------------------------------------------------------
MOONRAKER_BASE
=
"http://"
+
PI_IP
+
":"
+
str
(
MOONRAKER_PORT
)
QUERY
=
(
"extruder&heater_bed&fan&toolhead&motion_report&print_stats"
"&gcode_move&system_stats&idle_timeout&webhooks"
"&temperature_sensor
%20
einsy_board"
"&temperature_host
%20
raspberry_pi"
"&temperature_sensor
%20
raspberry_pi"
"&filament_switch_sensor
%20
filament_sensor"
"&tmc2130
%20
stepper_x&tmc2130
%20
stepper_y"
"&tmc2130
%20
stepper_z&tmc2130
%20
extruder"
"&firmware_retraction&virtual_sdcard"
"&pause_resume&display_status&exclude_object"
"&save_variables&stepper_enable&mcu"
"&heater_fan
%20
nozzle_cooling_fan&probe"
)
cam1
=
{
"jpeg"
:
None
,
"timestamp_us"
:
None
,
"lock"
:
threading
.
Lock
()}
cam2
=
{
"jpeg"
:
None
,
"timestamp_us"
:
None
,
"lock"
:
threading
.
Lock
()}
stop_event
=
threading
.
Event
()
# ---------------------------------------------------------------
# HELPERS
# ---------------------------------------------------------------
def
r
(
val
,
decimals
=
2
):
if
val
is
None
:
return
None
try
:
return
round
(
float
(
val
),
decimals
)
except
Exception
:
return
val
def
pct
(
val
):
if
val
is
None
:
return
None
try
:
return
round
(
float
(
val
)
*
100
,
1
)
except
Exception
:
return
val
def
safe
(
d
,
*
keys
,
default
=
None
):
for
k
in
keys
:
if
not
isinstance
(
d
,
dict
):
return
default
d
=
d
.
get
(
k
,
default
)
return
d
def
seconds_to_timecode
(
s
):
total_ms
=
int
(
round
(
s
*
1000
))
ms
=
total_ms
%
1000
sec
=
(
total_ms
//
1000
)
%
60
mn
=
(
total_ms
//
1000
)
//
60
return
str
(
mn
)
+
":"
+
str
(
sec
)
.
zfill
(
2
)
+
"."
+
str
(
ms
)
.
zfill
(
3
)
# ---------------------------------------------------------------
# HILO DE CAMARA
# ---------------------------------------------------------------
def
camera_thread
(
port
,
buf
):
url
=
"http://"
+
PI_IP
+
":"
+
str
(
port
)
+
"/?action=stream"
while
not
stop_event
.
is_set
():
try
:
with
urllib
.
request
.
urlopen
(
url
,
timeout
=
10
)
as
stream
:
buffer
=
b
""
timestamp_us
=
None
while
not
stop_event
.
is_set
():
chunk
=
stream
.
read
(
4096
)
if
not
chunk
:
break
buffer
+=
chunk
if
b
"X-Timestamp:"
in
buffer
and
timestamp_us
is
None
:
try
:
s
=
buffer
.
index
(
b
"X-Timestamp:"
)
+
len
(
b
"X-Timestamp:"
)
e
=
buffer
.
index
(
b
"
\r\n
"
,
s
)
timestamp_us
=
int
(
buffer
[
s
:
e
]
.
strip
())
except
Exception
:
pass
start
=
buffer
.
find
(
b
"
\xff\xd8
"
)
end
=
buffer
.
find
(
b
"
\xff\xd9
"
)
if
start
!=
-
1
and
end
!=
-
1
and
end
>
start
:
jpeg
=
buffer
[
start
:
end
+
2
]
with
buf
[
"lock"
]:
buf
[
"jpeg"
]
=
jpeg
buf
[
"timestamp_us"
]
=
timestamp_us
buffer
=
buffer
[
end
+
2
:]
timestamp_us
=
None
if
len
(
buffer
)
>
500000
:
buffer
=
buffer
[
-
100000
:]
except
Exception
as
e
:
if
not
stop_event
.
is_set
():
print
(
"[cam:"
+
str
(
port
)
+
"] Error: "
+
str
(
e
)
+
" - reconectando en 2s"
)
time
.
sleep
(
2
)
# ---------------------------------------------------------------
# MOONRAKER
# ---------------------------------------------------------------
def
get_printer_data
():
try
:
r_http
=
requests
.
get
(
MOONRAKER_BASE
+
"/printer/objects/query?"
+
QUERY
,
timeout
=
1.0
)
if
r_http
.
status_code
!=
200
:
return
{}
raw
=
r_http
.
json
()
status
=
safe
(
raw
,
"result"
,
"status"
,
default
=
{})
ext
=
status
.
get
(
"extruder"
,
{})
bed
=
status
.
get
(
"heater_bed"
,
{})
fan
=
status
.
get
(
"fan"
,
{})
th
=
status
.
get
(
"toolhead"
,
{})
mr_s
=
status
.
get
(
"motion_report"
,
{})
ps
=
status
.
get
(
"print_stats"
,
{})
gcm
=
status
.
get
(
"gcode_move"
,
{})
idle
=
status
.
get
(
"idle_timeout"
,
{})
wh
=
status
.
get
(
"webhooks"
,
{})
einsy
=
status
.
get
(
"temperature_sensor einsy_board"
,
{})
pi_th
=
status
.
get
(
"temperature_host raspberry_pi"
,
{})
pi_ts
=
status
.
get
(
"temperature_sensor raspberry_pi"
,
{})
fil
=
status
.
get
(
"filament_switch_sensor filament_sensor"
,
{})
tmcx
=
status
.
get
(
"tmc2130 stepper_x"
,
{})
tmcy
=
status
.
get
(
"tmc2130 stepper_y"
,
{})
tmcz
=
status
.
get
(
"tmc2130 stepper_z"
,
{})
tmce
=
status
.
get
(
"tmc2130 extruder"
,
{})
fret
=
status
.
get
(
"firmware_retraction"
,
{})
vsd
=
status
.
get
(
"virtual_sdcard"
,
{})
pr
=
status
.
get
(
"pause_resume"
,
{})
ds
=
status
.
get
(
"display_status"
,
{})
eo
=
status
.
get
(
"exclude_object"
,
{})
sv
=
status
.
get
(
"save_variables"
,
{})
steppers
=
status
.
get
(
"stepper_enable"
,
{})
mcu
=
status
.
get
(
"mcu"
,
{})
nozzle_fan
=
status
.
get
(
"heater_fan nozzle_cooling_fan"
,
{})
probe
=
status
.
get
(
"probe"
,
{})
sys_stats
=
status
.
get
(
"system_stats"
,
{})
try
:
import
psutil
cpu_pct
=
psutil
.
cpu_percent
()
mem_mb
=
round
(
psutil
.
virtual_memory
()
.
used
/
1024
/
1024
,
1
)
disk_free
=
round
(
psutil
.
disk_usage
(
"/"
)
.
free
/
1024
/
1024
/
1024
,
2
)
cpu_temp
=
None
temps
=
psutil
.
sensors_temperatures
()
if
"cpu_thermal"
in
temps
:
cpu_temp
=
round
(
temps
[
"cpu_thermal"
][
0
]
.
current
,
1
)
except
Exception
:
cpu_pct
=
cpu_temp
=
mem_mb
=
disk_free
=
None
lp
=
mr_s
.
get
(
"live_position"
,
[])
pos_x
=
r
(
lp
[
0
],
3
)
if
len
(
lp
)
>
0
else
None
pos_y
=
r
(
lp
[
1
],
3
)
if
len
(
lp
)
>
1
else
None
pos_z
=
r
(
lp
[
2
],
3
)
if
len
(
lp
)
>
2
else
None
pos_e
=
r
(
lp
[
3
],
3
)
if
len
(
lp
)
>
3
else
None
gp
=
gcm
.
get
(
"gcode_position"
,
[])
gx
=
r
(
gp
[
0
],
3
)
if
len
(
gp
)
>
0
else
None
gy
=
r
(
gp
[
1
],
3
)
if
len
(
gp
)
>
1
else
None
gz
=
r
(
gp
[
2
],
3
)
if
len
(
gp
)
>
2
else
None
return
{
"estado"
:
{
"state"
:
ps
.
get
(
"state"
),
"filename"
:
ps
.
get
(
"filename"
),
"print_duration_s"
:
r
(
ps
.
get
(
"print_duration"
),
1
),
"total_duration_s"
:
r
(
ps
.
get
(
"total_duration"
),
1
),
"filament_used_mm"
:
r
(
ps
.
get
(
"filament_used"
)),
"current_layer"
:
safe
(
ps
,
"info"
,
"current_layer"
),
"total_layers"
:
safe
(
ps
,
"info"
,
"total_layer"
),
"message"
:
ps
.
get
(
"message"
),
"is_paused"
:
pr
.
get
(
"is_paused"
),
"display_message"
:
ds
.
get
(
"message"
),
"was_interrupted"
:
safe
(
sv
,
"variables"
,
"was_interrupted"
),
"current_object"
:
eo
.
get
(
"current_object"
),
"excluded_objects"
:
eo
.
get
(
"excluded_objects"
),
},
"hotend"
:
{
"temp_c"
:
r
(
ext
.
get
(
"temperature"
)),
"target_c"
:
r
(
ext
.
get
(
"target"
),
1
),
"power_pct"
:
pct
(
ext
.
get
(
"power"
)),
"can_extrude"
:
ext
.
get
(
"can_extrude"
),
"pressure_advance"
:
r
(
ext
.
get
(
"pressure_advance"
),
4
),
"smooth_time"
:
r
(
ext
.
get
(
"smooth_time"
),
4
),
},
"cama"
:
{
"temp_c"
:
r
(
bed
.
get
(
"temperature"
)),
"target_c"
:
r
(
bed
.
get
(
"target"
),
1
),
"power_pct"
:
pct
(
bed
.
get
(
"power"
)),
},
"temperaturas"
:
{
"einsy_board_c"
:
r
(
einsy
.
get
(
"temperature"
)),
"einsy_min_c"
:
r
(
einsy
.
get
(
"measured_min_temp"
)),
"einsy_max_c"
:
r
(
einsy
.
get
(
"measured_max_temp"
)),
"pi_c"
:
r
(
pi_th
.
get
(
"temperature"
)),
"pi_min_c"
:
r
(
pi_ts
.
get
(
"measured_min_temp"
)),
"pi_max_c"
:
r
(
pi_ts
.
get
(
"measured_max_temp"
)),
"cpu_c"
:
cpu_temp
,
},
"posicion"
:
{
"x_mm"
:
pos_x
,
"y_mm"
:
pos_y
,
"z_mm"
:
pos_z
,
"e_mm"
:
pos_e
,
"live_velocity_mms"
:
r
(
mr_s
.
get
(
"live_velocity"
)),
"live_extruder_vel_mms"
:
r
(
mr_s
.
get
(
"live_extruder_velocity"
)),
},
"movimiento"
:
{
"speed_factor_pct"
:
pct
(
gcm
.
get
(
"speed_factor"
)),
"extrude_factor_pct"
:
pct
(
gcm
.
get
(
"extrude_factor"
)),
"gcode_x"
:
gx
,
"gcode_y"
:
gy
,
"gcode_z"
:
gz
,
"speed_mms"
:
r
(
gcm
.
get
(
"speed"
),
1
),
"absolute_coordinates"
:
gcm
.
get
(
"absolute_coordinates"
),
"absolute_extrude"
:
gcm
.
get
(
"absolute_extrude"
),
"max_velocity_mms"
:
r
(
th
.
get
(
"max_velocity"
),
1
),
"max_accel_mms2"
:
r
(
th
.
get
(
"max_accel"
),
1
),
"min_cruise_ratio"
:
r
(
th
.
get
(
"minimum_cruise_ratio"
),
3
),
"square_corner_vel_mms"
:
r
(
th
.
get
(
"square_corner_velocity"
),
1
),
"print_time_s"
:
r
(
th
.
get
(
"print_time"
),
3
),
"stalls"
:
th
.
get
(
"stalls"
),
},
"ventiladores"
:
{
"capa_speed_pct"
:
pct
(
fan
.
get
(
"speed"
)),
"nozzle_speed_pct"
:
pct
(
nozzle_fan
.
get
(
"speed"
)),
},
"drivers_tmc2130"
:
{
"x_run_current_a"
:
r
(
tmcx
.
get
(
"run_current"
),
4
),
"x_hold_current_a"
:
r
(
tmcx
.
get
(
"hold_current"
),
4
),
"y_run_current_a"
:
r
(
tmcy
.
get
(
"run_current"
),
4
),
"y_hold_current_a"
:
r
(
tmcy
.
get
(
"hold_current"
),
4
),
"z_run_current_a"
:
r
(
tmcz
.
get
(
"run_current"
),
4
),
"z_hold_current_a"
:
r
(
tmcz
.
get
(
"hold_current"
),
4
),
"e_run_current_a"
:
r
(
tmce
.
get
(
"run_current"
),
4
),
"e_hold_current_a"
:
r
(
tmce
.
get
(
"hold_current"
),
4
),
},
"motores_habilitados"
:
{
"stepper_x"
:
steppers
.
get
(
"stepper_x"
),
"stepper_y"
:
steppers
.
get
(
"stepper_y"
),
"stepper_z"
:
steppers
.
get
(
"stepper_z"
),
"extruder"
:
steppers
.
get
(
"extruder"
),
},
"filamento"
:
{
"detectado"
:
fil
.
get
(
"filament_detected"
),
"sensor_enabled"
:
fil
.
get
(
"enabled"
),
"retract_length_mm"
:
r
(
fret
.
get
(
"retract_length"
),
3
),
"retract_speed_mms"
:
r
(
fret
.
get
(
"retract_speed"
),
1
),
"unretract_extra_mm"
:
r
(
fret
.
get
(
"unretract_extra_length"
),
3
),
"unretract_speed_mms"
:
r
(
fret
.
get
(
"unretract_speed"
),
1
),
},
"sdcard"
:
{
"progress_pct"
:
pct
(
vsd
.
get
(
"progress"
)),
"file_position"
:
vsd
.
get
(
"file_position"
),
"file_size"
:
vsd
.
get
(
"file_size"
),
"is_active"
:
vsd
.
get
(
"is_active"
),
},
"probe"
:
{
"last_z_result"
:
r
(
probe
.
get
(
"last_z_result"
),
4
),
"last_query"
:
probe
.
get
(
"last_query"
),
},
"mcu"
:
{
"version"
:
mcu
.
get
(
"mcu_version"
),
"freq_hz"
:
safe
(
mcu
,
"mcu_stats"
,
"freq"
),
"mcu_awake_s"
:
r
(
safe
(
mcu
,
"mcu_stats"
,
"mcu_awake"
),
4
),
"mcu_task_avg_s"
:
r
(
safe
(
mcu
,
"mcu_stats"
,
"mcu_task_avg"
),
6
),
"mcu_task_stddev"
:
r
(
safe
(
mcu
,
"mcu_stats"
,
"mcu_task_stddev"
),
6
),
"bytes_write"
:
safe
(
mcu
,
"mcu_stats"
,
"bytes_write"
),
"bytes_read"
:
safe
(
mcu
,
"mcu_stats"
,
"bytes_read"
),
"bytes_retransmit"
:
safe
(
mcu
,
"mcu_stats"
,
"bytes_retransmit"
),
"send_seq"
:
safe
(
mcu
,
"mcu_stats"
,
"send_seq"
),
"receive_seq"
:
safe
(
mcu
,
"mcu_stats"
,
"receive_seq"
),
"srtt_s"
:
r
(
safe
(
mcu
,
"mcu_stats"
,
"srtt"
),
6
),
"rto_s"
:
r
(
safe
(
mcu
,
"mcu_stats"
,
"rto"
),
6
),
},
"sistema"
:
{
"cpu_pct"
:
cpu_pct
,
"mem_used_mb"
:
mem_mb
,
"disk_free_gb"
:
disk_free
,
"sysload"
:
r
(
sys_stats
.
get
(
"sysload"
),
3
),
},
"klipper"
:
{
"state"
:
wh
.
get
(
"state"
),
"idle_state"
:
idle
.
get
(
"state"
),
"idle_timeout_s"
:
r
(
idle
.
get
(
"idle_timeout"
),
1
),
"printing_time_s"
:
r
(
idle
.
get
(
"printing_time"
),
1
),
},
}
except
Exception
as
e
:
print
(
"[Moonraker] Error: "
+
str
(
e
))
return
{}
# ---------------------------------------------------------------
# GENERACION DE VIDEO con ffmpeg concat demuxer
# ---------------------------------------------------------------
def
generate_video
(
frames_dir
,
output_path
,
records
,
cam_key
):
if
len
(
records
)
<
2
:
print
(
"[Video] No hay suficientes frames para "
+
cam_key
)
return
concat_path
=
os
.
path
.
join
(
frames_dir
,
"concat.txt"
)
total_duration
=
0.0
with
open
(
concat_path
,
"w"
)
as
f
:
for
i
in
range
(
len
(
records
)
-
1
):
rec1
=
records
[
i
]
rec2
=
records
[
i
+
1
]
ts1
=
rec1
[
cam_key
][
"timestamp_us"
]
ts2
=
rec2
[
cam_key
][
"timestamp_us"
]
if
ts1
is
None
or
ts2
is
None
:
duration
=
INTERVAL_S
else
:
duration
=
(
ts2
-
ts1
)
/
1000000.0
if
duration
<=
0
:
duration
=
INTERVAL_S
total_duration
+=
duration
filename
=
os
.
path
.
basename
(
rec1
[
cam_key
][
"file"
])
f
.
write
(
"file "
+
filename
+
"
\n
"
)
f
.
write
(
"duration "
+
"{:.6f}"
.
format
(
duration
)
+
"
\n
"
)
# ffmpeg requiere repetir el ultimo frame sin duration
last_file
=
os
.
path
.
basename
(
records
[
-
1
][
cam_key
][
"file"
])
f
.
write
(
"file "
+
last_file
+
"
\n
"
)
print
(
"[Video] Frames: "
+
str
(
len
(
records
))
+
" | Duracion real: "
+
str
(
round
(
total_duration
,
1
))
+
"s"
)
print
(
"[Video] Generando "
+
output_path
+
" ..."
)
cmd
=
[
"ffmpeg"
,
"-y"
,
"-f"
,
"concat"
,
"-safe"
,
"0"
,
"-i"
,
concat_path
,
"-vsync"
,
"vfr"
,
"-c:v"
,
"libx264"
,
"-pix_fmt"
,
"yuv420p"
,
output_path
]
try
:
result
=
subprocess
.
run
(
cmd
,
capture_output
=
True
,
text
=
True
)
if
result
.
returncode
==
0
:
print
(
"[Video] OK: "
+
output_path
)
else
:
print
(
"[Video] Error ffmpeg: "
+
result
.
stderr
[
-
300
:])
except
FileNotFoundError
:
print
(
"[Video] ffmpeg no encontrado. Instala con: sudo apt install ffmpeg"
)
# ---------------------------------------------------------------
# BUCLE PRINCIPAL
# ---------------------------------------------------------------
def
capture_loop
():
session_dir
=
None
cam1_dir
=
None
cam2_dir
=
None
records
=
[]
frame_idx
=
0
started
=
False
last_ts1
=
None
last_ts2
=
None
session_start_time
=
None
session_start_ts
=
None
stop_reason
=
"manual"
print
(
"[Sistema] Esperando a que la impresora empiece a imprimir..."
)
try
:
while
True
:
loop_start
=
time
.
time
()
printer_data
=
get_printer_data
()
state
=
printer_data
.
get
(
"estado"
,
{})
.
get
(
"state"
,
"desconocido"
)
if
not
started
:
if
state
==
"printing"
:
started
=
True
session_start_time
=
time
.
time
()
session_name
=
"session_"
+
datetime
.
now
()
.
strftime
(
"
%
Y
%
m
%
d_
%
H
%
M
%
S"
)
session_dir
=
os
.
path
.
join
(
OUTPUT_DIR
,
session_name
)
cam1_dir
=
os
.
path
.
join
(
session_dir
,
"cam1_tmp"
)
cam2_dir
=
os
.
path
.
join
(
session_dir
,
"cam2_tmp"
)
os
.
makedirs
(
cam1_dir
,
exist_ok
=
True
)
os
.
makedirs
(
cam2_dir
,
exist_ok
=
True
)
print
(
"[Sistema] Impresion detectada -> Sesion: "
+
session_dir
)
else
:
print
(
"[Sistema] Estado: "
+
state
+
" - esperando..."
)
time
.
sleep
(
2
)
continue
if
state
in
(
"complete"
,
"finished"
,
"cancelled"
,
"error"
):
stop_reason
=
state
print
(
"[Sistema] Estado final: "
+
state
+
" - guardando ultima muestra..."
)
with
cam1
[
"lock"
]:
jpeg1_final
=
cam1
[
"jpeg"
]
ts1_final
=
cam1
[
"timestamp_us"
]
with
cam2
[
"lock"
]:
jpeg2_final
=
cam2
[
"jpeg"
]
ts2_final
=
cam2
[
"timestamp_us"
]
if
jpeg1_final
and
jpeg2_final
and
session_dir
:
name
=
"frame_"
+
str
(
frame_idx
)
.
zfill
(
6
)
+
".jpg"
sample_time_s
=
(
ts1_final
-
session_start_ts
)
/
1000000.0
if
ts1_final
and
session_start_ts
else
frame_idx
*
INTERVAL_S
with
open
(
os
.
path
.
join
(
cam1_dir
,
name
),
"wb"
)
as
f
:
f
.
write
(
jpeg1_final
)
with
open
(
os
.
path
.
join
(
cam2_dir
,
name
),
"wb"
)
as
f
:
f
.
write
(
jpeg2_final
)
records
.
append
({
"sample_index"
:
frame_idx
,
"video_timestamp"
:
seconds_to_timecode
(
sample_time_s
),
"wall_time_utc"
:
datetime
.
now
(
timezone
.
utc
)
.
isoformat
(),
"cam1"
:
{
"file"
:
"cam1/"
+
name
,
"timestamp_us"
:
ts1_final
,
"timestamp_s"
:
ts1_final
/
1000000.0
if
ts1_final
else
None
},
"cam2"
:
{
"file"
:
"cam2/"
+
name
,
"timestamp_us"
:
ts2_final
,
"timestamp_s"
:
ts2_final
/
1000000.0
if
ts2_final
else
None
},
"printer"
:
printer_data
,
})
break
with
cam1
[
"lock"
]:
jpeg1
=
cam1
[
"jpeg"
]
ts1
=
cam1
[
"timestamp_us"
]
with
cam2
[
"lock"
]:
jpeg2
=
cam2
[
"jpeg"
]
ts2
=
cam2
[
"timestamp_us"
]
if
jpeg1
is
None
or
jpeg2
is
None
:
print
(
"[Sistema] Esperando frames de las camaras..."
)
time
.
sleep
(
INTERVAL_S
)
continue
if
ts1
==
last_ts1
and
ts2
==
last_ts2
:
elapsed
=
time
.
time
()
-
loop_start
time
.
sleep
(
max
(
0
,
INTERVAL_S
-
elapsed
))
continue
last_ts1
=
ts1
last_ts2
=
ts2
name
=
"frame_"
+
str
(
frame_idx
)
.
zfill
(
6
)
+
".jpg"
if
session_start_ts
is
None
:
session_start_ts
=
ts1
sample_time_s
=
(
ts1
-
session_start_ts
)
/
1000000.0
if
ts1
and
session_start_ts
else
frame_idx
*
INTERVAL_S
timecode
=
seconds_to_timecode
(
sample_time_s
)
with
open
(
os
.
path
.
join
(
cam1_dir
,
name
),
"wb"
)
as
f
:
f
.
write
(
jpeg1
)
with
open
(
os
.
path
.
join
(
cam2_dir
,
name
),
"wb"
)
as
f
:
f
.
write
(
jpeg2
)
record
=
{
"sample_index"
:
frame_idx
,
"video_timestamp"
:
timecode
,
"wall_time_utc"
:
datetime
.
now
(
timezone
.
utc
)
.
isoformat
(),
"cam1"
:
{
"file"
:
"cam1/"
+
name
,
"timestamp_us"
:
ts1
,
"timestamp_s"
:
ts1
/
1000000.0
if
ts1
else
None
,
},
"cam2"
:
{
"file"
:
"cam2/"
+
name
,
"timestamp_us"
:
ts2
,
"timestamp_s"
:
ts2
/
1000000.0
if
ts2
else
None
,
},
"printer"
:
printer_data
,
}
records
.
append
(
record
)
temp
=
printer_data
.
get
(
"hotend"
,
{})
.
get
(
"temp_c"
,
0
)
or
0
cama
=
printer_data
.
get
(
"cama"
,
{})
.
get
(
"temp_c"
,
0
)
or
0
print
(
"["
+
str
(
frame_idx
)
.
zfill
(
6
)
+
"]"
+
" "
+
timecode
+
" T="
+
str
(
temp
)
+
"C"
+
" cama="
+
str
(
cama
)
+
"C"
+
" cam1_ts="
+
str
(
ts1
)
+
" cam2_ts="
+
str
(
ts2
)
)
frame_idx
+=
1
elapsed
=
time
.
time
()
-
loop_start
time
.
sleep
(
max
(
0
,
INTERVAL_S
-
elapsed
))
except
KeyboardInterrupt
:
stop_reason
=
"manual"
print
(
"
\n
[Sistema] Detenido manualmente."
)
finally
:
stop_event
.
set
()
if
session_dir
is
None
:
print
(
"[Sistema] No se inicio ninguna sesion (impresora nunca entro en estado printing)."
)
return
json_path
=
os
.
path
.
join
(
session_dir
,
"session_data.json"
)
with
open
(
json_path
,
"w"
)
as
f
:
json
.
dump
(
records
,
f
,
indent
=
2
)
print
(
"[Sistema] JSON guardado: "
+
json_path
+
" ("
+
str
(
len
(
records
))
+
" muestras)"
)
print
(
"[Sistema] Motivo de parada: "
+
stop_reason
)
generate_video
(
cam1_dir
,
os
.
path
.
join
(
session_dir
,
"video_cam1.mp4"
),
records
,
"cam1"
)
generate_video
(
cam2_dir
,
os
.
path
.
join
(
session_dir
,
"video_cam2.mp4"
),
records
,
"cam2"
)
print
(
"[Sistema] Borrando frames temporales..."
)
shutil
.
rmtree
(
cam1_dir
,
ignore_errors
=
True
)
shutil
.
rmtree
(
cam2_dir
,
ignore_errors
=
True
)
print
(
"[Sistema] Frames borrados."
)
print
(
"[Sistema] Sesion completa: "
+
session_dir
)
print
(
"[Sistema] Iniciando transferencia..."
)
transfer_result
=
subprocess
.
run
(
[
"/home/pi/File_Transfer/venv/bin/python3"
,
"/home/pi/File_Transfer/script/Transfer.py"
,
session_dir
],
capture_output
=
True
,
text
=
True
)
if
transfer_result
.
returncode
==
0
:
print
(
"[Transferencia] OK"
)
else
:
print
(
"[Transferencia] Error: "
+
transfer_result
.
stderr
[
-
300
:])
# ---------------------------------------------------------------
# ARRANQUE
# ---------------------------------------------------------------
def
main
():
print
(
"[Sistema] Esperando impresion para crear carpeta de sesion..."
)
t1
=
threading
.
Thread
(
target
=
camera_thread
,
args
=
(
CAM1_PORT
,
cam1
),
daemon
=
True
)
t2
=
threading
.
Thread
(
target
=
camera_thread
,
args
=
(
CAM2_PORT
,
cam2
),
daemon
=
True
)
t1
.
start
()
t2
.
start
()
time
.
sleep
(
2
)
capture_loop
()
if
__name__
==
"__main__"
:
main
()
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment