add example helper, translate more examples

update to wip servicepoint lib
This commit is contained in:
Vinzenz Schroeter 2025-05-10 14:58:50 +02:00
parent b8a55d0433
commit 4ab5305377
15 changed files with 195 additions and 61 deletions

3
Cargo.lock generated
View file

@ -415,8 +415,7 @@ dependencies = [
[[package]] [[package]]
name = "servicepoint" name = "servicepoint"
version = "0.14.1" version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://git.berlin.ccc.de/servicepoint/servicepoint.git?branch=next#d979d46d3e770222c5a7ca549e601b45eff48299"
checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"bzip2", "bzip2",

View file

@ -18,9 +18,10 @@ crate-type = ["staticlib", "cdylib", "rlib"]
cbindgen = "0.28.0" cbindgen = "0.28.0"
[dependencies.servicepoint] [dependencies.servicepoint]
package = "servicepoint" # version = "0.14.1"
version = "0.14.1"
default-features = false default-features = false
git = "https://git.berlin.ccc.de/servicepoint/servicepoint.git"
branch = "next"
[features] [features]
all_compressions = ["servicepoint/all_compressions"] all_compressions = ["servicepoint/all_compressions"]

View file

@ -22,10 +22,12 @@ usize_is_size_t = true
# this is needed because otherwise the order in the C bindings is different on different machines # this is needed because otherwise the order in the C bindings is different on different machines
sort_by = "Name" sort_by = "Name"
include_guard = "SERVICEPOINT_BINDINGS_C"
[parse] [parse]
parse_deps = true parse_deps = true
include = ["servicepoint", "std"] include = ["servicepoint", "std"]
extra_bindings = ["servicepoint"] extra_bindings = ["servicepoint", "servicepoint_binding_c"]
[parse.expand] [parse.expand]
features = ["full"] features = ["full"]

View file

@ -1,16 +1,9 @@
#include <stdio.h> #include "helpers.h"
#include "servicepoint.h"
int main(void) { int main(void) {
printf("test\n"); sock_init();
UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); sp_udp_send_header(sock, (Header) {.command_code = COMMAND_CODE_CLEAR});
//UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342);
if (connection == NULL)
return 1;
sp_udp_send_header(connection, (Header) {.command_code = COMMAND_CODE_CLEAR});
CharGrid *grid = sp_char_grid_new(5, 2); CharGrid *grid = sp_char_grid_new(5, 2);
if (grid == NULL) if (grid == NULL)
@ -30,8 +23,7 @@ int main(void) {
Packet *packet = sp_char_grid_into_packet(grid, 0, 0); Packet *packet = sp_char_grid_into_packet(grid, 0, 0);
if (packet == NULL) if (packet == NULL)
return 1; return 1;
sp_udp_send_packet(connection, packet); sp_udp_send_packet(sock, packet);
sp_udp_free(connection);
return 0; return 0;
} }

View file

@ -1,6 +1,5 @@
#include "servicepoint.h" #include "servicepoint.h"
#include "helpers.h"
static UdpSocket *connection = NULL;
void enable_all_pixels(void) { void enable_all_pixels(void) {
Bitmap *all_on = sp_bitmap_new_max_sized(); Bitmap *all_on = sp_bitmap_new_max_sized();
@ -9,7 +8,7 @@ void enable_all_pixels(void) {
BitmapCommand *bitmapCommand = sp_cmd_bitmap_from_bitmap(all_on); BitmapCommand *bitmapCommand = sp_cmd_bitmap_from_bitmap(all_on);
Packet *packet = sp_cmd_bitmap_try_into_packet(bitmapCommand); Packet *packet = sp_cmd_bitmap_try_into_packet(bitmapCommand);
if (packet != NULL) if (packet != NULL)
sp_udp_send_packet(connection, packet); sp_udp_send_packet(sock, packet);
} }
void make_brightness_pattern(BrightnessGrid *grid) { void make_brightness_pattern(BrightnessGrid *grid) {
@ -19,16 +18,8 @@ void make_brightness_pattern(BrightnessGrid *grid) {
} }
} }
void run_at_exit() {
sp_udp_free(connection);
}
int main(void) { int main(void) {
//UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); sock_init();
connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342);
if (connection == NULL)
return -1;
atexit(run_at_exit);
enable_all_pixels(); enable_all_pixels();
@ -39,6 +30,6 @@ int main(void) {
if (packet == NULL) if (packet == NULL)
return -2; return -2;
sp_udp_send_packet(connection, packet); sp_udp_send_packet(sock, packet);
return 0; return 0;
} }

45
example/src/helpers.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#ifndef SERVICEPOINT_BINDING_C_MSLEEP_H
#define SERVICEPOINT_BINDING_C_MSLEEP_H
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include "servicepoint.h"
static UdpSocket *sock = NULL;
void sock_free() {
sp_udp_free(sock);
}
void sock_init() {
//sock = sp_udp_open_ipv4(127, 0, 0, 1, 2342);
sock = sp_udp_open_ipv4(172, 23, 42, 29, 2342);
if (sock == NULL)
exit(-1);
atexit(sock_free);
}
/// TODO: all of this for sleeping n ms? There should be a better way!
int msleep(long msec) {
int res;
if (msec < 0) {
errno = EINVAL;
return -1;
}
struct timespec ts = {
.tv_sec = msec / 1000,
.tv_nsec = (msec % 1000) * 1000000,
};
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
return res;
}
#endif //SERVICEPOINT_BINDING_C_MSLEEP_H

34
example/src/moving_line.c Normal file
View file

@ -0,0 +1,34 @@
#include "servicepoint.h"
#include "helpers.h"
int main() {
sock_init();
int result = 0;
Bitmap *bitmap = sp_bitmap_new_max_sized();
for (int x = 0; x < sp_bitmap_width(bitmap); x++) {
sp_bitmap_fill(bitmap, false);
for (int y = 0; y < sp_bitmap_height(bitmap); y++) {
sp_bitmap_set(bitmap, (y + x) % PIXEL_WIDTH, y, true);
}
BitmapCommand *command = sp_cmd_bitmap_from_bitmap(sp_bitmap_clone(bitmap));
Packet *packet = sp_cmd_bitmap_try_into_packet(command);
if (packet == NULL) {
result = -2;
goto exit;
}
if (!sp_udp_send_packet(sock, packet)) {
result = -3;
goto exit;
}
msleep(SP_FRAME_PACING_MS);
}
exit:
sp_bitmap_free(bitmap);
return result;
}

View file

@ -1,10 +1,9 @@
#include <stdio.h> #include <stdio.h>
#include "servicepoint.h" #include "servicepoint.h"
#include "helpers.h"
int main(void) { int main(void) {
UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); sock_init();
if (connection == NULL)
return 1;
Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT); Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT);
if (pixels == NULL) if (pixels == NULL)
@ -19,8 +18,6 @@ int main(void) {
Header *header = sp_packet_get_header(packet); Header *header = sp_packet_get_header(packet);
printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d); printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d);
sp_udp_send_packet(connection, packet); sp_udp_send_packet(sock, packet);
sp_udp_free(connection);
return 0; return 0;
} }

