Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/browse/browse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ func TestBrowse(t *testing.T) {

// start the server
s := server.New(
ctx,
server.EndPoint("localhost", 4840),
)
populateServer(s)
if err := s.Start(ctx); err != nil {
t.Fatal(err)
}
defer s.Close()
defer s.Close(ctx)

// prepare the client
c, err := opcua.NewClient("opc.tcp://localhost:4840")
Expand Down
94 changes: 43 additions & 51 deletions examples/server/NodeSet2_server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
"crypto/rsa"
"crypto/tls"
"encoding/xml"
"errors"
"flag"
"io"
"log"
"log/slog"
"os"
"os/signal"
"time"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/gopcua/opcua/schema"
"github.com/gopcua/opcua/server"
"github.com/gopcua/opcua/ua"
"github.com/gopcua/opcua/ualog"
)

var (
Expand All @@ -30,33 +32,18 @@ var (
gencert = flag.Bool("gen-cert", false, "Generate a new certificate")
)

type Logger int

func (l Logger) Debug(msg string, args ...any) {
if l < 0 {
log.Printf(msg, args...)
}
}
func (l Logger) Info(msg string, args ...any) {
if l < 1 {
log.Printf(msg, args...)
}
}
func (l Logger) Warn(msg string, args ...any) {
if l < 2 {
log.Printf(msg, args...)
}
}
func (l Logger) Error(msg string, args ...any) {
if l < 3 {
log.Printf(msg, args...)
}
}

func main() {
flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging")
flag.Parse()
log.SetFlags(0)

ctx := ualog.New(context.Background(), ualog.WithHandler(
slog.NewJSONHandler(os.Stdout, func() *slog.HandlerOptions {
if debug.Enable {
return &slog.HandlerOptions{Level: slog.LevelDebug}
}
return nil
}()),
))

var opts []server.Option

Expand Down Expand Up @@ -96,7 +83,7 @@ func main() {
// be sure the hostname(s) also match the certificate the server is going to use.
hostname, err := os.Hostname()
if err != nil {
log.Fatalf("Error getting host name %v", err)
fatal(ctx, "error getting host name", err)
}

opts = append(opts,
Expand All @@ -105,14 +92,6 @@ func main() {
server.EndPoint(hostname, *port),
)

// the server.SetLogger takes a server.Logger interface. This interface is met by
// the slog.Logger{}. A simple wrapper could be made for other loggers if they don't already
// meet the interface.
logger := Logger(2)
opts = append(opts,
server.SetLogger(logger),
)

// Here is an example of certificate generation. This is not necessary if you already have a certificate.
if *gencert {
// it is important that the certificate is generated with the correct hostname/IP address URIs
Expand All @@ -125,29 +104,32 @@ func main() {

c, k, err := GenerateCert(endpoints, 4096, time.Minute*60*24*365*10)
if err != nil {
log.Fatalf("problem creating cert: %v", err)
fatal(ctx, "problem creating certificate", err)
}
err = os.WriteFile(*certfile, c, 0)
if err != nil {
log.Fatalf("problem writing cert: %v", err)
fatal(ctx, "problem writing certificate", err)
}
err = os.WriteFile(*keyfile, k, 0)
if err != nil {
log.Fatalf("problem writing key: %v", err)
fatal(ctx, "problem writing key", err)
}

}

var cert []byte
if *gencert || (*certfile != "" && *keyfile != "") {
log.Printf("Loading cert/key from %s/%s", *certfile, *keyfile)
ualog.Info(ctx, "loading certificate and key from files",
ualog.String("cert", *certfile),
ualog.String("key", *keyfile),
)

c, err := tls.LoadX509KeyPair(*certfile, *keyfile)
if err != nil {
log.Printf("Failed to load certificate: %s", err)
ualog.Error(ctx, "failed to load certificate", ualog.Err(err))
} else {
pk, ok := c.PrivateKey.(*rsa.PrivateKey)
if !ok {
log.Fatalf("Invalid private key")
fatal(ctx, "invalid private key", errors.New("incorrect type"))
}
cert = c.Certificate[0]
opts = append(opts, server.PrivateKey(pk), server.Certificate(cert))
Expand All @@ -157,7 +139,7 @@ func main() {
// Now that all the options are set, create the server.
// When the server is created, it will automatically create namespace 0 and populate it with
// the core opc ua nodes.
s := server.New(opts...)
s := server.New(ctx, opts...)

// Now we'll import our NodeSet2.xml file.
// These files often create additional namespaces and reference them assuming they
Expand All @@ -167,35 +149,45 @@ func main() {
// first, we read the file and unmarshal it into a schema.UANodeSet struct. Then it can be imported
file, err := os.Open("Opc.Ua.Di.NodeSet2.xml")
if err != nil {
log.Fatalf("error opening nodeset file: %v", err)
fatal(ctx, "unable to open nodeset file", err)
}

node_data, err := io.ReadAll(file)
if err != nil {
log.Fatalf("error reading nodeset file: %v", err)
fatal(ctx, "unable to read nodeset file", err)
}

var nodes schema.UANodeSet
xml.Unmarshal(node_data, &nodes)
s.ImportNodeSet(&nodes)
s.ImportNodeSet(ctx, &nodes)

// At this point you can lookup any specific node by its nodeid to add references or modify it or whatever
// your heart desires
node := s.Node(ua.NewNumericNodeID(1, 15044))
if node != nil {
log.Printf("Found node %v", node)
ualog.Info(ctx, "found node", ualog.Any("node", node))
}

// Start the server
if err := s.Start(context.Background()); err != nil {
log.Fatalf("Error starting server, exiting: %s", err)
if err := s.Start(ctx); err != nil {
fatal(ctx, "unable to start server", err)
}
defer s.Close()
defer s.Close(ctx)

// catch ctrl-c and gracefully shutdown the server.
sigch := make(chan os.Signal, 1)
signal.Notify(sigch, os.Interrupt)
defer signal.Stop(sigch)
log.Printf("Press CTRL-C to exit")

ualog.Info(ctx, "press ctrl-c to exit")

<-sigch
log.Printf("Shutting down the server...")

ualog.Info(ctx, "shutting down server...")
}

func fatal(ctx context.Context, reason string, err error) {
ualog.Error(ctx, "FATAL: "+reason, ualog.Err(err))
time.Sleep(time.Second)
os.Exit(1)
}
Loading