Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
Voter (N-way channel voting)

Files

file  src/redundancy/voter/sapi_voter.c
 N-way voting engine (ADR-025): validates parameters, dispatches send/receive to every registered healthy channel, and - for receive - groups the responses by mutual agreement and picks the largest group meeting quorum.

Data Structures

struct  sapi_voter_config_t
 Configuration for sapi_voter_init(). More...
struct  sapi_voter_storage_t
 Storage for one voter instance (opaque to caller). No dynamic memory. More...

Macros

#define SAPI_VOTER_MAX_CHANNELS   8U
 Maximum number of channels a single voter can register.
#define SAPI_VOTER_MAX_MESSAGE_SIZE   256U
 Maximum payload size sapi_voter_send()/_receive() supports.

Typedefs

typedef bool(*) sapi_voter_compare_fn(const void *a, const void *b, size_t size, void *user_ctx)
 Optional custom comparison callback.
typedef sapi_voter_storage_t sapi_voter_t
 Opaque handle to a voter instance.

Enumerations

enum  sapi_voting_strategy_t { SAPI_VOTING_2OO2 = 1 , SAPI_VOTING_2OO3 = 2 , SAPI_VOTING_NMR = 3 }
 Voting strategy: how many registered channels must agree. More...
enum  sapi_voting_result_t { SAPI_VOTING_AGREED = 0 , SAPI_VOTING_DISAGREED = 1 , SAPI_VOTING_TIMEOUT = 2 , SAPI_VOTING_INSUFFICIENT_QUORUM = 3 }
 Outcome of one sapi_voter_receive() call. More...

Functions

sapi_status_t sapi_voter_init (sapi_voter_storage_t *storage, const sapi_voter_config_t *config)
 Initializes a voter with zero registered channels.
sapi_status_t sapi_voter_register_channel (sapi_voter_t *voter, sapi_channel_t *channel)
 Registers one already-initialized channel with this voter.
sapi_status_t sapi_voter_send (sapi_voter_t *voter, const void *data, size_t data_size)
 Broadcasts data to every registered, healthy channel.
sapi_status_t sapi_voter_receive (sapi_voter_t *voter, void *data, size_t data_size, sapi_voting_result_t *result, size_t *bytes_received)
 Receives from every registered, healthy channel and votes.
sapi_status_t sapi_voter_get_aggregated_health (const sapi_voter_t *voter, uint32_t *healthy_count, uint32_t *total_disagreements)
 Aggregated health across every registered channel.
uint32_t sapi_voter_get_channel_count (const sapi_voter_t *voter)
 Number of channels currently registered with this voter.
sapi_channel_tsapi_voter_get_channel (const sapi_voter_t *voter, uint32_t index)
 Direct access to one registered channel, by index.
sapi_channel_tsapi_voter_get_channel_by_name (const sapi_voter_t *voter, const char *name)
 Direct access to one registered channel, by name (e.g. "ChannelAtoB") - the name each channel was given via sapi_channel_config_t::name at its own sapi_channel_init() time.
sapi_status_t sapi_voter_destroy (sapi_voter_t *voter)
 Destroys a voter instance.

Detailed Description

Macro Definition Documentation

◆ SAPI_VOTER_MAX_CHANNELS

#define SAPI_VOTER_MAX_CHANNELS   8U

Maximum number of channels a single voter can register.

Definition at line 111 of file sapi_voter.h.

◆ SAPI_VOTER_MAX_MESSAGE_SIZE

#define SAPI_VOTER_MAX_MESSAGE_SIZE   256U

Maximum payload size sapi_voter_send()/_receive() supports.

Definition at line 114 of file sapi_voter.h.

Typedef Documentation

◆ sapi_voter_compare_fn

typedef bool(*) sapi_voter_compare_fn(const void *a, const void *b, size_t size, void *user_ctx)

Optional custom comparison callback.

If not registered (sapi_voter_config_t::compare == NULL), the default is a full byte compare (memcmp(a, b, size) == 0) - the payloads being compared have already had their transport-integrity CRC verified by the channel layer (sapi_checksum) if the backend uses sapi_checksum_vital_message_verify(); this callback is about semantic/value comparison, not integrity checking.

Parameters
[in]aFirst buffer.
[in]bSecond buffer.
[in]sizeNumber of bytes to compare.
[in]user_ctxsapi_voter_config_t::compare_context, passed through verbatim.
Returns
true if a and b are considered equal for voting purposes.

