Skip to content

Commit b7b46ce

Browse files
Merge sedsprintf_rs upstream main
2 parents cdcf27a + 592f607 commit b7b46ce

File tree

12 files changed

+295
-368
lines changed

12 files changed

+295
-368
lines changed

sedsprintf_rs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
2-
version = "1.1.0"
32
name = "sedsprintf_rs_2026"
3+
version = "1.1.1"
44
edition = "2024"
55
build = "build.rs"
66
authors = ["Rylan Meilutis <[email protected]>"]

sedsprintf_rs/c-system-test/src/main.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ static void * sender_B(void * arg)
9393
make_series(buf, 3, 101.3f);
9494
assert(node_log(B, SEDS_DT_BAROMETER_DATA, buf, 3, sizeof(buf[0])) == SEDS_OK);
9595
usleep(gen_random_us());
96+
uint8_t buff[0];
97+
assert(node_log(B, SEDS_DT_HEARTBEAT, buff, 0, 0) == SEDS_OK);
98+
usleep(gen_random_us());
9699
}
97100
return NULL;
98101
}
@@ -177,8 +180,8 @@ int main(void)
177180
powerBoard.radio_hits, powerBoard.sd_hits);
178181

179182
// 7) Assertions (same as your single-thread example)
180-
assert(radioBoard.radio_hits == 25);
181-
assert(flightControllerBoard.sd_hits == 25);
183+
assert(radioBoard.radio_hits == 30);
184+
assert(flightControllerBoard.sd_hits == 30);
182185
assert(powerBoard.radio_hits == 0);
183186
assert(powerBoard.sd_hits == 0);
184187

