#include "gui_catalog.h" static size_t name_len(const char *name) { size_t n = 0U; if (name == NULL) { return 0U; } while ((name[n] != '\0') && (n <= GUI_NAME_SIZE)) { n++; } return n; } static uint8_t object_registers(uint8_t type) { return ((type == GUI_OBJ_U32) || (type == GUI_OBJ_I32)) ? 2U : 1U; } bool gui_catalog_validate(const gui_catalog_t *catalog, const pcan_gas_map_t *map) { if ((catalog == NULL) || ((catalog->count != 0U) && (catalog->items == NULL))) { return false; } for (uint16_t i = 0U; i < catalog->count; i++) { const gui_object_t *item = &catalog->items[i]; if (name_len(item->name) > GUI_NAME_SIZE) { return false; } if (item->type > GUI_OBJ_BITS) { return false; } if (map != NULL) { uint8_t words = object_registers(item->type); for (uint8_t w = 0U; w < words; w++) { uint32_t addr = (uint32_t)item->address + w; if (addr > 0xFFFFUL) { return false; } if (pcan_gas_find(map, (uint16_t)addr) == NULL) { return false; } } } } return true; } size_t gui_catalog_encode(const gui_catalog_t *catalog, uint16_t start_index, uint16_t max_count, uint8_t *out, size_t out_size) { if ((catalog == NULL) || (out == NULL) || (start_index >= catalog->count)) { return 0U; } uint16_t available = (uint16_t)(catalog->count - start_index); uint16_t count = ((max_count == 0U) || (max_count > available)) ? available : max_count; if (count > GUI_ENTRIES_PER_FRAME) { count = (uint16_t)GUI_ENTRIES_PER_FRAME; } size_t total = (size_t)GUI_CATALOG_HEADER + ((size_t)count * GUI_ENTRY_SIZE); if (out_size < total) { return 0U; } gui_put_u16(&out[0], catalog->count); gui_put_u16(&out[2], start_index); gui_put_u16(&out[4], count); for (uint16_t i = 0U; i < count; i++) { const gui_object_t *item = &catalog->items[start_index + i]; uint8_t *dst = &out[GUI_CATALOG_HEADER + ((size_t)i * GUI_ENTRY_SIZE)]; gui_put_u16(&dst[0], item->address); dst[2] = item->type; dst[3] = item->flags; dst[4] = (uint8_t)item->scale_pow10; dst[5] = item->unit; dst[6] = 0U; dst[7] = 0U; size_t n = name_len(item->name); if (n > GUI_NAME_SIZE) { n = GUI_NAME_SIZE; } for (size_t c = 0U; c < n; c++) { dst[8U + c] = (uint8_t)item->name[c]; } for (size_t c = n; c < GUI_NAME_SIZE; c++) { dst[8U + c] = 0U; } } return total; } void gui_watch_init(gui_watch_t *watch) { watch->period_ms = 0U; watch->count = 0U; watch->next_ms = 0U; watch->sent = 0U; watch->skipped = 0U; } uint16_t gui_watch_apply(gui_watch_t *watch, const pcan_gas_map_t *map, const uint8_t *payload, uint16_t size) { if (size < 4U) { return 0xFFFFU; } uint16_t period = gui_get_u16(&payload[0]); uint16_t requested = gui_get_u16(&payload[2]); if (size != (uint16_t)(4U + (requested * 2U))) { return 0xFFFFU; } if (requested > GUI_WATCH_MAX) { return 0xFFFFU; } uint16_t accepted = 0U; for (uint16_t i = 0U; i < requested; i++) { uint16_t address = gui_get_u16(&payload[4U + (i * 2U)]); /* Адрес вне карты молча пропускаем: иначе одна опечатка в GUI отменяла бы всю подписку. */ if ((map != NULL) && (pcan_gas_find(map, address) == NULL)) { continue; } watch->address[accepted++] = address; } watch->count = accepted; watch->period_ms = (accepted == 0U) ? 0U : period; watch->next_ms = 0U; return accepted; } size_t gui_watch_encode_ack(const gui_watch_t *watch, uint8_t *out, size_t out_size) { if (out_size < 4U) { return 0U; } gui_put_u16(&out[0], watch->period_ms); gui_put_u16(&out[2], watch->count); return 4U; } bool gui_watch_due(const gui_watch_t *watch, uint32_t now_ms) { if ((watch->period_ms == 0U) || (watch->count == 0U)) { return false; } /* Разность беззнаковая: переполнение счётчика миллисекунд обрабатывается само собой, сравнение с абсолютным временем - нет. */ return (uint32_t)(now_ms - watch->next_ms) < 0x80000000UL; } size_t gui_watch_encode_data(const gui_watch_t *watch, const pcan_gas_map_t *map, uint32_t timestamp_ms, uint8_t *out, size_t out_size) { if ((watch->count == 0U) || (out == NULL)) { return 0U; } size_t total = 6U + ((size_t)watch->count * 2U); if (out_size < total) { return 0U; } gui_put_u32(&out[0], timestamp_ms); gui_put_u16(&out[4], watch->count); for (uint16_t i = 0U; i < watch->count; i++) { uint16_t value = 0U; /* Регистр мог исчезнуть между подпиской и опросом - тогда 0, а не мусор из стека. */ (void)pcan_gas_read(map, watch->address[i], &value); gui_put_u16(&out[6U + ((size_t)i * 2U)], value); } return total; } void gui_watch_advance(gui_watch_t *watch, uint32_t now_ms) { if (watch->period_ms == 0U) { return; } uint32_t next = watch->next_ms + watch->period_ms; /* Если отстали больше чем на такт (линия была занята), не пытаемся нагнать пачкой пакетов - идём от текущего момента. */ if ((uint32_t)(now_ms - next) >= 0x80000000UL) { watch->next_ms = next; } else { watch->next_ms = now_ms + watch->period_ms; } watch->sent++; }