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_ctc_handler.c
Go to the documentation of this file.
1
6
7#include <stdio.h>
8#include "rte/oal/memory/rte_mem_util.h"
9#include "rte/oal/log/rte_log.h"
10#include "common/common_config.h"
11#include "common/rbc_util_frame.h"
12#include "gateway_c_common.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
22rte_status_t gateway_c_ctc_handler_init(gateway_c_context_t *ctx)
23{
24 rte_status_t status;
25
26 g_ctc.last_known_tag = "SUPPRESSED";
27 g_ctc.relay_consecutive_timeout = 0U;
28 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
29 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
30 g_ctc.send_down_since_ms[GATEWAY_C_PEER_A] = 0U;
31 g_ctc.send_down_since_ms[GATEWAY_C_PEER_B] = 0U;
32 g_ctc.a_seen_this_cycle = 0U;
33 g_ctc.a_stale_cycles = 0U;
34 gateway_c_dedup_init(&g_ctc.dedup);
35
36 /* Relay links to/from A and B */
37 status = rte_channel_service_setup(&g_ctc.relay_channel[GATEWAY_C_PEER_A], "c-relay-ctc-a");
38 if (status != RTE_STATUS_OK)
39 {
40 RTE_EXAMPLE_LOG("[GW %s] CTCHandler: relay A channel setup failed (%s)\n",
41 site_config_name(ctx->site), rte_status_to_string(status));
42 return status;
43 }
44
45 status = rte_channel_service_setup(&g_ctc.relay_channel[GATEWAY_C_PEER_B], "c-relay-ctc-b");
46 if (status != RTE_STATUS_OK)
47 {
48 RTE_EXAMPLE_LOG("[GW %s] CTCHandler: relay B channel setup failed (%s)\n",
49 site_config_name(ctx->site), rte_status_to_string(status));
50 return status;
51 }
52
53 /* CTCSim-facing link */
54 status = rte_channel_service_setup(&g_ctc.sim_channel, "c-sim-ctc");
55 if (status != RTE_STATUS_OK)
56 {
57 RTE_EXAMPLE_LOG("[GW %s] CTCHandler: sim channel setup failed (%s)\n",
58 site_config_name(ctx->site), rte_status_to_string(status));
59 return status;
60 }
61
62 return RTE_STATUS_OK;
63}
64
65static void send_to_relay(gateway_c_context_t *ctx, gateway_c_peer_t peer, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
66{
67 rte_status_t status = rte_channel_service_send(&g_ctc.relay_channel[peer],
68 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
69 /* NOT_CONNECTED (REQ-CHANSVC-001): this listen-role relay has not had a datagram from A/B yet (still starting),
70 * so nothing was sent. That is not a failed send: handled exactly as OK, as before the status existed (the
71 * channel service then returned OK and dropped the frame), so no send-failure window and no log. */
72 if ((status == RTE_STATUS_OK) || (status == RTE_STATUS_NOT_CONNECTED))
73 {
74 /* UDP gives no read error when the peer is partitioned, so a failed send is the only link-down
75 * signal; a successful send ends the window (see gateway_c_train_handler.c in RBC_GP). */
76 g_ctc.send_down_since_ms[peer] = 0U;
77 }
78 else
79 {
80 /* TIMEOUT counts too: the POSIX channel backend maps EHOSTUNREACH/ECONNREFUSED/ENOTCONN (what a partitioned
81 * peer produces) to TIMEOUT, so ignoring it left a partitioned Gateway running for good (Docker, C West
82 * partition test). Any successful send resets the window, so a busy queue cannot trip it. */
83 if (g_ctc.send_down_since_ms[peer] == 0U)
84 {
85 (void)rte_timer_now(&g_ctc.send_down_since_ms[peer]);
86 }
87 if (status == RTE_STATUS_TIMEOUT)
88 {
89 return;
90 }
91 RTE_EXAMPLE_LOG("[GW %s] CTCHandler: keepalive to %s failed (%s)\n", site_config_name(ctx->site),
92 peer_name(peer), rte_status_to_string(status));
93 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", peer_name(peer),
94 "ENVELOPE", rte_status_to_string(status), NULL);
95 }
96}
97
98static void send_to_sim(gateway_c_context_t *ctx, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
99{
100 rte_status_t status = rte_channel_service_send(&g_ctc.sim_channel,
101 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
102 /* NOT_CONNECTED: no CTC sim has sent on this listen-role link yet; silent, as OK was before REQ-CHANSVC-001. */
103 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT) && (status != RTE_STATUS_NOT_CONNECTED))
104 {
105 RTE_EXAMPLE_LOG("[GW %s] CTCHandler: forward to CTCSim failed (%s)\n",
106 site_config_name(ctx->site), rte_status_to_string(status));
107 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", "CTC", "ENVELOPE",
108 rte_status_to_string(status), NULL);
109 }
110}
111
112void gateway_c_ctc_handler_execute(gateway_c_context_t *ctx, uint32_t cycle)
113{
114 uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE];
115 char sim_status[RBC_UTIL_PAYLOAD_MAX + 1];
116 rte_status_t status;
117 (void)cycle;
118
119 /* CTCSim -> Gateway -> A/B */
120 while (rte_channel_service_read(&g_ctc.sim_channel, bytes, sizeof(bytes), 0U) == RTE_STATUS_OK)
121 {
122 /* ADR-038: the CTC sim's own-perspective status frame - republish to
123 * the C status broker, never forward to A/B (they never see this
124 * kind). */
125 if (rbc_envelope_is_sim_status(bytes, sim_status, (uint16_t)sizeof(sim_status)))
126 {
127 gateway_c_selfstatus_publish((uint8_t)RBC_UTIL_SRC_SIM_CTC, sim_status);
128 continue;
129 }
130 send_to_relay(ctx, GATEWAY_C_PEER_A, bytes);
131 send_to_relay(ctx, GATEWAY_C_PEER_B, bytes);
132 }
133
134 /* A/B -> Gateway -> CTCSim.
135 * RCA/OCORA PI-API: Gateway acts as external Service Unit / Adapter.
136 * 2oo2 arbitration is not needed here because channels A and B already
137 * compared data before emission. Gateway forwards whichever envelope arrives
138 * first and deduplicates identical frames arriving from the peer replica. */
139 {
140 uint32_t a_count = 0U;
141 uint32_t b_count = 0U;
142
143 while ((status = rte_channel_service_read(&g_ctc.relay_channel[GATEWAY_C_PEER_A],
144 bytes, sizeof(bytes), 0U)) == RTE_STATUS_OK)
145 {
146 if (rbc_envelope_is_keepalive(bytes))
147 {
148 a_count++;
149 continue;
150 }
151 if (!gateway_c_dedup_check_and_record(&g_ctc.dedup, bytes, sizeof(bytes)))
152 {
153 send_to_sim(ctx, bytes);
154 }
155 a_count++;
156 }
157 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT))
158 {
159 if (g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] == 0U)
160 {
161 (void)rte_timer_now(&g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A]);
162 }
163 }
164 else if (a_count > 0U)
165 {
166 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
167 g_ctc.relay_consecutive_timeout = 0U;
168 g_ctc.a_stale_cycles = 0U;
169 }
170 else
171 {
172 g_ctc.a_stale_cycles++;
173 }
174 g_ctc.a_seen_this_cycle = a_count;
175
176 while ((status = rte_channel_service_read(&g_ctc.relay_channel[GATEWAY_C_PEER_B],
177 bytes, sizeof(bytes), 0U)) == RTE_STATUS_OK)
178 {
179 if (rbc_envelope_is_keepalive(bytes))
180 {
181 b_count++;
182 continue;
183 }
184 if (!gateway_c_dedup_check_and_record(&g_ctc.dedup, bytes, sizeof(bytes)))
185 {
186 send_to_sim(ctx, bytes);
187 }
188 b_count++;
189 }
190 if ((status != RTE_STATUS_OK) && (status != RTE_STATUS_TIMEOUT))
191 {
192 if (g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] == 0U)
193 {
194 (void)rte_timer_now(&g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B]);
195 }
196 }
197 else if (b_count > 0U)
198 {
199 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
200 }
201
202 g_ctc.last_known_tag = ((a_count > 0U) || (b_count > 0U)) ? "ACTIVE" : "SUPPRESSED";
203 }
204}
205
206void gateway_c_ctc_handler_shutdown(void)
207{
208 (void)rte_channel_service_close(&g_ctc.sim_channel);
209 (void)rte_channel_service_close(&g_ctc.relay_channel[GATEWAY_C_PEER_A]);
210 (void)rte_channel_service_close(&g_ctc.relay_channel[GATEWAY_C_PEER_B]);
211}
212
213bool gateway_c_ctc_handler_check_link_down(rte_timestamp_ms_t threshold_ms, rte_timestamp_ms_t now_ms,
214 char *out_which, size_t which_size)
215{
216 gateway_c_peer_t peer;
217
218 for (peer = GATEWAY_C_PEER_A; peer <= GATEWAY_C_PEER_B; peer = (gateway_c_peer_t)((int)peer + 1))
219 {
220 rte_timestamp_ms_t down_since = g_ctc.relay_down_since_ms[peer];
221 rte_timestamp_ms_t send_down_since = g_ctc.send_down_since_ms[peer];
222
223 if (((down_since != 0U) && ((now_ms - down_since) >= threshold_ms)) ||
224 ((send_down_since != 0U) && ((now_ms - send_down_since) >= threshold_ms)))
225 {
226 (void)snprintf(out_which, which_size, "CTC relay link to %s", peer_name(peer));
227 return true;
228 }
229 }
230 return false;
231}
CTCHandler - Role C Gateway's TCP/IP server for the single CTC sim instance and its dedicated relay t...