sedsprintf_rs/c-system-test/src/telemetry_sim.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ SedsResult node_log(
253253
size_t element_count,
254254
size_t element_size)
255255
{
256-
if (!n || !n->r || !data || element_count == 0 || element_size == 0) return SEDS_ERR;
256+
if (!n || !n->r || !data) return SEDS_ERR;
257257

258258
const size_t total_bytes = element_count * element_size;
259259
// LOCK();

sedsprintf_rs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "maturin"
44

55
[project]
66
name = "sedsprintf_rs_2026"
7-
version = "1.1.0"
7+
version = "1.1.1"
88
description = "Rust telemetry and serialization library"
99
authors = [{ name = "Rylan Meilutis", email = "[email protected]" }]
1010
readme = "README.md"

sedsprintf_rs/python-example/main.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,25 +131,36 @@ def producer_proc(name: str, cmd_q: mp.Queue, n_iters: int, seed: int):
131131
random.seed(seed + os.getpid())
132132
print(f"[{name}] start PID={os.getpid()} iters={n_iters}")
133133
for i in range(n_iters):
134-
which = random.randint(0, 2)
134+
# 0=log_bytes, 1=log_f32, 2=log, 3=empty NoData packet
135+
which = random.randint(0, 3)
136+
135137
if which == 0:
136138
msg = f"{name} hello there {i}".encode("utf-8")
137139
cmd_q.put(deep_coerce_enums(("log_bytes", {
138140
"ty": DT.MESSAGE_DATA, "data": msg
139141
})))
142+
140143
elif which == 1:
141144
vals = [101325.0 + random.random() * 100.0,
142145
20.0 + random.random() * 10.0,
143146
-0.5 + random.random()]
144147
cmd_q.put(deep_coerce_enums(("log_f32", {
145148
"ty": DT.BAROMETER_DATA, "values": vals
146149
})))
147-
else:
150+
151+
elif which == 2:
148152
arr = np.array([random.randint(0, 1000) for _ in range(8)], dtype=np.uint16)
149153
cmd_q.put(deep_coerce_enums(("log", {
150154
"ty": DT.GPS_DATA, "data": arr,
151155
"elem_size": 2, "elem_kind": EK.UNSIGNED
152156
})))
157+
158+
else:
159+
# Replace DT.HEARTBEAT with your actual NoData DataType
160+
cmd_q.put(deep_coerce_enums(("log_bytes", {
161+
"ty": DT.HEARTBEAT, # or whatever logical DataType uses MessageDataType::NoData
162+
"data": b"",
163+
})))
153164
time.sleep(random.random() * 0.002) # 0–2ms jitter
154165
print(f"[{name}] done")
155166

sedsprintf_rs/src/c_api.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use crate::{
1919
};
2020
use alloc::{boxed::Box, string::String, sync::Arc, vec, vec::Vec};
2121
use core::{ffi::c_char, ffi::c_void, mem::size_of, ptr, slice, str::from_utf8};
22-
22+
use crate::config::get_message_data_type;
23+
use crate::MessageDataType::NoData;
2324
// ============================================================================
2425
// Constants / basic types shared with the C side
2526
// ============================================================================
@@ -242,7 +243,7 @@ unsafe fn write_str_to_buf(s: &str, buf: *mut c_char, buf_len: usize) -> i32 {
242243
/// Used in FFI logging helpers.
243244
#[inline]
244245
fn width_is_valid(width: usize) -> bool {
245-
matches!(width, 1 | 2 | 4 | 8 | 16)
246+
matches!(width, 0| 1 | 2 | 4 | 8 | 16)
246247
}
247248

248249
/// FFI-facing clock adapter that calls back into C when present.
@@ -593,6 +594,22 @@ fn finish_with<T: LeBytes + Copy>(
593594
required_elems: usize,
594595
elem_size: usize,
595596
) -> i32 {
597+
if get_message_data_type(ty) == NoData{
598+
return ok_or_status(unsafe {
599+
let router = &(*r).inner; // shared borrow
600+
if queue {
601+
match ts {
602+
Some(t) => router.log_queue_ts::<T>(ty, t, &[]),
603+
None => router.log_queue::<T>(ty, &[]),
604+
}
605+
} else {
606+
match ts {
607+
Some(t) => router.log_ts::<T>(ty, t, &[]),
608+
None => router.log::<T>(ty, &[]),
609+
}
610+
}
611+
});
612+
}
596613
let mut tmp: Vec<T> = Vec::with_capacity(required_elems);
597614
// vectorize_data reads unaligned little-endian elements into tmp
598615
if let Err(_) = vectorize_data::<T>(padded.as_ptr(), required_elems, elem_size, &mut tmp) {
@@ -1136,7 +1153,6 @@ pub extern "C" fn seds_pkt_data_ptr(
11361153
view.payload as *const c_void
11371154
}
11381155

1139-
11401156
macro_rules! impl_seds_pkt_get_typed_from_packet {
11411157
($fname:ident, $method:ident, $ty:ty) => {
11421158
#[unsafe(no_mangle)]
@@ -1182,7 +1198,6 @@ macro_rules! impl_seds_pkt_get_typed_from_packet {
11821198
};
11831199
}
11841200

1185-
11861201
// Typed getters using TelemetryPacket's data_as_* helpers.
11871202
// All use "query" semantics like seds_pkt_copy_data:
11881203
// - If out is NULL or out_elems < needed, return needed (element count) and do not copy.
@@ -1191,12 +1206,12 @@ macro_rules! impl_seds_pkt_get_typed_from_packet {
11911206
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_f32, data_as_f32, f32);
11921207
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_f64, data_as_f64, f64);
11931208

1194-
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u8, data_as_u8, u8);
1209+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u8, data_as_u8, u8);
11951210
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u16, data_as_u16, u16);
11961211
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u32, data_as_u32, u32);
11971212
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_u64, data_as_u64, u64);
11981213

1199-
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i8, data_as_i8, i8);
1214+
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i8, data_as_i8, i8);
12001215
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i16, data_as_i16, i16);
12011216
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i32, data_as_i32, i32);
12021217
impl_seds_pkt_get_typed_from_packet!(seds_pkt_get_i64, data_as_i64, i64);
@@ -1232,9 +1247,7 @@ pub extern "C" fn seds_pkt_get_string(
12321247
}
12331248

