Go OPC UA-此会话没有可用的订阅。StatusBadNoSubscription(0x80790000)

Go OPC UA-此会话没有可用的订阅。StatusBadNoSubscription(0x80790000),go,opc-ua,opc,Go,Opc Ua,Opc,我正在使用库从OPC UA服务器()获取数据。当我在不关闭客户端连接的情况下取消订阅时,有时代码会陷入试图无限期创建安全通道的循环中,服务器会继续发送带有代码的serviceFaultResponse,StatusBadNoSubscription(0x80790000) 下面是我正在使用的代码,可以重现这个问题。这并不总是发生 下面是启用了调试的日志文件,用于代码何时工作正常()和何时不工作() 什么可能导致此问题-服务器、客户端库或我的代码? 如何解决呢 package main impor

我正在使用库从OPC UA服务器()获取数据。当我在不关闭客户端连接的情况下取消订阅时,有时代码会陷入试图无限期创建安全通道的循环中,服务器会继续发送带有代码的
serviceFaultResponse
,StatusBadNoSubscription(0x80790000)

下面是我正在使用的代码,可以重现这个问题。这并不总是发生

下面是启用了调试的日志文件,用于代码何时工作正常()和何时不工作()

什么可能导致此问题-服务器、客户端库或我的代码? 如何解决呢

package main
import (
    "context"
    "flag"
    "fmt"
    "log"
    "time"

    "github.com/gopcua/opcua"
    "github.com/gopcua/opcua/debug"
    "github.com/gopcua/opcua/ua"
)
func main() {
    var (
        endpoint = flag.String("endpoint", "opc.tcp://192.168.189.1:49320", "OPC UA Endpoint URL")
        policy   = flag.String("policy", "None", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: None")
        mode     = flag.String("mode", "None", "Security mode: None, Sign, SignAndEncrypt. Default: None")
        certFile = flag.String("cert", "", "Path to cert.pem. Required for security mode/policy != None")
        keyFile  = flag.String("key", "", "Path to private key.pem. Required for security mode/policy != None")
        nodeID   = flag.String("node", "ns=2;s=HONDA.DEV1.T1", "node id to subscribe to")
        interval = flag.String("interval", opcua.DefaultSubscriptionInterval.String(), "subscription interval")
    )
    flag.BoolVar(&debug.Enable, "debug", true, "enable debug logging")
    flag.Parse()
    log.SetFlags(0)

    subInterval, err := time.ParseDuration(*interval)
    if err != nil {
        log.Fatal(err)
    }

    // add an arbitrary timeout to demonstrate how to stop a subscription
    // with a context.
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    endpoints, err := opcua.GetEndpoints(ctx, *endpoint)
    if err != nil {
        log.Fatal(err)
    }
    ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode))
    if ep == nil {
        log.Fatal("Failed to find suitable endpoint")
    }

    fmt.Println("*", ep.SecurityPolicyURI, ep.SecurityMode)

    opts := []opcua.Option{
        opcua.SecurityPolicy(*policy),
        opcua.SecurityModeString(*mode),
        opcua.CertificateFile(*certFile),
        opcua.PrivateKeyFile(*keyFile),
        opcua.AuthAnonymous(),
        opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous),
    }

    c := opcua.NewClient(ep.EndpointURL, opts...)
    if err := c.Connect(ctx); err != nil {
        log.Fatal(err)
    }
    defer c.Close()

    notifyCh := make(chan *opcua.PublishNotificationData)

    sub, err := c.Subscribe(&opcua.SubscriptionParameters{
        Interval: subInterval,
    }, notifyCh)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("Created subscription with id %v", sub.SubscriptionID)

    id, err := ua.ParseNodeID(*nodeID)
    if err != nil {
        log.Fatal(err)
    }

    miCreateRequest := opcua.NewMonitoredItemCreateRequestWithDefaults(id, ua.AttributeIDValue, uint32(42))

    res, err := sub.Monitor(ua.TimestampsToReturnBoth, miCreateRequest)
    if err != nil || res.Results[0].StatusCode != ua.StatusOK {
        log.Fatal(err)
    }

    stop := make(chan int)
    time.AfterFunc(5*time.Second, func(){
        stop <- 0
    })

    breakLoop := false
    for {
        if breakLoop {
            break
        }
    
        select {
        case <-stop:
            sub.Cancel()
            breakLoop = true
        case res := <-notifyCh:
            if res.Error != nil {
                log.Print(res.Error)
                continue
            }

            switch x := res.Value.(type) {
            case *ua.DataChangeNotification:
                for _, item := range x.MonitoredItems {
                    data := item.Value.Value.Value()
                    log.Printf("MonitoredItem with client handle %v = %v", item.ClientHandle, data)
                }

            case *ua.EventNotificationList:
                fmt.Println("Got an event, why ?")
            default:
                log.Printf("what's this publish result? %T", res.Value)
            }
        }
    }