View file

@ -0,0 +1,34 @@
#include "servicepoint.h"
#include "helpers.h"
int main() {
sock_init();
Bitmap *enabled_pixels = sp_bitmap_new_max_sized();
sp_bitmap_fill(enabled_pixels, true);
int result = 0;
for (int x = 0; x < PIXEL_WIDTH; x++) {
for (int y = 0; y < PIXEL_HEIGHT; y++) {
sp_bitmap_set(enabled_pixels, x, y, false);
}
BitVec *bitvec = sp_bitmap_into_bitvec(sp_bitmap_clone(enabled_pixels));
BitVecCommand *command = sp_cmd_bitvec_new(bitvec, 0, BINARY_OPERATION_AND, COMPRESSION_CODE_LZMA);
Packet *packet = sp_cmd_bitvec_try_into_packet(command);
if (packet == NULL) {
result = -2;
goto exit;
}
if (!sp_udp_send_packet(sock, packet)) {
result = -3;
goto exit;
}
msleep(SP_FRAME_PACING_MS);
}
exit:
sp_bitmap_free(enabled_pixels);
return result;
}

View file

@ -1,3 +1,6 @@
#ifndef SERVICEPOINT_BINDINGS_C
#define SERVICEPOINT_BINDINGS_C
/* Generated with cbindgen:0.28.0 */ /* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
@ -446,12 +449,19 @@ typedef struct ValueGrid_u8 ValueGrid_u8;
* - accesses to the memory pointed to with `start` is never accessed outside `length` * - accesses to the memory pointed to with `start` is never accessed outside `length`
* - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in * - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in
* the function returning this type. * the function returning this type.
* - if `start` is NULL or `length` is 0, do not dereference `start`.
*
* # Examples
*
* ```c
* ByteSlice empty = {.start: NULL, .length = 0};
* ```
*/ */
typedef struct { typedef struct {
/** /**
* The start address of the memory. * The start address of the memory.
*/ */
uint8_t */*notnull*/ start; uint8_t *start;
/** /**
* The amount of memory in bytes. * The amount of memory in bytes.
*/ */
@ -606,6 +616,13 @@ typedef struct {
/**
* Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
*/
#define SP_FRAME_PACING_MS 30
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif // __cplusplus #endif // __cplusplus
@ -1703,8 +1720,7 @@ void sp_packet_free(Packet */*notnull*/ packet);
* *
* returns: new instance. Will never return null. * returns: new instance. Will never return null.
*/ */
Packet */*notnull*/ sp_packet_from_parts(Header header, Packet */*notnull*/ sp_packet_from_parts(Header header, ByteSlice payload);
const ByteSlice *payload);
/** /**
* Returns a pointer to the header field of the provided packet. * Returns a pointer to the header field of the provided packet.
@ -1716,6 +1732,8 @@ Header */*notnull*/ sp_packet_get_header(Packet */*notnull*/ packet);
/** /**
* Returns a pointer to the current payload of the provided packet. * Returns a pointer to the current payload of the provided packet.
* *
* Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload.
*
* The returned memory can be changed and will be valid until a new payload is set. * The returned memory can be changed and will be valid until a new payload is set.
*/ */
ByteSlice sp_packet_get_payload(Packet */*notnull*/ packet); ByteSlice sp_packet_get_payload(Packet */*notnull*/ packet);
@ -1831,3 +1849,5 @@ bool sp_udp_send_packet(UdpSocket */*notnull*/ connection,
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif // __cplusplus #endif // __cplusplus
#endif /* SERVICEPOINT_BINDINGS_C */

View file

@ -103,7 +103,7 @@ pub unsafe extern "C" fn sp_cmd_bitvec_set_offset(
pub unsafe extern "C" fn sp_cmd_bitvec_get_operation( pub unsafe extern "C" fn sp_cmd_bitvec_get_operation(
command: NonNull<BitVecCommand>, command: NonNull<BitVecCommand>,
) -> BinaryOperation { ) -> BinaryOperation {
unsafe { command.as_ref().operation.clone() } // TODO remove clone unsafe { command.as_ref().operation }
} }
/// Overwrites the [BinaryOperation] of the command. /// Overwrites the [BinaryOperation] of the command.

View file

@ -1,7 +1,5 @@
//! FFI slice helper //! FFI slice helper
use std::ptr::NonNull;
/// Represents a span of memory (`&mut [u8]` ) as a struct. /// Represents a span of memory (`&mut [u8]` ) as a struct.
/// ///
/// # Safety /// # Safety
@ -11,28 +9,39 @@ use std::ptr::NonNull;
/// - accesses to the memory pointed to with `start` is never accessed outside `length` /// - accesses to the memory pointed to with `start` is never accessed outside `length`
/// - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in /// - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in
/// the function returning this type. /// the function returning this type.
/// - if `start` is NULL or `length` is 0, do not dereference `start`.
///
/// # Examples
///
/// ```c
/// ByteSlice empty = {.start: NULL, .length = 0};
/// ```
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ByteSlice { pub struct ByteSlice {
/// The start address of the memory. /// The start address of the memory.
pub start: NonNull<u8>, pub start: *mut u8,
/// The amount of memory in bytes. /// The amount of memory in bytes.
pub length: usize, pub length: usize,
} }
impl ByteSlice { impl ByteSlice {
pub(crate) const INVALID: ByteSlice = ByteSlice {
start: std::ptr::null_mut(),
length: 0,
};
pub(crate) unsafe fn as_slice(&self) -> &[u8] { pub(crate) unsafe fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) } unsafe { std::slice::from_raw_parts(self.start, self.length) }
} }
pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] { pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe { unsafe { std::slice::from_raw_parts_mut(self.start, self.length) }
std::slice::from_raw_parts_mut(self.start.as_ptr(), self.length)
}
} }
pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self { pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self {
Self { Self {
start: NonNull::new(slice.as_mut_ptr()).unwrap(), start: slice.as_mut_ptr(),
length: slice.len(), length: slice.len(),
} }
} }

View file

@ -35,10 +35,8 @@ pub mod packet;
/// Functions related to [UdpSocket]. /// Functions related to [UdpSocket].
pub mod udp; pub mod udp;
use std::time::Duration;
/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); pub const SP_FRAME_PACING_MS: u128 = 30;
/// This is a type only used by cbindgen to have a type for pointers. /// This is a type only used by cbindgen to have a type for pointers.
pub struct UdpSocket; pub struct UdpSocket;

View file

@ -20,13 +20,12 @@ pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_from_parts( pub unsafe extern "C" fn sp_packet_from_parts(
header: Header, header: Header,
payload: *const ByteSlice, payload: ByteSlice,
) -> NonNull<Packet> { ) -> NonNull<Packet> {
let payload = if payload.is_null() { let payload = if payload == ByteSlice::INVALID {
vec![] None
} else { } else {
let payload = unsafe { (*payload).as_slice() }; Some(Vec::from(unsafe { payload.as_slice() }))
Vec::from(payload)
}; };
heap_move_nonnull(Packet { header, payload }) heap_move_nonnull(Packet { header, payload })
@ -44,12 +43,19 @@ pub unsafe extern "C" fn sp_packet_get_header(
/// Returns a pointer to the current payload of the provided packet. /// Returns a pointer to the current payload of the provided packet.
/// ///
/// Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload.
///
/// The returned memory can be changed and will be valid until a new payload is set. /// The returned memory can be changed and will be valid until a new payload is set.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_get_payload( pub unsafe extern "C" fn sp_packet_get_payload(
packet: NonNull<Packet>, packet: NonNull<Packet>,
) -> ByteSlice { ) -> ByteSlice {
unsafe { ByteSlice::from_slice(&mut (*packet.as_ptr()).payload) } unsafe {
match &mut (*packet.as_ptr()).payload {
None => ByteSlice::INVALID,
Some(payload) => ByteSlice::from_slice(payload),
}
}
} }
/// Sets the payload of the provided packet to the provided data. /// Sets the payload of the provided packet to the provided data.
@ -60,7 +66,13 @@ pub unsafe extern "C" fn sp_packet_set_payload(
packet: NonNull<Packet>, packet: NonNull<Packet>,
data: ByteSlice, data: ByteSlice,
) { ) {
unsafe { (*packet.as_ptr()).payload = data.as_slice().to_vec() } unsafe {
(*packet.as_ptr()).payload = if data == ByteSlice::INVALID {
None
} else {
Some(data.as_slice().to_vec())
}
}
} }
/// Serialize the packet into the provided buffer. /// Serialize the packet into the provided buffer.

View file

@ -136,7 +136,7 @@ pub unsafe extern "C" fn sp_udp_send_header(
) -> bool { ) -> bool {
let packet = Packet { let packet = Packet {
header, header,
payload: vec![], payload: None,
}; };
unsafe { udp_connection.as_ref() } unsafe { udp_connection.as_ref() }
.send(&Vec::from(packet)) .send(&Vec::from(packet))