Definition at line 73 of file sapi_voter.h.

◆ sapi_voter_t

Opaque handle to a voter instance.

Definition at line 128 of file sapi_voter.h.

Enumeration Type Documentation

◆ sapi_voting_strategy_t

Voting strategy: how many registered channels must agree.

Enumerator
SAPI_VOTING_2OO2 

2 out of 2: both channels must agree.

SAPI_VOTING_2OO3 

2 out of 3: majority among 3 channels, tolerates 1 fault.

SAPI_VOTING_NMR 

N out of M: sapi_voter_config_t::quorum_size of registered channels must agree.

Definition at line 33 of file sapi_voter.h.

◆ sapi_voting_result_t

Outcome of one sapi_voter_receive() call.

Enumerator
SAPI_VOTING_AGREED 

A quorum-sized group of channels agreed on the data.

SAPI_VOTING_DISAGREED 

No group of channels large enough to meet quorum agreed.

SAPI_VOTING_TIMEOUT 

One or more channels timed out.

SAPI_VOTING_INSUFFICIENT_QUORUM 

Fewer than the required number of healthy/responding channels.

Definition at line 45 of file sapi_voter.h.

Function Documentation

◆ sapi_voter_init()

sapi_status_t sapi_voter_init ( sapi_voter_storage_t * storage,
const sapi_voter_config_t * config )

Initializes a voter with zero registered channels.

Parameters
[out]storagePre-allocated storage. Must not be NULL.
[in]configVoting configuration. Must not be NULL. For SAPI_VOTING_NMR, config->quorum_size must be >= 1.
Returns
SAPI_STATUS_OK on success
SAPI_STATUS_INVALID_PARAM if storage/config is NULL, or (NMR strategy and quorum_size == 0)

REQ-VOTER-001: No dynamic allocation; fixed SAPI_VOTER_MAX_CHANNELS array. REQ-VOTER-002: Rejects an unrecognized voting_strategy, or NMR with quorum_size == 0.

Definition at line 97 of file sapi_voter.c.

◆ sapi_voter_register_channel()

sapi_status_t sapi_voter_register_channel ( sapi_voter_t * voter,
sapi_channel_t * channel )

Registers one already-initialized channel with this voter.

Parameters
[in]voterVoter handle. Must not be NULL and must be initialized.
[in]channelAlready sapi_channel_init()'d channel. Must not be NULL.
Returns
SAPI_STATUS_OK on success
SAPI_STATUS_INVALID_PARAM if voter/channel is NULL
SAPI_STATUS_NOT_INITIALIZED if voter was never initialized
SAPI_STATUS_RESOURCE_EXHAUSTED if SAPI_VOTER_MAX_CHANNELS are already registered
Postcondition
For SAPI_VOTING_2OO2/2OO3, sapi_voter_send()/_receive() require exactly 2/3 channels to be registered respectively before use; calling them with the wrong count returns SAPI_STATUS_INVALID_PARAM.

Definition at line 136 of file sapi_voter.c.

◆ sapi_voter_send()

sapi_status_t sapi_voter_send ( sapi_voter_t * voter,
const void * data,
size_t data_size )

Broadcasts data to every registered, healthy channel.

Parameters
[in]voterVoter handle. Must not be NULL and initialized.
[in]dataData to send. Must not be NULL.
[in]data_sizeSize in bytes; must be > 0 and <= SAPI_VOTER_MAX_MESSAGE_SIZE.
Returns
SAPI_STATUS_OK if every healthy channel accepted the send
SAPI_STATUS_INVALID_PARAM for a bad argument or wrong registered-channel count for a fixed strategy (2OO2/2OO3)
SAPI_STATUS_RESOURCE_EXHAUSTED if data_size exceeds the max
SAPI_STATUS_HARDWARE_FAULT if no channel is healthy, or any healthy channel's send failed

REQ-VOTER-003: Requires the registered-channel count to match the configured strategy before sending.

Definition at line 165 of file sapi_voter.c.

◆ sapi_voter_receive()

sapi_status_t sapi_voter_receive ( sapi_voter_t * voter,
void * data,
size_t data_size,
sapi_voting_result_t * result,
size_t * bytes_received )

Receives from every registered, healthy channel and votes.

Groups the channels that responded successfully by mutual agreement (per config->compare, default memcmp); the largest group that meets the configured quorum wins - AGREED, with *data set to that group's value. If no group is large enough, DISAGREED. If fewer than 2 channels responded at all, TIMEOUT (0 responded) or INSUFFICIENT_QUORUM (1 responded, or not enough healthy channels to possibly meet quorum before even trying).

