Skip to content

Commit

Permalink
Merge pull request #737 from gopcua/server
Browse files Browse the repository at this point in the history
Experimental Server Branch
  • Loading branch information
magiconair authored Dec 5, 2024
2 parents 2c61e33 + 8be5d52 commit 7c3c4fb
Show file tree
Hide file tree
Showing 80 changed files with 86,043 additions and 320 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
go: ["1.20.x", "1.21.x"]
go: ["1.22.x", "1.23.x"]

steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup Go ${{ matrix.go }}
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
id: go
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ build/
dist/
__pycache__/
.vscode/
*.pem
*.crt
*.csr
*.exe
.idea/
__debug*
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# go test -count=1 disables the test cache so that all tests are run every time.

all: test integration examples
all: test integration selfintegration examples

test:
go test -count=1 -race ./...
Expand All @@ -9,22 +9,26 @@ lint:
staticcheck ./...

integration:
go test -count=1 -race -v -tags=integration ./uatest/...
go test -count=1 -race -v -tags=integration ./tests/python...

selfintegration:
go test -count=1 -race -v -tags=integration ./tests/go...

examples:
go build -o build/ ./examples/...

test-race:
go test -count=1 -race ./...
go test -count=1 -race -v -tags=integration ./uatest/...
go test -count=1 -race -v -tags=integration ./tests/python...
go test -count=1 -race -v -tags=integration ./tests/go...

install-py-opcua:
pip3 install opcua

gen:
go install golang.org/x/tools/cmd/stringer@latest
which stringer || go install golang.org/x/tools/cmd/stringer@latest
find . -name '*_gen.go' -delete
go generate ./...
go mod tidy

