diff --git a/client.go b/client.go index dc18cef6..58013e1c 100644 --- a/client.go +++ b/client.go @@ -35,7 +35,7 @@ func GetEndpoints(ctx context.Context, endpoint string, opts ...Option) ([]*ua.E return nil, err } defer c.Close() - res, err := c.GetEndpoints() + res, err := c.GetEndpointsWithContext(ctx) if err != nil { return nil, err } @@ -193,7 +193,7 @@ func (c *Client) Connect(ctx context.Context) (err error) { return err } - s, err := c.CreateSession(c.cfg.session) + s, err := c.CreateSessionWithContext(ctx, c.cfg.session) if err != nil { c.Close() stats.RecordError(err) @@ -201,7 +201,7 @@ func (c *Client) Connect(ctx context.Context) (err error) { return err } - if err := c.ActivateSession(s); err != nil { + if err := c.ActivateSessionWithContext(ctx, s); err != nil { c.Close() stats.RecordError(err) @@ -381,7 +381,7 @@ func (c *Client) monitor(ctx context.Context) { } dlog.Printf("trying to restore session") - if err := c.ActivateSession(s); err != nil { + if err := c.ActivateSessionWithContext(ctx, s); err != nil { dlog.Printf("restore session failed") action = recreateSession continue @@ -395,13 +395,13 @@ func (c *Client) monitor(ctx context.Context) { // create a new session to replace the previous one dlog.Printf("trying to recreate session") - s, err := c.CreateSession(c.cfg.session) + s, err := c.CreateSessionWithContext(ctx, c.cfg.session) if err != nil { dlog.Printf("recreate session failed: %v", err) action = createSecureChannel continue } - if err := c.ActivateSession(s); err != nil { + if err := c.ActivateSessionWithContext(ctx, s); err != nil { dlog.Printf("reactivate session failed: %v", err) action = createSecureChannel continue @@ -458,7 +458,7 @@ func (c *Client) monitor(ctx context.Context) { // populated in the previous step. for _, id := range subsToRepublish { - if err := c.republishSubscription(id, availableSeqs[id]); err != nil { + if err := c.republishSubscription(ctx, id, availableSeqs[id]); err != nil { dlog.Printf("republish of subscription %d failed", id) subsToRecreate = append(subsToRecreate, id) } @@ -644,6 +644,9 @@ type Session struct { // // See Part 4, 5.6.2 func (c *Client) CreateSession(cfg *uasc.SessionConfig) (*Session, error) { + return c.CreateSessionWithContext(context.Background(), cfg) +} +func (c *Client) CreateSessionWithContext(ctx context.Context, cfg *uasc.SessionConfig) (*Session, error) { if c.SecureChannel() == nil { return nil, ua.StatusBadServerNotConnected } @@ -670,7 +673,7 @@ func (c *Client) CreateSession(cfg *uasc.SessionConfig) (*Session, error) { var s *Session // for the CreateSessionRequest the authToken is always nil. // use c.SecureChannel().SendRequest() to enforce this. - err := c.SecureChannel().SendRequest(req, nil, func(v interface{}) error { + err := c.SecureChannel().SendRequestWithContext(ctx, req, nil, func(v interface{}) error { var res *ua.CreateSessionResponse if err := safeAssign(v, &res); err != nil { return err @@ -727,7 +730,11 @@ func anonymousPolicyID(endpoints []*ua.EndpointDescription) string { // session call DetachSession. // // See Part 4, 5.6.3 + func (c *Client) ActivateSession(s *Session) error { + return c.ActivateSessionWithContext(context.Background(), s) +} +func (c *Client) ActivateSessionWithContext(ctx context.Context, s *Session) error { if c.SecureChannel() == nil { return ua.StatusBadServerNotConnected } @@ -776,7 +783,7 @@ func (c *Client) ActivateSession(s *Session) error { UserIdentityToken: ua.NewExtensionObject(s.cfg.UserIdentityToken), UserTokenSignature: s.cfg.UserTokenSignature, } - return c.SecureChannel().SendRequest(req, s.resp.AuthenticationToken, func(v interface{}) error { + return c.SecureChannel().SendRequestWithContext(ctx, req, s.resp.AuthenticationToken, func(v interface{}) error { var res *ua.ActivateSessionResponse if err := safeAssign(v, &res); err != nil { return err @@ -803,8 +810,11 @@ func (c *Client) ActivateSession(s *Session) error { // // See Part 4, 5.6.4 func (c *Client) CloseSession() error { + return c.CloseSessionWithContext(context.Background()) +} +func (c *Client) CloseSessionWithContext(ctx context.Context) error { stats.Client().Add("CloseSession", 1) - if err := c.closeSession(c.Session()); err != nil { + if err := c.closeSession(ctx, c.Session()); err != nil { return err } c.setSession(nil) @@ -812,13 +822,13 @@ func (c *Client) CloseSession() error { } // closeSession closes the given session. -func (c *Client) closeSession(s *Session) error { +func (c *Client) closeSession(ctx context.Context, s *Session) error { if s == nil { return nil } req := &ua.CloseSessionRequest{DeleteSubscriptions: true} var res *ua.CloseSessionResponse - return c.Send(req, func(v interface{}) error { + return c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) } @@ -826,7 +836,15 @@ func (c *Client) closeSession(s *Session) error { // DetachSession removes the session from the client without closing it. The // caller is responsible to close or re-activate the session. If the client // does not have an active session the function returns no error. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) DetachSession() (*Session, error) { + return c.DetachSessionWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) DetachSessionWithContext(ctx context.Context) (*Session, error) { stats.Client().Add("DetachSession", 1) s := c.Session() c.setSession(nil) @@ -836,10 +854,18 @@ func (c *Client) DetachSession() (*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. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) Send(req ua.Request, h func(interface{}) error) error { + return c.SendWithContext(context.Background(), req, h) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) SendWithContext(ctx context.Context, req ua.Request, h func(interface{}) error) error { stats.Client().Add("Send", 1) - err := c.sendWithTimeout(req, c.cfg.sechan.RequestTimeout, h) + err := c.sendWithTimeout(ctx, req, c.cfg.sechan.RequestTimeout, h) stats.RecordError(err) return err @@ -848,7 +874,7 @@ func (c *Client) Send(req ua.Request, h func(interface{}) error) error { // 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(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 func(interface{}) error) error { if c.SecureChannel() == nil { return ua.StatusBadServerNotConnected } @@ -856,7 +882,7 @@ func (c *Client) sendWithTimeout(req ua.Request, timeout time.Duration, h func(i if s := c.Session(); s != nil { authToken = s.resp.AuthenticationToken } - return c.SecureChannel().SendRequestWithTimeout(req, authToken, timeout, h) + return c.SecureChannel().SendRequestWithTimeoutWithContext(ctx, req, authToken, timeout, h) } // Node returns a node object which accesses its attributes @@ -865,14 +891,23 @@ func (c *Client) Node(id *ua.NodeID) *Node { return &Node{ID: id, c: c} } +// GetEndpoints returns the list of available endpoints of the server. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) GetEndpoints() (*ua.GetEndpointsResponse, error) { + return c.GetEndpointsWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) GetEndpointsWithContext(ctx context.Context) (*ua.GetEndpointsResponse, error) { stats.Client().Add("GetEndpoints", 1) req := &ua.GetEndpointsRequest{ EndpointURL: c.endpointURL, } var res *ua.GetEndpointsResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err @@ -902,7 +937,15 @@ func cloneReadRequest(req *ua.ReadRequest) *ua.ReadRequest { // // By default, the function requests the value of the nodes // in the default encoding of the server. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) { + return c.ReadWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) ReadWithContext(ctx context.Context, req *ua.ReadRequest) (*ua.ReadResponse, error) { stats.Client().Add("Read", 1) stats.Client().Add("NodesToRead", int64(len(req.NodesToRead))) @@ -911,7 +954,7 @@ func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) { req = cloneReadRequest(req) var res *ua.ReadResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { err := safeAssign(v, &res) // If the client cannot decode an extension object then its @@ -937,12 +980,20 @@ func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) { } // Write executes a synchronous write request. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) Write(req *ua.WriteRequest) (*ua.WriteResponse, error) { + return c.WriteWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) WriteWithContext(ctx context.Context, req *ua.WriteRequest) (*ua.WriteResponse, error) { stats.Client().Add("Write", 1) stats.Client().Add("NodesToWrite", int64(len(req.NodesToWrite))) var res *ua.WriteResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err @@ -973,7 +1024,15 @@ func cloneBrowseRequest(req *ua.BrowseRequest) *ua.BrowseRequest { } // Browse executes a synchronous browse request. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) Browse(req *ua.BrowseRequest) (*ua.BrowseResponse, error) { + return c.BrowseWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) BrowseWithContext(ctx context.Context, req *ua.BrowseRequest) (*ua.BrowseResponse, error) { stats.Client().Add("Browse", 1) stats.Client().Add("NodesToBrowse", int64(len(req.NodesToBrowse))) @@ -982,21 +1041,29 @@ func (c *Client) Browse(req *ua.BrowseRequest) (*ua.BrowseResponse, error) { req = cloneBrowseRequest(req) var res *ua.BrowseResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err } // Call executes a synchronous call request for a single method. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) Call(req *ua.CallMethodRequest) (*ua.CallMethodResult, error) { + return c.CallWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) CallWithContext(ctx context.Context, req *ua.CallMethodRequest) (*ua.CallMethodResult, error) { stats.Client().Add("Call", 1) creq := &ua.CallRequest{ MethodsToCall: []*ua.CallMethodRequest{req}, } var res *ua.CallResponse - err := c.Send(creq, func(v interface{}) error { + err := c.SendWithContext(ctx, creq, func(v interface{}) error { return safeAssign(v, &res) }) if err != nil { @@ -1009,43 +1076,76 @@ func (c *Client) Call(req *ua.CallMethodRequest) (*ua.CallMethodResult, error) { } // BrowseNext executes a synchronous browse request. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) BrowseNext(req *ua.BrowseNextRequest) (*ua.BrowseNextResponse, error) { + return c.BrowseNextWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) BrowseNextWithContext(ctx context.Context, req *ua.BrowseNextRequest) (*ua.BrowseNextResponse, error) { stats.Client().Add("BrowseNext", 1) var res *ua.BrowseNextResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err } // RegisterNodes registers node ids for more efficient reads. +// // Part 4, Section 5.8.5 +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) RegisterNodes(req *ua.RegisterNodesRequest) (*ua.RegisterNodesResponse, error) { + return c.RegisterNodesWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) RegisterNodesWithContext(ctx context.Context, req *ua.RegisterNodesRequest) (*ua.RegisterNodesResponse, error) { stats.Client().Add("RegisterNodes", 1) stats.Client().Add("NodesToRegister", int64(len(req.NodesToRegister))) var res *ua.RegisterNodesResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err } // UnregisterNodes unregisters node ids previously registered with RegisterNodes. +// // Part 4, Section 5.8.6 +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) UnregisterNodes(req *ua.UnregisterNodesRequest) (*ua.UnregisterNodesResponse, error) { + return c.UnregisterNodesWithContext(context.Background(), req) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) UnregisterNodesWithContext(ctx context.Context, req *ua.UnregisterNodesRequest) (*ua.UnregisterNodesResponse, error) { stats.Client().Add("UnregisterNodes", 1) stats.Client().Add("NodesToUnregister", int64(len(req.NodesToUnregister))) var res *ua.UnregisterNodesResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err } +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) HistoryReadRawModified(nodes []*ua.HistoryReadValueID, details *ua.ReadRawModifiedDetails) (*ua.HistoryReadResponse, error) { + return c.HistoryReadRawModifiedWithContext(context.Background(), nodes, details) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) HistoryReadRawModifiedWithContext(ctx context.Context, nodes []*ua.HistoryReadValueID, details *ua.ReadRawModifiedDetails) (*ua.HistoryReadResponse, error) { stats.Client().Add("HistoryReadRawModified", 1) stats.Client().Add("HistoryReadValueID", int64(len(nodes))) @@ -1062,17 +1162,25 @@ func (c *Client) HistoryReadRawModified(nodes []*ua.HistoryReadValueID, details } var res *ua.HistoryReadResponse - err := c.Send(req, func(v interface{}) error { + err := c.SendWithContext(ctx, req, func(v interface{}) error { return safeAssign(v, &res) }) return res, err } // NamespaceArray returns the list of namespaces registered on the server. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) NamespaceArray() ([]string, error) { + return c.NamespaceArrayWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) NamespaceArrayWithContext(ctx context.Context) ([]string, error) { stats.Client().Add("NamespaceArray", 1) node := c.Node(ua.NewNumericNodeID(0, id.Server_NamespaceArray)) - v, err := node.Value() + v, err := node.ValueWithContext(ctx) if err != nil { return nil, err } @@ -1085,9 +1193,17 @@ func (c *Client) NamespaceArray() ([]string, error) { } // FindNamespace returns the id of the namespace with the given name. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) FindNamespace(name string) (uint16, error) { + return c.FindNamespaceWithContext(context.Background(), name) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) FindNamespaceWithContext(ctx context.Context, name string) (uint16, error) { stats.Client().Add("FindNamespace", 1) - nsa, err := c.NamespaceArray() + nsa, err := c.NamespaceArrayWithContext(ctx) if err != nil { return 0, err } @@ -1100,9 +1216,17 @@ func (c *Client) FindNamespace(name string) (uint16, error) { } // UpdateNamespaces updates the list of cached namespaces from the server. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (c *Client) UpdateNamespaces() error { + return c.UpdateNamespacesWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (c *Client) UpdateNamespacesWithContext(ctx context.Context) error { stats.Client().Add("UpdateNamespaces", 1) - ns, err := c.NamespaceArray() + ns, err := c.NamespaceArrayWithContext(ctx) if err != nil { return err } diff --git a/client_sub.go b/client_sub.go index 48efd214..7bd3c529 100644 --- a/client_sub.go +++ b/client_sub.go @@ -113,7 +113,7 @@ func (c *Client) transferSubscriptions(ids []uint32) (*ua.TransferSubscriptionsR } // republishSubscriptions sends republish requests for the given subscription id. -func (c *Client) republishSubscription(id uint32, availableSeq []uint32) error { +func (c *Client) republishSubscription(ctx context.Context, id uint32, availableSeq []uint32) error { c.subMux.RLock() defer c.subMux.RUnlock() @@ -123,7 +123,7 @@ func (c *Client) republishSubscription(id uint32, availableSeq []uint32) error { } debug.Printf("republishing subscription %d", sub.SubscriptionID) - if err := c.sendRepublishRequests(sub, availableSeq); err != nil { + if err := c.sendRepublishRequests(ctx, sub, availableSeq); err != nil { status, ok := err.(ua.StatusCode) if !ok { return err @@ -144,7 +144,7 @@ func (c *Client) republishSubscription(id uint32, availableSeq []uint32) error { // sendRepublishRequests sends republish requests for the given subscription // until it gets a BadMessageNotAvailable which implies that there are no // more messages to restore. -func (c *Client) sendRepublishRequests(sub *Subscription, availableSeq []uint32) error { +func (c *Client) sendRepublishRequests(ctx context.Context, sub *Subscription, availableSeq []uint32) error { // todo(fs): check if sub.nextSeq is in the available sequence numbers // todo(fs): if not then we need to decide whether we fail b/c of data loss // todo(fs): or whether we log it and continue. @@ -170,7 +170,7 @@ func (c *Client) sendRepublishRequests(sub *Subscription, availableSeq []uint32) debug.Printf("RepublishRequest: req=%s", debug.ToJSON(req)) var res *ua.RepublishResponse - err := c.SecureChannel().SendRequest(req, c.Session().resp.AuthenticationToken, func(v interface{}) error { + err := c.SecureChannel().SendRequestWithContext(ctx, req, c.Session().resp.AuthenticationToken, func(v interface{}) error { return safeAssign(v, &res) }) debug.Printf("RepublishResponse: res=%s err=%v", debug.ToJSON(res), err) @@ -379,7 +379,7 @@ func (c *Client) publish(ctx context.Context) error { // send the next publish request // note that res contains data even if an error was returned - res, err := c.sendPublishRequest() + res, err := c.sendPublishRequest(ctx) stats.RecordError(err) switch { case err == io.EOF: @@ -508,7 +508,7 @@ func (c *Client) handleNotification(ctx context.Context, sub *Subscription, res }) } -func (c *Client) sendPublishRequest() (*ua.PublishResponse, error) { +func (c *Client) sendPublishRequest(ctx context.Context) (*ua.PublishResponse, error) { dlog := debug.NewPrefixLogger("publish: ") c.subMux.RLock() @@ -522,7 +522,7 @@ func (c *Client) sendPublishRequest() (*ua.PublishResponse, error) { dlog.Printf("PublishRequest: %s", debug.ToJSON(req)) var res *ua.PublishResponse - err := c.sendWithTimeout(req, c.publishTimeout(), func(v interface{}) error { + err := c.sendWithTimeout(ctx, req, c.publishTimeout(), func(v interface{}) error { return safeAssign(v, &res) }) stats.RecordError(err) diff --git a/client_test.go b/client_test.go index 760225d5..6a0a1c19 100644 --- a/client_test.go +++ b/client_test.go @@ -1,6 +1,7 @@ package opcua import ( + "context" "testing" "github.com/gopcua/opcua/id" @@ -10,7 +11,7 @@ import ( func TestClient_Send_DoesNotPanicWhenDisconnected(t *testing.T) { c := NewClient("opc.tcp://example.com:4840") - err := c.Send(&ua.ReadRequest{}, func(i interface{}) error { + err := c.SendWithContext(context.Background(), &ua.ReadRequest{}, func(i interface{}) error { return nil }) verify.Values(t, "", err, ua.StatusBadServerNotConnected) diff --git a/examples/accesslevel/accesslevel.go b/examples/accesslevel/accesslevel.go index 6ae51ef1..d28a7d66 100644 --- a/examples/accesslevel/accesslevel.go +++ b/examples/accesslevel/accesslevel.go @@ -29,7 +29,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { @@ -37,19 +37,19 @@ func main() { } n := c.Node(id) - accessLevel, err := n.AccessLevel() + accessLevel, err := n.AccessLevelWithContext(ctx) if err != nil { log.Fatal(err) } log.Print("AccessLevel: ", accessLevel) - userAccessLevel, err := n.UserAccessLevel() + userAccessLevel, err := n.UserAccessLevelWithContext(ctx) if err != nil { log.Fatal(err) } log.Print("UserAccessLevel: ", userAccessLevel) - v, err := n.Value() + v, err := n.ValueWithContext(ctx) switch { case err != nil: log.Fatal(err) diff --git a/examples/browse/browse.go b/examples/browse/browse.go index 6310693f..3e564a4e 100644 --- a/examples/browse/browse.go +++ b/examples/browse/browse.go @@ -45,13 +45,13 @@ func join(a, b string) string { return a + "." + b } -func browse(n *opcua.Node, path string, level int) ([]NodeDef, error) { +func browse(ctx context.Context, n *opcua.Node, path string, level int) ([]NodeDef, error) { // fmt.Printf("node:%s path:%q level:%d\n", n, path, level) if level > 10 { return nil, nil } - attrs, err := n.Attributes(ua.AttributeIDNodeClass, ua.AttributeIDBrowseName, ua.AttributeIDDescription, ua.AttributeIDAccessLevel, ua.AttributeIDDataType) + attrs, err := n.AttributesWithContext(ctx, ua.AttributeIDNodeClass, ua.AttributeIDBrowseName, ua.AttributeIDDescription, ua.AttributeIDAccessLevel, ua.AttributeIDDataType) if err != nil { return nil, err } @@ -138,13 +138,13 @@ func browse(n *opcua.Node, path string, level int) ([]NodeDef, error) { } browseChildren := func(refType uint32) error { - refs, err := n.ReferencedNodes(refType, ua.BrowseDirectionForward, ua.NodeClassAll, true) + refs, err := n.ReferencedNodesWithContext(ctx, refType, ua.BrowseDirectionForward, ua.NodeClassAll, true) if err != nil { return errors.Errorf("References: %d: %s", refType, err) } // fmt.Printf("found %d child refs\n", len(refs)) for _, rn := range refs { - children, err := browse(rn, def.Path, level+1) + children, err := browse(ctx, rn, def.Path, level+1) if err != nil { return errors.Errorf("browse children: %s", err) } @@ -178,14 +178,14 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { log.Fatalf("invalid node id: %s", err) } - nodeList, err := browse(c.Node(id), "", 0) + nodeList, err := browse(ctx, c.Node(id), "", 0) if err != nil { log.Fatal(err) } diff --git a/examples/crypto/crypto.go b/examples/crypto/crypto.go index 05922697..ef80c304 100644 --- a/examples/crypto/crypto.go +++ b/examples/crypto/crypto.go @@ -65,7 +65,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) // Use our connection (read the server's time) v, err := c.Node(ua.NewNumericNodeID(0, 2258)).Value() diff --git a/examples/datetime/datetime.go b/examples/datetime/datetime.go index bde0aa1b..88a865d0 100644 --- a/examples/datetime/datetime.go +++ b/examples/datetime/datetime.go @@ -51,9 +51,9 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) - v, err := c.Node(ua.NewNumericNodeID(0, 2258)).Value() + v, err := c.Node(ua.NewNumericNodeID(0, 2258)).ValueWithContext(ctx) switch { case err != nil: log.Fatal(err) diff --git a/examples/history-read/history-read.go b/examples/history-read/history-read.go index cb2a2349..2fed4a28 100644 --- a/examples/history-read/history-read.go +++ b/examples/history-read/history-read.go @@ -28,7 +28,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { @@ -37,7 +37,7 @@ func main() { // HistoryRead with ContinuationPoint use nodesToRequest := []*ua.HistoryReadValueID{ - &ua.HistoryReadValueID{ + { NodeID: id, DataEncoding: &ua.QualifiedName{}, }, @@ -54,7 +54,7 @@ func main() { // Reset old nodes nodesToRequest = make([]*ua.HistoryReadValueID, 0) - data, err := c.HistoryReadRawModified(nodes, &ua.ReadRawModifiedDetails{ + data, err := c.HistoryReadRawModifiedWithContext(ctx, nodes, &ua.ReadRawModifiedDetails{ IsReadModified: false, StartTime: time.Now().UTC().AddDate(0, -1, 0), EndTime: time.Now().UTC().AddDate(0, 1, 0), diff --git a/examples/method/method.go b/examples/method/method.go index 19224087..b1cf17d7 100644 --- a/examples/method/method.go +++ b/examples/method/method.go @@ -28,7 +28,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) in := int64(12) req := &ua.CallMethodRequest{ @@ -37,7 +37,7 @@ func main() { InputArguments: []*ua.Variant{ua.MustVariant(in)}, } - resp, err := c.Call(req) + resp, err := c.CallWithContext(ctx, req) if err != nil { log.Fatal(err) } diff --git a/examples/monitor/monitor.go b/examples/monitor/monitor.go index 8663a0b3..4e4118ac 100644 --- a/examples/monitor/monitor.go +++ b/examples/monitor/monitor.go @@ -68,7 +68,7 @@ func main() { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) m, err := monitor.NewNodeMonitor(c) if err != nil { diff --git a/examples/read/read.go b/examples/read/read.go index ec50d9dd..2e5beac5 100644 --- a/examples/read/read.go +++ b/examples/read/read.go @@ -29,7 +29,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { @@ -44,7 +44,7 @@ func main() { TimestampsToReturn: ua.TimestampsToReturnBoth, } - resp, err := c.Read(req) + resp, err := c.ReadWithContext(ctx, req) if err != nil { log.Fatalf("Read failed: %s", err) } diff --git a/examples/regread/regread.go b/examples/regread/regread.go index 942f77a3..db5d1da7 100644 --- a/examples/regread/regread.go +++ b/examples/regread/regread.go @@ -29,14 +29,14 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { log.Fatalf("invalid node id: %v", err) } - regResp, err := c.RegisterNodes(&ua.RegisterNodesRequest{ + regResp, err := c.RegisterNodesWithContext(ctx, &ua.RegisterNodesRequest{ NodesToRegister: []*ua.NodeID{id}, }) if err != nil { @@ -46,12 +46,12 @@ func main() { req := &ua.ReadRequest{ MaxAge: 2000, NodesToRead: []*ua.ReadValueID{ - &ua.ReadValueID{NodeID: regResp.RegisteredNodeIDs[0]}, + {NodeID: regResp.RegisteredNodeIDs[0]}, }, TimestampsToReturn: ua.TimestampsToReturnBoth, } - resp, err := c.Read(req) + resp, err := c.ReadWithContext(ctx, req) if err != nil { log.Fatalf("Read failed: %s", err) } @@ -60,7 +60,7 @@ func main() { } log.Print(resp.Results[0].Value.Value()) - _, err = c.UnregisterNodes(&ua.UnregisterNodesRequest{ + _, err = c.UnregisterNodesWithContext(ctx, &ua.UnregisterNodesRequest{ NodesToUnregister: []*ua.NodeID{id}, }) if err != nil { diff --git a/examples/subscribe/subscribe.go b/examples/subscribe/subscribe.go index d9fd6ce0..006cd65f 100644 --- a/examples/subscribe/subscribe.go +++ b/examples/subscribe/subscribe.go @@ -62,7 +62,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) notifyCh := make(chan *opcua.PublishNotificationData) diff --git a/examples/translate/translate.go b/examples/translate/translate.go index f126002c..ad4b669b 100644 --- a/examples/translate/translate.go +++ b/examples/translate/translate.go @@ -31,10 +31,10 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) root := c.Node(ua.NewTwoByteNodeID(id.ObjectsFolder)) - nodeID, err := root.TranslateBrowsePathInNamespaceToNodeID(uint16(*ns), *nodePath) + nodeID, err := root.TranslateBrowsePathInNamespaceToNodeIDWithContext(ctx, uint16(*ns), *nodePath) if err != nil { log.Fatal(err) } diff --git a/examples/trigger/trigger.go b/examples/trigger/trigger.go index f1a84ade..2cc37293 100644 --- a/examples/trigger/trigger.go +++ b/examples/trigger/trigger.go @@ -63,7 +63,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) notifyCh := make(chan *opcua.PublishNotificationData) diff --git a/examples/udt/udt.go b/examples/udt/udt.go index cf4b0d32..7cba5163 100644 --- a/examples/udt/udt.go +++ b/examples/udt/udt.go @@ -59,9 +59,9 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) - v, err := c.Node(id).Value() + v, err := c.Node(id).ValueWithContext(ctx) switch { case err != nil: log.Fatal(err) diff --git a/examples/write/write.go b/examples/write/write.go index 54d82296..1521a6c2 100644 --- a/examples/write/write.go +++ b/examples/write/write.go @@ -30,7 +30,7 @@ func main() { if err := c.Connect(ctx); err != nil { log.Fatal(err) } - defer c.Close() + defer c.CloseSessionWithContext(ctx) id, err := ua.ParseNodeID(*nodeID) if err != nil { @@ -55,7 +55,7 @@ func main() { }, } - resp, err := c.Write(req) + resp, err := c.WriteWithContext(ctx, req) if err != nil { log.Fatalf("Read failed: %s", err) } diff --git a/node.go b/node.go index d0cdc76b..87931a68 100644 --- a/node.go +++ b/node.go @@ -5,6 +5,7 @@ package opcua import ( + "context" "strings" "github.com/gopcua/opcua/id" @@ -26,8 +27,16 @@ func (n *Node) String() string { } // NodeClass returns the node class attribute. -func (n *Node) NodeClass() (ua.NodeClass, error) { - v, err := n.Attribute(ua.AttributeIDNodeClass) +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. +func (n *Node) NodeClass(ctx context.Context) (ua.NodeClass, error) { + return n.NodeClassWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) NodeClassWithContext(ctx context.Context) (ua.NodeClass, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDNodeClass) if err != nil { return 0, err } @@ -35,8 +44,16 @@ func (n *Node) NodeClass() (ua.NodeClass, error) { } // BrowseName returns the browse name of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) BrowseName() (*ua.QualifiedName, error) { - v, err := n.Attribute(ua.AttributeIDBrowseName) + return n.BrowseNameWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) BrowseNameWithContext(ctx context.Context) (*ua.QualifiedName, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDBrowseName) if err != nil { return nil, err } @@ -44,8 +61,16 @@ func (n *Node) BrowseName() (*ua.QualifiedName, error) { } // Description returns the description of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) Description() (*ua.LocalizedText, error) { - v, err := n.Attribute(ua.AttributeIDDescription) + return n.DescriptionWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) DescriptionWithContext(ctx context.Context) (*ua.LocalizedText, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDDescription) if err != nil { return nil, err } @@ -53,8 +78,16 @@ func (n *Node) Description() (*ua.LocalizedText, error) { } // DisplayName returns the display name of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) DisplayName() (*ua.LocalizedText, error) { - v, err := n.Attribute(ua.AttributeIDDisplayName) + return n.DisplayNameWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) DisplayNameWithContext(ctx context.Context) (*ua.LocalizedText, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDDisplayName) if err != nil { return nil, err } @@ -64,8 +97,16 @@ func (n *Node) DisplayName() (*ua.LocalizedText, error) { // AccessLevel returns the access level of the node. // The returned value is a mask where multiple values can be // set, e.g. read and write. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) AccessLevel() (ua.AccessLevelType, error) { - v, err := n.Attribute(ua.AttributeIDAccessLevel) + return n.AccessLevelWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) AccessLevelWithContext(ctx context.Context) (ua.AccessLevelType, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDAccessLevel) if err != nil { return 0, err } @@ -74,8 +115,16 @@ func (n *Node) AccessLevel() (ua.AccessLevelType, error) { // HasAccessLevel returns true if all bits from mask are // set in the access level mask of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) HasAccessLevel(mask ua.AccessLevelType) (bool, error) { - v, err := n.AccessLevel() + return n.HasAccessLevelWithContext(context.Background(), mask) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) HasAccessLevelWithContext(ctx context.Context, mask ua.AccessLevelType) (bool, error) { + v, err := n.AccessLevelWithContext(ctx) if err != nil { return false, err } @@ -83,8 +132,16 @@ func (n *Node) HasAccessLevel(mask ua.AccessLevelType) (bool, error) { } // UserAccessLevel returns the access level of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) UserAccessLevel() (ua.AccessLevelType, error) { - v, err := n.Attribute(ua.AttributeIDUserAccessLevel) + return n.UserAccessLevelWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) UserAccessLevelWithContext(ctx context.Context) (ua.AccessLevelType, error) { + v, err := n.AttributeWithContext(ctx, ua.AttributeIDUserAccessLevel) if err != nil { return 0, err } @@ -93,8 +150,16 @@ func (n *Node) UserAccessLevel() (ua.AccessLevelType, error) { // HasUserAccessLevel returns true if all bits from mask are // set in the user access level mask of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) HasUserAccessLevel(mask ua.AccessLevelType) (bool, error) { - v, err := n.UserAccessLevel() + return n.HasUserAccessLevelWithContext(context.Background(), mask) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) HasUserAccessLevelWithContext(ctx context.Context, mask ua.AccessLevelType) (bool, error) { + v, err := n.UserAccessLevelWithContext(ctx) if err != nil { return false, err } @@ -102,15 +167,31 @@ func (n *Node) HasUserAccessLevel(mask ua.AccessLevelType) (bool, error) { } // Value returns the value of the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) Value() (*ua.Variant, error) { - return n.Attribute(ua.AttributeIDValue) + return n.ValueWithContext(context.Background()) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) ValueWithContext(ctx context.Context) (*ua.Variant, error) { + return n.AttributeWithContext(ctx, ua.AttributeIDValue) } // Attribute returns the attribute of the node. with the given id. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) Attribute(attrID ua.AttributeID) (*ua.Variant, error) { + return n.AttributeWithContext(context.Background(), attrID) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) AttributeWithContext(ctx context.Context, attrID ua.AttributeID) (*ua.Variant, error) { rv := &ua.ReadValueID{NodeID: n.ID, AttributeID: attrID} req := &ua.ReadRequest{NodesToRead: []*ua.ReadValueID{rv}} - res, err := n.c.Read(req) + res, err := n.c.ReadWithContext(ctx, req) if err != nil { return nil, err } @@ -126,32 +207,59 @@ func (n *Node) Attribute(attrID ua.AttributeID) (*ua.Variant, error) { return value, nil } +// Attributes returns the given node attributes. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) Attributes(attrID ...ua.AttributeID) ([]*ua.DataValue, error) { + return n.AttributesWithContext(context.Background(), attrID...) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) AttributesWithContext(ctx context.Context, attrID ...ua.AttributeID) ([]*ua.DataValue, error) { req := &ua.ReadRequest{} for _, id := range attrID { rv := &ua.ReadValueID{NodeID: n.ID, AttributeID: id} req.NodesToRead = append(req.NodesToRead, rv) } - res, err := n.c.Read(req) + res, err := n.c.ReadWithContext(ctx, req) if err != nil { return nil, err } return res.Results, nil } +// Children returns the child nodes which match the node class mask. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) Children(refs uint32, mask ua.NodeClass) ([]*Node, error) { + return n.ChildrenWithContext(context.Background(), refs, mask) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) ChildrenWithContext(ctx context.Context, refs uint32, mask ua.NodeClass) ([]*Node, error) { if refs == 0 { refs = id.HierarchicalReferences } - return n.ReferencedNodes(refs, ua.BrowseDirectionForward, mask, true) + return n.ReferencedNodesWithContext(ctx, refs, ua.BrowseDirectionForward, mask, true) } +// ReferencedNodes returns the nodes referenced by this node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) ReferencedNodes(refs uint32, dir ua.BrowseDirection, mask ua.NodeClass, includeSubtypes bool) ([]*Node, error) { + return n.ReferencedNodesWithContext(context.Background(), refs, dir, mask, includeSubtypes) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) ReferencedNodesWithContext(ctx context.Context, refs uint32, dir ua.BrowseDirection, mask ua.NodeClass, includeSubtypes bool) ([]*Node, error) { if refs == 0 { refs = id.References } var nodes []*Node - res, err := n.References(refs, dir, mask, includeSubtypes) + res, err := n.ReferencesWithContext(ctx, refs, dir, mask, includeSubtypes) if err != nil { return nil, err } @@ -162,9 +270,18 @@ func (n *Node) ReferencedNodes(refs uint32, dir ua.BrowseDirection, mask ua.Node } // References returns all references for the node. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. +// // todo(fs): this is not complete since it only returns the // todo(fs): top-level reference at this point. func (n *Node) References(refType uint32, dir ua.BrowseDirection, mask ua.NodeClass, includeSubtypes bool) ([]*ua.ReferenceDescription, error) { + return n.ReferencesWithContext(context.Background(), refType, dir, mask, includeSubtypes) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) ReferencesWithContext(ctx context.Context, refType uint32, dir ua.BrowseDirection, mask ua.NodeClass, includeSubtypes bool) ([]*ua.ReferenceDescription, error) { if refType == 0 { refType = id.References } @@ -189,21 +306,21 @@ func (n *Node) References(refType uint32, dir ua.BrowseDirection, mask ua.NodeCl NodesToBrowse: []*ua.BrowseDescription{desc}, } - resp, err := n.c.Browse(req) + resp, err := n.c.BrowseWithContext(ctx, req) if err != nil { return nil, err } - return n.browseNext(resp.Results) + return n.browseNext(ctx, resp.Results) } -func (n *Node) browseNext(results []*ua.BrowseResult) ([]*ua.ReferenceDescription, error) { +func (n *Node) browseNext(ctx context.Context, results []*ua.BrowseResult) ([]*ua.ReferenceDescription, error) { refs := results[0].References for len(results[0].ContinuationPoint) > 0 { req := &ua.BrowseNextRequest{ ContinuationPoints: [][]byte{results[0].ContinuationPoint}, ReleaseContinuationPoints: false, } - resp, err := n.c.BrowseNext(req) + resp, err := n.c.BrowseNextWithContext(ctx, req) if err != nil { return nil, err } @@ -214,7 +331,15 @@ func (n *Node) browseNext(results []*ua.BrowseResult) ([]*ua.ReferenceDescriptio } // TranslateBrowsePathsToNodeIDs translates an array of browseName segments to NodeIDs. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) TranslateBrowsePathsToNodeIDs(pathNames []*ua.QualifiedName) (*ua.NodeID, error) { + return n.TranslateBrowsePathsToNodeIDsWithContext(context.Background(), pathNames) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) TranslateBrowsePathsToNodeIDsWithContext(ctx context.Context, pathNames []*ua.QualifiedName) (*ua.NodeID, error) { req := ua.TranslateBrowsePathsToNodeIDsRequest{ BrowsePaths: []*ua.BrowsePath{ { @@ -236,7 +361,7 @@ func (n *Node) TranslateBrowsePathsToNodeIDs(pathNames []*ua.QualifiedName) (*ua } var nodeID *ua.NodeID - err := n.c.Send(&req, func(i interface{}) error { + err := n.c.SendWithContext(ctx, &req, func(i interface{}) error { if resp, ok := i.(*ua.TranslateBrowsePathsToNodeIDsResponse); ok { if len(resp.Results) == 0 { return ua.StatusBadUnexpectedError @@ -258,12 +383,20 @@ func (n *Node) TranslateBrowsePathsToNodeIDs(pathNames []*ua.QualifiedName) (*ua } // TranslateBrowsePathInNamespaceToNodeID translates a browseName to a NodeID within the same namespace. +// +// Note: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (n *Node) TranslateBrowsePathInNamespaceToNodeID(ns uint16, browsePath string) (*ua.NodeID, error) { + return n.TranslateBrowsePathInNamespaceToNodeIDWithContext(context.Background(), ns, browsePath) +} + +// Note: Starting with v0.5 this method is superseded by the non 'WithContext' method. +func (n *Node) TranslateBrowsePathInNamespaceToNodeIDWithContext(ctx context.Context, ns uint16, browsePath string) (*ua.NodeID, error) { segments := strings.Split(browsePath, ".") var names []*ua.QualifiedName for _, segment := range segments { qn := &ua.QualifiedName{NamespaceIndex: ns, Name: segment} names = append(names, qn) } - return n.TranslateBrowsePathsToNodeIDs(names) + return n.TranslateBrowsePathsToNodeIDsWithContext(ctx, names) } diff --git a/uasc/secure_channel.go b/uasc/secure_channel.go index 3bacc113..960a5c39 100644 --- a/uasc/secure_channel.go +++ b/uasc/secure_channel.go @@ -722,14 +722,26 @@ func (s *SecureChannel) Renew(ctx context.Context) error { } // SendRequest sends the service request and calls h with the response. +// Deprecated: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (s *SecureChannel) SendRequest(req ua.Request, authToken *ua.NodeID, h func(interface{}) error) error { - return s.SendRequestWithTimeout(req, authToken, s.cfg.RequestTimeout, h) + return s.SendRequestWithContext(context.Background(), req, authToken, h) } +// Note: This method will be replaced by the non "WithContext()" version +// of this method. +func (s *SecureChannel) SendRequestWithContext(ctx context.Context, req ua.Request, authToken *ua.NodeID, h func(interface{}) error) error { + return s.SendRequestWithTimeoutWithContext(ctx, req, authToken, s.cfg.RequestTimeout, h) +} + +// Deprecated: Starting with v0.5 this method will require a context +// and the corresponding XXXWithContext(ctx) method will be removed. func (s *SecureChannel) SendRequestWithTimeout(req ua.Request, authToken *ua.NodeID, timeout time.Duration, h func(interface{}) error) error { return s.SendRequestWithTimeoutWithContext(context.Background(), req, authToken, timeout, h) } +// Note: This method will be replaced by the non "WithContext()" version +// of this method. func (s *SecureChannel) SendRequestWithTimeoutWithContext(ctx context.Context, req ua.Request, authToken *ua.NodeID, timeout time.Duration, h func(interface{}) error) error { s.reqLocker.waitIfLock() active, err := s.getActiveChannelInstance() @@ -848,7 +860,7 @@ func (s *SecureChannel) close() error { default: } - err := s.SendRequest(&ua.CloseSecureChannelRequest{}, nil, nil) + err := s.SendRequestWithContext(context.Background(), &ua.CloseSecureChannelRequest{}, nil, nil) if err != nil { return err }