Safe API Framework 0.1.0 build 384e6ae
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
Status Module - Architecture

Design Overview

The Status module defines a unified error handling system across the entire framework. Every operation returns a rte_status_t code that indicates success or the specific failure reason.

Design principle:** Consistency. All modules use the same status codes, making error handling predictable and consistent.

Status Code Definitions

The numeric values are part of the ABI (rte_status.h is authoritative): existing entries are never renumbered, new ones are appended.

typedef enum {
RTE_STATUS_OK = 0, // Success
RTE_STATUS_INVALID_PARAM = 1, // Null pointer or out-of-range argument
RTE_STATUS_NOT_INITIALIZED = 2, // Object used before create/init
RTE_STATUS_ALREADY_INITIALIZED = 3, // Object already created/initialized
RTE_STATUS_TIMEOUT = 4, // Blocking call exceeded its deadline
RTE_STATUS_RESOURCE_EXHAUSTED = 5, // Static pool/storage/slots full
RTE_STATUS_NOT_SUPPORTED = 6, // Valid request, OSAdapter cannot perform it
RTE_STATUS_NOT_IMPLEMENTED = 7, // OSAdapter is a stub
RTE_STATUS_HARDWARE_FAULT = 8, // OSAdapter reported a hardware-level fault
RTE_STATUS_DATA_CORRUPTION = 9, // Integrity check (e.g. NVM CRC) failed
RTE_STATUS_INTERNAL_ERROR = 10, // Defensive catch-all
RTE_STATUS_VALUE_OUT_OF_RANGE = 11, // Checked cast: value does not fit
RTE_STATUS_INVALID_STATE = 12, // Not permitted in the current lifecycle phase
RTE_STATUS_NOT_CONNECTED = 13 // Listen-role link has no peer yet; nothing sent (REQ-CHANSVC-001)
rte_status_t
Common result/status codes.
Definition rte_status.h:27
@ RTE_STATUS_INTERNAL_ERROR
Definition rte_status.h:38
@ RTE_STATUS_INVALID_STATE
Definition rte_status.h:40
@ RTE_STATUS_NOT_IMPLEMENTED
Definition rte_status.h:35
@ RTE_STATUS_OK
Definition rte_status.h:28
@ RTE_STATUS_ALREADY_INITIALIZED
Definition rte_status.h:31
@ RTE_STATUS_VALUE_OUT_OF_RANGE
Definition rte_status.h:39
@ RTE_STATUS_INVALID_PARAM
Definition rte_status.h:29
@ RTE_STATUS_NOT_SUPPORTED
Definition rte_status.h:34
@ RTE_STATUS_DATA_CORRUPTION
Definition rte_status.h:37
@ RTE_STATUS_NOT_INITIALIZED
Definition rte_status.h:30
@ RTE_STATUS_HARDWARE_FAULT
Definition rte_status.h:36
@ RTE_STATUS_RESOURCE_EXHAUSTED
Definition rte_status.h:33
@ RTE_STATUS_NOT_CONNECTED
Definition rte_status.h:44
@ RTE_STATUS_TIMEOUT
Definition rte_status.h:32

Code Allocation Strategy

0: RTE_STATUS_OK (success - no error)
1-13: Framework core status codes (parameter, timeout, hardware, etc.)
14-999: Reserved for framework expansion
1000+: Application-specific error codes (user can extend)

Design Rationale

Why Unified Status Codes?

Without framework status codes:**

int timer_result = rte_timer_create(...); // What does -1 mean?
bool buffer_result = rte_buffer_write(...); // What does false mean?
errno = ...; // Different modules use different mechanisms
rte_status_t rte_timer_create(rte_timer_storage_t *storage, const rte_timer_config_t *config, rte_timer_handle_t *out_handle)
Creates a timer bound to caller-owned storage. Does not start it.
Definition rte_timer.c:44

Problem: Inconsistent error handling across modules.

With framework status codes:**

rte_status_t buffer_rc = rte_buffer_write(...);
// Both return the SAME code type
// Same error handling everywhere

Why Not Use errno?

  • errno is thread-unsafe (global state)
  • errno is platform-specific (POSIX assumption)
  • errno values vary by system
  • MISRA-compliant systems avoid global state

Application-Specific Codes

Applications can define their own status codes:

// In application header
typedef enum {
// ... framework codes ...
// Application-specific (starting from 1000)
APP_STATUS_TRAIN_SPEED_LIMIT_EXCEEDED = 1000,
APP_STATUS_TRACK_INTEGRITY_CHECK_FAILED = 1001,
APP_STATUS_SIGNAL_ASPECT_UNKNOWN = 1002,
} app_status_t;

String Conversion

The module provides rte_status_to_string() for diagnostics:

rte_status_t rc = some_operation();
if (rc != RTE_STATUS_OK) {
printf("Error: %s\n", rte_status_to_string(rc));
// Output: "Error: TIMEOUT"
}
const char * rte_status_to_string(rte_status_t status)
Returns a short, static, human-readable string for a status code. Intended for diagnostics/logging on...
Definition rte_status.c:22

The strings are the short names without the RTE_STATUS_ prefix ("OK", "TIMEOUT", "NOT_CONNECTED", ...); a value outside the enum yields "UNKNOWN_STATUS". The result is never NULL.

Implementation** (src/utils/status/rte_status.c, abridged):

{
switch (status)
{
case RTE_STATUS_OK: return "OK";
case RTE_STATUS_INVALID_PARAM: return "INVALID_PARAM";
case RTE_STATUS_TIMEOUT: return "TIMEOUT";
// ... one case per enumerator, up to RTE_STATUS_NOT_CONNECTED ...
default: return "UNKNOWN_STATUS";
}
}

Usage Patterns in Framework

Pattern 1: Initialization Check

Every module follows this pattern:

rte_status_t rte_module_init(config_t *config) {
// Validate input
if (config == NULL) {
}
// Check if already initialized
if (is_initialized) {
}
// Perform initialization
// ...
return RTE_STATUS_OK;
}

Pattern 2: Operation with Timeout

Operations that can timeout:

rte_status_t rte_channel_recv(handle, buffer, size, timeout) {
// Try to receive data within timeout
// ...
if (no_data_within_timeout) {
return RTE_STATUS_TIMEOUT; // Caller knows why
}
// ...
return RTE_STATUS_OK;
}

Pattern 3: Resource Exhaustion

Operations that can fail due to capacity:

rte_status_t rte_buffer_write(handle, data, size, written) {
// Check buffer capacity
if (buffer_remaining < size) {
}
// ...
return RTE_STATUS_OK;
}

Integration with Safe-State

Status codes inform safe-state decisions. The two snippets below are example application policy, not a rule the framework enforces: which status from which call leads to a safe state is decided by the application (the framework itself enters a safe state only where a requirement says so, e.g. REQ-CHECKPOINT-003, REQ-VOTER-005).

Critical failure (example policy):**

rte_status_t rc = vital_operation();
if (rc != RTE_STATUS_OK) {
// Example policy: this application treats any failure of this vital call as unsafe.
// APP_REASON_VITAL_OP_FAILED: an application-defined reason code
// (>= RTE_SAFESTATE_REASON_APPLICATION_BASE, see rte_safestate.h).
RTE_SAFESTATE(RTE_SAFESTATE_LEVEL_SAFE, APP_REASON_VITAL_OP_FAILED);
}
#define RTE_SAFESTATE(level, reason)
Explicitly enters the given safe-state level with a reason code, capturing the call site automaticall...
@ RTE_SAFESTATE_LEVEL_SAFE

Non-critical operation:**

rte_status_t rc = diagnostics_operation();
if (rc != RTE_STATUS_OK) {
// Log but DON'T trigger safe-state
log_debug("Diagnostics error: %s", rte_status_to_string(rc));
}

MISRA Compliance

The status module supports MISRA C:2012:

  • ✓ Fixed return type (rte_status_t enum)
  • ✓ No implicit conversions (explicit enum values)
  • ✓ No global state (errno-free)
  • ✓ Deterministic (same code always means same error)
  • ✓ Thread-safe (no shared state)

Implementation Details

Files

String Conversion

rte_status_to_string() is a switch with one case per enumerator and a default clause (MISRA C:2012 rule 16.4) returning "UNKNOWN_STATUS"; there is no lookup table (see the Implementation snippet above).

Pros: no array indexed by an external value (no out-of-bounds read for a value outside the enum), and a value outside the enum is handled explicitly by default. Cons: a new enumerator needs its own case and its own entry in the unit test's codes[] list; with both, the test catches a missing case (it would return "UNKNOWN_STATUS").

Testing Strategy

Unit Tests

tests/status/test_rte_status.c (abridged):

for (i = 0; i < sizeof(codes) / sizeof(codes[0]); ++i) {
const char *s = rte_status_to_string(codes[i]);
assert(s != NULL);
assert(strcmp(s, "UNKNOWN_STATUS") != 0); // every known code has its own name
// ... and no two codes share a value or a string
}
assert(strcmp(rte_status_to_string(RTE_STATUS_NOT_CONNECTED), "NOT_CONNECTED") == 0);
// Value outside the enum: the default clause
assert(strcmp(rte_status_to_string((rte_status_t)999), "UNKNOWN_STATUS") == 0);

Future Extensions

Possible extensions (reserved code space):

  • Additional timeout types (READ_TIMEOUT vs WRITE_TIMEOUT)
  • Buffer-specific codes (BUFFER_EMPTY, BUFFER_FULL)
  • Network codes (CONNECTION_REFUSED, CONNECTION_RESET)
  • All would fit within 1000+ application-specific range