Channel Identification with Names and Callbacks
Each channel has a unique name and optional callback function. The App Manager controls WHEN to read channels. When the App Manager decides to read, callbacks are invoked for channels that have data available.
Key Design:** Not direct event-driven, but controlled by App Manager. App Manager decides when to poll channels, and callbacks are invoked only when the App Manager explicitly reads.
Channel Naming Convention
Channel names serve as unique identifiers and should follow a consistent pattern:
"<source>_<dest>_<purpose>"
Examples:
"online_to_standby_vital" - Online sends commands to standby
"standby_to_online_feedback" - Standby replies with status
"online_to_diag_telemetry" - Online sends telemetry to diagnostics service
"config_update_channel" - Configuration updates from external source
Benefits of Named Channels:**
- ✓ Easy to identify which channel has data in logs
- ✓ Debugging: "Data from online_to_standby_vital"
- ✓ Monitoring: Track metrics per channel name
- ✓ Routing: Dispatch based on channel name
Callback Registration Pattern
Callback Function Signature
const char *channel_name,
const void *data,
size_t data_size,
void *context
);
void(*) rte_channel_callback_t(const char *channel_name, const void *data, size_t data_size, void *context)
Callback invoked when data is available on a channel.
Extended Channel Configuration
typedef struct {
const char *name;
const char *remote_ip;
uint16_t remote_port;
size_t message_size;
uint32_t timeout_ms;
bool server_mode;
void *callback_context;
} rte_ipc_config_tcp_t;
Example: Registering Callbacks
void on_command_received(const char *channel_name,
const void *data, size_t size,
void *context)
{
printf("Data received on channel: %s (%zu bytes)\n", channel_name, size);
process_command(cmd);
}
rte_ipc_config_tcp_t tcp_config = {
.name = "online_to_standby_vital",
.remote_ip = "192.168.1.100",
.remote_port = 5000,
.message_size = 256,
.timeout_ms = 1000,
.server_mode = false,
.on_data_available = on_command_received,
.callback_context = &app_context,
};
rte_ipc_create_tcp(&tcp_channel, &tcp_config);
struct rte_ipc_impl_s * rte_ipc_handle_t
Opaque handle to a created IPC channel, returned by rte_ipc_create().
Event-Driven Application Loop
Instead of polling channels, the framework calls your callbacks:
while (running) {
if (vital_channel_recv(&vital, &cmd, timeout=0) == OK) {
process_command(&cmd);
}
if (feedback_channel_recv(&feedback, &status, timeout=0) == OK) {
update_status(&status);
}
if (config_channel_recv(&config, &cfg, timeout=0) == OK) {
apply_config(&cfg);
}
sleep(10ms);
}
vital_config.on_data_available = on_command_received;
feedback_config.on_data_available = on_feedback_received;
config_config.on_data_available = on_config_update;
while (running) {
wait_for_event(channels[], timeout=1000);
}
Benefits:**
- ✓ Responsive: Data triggers callback immediately
- ✓ CPU-efficient: No busy-waiting or unnecessary polling
- ✓ Clean: Callbacks handle their own data
- ✓ Scalable: Works with many channels
Callback Examples by Channel Type
Example 1: Vital Command Channel
void on_vital_command(const char *channel_name,
const void *data, size_t size,
void *context)
{
log_info("Vital command received on %s", channel_name);
if (size != sizeof(*cmd)) {
log_error("Invalid command size: expected %zu, got %zu",
sizeof(*cmd), size);
return;
}
execute_train_command(cmd);
app->vital_commands_received++;
}
rte_ipc_config_tcp_t vital_config = {
.name = "online_to_standby_vital",
.on_data_available = on_vital_command,
.callback_context = &app_context,
};
Example 2: Feedback/Health Channel
void on_standby_feedback(const char *channel_name,
const void *data, size_t size,
void *context)
{
log_debug("Feedback from %s", channel_name);
app->standby_healthy = status->is_healthy;
app->standby_last_heartbeat = get_time_ms();
if (status->disagreement_count > THRESHOLD) {
log_warning("Standby disagreement rate high on %s", channel_name);
trigger_watchdog_check();
}
}
feedback_config.on_data_available = on_standby_feedback;
feedback_config.callback_context = &app_context;
Example 3: Non-Vital Diagnostics Channel
void on_telemetry(const char *channel_name,
const void *data, size_t size,
void *context)
{
log_debug("Telemetry on %s", channel_name);
telemetry_t *telem = (telemetry_t *)data;
send_to_influxdb(channel_name, telem);
}
telemetry_config.on_data_available = on_telemetry;
Example 4: Configuration Update Channel
void on_config_update(const char *channel_name,
const void *data, size_t size,
void *context)
{
log_info("Configuration update on %s", channel_name);
config_t *new_config = (config_t *)data;
if (!validate_config(new_config)) {
log_error("Invalid config on %s", channel_name);
return;
}
apply_configuration(new_config);
log_info("Config applied from %s", channel_name);
}
config_config.on_data_available = on_config_update;
Channel Registry Pattern
For systems with many channels, maintain a registry:
typedef struct {
const char *name;
void *context;
typedef struct {
uint32_t count;
const char *name,
void *context)
{
if (reg->count >= MAX_CHANNELS) {
}
reg->channels[reg->count] = {
.name = name,
.handle = handle,
.callback = callback,
.context = context,
};
reg->count++;
}
const char *name)
{
for (uint32_t i = 0; i < reg->count; i++) {
if (strcmp(reg->channels[i].name, name) == 0) {
return ®->channels[i];
}
}
return NULL;
}
"online_to_standby_vital");
if (vital_ch) {
}
@ RTE_STATUS_RESOURCE_EXHAUSTED
Logging and Monitoring via Channel Names
Channel names enable structured logging and monitoring:
void log_channel_activity(const char *channel_name, const char *event) {
uint64_t timestamp = get_time_ms();
printf("[%llu] CHANNEL[%s] %s\n", timestamp, channel_name, event);
}
typedef struct {
const char *channel_name;
uint32_t messages_received;
uint32_t errors;
uint32_t bytes_transferred;
uint64_t last_activity_time;
} channel_metrics_t;
channel_metrics_t *find_or_create_metrics(const char *channel_name) {
}
void on_data_with_metrics(const char *channel_name,
const void *data, size_t size,
void *context)
{
channel_metrics_t *metrics = find_or_create_metrics(channel_name);
metrics->messages_received++;
metrics->bytes_transferred += size;
metrics->last_activity_time = get_time_ms();
process_data(channel_name, data, size);
}
Best Practices
Keep Callbacks Short
- Do minimal work in callback
- Queue data for later processing if needed
- Avoid blocking operations
void on_data_short(const char *channel_name,
const void *data, size_t size,
void *context)
{
queue_push(&app->work_queue, data, size);
}
Use Channel Name for Routing
- Identify which channel has data
- Route to appropriate handler
- Easy debugging
if (strcmp(channel_name, "vital_command") == 0) {
handle_vital_command(data);
} else if (strcmp(channel_name, "feedback") == 0) {
handle_feedback(data);
}
Log Channel Names in Errors
- Always include channel name in error logs
- Helps with debugging and monitoring
if (size != expected_size) {
log_error("Channel %s: invalid size %zu (expected %zu)",
channel_name, size, expected_size);
}
- Register Callbacks at Startup
- Don't change callbacks at runtime (if possible)
- Register all channels before main loop
- Makes startup sequence clear
OS Integrator Requirements
OS OSAdapter must:**