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

问题描述

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

StatusBadNoSubscription (0x80790000)

以下是我正在使用的可以重现该问题的代码。它并不总是发生。

以下是启用调试的日志文件,用于代码正常工作时 (logs_ok.txt) 和不工作时 (logs_not_ok.txt)。

导致此问题的原因是什么 - 服务器、客户端库还是我的代码? 怎么解决

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","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)
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)