1use super::port;
12use super::proto::{self, DeviceRoute, Packet, ProxyStatus};
13use super::proxy_core::{ProxyClient, ProxyCore};
14use super::util;
15use super::util::{TioRpcReplyable, TioRpcRequestable};
16
17use std::env;
18use std::thread;
19use std::time::Duration;
20
21use crossbeam::channel;
22
23#[derive(Debug)]
25pub enum Event {
26 SensorConnected,
27 SensorDisconnected,
28 SensorReconnected,
29 FailedToConnect,
30 FailedToReconnect,
31 Exiting,
32 ProtocolError(proto::Error),
33 FatalError(port::RecvError),
34 NewClient(u64),
35 RpcRemap((u64, u16), u16),
36 RpcRestore(u16, (u64, u16)),
37 RpcRestoreNotFound(u16),
38 RpcClientNotFound(u64),
39 RpcTimeout(u16),
40 RpcCancel(u16),
41 ClientSendFailed(u64),
42 ClientTerminated(u64),
43 RootDeviceRestarted,
44 AutoRateGaveUp,
45 AutoRateQueried(u32),
46 AutoRateRpcError(proto::RpcErrorCode),
47 AutoRateRpcInvalid,
48 AutoRateIncompatible(u32),
49 AutoRateCompatible(u32),
50 AutoRateWait,
51 AutoRateSet(u32),
52 SetRate(u32),
53 SetRateFailed,
54 NoData,
55}
56
57impl From<ProxyStatus> for super::proxy::Event {
58 fn from(status: ProxyStatus) -> Self {
59 match status {
60 ProxyStatus::SensorDisconnected => Event::SensorDisconnected,
61 ProxyStatus::SensorReconnected => Event::SensorReconnected,
62 ProxyStatus::FailedToReconnect => Event::FailedToReconnect,
63 ProxyStatus::FailedToConnect => Event::FailedToConnect,
64 ProxyStatus::Unknown(_) => Event::SensorDisconnected,
65 }
66 }
67}
68
69pub struct Port {
71 tx: channel::Sender<Packet>,
72 rx: channel::Receiver<Packet>,
73 depth: usize,
74 scope: DeviceRoute,
75}
76
77#[derive(Debug, Clone, thiserror::Error)]
78pub enum SendError {
79 #[error("channel full")]
80 WouldBlock(Packet),
81 #[error("proxy disconnected")]
82 ProxyDisconnected(Packet),
83 #[error("route exceeds port scope")]
84 InvalidRoute(Packet),
85}
86
87#[derive(Debug, Clone, thiserror::Error)]
88pub enum RecvError {
89 #[error("no packet available")]
90 WouldBlock,
91 #[error("proxy disconnected")]
92 ProxyDisconnected,
93}
94
95#[derive(Debug, Clone, thiserror::Error)]
96pub enum RpcError {
97 #[error("failed to send RPC request: {0}")]
98 SendFailed(#[from] SendError),
99 #[error("device returned error: {0}")]
100 ExecError(proto::RpcErrorPayload),
101 #[error("failed to receive RPC reply: {0}")]
102 RecvFailed(#[from] RecvError),
103 #[error("RPC reply did not match expected type")]
104 TypeError,
105}
106
107impl Port {
108 pub fn scope(&self) -> DeviceRoute {
110 self.scope.clone()
111 }
112
113 pub fn send(&self, packet: Packet) -> Result<(), SendError> {
116 if packet.routing.len() > self.depth {
117 return Err(SendError::InvalidRoute(packet));
118 }
119 match self.tx.send(packet) {
120 Ok(()) => Ok(()),
121 Err(se) => Err(SendError::ProxyDisconnected(se.into_inner())),
122 }
123 }
124
125 pub fn try_send(&self, packet: Packet) -> Result<(), SendError> {
127 if packet.routing.len() > self.depth {
128 return Err(SendError::InvalidRoute(packet));
129 }
130 match self.tx.try_send(packet) {
131 Ok(()) => Ok(()),
132 Err(crossbeam::channel::TrySendError::Full(pkt)) => Err(SendError::WouldBlock(pkt)),
133 Err(crossbeam::channel::TrySendError::Disconnected(pkt)) => {
134 Err(SendError::ProxyDisconnected(pkt))
135 }
136 }
137 }
138
139 pub fn select_send<'a>(&'a self, sel: &mut crossbeam::channel::Select<'a>) -> usize {
141 sel.send(&self.tx)
142 }
143
144 pub fn recv(&self) -> Result<Packet, RecvError> {
146 match self.rx.recv() {
147 Ok(pkt) => Ok(pkt),
148 Err(crossbeam::channel::RecvError) => Err(RecvError::ProxyDisconnected),
149 }
150 }
151
152 pub fn try_recv(&self) -> Result<Packet, RecvError> {
154 match self.rx.try_recv() {
155 Ok(pkt) => Ok(pkt),
156 Err(crossbeam::channel::TryRecvError::Empty) => Err(RecvError::WouldBlock),
157 Err(crossbeam::channel::TryRecvError::Disconnected) => {
158 Err(RecvError::ProxyDisconnected)
159 }
160 }
161 }
162
163 pub fn select_recv<'a>(&'a self, sel: &mut crossbeam::channel::Select<'a>) -> usize {
165 sel.recv(&self.rx)
166 }
167
168 pub fn receiver<'a>(&'a self) -> &'a crossbeam::channel::Receiver<Packet> {
170 &self.rx
171 }
172
173 pub fn iter(&self) -> crossbeam::channel::Iter<'_, Packet> {
175 self.rx.iter()
176 }
177
178 pub fn try_iter(&self) -> crossbeam::channel::TryIter<'_, Packet> {
180 self.rx.try_iter()
181 }
182
183 pub fn raw_rpc(&self, name: &str, arg: &[u8]) -> Result<Vec<u8>, RpcError> {
185 if let Err(err) = self.send(util::PacketBuilder::make_rpc_request(
186 name,
187 arg,
188 0,
189 DeviceRoute::root(),
190 )) {
191 return Err(RpcError::SendFailed(err));
192 }
193 loop {
194 match self.recv() {
195 Ok(pkt) => match pkt.payload {
196 proto::Payload::RpcReply(rep) => return Ok(rep.reply),
197 proto::Payload::RpcError(err) => return Err(RpcError::ExecError(err)),
198 _ => continue,
199 },
200 Err(err) => {
201 return Err(RpcError::RecvFailed(err));
202 }
203 }
204 }
205 }
206
207 pub fn rpc<ReqT: TioRpcRequestable<ReqT>, RepT: TioRpcReplyable<RepT>>(
208 &self,
209 name: &str,
210 arg: ReqT,
211 ) -> Result<RepT, RpcError> {
212 let ret = self.raw_rpc(name, &arg.to_request())?;
213 if let Ok(val) = RepT::from_reply(&ret) {
214 Ok(val)
215 } else {
216 Err(RpcError::TypeError)
217 }
218 }
219
220 pub fn action(&self, name: &str) -> Result<(), RpcError> {
222 self.rpc(name, ())
223 }
224
225 pub fn get<T: TioRpcReplyable<T>>(&self, name: &str) -> Result<T, RpcError> {
226 self.rpc(name, ())
227 }
228}
229
230#[derive(Debug, Clone, thiserror::Error)]
231pub enum PortError {
232 #[error("RPC timeout too short")]
233 RpcTimeoutTooShort,
234 #[error("RPC timeout too long")]
235 RpcTimeoutTooLong,
236 #[error("failed to set up new proxy client")]
237 FailedNewClientSetup,
238}
239
240pub struct Interface {
242 new_client_queue: channel::Sender<ProxyClient>,
243 new_client_confirm: Option<channel::Receiver<Event>>,
244 client_rx_channel_size: usize,
245 client_tx_channel_size: usize,
246}
247
248impl Interface {
249 pub fn new_proxy(
251 url: &str,
252 reconnect_timeout: Option<Duration>,
253 status_queue: Option<channel::Sender<Event>>,
254 ) -> Interface {
255 let (client_sender, client_receiver) = channel::bounded::<ProxyClient>(5);
256 let (status_sender, status_receiver, only_clients) = {
257 if let Some(status_sender) = status_queue {
258 (status_sender, None, false)
259 } else {
260 let (s, r) = channel::bounded::<Event>(50);
261 (s, Some(r), true)
262 }
263 };
264 let url_string = url.to_string();
265 thread::spawn(move || {
266 #[cfg(target_os = "windows")]
267 let _priority = super::os::windows_helpers::ActivityGuard::latency_critical()
268 .map_err(|e| eprintln!("proxy core: failed to raise thread priority: {e}"))
269 .ok();
270
271 #[cfg(target_os = "macos")]
272 let _activity =
273 super::os::macos_helpers::ActivityGuard::latency_critical("Twinleaf proxy core");
274
275 let mut proxy = ProxyCore::new(
276 url_string,
277 reconnect_timeout,
278 client_receiver,
279 status_sender,
280 only_clients,
281 );
282 proxy.run();
283 });
284 Interface {
285 new_client_queue: client_sender,
286 new_client_confirm: status_receiver,
287 client_rx_channel_size: Self::get_client_rx_channel_size(),
288 client_tx_channel_size: Self::get_client_tx_channel_size(),
289 }
290 }
291
292 pub fn new(url: &str) -> Interface {
294 Self::new_proxy(url, None, None)
295 }
296
297 pub fn default() -> Interface {
300 Self::new(util::default_proxy_url())
301 }
302
303 pub fn get_client_rx_channel_size() -> usize {
304 let min_size = port::DEFAULT_RX_CHANNEL_SIZE;
305 if let Ok(req) = env::var("TWINLEAF_PROXY_INTERFACE_RX_BUFSIZE") {
306 std::cmp::max(req.parse().unwrap_or(0), min_size)
307 } else {
308 min_size
309 }
310 }
311
312 pub fn get_client_tx_channel_size() -> usize {
313 let min_size = port::DEFAULT_TX_CHANNEL_SIZE;
314 if let Ok(req) = env::var("TWINLEAF_PROXY_INTERFACE_TX_BUFSIZE") {
315 std::cmp::max(req.parse().unwrap_or(0), min_size)
316 } else {
317 min_size
318 }
319 }
320
321 pub fn new_port(
323 &self,
324 rpc_timeout: Option<Duration>,
325 scope: DeviceRoute,
326 depth: usize,
327 forward_data: bool,
328 forward_nonrpc: bool,
329 ) -> Result<Port, PortError> {
330 let default_rpc_timeout = Duration::from_millis(3000);
331 let rpc_timeout = rpc_timeout.unwrap_or(default_rpc_timeout);
332 if rpc_timeout < Duration::from_millis(100) {
333 return Err(PortError::RpcTimeoutTooShort);
334 }
335 if rpc_timeout > Duration::from_secs(60) {
336 return Err(PortError::RpcTimeoutTooLong);
337 }
338
339 let (client_to_proxy_sender, proxy_from_client_receiver) =
340 channel::bounded::<Packet>(self.client_tx_channel_size);
341 let (proxy_to_client_sender, client_from_proxy_receiver) =
342 channel::bounded::<Packet>(self.client_rx_channel_size);
343 if let Err(_) = self.new_client_queue.send(ProxyClient::new(
344 proxy_to_client_sender,
345 proxy_from_client_receiver,
346 rpc_timeout,
347 scope.clone(),
348 depth,
349 forward_data,
350 forward_nonrpc,
351 )) {
352 return Err(PortError::FailedNewClientSetup);
353 }
354 if let Some(confirm) = &self.new_client_confirm {
355 if let Err(_) = confirm.recv() {
356 return Err(PortError::FailedNewClientSetup);
357 }
358 }
359 Ok(Port {
360 tx: client_to_proxy_sender,
361 rx: client_from_proxy_receiver,
362 depth: depth,
363 scope: scope,
364 })
365 }
366
367 pub fn subtree_full(&self, subtree_root: DeviceRoute) -> Result<Port, PortError> {
369 self.new_port(None, subtree_root, usize::MAX, true, true)
370 }
371
372 pub fn subtree_rpc(&self, subtree_root: DeviceRoute) -> Result<Port, PortError> {
374 self.new_port(None, subtree_root, usize::MAX, false, false)
375 }
376
377 pub fn subtree_probe(&self, subtree_root: DeviceRoute) -> Result<Port, PortError> {
379 self.new_port(None, subtree_root, usize::MAX, false, true)
380 }
381
382 pub fn tree_probe(&self) -> Result<Port, PortError> {
384 self.subtree_probe(DeviceRoute::root())
385 }
386
387 pub fn tree_full(&self) -> Result<Port, PortError> {
389 self.subtree_full(DeviceRoute::root())
390 }
391
392 pub fn tree_rpc(&self) -> Result<Port, PortError> {
394 self.subtree_rpc(DeviceRoute::root())
395 }
396
397 pub fn device_full(&self, address: DeviceRoute) -> Result<Port, PortError> {
399 self.new_port(None, address, 0, true, true)
400 }
401
402 pub fn device_rpc(&self, address: DeviceRoute) -> Result<Port, PortError> {
404 self.new_port(None, address, 0, false, false)
405 }
406
407 pub fn root_full(&self) -> Result<Port, PortError> {
409 self.device_full(DeviceRoute::root())
410 }
411
412 pub fn root_rpc(&self) -> Result<Port, PortError> {
414 self.device_rpc(DeviceRoute::root())
415 }
416}