Chapter 9 — Login State Machine
The login state machine walks each node through the OpenLCB initialization sequence:
sending Initialization Complete, announcing all producer and consumer events, and
finally transitioning to the RUNSTATE_RUN state where normal protocol
processing begins.
src/openlcb/openlcb_login_statemachine.c,
src/openlcb/openlcb_login_statemachine.h
9.1 State Progression
Each node tracks its current state in openlcb_node->state.run_state.
The login state machine only processes nodes whose run_state is less than
RUNSTATE_RUN. Nodes already in RUNSTATE_RUN are skipped.
| State | Handler | Description |
|---|---|---|
RUNSTATE_INIT through RUNSTATE_SEND_AMD |
(CAN login state machine) | Transport-specific initialization handled by the CAN layer (alias reservation, CID/RID/AMD). |
RUNSTATE_LOAD_INITIALIZATION_COMPLETE |
load_initialization_complete() |
Builds and sends the Initialization Complete message with this node's full Node ID. |
RUNSTATE_LOAD_PRODUCER_EVENTS |
load_producer_events() |
Iterates through the node's producer event list, sending Producer Identified for each. Uses the enumerate flag for multi-message sequencing. |
RUNSTATE_LOAD_CONSUMER_EVENTS |
load_consumer_events() |
Same as above but for consumer events. Sends Consumer Identified for each event in the list. |
RUNSTATE_LOGIN_COMPLETE |
on_login_complete() |
Optional callback for application-specific post-login work. If it returns false, the state machine retries next cycle. On success (or if NULL), transitions to RUNSTATE_RUN. |
RUNSTATE_RUN |
(main dispatcher) | Normal protocol operation. The login state machine skips this node from now on. |
9.2 Priority Dispatch
Like the main dispatcher, OpenLcbLoginMainStatemachine_run() uses a priority
chain where the first handler that does work causes an immediate return:
9.3 Event Announcement
During the RUNSTATE_LOAD_PRODUCER_EVENTS and
RUNSTATE_LOAD_CONSUMER_EVENTS phases, the login state machine announces
all events a node produces or consumes. This is accomplished through the enumerate flag:
- The handler checks if the node has events at the current index.
- If yes, it builds a Producer/Consumer Identified message with the event's status
(Unknown, Set, or Clear), sets
outgoing_msg_info.valid = trueandoutgoing_msg_info.enumerate = true, and increments the event index. - The dispatcher sends the message, then re-enters the handler (priority 2).
- When all events have been announced, the handler clears the enumerate flag and
advances the node's
run_stateto the next phase.
9.4 Multi-Node Login
The login state machine uses the same round-robin enumeration pattern as the main
dispatcher. Each call to _run() processes one node at a time. If a node
needs to send multiple messages (event announcements), it uses the enumerate flag
to hold the enumeration position until all messages are sent before advancing to the
next node.
The node enumerator key OPENLCB_LOGIN_STATMACHINE_NODE_ENUMERATOR_INDEX
is separate from the main dispatcher's key, so the two state machines maintain
independent iteration positions.
9.5 The on_login_complete Callback
The optional on_login_complete callback gives the application a chance to
perform post-login initialization (e.g., starting timers, querying the network).
The callback signature:
bool (*on_login_complete)(openlcb_node_t *openlcb_node);
- Returns true: Login is finished; node transitions to
RUNSTATE_RUN. - Returns false: Login is not ready yet; the state machine retries on the next pass.
- NULL: No callback needed; node transitions to
RUNSTATE_RUNimmediately.
9.6 Login Context Structure
The login state machine maintains its own context, openlcb_login_statemachine_info_t,
separate from the main dispatcher context. Its outgoing message buffer uses
BASIC-sized payload (sufficient for Init Complete and event identification
messages, which carry at most 8 bytes of payload).
typedef struct {
openlcb_msg_info_t outgoing_msg_info; // worker buffer + valid/enumerate flags
openlcb_node_t *openlcb_node; // node currently being enumerated
} openlcb_login_statemachine_info_t;