Overview
The Application Manager provides lifecycle management (init → execute → shutdown). The developer implements the execute() function which runs the main loop.
Key Point:** App Manager does NOT provide:
Integration Architecture
│
└─ Calls ops->init(&context) ◄─ You implement this
│
└─ Loop: Calls ops->execute(&context) ◄─ You implement this
│ ├─ Read channels with callbacks
│ ├─ Handle timers
│ ├─ Handle tasks
│ ├─ Prepare data
│ └─ Send output
│
└─ Calls ops->shutdown(&context) ◄─ You implement this
int rte_appmanager_run(const rte_appmanager_config_t *config)
Run application with lifecycle management.
Application Lifecycle
1. Initialization (init)
Called once at startup. Your responsibility:
- Create and register named channels
- Initialize channel registry
- Initialize timer service
- Initialize task queue
- Allocate application state
my_app_t *app = (my_app_t *)context;
app->channel_registry = create_channel_registry();
tcp_config.name = "online_to_standby_vital";
tcp_config.remote_ip = "192.168.1.100";
tcp_config.remote_port = 5000;
tcp_config.on_data_available = on_vital_command;
tcp_config.callback_context = app;
rte_ipc_create_tcp(&app->vital_channel, &tcp_config);
app->channel_registry,
"online_to_standby_vital",
on_vital_command,
app
);
app->timers = timer_service_init(MAX_TIMERS);
app->tasks = task_queue_init(MAX_TASKS);
}
rte_status_t channel_registry_register(const char *name, rte_channel_callback_t callback, void *context)
Register a named channel with callback.
rte_status_t
Common result/status codes.
2. Main Loop (execute)
Called repeatedly. YOUR MAIN LOOP - typical 10ms cycle:
my_app_t *app = (my_app_t *)context;
uint64_t cycle_start = get_time_ms();
read_channels_with_callbacks(app->channel_registry);
timer_event_t expired[10];
int count = timer_service_get_expired(app->timers, expired, 10);
for (int i = 0; i < count; i++) {
handle_timer(&expired[i], app);
}
task_t pending[10];
int task_count = task_queue_get_pending(app->tasks, pending, 10);
for (int i = 0; i < task_count; i++) {
execute_task(&pending[i], app);
}
prepare_dual_output(&app->state, &output);
telemetry_t telemetry;
prepare_telemetry(&app->state, &telemetry);
uint64_t elapsed = get_time_ms() - cycle_start;
if (elapsed < 10) {
sleep_ms(10 - elapsed);
} else if (elapsed > 15) {
log_warning("Cycle overrun: %llu ms", elapsed);
}
}
rte_status_t rte_ipc_send(rte_ipc_handle_t handle, const void *message, size_t message_size, rte_duration_ms_t timeout_ms)
Sends one message, blocking at most timeout_ms.
rte_status_t rte_channel_send(rte_channel_t *handle, const void *data, size_t data_size)
Sends data on this channel via its OSAdapter send callback.
What App Manager Does:**
3. Shutdown (shutdown)
Called once at termination (always, even on error):
my_app_t *app = (my_app_t *)context;
timer_service_destroy(app->timers);
task_queue_destroy(app->tasks);
channel_registry_destroy(app->channel_registry);
log_info("Application shutdown complete");
}
rte_status_t rte_ipc_destroy(rte_ipc_handle_t handle)
Destroys a message channel.
Complete Example
Application Context
typedef struct {
timer_service_t *timers;
task_queue_t *tasks;
uint32_t iteration_count;
uint32_t vital_messages_received;
uint32_t feedback_messages_received;
} my_app_t;
struct rte_ipc_impl_s * rte_ipc_handle_t
Opaque handle to a created IPC channel, returned by rte_ipc_create().
rte_channel_storage_t rte_channel_t
Opaque handle to a vital channel instance.
Main Function
int main(void) {
my_app_t app = {0};
.init = my_app_init,
.execute = my_app_execute,
.shutdown = my_app_shutdown,
.get_name = my_app_get_name,
.get_version = my_app_get_version,
};
.ops = &ops,
.context = &app,
.max_iterations = 0,
.error_threshold = 10,
};
}
Application manager configuration.
Application lifecycle operations.
App Manager Behavior:**
- Calls my_app_init(&app)
- Creates channels, timers, tasks
- Loop: Calls my_app_execute(&app) repeatedly
- Your main loop runs here
- Reads channels with callbacks
- Processes timers and tasks
- Sends output
- Calls my_app_shutdown(&app)
- Returns 0 (success) or 1 (failure)
Responsibility Summary
App Manager Provides:**
What You Need to Implement
These are NOT provided by the framework - you implement them:
typedef struct {
channel_entry_t channels[MAX_CHANNELS];
uint32_t count;
const char *name,
void *context);
timer_service_t *timer_service_init(uint32_t max_timers);
int timer_service_get_expired(timer_service_t *ts,
timer_event_t *expired,
int max_count);
void timer_service_destroy(timer_service_t *ts);
task_queue_t *task_queue_init(uint32_t max_tasks);
int task_queue_get_pending(task_queue_t *tq,
task_t *pending,
int max_count);
void task_queue_destroy(task_queue_t *tq);
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.
These are project-specific - the framework gives you the patterns, you implement the concrete services.
Design Guidelines
- Deterministic Timing
- Decide fixed cycle time (e.g., 10ms)
- App Manager calls execute() repeatedly
- YOU maintain the cycle time inside execute()
- Channel Reading
- App Manager does NOT read channels automatically
- YOU explicitly call read_channels_with_callbacks()
- YOU decide when in the cycle to read
- Error Handling
- execute() errors are caught by App Manager
- Count errors, trigger shutdown at threshold
- Graceful degradation (continue if possible)
- Shutdown
- shutdown() is always called
- Must be robust (even if init/execute failed)
- Close resources, log final state
Next Steps
- Understand App Manager lifecycle (init/execute/shutdown)
- Implement channel registry for named channels
- Implement timer service
- Implement task queue
- Build main loop in execute() function
- Test with your actual transports (TCP, FIFO, etc.)