1pub mod error;
18pub mod mode;
19pub mod vfo;
20pub mod commands;
21pub mod adif;
22
23use crate::commands::{get_freq, get_info, get_mode, get_split_freq, get_split_mode, get_split_vfo, get_vfo};
24use crate::error::RigCtlError;
25use crate::vfo::VFO;
26use std::time::Duration;
27use tokio::io::{AsyncReadExt, AsyncWriteExt};
28use tokio::net::TcpStream;
29use tokio::time;
30
31pub struct RigCtlClient {
32 host: String,
33 port: u16,
34 stream: Option<TcpStream>,
35 timeout: Duration,
36}
37
38impl RigCtlClient {
39 pub fn new(host: &str, port: u16, timeout: Option<u64>) -> Self {
40 Self {
41 host: String::from(host),
42 port,
43 stream: None,
44 timeout: Duration::from_millis(timeout.unwrap_or(1000)),
45 }
46 }
47
48 pub async fn connect(&mut self) -> Result<(), RigCtlError> {
49 if self.is_connected() {
50 return Err(RigCtlError::AlreadyConnected);
51 }
52
53 let connection_string = format!("{}:{}", self.host, self.port);
54
55 let stream = TcpStream::connect(connection_string).await?;
56 self.stream = Some(stream);
57
58 Ok(())
59 }
60
61 pub fn disconnect(&mut self) {
62 if !self.is_connected() {
63 return;
64 }
65
66 self.stream = None;
67 }
68
69 pub fn is_connected(&self) -> bool {
70 self.stream.is_some()
71 }
72
73 pub fn set_communication_timeout(&mut self, timeout: u64) {
74 self.timeout = Duration::from_millis(timeout);
75 }
76
77 pub async fn get_info(&mut self) -> Result<get_info::Response, RigCtlError> {
78 let cmd = "get_info".to_string();
79 let response = self.execute_command(&cmd).await?;
80 get_info::parse(&response)
81 }
82
83 pub async fn get_mode(&mut self, vfo: VFO) -> Result<get_mode::Response, RigCtlError> {
84 let cmd = format!("get_mode {}", vfo);
85 let response = self.execute_command(&cmd).await?;
86 get_mode::parse(&response)
87 }
88
89 pub async fn get_freq(&mut self, vfo: VFO) -> Result<get_freq::Response, RigCtlError> {
90 let cmd = format!("get_freq {}", vfo);
91 let response = self.execute_command(&cmd).await?;
92 get_freq::parse(&response)
93 }
94
95 pub async fn get_vfo(&mut self) -> Result<get_vfo::Response, RigCtlError> {
96 let cmd = "get_vfo".to_string();
97 let response = self.execute_command(&cmd).await?;
98 get_vfo::parse(&response)
99 }
100
101 pub async fn get_split_vfo(&mut self) -> Result<get_split_vfo::Response, RigCtlError> {
102 let cmd = "get_split_vfo 0".to_string();
103 let response = self.execute_command(&cmd).await?;
104 get_split_vfo::parse(&response)
105 }
106
107 pub async fn get_split_mode(&mut self, vfo: VFO) -> Result<get_split_mode::Response, RigCtlError> {
108 let cmd = format!("get_split_mode {}", vfo);
109 let response = self.execute_command(&cmd).await?;
110 get_split_mode::parse(&response)
111 }
112
113 pub async fn get_split_freq(&mut self, vfo: VFO) -> Result<get_split_freq::Response, RigCtlError> {
114 let cmd = format!("get_split_freq {}", vfo);
115 let response = self.execute_command(&cmd).await?;
116 get_split_freq::parse(&response)
117 }
118
119 fn compose_command(&self, command: &str) -> String {
120 format!("|\\{}", command)
121 }
122
123 async fn execute_command(&mut self, command: &str) -> Result<String, RigCtlError> {
124 let cmd = self.compose_command(&command);
125 self.write_line(&cmd).await?;
126 self.read_line().await
127 }
128
129 async fn read_line(&mut self) -> Result<String, RigCtlError> {
130 log::debug!("Reading line");
131
132 let mut buf = [0u8; 4096];
133 let bytes_read = time::timeout(
134 self.timeout,
135 self.stream.
136 as_mut().unwrap()
137 .read(&mut buf))
138 .await
139 .map_err(|_| RigCtlError::CommunicationTimeout)??;
140
141 let line = String::from_utf8(buf[0..bytes_read].to_owned())?
142 .trim_end().to_string();
143
144 log::trace!(" <<< [{}] ({} bytes)", line, line.len());
145
146 Ok(line)
147 }
148
149 async fn write_line(&mut self, data: &str) -> Result<(), RigCtlError> {
150 log::debug!("Writing line");
151 log::trace!(" >>> [{}] ({} bytes)", data, data.len());
152 time::timeout(
153 self.timeout,
154 self.stream
155 .as_mut().unwrap()
156 .write_all(format!("{}\n", data).as_bytes()))
157 .await
158 .map_err(|_| RigCtlError::CommunicationTimeout)??;
159 Ok(())
160 }
161}