Files
video-setup/include/reconciler.h
mikael-lovqvists-claude-agent 639a84b1b9 Add reconciler and ingest modules with CLI driver
reconciler: generic resource state machine — BFS pathfinding from current
to wanted state, dependency constraints, event/periodic tick model.
reconciler_cli exercises it with simulated device/transport/stream resources.

ingest: V4L2 capture module — open device, negotiate MJPEG format, MMAP
buffer pool, capture thread with on_frame callback. start/stop lifecycle
designed for reconciler management. Transport-agnostic: caller wires
on_frame to proto_write_video_frame.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 01:52:17 +00:00

100 lines
3.5 KiB
C

#pragma once
/*
* Generic declarative state machine reconciler.
*
* Each managed resource is described as a directed graph of states
* with labelled transitions. The reconciler finds the shortest path
* (BFS) from a resource's current state to its wanted state and
* executes one transition per tick.
*
* Dependencies between resources prevent a resource from advancing
* past a threshold state until a prerequisite resource reaches a
* minimum state.
*
* Usage:
* struct Reconciler *r = reconciler_create();
*
* static const struct Rec_Transition dev_trans[] = {
* {0, 1, open_device},
* {1, 0, close_device},
* {1, 2, start_capture},
* {2, 1, stop_capture},
* {-1, -1, NULL}
* };
* static const char *dev_states[] = {"CLOSED", "OPEN", "STREAMING"};
* struct Rec_Resource *dev = reconciler_add_resource(r, "device",
* dev_trans, 3, dev_states, 0, &my_device);
*
* reconciler_set_wanted(dev, 2);
* while (!reconciler_is_stable(r)) {
* reconciler_tick(r);
* }
*/
/* Transition table entry. Sentinel: {-1, -1, NULL}.
* action: return 1 on success, 0 on failure.
* On failure the resource stays in 'from' state. */
struct Rec_Transition {
int from;
int to;
int (*action)(void *userdata);
};
typedef enum {
REC_STATUS_STABLE, /* current == wanted */
REC_STATUS_WORKING, /* current != wanted, next transition is eligible */
REC_STATUS_BLOCKED, /* current != wanted, a dependency is unsatisfied */
REC_STATUS_NO_PATH, /* current != wanted, no transition path exists */
} Rec_Status;
struct Reconciler;
struct Rec_Resource;
/* Optional log callback — called after each transition attempt. */
typedef void (*Rec_Log_Fn)(
const struct Rec_Resource *res,
int from, int to, int success,
void *userdata);
struct Reconciler *reconciler_create(void);
void reconciler_destroy(struct Reconciler *r);
/* Set a log callback. Called after every transition attempt. */
void reconciler_set_log(struct Reconciler *r, Rec_Log_Fn fn, void *userdata);
/* Add a resource.
* transitions: caller-owned, sentinel-terminated {-1,-1,NULL}.
* state_names: optional array of state_count strings; NULL for numeric display.
* initial_state: sets both current and wanted initially. */
struct Rec_Resource *reconciler_add_resource(
struct Reconciler *r,
const char *name,
const struct Rec_Transition *transitions,
int state_count,
const char **state_names,
int initial_state,
void *userdata);
/* Add a dependency: resource cannot reach state >= blocked_below
* unless dep is currently in state >= dep_min_state. */
void reconciler_add_dep(
struct Rec_Resource *resource,
int blocked_below,
struct Rec_Resource *dep,
int dep_min_state);
void reconciler_set_wanted(struct Rec_Resource *r, int wanted_state);
int reconciler_get_current(const struct Rec_Resource *r);
int reconciler_get_wanted(const struct Rec_Resource *r);
const char *reconciler_get_name(const struct Rec_Resource *r);
const char *reconciler_state_name(const struct Rec_Resource *r, int state);
Rec_Status reconciler_get_status(const struct Rec_Resource *r);
/* Run one reconciliation pass over all resources.
* Returns number of transitions attempted (success or failure). */
int reconciler_tick(struct Reconciler *r);
/* Returns 1 if all resources have current == wanted. */
int reconciler_is_stable(const struct Reconciler *r);