SafeAPI RBC 2oo2 Generic Application
IL/CTC route and train logic for the safeAPIRBC2oo2 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 "safeapi/oal/memory/sapi_mem_util.h"
9#include "safeapi/oal/log/sapi_log.h"
10#include "common/common_config.h"
11#include "common/rbc_util_frame.h"
12#include "gateway_c_selfstatus.h"
13
15
16static const char *peer_name(gateway_c_peer_t peer)
17{
18 return (peer == GATEWAY_C_PEER_A) ? "A" : "B";
19}
20
21sapi_status_t gateway_c_ctc_handler_init(gateway_c_context_t *ctx)
22{
23 sapi_status_t status;
24
25 g_ctc.last_known_tag = "SUPPRESSED";
26 g_ctc.relay_consecutive_timeout = 0U;
27 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
28 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
29 g_ctc.a_seen_this_cycle = 0U;
30 g_ctc.a_stale_cycles = 0U;
31
32 /* Relay links to/from A and B */
33 status = sapi_channel_service_setup(&g_ctc.relay_channel[GATEWAY_C_PEER_A], "c-relay-ctc-a");
34 if (status != SAPI_STATUS_OK)
35 {
36 SAFEAPI_EXAMPLE_LOG("[GW %s] CTCHandler: relay A channel setup failed (%s)\n",
37 site_config_name(ctx->site), sapi_status_to_string(status));
38 return status;
39 }
40
41 status = sapi_channel_service_setup(&g_ctc.relay_channel[GATEWAY_C_PEER_B], "c-relay-ctc-b");
42 if (status != SAPI_STATUS_OK)
43 {
44 SAFEAPI_EXAMPLE_LOG("[GW %s] CTCHandler: relay B channel setup failed (%s)\n",
45 site_config_name(ctx->site), sapi_status_to_string(status));
46 return status;
47 }
48
49 /* CTCSim-facing link */
50 status = sapi_channel_service_setup(&g_ctc.sim_channel, "c-sim-ctc");
51 if (status != SAPI_STATUS_OK)
52 {
53 SAFEAPI_EXAMPLE_LOG("[GW %s] CTCHandler: sim channel setup failed (%s)\n",
54 site_config_name(ctx->site), sapi_status_to_string(status));
55 return status;
56 }
57
58 return SAPI_STATUS_OK;
59}
60
61static void send_to_relay(gateway_c_context_t *ctx, gateway_c_peer_t peer, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
62{
63 sapi_status_t status = sapi_channel_service_send(&g_ctc.relay_channel[peer],
64 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
65 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
66 {
67 SAFEAPI_EXAMPLE_LOG("[GW %s] CTCHandler: keepalive to %s failed (%s)\n", site_config_name(ctx->site),
68 peer_name(peer), sapi_status_to_string(status));
69 sapi_log_write_event(SAPI_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", peer_name(peer),
70 "ENVELOPE", sapi_status_to_string(status), NULL);
71 }
72}
73
74static void send_to_sim(gateway_c_context_t *ctx, const uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE])
75{
76 sapi_status_t status = sapi_channel_service_send(&g_ctc.sim_channel,
77 bytes, RBC_ENVELOPE_WIRE_SIZE, 0U);
78 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
79 {
80 SAFEAPI_EXAMPLE_LOG("[GW %s] CTCHandler: forward to CTCSim failed (%s)\n",
81 site_config_name(ctx->site), sapi_status_to_string(status));
82 sapi_log_write_event(SAPI_LOG_LEVEL_ERROR, site_config_name(ctx->site), 0U, "GW", "CTC", "ENVELOPE",
83 sapi_status_to_string(status), NULL);
84 }
85}
86
87void gateway_c_ctc_handler_execute(gateway_c_context_t *ctx, uint32_t cycle)
88{
89 uint8_t bytes[RBC_ENVELOPE_WIRE_SIZE];
90 char sim_status[RBC_UTIL_PAYLOAD_MAX + 1];
91 sapi_status_t status;
92 (void)cycle;
93
94 /* CTCSim -> Gateway -> A/B */
95 while (sapi_channel_service_read(&g_ctc.sim_channel, bytes, sizeof(bytes), 0U) == SAPI_STATUS_OK)
96 {
97 /* ADR-038: the CTC sim's own-perspective status frame - republish to
98 * the C status broker, never forward to A/B (they never see this
99 * kind). */
100 if (rbc_envelope_is_sim_status(bytes, sim_status, (uint16_t)sizeof(sim_status)))
101 {
102 gateway_c_selfstatus_publish((uint8_t)RBC_UTIL_SRC_SIM_CTC, sim_status);
103 continue;
104 }
105 send_to_relay(ctx, GATEWAY_C_PEER_A, bytes);
106 send_to_relay(ctx, GATEWAY_C_PEER_B, bytes);
107 }
108
109 /* A/B -> Gateway -> CTCSim */
110 {
111 uint32_t a_count = 0U;
112 bool use_b_fallback;
113
114 while ((status = sapi_channel_service_read(&g_ctc.relay_channel[GATEWAY_C_PEER_A],
115 bytes, sizeof(bytes), 0U)) == SAPI_STATUS_OK)
116 {
117 send_to_sim(ctx, bytes);
118 a_count++;
119 }
120 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
121 {
122 if (g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] == 0U)
123 {
124 (void)sapi_timer_now(&g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A]);
125 }
126 }
127 else if (a_count > 0U)
128 {
129 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_A] = 0U;
130 g_ctc.relay_consecutive_timeout = 0U;
131 g_ctc.a_stale_cycles = 0U;
132 }
133 else
134 {
135 g_ctc.a_stale_cycles++;
136 }
137 g_ctc.a_seen_this_cycle = a_count;
138
139 use_b_fallback = (g_ctc.a_stale_cycles >= (unsigned int)SAFEAPI_EXAMPLE_C_A_STALE_CYCLE_LIMIT);
140 if (use_b_fallback)
141 {
142 uint32_t b_count = 0U;
143
144 while ((status = sapi_channel_service_read(&g_ctc.relay_channel[GATEWAY_C_PEER_B],
145 bytes, sizeof(bytes), 0U)) == SAPI_STATUS_OK)
146 {
147 send_to_sim(ctx, bytes);
148 b_count++;
149 }
150 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
151 {
152 if (g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] == 0U)
153 {
154 (void)sapi_timer_now(&g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B]);
155 }
156 }
157 else if (b_count > 0U)
158 {
159 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
160 }
161 g_ctc.last_known_tag = (b_count > 0U) ? "ACTIVE" : "SUPPRESSED";
162 }
163 else
164 {
165 uint32_t b_count = 0U;
166
167 while ((status = sapi_channel_service_read(&g_ctc.relay_channel[GATEWAY_C_PEER_B],
168 bytes, sizeof(bytes), 0U)) == SAPI_STATUS_OK)
169 {
170 b_count++;
171 }
172 if ((status != SAPI_STATUS_OK) && (status != SAPI_STATUS_TIMEOUT))
173 {
174 if (g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] == 0U)
175 {
176 (void)sapi_timer_now(&g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B]);
177 }
178 }
179 else if (b_count > 0U)
180 {
181 g_ctc.relay_down_since_ms[GATEWAY_C_PEER_B] = 0U;
182 }
183 g_ctc.last_known_tag = (a_count > 0U) ? "ACTIVE" : "SUPPRESSED";
184 }
185 }
186}
187
188void gateway_c_ctc_handler_shutdown(void)
189{
190 (void)sapi_channel_service_close(&g_ctc.sim_channel);
191 (void)sapi_channel_service_close(&g_ctc.relay_channel[GATEWAY_C_PEER_A]);
192 (void)sapi_channel_service_close(&g_ctc.relay_channel[GATEWAY_C_PEER_B]);
193}
194
195bool gateway_c_ctc_handler_check_link_down(sapi_timestamp_ms_t threshold_ms, sapi_timestamp_ms_t now_ms,
196 char *out_which, size_t which_size)
197{
198 gateway_c_peer_t peer;
199
200 for (peer = GATEWAY_C_PEER_A; peer <= GATEWAY_C_PEER_B; peer = (gateway_c_peer_t)((int)peer + 1))
201 {
202 sapi_timestamp_ms_t down_since = g_ctc.relay_down_since_ms[peer];
203
204 if ((down_since != 0U) && ((now_ms - down_since) >= threshold_ms))
205 {
206 (void)snprintf(out_which, which_size, "CTC relay link to %s", peer_name(peer));
207 return true;
208 }
209 }
210 return false;
211}
CTCHandler - Role C Gateway's TCP/IP server for the single CTC sim instance and its dedicated relay t...