1use crate::common::ApiState;
8use crate::common::{ApiError, ApiResult, Json, State};
9use serde::Serialize;
10use std::collections::HashMap;
11
12#[utoipa::path(
14 get,
15 path = "/v1/input/vision",
16 tag = "input",
17 responses(
18 (status = 200, description = "Vision input configuration", body = HashMap<String, serde_json::Value>),
19 (status = 500, description = "Internal server error")
20 )
21)]
22pub async fn get_vision(
23 State(_state): State<ApiState>,
24) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
25 Ok(Json(HashMap::new()))
27}
28
29#[utoipa::path(
31 post,
32 path = "/v1/input/vision",
33 tag = "input",
34 responses(
35 (status = 200, description = "Vision input updated", content_type = "application/json"),
36 (status = 500, description = "Not yet implemented")
37 )
38)]
39pub async fn post_vision(
40 State(_state): State<ApiState>,
41 Json(_req): Json<HashMap<String, serde_json::Value>>,
42) -> ApiResult<Json<HashMap<String, String>>> {
43 Err(ApiError::internal("Not yet implemented"))
44}
45
46#[utoipa::path(get, path = "/v1/input/sources", tag = "input")]
48pub async fn get_sources(State(_state): State<ApiState>) -> ApiResult<Json<Vec<String>>> {
49 Ok(Json(vec!["vision".to_string()]))
50}
51
52#[utoipa::path(post, path = "/v1/input/configure", tag = "input")]
54pub async fn post_configure(
55 State(_state): State<ApiState>,
56 Json(_req): Json<HashMap<String, serde_json::Value>>,
57) -> ApiResult<Json<HashMap<String, String>>> {
58 Ok(Json(HashMap::from([(
59 "message".to_string(),
60 "Input configured".to_string(),
61 )])))
62}
63
64#[derive(Serialize, Clone, Debug, utoipa::ToSchema)]
70pub struct SensorTapSample {
71 pub x: u32,
72 pub y: u32,
73 pub z: u32,
74 pub potential: f32,
75}
76
77#[derive(Serialize, Clone, Debug, utoipa::ToSchema)]
79pub struct SensorTapArea {
80 pub cortical_id: String,
81 pub cortical_idx: u32,
82 pub neuron_count: usize,
83 pub samples: Vec<SensorTapSample>,
84}
85
86#[derive(Serialize, Clone, Debug, utoipa::ToSchema)]
88pub struct SensorSnapshotResponse {
89 pub burst_num: u64,
92 pub timestamp_ms: i64,
94 pub has_data: bool,
96 pub total_areas: usize,
98 pub total_neurons: usize,
100 pub areas: Vec<SensorTapArea>,
102}
103
104#[utoipa::path(
110 get,
111 path = "/v1/input/sensor_snapshot/last",
112 tag = "input",
113 params(
114 ("cortical_id" = Option<String>, Query, description = "Filter areas by base64 cortical id")
115 ),
116 responses(
117 (status = 200, description = "Latest sensory pipeline snapshot", body = SensorSnapshotResponse),
118 (status = 500, description = "Internal server error")
119 )
120)]
121pub async fn get_sensor_snapshot_last(
122 State(_state): State<ApiState>,
123 axum::extract::Query(query): axum::extract::Query<HashMap<String, String>>,
124) -> ApiResult<Json<SensorSnapshotResponse>> {
125 let snap = feagi_npu_burst_engine::BurstTaps::instance().sensor_snapshot();
126 let cortical_filter = query.get("cortical_id").cloned();
127
128 let mut areas: Vec<SensorTapArea> = snap
129 .areas
130 .into_iter()
131 .filter(|a| match &cortical_filter {
132 Some(filter) => filter == &a.cortical_id,
133 None => true,
134 })
135 .map(|a| SensorTapArea {
136 cortical_id: a.cortical_id,
137 cortical_idx: a.cortical_idx,
138 neuron_count: a.neuron_count,
139 samples: a
140 .samples
141 .into_iter()
142 .map(|s| SensorTapSample {
143 x: s.x,
144 y: s.y,
145 z: s.z,
146 potential: s.potential,
147 })
148 .collect(),
149 })
150 .collect();
151 areas.sort_by(|a, b| a.cortical_id.cmp(&b.cortical_id));
152
153 let total_areas = areas.len();
154 let total_neurons: usize = areas.iter().map(|a| a.neuron_count).sum();
155 let has_data = total_areas > 0 && snap.burst_num > 0;
156
157 Ok(Json(SensorSnapshotResponse {
158 burst_num: snap.burst_num,
159 timestamp_ms: snap.timestamp_ms,
160 has_data,
161 total_areas,
162 total_neurons,
163 areas,
164 }))
165}