Parameters
[in]voterVoter handle. Must not be NULL and initialized.
[out]dataBuffer to receive the winning group's data. Must not be NULL.
[in]data_sizeSize of data buffer; must be > 0 and <= SAPI_VOTER_MAX_MESSAGE_SIZE.
[out]resultVoting outcome. Can be NULL.
[out]bytes_receivedBytes written to data. Can be NULL.
Returns
SAPI_STATUS_OK if result is SAPI_VOTING_AGREED
SAPI_STATUS_INVALID_PARAM for a bad argument or wrong registered-channel count for a fixed strategy
SAPI_STATUS_HARDWARE_FAULT otherwise (DISAGREED/TIMEOUT/ INSUFFICIENT_QUORUM)
Postcondition
On SAPI_VOTING_DISAGREED, if config->trigger_safestate_on_disagreement is true (the default expectation), this function does not return - see SAPI_SAFESTATE_LEVEL_SAFE.

REQ-VOTER-003: Requires the registered-channel count to match the configured strategy before receiving/voting. REQ-VOTER-004: Groups responses by mutual agreement and selects the largest quorum-meeting group (majority vote), not a pairwise compare against a single reference channel. REQ-VOTER-005: On DISAGREED, enters SAPI_SAFESTATE_LEVEL_SAFE when trigger_safestate_on_disagreement is true, and always invokes on_disagreement (if registered) regardless of that flag.

Definition at line 227 of file sapi_voter.c.

◆ sapi_voter_get_aggregated_health()

sapi_status_t sapi_voter_get_aggregated_health ( const sapi_voter_t * voter,
uint32_t * healthy_count,
uint32_t * total_disagreements )

Aggregated health across every registered channel.

Parameters
[in]voterVoter handle. Must not be NULL.
[out]healthy_countNumber of currently-healthy registered channels. Can be NULL.
[out]total_disagreementsRunning count of DISAGREED results returned by this voter. Can be NULL.
Returns
SAPI_STATUS_OK on success
SAPI_STATUS_INVALID_PARAM if voter is NULL

Definition at line 412 of file sapi_voter.c.

◆ sapi_voter_get_channel_count()

uint32_t sapi_voter_get_channel_count ( const sapi_voter_t * voter)

Number of channels currently registered with this voter.

Intended for callers (e.g. sapi_channel_checkpoint()) that need to iterate the voter's registered channels directly rather than through send()/receive()'s own voting.

Parameters
[in]voterVoter handle. Must not be NULL.
Returns
Registered channel count, or 0 if voter is NULL.

Definition at line 431 of file sapi_voter.c.

◆ sapi_voter_get_channel()

sapi_channel_t * sapi_voter_get_channel ( const sapi_voter_t * voter,
uint32_t index )

Direct access to one registered channel, by index.

Parameters
[in]voterVoter handle. Must not be NULL.
[in]index0-based index; must be < sapi_voter_get_channel_count(voter).
Returns
The registered channel, or NULL if voter is NULL or index is out of range.

Definition at line 436 of file sapi_voter.c.

◆ sapi_voter_get_channel_by_name()

sapi_channel_t * sapi_voter_get_channel_by_name ( const sapi_voter_t * voter,
const char * name )

Direct access to one registered channel, by name (e.g. "ChannelAtoB") - the name each channel was given via sapi_channel_config_t::name at its own sapi_channel_init() time.

Linear search over the registered channels (channel counts here are small - SAPI_VOTER_MAX_CHANNELS - so this is not a hot-path lookup by design); compares with strcmp(), so an exact, case-sensitive match is required.

Parameters
[in]voterVoter handle. Must not be NULL.
[in]nameName to search for. Must not be NULL.
Returns
The first registered channel whose own name matches (exact strcmp() equality); NULL if voter or name is NULL, or no registered channel has a matching (non-NULL) name.

Definition at line 445 of file sapi_voter.c.

◆ sapi_voter_destroy()

sapi_status_t sapi_voter_destroy ( sapi_voter_t * voter)

Destroys a voter instance.

No dynamic memory to free; registered channels are NOT destroyed (caller retains ownership).

Parameters
[in]voterVoter handle. May be NULL.
Returns
SAPI_STATUS_OK always.
Safety:
Idempotent; safe to call with NULL.

Definition at line 465 of file sapi_voter.c.