chore(repo): keep only current main snapshot
History squashed to one root commit to permanently drop old branch history and objects.
This commit is contained in:
464
components/domain/src/printer_protocol_worker.c
Normal file
464
components/domain/src/printer_protocol_worker.c
Normal file
@@ -0,0 +1,464 @@
|
||||
#include "domain.h"
|
||||
#include "printer_protocol_internal.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.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 bool request_status_sync(uint32_t timeout_ms) {
|
||||
int64_t old_ms;
|
||||
|
||||
if (printer_protocol_get_backend() != PRINTER_BACKEND_BLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (printer_protocol_is_stopping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
old_ms = s_status.updated_ms;
|
||||
xSemaphoreGive(s_mutex);
|
||||
|
||||
uint8_t payload = 0x00;
|
||||
if (printer_protocol_send_frame(CMD_GET_STATUS, &payload, 0, true) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t deadline = esp_timer_get_time() / 1000 + timeout_ms;
|
||||
while ((esp_timer_get_time() / 1000) < deadline) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool updated = s_status.updated_ms > old_ms;
|
||||
xSemaphoreGive(s_mutex);
|
||||
|
||||
if (updated) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 direct_ignore_precheck, char *err, size_t err_len) {
|
||||
if (printer_protocol_get_backend() == PRINTER_BACKEND_BLE) {
|
||||
if (!request_status_sync(1200)) {
|
||||
snprintf(err, err_len, "status timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_status_t status;
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
snprintf(err, err_len, "status lock failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
status = s_status;
|
||||
xSemaphoreGive(s_mutex);
|
||||
|
||||
if (!status.has_paper) {
|
||||
snprintf(err, err_len, "printer out of paper");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.battery <= 40) {
|
||||
snprintf(err, err_len, "battery too low");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.temperature >= 60.0f) {
|
||||
snprintf(err, err_len, "temperature too high");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (direct_ignore_precheck) {
|
||||
return true;
|
||||
}
|
||||
|
||||
platform_printer_sensors_t sensors = {0};
|
||||
if (platform_direct_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_direct_printer_temp_min_c();
|
||||
float temp_max = runtime_policy_direct_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_direct_printer_battery_min_percent()) {
|
||||
snprintf(err, err_len, "battery too low");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_print_job_ble(job_slot_t *job) {
|
||||
char err[96];
|
||||
err[0] = '\0';
|
||||
uint8_t next_progress_log = 25;
|
||||
|
||||
if (!ble_printer_client_is_connected()) {
|
||||
snprintf(job->error, sizeof(job->error), "printer not connected");
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!precheck_printer_ready(false, err, sizeof(err))) {
|
||||
snprintf(job->error, sizeof(job->error), "%s", err);
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (job->cancel_requested) {
|
||||
snprintf(job->error, sizeof(job->error), "job canceled");
|
||||
ESP_LOGW(TAG, "job %u print canceled before start", (unsigned)job->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t power_on = 0x01;
|
||||
if (!printer_protocol_send_cmd_with_ack(CMD_POWER, &power_on, 1, true, 1500, true)) {
|
||||
snprintf(job->error, sizeof(job->error), "power on failed");
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t hot_time = density_to_hot_time(job->density);
|
||||
uint8_t param[4] = {
|
||||
0x01,
|
||||
0x02,
|
||||
(uint8_t)((hot_time >> 8) & 0xFF),
|
||||
(uint8_t)(hot_time & 0xFF),
|
||||
};
|
||||
|
||||
if (!printer_protocol_send_cmd_with_ack(CMD_SET_PARAM, param, sizeof(param), true, 1500, true)) {
|
||||
snprintf(job->error, sizeof(job->error), "set print param failed");
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t chunk_max = 240;
|
||||
size_t total_chunks = (size_t)ceil((double)job->data_len / (double)chunk_max);
|
||||
ESP_LOGI(TAG,
|
||||
"job %u data transfer start, chunks=%u, chunk_bytes=%u, hot_time=%u",
|
||||
(unsigned)job->id,
|
||||
(unsigned)total_chunks,
|
||||
(unsigned)chunk_max,
|
||||
(unsigned)hot_time);
|
||||
|
||||
for (size_t i = 0; i < total_chunks; ++i) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
snprintf(job->error, sizeof(job->error), "protocol stopping");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (job->cancel_requested) {
|
||||
snprintf(job->error, sizeof(job->error), "job canceled");
|
||||
ESP_LOGW(TAG,
|
||||
"job %u print canceled at chunk %u/%u",
|
||||
(unsigned)job->id,
|
||||
(unsigned)(i + 1),
|
||||
(unsigned)total_chunks);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t start = i * chunk_max;
|
||||
size_t remain = job->data_len - start;
|
||||
size_t chunk_len = remain > chunk_max ? chunk_max : remain;
|
||||
|
||||
if (!printer_protocol_send_cmd_with_ack(CMD_SEND_DATA,
|
||||
&job->data[start],
|
||||
(uint16_t)chunk_len,
|
||||
false,
|
||||
2500,
|
||||
true)) {
|
||||
snprintf(job->error, sizeof(job->error), "send chunk timeout at %u", (unsigned)i);
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
job->progress = (uint8_t)(((i + 1) * 90) / total_chunks);
|
||||
if (job->progress < 5) {
|
||||
job->progress = 5;
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
while (job->progress >= next_progress_log && next_progress_log <= 90) {
|
||||
ESP_LOGI(TAG,
|
||||
"job %u print progress=%u%% (%u/%u chunks)",
|
||||
(unsigned)job->id,
|
||||
(unsigned)job->progress,
|
||||
(unsigned)(i + 1),
|
||||
(unsigned)total_chunks);
|
||||
next_progress_log = (uint8_t)(next_progress_log + 25);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "job %u data transfer done", (unsigned)job->id);
|
||||
|
||||
uint8_t power_off = 0x00;
|
||||
(void)printer_protocol_send_cmd_with_ack(CMD_POWER, &power_off, 1, true, 1200, false);
|
||||
|
||||
uint8_t feed_payload[3] = {0x2B, 0x00, 0x0C};
|
||||
(void)printer_protocol_send_cmd_with_ack(CMD_SET_DISTANCE,
|
||||
feed_payload,
|
||||
sizeof(feed_payload),
|
||||
true,
|
||||
1200,
|
||||
false);
|
||||
|
||||
ESP_LOGI(TAG, "job %u print command sequence done", (unsigned)job->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_print_job_direct(job_slot_t *job) {
|
||||
if (!platform_direct_printer_is_connected()) {
|
||||
snprintf(job->error, sizeof(job->error), "printer not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
char check_err[96];
|
||||
if (!precheck_printer_ready(job->direct_ignore_precheck, check_err, sizeof(check_err))) {
|
||||
snprintf(job->error, sizeof(job->error), "%s", check_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
platform_direct_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_direct_printer_strobe_interval_us(),
|
||||
.motor_step_delay_us = runtime_policy_direct_printer_motor_step_us(),
|
||||
.motor_steps_per_line = runtime_policy_direct_printer_steps_per_line(),
|
||||
.timeout_ms = runtime_policy_direct_printer_operation_timeout_ms(),
|
||||
.ignore_precheck = job->direct_ignore_precheck,
|
||||
.cancel_flag = &job->cancel_requested,
|
||||
};
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
job->progress = 10;
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
char direct_err[96] = {0};
|
||||
esp_err_t rc = platform_direct_printer_print(&req, direct_err, sizeof(direct_err));
|
||||
if (rc != ESP_OK) {
|
||||
if (direct_err[0] != '\0') {
|
||||
snprintf(job->error, sizeof(job->error), "%s", direct_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), "direct 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, backend=%s, raster_bytes=%u, size=%ux%u, density=%s, ignore_precheck=%d",
|
||||
(unsigned)job->id,
|
||||
printer_protocol_get_backend() == PRINTER_BACKEND_BLE ? "ble" : "direct",
|
||||
(unsigned)job->data_len,
|
||||
(unsigned)job->width,
|
||||
(unsigned)job->height,
|
||||
job->density,
|
||||
job->direct_ignore_precheck);
|
||||
|
||||
if (printer_protocol_is_stopping()) {
|
||||
snprintf(job->error, sizeof(job->error), "protocol stopping");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (printer_protocol_get_backend() == PRINTER_BACKEND_BLE) {
|
||||
return run_print_job_ble(job);
|
||||
}
|
||||
return run_print_job_direct(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);
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED, 1);
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user