12341249
#[unsafe(no_mangle)]
1235-
pub extern "C" fn seds_pkt_get_string_len(
1236-
pkt: *const SedsPacketView,
1237-
) -> i32 {
1250+
pub extern "C" fn seds_pkt_get_string_len(pkt: *const SedsPacketView) -> i32 {
12381251
if pkt.is_null() {
12391252
return status_from_err(TelemetryError::BadArg);
12401253
}

sedsprintf_rs/src/config.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ pub enum DataType {
114114
Abort,
115115
FuelFlow,
116116
FuelTankPressure,
117+
FlightState,
118+
Heartbeat,
117119
/// Generic string message payload.
118120
MessageData,
119121
}
@@ -137,6 +139,8 @@ impl DataType {
137139
DataType::Abort => "ABORT",
138140
DataType::FuelFlow => "FUEL_FLOW",
139141
DataType::FuelTankPressure => "FUEL_TANK_PRESSURE",
142+
DataType::FlightState => "FLIGHT_STATE",
143+
DataType::Heartbeat => "HEARTBEAT",
140144
DataType::MessageData => "MESSAGE_DATA",
141145
}
142146
}
@@ -166,10 +170,12 @@ pub const fn get_message_data_type(data_type: DataType) -> MessageDataType {
166170
DataType::GyroData => MessageDataType::Float32,
167171
DataType::AccelData => MessageDataType::Float32,
168172
DataType::BarometerData => MessageDataType::Float32,
169-
DataType::Abort => MessageDataType::Bool,
173+
DataType::Abort => MessageDataType::NoData,
170174
DataType::FuelFlow => MessageDataType::Float32,
171175
DataType::FuelTankPressure => MessageDataType::Float32,
176+
DataType::FlightState => MessageDataType::UInt8,
172177
DataType::MessageData => MessageDataType::String,
178+
DataType::Heartbeat => MessageDataType::NoData,
173179
}
174180
}
175181

@@ -191,7 +197,9 @@ pub const fn get_message_info_types(message_type: DataType) -> MessageType {
191197
DataType::Abort => MessageType::Error,
192198
DataType::FuelFlow => MessageType::Info,
193199
DataType::FuelTankPressure => MessageType::Info,
200+
DataType::FlightState => MessageType::Info,
194201
DataType::MessageData => MessageType::Info,
202+
DataType::Heartbeat => MessageType::Info,
195203
}
196204
}
197205

