Files
AI_Printer/components/domain/src/printer_protocol_worker.c

258 lines
8.3 KiB
C

#include "domain.h"
#include "printer_protocol_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "printer_protocol";
static uint16_t density_to_hot_time(const char *density) {
if (density == NULL) {
return 2000;
}
if (strcmp(density, "较淡") == 0) {
return 1000;
}
if (strcmp(density, "中等") == 0) {
return 1500;
}
if (strcmp(density, "较浓") == 0) {
return 2000;
}
if (strcmp(density, "最深") == 0) {
return 3000;
}
return 2000;
}
static bool precheck_printer_ready(bool ignore_precheck, char *err, size_t err_len) {
if (ignore_precheck) {
return true;
}
platform_printer_sensors_t sensors = {0};
if (platform_printer_get_sensors(&sensors) != ESP_OK) {
snprintf(err, err_len, "sensor read failed");
return false;
}
if (!sensors.has_paper) {
snprintf(err, err_len, "printer out of paper");
return false;
}
float temp_min = runtime_policy_printer_temp_min_c();
float temp_max = runtime_policy_printer_temp_max_c();
if (temp_max <= temp_min) {
temp_max = temp_min + 1.0f;
}
if (sensors.temperature_c < temp_min || sensors.temperature_c > temp_max) {
snprintf(err, err_len, "temperature out of range");
return false;
}
if (sensors.battery_percent < runtime_policy_printer_battery_min_percent()) {
snprintf(err, err_len, "battery too low");
return false;
}
return true;
}
static bool run_print_job_printer_driver(job_slot_t *job) {
char check_err[96];
if (!precheck_printer_ready(job->ignore_precheck, check_err, sizeof(check_err))) {
snprintf(job->error, sizeof(job->error), "%s", check_err);
return false;
}
platform_printer_print_request_t req = {
.width = job->width,
.height = job->height,
.raster = job->data,
.raster_len = job->data_len,
.strobe_on_time_us = density_to_hot_time(job->density),
.strobe_interval_us = runtime_policy_printer_strobe_interval_us(),
.motor_step_delay_us = runtime_policy_printer_motor_step_us(),
.motor_steps_per_line = runtime_policy_printer_steps_per_line(),
.timeout_ms = runtime_policy_printer_operation_timeout_ms(),
.ignore_precheck = job->ignore_precheck,
.cancel_flag = &job->cancel_requested,
};
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 10;
xSemaphoreGive(s_mutex);
}
char print_err[96] = {0};
esp_err_t rc = platform_printer_print(&req, print_err, sizeof(print_err));
if (rc != ESP_OK) {
if (print_err[0] != 0) {
snprintf(job->error, sizeof(job->error), "%s", print_err);
} else if (rc == ESP_ERR_TIMEOUT) {
snprintf(job->error, sizeof(job->error), "print timeout");
} else if (rc == ESP_ERR_INVALID_STATE && job->cancel_requested) {
snprintf(job->error, sizeof(job->error), "job canceled");
} else {
snprintf(job->error, sizeof(job->error), "printer print failed");
}
return false;
}
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 95;
xSemaphoreGive(s_mutex);
}
return true;
}
static bool run_print_job(job_slot_t *job) {
ESP_LOGI(TAG,
"job %u print start, raster_bytes=%u, size=%ux%u, density=%s, ignore_precheck=%d",
(unsigned)job->id,
(unsigned)job->data_len,
(unsigned)job->width,
(unsigned)job->height,
job->density,
job->ignore_precheck);
if (printer_protocol_is_stopping()) {
snprintf(job->error, sizeof(job->error), "protocol stopping");
return false;
}
return run_print_job_printer_driver(job);
}
void printer_protocol_worker_task(void *arg) {
(void)arg;
while (true) {
if (printer_protocol_is_stopping()) {
break;
}
uint32_t job_id = 0;
if (xQueueReceive(s_job_queue,
&job_id,
pdMS_TO_TICKS(runtime_policy_printer_worker_queue_wait_ms())) != pdTRUE) {
continue;
}
if (job_id == PRINTER_JOB_SENTINEL_STOP || printer_protocol_is_stopping()) {
break;
}
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
continue;
}
int idx = printer_protocol_find_job_idx_locked(job_id);
if (idx < 0) {
xSemaphoreGive(s_mutex);
continue;
}
if (s_jobs[idx].state == PRINT_JOB_STATE_CANCELED) {
free(s_jobs[idx].data);
s_jobs[idx].data = NULL;
if (s_jobs[idx].finished_ms == 0) {
s_jobs[idx].finished_ms = esp_timer_get_time() / 1000;
}
xSemaphoreGive(s_mutex);
continue;
}
if (s_busy_refcnt != 0) {
xSemaphoreGive(s_mutex);
(void)xQueueSendToFront(s_job_queue, &job_id, 0);
vTaskDelay(pdMS_TO_TICKS(runtime_policy_printer_queue_retry_delay_ms()));
continue;
}
job_slot_t *job = &s_jobs[idx];
job->state = PRINT_JOB_STATE_RUNNING;
job->started_ms = esp_timer_get_time() / 1000;
job->progress = 1;
s_busy_refcnt = 1;
int64_t queue_wait_ms = 0;
if (job->started_ms >= job->created_ms) {
queue_wait_ms = job->started_ms - job->created_ms;
}
ESP_LOGI(TAG,
"job %u running, queue_wait_ms=%lld, raster_bytes=%u, size=%ux%u, density=%s",
(unsigned)job->id,
(long long)queue_wait_ms,
(unsigned)job->data_len,
(unsigned)job->width,
(unsigned)job->height,
job->density);
xSemaphoreGive(s_mutex);
bool ok = run_print_job(job);
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
continue;
}
if (s_busy_refcnt > 0) {
--s_busy_refcnt;
}
s_jobs[idx].finished_ms = esp_timer_get_time() / 1000;
if (s_jobs[idx].state != PRINT_JOB_STATE_CANCELED) {
s_jobs[idx].progress = 100;
}
if (ok) {
s_jobs[idx].state = PRINT_JOB_STATE_SUCCESS;
s_jobs[idx].error[0] = 0;
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUCCESS, 1);
} else if (s_jobs[idx].cancel_requested || printer_protocol_is_stopping()) {
s_jobs[idx].state = PRINT_JOB_STATE_CANCELED;
if (s_jobs[idx].error[0] == 0) {
strlcpy(s_jobs[idx].error, "job canceled", sizeof(s_jobs[idx].error));
}
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED, 1);
} else {
s_jobs[idx].state = PRINT_JOB_STATE_FAILED;
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_FAILED, 1);
}
int64_t duration_ms = 0;
if (s_jobs[idx].finished_ms >= s_jobs[idx].started_ms) {
duration_ms = s_jobs[idx].finished_ms - s_jobs[idx].started_ms;
}
if (s_jobs[idx].state == PRINT_JOB_STATE_SUCCESS) {
ESP_LOGI(TAG,
"job %u done, state=%s, duration_ms=%lld",
(unsigned)s_jobs[idx].id,
printer_protocol_job_state_str(s_jobs[idx].state),
(long long)duration_ms);
} else {
ESP_LOGW(TAG,
"job %u done, state=%s, duration_ms=%lld, error=%s",
(unsigned)s_jobs[idx].id,
printer_protocol_job_state_str(s_jobs[idx].state),
(long long)duration_ms,
s_jobs[idx].error[0] != 0 ? s_jobs[idx].error : "-");
}
free(s_jobs[idx].data);
s_jobs[idx].data = NULL;
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH,
(int32_t)uxQueueMessagesWaiting(s_job_queue));
xSemaphoreGive(s_mutex);
}
if (s_evt != NULL) {
xEventGroupSetBits(s_evt, EVT_WORKER_EXITED);
}
s_worker_task = NULL;
vTaskDelete(NULL);
}