// doing some tasks here
    time.Sleep(2*time.Second)
}
主程序包
进口(
“上下文”
“旗帜”
“fmt”
“日志”
“时间”
“github.com/gopcua/opcua”
“github.com/gopcua/opcua/debug”
“github.com/gopcua/opcua/ua”
)
func main(){
变量(
endpoint=flag.String(“endpoint”、“opc”。tcp://192.168.189.1:49320“,“OPC UA端点URL”)
policy=flag.String(“策略”、“无”、“安全策略:无、Basic128Rsa15、Basic256、Basic256Sha256。默认值:无”)
mode=flag.String(“mode”、“None”、“安全模式:None、Sign、SignAndEncrypt.Default:None”)
certFile=flag.String(“cert”,“”,“安全模式/策略所需的cert.pem.路径!=None”)
keyFile=flag.String(“key”、“”、“private key.pem.的路径,安全模式/策略所需!=None”)
nodeID=flag.String(“节点”,“ns=2;s=HONDA.DEV1.T1”,“要订阅的节点id”)
interval=flag.String(“interval”,opcua.DefaultSubscriptionInterval.String(),“subscription interval”)
)
flag.BoolVar(&debug.Enable,“debug”,true,“Enable debug logging”)
flag.Parse()
log.SetFlags(0)
子间隔,错误:=time.ParseDuration(*interval)
如果错误!=零{
log.Fatal(错误)
}
//添加任意超时以演示如何停止订阅
//有背景的。
ctx,cancel:=context.WithCancel(context.Background())
推迟取消
端点,err:=opcua.GetEndpoints(ctx,*endpoint)
如果错误!=零{
log.Fatal(错误)
}
ep:=opcua.SelectEndpoint(端点、*策略、ua.MessageSecurityModeFromString(*模式))
如果ep==nil{
log.Fatal(“未能找到合适的端点”)
}
fmt.Println(“*”,ep.SecurityPolicyURI,ep.SecurityMode)
选项:=[]opcua.Option{
opcua.安全政策(*政策),
opcua.SecurityModeString(*模式),
opcua.CertificateFile(*certFile),
opcua.PrivateKeyFile(*keyFile),
opcua.AuthAnonymous(),
opcua.SecurityFromEndpoint(ep、ua.UserTokenTypeAnonymous),
}
c:=opcua.NewClient(ep.EndpointURL,opts…)
如果错误:=c.Connect(ctx);错误!=nil{
log.Fatal(错误)
}
推迟c.结束()
notifyCh:=make(chan*opcua.PublishNotificationData)
sub,err:=c.Subscribe(&opcua.SubscriptionParameters){
区间:子区间,
},notifyCh)
如果错误!=零{
log.Fatal(错误)
}
log.Printf(“创建了id为%v的订阅”,sub.SubscriptionID)
id,err:=ua.ParseNodeID(*nodeID)
如果错误!=零{
log.Fatal(错误)
}
miCreateRequest:=opcua.NewMonitoredItemCreateRequestWithDefaults(id,ua.AttributeIDValue,uint32(42))
res,err:=sub.Monitor(ua.TimestampStoreTurnTworth,miCreateRequest)
如果错误!=nil | | res.Results[0]。状态代码!=ua.StatusOK{
log.Fatal(错误)
}
停止:=make(chan int)
time.AfterFunc(5*time.Second,func(){
停止