Closed zemirco closed 3 years ago
Hi Mirco, you won't be able to compile OPC UA to web assembly. It has too many external dependencies including on OpenSSL and uses features like multithreading, sockets etc.
If you want to run OPC UA to a web browser then you need to do something like my samples/web-client. This is a webserver with a websocket which is also a compiled OPC UA client that can connect to an OPC UA server. So the logic of OPC is in the webserver itself. The webclient is a HTML, Javascript that communicates to the server with a websocket.
I suspect it might be possible with some effort to make the opcua-types crate to web assembly so that in theory the serializable types could be sent over a websocket but I haven't attempted to try it.
Hey,
thank you for your answer. I wasn't clear enough, sorry about that. I read through my question and you're right, it was hard to understand what I really want.
I'd like to use ONLY the types, no SSL, no multithreading and no sockets. I know that it's not doable in wasm. I'd like to initiate the WebSocket connection in the JS world. So JS is responsible for creating the socket and also handling security.
I just want to create messages and receive messages. I somehow have to serialize and deserialize the binary packets coming in / going out over the wire. OPC UA has so many types and I don't want to create all of them in JavaScript / TypeScript.
I wrote a huge part of the Go OPC UA implementation https://github.com/gopcua/opcua and did the same. I compiled it to wasm and used it to unpack binary messages. However the wasm is quite large since the whole Go runtime is included. wasm is a first class citizen for Rust and the wasm files are much smaller. That's why I'm asking.
As I said I'm currently stuck at the UAString
. It has a string inside and therefore cannot simply implement the copy trait.
Any ideas?
Okay I understand now. I'm typing this on a mobile so apologies for typos. In Rust you can only implement Copy trait on intrinsic types or things composed from intrinsic types. Anything that involves memory allocation has to implement the Clone trait and an explicit .clone() function. Since a String is allocated that includes that type. I'll see if I can find how you might bind to it from wasm
I'm learning wasm-bindgen as I go but from the looks of it UAString would have to implement the wasm_bindgen::convert::IntoWasmAbi to know how to marshal the type into JavaScript. It seems odd that it can't do this automatically from that tag although it may be possible to implement the trait manually. I can't say for sure if it would be easy
I'm still learning about wasm and JS but the issue appears to be that JS and wasm do not share their address / object space so tools like wasm-bindgen do their best to create wrappers that marshal types back and forth for either side to see.
https://rustwasm.github.io/book/reference/js-ffi.html
So this problem with strings seems to be inherent to wasm / js, that because the string is owned by Rust (wasm), it is not easy to make a copy on the JS side and keep them in sync.
If I were you I would ask the wasm-bindgen project if there is something that can be done to overcome this issue.
In the meantime, the problem boils down marshalling a type across the boundary between JS and wasm. Internal to wasm you should be clear to use the type as-is. So perhaps instead of trying to get the type out you should use JSON on the JS side and your boundary function should deserialize the JSON string into the appropriate struct using serde.
At present I only implement serde's Serialize / Deserialize traits on Variant and types that a Variant can contain, but it might prove the concept.
If you look for json_serde::from_str and json_serde::to_string you should see how a type can be turned to and from a string. I use these calls in some unit tests and in the web-client sample.
Thank you for digging into this. That's a great feedback although we didn't really find a solution. However I will start looking into your ideas.
I've opened a question on wasm-bindgen to see if they have any general help on the Option
In the meantime I've also moved some code out of types into core to cut down the dependencies a little bit.
Unfortunately the issue was closed without a lot of information. Maybe we can work on this together. I've taken your example and tried to add the setters and getters as described in the other issue.
pub struct Test {
pub value: Option<String>
}
#[wasm_bindgen]
impl Test {
#[wasm_bindgen(getter)]
pub fn value(&self) -> String {
if self.value.is_some() {
return self.value.unwrap()
}
return "".to_string()
}
#[wasm_bindgen(setter)]
pub fn set_field(&mut self, value: String) {
self.value = Some(value);
// self.value = value;
}
}
Now I'm getting different errors.
error[E0277]: the trait bound `string::Test: string::wasm_bindgen::convert::RefFromWasmAbi` is not satisfied
--> types/src/string.rs:32:1
|
32 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ the trait `string::wasm_bindgen::convert::RefFromWasmAbi` is not implemented for `string::Test`
error[E0277]: the trait bound `string::Test: string::wasm_bindgen::convert::RefMutFromWasmAbi` is not satisfied
--> types/src/string.rs:32:1
|
32 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ the trait `string::wasm_bindgen::convert::RefMutFromWasmAbi` is not implemented for `string::Test`
error: aborting due to 2 previous errors
Just wanted to let you know what I'm currently trying. I've got meetings now but will hopefully work on this later.
This seems to work:
#[wasm_bindgen]
pub struct TestString {
value: Option<String>
}
#[wasm_bindgen]
impl TestString {
pub fn new(value: &str) -> TestString {
TestString { value: Some(value.into()) }
}
pub fn value(&self) -> Option<String> {
self.value.clone()
}
pub fn set_value(&mut self, value: Option<String>) {
self.value = value;
}
}
So value is not public any more and there are setter/getters on the impl to get the value (via a clone()) or set it.
By "work" I mean it generates pkg/wasm_types.js with a TestString struct in it which I could presumably say from JS - console.log(TestString.new("Hello World").value())
and get an object of that type. I haven't tested it. It's worth looking at what it does generate though because it looks like it basically generates wrappers around Rust types to marshal things in and out of JS, which is why there are all these rules and conditions.
I have modified the real UAString to make the internal value non-public however my getter currently returns a reference, which wasm-pack doesn't like - it doesn't like borrowed values being returned from functions.
For the rest of the structs in opcua-types you may be compelled to do something similar. Some structs contain Vec<> fields so they might need a similar getter and setter. I'd suggest forking opcua and maybe playing with `tools/schema/gen_types.js" because that's the script I use to machine generate most of the files. Maybe you could modify it to add getters and setters for all the fields and make the fields themselves private.
Closing since it's unlikely to compile to wasm any time soon
Hey,
thank you for creating this library. :100:
I'm trying to use it inside the browser via WebAssembly. The basic idea is to have a WebSocket connection to a server, use the binary protocol for communication and handle serialization/deserialization of messages inside wasm. Within my JavaScript application I can use the wrapper which was automatically created by
wasm-pack
.Here is a short demo with the code from one of the tests. It simply converts some bytes into a
UAString
.Compile this code (for Node.js so we don't have to set up a server and start the browser).
Call our
convert
function.This returns
水Boy
as expected. That's already great :+1:Now I'm trying to use
LocalizedText
and I've got several problems. I'm pretty good at JS but not so good with Rust. Maybe you've got an idea.I tried to add
#[wasm_bindgen]
toLocalizedText
. This gives the error messageSo I also added
#[wasm_bindgen]
toUAString
. This gives the error messageSo I tried to add
Copy
toUAString
.And finally the compiles tells me that
So I tried to find a solution for this and found What Is Ownership?. To me it looks like strings do not have a copy method and now I'm stuck.
Any hint you could give me? Thank you very much!