#include "gui_frame.h" uint32_t gui_crc32(const uint8_t *data, size_t len) { uint32_t crc = 0xFFFFFFFFUL; for (size_t i = 0U; i < len; i++) { crc ^= data[i]; for (uint8_t bit = 0U; bit < 8U; bit++) { crc = (crc & 1UL) ? ((crc >> 1) ^ 0xEDB88320UL) : (crc >> 1); } } return crc ^ 0xFFFFFFFFUL; } size_t gui_frame_encode(uint8_t type, uint16_t sequence, const uint8_t *payload, uint16_t size, uint8_t *out, size_t out_size) { if ((out == NULL) || (size > GUI_MAX_PAYLOAD) || ((size > 0U) && (payload == NULL))) { return 0U; } size_t total = (size_t)GUI_HEADER_SIZE + size + GUI_CRC_SIZE; if (out_size < total) { return 0U; } out[0] = GUI_SOF0; out[1] = GUI_SOF1; /* CRC считается от version до конца payload; SOF в него не входит. */ out[2] = GUI_PROTOCOL_VERSION; out[3] = type; out[4] = (uint8_t)(sequence >> 8); /* заголовок big-endian */ out[5] = (uint8_t)(sequence & 0xFFU); out[6] = (uint8_t)(size >> 8); out[7] = (uint8_t)(size & 0xFFU); for (uint16_t i = 0U; i < size; i++) { out[GUI_HEADER_SIZE + i] = payload[i]; } uint32_t crc = gui_crc32(&out[2], (size_t)6U + size); gui_put_u32(&out[GUI_HEADER_SIZE + size], crc); /* CRC little-endian */ return total; } void gui_parser_init(gui_parser_t *p) { p->state = GUI_ST_SOF0; p->idx = 0U; p->size = 0U; p->stats.frames = 0U; p->stats.crc_errors = 0U; p->stats.version_errors = 0U; p->stats.length_errors = 0U; p->stats.stray_bytes = 0U; } bool gui_parser_push(gui_parser_t *p, uint8_t byte, gui_frame_t *out) { switch (p->state) { case GUI_ST_SOF0: if (byte == GUI_SOF0) { p->state = GUI_ST_SOF1; } else { p->stats.stray_bytes++; } break; case GUI_ST_SOF1: if (byte == GUI_SOF1) { p->idx = 0U; p->state = GUI_ST_HEADER; } else if (byte == GUI_SOF0) { /* A5 A5 5A - тоже валидное начало, остаёмся здесь. */ p->stats.stray_bytes++; } else { p->stats.stray_bytes += 2U; p->state = GUI_ST_SOF0; } break; case GUI_ST_HEADER: /* Версию проверяем сразу на первом байте заголовка. Если ждать все шесть, ложная сигнатура успеет проглотить начало настоящего кадра и он будет потерян целиком. */ if ((p->idx == 0U) && (byte != GUI_PROTOCOL_VERSION)) { p->stats.version_errors++; p->state = (byte == GUI_SOF0) ? GUI_ST_SOF1 : GUI_ST_SOF0; break; } p->header[p->idx++] = byte; if (p->idx < 6U) { break; } p->size = (uint16_t)(((uint16_t)p->header[4] << 8) | p->header[5]); if (p->size > GUI_RX_PAYLOAD_MAX) { /* Либо мусор, либо кадр, который прибор обслужить не может. И то и другое - повод вернуться к поиску сигнатуры. */ p->stats.length_errors++; p->state = GUI_ST_SOF0; break; } p->idx = 0U; p->state = (p->size > 0U) ? GUI_ST_PAYLOAD : GUI_ST_CRC; break; case GUI_ST_PAYLOAD: p->payload[p->idx++] = byte; if (p->idx >= p->size) { p->idx = 0U; p->state = GUI_ST_CRC; } break; case GUI_ST_CRC: { p->crc_bytes[p->idx++] = byte; if (p->idx < GUI_CRC_SIZE) { break; } p->state = GUI_ST_SOF0; uint32_t received = ((uint32_t)p->crc_bytes[0]) | ((uint32_t)p->crc_bytes[1] << 8) | ((uint32_t)p->crc_bytes[2] << 16) | ((uint32_t)p->crc_bytes[3] << 24); /* CRC защищает header[0..5] и payload; считаем по частям, чтобы не собирать кадр целиком во втором буфере. */ uint32_t crc = 0xFFFFFFFFUL; for (uint8_t i = 0U; i < 6U; i++) { crc ^= p->header[i]; for (uint8_t bit = 0U; bit < 8U; bit++) { crc = (crc & 1UL) ? ((crc >> 1) ^ 0xEDB88320UL) : (crc >> 1); } } for (uint16_t i = 0U; i < p->size; i++) { crc ^= p->payload[i]; for (uint8_t bit = 0U; bit < 8U; bit++) { crc = (crc & 1UL) ? ((crc >> 1) ^ 0xEDB88320UL) : (crc >> 1); } } crc ^= 0xFFFFFFFFUL; if (crc != received) { p->stats.crc_errors++; return false; } out->type = p->header[1]; out->sequence = (uint16_t)(((uint16_t)p->header[2] << 8) | p->header[3]); out->size = p->size; out->payload = p->payload; p->stats.frames++; return true; } default: p->state = GUI_ST_SOF0; break; } return false; } size_t gui_parser_feed(gui_parser_t *p, const uint8_t *data, size_t len, gui_frame_cb_t cb, void *user) { gui_frame_t frame; size_t count = 0U; for (size_t i = 0U; i < len; i++) { if (gui_parser_push(p, data[i], &frame)) { count++; if (cb != NULL) { cb(&frame, user); } } } return count; } bool gui_link_init(gui_link_t *link, const pcan_io_t *io, gui_frame_cb_t on_frame, void *user) { if ((link == NULL) || (io == NULL) || (io->write == NULL)) { return false; } link->io = *io; link->on_frame = on_frame; link->user = user; link->tx_frames = 0U; link->tx_dropped = 0U; gui_parser_init(&link->parser); return true; } bool gui_link_can_send(const gui_link_t *link, uint16_t size) { if (link->io.tx_space == NULL) { return true; } return link->io.tx_space(link->io.ctx) >= ((size_t)GUI_HEADER_SIZE + size + GUI_CRC_SIZE); } bool gui_link_send(gui_link_t *link, uint8_t type, uint16_t sequence, const uint8_t *payload, uint16_t size) { size_t n = gui_frame_encode(type, sequence, payload, size, link->tx_buf, sizeof link->tx_buf); if (n == 0U) { link->tx_dropped++; return false; } if (link->io.write(link->io.ctx, link->tx_buf, n) != n) { link->tx_dropped++; return false; } link->tx_frames++; return true; } bool gui_link_nack(gui_link_t *link, uint16_t sequence, uint16_t result) { uint8_t payload[2]; gui_put_u16(payload, result); return gui_link_send(link, GUI_MSG_NACK, sequence, payload, sizeof payload); } size_t gui_link_feed(gui_link_t *link, const uint8_t *data, size_t len) { return gui_parser_feed(&link->parser, data, len, link->on_frame, link->user); }