@@ -276,7 +284,7 @@ pub const fn get_message_meta(data_type: DataType) -> MessageMeta {
276284
DataType::Abort => {
277285
MessageMeta {
278286
// Abort Command
279-
element_count: MessageElementCount::Static(1), // Abort messages carry 1 boolean element
287+
element_count: MessageElementCount::Static(0), // Abort messages carry 1 boolean element
280288
endpoints: &[
281289
DataEndpoint::Abort,
282290
],
@@ -296,6 +304,20 @@ pub const fn get_message_meta(data_type: DataType) -> MessageMeta {
296304
endpoints: &[DataEndpoint::GroundStation, DataEndpoint::SdCard],
297305
}
298306
}
307+
DataType::FlightState => {
308+
MessageMeta {
309+
// Flight State Data
310+
element_count: MessageElementCount::Static(1), // Flight State messages carry 1 uint8 element
311+
endpoints: &[DataEndpoint::GroundStation, DataEndpoint::SdCard],
312+
}
313+
}
314+
DataType::Heartbeat => {
315+
MessageMeta {
316+
// Heartbeat Data
317+
element_count: MessageElementCount::Static(0), // Heartbeat messages carry 1 binary element
318+
endpoints: &[DataEndpoint::GroundStation, DataEndpoint::SdCard],
319+
}
320+
}
299321
DataType::MessageData => {
300322
MessageMeta {
301323
// Message Data

sedsprintf_rs/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ pub const fn get_data_type(ty: DataType) -> MessageDataType {
244244
get_message_data_type(ty)
245245
}
246246

247+
/// Return the default endpoints for a given `DataType`.
248+
/// # Arguments
249+
/// - `ty`: Logical data type to query.
250+
/// # Returns
251+
/// - Slice of allowed `DataEndpoint` values.
252+
#[inline]
253+
pub const fn endpoints_from_datatype(ty: DataType) -> &'static [DataEndpoint] {
254+
get_message_meta(ty).endpoints
255+
}
247256
/// Primitive element type used by a message.
248257
///
249258
/// This is the underlying "slot" type, not the high-level `DataType`
@@ -266,6 +275,7 @@ pub enum MessageDataType {
266275
Bool,
267276
String,
268277
Binary,
278+
NoData,
269279
}
270280

271281
/// Size in bytes of a single element for the given [`MessageDataType`].
@@ -294,6 +304,7 @@ pub const fn data_type_size(dt: MessageDataType) -> usize {
294304
MessageDataType::Bool => size_of::<bool>(),
295305
MessageDataType::String => MAX_STATIC_STRING_LENGTH,
296306
MessageDataType::Binary => MAX_STATIC_HEX_LENGTH,
307+
MessageDataType::NoData => 0,
297308
}
298309
}
299310

sedsprintf_rs/src/macros.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,62 @@ macro_rules! do_vec_log_typed {
199199
}
200200
}};
201201
}
202+
203+
/// Helper macros for implementing `TelemetryPacket` constructors and
204+
/// accessors for primitive slices.
205+
#[macro_export]
206+
macro_rules! impl_from_prim_slices {
207+
($($fn_name:ident, $elem_ty:ty);+ $(;)?) => {
208+
$(
209+
#[inline]
210+
pub fn $fn_name(
211+
ty: DataType,
212+
values: &[$elem_ty],
213+
endpoints: &[DataEndpoint],
214+
timestamp: u64,
215+
) -> TelemetryResult<Self> {
216+
Self::from_prim_le_slice(ty, values, endpoints, timestamp)
217+
}
218+
)+
219+
};
220+
}
221+
/// Helper macros for implementing `TelemetryPacket` data accessors for
222+
/// primitive slices.
223+
#[macro_export]
224+
macro_rules! impl_data_as_prim {
225+
($($name:ident, $ty:ty, $variant:expr);+ $(;)?) => {
226+
$(
227+
#[doc = concat!("Decode payload as little-endian `", stringify!($ty), "` values.")]
228+
#[inline]
229+
pub fn $name(&self) -> TelemetryResult<Vec<$ty>> {
230+
self._as_le_bytes::<$ty>($variant)
231+
}
232+
)+
233+
};
234+
}
235+
236+
/// Implement the [`LeDecode`] trait for a numeric type with fixed size.
237+
///
238+
/// This is used to unify little-endian deserialization for primitive types
239+
/// like `u16`, `u32`, `f32`, etc.
240+
///
241+
/// `$ty`: concrete numeric type (e.g. `u16`, `i32`, `f32`)
242+
///# Example
243+
/// ```text
244+
/// impl_ledecode_auto!(u32);
245+
/// impl_ledecode_auto!(f32);
246+
/// ```
247+
#[macro_export]
248+
macro_rules! impl_ledecode_auto {
249+
($ty:ty) => {
250+
impl LeDecode for $ty {
251+
const WIDTH: usize = core::mem::size_of::<$ty>();
252+
#[inline]
253+
fn from_le(slice: &[u8]) -> Self {
254+
let arr: [u8; core::mem::size_of::<$ty>()] =
255+
slice.try_into().expect("slice length mismatch");
256+
<$ty>::from_le_bytes(arr)
257+
}
258+
}
259+
};
260+
}

0 commit comments

Comments
 (0)