Skip to content

Commit

Permalink
Make NT bindings pretty much complete
Browse files Browse the repository at this point in the history
Last thing is the multi subscriber
  • Loading branch information
BlueZeeKing committed Aug 1, 2024
1 parent 33f6889 commit d84dfc2
Show file tree
Hide file tree
Showing 4 changed files with 728 additions and 21 deletions.
174 changes: 174 additions & 0 deletions crates/nt/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,176 @@
#[allow(warnings)]
pub mod bindings;
pub mod options;
pub mod payloads;

use std::{ffi::CString, marker::PhantomData, time::Duration};

use bindings::*;
use options::PubSubOptions;
use payloads::Payload;

pub struct Instance {
handle: NT_Inst,
}

impl Default for Instance {
fn default() -> Self {
Self {
handle: unsafe { NT_GetDefaultInstance() },
}
}
}

impl Instance {
pub fn new() -> Self {
Self {
handle: unsafe { NT_CreateInstance() },
}
}

pub fn topic(&self, name: &str) -> Topic {
Topic {
handle: unsafe {
NT_GetTopic(
self.handle,
CString::new(name).unwrap().into_raw(),
name.len(),
)
},
}
}

pub fn start_server(&self, persist: &str) {
unsafe {
NT_StartServer(
self.handle,
CString::new(persist).unwrap().into_raw(),
c"".as_ptr(),
1735,
5810,
)
};
}

pub fn is_starting(&self) -> bool {
let mode = unsafe { NT_GetNetworkMode(self.handle) };
mode & NT_NetworkMode_NT_NET_MODE_STARTING != 0
}
}

pub struct Topic {
handle: NT_Topic,
}

impl Topic {
pub fn publish<T: Payload>(&self, options: PubSubOptions) -> Publisher<T> {
Publisher {
handle: unsafe {
NT_Publish(
self.handle,
T::DATA_TYPE.into(),
T::DATA_TYPE_NAME.as_ptr(),
&options.build(),
)
},
payload: PhantomData,
}
}

pub fn publish_with_type_str<T: Payload>(
&self,
options: PubSubOptions,
type_str: &str,
) -> Publisher<T> {
Publisher {
handle: unsafe {
NT_Publish(
self.handle,
T::DATA_TYPE.into(),
CString::new(type_str).unwrap().into_raw(),
&options.build(),
)
},
payload: PhantomData,
}
}

pub fn subscribe<T: Payload>(&self, options: PubSubOptions) -> Subscriber<T> {
Subscriber {
handle: unsafe {
NT_Subscribe(
self.handle,
T::DATA_TYPE.into(),
T::DATA_TYPE_NAME.as_ptr(),
&options.build(),
)
},
payload: PhantomData,
}
}

pub fn subscribe_with_type_str<T: Payload>(
&self,
options: PubSubOptions,
type_str: &str,
) -> Subscriber<T> {
Subscriber {
handle: unsafe {
NT_Subscribe(
self.handle,
T::DATA_TYPE.into(),
CString::new(type_str).unwrap().into_raw(),
&options.build(),
)
},
payload: PhantomData,
}
}
}

pub struct Publisher<T> {
handle: NT_Publisher,
payload: PhantomData<T>,
}

impl<T: Payload> Publisher<T> {
pub fn set(&self, value: T) {
value.to_entry(self.handle, unsafe { NT_Now() });
}
}

pub struct Subscriber<T> {
handle: NT_Subscriber,
payload: PhantomData<T>,
}

impl<T: Payload> Subscriber<T> {
pub fn get_with_default(&self, default: T) -> T {
T::from_entry(self.handle, default)
}

pub fn get(&self) -> T
where
T: Default,
{
self.get_with_default(T::default())
}
}

impl<T> Drop for Subscriber<T> {
fn drop(&mut self) {
unsafe { NT_Unsubscribe(self.handle) }
}
}

impl<T> Drop for Publisher<T> {
fn drop(&mut self) {
unsafe { NT_Unpublish(self.handle) }
}
}

impl Drop for Instance {
fn drop(&mut self) {
unsafe { NT_DestroyInstance(self.handle) }
}
}
45 changes: 24 additions & 21 deletions crates/nt/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
use std::{ffi::CString, thread, time::Duration};
use std::{thread, time::Duration};

