2026-09-19 15:10:01 +03:00
|
|
|
|
use super::models::*;
|
|
|
|
|
|
use super::printer::{spawn_print_task, start_printing_locked, begin_next_locked};
|
|
|
|
|
|
use axum::{extract::{Path as AxumPath, State}, http::StatusCode, Json};
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn job_advance(
|
|
|
|
|
|
State(store): State<JobStore>,
|
|
|
|
|
|
AxumPath(job_id): AxumPath<String>,
|
|
|
|
|
|
) -> Result<Json<JobView>, (StatusCode, String)> {
|
|
|
|
|
|
let (view, spawn_info) = {
|
|
|
|
|
|
let mut m = store.lock().unwrap();
|
|
|
|
|
|
let job = m.get_mut(&job_id).ok_or_else(|| (StatusCode::NOT_FOUND, "Задание не найдено".into()))?;
|
|
|
|
|
|
|
|
|
|
|
|
let si = match job.phase {
|
2026-09-19 23:10:41 +03:00
|
|
|
|
// Старт печати текущего файла (первого или после очистки)
|
|
|
|
|
|
JobPhase::WaitingStart | JobPhase::AwaitingClearOutput => {
|
|
|
|
|
|
let idx = job.current.unwrap_or(0);
|
|
|
|
|
|
Some(start_printing_locked(job, idx))
|
2026-09-19 15:10:01 +03:00
|
|
|
|
}
|
2026-09-19 23:10:41 +03:00
|
|
|
|
|
2026-09-19 15:10:01 +03:00
|
|
|
|
JobPhase::AwaitingPickup => {
|
|
|
|
|
|
let c = job.current.unwrap_or(0);
|
|
|
|
|
|
if job.duplex_first_pass_done {
|
2026-09-19 23:10:41 +03:00
|
|
|
|
// Первый проход двусторонней печати завершен -> просим переложить
|
2026-09-19 15:10:01 +03:00
|
|
|
|
job.duplex_first_pass_done = false;
|
2026-09-19 23:10:41 +03:00
|
|
|
|
job.files[c].status = FileStatus::AwaitingFlip;
|
|
|
|
|
|
job.phase = JobPhase::AwaitingFlip;
|
|
|
|
|
|
None
|
2026-09-19 15:10:01 +03:00
|
|
|
|
} else {
|
2026-09-19 23:10:41 +03:00
|
|
|
|
// Файл полностью напечатан
|
2026-09-19 15:10:01 +03:00
|
|
|
|
job.files[c].status = FileStatus::Done;
|
2026-09-19 23:10:41 +03:00
|
|
|
|
|
|
|
|
|
|
// Ищем следующий файл
|
|
|
|
|
|
if let Some(next_si) = begin_next_locked(job, c) {
|
|
|
|
|
|
let next_idx = job.current.unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
// Проверяем, является ли СЛЕДУЮЩИЙ файл двусторонним
|
|
|
|
|
|
let is_next_duplex = job.files[next_idx].settings.as_ref()
|
|
|
|
|
|
.map(|s| s.sides.as_deref() == Some("Двусторонняя"))
|
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
|
|
if is_next_duplex {
|
|
|
|
|
|
// Следующий двусторонний -> просим очистить лоток ПЕРЕД его стартом
|
|
|
|
|
|
// ВАЖНО: Мы НЕ спавним задачу печати здесь.
|
|
|
|
|
|
// Мы меняем статус и ждем вызова advance (кнопки или автостарта)
|
|
|
|
|
|
job.files[next_idx].status = FileStatus::AwaitingClearOutput;
|
|
|
|
|
|
job.phase = JobPhase::AwaitingClearOutput;
|
|
|
|
|
|
None
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Следующий односторонний -> печатаем сразу
|
|
|
|
|
|
Some(next_si)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None // Задание завершено
|
|
|
|
|
|
}
|
2026-09-19 15:10:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-19 23:10:41 +03:00
|
|
|
|
|
2026-09-19 15:10:01 +03:00
|
|
|
|
JobPhase::AwaitingFlip => {
|
|
|
|
|
|
let c = job.current.unwrap();
|
|
|
|
|
|
job.files[c].status = FileStatus::Printing;
|
|
|
|
|
|
job.phase = JobPhase::Printing;
|
|
|
|
|
|
Some(SpawnInfo {
|
2026-09-19 23:10:41 +03:00
|
|
|
|
job_id: job.id.clone(),
|
|
|
|
|
|
idx: c,
|
2026-09-19 15:10:01 +03:00
|
|
|
|
path: job.files[c].path.to_path_buf(),
|
|
|
|
|
|
settings: job.files[c].settings.clone(),
|
|
|
|
|
|
is_second_pass: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
|
|
|
|
|
(job.view(), si)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-19 23:10:41 +03:00
|
|
|
|
if let Some(si) = spawn_info {
|
|
|
|
|
|
spawn_print_task(store.clone(), si);
|
|
|
|
|
|
}
|
2026-09-19 15:10:01 +03:00
|
|
|
|
Ok(Json(view))
|
|
|
|
|
|
}
|