telnet_client/
telnet_client.rs1use roland_core::{Address, Command, Response, RolandError};
7use std::io::{Read, Write};
8use std::net::TcpStream;
9use std::time::Duration;
10
11#[derive(Debug)]
13pub enum TelnetError {
14 Protocol(RolandError),
16 Io(std::io::Error),
18 ConnectionClosed,
20}
21
22impl std::fmt::Display for TelnetError {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 match self {
25 TelnetError::Protocol(e) => write!(f, "Protocol error: {}", e),
26 TelnetError::Io(e) => write!(f, "I/O error: {}", e),
27 TelnetError::ConnectionClosed => write!(f, "Connection closed"),
28 }
29 }
30}
31
32impl std::error::Error for TelnetError {}
33
34impl From<RolandError> for TelnetError {
35 fn from(e: RolandError) -> Self {
36 TelnetError::Protocol(e)
37 }
38}
39
40impl From<std::io::Error> for TelnetError {
41 fn from(e: std::io::Error) -> Self {
42 TelnetError::Io(e)
43 }
44}
45
46pub struct TelnetClient {
48 stream: TcpStream,
49 buffer: Vec<u8>,
50}
51
52impl TelnetClient {
53 pub fn connect(host: &str, port: u16) -> Result<Self, TelnetError> {
62 let addr = format!("{}:{}", host, port);
63 let stream = TcpStream::connect(&addr)?;
64
65 stream.set_read_timeout(Some(Duration::from_secs(5)))?;
67
68 stream.set_write_timeout(Some(Duration::from_secs(5)))?;
70
71 Ok(Self {
72 stream,
73 buffer: Vec::new(),
74 })
75 }
76
77 pub fn send_command(&mut self, command: &Command) -> Result<Response, TelnetError> {
85 let cmd_str = command.encode();
87 let cmd_bytes = cmd_str.as_bytes();
88
89 self.stream.write_all(cmd_bytes)?;
91 self.stream.flush()?;
92
93 self.read_response()
95 }
96
97 fn read_response(&mut self) -> Result<Response, TelnetError> {
99 let mut buf = [0u8; 1024];
100
101 let n = self.stream.read(&mut buf)?;
103
104 if n == 0 {
105 return Err(TelnetError::ConnectionClosed);
106 }
107
108 self.buffer.extend_from_slice(&buf[..n]);
110
111 let response_str = String::from_utf8_lossy(&self.buffer);
114
115 if response_str.ends_with(';') ||
117 response_str.contains('\x06') || response_str.contains('\x11') || response_str.contains('\x13')
120 {
121 let response = Response::parse(&response_str)?;
123 self.buffer.clear();
124 Ok(response)
125 } else {
126 std::thread::sleep(Duration::from_millis(100));
128 self.read_response()
129 }
130 }
131
132 pub fn write_parameter(&mut self, address: &str, value: u8) -> Result<(), TelnetError> {
141 let addr = Address::from_hex(address)?;
142 let cmd = Command::WriteParameter {
143 address: addr,
144 value,
145 };
146 let response = self.send_command(&cmd)?;
147
148 match response {
149 Response::Acknowledge => Ok(()),
150 Response::Error(e) => Err(TelnetError::Protocol(e)),
151 _ => Err(TelnetError::Protocol(RolandError::InvalidResponse)),
152 }
153 }
154
155 pub fn read_parameter(&mut self, address: &str, size: u32) -> Result<u8, TelnetError> {
164 let addr = Address::from_hex(address)?;
165 let cmd = Command::ReadParameter {
166 address: addr,
167 size,
168 };
169 let response = self.send_command(&cmd)?;
170
171 match response {
172 Response::Data { value, .. } => Ok(value),
173 Response::Error(e) => Err(TelnetError::Protocol(e)),
174 _ => Err(TelnetError::Protocol(RolandError::InvalidResponse)),
175 }
176 }
177
178 pub fn get_version(&mut self) -> Result<(String, String), TelnetError> {
183 let cmd = Command::GetVersion;
184 let response = self.send_command(&cmd)?;
185
186 match response {
187 Response::Version { product, version } => Ok((product, version)),
188 Response::Error(e) => Err(TelnetError::Protocol(e)),
189 _ => Err(TelnetError::Protocol(RolandError::InvalidResponse)),
190 }
191 }
192}
193
194fn main() -> Result<(), Box<dyn std::error::Error>> {
195 let args: Vec<String> = std::env::args().collect();
197 if args.len() < 2 {
198 eprintln!("Usage: {} <ip_address> [port]", args[0]);
199 eprintln!("Example: {} 192.168.1.100", args[0]);
200 std::process::exit(1);
201 }
202
203 let host = &args[1];
204 let port = args.get(2).and_then(|p| p.parse().ok()).unwrap_or(23);
205
206 println!("Connecting to {}:{}...", host, port);
207
208 let mut client = TelnetClient::connect(host, port)?;
209 println!("Connected!");
210
211 println!("\nGetting version information...");
213 match client.get_version() {
214 Ok((product, version)) => {
215 println!("Product: {}", product);
216 println!("Version: {}", version);
217 }
218 Err(e) => {
219 eprintln!("Error getting version: {}", e);
220 }
221 }
222
223 println!("\nReading parameter at address 000000...");
225 match client.read_parameter("000000", 1) {
226 Ok(value) => {
227 println!("Value: 0x{:02X} ({})", value, value);
228 }
229 Err(e) => {
230 eprintln!("Error reading parameter: {}", e);
231 }
232 }
233
234 Ok(())
247}