fn main() {
unsafe {
let inst = nt::bindings::NT_GetDefaultInstance();
use nt::{Instance, Publisher, Subscriber};

let address = CString::new("/home/lvuser/networktables.json").unwrap();
let local_path = CString::new("").unwrap();
fn main() {
let server = Instance::default();
server.start_server("nt.json");
if server.is_starting() {
let mut started = false;

let address_ptr = address.as_ptr();
let local_path_ptr = local_path.as_ptr();
for _ in 0..3 {
thread::sleep(Duration::from_millis(15));

std::mem::forget(address);
std::mem::forget(local_path);
if !server.is_starting() {
started = true;
break;
}
}

nt::bindings::NT_StartServer(inst, local_path_ptr, address_ptr, 1735, 5810);
if !started {
panic!("Failed to start server");
}
}

let mut amount_skipped: u8 = 0;
let publisher: Publisher<String> = server.topic("/test/value").publish(Default::default());

while nt::bindings::NT_GetNetworkMode(inst)
== nt::bindings::NT_NetworkMode_NT_NET_MODE_STARTING
{
thread::sleep(Duration::from_millis(10));
publisher.set("Test".to_string());

amount_skipped += 1;
let subscriber: Subscriber<String> = server.topic("/test/value").subscribe(Default::default());

if amount_skipped > 100 {
panic!("Time out");
}
}
loop {
dbg!(subscriber.get());
thread::sleep(Duration::from_secs(1));
}
}
159 changes: 159 additions & 0 deletions crates/nt/src/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use std::time::Duration;

use crate::{NT_PubSubOptions, NT_Publisher, Publisher};

/// NetworkTables publish/subscribe options.
#[derive(Default, Clone)]
pub struct PubSubOptions {
poll_storage: Option<u32>,
periodic: Option<Duration>,
exclude_publisher: Option<NT_Publisher>,
send_all: Option<bool>,
topics_only: Option<bool>,
prefix_match: Option<bool>,
keep_duplicates: Option<bool>,
disable_remote: Option<bool>,
disable_local: Option<bool>,
exclude_self: Option<bool>,
hidden: Option<bool>,
}

impl PubSubOptions {
/// Polling storage size for a subscription. Specifies the maximum number of\n updates NetworkTables should store between calls to the subscriber's\n ReadQueue() function. If zero, defaults to 1 if sendAll is false, 20 if\n sendAll is true.
pub fn poll_storage(self, poll_storage: u32) -> Self {
Self {
poll_storage: Some(poll_storage),
..self
}
}

/// How frequently changes will be sent over the network, in seconds.\n NetworkTables may send more frequently than this (e.g. use a combined\n minimum period for all values) or apply a restricted range to this value.\n The default is 100 ms.
pub fn periodic(self, periodic: Duration) -> Self {
Self {
periodic: Some(periodic),
..self
}
}

/// For subscriptions, if non-zero, value updates for ReadQueue() are not\n queued for this publisher.
pub fn exclude_publisher<T>(self, publisher: Publisher<T>) -> Self {
Self {
exclude_publisher: Some(publisher.handle),
..self
}
}

/// Send all value changes over the network.
pub fn send_all(self, send_all: bool) -> Self {
Self {
send_all: Some(send_all),
..self
}
}

/// For subscriptions, don't ask for value changes (only topic announcements).
pub fn topics_only(self, topics_only: bool) -> Self {
Self {
topics_only: Some(topics_only),
..self
}
}

/// Perform prefix match on subscriber topic names. Is ignored/overridden by\n Subscribe() functions; only present in struct for the purposes of getting\n information about subscriptions.
pub fn prefix_match(self, prefix_match: bool) -> Self {
Self {
prefix_match: Some(prefix_match),
..self
}
}

/// Preserve duplicate value changes (rather than ignoring them).
pub fn keep_duplicates(self, keep_duplicates: bool) -> Self {
Self {
keep_duplicates: Some(keep_duplicates),
..self
}
}

/// For subscriptions, if remote value updates should not be queued for\n ReadQueue(). See also disableLocal.
pub fn disable_remote(self, disable_remote: bool) -> Self {
Self {
disable_remote: Some(disable_remote),
..self
}
}

/// For subscriptions, if local value updates should not be queued for\n ReadQueue(). See also disableRemote.
pub fn disable_local(self, disable_local: bool) -> Self {
Self {
disable_local: Some(disable_local),
..self
}
}

/// For entries, don't queue (for ReadQueue) value updates for the entry's\n internal publisher.
pub fn exclude_self(self, exclude_self: bool) -> Self {
Self {
exclude_self: Some(exclude_self),
..self
}
}

/// For subscriptions, don't share the existence of the subscription with the\n network. Note this means updates will not be received from the network\n unless another subscription overlaps with this one, and the subscription\n will not appear in metatopics.
pub fn hidden(self, hidden: bool) -> Self {
Self {
hidden: Some(hidden),
..self
}
}

pub(crate) fn build(self) -> NT_PubSubOptions {
NT_PubSubOptions {
structSize: size_of::<NT_PubSubOptions>() as u32,
pollStorage: self.poll_storage.unwrap_or_else(|| {
if self.send_all.unwrap_or(false) {
20
} else {
1
}
}),
periodic: self
.periodic
.unwrap_or_else(|| Duration::from_millis(100))
.as_secs_f64(),
excludePublisher: self.exclude_publisher.unwrap_or(0),
sendAll: if self.send_all.unwrap_or(false) { 1 } else { 0 },
topicsOnly: if self.topics_only.unwrap_or(false) {
1
} else {
0
},
prefixMatch: if self.prefix_match.unwrap_or(false) {
1
} else {
0
},
keepDuplicates: if self.keep_duplicates.unwrap_or(false) {
1
} else {
0
},
disableRemote: if self.disable_remote.unwrap_or(false) {
1
} else {
0
},
disableLocal: if self.disable_local.unwrap_or(false) {
1
} else {
0
},
excludeSelf: if self.exclude_self.unwrap_or(false) {
1
} else {
0
},
hidden: if self.hidden.unwrap_or(false) { 1 } else { 0 },
}
}
}
Loading

0 comments on commit d84dfc2

Please sign in to comment.