-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make NT bindings pretty much complete
Last thing is the multi subscriber
- Loading branch information
1 parent
33f6889
commit d84dfc2
Showing
4 changed files
with
728 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }, | ||
} | ||
} | ||
} |
Oops, something went wrong.