Skip to content

gopcua in the browser

Frank Schröder edited this page Apr 9, 2019 · 3 revisions

Note

This code is based on an older version of the library and will not work as described. We will leave it as a reference for someone who is interested. Feel free to update it to the current state of the library.


With the latest Go 1.11 you're able to compile Go code to WebAssembly. OPC UA is able to communicate via websockets. So why not use gopcua in the browser to decode/encode binary OPC UA messages directly in the browser?

Here is a very rough demo with a hard coded float value. Much of the code is taken from the Go WebAssembly wiki.

  1. You need a server.go to serve some static files from disk.
package main

import (
	"flag"
	"log"
	"net/http"
)

var (
	listen = flag.String("listen", ":7070", "listen address")
	dir    = flag.String("dir", ".", "directory to serve")
)

func main() {
	flag.Parse()
	log.Printf("listening on %q...", *listen)
	log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir))))
}
  1. You need an index.html file to load wasm_exec.js and instantiate the WebAssembly code.
<!doctype html>
<html>

  <head>
    <meta charset="utf-8">
    <title>Go wasm</title>
  </head>

  <body>
    <script src="wasm_exec.js"></script>
    <script>
      const go = new Go()

      const awesome = () => {
        // same data as in datatypes/float_test.go
        const data = [0x64, 0x06, 0xa0, 0x40]
        // decode is a global function created by main.go / lib.wasm
        decode(data)
      }

      WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(async result => {
        await go.run(result.instance)
      }).catch((err) => {
        console.error(err)
      })

    </script>
    <button onClick="awesome()" id="addButton">Add</button>
  </body>

</html>
  1. Your Go code in main.go looks like this. This is the code that will be compiled to WebAssembly and run in the browser.
package main

import (
	"fmt"
	"log"
	"syscall/js"

	"github.com/wmnsk/gopcua/datatypes"
)

func decode(args []js.Value) {
	data := args[0]
	// convert incoming values into byte slice
	b := make([]byte, data.Length())
	for index := 0; index < data.Length(); index++ {
		b[index] = byte(data.Index(index).Int())
	}
	// decode byte slice
	s, err := datatypes.DecodeFloat(b)
	if err != nil {
		log.Println(err)
	}
        // prints 5.00078 as expected \o/
	log.Println(s.Value)
}

func main() {
	c := make(chan struct{}, 0)

        // just to make sure everything is running
	fmt.Println("hello world")

	js.Global().Set("decode", js.NewCallback(decode))
	<-c
}
  1. Compile main.go to lib.wasm. I've created a little Makefile to make this easier.
.PHONY: wasm
wasm:
	GOARCH=wasm GOOS=js go build -o lib.wasm main.go

Now go to http://localhost:7070 and click the button. You should see the result in the console.

Clone this wiki locally