RTE RBC 2oo2 Reference Application 0.1.0 build 0006685
SIL 4 2oo2 Dual-Site Fault-Tolerant ERTMS Radio Block Centre Reference Architecture
Loading...
Searching...
No Matches
ab_gp_config.c
Go to the documentation of this file.
1
8
9#include "ab_gp_config.h"
10
11#include <ctype.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include "rte/oal/memory/rte_mem_util.h"
18
19#define MAX_LINE_LEN 256U
20
21static void trim_whitespace(char *str)
22{
23 char *end;
24 char *start = str;
25
26 if (str == NULL)
27 {
28 return;
29 }
30
31 while ((*start != '\0') && (isspace((unsigned char)*start) != 0))
32 {
33 start++;
34 }
35
36 if (start != str)
37 {
38 (void)memmove(str, start, strlen(start) + 1U);
39 }
40
41 if (*str == '\0')
42 {
43 return;
44 }
45
46 end = str + strlen(str) - 1U;
47 while ((end >= str) && (isspace((unsigned char)*end) != 0))
48 {
49 *end = '\0';
50 end--;
51 }
52}
53
55{
56 uint8_t i;
57
58 for (i = 0U; i < cfg->channel_count; i++)
59 {
60 if (strcmp(cfg->channels[i].name, name) == 0)
61 {
62 return &cfg->channels[i];
63 }
64 }
65
67 {
69 cfg->channel_count++;
70 rte_mem_set(ch, 0, sizeof(*ch));
71 (void)snprintf(ch->name, sizeof(ch->name), "%.*s", (int)(sizeof(ch->name) - 1U), name);
72 ch->is_configured = true;
73 return ch;
74 }
75
76 return NULL;
77}
78
79rte_status_t ab_gp_config_init_defaults(rte_example_site_t site, bool is_role_a, ab_gp_config_t *out_cfg)
80{
81 if (out_cfg == NULL)
82 {
83 return RTE_STATUS_INVALID_PARAM;
84 }
85
86 rte_mem_set(out_cfg, 0, sizeof(*out_cfg));
87 out_cfg->site = site;
88 out_cfg->is_role_a = is_role_a;
89
90 /* Default timer configurations */
103
104 return RTE_STATUS_OK;
105}
106
107/* One `[timers]` section key=value pair, extracted from ab_gp_config_load_file()'s own former
108 * inline dispatch. Unknown keys are silently ignored - unchanged from before this extraction. */
109static void apply_timer_setting(ab_gp_config_timers_t *timers, const char *key, const char *val)
110{
111 uint32_t num_val = (uint32_t)strtoul(val, NULL, 10);
112
113 if (strcmp(key, "cycle_period_ms") == 0)
114 {
115 timers->cycle_period_ms = num_val;
116 }
117 else if (strcmp(key, "watchdog_timeout_ms") == 0)
118 {
119 timers->watchdog_timeout_ms = num_val;
120 }
121 else if (strcmp(key, "checkpoint_max_delay_ms") == 0)
122 {
123 timers->checkpoint_max_delay_ms = num_val;
124 }
125 else if (strcmp(key, "checkpoint_relaxed_max_delay_ms") == 0)
126 {
127 timers->checkpoint_relaxed_max_delay_ms = num_val;
128 }
129 else if (strcmp(key, "checkpoint_relax_duration_ms") == 0)
130 {
131 timers->checkpoint_relax_duration_ms = num_val;
132 }
133 else if (strcmp(key, "checkpoint_reenable_grace_ms") == 0)
134 {
135 timers->checkpoint_reenable_grace_ms = num_val;
136 }
137 else if (strcmp(key, "dual_transfer_timeout_ms") == 0)
138 {
139 timers->dual_transfer_timeout_ms = num_val;
140 }
141 else if (strcmp(key, "channel_down_reboot_ms") == 0)
142 {
143 timers->channel_down_reboot_ms = num_val;
144 }
145 else if (strcmp(key, "link_timeout_ms") == 0)
146 {
147 timers->link_timeout_ms = num_val;
148 }
149 else if (strcmp(key, "connect_timeout_ms") == 0)
150 {
151 timers->connect_timeout_ms = num_val;
152 }
153 else if (strcmp(key, "reconnect_attempt_timeout_ms") == 0)
154 {
155 timers->reconnect_attempt_timeout_ms = num_val;
156 }
157 else if (strcmp(key, "neg_ack_timeout_ms") == 0)
158 {
159 timers->neg_ack_timeout_ms = num_val;
160 }
161 else
162 {
163 /* Ignore unknown timer key */
164 }
165}
166
167/* One `[channel.<name>]` section key=value pair, extracted the same way as
168 * apply_timer_setting() above. Unknown keys are silently ignored - unchanged from before this
169 * extraction. */
170static void apply_channel_setting(ab_gp_config_channel_t *ch, const char *key, const char *val)
171{
172 if (strcmp(key, "id") == 0)
173 {
174 ch->id = (uint32_t)strtoul(val, NULL, 10);
175 }
176 else if (strcmp(key, "role") == 0)
177 {
178 if ((strcmp(val, "LISTEN") == 0) || (strcmp(val, "listen") == 0))
179 {
180 ch->role = RTE_NETLINK_ROLE_LISTEN;
181 }
182 else
183 {
184 ch->role = RTE_NETLINK_ROLE_CONNECT;
185 }
186 }
187 else if (strcmp(key, "host") == 0)
188 {
189 (void)snprintf(ch->host, sizeof(ch->host), "%.*s", (int)(sizeof(ch->host) - 1U), val);
190 }
191 else if (strcmp(key, "port") == 0)
192 {
193 ch->port = (uint16_t)strtoul(val, NULL, 10);
194 }
195 else if (strcmp(key, "message_size") == 0)
196 {
197 ch->message_size = (uint32_t)strtoul(val, NULL, 10);
198 }
199 else if (strcmp(key, "connect_timeout_ms") == 0)
200 {
201 ch->connect_timeout_ms = (uint32_t)strtoul(val, NULL, 10);
202 }
203 else
204 {
205 /* Ignore unknown channel key */
206 }
207}
208
209/* A top-level (no `[section]` yet open, and not inside a `[channel.*]` section) key=value pair,
210 * extracted the same way as apply_timer_setting() above. Unknown keys are silently ignored -
211 * unchanged from before this extraction. */
212static void apply_global_setting(ab_gp_config_t *cfg, const char *key, const char *val)
213{
214 if (strcmp(key, "site") == 0)
215 {
216 (void)site_config_parse(val, &cfg->site);
217 }
218 else if (strcmp(key, "role") == 0)
219 {
220 cfg->is_role_a = ((strcmp(val, "A") == 0) || (strcmp(val, "a") == 0));
221 }
222 else
223 {
224 /* Ignore other top-level keys */
225 }
226}
227
228rte_status_t ab_gp_config_load_file(const char *filepath, ab_gp_config_t *out_cfg)
229{
230 FILE *fp;
231 char line[MAX_LINE_LEN];
232 char current_section[64] = "";
233 ab_gp_config_channel_t *current_ch = NULL;
234
235 if ((filepath == NULL) || (out_cfg == NULL))
236 {
237 return RTE_STATUS_INVALID_PARAM;
238 }
239
240 fp = fopen(filepath, "r");
241 if (fp == NULL)
242 {
243 return RTE_STATUS_RESOURCE_EXHAUSTED;
244 }
245
246 while (fgets(line, (int)sizeof(line), fp) != NULL)
247 {
248 char *comment;
249 char *eq;
250 char *key;
251 char *val;
252
253 /* Strip comments starting with '#' or ';' */
254 comment = strpbrk(line, "#;");
255 if (comment != NULL)
256 {
257 *comment = '\0';
258 }
259
260 trim_whitespace(line);
261 if (line[0] == '\0')
262 {
263 continue;
264 }
265
266 /* Section header: [section] */
267 if ((line[0] == '[') && (line[strlen(line) - 1U] == ']'))
268 {
269 line[strlen(line) - 1U] = '\0';
270 /* Over-long section names are cut to the buffer on purpose (they match no known section). */
271 (void)snprintf(current_section, sizeof(current_section), "%.*s", (int)(sizeof(current_section) - 1U), &line[1]);
272 trim_whitespace(current_section);
273
274 if (strncmp(current_section, "channel.", 8) == 0)
275 {
276 current_ch = get_or_create_channel(out_cfg, &current_section[8]);
277 }
278 else
279 {
280 current_ch = NULL;
281 }
282 continue;
283 }
284
285 /* Key = Value */
286 eq = strchr(line, '=');
287 if (eq == NULL)
288 {
289 continue;
290 }
291
292 *eq = '\0';
293 key = line;
294 val = eq + 1;
295 trim_whitespace(key);
296 trim_whitespace(val);
297
298 if (strcmp(current_section, "timers") == 0)
299 {
300 apply_timer_setting(&out_cfg->timers, key, val);
301 }
302 else if (current_ch != NULL)
303 {
304 apply_channel_setting(current_ch, key, val);
305 }
306 else
307 {
308 apply_global_setting(out_cfg, key, val);
309 }
310 }
311
312 (void)fclose(fp);
313 return RTE_STATUS_OK;
314}
315
316rte_status_t ab_gp_config_find_channel(const ab_gp_config_t *cfg, const char *channel_name,
317 rte_netlink_config_t *out_netlink_cfg)
318{
319 uint8_t i;
320
321 if ((cfg == NULL) || (channel_name == NULL) || (out_netlink_cfg == NULL))
322 {
323 return RTE_STATUS_INVALID_PARAM;
324 }
325
326 for (i = 0U; i < cfg->channel_count; i++)
327 {
328 const ab_gp_config_channel_t *ch = &cfg->channels[i];
329 if (ch->is_configured && (strcmp(ch->name, channel_name) == 0))
330 {
331 rte_mem_set(out_netlink_cfg, 0, sizeof(*out_netlink_cfg));
332 out_netlink_cfg->role = ch->role;
333 out_netlink_cfg->host = (ch->role == RTE_NETLINK_ROLE_CONNECT && ch->host[0] != '\0') ? ch->host : NULL;
334 out_netlink_cfg->port = ch->port;
335 out_netlink_cfg->message_size = ch->message_size;
336 out_netlink_cfg->connect_timeout_ms = (ch->connect_timeout_ms > 0U) ? ch->connect_timeout_ms
338 return RTE_STATUS_OK;
339 }
340 }
341
342 return RTE_STATUS_RESOURCE_EXHAUSTED;
343}
344
345rte_status_t ab_gp_config_find_channel_by_id(const ab_gp_config_t *cfg, uint32_t channel_id,
346 rte_netlink_config_t *out_netlink_cfg)
347{
348 uint8_t i;
349
350 if ((cfg == NULL) || (channel_id == 0U) || (out_netlink_cfg == NULL))
351 {
352 return RTE_STATUS_INVALID_PARAM;
353 }
354
355 for (i = 0U; i < cfg->channel_count; i++)
356 {
357 const ab_gp_config_channel_t *ch = &cfg->channels[i];
358 if (ch->is_configured && (ch->id == channel_id))
359 {
360 rte_mem_set(out_netlink_cfg, 0, sizeof(*out_netlink_cfg));
361 out_netlink_cfg->role = ch->role;
362 out_netlink_cfg->host = (ch->role == RTE_NETLINK_ROLE_CONNECT && ch->host[0] != '\0') ? ch->host : NULL;
363 out_netlink_cfg->port = ch->port;
364 out_netlink_cfg->message_size = ch->message_size;
365 out_netlink_cfg->connect_timeout_ms = (ch->connect_timeout_ms > 0U) ? ch->connect_timeout_ms
367 return RTE_STATUS_OK;
368 }
369 }
370
371 return RTE_STATUS_RESOURCE_EXHAUSTED;
372}
373
static ab_gp_config_channel_t * get_or_create_channel(ab_gp_config_t *cfg, const char *name)
#define MAX_LINE_LEN
static void apply_channel_setting(ab_gp_config_channel_t *ch, const char *key, const char *val)
static void apply_global_setting(ab_gp_config_t *cfg, const char *key, const char *val)
rte_status_t ab_gp_config_find_channel_by_id(const ab_gp_config_t *cfg, uint32_t channel_id, rte_netlink_config_t *out_netlink_cfg)
Resolves netlink configuration for a channel by numeric ID from the parsed config.
rte_status_t ab_gp_config_load_file(const char *filepath, ab_gp_config_t *out_cfg)
Parses an INI-style configuration file into the static ab_gp_config_t structure.
rte_status_t ab_gp_config_init_defaults(rte_example_site_t site, bool is_role_a, ab_gp_config_t *out_cfg)
Initializes default configuration in memory based on site and role.
static void trim_whitespace(char *str)
static void apply_timer_setting(ab_gp_config_timers_t *timers, const char *key, const char *val)
rte_status_t ab_gp_config_find_channel(const ab_gp_config_t *cfg, const char *channel_name, rte_netlink_config_t *out_netlink_cfg)
Resolves netlink configuration for a named channel from the parsed config.
Zero-dependency, statically-allocated configuration parser for Role A/B.
#define AB_GP_CONFIG_MAX_CHANNELS
#define RTE_EXAMPLE_AB_DUAL_TRANSFER_TIMEOUT_MS
#define RTE_EXAMPLE_RECONNECT_ATTEMPT_TIMEOUT_MS
#define RTE_EXAMPLE_NEG_ACK_TIMEOUT_MS
#define RTE_EXAMPLE_AB_CHECKPOINT_MAX_DELAY_MS
#define RTE_EXAMPLE_AB_WATCHDOG_TIMEOUT_MS
#define RTE_EXAMPLE_CHANNEL_DOWN_REBOOT_MS
#define RTE_EXAMPLE_CHECKPOINT_REENABLE_GRACE_MS
#define RTE_EXAMPLE_CHECKPOINT_RELAX_DURATION_MS
#define RTE_EXAMPLE_AB_CHECKPOINT_RELAXED_MAX_DELAY_MS
#define RTE_EXAMPLE_LINK_TIMEOUT_MS
#define RTE_EXAMPLE_CONNECT_TIMEOUT_MS
#define RTE_EXAMPLE_AB_CYCLE_PERIOD_MS
bool site_config_parse(const char *arg, rte_example_site_t *out_site)
Parses a site name argument ("WEST"/"EAST", case-insensitive).
Definition site_config.c:33
rte_example_site_t
Definition site_config.h:20
char host[AB_GP_CONFIG_HOST_MAX_LEN]
rte_netlink_role_t role
char name[AB_GP_CONFIG_NAME_MAX_LEN]
uint8_t channel_count
ab_gp_config_channel_t channels[AB_GP_CONFIG_MAX_CHANNELS]
ab_gp_config_timers_t timers
rte_example_site_t site
uint32_t checkpoint_relaxed_max_delay_ms
uint32_t dual_transfer_timeout_ms
uint32_t checkpoint_max_delay_ms
uint32_t channel_down_reboot_ms
uint32_t checkpoint_relax_duration_ms
uint32_t reconnect_attempt_timeout_ms
uint32_t checkpoint_reenable_grace_ms