-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstore_forward_mailbox.rivr
More file actions
123 lines (108 loc) · 6.25 KB
/
store_forward_mailbox.rivr
File metadata and controls
123 lines (108 loc) · 6.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// examples/store_forward_mailbox.rivr
//
// Store-and-forward mailbox for intermittently-reachable nodes.
//
// Use case: a node at the edge of the mesh that only becomes reachable for
// brief periods (e.g. a mobile node, or one with a directional antenna).
// This program works with the new PKT_MAILBOX (9) service type, which
// provides a dedicated store-and-forward primitive with:
//
// Wire format (PKT_MAILBOX payload, ≤ RIVR_PKT_MAX_PAYLOAD bytes):
// [0–3] recipient_id u32 LE — final recipient; 0 = any store node
// [4–5] msg_seq u16 LE — per-source sequence counter
// [6] flags u8 — MB_FLAG_NEW | MB_FLAG_DELIVERED | MB_FLAG_FORWARD
// [7..N] text payload UTF-8 — message body
//
// C-layer behaviour (handle_mailbox_store in rivr_svc.c):
// • Frames addressed to this node (recipient_id == my_id || 0) are stored
// in g_mailbox_store (8-entry LRU ring buffer in BSS; no heap).
// • A @MAIL JSON log line is printed for every received frame.
// • Other frames continue through the relay path to their destination.
//
// RIVR program role:
// The RIVR engine controls relay gating and timing. The window collects
// up to 8 mailbox frames per 12-tick window; overflow evicts the oldest.
// A 3-tick delay separates re-forwards from the original flood burst.
// The 4 % duty-cycle cap prevents the re-forward burst from saturating
// the channel on crowded mesh segments.
//
// Also keeps PKT_CHAT (1) buffering for nodes that have not migrated to
// the PKT_MAILBOX service yet (backward-compatible transition period).
source rf_rx @lmp = rf; // LoRa receive stream (Lamport clock)
source beacon_tick = timer(60000); // 60-second poll tick — C layer applies interval+jitter gate
// ── PKT_MAILBOX buffer (primary) ─────────────────────────────────────────────
// New dedicated store-and-forward service: collect up to 8 frames per window,
// defer forwarding by 3 ticks, then re-emit under a tight duty-cycle cap.
let mailbox_buffered = rf_rx
|> filter.pkt_type(9)
|> window.ticks(12, 8, "drop_oldest")
|> delay.ticks(3)
|> budget.toa_us(600000, 0.04, 280000);
// ── PKT_CHAT buffer (legacy, backward-compatible) ────────────────────────────
// Same windowing strategy as mailbox for nodes that still use plain chat.
let chat_buffered = rf_rx
|> filter.pkt_type(1)
|> window.ticks(12, 8, "drop_oldest")
|> delay.ticks(3)
|> budget.toa_us(600000, 0.04, 280000);
// ── PKT_ALERT pass-through (no buffering — always forward immediately) ────────
// Priority alerts must not be delayed by window accumulation.
let alerts = rf_rx
|> filter.pkt_type(10)
|> budget.toa_us(600000, 0.20, 280000);
// ── Emit ────────────────────────────────────────────────────────────────────
emit { io.lora.tx(mailbox_buffered); } // re-forward mailbox burst over LoRa
emit { io.usb.print(mailbox_buffered); } // local serial log of mailbox bursts
emit { io.lora.tx(chat_buffered); } // re-forward legacy chat burst
emit { io.lora.tx(alerts); } // immediate alert relay
emit { io.lora.beacon(beacon_tick); } // periodic presence advertisement
// Use case: a node at the edge of the mesh that only becomes reachable for
// brief periods (e.g. a mobile node, or one with a directional antenna).
// This program collects incoming chat messages in a bounded ring buffer and
// re-forwards them in a burst when the retry timer fires, giving late-joining
// nodes a chance to receive recent traffic even after the original flood.
//
// Key design choices:
//
// window.ticks(12, 8, "drop_oldest")
// Accumulates up to 8 PKT_CHAT frames in a 12-tick sliding window.
// If a 9th message arrives before the window closes, the oldest is
// evicted ("drop_oldest"), so recent messages always take priority.
// A full Window event is emitted at tick boundary or when CAP is hit.
//
// delay.ticks(3)
// Defers forwarding by 3 Lamport ticks after the window closes.
// This separates the re-forward burst from the original flood so the
// two do not collide on the channel.
//
// budget.toa_us(600000, 0.04, 280000)
// Tight 4 % duty-cycle budget over a 10-minute window: the store-and-
// forward burst is explicitly rate-limited so it cannot starve live
// traffic on congested mesh segments.
//
// Topology note:
// The re-forwarded frames have an incremented hop count (handled by the
// C-layer relay) so they are distinguishable from originals and will
// not be deduplicated against them by receiving nodes that already
// processed the first pass.
//
// Tuning for your environment:
// - Increase CAP (8) to store more messages per window.
// - Decrease delay.ticks (3) on fast-hopping meshes.
// - Replace budget.toa_us parameters to match your sub-band limits.
source rf_rx @lmp = rf; // LoRa receive stream (Lamport clock)
source beacon_tick = timer(60000); // 60-second poll tick — C layer applies interval+jitter gate
// ── Message buffer ──────────────────────────────────────────────────────────
// 1. Filter to only PKT_CHAT frames.
// 2. Collect into a capped window — oldest messages are dropped on overflow.
// 3. Delay the flush by 3 ticks so re-forwards and originals do not overlap.
// 4. Apply a duty-cycle guard on the re-forward burst.
let buffered = rf_rx
|> filter.pkt_type(1)
|> window.ticks(12, 8, "drop_oldest")
|> delay.ticks(3)
|> budget.toa_us(600000, 0.04, 280000);
// ── Emit ────────────────────────────────────────────────────────────────────
emit { io.lora.tx(buffered); } // re-forward stored burst over LoRa
emit { io.usb.print(buffered); } // local serial log of re-forwarded msgs
emit { io.lora.beacon(beacon_tick); } // periodic presence advertisement