1use core::future::Future;
2use core::mem::MaybeUninit;
3
4use embassy_futures::join::join;
5use embassy_sync::blocking_mutex::raw::RawMutex;
6use embassy_sync::channel::{Channel, Receiver};
7use embedded_storage_async::nor_flash::NorFlash;
8use heapless::Vec as HeaplessVec;
9use static_cell::StaticCell;
10
11use crate::engine::{IssuedCommand, Journaled, MAX_SEND_REQUEST_DATA_LEN};
12use crate::interfaces::{InterfaceDescriptor, InterfaceId, InterfaceIfac};
13use crate::manifold::driver::{
14 run_pooled, InterfaceLifecycle, PooledEgress, PooledWiring, ResumableHost,
15};
16use crate::manifold::grant::ManifoldLaneReader;
17use crate::manifold::Host;
18use crate::storage::StorageLayout;
19
20use super::super::request_endpoints::RequestEndpointSet;
21use super::super::request_runner::{run_router, RunnerRequest};
22use super::super::{
23 EmbassyInterfaceStore, EmbeddedFlashPersistence, EmbeddedPersistenceDiagnostic,
24 EmbeddedPersistenceRestoreReport, InterfaceInspectionStore, ManifoldPersistence,
25 ManuallyAttached, NoInterfaceInspectionStore, NoManifoldPersistence, PreConfiguredDestination,
26 PrnsEvent, PrnsNodeRecipe, RouteSnapshotKeys,
27};
28use super::command_handle::PrnsNodeHandle;
29use prns_runtime::runtime::placement::assemble_node_in_place;
30use prns_runtime::runtime::{assemble_node, AssembledNode, NoPersistence};
31
32pub struct ManifoldWiring<
33 M,
34 const LANE_COUNT: usize,
35 const NOTIFY: usize,
36 const COMMANDS: usize,
37 const LIFECYCLE: usize,
38 const COMPLETIONS: usize,
39> where
40 M: RawMutex + 'static,
41{
42 pub(super) inbound: HeaplessVec<(InterfaceId, &'static mut dyn ManifoldLaneReader), LANE_COUNT>,
43 pub(super) egress: PooledEgress<LANE_COUNT>,
44 pub(super) initial: HeaplessVec<InterfaceDescriptor, LANE_COUNT>,
45 pub(super) ifacs: HeaplessVec<InterfaceIfac, LANE_COUNT>,
46 pub(super) notify: Receiver<'static, M, InterfaceId, NOTIFY>,
47 pub(super) commands: Receiver<'static, M, IssuedCommand, COMMANDS>,
48 pub(super) lifecycle: Receiver<'static, M, InterfaceLifecycle, LIFECYCLE>,
49 pub(super) handle: PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS>,
50}
51
52pub struct PrnsNode<
53 St,
54 R,
55 F,
56 S,
57 H,
58 M,
59 const LANE_COUNT: usize,
60 const INTERFACE_CAPACITY: usize,
61 const NOTIFY: usize,
62 const COMMANDS: usize,
63 const LIFECYCLE: usize,
64 const COMPLETIONS: usize,
65 const ROUTED_REQUESTS: usize = 4,
66 const ROUTED_REQUEST_BYTES: usize = MAX_SEND_REQUEST_DATA_LEN,
67> where
68 S: StorageLayout,
69 M: RawMutex + 'static,
70{
71 node: AssembledNode<St, R, F, S>,
72 inbound: HeaplessVec<(InterfaceId, &'static mut dyn ManifoldLaneReader), LANE_COUNT>,
73 egress: PooledEgress<LANE_COUNT>,
74 notify: Receiver<'static, M, InterfaceId, NOTIFY>,
75 commands: Receiver<'static, M, IssuedCommand, COMMANDS>,
76 lifecycle: Receiver<'static, M, InterfaceLifecycle, LIFECYCLE>,
77 handle: PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS>,
78 host: H,
79 descriptors: HeaplessVec<InterfaceDescriptor, INTERFACE_CAPACITY>,
80 ifacs: HeaplessVec<InterfaceIfac, LANE_COUNT>,
81}
82
83pub struct RequestRoutingCapacity<const REQUESTS: usize, const REQUEST_BYTES: usize>;
84
85impl<const REQUESTS: usize, const REQUEST_BYTES: usize> Default
86 for RequestRoutingCapacity<REQUESTS, REQUEST_BYTES>
87{
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl<const REQUESTS: usize, const REQUEST_BYTES: usize>
94 RequestRoutingCapacity<REQUESTS, REQUEST_BYTES>
95{
96 #[must_use]
97 pub const fn new() -> Self {
98 Self
99 }
100}
101
102impl<
103 St,
104 R,
105 F,
106 S,
107 H,
108 M,
109 const LANE_COUNT: usize,
110 const INTERFACE_CAPACITY: usize,
111 const NOTIFY: usize,
112 const COMMANDS: usize,
113 const LIFECYCLE: usize,
114 const COMPLETIONS: usize,
115 >
116 PrnsNode<
117 St,
118 R,
119 F,
120 S,
121 H,
122 M,
123 LANE_COUNT,
124 INTERFACE_CAPACITY,
125 NOTIFY,
126 COMMANDS,
127 LIFECYCLE,
128 COMPLETIONS,
129 4,
130 MAX_SEND_REQUEST_DATA_LEN,
131 >
132where
133 R: RequestEndpointSet<St>,
134 F: FnMut(PrnsEvent<'_>, &St),
135 S: StorageLayout,
136 H: Host,
137 M: RawMutex + 'static,
138{
139 pub fn new<'d, D>(
140 recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
141 wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
142 host: H,
143 ) -> Self
144 where
145 D: IntoIterator<Item = PreConfiguredDestination<'d>>,
146 {
147 Self::build(recipe, wiring, host)
148 }
149}
150
151impl<
152 St,
153 R,
154 F,
155 S,
156 H,
157 M,
158 const LANE_COUNT: usize,
159 const INTERFACE_CAPACITY: usize,
160 const NOTIFY: usize,
161 const COMMANDS: usize,
162 const LIFECYCLE: usize,
163 const COMPLETIONS: usize,
164 const ROUTED_REQUESTS: usize,
165 const ROUTED_REQUEST_BYTES: usize,
166 >
167 PrnsNode<
168 St,
169 R,
170 F,
171 S,
172 H,
173 M,
174 LANE_COUNT,
175 INTERFACE_CAPACITY,
176 NOTIFY,
177 COMMANDS,
178 LIFECYCLE,
179 COMPLETIONS,
180 ROUTED_REQUESTS,
181 ROUTED_REQUEST_BYTES,
182 >
183where
184 R: RequestEndpointSet<St>,
185 F: FnMut(PrnsEvent<'_>, &St),
186 S: StorageLayout,
187 H: Host,
188 M: RawMutex + 'static,
189{
190 pub fn init_static<'d, D>(
191 cell: &'static StaticCell<Self>,
192 recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
193 wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
194 host: H,
195 ) -> &'static mut Self
196 where
197 D: IntoIterator<Item = PreConfiguredDestination<'d>>,
198 {
199 let (node, NoPersistence) = Self::init_static_with_persistence(cell, recipe, wiring, host);
200 node
201 }
202
203 #[expect(
204 unsafe_code,
205 clippy::undocumented_unsafe_blocks,
206 clippy::mut_from_ref,
207 reason = "every PrnsNode field is initialized before the slot is exposed"
208 )]
209 pub fn init_static_with_persistence<'d, D, P>(
210 cell: &'static StaticCell<Self>,
211 recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S, P>,
212 wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
213 host: H,
214 ) -> (&'static mut Self, P)
215 where
216 D: IntoIterator<Item = PreConfiguredDestination<'d>>,
217 {
218 const {
219 assert!(
220 INTERFACE_CAPACITY >= LANE_COUNT,
221 "PrnsNode INTERFACE_CAPACITY must cover every manifold lane"
222 );
223 }
224 let slot = cell.uninit();
225 let ManifoldWiring {
226 inbound,
227 egress,
228 initial,
229 ifacs,
230 notify,
231 commands,
232 lifecycle,
233 handle,
234 } = wiring;
235 let node = slot.as_mut_ptr();
236 let persistence = unsafe {
237 let assembled = &mut *core::ptr::addr_of_mut!((*node).node)
238 .cast::<MaybeUninit<AssembledNode<St, R, F, S>>>();
239 let (_, ManuallyAttached, persistence) = assemble_node_in_place(assembled, recipe);
240 core::ptr::addr_of_mut!((*node).inbound).write(inbound);
241 core::ptr::addr_of_mut!((*node).egress).write(egress);
242 core::ptr::addr_of_mut!((*node).notify).write(notify);
243 core::ptr::addr_of_mut!((*node).commands).write(commands);
244 core::ptr::addr_of_mut!((*node).lifecycle).write(lifecycle);
245 core::ptr::addr_of_mut!((*node).handle).write(handle);
246 core::ptr::addr_of_mut!((*node).host).write(host);
247 core::ptr::addr_of_mut!((*node).descriptors).write(HeaplessVec::new());
248 core::ptr::addr_of_mut!((*node).ifacs).write(ifacs);
249 persistence
250 };
251 let node = unsafe { slot.assume_init_mut() };
252 for descriptor in initial {
253 if node.descriptors.push(descriptor).is_err() {
254 unreachable!()
255 }
256 }
257 (node, persistence)
258 }
259
260 pub fn new_with_request_capacity<'d, D>(
261 recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
262 wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
263 host: H,
264 _capacity: RequestRoutingCapacity<ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>,
265 ) -> Self
266 where
267 D: IntoIterator<Item = PreConfiguredDestination<'d>>,
268 {
269 Self::build(recipe, wiring, host)
270 }
271
272 fn build<'d, D>(
273 recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
274 wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
275 host: H,
276 ) -> Self
277 where
278 D: IntoIterator<Item = PreConfiguredDestination<'d>>,
279 {
280 const {
281 assert!(
282 INTERFACE_CAPACITY >= LANE_COUNT,
283 "PrnsNode INTERFACE_CAPACITY must cover every manifold lane"
284 );
285 }
286 let (node, ManuallyAttached, NoPersistence) = assemble_node(recipe);
287 let mut descriptors = HeaplessVec::new();
288 for descriptor in wiring.initial {
289 if descriptors.push(descriptor).is_err() {
290 unreachable!()
291 }
292 }
293
294 PrnsNode {
295 node,
296 inbound: wiring.inbound,
297 egress: wiring.egress,
298 notify: wiring.notify,
299 commands: wiring.commands,
300 lifecycle: wiring.lifecycle,
301 handle: wiring.handle,
302 host,
303 descriptors,
304 ifacs: wiring.ifacs,
305 }
306 }
307
308 pub fn set_protocol_policy(&mut self, policy: crate::engine::EngineProtocolPolicy) {
309 self.node.engine.set_protocol_policy(policy);
310 }
311
312 #[must_use]
313 pub fn handle(&self) -> PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS> {
314 self.handle
315 }
316
317 pub async fn run(self, drive: impl Future<Output = ()>) {
319 self.run_with_inspection_store(&NoInterfaceInspectionStore, drive)
320 .await;
321 }
322
323 pub async fn run_with_interface_store<
324 const INTERFACES: usize,
325 const PACKET_PHY_CAPACITY: usize,
326 const PACKET_PHY_INDEX_BUCKETS: usize,
327 >(
328 self,
329 store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
330 drive: impl Future<Output = ()>,
331 ) where
332 M: Sync,
333 {
334 const {
335 assert!(
336 INTERFACES >= INTERFACE_CAPACITY,
337 "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
338 );
339 }
340 self.run_with_inspection_store(store, drive).await;
341 }
342
343 async fn run_with_inspection_store<Store>(self, store: &Store, drive: impl Future<Output = ()>)
344 where
345 Store: InterfaceInspectionStore,
346 {
347 let PrnsNode {
348 node,
349 mut inbound,
350 mut egress,
351 notify,
352 commands,
353 lifecycle,
354 handle,
355 mut host,
356 mut descriptors,
357 mut ifacs,
358 } = self;
359 let AssembledNode {
360 mut engine,
361 state,
362 mut on_event,
363 request_endpoints: _,
364 } = node;
365 let request_channel =
366 Channel::<M, RunnerRequest<ROUTED_REQUEST_BYTES>, ROUTED_REQUESTS>::new();
367 let request_sender = request_channel.sender();
368 let mut persistence = NoManifoldPersistence;
369 let manifold = run_pooled(
370 &mut engine,
371 &mut host,
372 PooledWiring {
373 descriptors: &mut descriptors,
374 ifacs: &mut ifacs,
375 inbound: &mut inbound,
376 egress: &mut egress,
377 notify,
378 commands,
379 lifecycle,
380 },
381 |journaled| {
382 if let Journaled::CommandSettled { id, settlement } = &journaled {
383 if handle.settle(*id, settlement.clone()) {
384 return;
385 }
386 }
387 if let Some(request) = RunnerRequest::copy_from(&journaled) {
388 let _ = request_sender.try_send(request);
389 }
390 on_event(PrnsEvent::from(journaled), &state);
391 },
392 crate::manifold::decline_all(),
393 store,
394 &mut persistence,
395 );
396 let router =
397 run_router::<St, R, M, COMMANDS, COMPLETIONS, ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>(
398 &state,
399 request_channel.receiver(),
400 handle,
401 );
402 join(join(manifold, router), drive).await;
403 }
404
405 pub async fn run_manifold(&mut self) {
407 self.run_manifold_with_inspection_store(&NoInterfaceInspectionStore)
408 .await;
409 }
410
411 pub async fn run_manifold_with_interface_store<
412 const INTERFACES: usize,
413 const PACKET_PHY_CAPACITY: usize,
414 const PACKET_PHY_INDEX_BUCKETS: usize,
415 >(
416 &mut self,
417 store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
418 ) where
419 M: Sync,
420 {
421 const {
422 assert!(
423 INTERFACES >= INTERFACE_CAPACITY,
424 "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
425 );
426 }
427 self.run_manifold_with_inspection_store(store).await;
428 }
429
430 async fn run_manifold_with_inspection_store<Store>(&mut self, store: &Store)
431 where
432 Store: InterfaceInspectionStore,
433 {
434 let mut persistence = NoManifoldPersistence;
435 self.run_manifold_with_inspection_store_and_persistence(store, &mut persistence)
436 .await;
437 }
438
439 pub async fn run_manifold_with_persistence_and_interface_store<
440 Fl,
441 Keys,
442 Observe,
443 const PENDING: usize,
444 const INTERFACES: usize,
445 const PACKET_PHY_CAPACITY: usize,
446 const PACKET_PHY_INDEX_BUCKETS: usize,
447 >(
448 &mut self,
449 store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
450 persistence: &mut EmbeddedFlashPersistence<Fl, Keys, Observe, PENDING>,
451 ) where
452 M: Sync,
453 Fl: NorFlash,
454 Keys: RouteSnapshotKeys,
455 Observe: FnMut(EmbeddedPersistenceDiagnostic),
456 {
457 const {
458 assert!(
459 INTERFACES >= INTERFACE_CAPACITY,
460 "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
461 );
462 }
463 self.run_manifold_with_inspection_store_and_persistence(store, persistence)
464 .await;
465 }
466
467 pub async fn restore_embedded_persistence<Fl, Keys, Observe, const PENDING: usize>(
468 &mut self,
469 persistence: &mut EmbeddedFlashPersistence<Fl, Keys, Observe, PENDING>,
470 ) -> EmbeddedPersistenceRestoreReport
471 where
472 Fl: NorFlash,
473 Keys: RouteSnapshotKeys,
474 Observe: FnMut(EmbeddedPersistenceDiagnostic),
475 H: ResumableHost,
476 {
477 let report = persistence
478 .restore(&mut self.node.engine, self.host.now())
479 .await;
480 self.host.resume_at(report.logical_start);
481 report
482 }
483
484 async fn run_manifold_with_inspection_store_and_persistence<Store, P>(
485 &mut self,
486 store: &Store,
487 persistence: &mut P,
488 ) where
489 Store: InterfaceInspectionStore,
490 P: ManifoldPersistence<S>,
491 {
492 let PrnsNode {
493 node,
494 inbound,
495 egress,
496 notify,
497 commands,
498 lifecycle,
499 handle,
500 host,
501 descriptors,
502 ifacs,
503 } = self;
504 let AssembledNode {
505 engine,
506 state,
507 on_event,
508 request_endpoints: _,
509 } = node;
510 let request_channel =
511 Channel::<M, RunnerRequest<ROUTED_REQUEST_BYTES>, ROUTED_REQUESTS>::new();
512 let request_sender = request_channel.sender();
513 let manifold = run_pooled(
514 engine,
515 host,
516 PooledWiring {
517 descriptors,
518 ifacs,
519 inbound,
520 egress,
521 notify: *notify,
522 commands: *commands,
523 lifecycle: *lifecycle,
524 },
525 |journaled| {
526 if let Journaled::CommandSettled { id, settlement } = &journaled {
527 if handle.settle(*id, settlement.clone()) {
528 return;
529 }
530 }
531 if let Some(request) = RunnerRequest::copy_from(&journaled) {
532 let _ = request_sender.try_send(request);
533 }
534 on_event(PrnsEvent::from(journaled), state);
535 },
536 crate::manifold::decline_all(),
537 store,
538 persistence,
539 );
540 let router =
541 run_router::<St, R, M, COMMANDS, COMPLETIONS, ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>(
542 state,
543 request_channel.receiver(),
544 *handle,
545 );
546 join(manifold, router).await;
547 }
548}
549
550#[cfg(test)]
551mod tests;