use std::collections::HashMap;
use std::convert::TryInto;
use std::io::{stdin, Write};
use websocket::client::{sync::Client, ClientBuilder};
use websocket::stream::sync::NetworkStream;
use websocket::{CloseData, OwnedMessage, WebSocketResult};
type WebSocketClient = Client<Box<dyn NetworkStream + Send>>;
use ichen_openprotocol::{Filters, JobCard, Message};
struct Constants {
users: HashMap<&'static str, (u8, String)>,
jobs: Vec<JobCard<'static>>,
}
fn display_message(prefix: &str, msg: &Message) {
print!("{}", prefix);
match msg {
Message::Alive { options, .. } => println!("Alive({})", options.sequence()),
Message::RequestControllersList { controller_id: None, options, .. } => {
println!("RequestControllersList({})", options.sequence())
}
Message::RequestControllersList { controller_id: Some(id), options, .. } => {
println!("RequestControllersList({}, {})", id, options.sequence())
}
Message::RequestJobCardsList { controller_id, options, .. } => {
println!("RequestJobCardsList({}, {})", controller_id, options.sequence())
}
Message::RequestMoldData { controller_id, options, .. } => {
println!("RequestMoldData({}, {})", controller_id, options.sequence())
}
Message::ReadMoldData { controller_id, field: None, options, .. } => {
println!("RequestMoldData({}, ALL, {})", controller_id, options.sequence())
}
Message::ReadMoldData { controller_id, field: Some(fld), options, .. } => {
println!("RequestMoldData({}, [{}], {})", controller_id, fld, options.sequence())
}
Message::ControllerAction { controller_id, action_id, options, .. } => {
println!("ControllerAction({}, [{}], {})", controller_id, action_id, options.sequence())
}
m if prefix.is_empty() => println!("{:#?}", m),
m => println!("\n{:#?}", m),
}
}
fn process_incoming_message<'a>(json: &'a str, builtin: &'a Constants) -> Option<Message<'a>> {
let message = match Message::parse_from_json_str(json) {
Ok(m) => {
display_message(">>> ", &m);
m
}
Err(err) => {
eprintln!("Error parsing message: {}", err);
return None;
}
};
match message {
Message::Alive { .. } => Some(Message::new_alive()),
Message::JoinResponse { result, .. } if result < 100 => {
eprintln!("Failed to JOIN: error code = {}", result);
None
}
Message::JoinResponse { .. } => Some(Message::RequestControllersList {
controller_id: None,
options: Default::default(),
}),
Message::LoginOperator { controller_id, password, .. } => match builtin.users.get(password)
{
Some((level, name)) => {
println!("User found: password=[{}], access level={}.", password, level);
Some(Message::OperatorInfo {
controller_id,
operator_id: Some((u32::from(*level) + 1).try_into().unwrap()),
name: name[..].try_into().unwrap(),
password: password.try_into().unwrap(),
level: *level,
options: Default::default(),
})
}
None => {
println!("No user found with password: [{}].", password);
Some(Message::OperatorInfo {
controller_id,
operator_id: None,
name: "Not Allowed".try_into().unwrap(),
password: password.try_into().unwrap(),
level: 0,
options: Default::default(),
})
}
},
Message::RequestJobCardsList { controller_id, .. } => Some(Message::JobCardsList {
controller_id,
data: builtin
.jobs
.iter()
.map(|jc| (jc.job_card_id().try_into().unwrap(), jc.clone()))
.collect(), options: Default::default(),
}),
_ => None,
}
}
fn send(client: &mut WebSocketClient, message: &OwnedMessage) -> WebSocketResult<()> {
match client.send_message(message) {
Ok(_) => match message {
OwnedMessage::Close(Some(data)) => {
println!("Closing WebSocket connection: ({}) {}", data.status_code, data.reason)
}
OwnedMessage::Close(None) => println!("Closing WebSocket connection..."),
OwnedMessage::Text(json) => println!("Sent [{}]: {}", json.len(), json),
OwnedMessage::Binary(data) => println!("Sent data: {} byte(s)", data.len()),
_ => (),
},
Err(err) => {
eprintln!("Error sending message: {}", err);
client.send_message(&websocket::Message::close())?;
println!("Closing WebSocket connection...");
}
}
Ok(())
}
fn run(mut client: WebSocketClient, builtin: &Constants) -> WebSocketResult<()> {
loop {
let message = match client.recv_message() {
Ok(msg) => msg,
Err(err) => {
eprintln!("Error receiving message: {}", err);
let data = CloseData::new(1, format!("Error receiving message: {}", err));
send(&mut client, &OwnedMessage::Close(Some(data)))?;
return Ok(());
}
};
match message {
OwnedMessage::Close(Some(data)) => {
println!("WebSocket closed: ({}) {}", data.status_code, data.reason);
return Ok(());
}
OwnedMessage::Close(None) => {
println!("WebSocket closed.");
return Ok(());
}
OwnedMessage::Ping(data) => send(&mut client, &OwnedMessage::Pong(data))?,
OwnedMessage::Text(json) => {
println!("Received [{}]: {}", json.len(), json);
if let Some(msg) = process_incoming_message(&json, &builtin) {
match msg.to_json_str() {
Ok(resp) => {
send(&mut client, &OwnedMessage::Text(resp))?;
display_message("<<< ", &msg);
}
Err(err) => eprintln!("Error serializing message: {}", err),
}
}
}
OwnedMessage::Binary(data) => println!("Received binary data: {} byte(s)", data.len()),
_ => println!("Received: {:#?}", message),
}
}
}
fn main() {
println!("iChen 4 Open Protocol Viewer");
println!();
print!("WebSocket URL (example: ws://x.x.x.x:port or wss://x.x.x.x:port): ");
std::io::stdout().flush().expect("Failed to flush stdout.");
let mut input = String::new();
stdin().read_line(&mut input).expect("Failed to read line from stdin.");
let conn = input.trim();
if conn.is_empty() {
eprintln!("URL cannot be empty.");
return;
} else if !conn.starts_with("ws://") && !conn.starts_with("wss://") {
eprintln!(
"Invalid WebSocket URL format. \
Should be: ws://x.x.x.x:port or wss://x.x.x.x:port"
);
return;
}
print!("Password: ");
std::io::stdout().flush().expect("Failed to flush stdout.");
let mut input = String::new();
stdin().read_line(&mut input).expect("Failed to read line from stdin.");
let password = input.trim();
if password.is_empty() {
eprintln!("Password cannot be empty.");
return;
}
println!("Connecting to iChen Server at {}...", conn);
let mut ws_builder = match ClientBuilder::new(conn) {
Ok(b) => b,
Err(err) => {
eprintln!("Invalid URL: {}", err);
return;
}
};
let mut client = match ws_builder.connect(None) {
Ok(c) => c,
Err(err) => {
eprintln!("Connect connect to server: {}", &err);
eprintln!("{}", err);
return;
}
};
println!("Connection to iChen Server established.");
let builtin = Constants {
users: [
"000000", "111111", "222222", "333333", "444444", "555555", "666666", "777777",
"888888", "999999", "123456",
]
.iter()
.enumerate()
.map(|(i, &v)| (v, (i as u8, format!("MISUser{}", i))))
.collect(),
jobs: vec![
JobCard::try_new("JOB_CARD_1", "ABC-123", 0, 8000).unwrap(),
JobCard::try_new("JOB_CARD_2", "M002", 2000, 10000).unwrap(),
JobCard::try_new("JOB_CARD_3", "MOULD_003", 888, 3333).unwrap(),
JobCard::try_new("JOB_CARD_4", "MOULD_004", 123, 45678).unwrap(),
],
};
println!("=================================================");
println!("Built-in Users for Testing:");
builtin.users.iter().for_each(|(user, (level, name))| {
println!("> Name={}, Password={}, Level={}", name, user, level)
});
println!("=================================================");
println!("Built-in Job Cards for Testing:");
builtin.jobs.iter().for_each(|j| {
println!(
"> Name={}, Mold={}, Quantity={}/{}",
j.job_card_id(),
j.mold_id(),
j.progress(),
j.total()
)
});
println!("=================================================");
println!("Sending JOIN message...");
let msg = Message::new_join(password, Filters::All + Filters::JobCards + Filters::Operators);
match msg.to_json_str() {
Ok(m) => {
if let Err(err) = send(&mut client, &OwnedMessage::Text(m)) {
eprintln!("Error when sending JOIN message: {}", err);
}
}
Err(err) => eprintln!("Error in JOIN message: {}", err),
}
println!("Process loop started...");
match run(client, &builtin) {
Ok(_) => println!("Process loop stopped."),
Err(err) => eprintln!("Error in process loop: {}", err),
}
println!("Program terminated.");
}