use yew::prelude::*;
use yew::agent::Bridge;
use crate::coapws;
use crate::binentry;
use crate::message_editor;
use coap_message::heapmessage::HeapMessage;
use crate::coapwsmessage;
use coap_message::{MinimalWritableMessage, ReadableMessage, MessageOption};
use coapwsmessage::{CoAPWSMessageR, CoAPWSMessageW};
pub struct CoAPWSMessageExchanger {
connection: Box<dyn Bridge<coapws::Connection>>,
events: Vec<Event>,
link: ComponentLink<Self>,
out_next_token: Vec<u8>,
out_message: HeapMessage,
out_message_editorstate: message_editor::EditorState,
server_uri: String,
}
enum Event {
Text(String),
Received(CoAPWSMessageR<Box<[u8]>>),
}
fn format_message<M>(
f: &mut std::fmt::Formatter,
m: &M)
-> Result<(), std::fmt::Error>
where
M: ReadableMessage
{
coap_numbers::code::format_dotted(m.code().into(), f)?;
if let Some(s) =coap_numbers::code::to_name(m.code().into()) {
write!(f, " ({})", s)?;
}
let option_to_name = |optnum: u16| {
match coap_numbers::code::classify(m.code().into()) {
coap_numbers::code::Range::Signaling => coap_numbers::signaling_option::to_name(m.code().into(), optnum.into()),
_ => coap_numbers::option::to_name(optnum.into()),
}
};
for o in m.options() {
write!(f, "; {} ({}) {:?}", o.number(), option_to_name(o.number()).unwrap_or("unknown"), o.value())?;
}
match std::str::from_utf8(m.payload()) {
Ok("") => {}
Ok(x) => {
write!(f, "; Payload \"{}\"", x)?;
}
Err(_) => {
write!(f, "; Payload {:?}", m.payload())?;
}
}
Ok(())
}
impl std::fmt::Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Event::Text(s) => f.write_str(&s),
Event::Received(m) => {
write!(f, "Received on {}: ",
match m.token() {
&[] => "empty token".to_string(),
t => hex::encode(t),
}
)?;
format_message(f, m)?;
Ok(())
}
}
}
}
fn build_getwkc() -> HeapMessage {
use coap_numbers::option::*;
use coap_numbers::code::*;
let mut msg = HeapMessage::new();
msg.set_code(GET);
msg.add_option(URI_PATH, b".well-known");
msg.add_option(URI_PATH, b"core");
msg
}
fn build_observetime() -> HeapMessage {
use coap_numbers::option::*;
use coap_numbers::code::*;
let mut msg = HeapMessage::new();
msg.set_code(GET);
msg.add_option(OBSERVE, b"");
msg.add_option(URI_PATH, b"time");
msg
}
fn build_proxyget() -> HeapMessage {
use coap_numbers::option::*;
use coap_numbers::code::*;
let mut msg = HeapMessage::new();
msg.set_code(GET);
msg.add_option(URI_HOST, b"coap.me");
msg.add_option(URI_PATH, b".well-known");
msg.add_option(URI_PATH, b"core");
msg.add_option(PROXY_SCHEME, b"coap");
msg.add_option(BLOCK2, &[5]);
msg
}
static DEFAULT_SERVER: &'static str = "ws://localhost:8683/.well-known/coap";
impl Component for CoAPWSMessageExchanger {
type Message = ExchangerMsg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
let mut events = Vec::new();
events.push(Event::Text("Startup...".to_string()));
let out_next_token = vec![0, 0, 0, 1];
let mut connection = coapws::Connection::bridge(link.callback(|o| ExchangerMsg::Socket(o)));
connection.send(coapws::Input::Initialize { uri: DEFAULT_SERVER.to_string() });
Self { connection, events, out_message: build_getwkc(), out_next_token, link, out_message_editorstate: Default::default(), server_uri: DEFAULT_SERVER.to_string() }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
ExchangerMsg::Socket(coapws::Output::Connected) => {
self.events.push(Event::Text("Ready to send requests".to_string()));
}
ExchangerMsg::Socket(coapws::Output::Message(m)) | ExchangerMsg::Socket(coapws::Output::SignalingInfo(m)) => {
self.events.push(Event::Received(m));
}
ExchangerMsg::Socket(m) => {
self.events.push(Event::Text(format!("Non-message event received: {:?}", m)));
}
ExchangerMsg::Edit(e) => {
message_editor::apply(e, &mut self.out_message, &mut self.out_message_editorstate);
}
ExchangerMsg::ChangeToken(v) => {
self.out_next_token = v;
}
ExchangerMsg::Submit => {
let mut serialized = CoAPWSMessageW::new(&self.out_next_token);
serialized.set_from_message(&self.out_message);
self.connection.send(coapws::Input::Message(serialized));
let new_token_tail = self.out_next_token
.pop()
.unwrap_or(0)
.wrapping_add(1);
self.out_next_token.push(new_token_tail);
}
ExchangerMsg::ChangeToPreset(f) => {
self.out_message = f();
}
ExchangerMsg::SetServer(u) => {
self.server_uri = u;
}
ExchangerMsg::Reconnect => {
let mut connection = coapws::Connection::bridge(self.link.callback(|o| ExchangerMsg::Socket(o)));
connection.send(coapws::Input::Initialize { uri: self.server_uri.clone() });
self.connection = connection
}
}
true
}
fn change(&mut self, _: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
html! { <>
<h1>{"CoAP over WebSockets: Message test tool"}</h1>
<section>
<h2> { "Server" } </h2>
<p><label> { "Server URI:" } <input
type="text"
onchange=self.link.callback(move |v| ExchangerMsg::SetServer(match v {
yew::events::ChangeData::Value(u) => u,
_ => unreachable!("Text input has value"),
}))
value=self.server_uri
/></label></p>
<p><button
onclick=self.link.callback(|_| ExchangerMsg::Reconnect)
> { "(Re)connect" } </button></p>
</section>
<section>
<h2> {"Send messages"} </h2>
<p> { "Presets:" }
<button onclick=self.link.callback(|_| ExchangerMsg::ChangeToPreset(build_getwkc))> {"Get /.well-known/core"} </button>
<button onclick=self.link.callback(|_| ExchangerMsg::ChangeToPreset(build_observetime))> {"Observe /time"} </button>
<button onclick=self.link.callback(|_| ExchangerMsg::ChangeToPreset(build_proxyget))> {"Proxy for coap.me"} </button>
</p>
<p><label> {"Token:"}
<binentry::BinaryEntry
onchange=self.link.callback(|v| ExchangerMsg::ChangeToken(v))
value=self.out_next_token.clone()
/></label>
</p>
{ message_editor::render(
&self.out_message,
&self.out_message_editorstate,
&self.link,
|x| ExchangerMsg::Edit(x),
)}
<button onclick=self.link.callback(|_| ExchangerMsg::Submit)> { "submit" } </button>
</section>
<section>
<h2> { "Messages received" } </h2>
<ul>
{ for self.events.iter().map(|t| html!(<li>{t}</li>)) }
</ul>
</section>
</>
}
}
}
#[derive(Debug)]
pub enum ExchangerMsg {
Socket(coapws::Output),
Edit(message_editor::EditMsg),
ChangeToken(Vec<u8>),
Submit,
ChangeToPreset(fn() -> HeapMessage),
SetServer(String),
Reconnect,
}