SafeAPI RBC 2oo2 Generic Application
IL/CTC route and train logic for the safeAPIRBC2oo2 reference RBC
Loading...
Searching...
No Matches
gateway_c_il_handler.c
Go to the documentation of this file.
1
7
8#include <stdio.h>
9#include "safeapi/oal/memory/sapi_mem_util.h"
10#include "safeapi/oal/log/sapi_log.h"
11#include "common/common_config.h"
12#include "common/rbc_util_frame.h"
13#include "gateway_c_selfstatus.h"
14
16
17static const char *peer_name(gateway_c_peer_t peer)
18{
19 return (peer == GATEWAY_C_PEER_A) ? "A" : "B";
20}
21
22static void broadcast_one(gateway_c_peer_t peer, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
23{
24 sapi_status_t status = sapi_channel_service_send(&g_il.relay_channel[peer], bytes,
25 RBC_ENVELOPE_WIRE_SIZE, 0U);
26
27 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
28 {
29 sapi_log_write_event(SAPI_LOG_LEVEL_WARNING, "-", 0U, "GW", peer_name(peer), "LOG_LEVEL",
30 sapi_status_to_string(status), NULL);
31 }
32}
33
34void gateway_c_il_handler_broadcast_to_ab(const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
35{
36 broadcast_one(GATEWAY_C_PEER_A, bytes);
37 broadcast_one(GATEWAY_C_PEER_B, bytes);
38}
39
40sapi_status_t gateway_c_il_handler_init(gateway_c_context_t *ctx)
41{
42 sapi_status_t status;
43
44 g_il.last_known_tag = "SUPPRESSED";
45 g_il.relay_consecutive_timeout = 0U;
46 g_il.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
47 g_il.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
48 g_il.a_seen_this_cycle = 0U;
49 g_il.a_stale_cycles = 0U;
50
51 status = sapi_channel_service_setup(&g_il.relay_channel[GATEWAY_C_PEER_A], "c-relay-il-a");
52 if (status != SAPI_STATUS_OK)
53 {
54 SAFEAPI_EXAMPLE_LOG("[GW %s] ILHandler: relay A channel setup failed (%s)\n",
55 site_config_name(ctx->site), sapi_status_to_string(status));
56 return status;
57 }
58 status = sapi_channel_service_setup(&g_il.relay_channel[GATEWAY_C_PEER_B], "c-relay-il-b");
59 if (status != SAPI_STATUS_OK)
60 {
61 SAFEAPI_EXAMPLE_LOG("[GW %s] ILHandler: relay B channel setup failed (%s)\n",
62 site_config_name(ctx->site), sapi_status_to_string(status));
63 return status;
64 }
65 status = sapi_channel_service_setup(&g_il.sim_channel, "c-sim-il");
66 if (status != SAPI_STATUS_OK)
67 {
68 SAFEAPI_EXAMPLE_LOG("[GW %s] ILHandler: sim channel setup failed (%s)\n",
69 site_config_name(ctx->site), sapi_status_to_string(status));
70 return status;
71 }
72 return SAPI_STATUS_OK;
73}
74
75static void send_to_relay(gateway_c_context_t *ctx, gateway_c_peer_t peer,
76 const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
77{
78 sapi_status_t status = sapi_channel_service_send(&g_il.relay_channel[peer],
79 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
80 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
81 {
82 SAFEAPI_EXAMPLE_LOG("[GW %s] ILHandler: forward to %s failed (%s)\n", site_config_name(ctx->site),
83 peer_name(peer), sapi_status_to_string(status));
84 sapi_log_write_event(SAPI_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", peer_name(peer),
85 "ENVELOPE", sapi_status_to_string(status), NULL);
86 }
87}
88
89static void send_to_sim(gateway_c_context_t *ctx, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
90{
91 sapi_status_t status = sapi_channel_service_send(&g_il.sim_channel,
92 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
93 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
94 {
95 SAFEAPI_EXAMPLE_LOG("[GW %s] ILHandler: forward to ILSim failed (%s)\n",
96 site_config_name(ctx->site), sapi_status_to_string(status));
97 sapi_log_write_event(SAPI_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", "IL", "ENVELOPE",
98 sapi_status_to_string(status), NULL);
99 }
100}
101
102void gateway_c_il_handler_execute(gateway_c_context_t *ctx, uint32_t cycle)
103{
104 uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE];
105 char sim_status[RBC_UTIL_PAYLOAD_MAX + 1];
106 sapi_status_t status;
107 bool forwarded_this_cycle = false;
108 (void)cycle;
109
110 /* ILSim -> Gateway -> A and B (verbatim) */
111 while (sapi_channel_service_read(&g_il.sim_channel, bytes, sizeof(bytes), 0U) == SAPI_STATUS_OK)
112 {
113 /* ADR-038: the IL sim's own-perspective status frame - republish to
114 * the C status broker, never forward to A/B (they never see this
115 * kind). Not counted as relay traffic (idle-cycle keepalive below
116 * still fires if this was the only frame). */
117 if (rbc_envelope_is_sim_status(bytes, sim_status, (uint16_t)sizeof(sim_status)))
118 {
119 gateway_c_selfstatus_publish((uint8_t)RBC_UTIL_SRC_SIM_IL, sim_status);
120 continue;
121 }
122 send_to_relay(ctx, GATEWAY_C_PEER_A, bytes);
123 send_to_relay(ctx, GATEWAY_C_PEER_B, bytes);
124 forwarded_this_cycle = true;
125 }
126
127 /* Keepalive the C->A/B relay every idle cycle - the IL sim is
128 * command-driven (long silent stretches), and with one multiplexed
129 * link that silence would trip A/B's relay-RX staleness detector.
130 * Same rationale as gateway_c_train_handler.c's own keepalive; A/B
131 * drop it before any IL decode (pre_execute.c). */
132 if (!forwarded_this_cycle)
133 {
134 uint8_t keepalive[RBC_ENVELOPE_WIRE_SIZE];
135
136 rbc_envelope_write_keepalive(keepalive);
137 send_to_relay(ctx, GATEWAY_C_PEER_A, keepalive);
138 send_to_relay(ctx, GATEWAY_C_PEER_B, keepalive);
139 }
140
141 /* A/B -> Gateway -> ILSim (A primary, B drained always, forwarded
142 * only in fallback). */
143 {
144 uint32_t a_count = 0U;
145 bool use_b_fallback;
146
147 while ((status = sapi_channel_service_read(&g_il.relay_channel[GATEWAY_C_PEER_A],
148 bytes, sizeof(bytes), 0U)) == SAPI_STATUS_OK)
149 {
150 send_to_sim(ctx, bytes);
151 a_count++;
152 }
153 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
154 {
155 if (g_il.relay_down_since_ms[GATEWAY_C_PEER_A] == 0U)
156 {
157 (void)sapi_timer_now(&g_il.relay_down_since_ms[GATEWAY_C_PEER_A]);
158 }
159 }
160 else if (a_count > 0U)
161 {
162 g_il.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
163 g_il.relay_consecutive_timeout = 0U;
164 g_il.a_stale_cycles = 0U;
165 }
166 else
167 {
168 g_il.a_stale_cycles++;
169 }
170 g_il.a_seen_this_cycle = a_count;
171
172 use_b_fallback = (g_il.a_stale_cycles >= (unsigned int)SAFEAPI_EXAMPLE_C_A_STALE_CYCLE_LIMIT);
173 {
174 uint32_t b_count = 0U;
175
176 while ((status = sapi_channel_service_read(&g_il.relay_channel[GATEWAY_C_PEER_B],
177 bytes, sizeof(bytes), 0U)) == SAPI_STATUS_OK)
178 {
179 if (use_b_fallback)
180 {
181 send_to_sim(ctx, bytes);
182 }
183 b_count++;
184 }
185 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
186 {
187 if (g_il.relay_down_since_ms[GATEWAY_C_PEER_B] == 0U)
188 {
189 (void)sapi_timer_now(&g_il.relay_down_since_ms[GATEWAY_C_PEER_B]);
190 }
191 }
192 else if (b_count > 0U)
193 {
194 g_il.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
195 }
196 if (use_b_fallback)
197 {
198 g_il.last_known_tag = (b_count > 0U) ? "ACTIVE" : "SUPPRESSED";
199 }
200 else
201 {
202 g_il.last_known_tag = (a_count > 0U) ? "ACTIVE" : "SUPPRESSED";
203 }
204 }
205 }
206}
207
208void gateway_c_il_handler_shutdown(void)
209{
210 (void)sapi_channel_service_close(&g_il.sim_channel);
211 (void)sapi_channel_service_close(&g_il.relay_channel[GATEWAY_C_PEER_A]);
212 (void)sapi_channel_service_close(&g_il.relay_channel[GATEWAY_C_PEER_B]);
213}
214
215bool gateway_c_il_handler_check_link_down(sapi_timestamp_ms_t threshold_ms, sapi_timestamp_ms_t now_ms,
216 char *out_which, size_t which_size)
217{
218 gateway_c_peer_t peer;
219
220 for (peer = GATEWAY_C_PEER_A; peer <= GATEWAY_C_PEER_B; peer = (gateway_c_peer_t)((int)peer + 1))
221 {
222 sapi_timestamp_ms_t down_since = g_il.relay_down_since_ms[peer];
223
224 if ((down_since != 0U) && ((now_ms - down_since) >= threshold_ms))
225 {
226 (void)snprintf(out_which, which_size, "IL relay link to %s", peer_name(peer));
227 return true;
228 }
229 }
230 return false;
231}
void gateway_c_il_handler_broadcast_to_ab(const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
Sends one RBC_ENVELOPE_WIRE_SIZE frame to BOTH A and B on the IL relay link. Used by gateway_c_logctl...
ILHandler - Role C Gateway's TCP/IP server for the ONE multiplexed IL sim connection and the ONE dedi...