From 45db182e6db35b79d50153b35a29c3b8130e8561 Mon Sep 17 00:00:00 2001 From: saturnhafen Date: Tue, 1 Feb 2022 15:59:50 +0100 Subject: [PATCH 1/2] switch to websocket library for windows support for wss:// --- ev3-tunnel-entry/Cargo.toml | 2 +- ev3-tunnel-entry/src/main.rs | 190 ++++++++++++++++------------------- 2 files changed, 90 insertions(+), 102 deletions(-) diff --git a/ev3-tunnel-entry/Cargo.toml b/ev3-tunnel-entry/Cargo.toml index b184bd8..663ecce 100644 --- a/ev3-tunnel-entry/Cargo.toml +++ b/ev3-tunnel-entry/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tungstenite = "0.16.0" +websocket = "0.26.2" serde_json = "1" confy = "0.4.0" serde = { version = "1.0", features = ["derive"] } diff --git a/ev3-tunnel-entry/src/main.rs b/ev3-tunnel-entry/src/main.rs index 9509a5b..3a7a5c3 100644 --- a/ev3-tunnel-entry/src/main.rs +++ b/ev3-tunnel-entry/src/main.rs @@ -3,9 +3,9 @@ use serde::{Deserialize, Serialize}; use serde_json::{from_str, Value}; use std::io::prelude::*; use std::net::TcpStream; -use tungstenite::client::connect; -use tungstenite::protocol::{Message, WebSocket}; -use tungstenite::stream::MaybeTlsStream; +use websocket::sync::stream::TlsStream; +use websocket::sync::Client; +use websocket::{ClientBuilder, Message, OwnedMessage}; mod labview; @@ -36,22 +36,17 @@ fn load_config() -> Config { config } -fn connect_ws(mut config: Config) -> WebSocket> { +fn connect_ws(mut config: Config) -> Client> { println!( "Connecting to {}:{}/{}", config.remote, config.port, config.path ); - let (mut connection, _response) = connect(format![ - "ws://{}:{}/{}", - config.remote, config.port, config.path - ]) - .expect( - format![ - "Couldn't connect to remote <{}> on port <{}>", - config.remote, config.port - ] - .as_str(), - ); + let mut client = ClientBuilder::new( + format!["wss://{}:{}/{}", config.remote, config.port, config.path].as_str(), + ) + .unwrap() + .connect_secure(None) + .unwrap(); let message: String; @@ -61,71 +56,75 @@ fn connect_ws(mut config: Config) -> WebSocket> { } println!("Sending: {}", message); - connection - .write_message(Message::Text(message)) // JSON as specifiied in ev3cconnect README + + client + .send_message(&Message::text(message)) // JSON as specifiied in ev3cconnect README .expect("Couldn't queue init message"); - let response: Message = connection - .read_message() + let response = client + .recv_message() .expect("Couldn't read from Websocket..."); - if response.is_text() { - println!("Got: {:?}", response.to_text().unwrap()); + match response { + OwnedMessage::Text(payload) => { + println!("Got: {:?}", payload); - let resp: Value = from_str(response.to_text().expect("Couldn't read text...")) - .expect("Couldn't parse json..."); + let resp: Value = from_str(&payload).expect("Couldn't parse json..."); - if resp["Rejected"] != Value::Null { - let error = resp["Rejected"].to_string(); + if resp["Control"] != Value::Null { + let ev3 = resp["Control"].to_string(); + config.ev3 = Some(ev3); - println!( - "Couldn't connect to remote, please inform the server operator. Reason: {}", - error - ); - panic!(); - } + store_path(CONFIG_PATH, &config).expect("Couldn't store config..."); + } - if resp["Control"] != Value::Null { - let ev3 = resp["Control"].to_string(); - config.ev3 = Some(ev3); + if resp["Rejected"] != Value::Null { + let error = resp["Rejected"].to_string(); - store_path(CONFIG_PATH, &config).expect("Couldn't store config..."); - } - // store to Config - - if resp["Queue"] != Value::Null { - println!( - "--------------------------------------------------------------------------------" - ); - println!( - "Awaiting control from ev3. Until then, the ev3 will NOT show up in LEGO LabView!" - ); - println!( - "--------------------------------------------------------------------------------" - ); + println!( + "Couldn't connect to remote, please inform the server operator. Reason: {}", + error + ); + panic!(); + } - loop { - let response: Message = connection - .read_message() - .expect("Couldn't read from Websocket..."); + if resp["Queue"] != Value::Null { + println!( + "--------------------------------------------------------------------------------" + ); + println!( + "Awaiting control from ev3. Until then, the ev3 will NOT show up in LEGO LabView!" + ); + println!( + "--------------------------------------------------------------------------------" + ); - if response.is_text() { - let resp: Value = from_str(response.to_text().expect("Couldn't read text...")) - .expect("Couldn't parse json..."); + loop { + let response = client + .recv_message() + .expect("Couldn't read from Websocket..."); - if resp["Control"] != Value::Null { - let ev3 = resp["Control"].to_string(); - config.ev3 = Some(ev3); + match response { + OwnedMessage::Text(payload) => { + let resp: Value = from_str(&payload).expect("Couldn't parse json..."); - store_path(CONFIG_PATH, &config).expect("Couldn't store config..."); - break; + if resp["Control"] != Value::Null { + let ev3 = resp["Control"].to_string(); + config.ev3 = Some(ev3); + + store_path(CONFIG_PATH, &config).expect("Couldn't store config..."); + break; + } + } + _ => {} } } } } + _ => (), } - connection + client } fn main() { @@ -134,64 +133,53 @@ fn main() { let mut labview_connection = labview::connect(); let mut buf = [0; 65555]; // 65536 is max value of u16, just use a few more for good measure more for length - let mut length; let mut response; loop { - let result = labview_connection.read(&mut buf); - - match result { - Ok(x) => length = x, - Err(x) => { - println!( - "Connection to LabView was closed, attempting to reconnect... (Error: {:?})", - x - ); - labview_connection = labview::connect(); - continue; - } - } + let len = labview_connection + .read(&mut buf) + .expect("Couldn't read from LabView connection"); websocket - .write_message(Message::Binary((&buf[..length]).to_vec())) + .send_message(&Message::binary(&buf[..len])) .expect("Couldn't write to WebSocket connection..."); if buf[4].eq(&0x00) || buf[4].eq(&0x01) { // DIRECT_COMMAND_REPLY || SYSTEM_COMMAND_REPLY loop { response = websocket - .read_message() + .recv_message() .expect("Couldn't read from websocket connection..."); - if response.is_binary() { - break; - } else if response.is_ping() { - websocket - .write_message(Message::Pong(response.into_data())) - .expect("Couldn't send pong reply"); - } else { - println!("No binary frame, got: {}", response); - } - } - let data = response.into_data(); + match response { + OwnedMessage::Ping(payload) => { + websocket + .send_message(&Message::pong(payload)) + .expect("Couldn't send pong reply"); + } - //println!(" | len | cnt |rs| pl "); - //println!("Recv: 0x|{}|", to_hex_string(&data)); + OwnedMessage::Binary(payload) => { + if (payload.len() - 2) as u16 + != (((payload[1] as u16) << 8) | payload[0] as u16) + { + panic!( + "Expected size does not match received size! (Expected: {}, Received: {})", + (((payload[0] as u16) << 8) | payload[1] as u16), + payload.len() - 2 + ); + } + + labview_connection + .write(&payload) + .expect("Couldn't write to LabView connection..."); - if (data.len() - 2) as u16 != (((data[1] as u16) << 8) | data[0] as u16) { - panic!( - "Expected size does not match received size! (Expected: {}, Received: {})", - (((data[0] as u16) << 8) | data[1] as u16), - data.len() - 2 - ); + break; + } + _ => {} + } } - - labview_connection - .write(&data) - .expect("Couldn't write to LabView connection..."); } else if buf[4].eq(&0x80) || buf[4].eq(&0x81) { // DIRECT_COMMAND_NO_REPLY || SYSTEM_COMMAND_NO_REPLY - continue; } else { debug_assert!(false, "Got a strange message type!"); From 20c88593ab53969509d8bed64a2cd4aae37ee2dd Mon Sep 17 00:00:00 2001 From: saturnhafen Date: Tue, 1 Feb 2022 16:01:57 +0100 Subject: [PATCH 2/2] switch to websocket library for windows wss:// support; add io_bluetooth for bluetooth connectivity (instead of wifi) --- ev3-tunnel-exit/Cargo.toml | 4 +- ev3-tunnel-exit/src/ev3.rs | 76 ++++++--------------------------- ev3-tunnel-exit/src/main.rs | 84 ++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 103 deletions(-) diff --git a/ev3-tunnel-exit/Cargo.toml b/ev3-tunnel-exit/Cargo.toml index 75169cd..dffa8d5 100644 --- a/ev3-tunnel-exit/Cargo.toml +++ b/ev3-tunnel-exit/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tungstenite = "0.16.0" -regex = "1" +websocket = "0.26.2" +io_bluetooth = "0.1" confy = "0.4.0" serde = { version = "1.0", features = ["derive"] } diff --git a/ev3-tunnel-exit/src/ev3.rs b/ev3-tunnel-exit/src/ev3.rs index d9bec44..94dc2de 100644 --- a/ev3-tunnel-exit/src/ev3.rs +++ b/ev3-tunnel-exit/src/ev3.rs @@ -1,7 +1,5 @@ -use regex::Regex; -use std::io::prelude::*; -use std::net::{SocketAddr, TcpStream, UdpSocket}; -use std::str::from_utf8; +use io_bluetooth::bt::{self, BtAddr, BtStream}; +use std::iter; pub fn to_hex_string(bytes: &[u8]) -> String { let strs: Vec = bytes.iter().map(|b| format!("{:02X}", b)).collect(); @@ -10,79 +8,31 @@ pub fn to_hex_string(bytes: &[u8]) -> String { } pub struct EV3 { - connection: TcpStream, + connection: BtStream, pub name: String, } impl EV3 { - pub fn connect() -> EV3 { - let socket = UdpSocket::bind(SocketAddr::from(([0, 0, 0, 0], 3015))) - .expect("couldn't bind to address"); + pub fn connect(ev3: BtAddr, name: &String) -> EV3 { + println!("Connecting to {}", ev3); - let mut buf = [0; 70]; + let socket = BtStream::connect(iter::once(&ev3), bt::BtProtocol::RFCOMM).unwrap(); - println!("Receiving data from ev3..."); - let (mut recv_count, remote) = socket.recv_from(&mut buf).expect("Didn't receive data"); - - let mut recv_data = &mut buf[..recv_count]; - - let re = - Regex::new(r"Serial-Number: (\w*)\s\nPort: (\d{1,5})\s\nName: (.*)\s\nProtocol: EV3") - .unwrap(); - - let re_match = re - .captures(from_utf8(&recv_data).expect("Invalid utf-8 sequence")) - .unwrap(); - - println!("Replying to enable TCP..."); - let send_count = socket - .send_to(&[0; 10], remote) - .expect("Couldn't send data"); - - if send_count != 10 { - panic!( - "Should be able to send packet with size 10, was able to send {}!", - send_count - ); + match socket.peer_addr() { + Ok(addr) => println!("Successfully connected to {}.", addr.to_string()), + Err(err) => panic!("An error occured while connecting: {:?}", err), } - let mut stream = TcpStream::connect(SocketAddr::from((remote.ip(), 5555))) - .expect("TCP-Connection failed"); - - let unlock_payload = format!( - "GET /target?sn={} VMTP1.0\nProtocol: EV3", - re_match.get(1).unwrap().as_str() - ); - - let name = re_match.get(3).unwrap().as_str().to_string(); - - stream - .write(unlock_payload.as_bytes()) - .expect("Couldn't send data to connection"); - - recv_count = stream - .read(&mut buf) - .expect("Couldn't read data from connection"); - - recv_data = &mut buf[..recv_count]; - - print!( - "Brick reply: {}", - from_utf8(&recv_data).expect("Invalid utf-8 sequence") - ); - - println!("Brick should be unlocked!"); - EV3 { - connection: stream, - name, + connection: socket, + name: name.to_string(), } } pub fn send_command(&mut self, payload: &[u8]) -> Vec { let _send_count = self .connection - .write(&payload) + .send(&payload) .expect("Couldn't write to EV3 connection..."); println!("Send to EV3:"); @@ -97,7 +47,7 @@ impl EV3 { } else { let recv_count = self .connection - .read(&mut recv_buf) + .recv(&mut recv_buf) .expect("Couldn't read from connection"); let recv_data = &recv_buf[..recv_count]; diff --git a/ev3-tunnel-exit/src/main.rs b/ev3-tunnel-exit/src/main.rs index a3cfaf0..d65a227 100644 --- a/ev3-tunnel-exit/src/main.rs +++ b/ev3-tunnel-exit/src/main.rs @@ -1,10 +1,11 @@ use confy::load_path; use ev3::EV3; +use io_bluetooth::bt::BtAddr; use serde::{Deserialize, Serialize}; use std::net::TcpStream; -use tungstenite::client::connect; -use tungstenite::protocol::{Message, WebSocket}; -use tungstenite::stream::MaybeTlsStream; +use websocket::sync::stream::TlsStream; +use websocket::sync::Client; +use websocket::{ClientBuilder, Message, OwnedMessage}; mod ev3; @@ -13,6 +14,9 @@ struct Config { remote: String, port: u16, path: String, + nap: u16, + sap: u32, + name: String, } impl ::std::default::Default for Config { @@ -21,6 +25,9 @@ impl ::std::default::Default for Config { remote: "localhost".to_string(), port: 8800, path: "ev3c".to_string(), + nap: 0x0, + sap: 0x0, + name: "EV3".to_string(), } } } @@ -31,61 +38,62 @@ fn load_config() -> Config { config } -fn connect_ev3() -> EV3 { - let ev3 = EV3::connect(); +fn connect_ev3(ev3: BtAddr, name: &String) -> EV3 { + let ev3 = EV3::connect(ev3, name); println!("Connected to: {}!", &ev3.name); ev3 } -fn connect_ws( - remote: &str, - port: u16, - path: &str, - ev3_name: &String, -) -> WebSocket> { - println!("Connecting to {}:{}/{}", remote, port, path); - let (mut connection, _response) = connect(format!["ws://{}:{}/{}", remote, port, path]) - .expect(format!["Couldn't connect to remote <{}> on port <{}>", remote, port].as_str()); - - connection - .write_message(Message::Text(format![ +fn connect_ws(config: Config, ev3_name: &String) -> Client> { + println!( + "Connecting to {}:{}/{}", + config.remote, config.port, config.path + ); + let mut client = ClientBuilder::new( + format!["wss://{}:{}/{}", config.remote, config.port, config.path].as_str(), + ) + .unwrap() + .connect_secure(None) + .unwrap(); + client + .send_message(&Message::text(format![ "{{\"id\": \"{}\"}}", ev3_name.as_str() ])) // JSON as specifiied in ev3cconnect README .expect("Couldn't queue init message"); - connection + println!("Connected to remote!"); + + client } fn main() { let config = load_config(); - - let mut ev3 = connect_ev3(); - let mut websocket = connect_ws(&config.remote, config.port, &config.path, &ev3.name); - - let mut seq_num: u16 = 0; + let mut ev3 = connect_ev3(BtAddr::nap_sap(config.nap, config.sap), &config.name); + let mut websocket = connect_ws(config, &ev3.name); loop { - let msg = websocket.read_message().unwrap(); - - if msg.is_binary() { - let buf = &msg.into_data(); - let response = ev3.send_command(buf); - if buf[4].eq(&0x80) || buf[4].eq(&0x81) { - // DIRECT_COMMAND_NO_REPLY || SYSTEM_COMMAND_NO_REPLY -> No need to read data from ev3 connection - continue; + let msg = websocket.recv_message().unwrap(); + + match msg { + OwnedMessage::Binary(payload) => { + let response = ev3.send_command(&payload); + if payload[4].eq(&0x80) || payload[4].eq(&0x81) { + // DIRECT_COMMAND_NO_REPLY || SYSTEM_COMMAND_NO_REPLY -> No need to read data from ev3 connection + continue; + } + + websocket + .send_message(&Message::binary(response)) + .expect("Couldn't write response!"); } - let seq_num_new = ((buf[3] as u16) << 8) | buf[2] as u16; - if seq_num_new < seq_num { - println!("======================================== \nCaution: Sequence-number rollover!\n==========================================="); + OwnedMessage::Ping(payload) => { + websocket.send_message(&Message::pong(payload)).unwrap(); } - seq_num = seq_num_new; - websocket - .write_message(Message::Binary(response)) - .expect("Couldn't write response!"); + _ => {} } } }