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
gateway_c_common.c
Go to the documentation of this file.
1
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "rte/app/appmanager/rte_appmanager.h"
15#include "rte/core/rte_core_version.h"
16#include "rte/oal/log/rte_log.h"
17#include "rte/oal/memory/rte_mem_util.h"
18#include "rte/posix_osadapter/rte_posix_osadapter_channel_service.h"
19#include "rte/utils/safestate/rte_safestate.h"
20
24#include "gateway_c_common.h"
26#include "gateway_c_config.h"
27#include "gateway_c_types.h"
28#include "gateway_c_util.h"
29#include "rte_rbc2oo2_version.h"
30
36
37static rte_status_t gateway_c_shutdown(void *context);
38
39static rte_status_t resolve_channel_c(const char *name, rte_netlink_config_t *config, void *context)
40{
42
43 if ((name == NULL) || (config == NULL) || (ctx == NULL))
44 {
45 return RTE_STATUS_INVALID_PARAM;
46 }
47
48 if (gateway_c_config_find_channel(&ctx->config, name, config) == RTE_STATUS_OK)
49 {
50 /* The JSON lists every c-broker-* channel once, as the status service's LISTEN side. A train/il/ctc
51 * gateway must instead DIAL its own broker channel (see the fallback below); without this override both
52 * sides listened, no gateway ever reached the broker and the SUMMARY showed every gateway and sim down. */
53 if ((g_svc != NULL) && (g_svc->broker_channel != NULL) && (strcmp(name, g_svc->broker_channel) == 0))
54 {
55 config->role = RTE_NETLINK_ROLE_CONNECT;
56 config->host = "127.0.0.1";
57 }
58 return RTE_STATUS_OK;
59 }
60
61 rte_mem_set(config, 0, sizeof(*config));
62 config->role = RTE_NETLINK_ROLE_LISTEN;
63 config->host = NULL;
64 config->message_size = RBC_ENVELOPE_WIRE_SIZE;
65 config->connect_timeout_ms = ctx->config.timers.reconnect_attempt_timeout_ms;
66
67 /* ADR-036: one link per {kind} - the Train and IL sim connections
68 * carry every train, demuxed by the envelope's train_id (in A/B, not
69 * here). No per-instance -%u suffix any more.
70 * RBC_MSG_ERTMS_ENVELOPE (rbc_wire_types.h): the Train sim link's
71 * three channels (this LISTEN one plus its two DIAL relay legs to
72 * A/B below) all now carry the wider RBC_TRAIN_ENVELOPE_WIRE_SIZE
73 * frame, not the default RBC_ENVELOPE_WIRE_SIZE set above - same
74 * override pattern the UTIL/broker channels already use for
75 * RBC_UTIL_FRAME_SIZE. gateway_c_train_handler.c is a pure byte-
76 * blind relay either way (never inspects message content), but the
77 * transport's own per-channel message_size still has to match what
78 * actually gets sent on it. */
79 if (strcmp(name, "c-sim-train") == 0)
80 {
81 config->message_size = RBC_TRAIN_ENVELOPE_WIRE_SIZE;
82 config->port = site_config_c_port_for_train(ctx->site);
83 return RTE_STATUS_OK;
84 }
85 if (strcmp(name, "c-relay-train-a") == 0)
86 {
87 config->message_size = RBC_TRAIN_ENVELOPE_WIRE_SIZE;
89 return RTE_STATUS_OK;
90 }
91 if (strcmp(name, "c-relay-train-b") == 0)
92 {
93 config->message_size = RBC_TRAIN_ENVELOPE_WIRE_SIZE;
95 return RTE_STATUS_OK;
96 }
97
98 if (strcmp(name, "c-sim-il") == 0)
99 {
100 config->port = site_config_c_port_for_il(ctx->site);
101 return RTE_STATUS_OK;
102 }
103 if (strcmp(name, "c-relay-il-a") == 0)
104 {
106 return RTE_STATUS_OK;
107 }
108 if (strcmp(name, "c-relay-il-b") == 0)
109 {
111 return RTE_STATUS_OK;
112 }
113
114 if (strcmp(name, "c-sim-ctc") == 0)
115 {
116 config->port = site_config_c_port_for_ctc(ctx->site);
117 return RTE_STATUS_OK;
118 }
119 if (strcmp(name, "c-relay-ctc-a") == 0)
120 {
122 return RTE_STATUS_OK;
123 }
124 if (strcmp(name, "c-relay-ctc-b") == 0)
125 {
127 return RTE_STATUS_OK;
128 }
129
130 /* ADR-038: the status/broker service's LISTEN links - A's/B's util
131 * side channel + one broker port per local gateway service. The util
132 * links carry rbc_util_frame_t (256 B), not rbc_envelope_t. */
133 if ((strcmp(name, "c-relay-util-a") == 0) || (strcmp(name, "c-relay-util-b") == 0)
134 || (strcmp(name, "c-broker-train") == 0) || (strcmp(name, "c-broker-il") == 0)
135 || (strcmp(name, "c-broker-ctc") == 0))
136 {
137 config->message_size = RBC_UTIL_FRAME_SIZE;
138 if (strcmp(name, "c-relay-util-a") == 0)
139 {
141 }
142 else if (strcmp(name, "c-relay-util-b") == 0)
143 {
145 }
146 else if (strcmp(name, "c-broker-train") == 0)
147 {
149 }
150 else if (strcmp(name, "c-broker-il") == 0)
151 {
153 }
154 else
155 {
157 }
158 /* ADR-038: the status service LISTENs on every c-broker-* (default
159 * role above); a train/il/ctc gateway DIALS the one broker channel
160 * named in its gateway_c_service_def_t. All Role C services share
161 * one c-<site> container, so the broker link is pure loopback. */
162 if ((g_svc != NULL) && (g_svc->broker_channel != NULL)
163 && (strcmp(name, g_svc->broker_channel) == 0))
164 {
165 config->role = RTE_NETLINK_ROLE_CONNECT;
166 config->host = "127.0.0.1";
167 }
168 return RTE_STATUS_OK;
169 }
170
171 return RTE_STATUS_INVALID_PARAM;
172}
173
174static void cycle_tick_callback(rte_timer_handle_t handle, void *user_ctx)
175{
176 gateway_c_context_t *ctx = (gateway_c_context_t *)user_ctx;
177 (void)handle;
178 ctx->cycle_ticks++;
179}
180
181static rte_status_t gateway_c_init(void *context)
182{
184 rte_status_t status;
185 rte_timer_config_t timer_cfg;
186
187 RTE_EXAMPLE_LOG("[GW-%s %s] starting\n", g_svc->log_tag, site_config_name(ctx->site));
188 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(ctx->site), 0U, g_svc->log_tag, "-", "LIFECYCLE",
189 "gateway service starting", NULL);
190
191 ctx->stop_requested = false;
192
193 (void)gateway_c_config_init_defaults(ctx->site, &ctx->config);
194 {
195 const char *env_cfg = getenv("RTE_CONFIG_FILE");
196 char default_cfg_path[128];
197 const char *site_str = (ctx->site == RTE_EXAMPLE_SITE_WEST) ? "west" : "east";
198
199 if (env_cfg != NULL)
200 {
201 (void)gateway_c_config_load_file(env_cfg, &ctx->config);
202 }
203 else
204 {
205 (void)snprintf(default_cfg_path, sizeof(default_cfg_path), "etc/config/c_%s.json", site_str);
206 if (gateway_c_config_load_file(default_cfg_path, &ctx->config) != RTE_STATUS_OK)
207 {
208 (void)snprintf(default_cfg_path, sizeof(default_cfg_path), "/etc/sapi/c_%s.json", site_str);
209 (void)gateway_c_config_load_file(default_cfg_path, &ctx->config);
210 }
211 }
212 }
213
214 status = rte_posix_osadapter_channel_service_register_resolver(resolve_channel_c, ctx);
215 if (status != RTE_STATUS_OK)
216 {
217 return status;
218 }
219
220 /* REQ-RBC-009: handler_init() sets up the service's links without waiting for an external Train/IL/CTC sim
221 * (each sim channel is listen-role), so the service starts whether or not its sim is running yet. */
222 status = g_svc->handler_init(ctx);
223 if (status != RTE_STATUS_OK)
224 {
225 return status;
226 }
227
228 /* ADR-038: opt-in per-service status heartbeat to the C broker. */
230
231 ctx->cycle = 1U;
232 ctx->cycle_ticks = 0U;
233 rte_mem_set(&timer_cfg, 0, sizeof(timer_cfg));
234 timer_cfg.mode = RTE_TIMER_MODE_PERIODIC;
235 timer_cfg.period_ms = (rte_duration_ms_t)ctx->config.timers.report_period_ms;
236 timer_cfg.callback = cycle_tick_callback;
237 timer_cfg.user_ctx = ctx;
238 status = rte_timer_create(&ctx->cycle_timer_storage, &timer_cfg, &ctx->cycle_timer_handle);
239 if (status != RTE_STATUS_OK)
240 {
241 RTE_EXAMPLE_LOG("[GW-%s %s] cycle timer_create failed (%s)\n", g_svc->log_tag,
242 site_config_name(ctx->site), rte_status_to_string(status));
243 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, g_svc->log_tag, "-", "TIMER",
244 "cycle timer_create failed", NULL);
245 return status;
246 }
247 status = rte_timer_start(ctx->cycle_timer_handle);
248 if (status != RTE_STATUS_OK)
249 {
250 RTE_EXAMPLE_LOG("[GW-%s %s] cycle timer_start failed (%s)\n", g_svc->log_tag,
251 site_config_name(ctx->site), rte_status_to_string(status));
252 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, g_svc->log_tag, "-", "TIMER",
253 "cycle timer_start failed", NULL);
254 return status;
255 }
256
257 RTE_EXAMPLE_LOG("[GW-%s %s] RBC relay armed (period=%ums)\n", g_svc->log_tag, site_config_name(ctx->site),
258 (unsigned int)ctx->config.timers.report_period_ms);
259 return RTE_STATUS_OK;
260}
261
262static void gateway_c_check_channel_down_reboot(gateway_c_context_t *ctx, rte_timestamp_ms_t now_ms)
263{
264 const rte_timestamp_ms_t threshold_ms = (rte_timestamp_ms_t)RTE_EXAMPLE_CHANNEL_DOWN_REBOOT_MS;
265 char which[64];
266
267 which[0] = '\0';
268 if (g_svc->handler_check_link_down(threshold_ms, now_ms, which, sizeof(which)))
269 {
270 RTE_EXAMPLE_LOG("[GW-%s %s] %s down >= %ums: rebooting Gateway service\n",
271 g_svc->log_tag, site_config_name(ctx->site), which, (unsigned int)threshold_ms);
272 rte_log_write_event(RTE_LOG_LEVEL_WARNING, site_config_name(ctx->site), 0U, g_svc->log_tag, "-",
273 "CHANNEL_REBOOT", which, NULL);
274 (void)gateway_c_shutdown(ctx);
275 RTE_REBOOT(0U);
276 }
277}
278
279static rte_status_t gateway_c_execute(void *context)
280{
282 rte_timestamp_ms_t now_ms = 0U;
283
284 (void)rte_timer_now(&now_ms);
285
286 /* Optional per-service pre-hook (IL: gateway_c_logctl_poll - the
287 * non-safety diagnostic log-level control, applied to this process
288 * and fanned to A/B; before the handler so a level bump takes effect
289 * for this same cycle's own logging). */
290 if (g_svc->pre_execute_hook != NULL)
291 {
293 }
294
295 g_svc->handler_execute(ctx, ctx->cycle);
296
297 if (g_svc->broker_channel != NULL)
298 {
300 }
301
303
304 while ((ctx->cycle_ticks == 0U) && !ctx->stop_requested)
305 {
306 if (g_svc->idle_hook != NULL)
307 {
308 g_svc->idle_hook(ctx);
309 }
311 }
312 if (ctx->cycle_ticks > 0U)
313 {
314 ctx->cycle_ticks--;
315 }
316
317 ctx->cycle++;
318 return RTE_STATUS_OK;
319}
320
321static rte_status_t gateway_c_shutdown(void *context)
322{
324
325 RTE_EXAMPLE_LOG("[GW-%s %s] shutting down\n", g_svc->log_tag, site_config_name(ctx->site));
326 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(ctx->site), 0U, g_svc->log_tag, "-", "LIFECYCLE",
327 "gateway service shutting down", NULL);
328
329 ctx->stop_requested = true;
330 (void)rte_timer_stop(ctx->cycle_timer_handle);
331 (void)rte_timer_destroy(ctx->cycle_timer_handle);
332
335
336 RTE_EXAMPLE_LOG("[GW-%s %s] shutdown complete\n", g_svc->log_tag, site_config_name(ctx->site));
337 return RTE_STATUS_OK;
338}
339
340static const char *gateway_c_get_name(void)
341{
342 return g_svc->app_name;
343}
344
345static const char *gateway_c_get_version(void)
346{
347 return "3.0.0";
348}
349
350static rte_appmanager_config_t gateway_c_common_make_config(rte_example_site_t site,
351 const gateway_c_service_def_t *svc)
352{
353 static const rte_appmanager_operations_t ops = {
354 .init = gateway_c_init,
355 .pre_execute = NULL,
356 .execute = gateway_c_execute,
357 .post_execute = NULL,
358 .shutdown = gateway_c_shutdown,
359 .get_name = gateway_c_get_name,
360 .get_version = gateway_c_get_version
361 };
362 rte_appmanager_config_t config;
363
364 g_svc = svc;
365
366 rte_mem_set(&g_c_ctx, 0, sizeof(g_c_ctx));
367 g_c_ctx.site = site;
368
369 rte_mem_set(&config, 0, sizeof(config));
370 config.ops = &ops;
371 config.context = &g_c_ctx;
372 config.max_iterations = 0U;
373 return config;
374}
375
376int gateway_c_main(int argc, char **argv, const gateway_c_service_def_t *svc)
377{
378 rte_status_t status;
380 rte_appmanager_config_t config;
381 const char *framework_actual_version;
382
383 /* Printed unconditionally, before arg validation - see main.c's own
384 * identical block (AB/GP/main/main.c) for the full rationale, shared
385 * by every one of this project's binaries. */
386 framework_actual_version = rte_core_get_version();
387 (void)fprintf(stdout, "%s version %s (framework %s, expected %s)\n", argv[0], RTE_RBC2OO2_VERSION,
388 framework_actual_version, RTE_FRAMEWORK_EXPECTED_VERSION);
389 if (strcmp(framework_actual_version, RTE_FRAMEWORK_EXPECTED_VERSION) != 0)
390 {
391 (void)fprintf(stderr,
392 "WARNING: linked safeAPIFreamwork version (%s) does not match what this binary was "
393 "built against (%s) - a stale library archive or wrong framework dist/ may be linked in\n",
394 framework_actual_version, RTE_FRAMEWORK_EXPECTED_VERSION);
395 }
396
397 if (argc < 2)
398 {
399 (void)fprintf(stderr, "usage: %s {WEST|EAST}\n", argv[0]);
400 return 1;
401 }
402 if (!site_config_parse(argv[1], &site))
403 {
404 (void)fprintf(stderr, "unknown site '%s' - expected WEST or EAST\n", argv[1]);
405 return 1;
406 }
407
408 status = app_main_common_init(argc, argv);
409 if (status != RTE_STATUS_OK)
410 {
411 return 1;
412 }
413
414 config = gateway_c_common_make_config(site, svc);
415 return rte_appmanager_run(&config);
416}
417
418static uint64_t gateway_c_hash_bytes(const uint8_t *bytes, size_t size)
419{
420 uint64_t h = 14695981039346656037ULL;
421 size_t i;
422 for (i = 0U; i < size; i++)
423 {
424 h ^= (uint64_t)bytes[i];
425 h *= 1099511628211ULL;
426 }
427 return h;
428}
429
431{
432 if (dedup != NULL)
433 {
434 rte_mem_set(dedup, 0, sizeof(*dedup));
435 }
436}
437
438bool gateway_c_dedup_check_and_record(gateway_c_dedup_t *dedup, const uint8_t *bytes, size_t size)
439{
440 uint64_t h;
441 rte_timestamp_ms_t now_ms = 0U;
442 rte_timestamp_ms_t expiry_ms;
443 uint32_t i;
444
445 if ((dedup == NULL) || (bytes == NULL) || (size == 0U))
446 {
447 return false;
448 }
449
450 (void)rte_timer_now(&now_ms);
451 h = gateway_c_hash_bytes(bytes, size);
452 expiry_ms = (bytes[0] == (uint8_t)RBC_MSG_SAF_ENVELOPE) ? (rte_timestamp_ms_t)GATEWAY_C_DEDUP_SAF_EXPIRY_MS
453 : (rte_timestamp_ms_t)GATEWAY_C_DEDUP_EXPIRY_MS;
454
455 /* Check if duplicate within expiry window */
456 for (i = 0U; i < GATEWAY_C_DEDUP_WINDOW_SIZE; i++)
457 {
458 if (dedup->entries[i].hash == h)
459 {
460 if ((now_ms >= dedup->entries[i].timestamp_ms) &&
461 ((now_ms - dedup->entries[i].timestamp_ms) <= expiry_ms))
462 {
463 return true; /* duplicate frame! */
464 }
465 }
466 }
467
468 /* Record first arrival */
469 dedup->entries[dedup->head].hash = h;
470 dedup->entries[dedup->head].timestamp_ms = now_ms;
471 dedup->head = (dedup->head + 1U) % GATEWAY_C_DEDUP_WINDOW_SIZE;
472
473 return false; /* first arrival, forward to sim */
474}
475
static void cycle_tick_callback(rte_timer_handle_t handle, void *user_ctx)
rte_status_t app_main_common_init(int argc, char **argv)
Runs this process's one-time runtime setup, common to every role: unbuffered stdout,...
Process-wide runtime setup shared by every one of this example's binaries (main.c for A/B,...
#define RTE_EXAMPLE_LOG(...)
#define RTE_EXAMPLE_CHANNEL_DOWN_REBOOT_MS
#define RBC_ENVELOPE_WIRE_SIZE
#define RBC_TRAIN_ENVELOPE_WIRE_SIZE
static rte_status_t gateway_c_shutdown(void *context)
static rte_status_t gateway_c_execute(void *context)
int gateway_c_main(int argc, char **argv, const gateway_c_service_def_t *svc)
Full process entry for a Role C gateway service: prints the version banner, parses argv[1] as {WEST|E...
static const char * gateway_c_get_name(void)
static const gateway_c_service_def_t * g_svc
void gateway_c_dedup_init(gateway_c_dedup_t *dedup)
Initialize a first-arrival deduplication tracker.
static uint64_t gateway_c_hash_bytes(const uint8_t *bytes, size_t size)
bool gateway_c_dedup_check_and_record(gateway_c_dedup_t *dedup, const uint8_t *bytes, size_t size)
Check if bytes was already forwarded within the expiry window. If not seen, records bytes with curren...
static void gateway_c_check_channel_down_reboot(gateway_c_context_t *ctx, rte_timestamp_ms_t now_ms)
static rte_appmanager_config_t gateway_c_common_make_config(rte_example_site_t site, const gateway_c_service_def_t *svc)
static gateway_c_context_t g_c_ctx
static const char * gateway_c_get_version(void)
static rte_status_t gateway_c_init(void *context)
static rte_status_t resolve_channel_c(const char *name, rte_netlink_config_t *config, void *context)
Shared skeleton for the three Role C gateway service binaries (rte_gateway_c_train / _il / _ctc - ADR...
rte_status_t gateway_c_config_find_channel(const gateway_c_config_t *cfg, const char *channel_name, rte_netlink_config_t *out_netlink_cfg)
rte_status_t gateway_c_config_init_defaults(rte_example_site_t site, gateway_c_config_t *out_cfg)
rte_status_t gateway_c_config_load_file(const char *filepath, gateway_c_config_t *out_cfg)
Configuration data structures and parser for Role C Gateway (JSON-based).
void gateway_c_selfstatus_shutdown(void)
void gateway_c_selfstatus_tick(const gateway_c_context_t *ctx, uint32_t cycle, const char *log_tag, uint8_t src_id)
Call once per cycle. Every RTE_EXAMPLE_STATUS_PERIOD_MS (and on cycle 1) logs [unit] [-] [STATUS] [se...
void gateway_c_selfstatus_init(const char *broker_channel_name)
Setup the broker CONNECT link. broker_channel_name is one of "c-broker-train"/"-il"/"-ctc"....
ADR-038: a role-C gateway service's own periodic status - a self log line plus a PUBLISH(STATUS) rbc_...
Shared wire-format constants and gateway_c_context_t.
#define GATEWAY_C_DEDUP_SAF_EXPIRY_MS
#define GATEWAY_C_DEDUP_WINDOW_SIZE
void gateway_c_sleep_ms(unsigned int ms)
#define GATEWAY_C_DEDUP_EXPIRY_MS
Declares gateway_c_sleep_ms() - see gateway_c_types.h's own doc.
ADR-038: the wire frame for the A/B<->C util side channel (RTE_EXAMPLE_RELAY_KIND_UTIL) and the C-sid...
#define RBC_UTIL_FRAME_SIZE
@ RBC_MSG_SAF_ENVELOPE
uint16_t site_config_c_port_for_broker(rte_example_site_t site, rte_example_relay_kind_t gw_kind)
ADR-038: the status/broker service's LISTEN port for one local gateway service to CONNECT and publish...
uint16_t site_config_c_port_for_ctc(rte_example_site_t site)
Definition site_config.c:95
const char * site_config_name(rte_example_site_t site)
Definition site_config.c:52
uint16_t site_config_c_port_for_il(rte_example_site_t site)
Definition site_config.c:90
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
uint16_t site_config_c_port_for_b(rte_example_site_t site, rte_example_relay_kind_t kind)
Definition site_config.c:76
uint16_t site_config_c_port_for_train(rte_example_site_t site)
C's single listen port for the Train sim, for this site (ADR-036: one multiplexed connection carries ...
Definition site_config.c:85
uint16_t site_config_c_port_for_a(rte_example_site_t site, rte_example_relay_kind_t kind)
C's listen port for A's dedicated kind relay link, for this site (rte_example_relay_kind_t's own doc)...
Definition site_config.c:67
rte_example_site_t
Definition site_config.h:20
@ RTE_EXAMPLE_SITE_WEST
Definition site_config.h:21
@ RTE_EXAMPLE_RELAY_KIND_UTIL
Definition site_config.h:48
@ RTE_EXAMPLE_RELAY_KIND_CTC
Definition site_config.h:42
@ RTE_EXAMPLE_RELAY_KIND_IL
Definition site_config.h:41
@ RTE_EXAMPLE_RELAY_KIND_TRAIN
Definition site_config.h:40
gateway_c_config_timers_t timers
One gateway_c_context_t instance's state.
volatile bool stop_requested
rte_example_site_t site
gateway_c_config_t config
rte_timer_storage_t cycle_timer_storage
rte_timer_handle_t cycle_timer_handle
volatile uint32_t cycle_ticks
uint64_t hash
rte_timestamp_ms_t timestamp_ms
gateway_c_dedup_entry_t entries[GATEWAY_C_DEDUP_WINDOW_SIZE]
One gateway service's identity and its single handler's entry points - a pure-literal table each main...
bool(*) handler_check_link_down(rte_timestamp_ms_t threshold_ms, rte_timestamp_ms_t now_ms, char *out_which, size_t which_size)
void(*) handler_execute(gateway_c_context_t *ctx, uint32_t cycle)
void(*) pre_execute_hook(gateway_c_context_t *ctx)
rte_status_t(*) handler_init(gateway_c_context_t *ctx)
void(*) idle_hook(gateway_c_context_t *ctx)