release:
GITHUB_TOKEN=$$(security find-generic-password -gs GITHUB_TOKEN -w) goreleaser --clean
Expand Down
111 changes: 69 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ We would be happy if you can add your equipment to the list. Just open a PR :)
| OMRON NX102-9020 |v0.3.x | production | [IOTech Systems](https://www.iotechsys.com/) |
| InfluxDB Telegraf plugin | v0.3.x | ? | Community |

## Supported Features
## Supported Client Features

The current focus is on the OPC UA Binary protocol over TCP. No other protocols are supported at this point.

Expand All @@ -191,51 +191,78 @@ The current focus is on the OPC UA Binary protocol over TCP. No other protocols
| | User Name Password | Yes | |
| | X509 Certificate | Yes | |

## Supported Server Features

The current focus is on the OPC UA Binary protocol over TCP. No other protocols are supported at this point.

| Categories | Features | Supported | Notes |
|----------------|----------------------------------|-----------|-------------|
| Encoding | OPC UA Binary | Yes | |
| | OPC UA JSON | | not planned |
| | OPC UA XML | | not planned |
| Transport | UA-TCP UA-SC UA Binary | Yes | |
| | OPC UA HTTPS | | not planned |
| | SOAP-HTTP WS-SC UA Binary | | not planned |
| | SOAP-HTTP WS-SC UA XML | | not planned |
| | SOAP-HTTP WS-SC UA XML-UA Binary | | not planned |
| Encryption | None | Yes | |
| | Basic128Rsa15 | Untested | |
| | Basic256 | Untested | |
| | Basic256Sha256 | Untested | |
| Authentication | Anonymous | Yes | |
| | User Name Password | Untested | |
| | X509 Certificate | Untested | |


### Services

Here is the current set of supported services. For low-level access use the client `Send` function directly.

| Service Set | Service | Client | Notes |
|-----------------------------|-------------------------------|--------|--------------|
| Discovery Service Set | FindServers | Yes | |
| | FindServersOnNetwork | Yes | |
| | GetEndpoints | Yes | |
| | RegisterServer | | |
| | RegisterServer2 | | |
| Secure Channel Service Set | OpenSecureChannel | Yes | |
| | CloseSecureChannel | Yes | |
| Session Service Set | CreateSession | Yes | |
| | CloseSession | Yes | |
| | ActivateSession | Yes | |
| | Cancel | | |
| Node Management Service Set | AddNodes | | |
| | AddReferences | | |
| | DeleteNodes | | |
| | DeleteReferences | | |
| View Service Set | Browse | Yes | |
| | BrowseNext | Yes | |
| | TranslateBrowsePathsToNodeIds | | |
| | RegisterNodes | Yes | |
| | UnregisterNodes | Yes | |
| Query Service Set | QueryFirst | | |
| | QueryNext | | |
| Attribute Service Set | Read | Yes | |
| | Write | Yes | |
| | HistoryRead | Yes | |
| | HistoryUpdate | | |
| Method Service Set | Call | Yes | |
| MonitoredItems Service Set | CreateMonitoredItems | Yes | |
| | DeleteMonitoredItems | Yes | |
| | ModifyMonitoredItems | Yes | |
| | SetMonitoringMode | Yes | |
| | SetTriggering | | |
| Subscription Service Set | CreateSubscription | Yes | |
| | ModifySubscription | | |
| | SetPublishingMode | | |
| | Publish | Yes | |
| | Republish | | |
| | DeleteSubscriptions | Yes | |
| | TransferSubscriptions | | |

| Service Set | Service | Client | Server | Notes |
|-----------------------------|-------------------------------|--------|--------|--------------|
| Discovery Service Set | FindServers | Yes | | |
| | FindServersOnNetwork | Yes | | |
| | GetEndpoints | Yes | | |
| | RegisterServer | | | |
| | RegisterServer2 | | | |
| Secure Channel Service Set | OpenSecureChannel | Yes | Yes* | |
| | CloseSecureChannel | Yes | Yes* | |
| Session Service Set | CreateSession | Yes | Yes | |
| | CloseSession | Yes | Yes | |
| | ActivateSession | Yes | Yes | |
| | Cancel | | | |
| Node Management Service Set | AddNodes | | | |
| | AddReferences | | | |
| | DeleteNodes | | | |
| | DeleteReferences | | | |
| View Service Set | Browse | Yes | Yes | |
| | BrowseNext | Yes | | |
| | TranslateBrowsePathsToNodeIds | | | |
| | RegisterNodes | Yes | | |
| | UnregisterNodes | Yes | | |
| Query Service Set | QueryFirst | | | |
| | QueryNext | | | |
| Attribute Service Set | Read | Yes | Yes | |
| | Write | Yes | Yes | |
| | HistoryRead | Yes | | |
| | HistoryUpdate | | | |
| Method Service Set | Call | Yes | | |
| MonitoredItems Service Set | CreateMonitoredItems | Yes | Yes | |
| | DeleteMonitoredItems | Yes | Yes | |
| | ModifyMonitoredItems | Yes | Yes | |
| | SetMonitoringMode | Yes | Yes | |
| | SetTriggering | | | |
| Subscription Service Set | CreateSubscription | Yes | Yes | |
| | ModifySubscription | | | |
| | SetPublishingMode | | | |
| | Publish | Yes | Yes | |
| | Republish | | | |
| | DeleteSubscriptions | Yes | Yes | |
| | TransferSubscriptions | | | |

* not all encryption schemes are fully functional at this time


## Authors

Expand Down
38 changes: 19 additions & 19 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ func (c *Client) CreateSession(ctx context.Context, cfg *uasc.SessionConfig) (*S
var s *Session
// for the CreateSessionRequest the authToken is always nil.
// use c.SecureChannel().SendRequest() to enforce this.
err := c.SecureChannel().SendRequest(ctx, req, nil, func(v interface{}) error {
err := c.SecureChannel().SendRequest(ctx, req, nil, func(v ua.Response) error {
var res *ua.CreateSessionResponse
if err := safeAssign(v, &res); err != nil {
return err
Expand Down Expand Up @@ -876,7 +876,7 @@ func (c *Client) ActivateSession(ctx context.Context, s *Session) error {
UserIdentityToken: ua.NewExtensionObject(s.cfg.UserIdentityToken),
UserTokenSignature: s.cfg.UserTokenSignature,
}
return c.SecureChannel().SendRequest(ctx, req, s.resp.AuthenticationToken, func(v interface{}) error {
return c.SecureChannel().SendRequest(ctx, req, s.resp.AuthenticationToken, func(v ua.Response) error {
var res *ua.ActivateSessionResponse
if err := safeAssign(v, &res); err != nil {
return err
Expand Down Expand Up @@ -918,7 +918,7 @@ func (c *Client) closeSession(ctx context.Context, s *Session) error {
}
req := &ua.CloseSessionRequest{DeleteSubscriptions: true}
var res *ua.CloseSessionResponse
return c.Send(ctx, req, func(v interface{}) error {
return c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
}
Expand All @@ -936,7 +936,7 @@ func (c *Client) DetachSession(ctx context.Context) (*Session, error) {
// Send sends the request via the secure channel and registers a handler for
// the response. If the client has an active session it injects the
// authentication token.
func (c *Client) Send(ctx context.Context, req ua.Request, h func(interface{}) error) error {
func (c *Client) Send(ctx context.Context, req ua.Request, h func(ua.Response) error) error {
stats.Client().Add("Send", 1)

err := c.sendWithTimeout(ctx, req, c.cfg.sechan.RequestTimeout, h)
Expand All @@ -948,7 +948,7 @@ func (c *Client) Send(ctx context.Context, req ua.Request, h func(interface{}) e
// sendWithTimeout sends the request via the secure channel with a custom timeout and registers a handler for
// the response. If the client has an active session it injects the
// authentication token.
func (c *Client) sendWithTimeout(ctx context.Context, req ua.Request, timeout time.Duration, h func(interface{}) error) error {
func (c *Client) sendWithTimeout(ctx context.Context, req ua.Request, timeout time.Duration, h uasc.ResponseHandler) error {
sc := c.SecureChannel()
if sc == nil {
return ua.StatusBadServerNotConnected
Expand Down Expand Up @@ -981,7 +981,7 @@ func (c *Client) FindServers(ctx context.Context) (*ua.FindServersResponse, erro
EndpointURL: c.endpointURL,
}
var res *ua.FindServersResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -993,7 +993,7 @@ func (c *Client) FindServersOnNetwork(ctx context.Context) (*ua.FindServersOnNet

req := &ua.FindServersOnNetworkRequest{}
var res *ua.FindServersOnNetworkResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1007,7 +1007,7 @@ func (c *Client) GetEndpoints(ctx context.Context) (*ua.GetEndpointsResponse, er
EndpointURL: c.endpointURL,
}
var res *ua.GetEndpointsResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand Down Expand Up @@ -1046,7 +1046,7 @@ func (c *Client) Read(ctx context.Context, req *ua.ReadRequest) (*ua.ReadRespons
req = cloneReadRequest(req)

var res *ua.ReadResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
err := safeAssign(v, &res)
if err != nil {
return err
Expand Down Expand Up @@ -1077,7 +1077,7 @@ func (c *Client) Write(ctx context.Context, req *ua.WriteRequest) (*ua.WriteResp
stats.Client().Add("NodesToWrite", int64(len(req.NodesToWrite)))

var res *ua.WriteResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand Down Expand Up @@ -1117,7 +1117,7 @@ func (c *Client) Browse(ctx context.Context, req *ua.BrowseRequest) (*ua.BrowseR
req = cloneBrowseRequest(req)

var res *ua.BrowseResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1131,7 +1131,7 @@ func (c *Client) Call(ctx context.Context, req *ua.CallMethodRequest) (*ua.CallM
MethodsToCall: []*ua.CallMethodRequest{req},
}
var res *ua.CallResponse
err := c.Send(ctx, creq, func(v interface{}) error {
err := c.Send(ctx, creq, func(v ua.Response) error {
return safeAssign(v, &res)
})
if err != nil {
Expand All @@ -1148,7 +1148,7 @@ func (c *Client) BrowseNext(ctx context.Context, req *ua.BrowseNextRequest) (*ua
stats.Client().Add("BrowseNext", 1)

var res *ua.BrowseNextResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1162,7 +1162,7 @@ func (c *Client) RegisterNodes(ctx context.Context, req *ua.RegisterNodesRequest
stats.Client().Add("NodesToRegister", int64(len(req.NodesToRegister)))

var res *ua.RegisterNodesResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1176,7 +1176,7 @@ func (c *Client) UnregisterNodes(ctx context.Context, req *ua.UnregisterNodesReq
stats.Client().Add("NodesToUnregister", int64(len(req.NodesToUnregister)))

var res *ua.UnregisterNodesResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1199,7 +1199,7 @@ func (c *Client) HistoryReadEvent(ctx context.Context, nodes []*ua.HistoryReadVa
}

var res *ua.HistoryReadResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1222,7 +1222,7 @@ func (c *Client) HistoryReadRawModified(ctx context.Context, nodes []*ua.History
}

var res *ua.HistoryReadResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1245,7 +1245,7 @@ func (c *Client) HistoryReadProcessed(ctx context.Context, nodes []*ua.HistoryRe
}

var res *ua.HistoryReadResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand All @@ -1268,7 +1268,7 @@ func (c *Client) HistoryReadAtTime(ctx context.Context, nodes []*ua.HistoryReadV
}

var res *ua.HistoryReadResponse
err := c.Send(ctx, req, func(v interface{}) error {
err := c.Send(ctx, req, func(v ua.Response) error {
return safeAssign(v, &res)
})
return res, err
Expand Down
Loading

0 comments on commit 7c3c4fb

Please sign in to comment.