Files
templates/c/set-protocol/src/gui_catalog.c

196 lines
6.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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++;
}