Skip to content

gopcua in the browser

Mirco Zeiss edited this page Sep 19, 2018 · 3 revisions

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