denm_sender_receiver/
denm_sender_receiver.rs1use rustflexstack::btp::router::Router as BTPRouter;
33use rustflexstack::facilities::decentralized_environmental_notification_service::{
34 denm_coder::{AccidentSubCauseCode, CauseCodeChoice},
35 DENRequest, DecentralizedEnvironmentalNotificationService, VehicleData,
36};
37use rustflexstack::facilities::location_service::{GpsFix, LocationService};
38use rustflexstack::geonet::gn_address::{GNAddress, M, MID, ST};
39use rustflexstack::geonet::mib::Mib;
40use rustflexstack::geonet::position_vector::LongPositionVector;
41use rustflexstack::geonet::router::Router as GNRouter;
42use rustflexstack::link_layer::raw_link_layer::RawLinkLayer;
43use std::env;
44use std::sync::mpsc;
45use std::thread;
46use std::time::Duration;
47
48fn main() {
49 let iface = env::args().nth(1).unwrap_or_else(|| "lo".to_string());
51
52 println!("=== DENM Sender/Receiver Example (DEN Service) ===");
53 println!("Interface: {}", iface);
54
55 let mac = {
57 use std::time::{SystemTime, UNIX_EPOCH};
58 let seed = SystemTime::now()
59 .duration_since(UNIX_EPOCH)
60 .unwrap()
61 .subsec_nanos();
62 [
63 0x02u8,
64 (seed >> 24) as u8,
65 (seed >> 16) as u8,
66 (seed >> 8) as u8,
67 seed as u8,
68 0xCC,
69 ]
70 };
71 println!(
72 "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
73 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
74 );
75
76 let mut mib = Mib::new();
78 mib.itsGnLocalGnAddr = GNAddress::new(M::GnMulticast, ST::PassengerCar, MID::new(mac));
79 mib.itsGnBeaconServiceRetransmitTimer = 0;
80
81 let mut loc_svc = LocationService::new();
86 let gn_gps_rx = loc_svc.subscribe();
87
88 let (gn_handle, gn_to_ll_rx, gn_to_btp_rx) = GNRouter::spawn(mib, None, None, None);
90
91 let (btp_handle, btp_to_gn_rx) = BTPRouter::spawn(mib);
93
94 let (ll_to_gn_tx, ll_to_gn_rx) = mpsc::channel::<Vec<u8>>();
96 let raw_ll = RawLinkLayer::new(ll_to_gn_tx, gn_to_ll_rx, &iface, mac);
97 raw_ll.start();
98
99 let gn_h1 = gn_handle.clone();
103 thread::spawn(move || {
104 while let Ok(pkt) = ll_to_gn_rx.recv() {
105 gn_h1.send_incoming_packet(pkt);
106 }
107 });
108
109 let btp_h1 = btp_handle.clone();
111 thread::spawn(move || {
112 while let Ok(ind) = gn_to_btp_rx.recv() {
113 btp_h1.send_gn_data_indication(ind);
114 }
115 });
116
117 let gn_h2 = gn_handle.clone();
119 thread::spawn(move || {
120 while let Ok(req) = btp_to_gn_rx.recv() {
121 gn_h2.send_gn_data_request(req);
122 }
123 });
124
125 let gn_h3 = gn_handle.clone();
127 thread::spawn(move || {
128 while let Ok(fix) = gn_gps_rx.recv() {
129 let mut epv = LongPositionVector::decode([0u8; 24]);
130 epv.update_from_gps(
131 fix.latitude,
132 fix.longitude,
133 fix.speed_mps,
134 fix.heading_deg,
135 fix.pai,
136 );
137 gn_h3.update_position_vector(epv);
138 }
139 });
140
141 let station_id = u32::from_be_bytes([mac[2], mac[3], mac[4], mac[5]]);
143 let vehicle_data = VehicleData {
144 station_id,
145 station_type: 5, };
147
148 let (den_svc, denm_rx) =
152 DecentralizedEnvironmentalNotificationService::new(btp_handle.clone(), vehicle_data);
153
154 thread::spawn(move || {
156 while let Ok(denm) = denm_rx.recv() {
157 let lat = denm.denm.management.event_position.latitude.0 as f64 / 1e7;
158 let lon = denm.denm.management.event_position.longitude.0 as f64 / 1e7;
159 let seq = denm.denm.management.action_id.sequence_number.0;
160 println!(
161 "[DENM RX] station={:>10} seq={:>5} event_lat={:.5} event_lon={:.5}",
162 denm.header.station_id.0, seq, lat, lon,
163 );
164 }
165 });
166
167 thread::sleep(Duration::from_millis(100));
169
170 let gps_fix = GpsFix {
172 latitude: 41.552,
173 longitude: 2.134,
174 altitude_m: 120.0,
175 speed_mps: 14.0, heading_deg: 90.0,
177 pai: true,
178 };
179 loc_svc.publish(gps_fix);
180
181 println!("Triggering road-hazard DENM (accident) for 30 s @ 1 Hz");
185 den_svc.trigger_denm(DENRequest {
186 event_latitude: gps_fix.latitude,
187 event_longitude: gps_fix.longitude,
188 event_altitude_m: gps_fix.altitude_m,
189 cause_code: CauseCodeChoice::accident2(AccidentSubCauseCode(0)),
190 information_quality: 4,
191 event_speed_raw: (gps_fix.speed_mps * 100.0) as u16,
192 event_heading_raw: (gps_fix.heading_deg * 10.0) as u16,
193 denm_interval_ms: 1_000,
194 time_period_ms: 30_000,
195 relevance_radius_m: 1_000,
196 });
197
198 println!("Publishing GPS fixes @ 1 Hz — Ctrl+C to stop\n");
200 loop {
201 thread::sleep(Duration::from_secs(1));
202 loc_svc.publish(gps_fix);
203 }
204}