Files
modbus-rtu/main/config_store.c
2026-04-14 15:19:00 -05:00

407 lines
9.3 KiB
C

#include "config_store.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
static void trim_whitespace(char *s)
{
char *start;
char *end;
if (s == NULL || *s == '\0')
return;
start = s;
while (*start != '\0' && isspace((unsigned char)*start))
start++;
if (start != s)
memmove(s, start, strlen(start) + 1U);
if (*s == '\0')
return;
end = s + strlen(s) - 1;
while (end >= s && isspace((unsigned char)*end))
{
*end = '\0';
end--;
}
}
static bool str_ieq(const char *a, const char *b)
{
unsigned char ca, cb;
if (a == NULL || b == NULL)
return false;
while (*a != '\0' && *b != '\0')
{
ca = (unsigned char)tolower((unsigned char)*a);
cb = (unsigned char)tolower((unsigned char)*b);
if (ca != cb)
return false;
a++;
b++;
}
return (*a == '\0' && *b == '\0');
}
static bool parse_bool(const char *s, bool *out)
{
if (s == NULL || out == NULL)
return false;
if (str_ieq(s, "true") || str_ieq(s, "yes") || strcmp(s, "1") == 0)
{
*out = true;
return true;
}
if (str_ieq(s, "false") || str_ieq(s, "no") || strcmp(s, "0") == 0)
{
*out = false;
return true;
}
return false;
}
static bool parse_u32(const char *s, uint32_t *out)
{
char *endptr = NULL;
unsigned long v;
if (s == NULL || out == NULL || *s == '\0')
return false;
v = strtoul(s, &endptr, 10);
if (endptr == NULL || *endptr != '\0')
return false;
*out = (uint32_t)v;
return true;
}
static bool parse_i32(const char *s, int *out)
{
char *endptr = NULL;
long v;
if (s == NULL || out == NULL || *s == '\0')
return false;
v = strtol(s, &endptr, 10);
if (endptr == NULL || *endptr != '\0')
return false;
*out = (int)v;
return true;
}
static void copy_str(char *dst, size_t dst_size, const char *src)
{
if (dst == NULL || dst_size == 0U)
return;
if (src == NULL)
src = "";
strncpy(dst, src, dst_size - 1U);
dst[dst_size - 1U] = '\0';
}
static int parse_device_section_index(const char *section)
{
unsigned long idx;
char *endptr = NULL;
if (section == NULL)
return -1;
if (strlen(section) < 8U)
return -1;
if (tolower((unsigned char)section[0]) != 'd' ||
tolower((unsigned char)section[1]) != 'e' ||
tolower((unsigned char)section[2]) != 'v' ||
tolower((unsigned char)section[3]) != 'i' ||
tolower((unsigned char)section[4]) != 'c' ||
tolower((unsigned char)section[5]) != 'e')
{
return -1;
}
if (section[6] != ' ')
return -1;
idx = strtoul(&section[7], &endptr, 10);
if (endptr == NULL || *endptr != '\0')
return -1;
if (idx == 0UL || idx > MAX_VIRTUAL_DEVICES)
return -1;
return (int)(idx - 1UL);
}
static bool parse_parity(const char *s, modbus_rtu_parity_t *out)
{
if (s == NULL || out == NULL)
return false;
if (str_ieq(s, "none"))
{
*out = MB_RTU_PARITY_NONE;
return true;
}
if (str_ieq(s, "even"))
{
*out = MB_RTU_PARITY_EVEN;
return true;
}
if (str_ieq(s, "odd"))
{
*out = MB_RTU_PARITY_ODD;
return true;
}
return false;
}
static void set_device_defaults(virtual_device_settings_t *dev, uint8_t index)
{
char default_name[DEVICE_NAME_MAX_LEN];
if (dev == NULL)
return;
memset(dev, 0, sizeof(*dev));
dev->enabled = false;
dev->unit_id = (uint8_t)(index + 1U);
snprintf(default_name, sizeof(default_name), "Device_%u", (unsigned)(index + 1U));
copy_str(dev->name, sizeof(dev->name), default_name);
dev->csv[0] = '\0';
}
void config_store_set_defaults(device_config_t *cfg)
{
size_t i;
if (cfg == NULL)
return;
memset(cfg, 0, sizeof(*cfg));
cfg->rtu.baud_rate = 19200U;
cfg->rtu.parity = MB_RTU_PARITY_NONE;
cfg->rtu.stop_bits = 1U;
cfg->rtu.uart_num = 1U;
cfg->rtu.tx_pin = 17;
cfg->rtu.rx_pin = 16;
cfg->rtu.de_pin = -1; /* shield AUTO mode */
cfg->modbus.device_count = 1U;
for (i = 0; i < MAX_VIRTUAL_DEVICES; i++)
set_device_defaults(&cfg->devices[i], (uint8_t)i);
}
bool config_store_load(const char *filename, device_config_t *cfg)
{
FILE *fp;
char line[256];
char section[32];
if (filename == NULL || cfg == NULL)
return false;
config_store_set_defaults(cfg);
memset(section, 0, sizeof(section));
fp = fopen(filename, "r");
if (fp == NULL)
return false;
while (fgets(line, sizeof(line), fp) != NULL)
{
char *eq;
char *key;
char *value;
line[strcspn(line, "\r\n")] = '\0';
trim_whitespace(line);
if (line[0] == '\0')
continue;
if (line[0] == ';' || line[0] == '#')
continue;
if (line[0] == '[')
{
size_t len = strlen(line);
if (len >= 3U && line[len - 1U] == ']')
{
line[len - 1U] = '\0';
copy_str(section, sizeof(section), &line[1]);
trim_whitespace(section);
}
continue;
}
eq = strchr(line, '=');
if (eq == NULL)
continue;
*eq = '\0';
key = line;
value = eq + 1;
trim_whitespace(key);
trim_whitespace(value);
if (str_ieq(section, "rtu"))
{
uint32_t v;
int iv;
modbus_rtu_parity_t parity;
if (str_ieq(key, "baud_rate"))
{
if (parse_u32(value, &v) && v > 0U)
cfg->rtu.baud_rate = v;
}
else if (str_ieq(key, "parity"))
{
if (parse_parity(value, &parity))
cfg->rtu.parity = parity;
}
else if (str_ieq(key, "stop_bits"))
{
if (parse_u32(value, &v) && (v == 1U || v == 2U))
cfg->rtu.stop_bits = (uint8_t)v;
}
else if (str_ieq(key, "uart_num"))
{
if (parse_u32(value, &v) && v <= 2U)
cfg->rtu.uart_num = (uint8_t)v;
}
else if (str_ieq(key, "tx_pin"))
{
if (parse_i32(value, &iv))
cfg->rtu.tx_pin = iv;
}
else if (str_ieq(key, "rx_pin"))
{
if (parse_i32(value, &iv))
cfg->rtu.rx_pin = iv;
}
else if (str_ieq(key, "de_pin"))
{
if (parse_i32(value, &iv))
cfg->rtu.de_pin = iv;
}
}
else if (str_ieq(section, "modbus"))
{
uint32_t v;
if (str_ieq(key, "device_count"))
{
if (parse_u32(value, &v) && v > 0U)
{
if (v > MAX_VIRTUAL_DEVICES)
cfg->modbus.device_count = MAX_VIRTUAL_DEVICES;
else
cfg->modbus.device_count = (uint8_t)v;
}
}
}
else
{
int dev_index = parse_device_section_index(section);
if (dev_index >= 0)
{
virtual_device_settings_t *dev = &cfg->devices[dev_index];
uint32_t v;
if (str_ieq(key, "enabled"))
{
bool b;
if (parse_bool(value, &b))
dev->enabled = b;
}
else if (str_ieq(key, "unit_id"))
{
if (parse_u32(value, &v) && v > 0U && v <= 247U)
dev->unit_id = (uint8_t)v;
}
else if (str_ieq(key, "name"))
{
copy_str(dev->name, sizeof(dev->name), value);
}
else if (str_ieq(key, "csv"))
{
copy_str(dev->csv, sizeof(dev->csv), value);
}
}
}
}
fclose(fp);
if (cfg->rtu.baud_rate == 0U)
return false;
if (cfg->rtu.stop_bits != 1U && cfg->rtu.stop_bits != 2U)
return false;
if (cfg->modbus.device_count == 0U || cfg->modbus.device_count > MAX_VIRTUAL_DEVICES)
return false;
for (uint8_t i = 0; i < cfg->modbus.device_count; i++)
{
uint8_t j;
virtual_device_settings_t *dev = &cfg->devices[i];
if (!dev->enabled)
continue;
if (dev->unit_id == 0U || dev->unit_id > 247U)
return false;
if (dev->csv[0] == '\0')
return false;
if (dev->name[0] == '\0')
{
char default_name[DEVICE_NAME_MAX_LEN];
snprintf(default_name, sizeof(default_name), "Device_%u", (unsigned)(i + 1U));
copy_str(dev->name, sizeof(dev->name), default_name);
}
for (j = (uint8_t)(i + 1U); j < cfg->modbus.device_count; j++)
{
if (!cfg->devices[j].enabled)
continue;
if (dev->unit_id == cfg->devices[j].unit_id)
return false;
}
}
return true;
}