RTE RBC 2oo2 Generic Application 0.1.0 build 308eda7
IL/CTC route and train logic for the RBC_GP 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 "rte/oal/memory/rte_mem_util.h"
10#include "rte/oal/log/rte_log.h"
11#include "common/common_config.h"
12#include "common/rbc_util_frame.h"
13#include "gateway_c_common.h"
14#include "gateway_c_selfstatus.h"
15
17
18static const char *peer_name(gateway_c_peer_t peer)
19{
20 return (peer == GATEWAY_C_PEER_A) ? "A" : "B";
21}
22
23static void broadcast_one(gateway_c_peer_t peer, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
24{
25 rte_status_t status = rte_channel_service_send(&g_il.relay_channel[peer], bytes,
26 RBC_ENVELOPE_WIRE_SIZE, 0U);
27
28 /* NOT_CONNECTED: A/B have not sent on this listen-role relay yet; silent, as OK was before REQ-CHANSVC-001. */
29 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT) && (status != RTE_STATUS_NOT_CONNECTED))
30 {
31 rte_log_write_event(RTE_LOG_LEVEL_WARNING, "-", 0U, "GW", peer_name(peer), "LOG_LEVEL",
32 rte_status_to_string(status), NULL);
33 }
34}
35
36void gateway_c_il_handler_broadcast_to_ab(const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
37{
38 broadcast_one(GATEWAY_C_PEER_A, bytes);
39 broadcast_one(GATEWAY_C_PEER_B, bytes);
40}
41
42rte_status_t gateway_c_il_handler_init(gateway_c_context_t *ctx)
43{
44 rte_status_t status;
45
46 g_il.last_known_tag = "SUPPRESSED";
47 g_il.relay_consecutive_timeout = 0U;
48 g_il.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
49 g_il.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
50 g_il.send_down_since_ms[GATEWAY_C_PEER_A] = 0U;
51 g_il.send_down_since_ms[GATEWAY_C_PEER_B] = 0U;
52 g_il.a_seen_this_cycle = 0U;
53 g_il.a_stale_cycles = 0U;
54 gateway_c_dedup_init(&g_il.dedup);
55
56 status = rte_channel_service_setup(&g_il.relay_channel[GATEWAY_C_PEER_A], "c-relay-il-a");
57 if (status != RTE_STATUS_OK)
58 {
59 RTE_EXAMPLE_LOG("[GW %s] ILHandler: relay A channel setup failed (%s)\n",
60 site_config_name(ctx->site), rte_status_to_string(status));
61 return status;
62 }
63 status = rte_channel_service_setup(&g_il.relay_channel[GATEWAY_C_PEER_B], "c-relay-il-b");
64 if (status != RTE_STATUS_OK)
65 {
66 RTE_EXAMPLE_LOG("[GW %s] ILHandler: relay B channel setup failed (%s)\n",
67 site_config_name(ctx->site), rte_status_to_string(status));
68 return status;
69 }
70 status = rte_channel_service_setup(&g_il.sim_channel, "c-sim-il");
71 if (status != RTE_STATUS_OK)
72 {
73 RTE_EXAMPLE_LOG("[GW %s] ILHandler: sim channel setup failed (%s)\n",
74 site_config_name(ctx->site), rte_status_to_string(status));
75 return status;
76 }
77 return RTE_STATUS_OK;
78}
79
80static void send_to_relay(gateway_c_context_t *ctx, gateway_c_peer_t peer,
81 const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
82{
83 rte_status_t status = rte_channel_service_send(&g_il.relay_channel[peer],
84 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
85 /* NOT_CONNECTED (REQ-CHANSVC-001): this listen-role relay has not had a datagram from A/B yet (still starting),
86 * so nothing was sent. That is not a failed send: handled exactly as OK, as before the status existed (the
87 * channel service then returned OK and dropped the frame), so no send-failure window and no log. */
88 if ((status == RTE_STATUS_OK) || (status == RTE_STATUS_NOT_CONNECTED))
89 {
90 /* UDP gives no read error when the peer is partitioned, so a failed send is the only link-down
91 * signal; a successful send ends the window (see gateway_c_train_handler.c in RBC_GP). */
92 g_il.send_down_since_ms[peer] = 0U;
93 }
94 else
95 {
96 /* TIMEOUT counts too: the POSIX channel backend maps EHOSTUNREACH/ECONNREFUSED/ENOTCONN (what a partitioned
97 * peer produces) to TIMEOUT, so ignoring it left a partitioned Gateway running for good (Docker, C West
98 * partition test). Any successful send resets the window, so a busy queue cannot trip it. */
99 if (g_il.send_down_since_ms[peer] == 0U)
100 {
101 (void)rte_timer_now(&g_il.send_down_since_ms[peer]);
102 }
103 if (status == RTE_STATUS_TIMEOUT)
104 {
105 return;
106 }
107 RTE_EXAMPLE_LOG("[GW %s] ILHandler: forward to %s failed (%s)\n", site_config_name(ctx->site),
108 peer_name(peer), rte_status_to_string(status));
109 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", peer_name(peer),
110 "ENVELOPE", rte_status_to_string(status), NULL);
111 }
112}
113
114static void send_to_sim(gateway_c_context_t *ctx, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
115{
116 rte_status_t status = rte_channel_service_send(&g_il.sim_channel,
117 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
118 /* NOT_CONNECTED: no IL sim has sent on this listen-role link yet; silent, as OK was before REQ-CHANSVC-001. */
119 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT) && (status != RTE_STATUS_NOT_CONNECTED))
120 {
121 RTE_EXAMPLE_LOG("[GW %s] ILHandler: forward to ILSim failed (%s)\n",
122 site_config_name(ctx->site), rte_status_to_string(status));
123 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", "IL", "ENVELOPE",
124 rte_status_to_string(status), NULL);
125 }
126}
127
128void gateway_c_il_handler_execute(gateway_c_context_t *ctx, uint32_t cycle)
129{
130 uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE];
131 char sim_status[RBC_UTIL_PAYLOAD_MAX + 1];
132 rte_status_t status;
133 bool forwarded_this_cycle = false;
134 (void)cycle;
135
136 /* ILSim -> Gateway -> A and B (verbatim) */
137 while (rte_channel_service_read(&g_il.sim_channel, bytes, sizeof(bytes), 0U) == RTE_STATUS_OK)
138 {
139 /* ADR-038: the IL sim's own-perspective status frame - republish to
140 * the C status broker, never forward to A/B (they never see this
141 * kind). Not counted as relay traffic (idle-cycle keepalive below
142 * still fires if this was the only frame). */
143 if (rbc_envelope_is_sim_status(bytes, sim_status, (uint16_t)sizeof(sim_status)))
144 {
145 gateway_c_selfstatus_publish((uint8_t)RBC_UTIL_SRC_SIM_IL, sim_status);
146 continue;
147 }
148 send_to_relay(ctx, GATEWAY_C_PEER_A, bytes);
149 send_to_relay(ctx, GATEWAY_C_PEER_B, bytes);
150 forwarded_this_cycle = true;
151 }
152
153 /* Keepalive the C->A/B relay every idle cycle - the IL sim is
154 * command-driven (long silent stretches), and with one multiplexed
155 * link that silence would trip A/B's relay-RX staleness detector.
156 * Same rationale as gateway_c_train_handler.c's own keepalive; A/B
157 * drop it before any IL decode (pre_execute.c). */
158 if (!forwarded_this_cycle)
159 {
160 uint8_t keepalive[RBC_ENVELOPE_WIRE_SIZE];
161
162 rbc_envelope_write_keepalive(keepalive);
163 send_to_relay(ctx, GATEWAY_C_PEER_A, keepalive);
164 send_to_relay(ctx, GATEWAY_C_PEER_B, keepalive);
165 }
166
167 /* A/B -> Gateway -> ILSim.
168 * RCA/OCORA PI-API: Gateway acts as external Service Unit / Adapter.
169 * 2oo2 arbitration is not needed here because channels A and B already
170 * compared data before emission. Gateway forwards whichever envelope arrives
171 * first and deduplicates identical frames arriving from the peer replica. */
172 {
173 uint32_t a_count = 0U;
174 uint32_t b_count = 0U;
175
176 while ((status = rte_channel_service_read(&g_il.relay_channel[GATEWAY_C_PEER_A],
177 bytes, sizeof(bytes), 0U)) == RTE_STATUS_OK)
178 {
179 if (rbc_envelope_is_keepalive(bytes))
180 {
181 a_count++;
182 continue;
183 }
184 if (!gateway_c_dedup_check_and_record(&g_il.dedup, bytes, sizeof(bytes)))
185 {
186 send_to_sim(ctx, bytes);
187 }
188 a_count++;
189 }
190 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT))
191 {
192 if (g_il.relay_down_since_ms[GATEWAY_C_PEER_A] == 0U)
193 {
194 (void)rte_timer_now(&g_il.relay_down_since_ms[GATEWAY_C_PEER_A]);
195 }
196 }
197 else if (a_count > 0U)
198 {
199 g_il.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
200 g_il.relay_consecutive_timeout = 0U;
201 g_il.a_stale_cycles = 0U;
202 }
203 else
204 {
205 g_il.a_stale_cycles++;
206 }
207 g_il.a_seen_this_cycle = a_count;
208
209 while ((status = rte_channel_service_read(&g_il.relay_channel[GATEWAY_C_PEER_B],
210 bytes, sizeof(bytes), 0U)) == RTE_STATUS_OK)
211 {
212 if (rbc_envelope_is_keepalive(bytes))
213 {
214 b_count++;
215 continue;
216 }
217 if (!gateway_c_dedup_check_and_record(&g_il.dedup, bytes, sizeof(bytes)))
218 {
219 send_to_sim(ctx, bytes);
220 }
221 b_count++;
222 }
223 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT))
224 {
225 if (g_il.relay_down_since_ms[GATEWAY_C_PEER_B] == 0U)
226 {
227 (void)rte_timer_now(&g_il.relay_down_since_ms[GATEWAY_C_PEER_B]);
228 }
229 }
230 else if (b_count > 0U)
231 {
232 g_il.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
233 }
234
235 g_il.last_known_tag = ((a_count > 0U) || (b_count > 0U)) ? "ACTIVE" : "SUPPRESSED";
236 }
237}
238
239void gateway_c_il_handler_shutdown(void)
240{
241 (void)rte_channel_service_close(&g_il.sim_channel);
242 (void)rte_channel_service_close(&g_il.relay_channel[GATEWAY_C_PEER_A]);
243 (void)rte_channel_service_close(&g_il.relay_channel[GATEWAY_C_PEER_B]);
244}
245
246bool gateway_c_il_handler_check_link_down(rte_timestamp_ms_t threshold_ms, rte_timestamp_ms_t now_ms,
247 char *out_which, size_t which_size)
248{
249 gateway_c_peer_t peer;
250
251 for (peer = GATEWAY_C_PEER_A; peer <= GATEWAY_C_PEER_B; peer = (gateway_c_peer_t)((int)peer + 1))
252 {
253 rte_timestamp_ms_t down_since = g_il.relay_down_since_ms[peer];
254 rte_timestamp_ms_t send_down_since = g_il.send_down_since_ms[peer];
255
256 if (((down_since != 0U) && ((now_ms - down_since) >= threshold_ms)) ||
257 ((send_down_since != 0U) && ((now_ms - send_down_since) >= threshold_ms)))
258 {
259 (void)snprintf(out_which, which_size, "IL relay link to %s", peer_name(peer));
260 return true;
261 }
262 }
263 return false;
264}
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...