越来越坏MonitoredItemFilterUnsupported (0x80440000)

Getting BadMonitoredItemFilterUnsupported (0x80440000)

我正在使用 GOPCUA v0.2.0-rc2 库连接到 OPC UA 服务器。如果我不在监控项中使用任何过滤器,一切正常。但我想使用 datachange 过滤器,以便仅当它们按一定百分比或绝对值变化时才能够接收值。以下是完整代码。

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", false, "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.
    d := 10 * time.Second
    ctx, cancel := context.WithTimeout(context.Background(), d)
    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)
    }
    defer sub.Cancel()
    log.Printf("Created subscription with id %v", sub.SubscriptionID)

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

    var miCreateRequest *ua.MonitoredItemCreateRequest
    var eventFieldNames []string
    miCreateRequest = valueRequest(id)
    res, err := sub.Monitor(ua.TimestampsToReturnBoth, miCreateRequest)
    if err != nil || res.Results[0].StatusCode != ua.StatusOK {
        log.Fatal(err)
    }

    // read from subscription's notification channel until ctx is cancelled
    for {
        select {
        case <-ctx.Done():
            return
        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:
                for _, item := range x.Events {
                    log.Printf("Event for client handle: %v\n", item.ClientHandle)
                    for i, field := range item.EventFields {
                        log.Printf("%v: %v of Type: %T", eventFieldNames[i], field.Value(), field.Value())
                    }
                    log.Println()
                }

            default:
                log.Printf("what's this publish result? %T", res.Value)
            }
        }
    }
}

func valueRequest(nodeID *ua.NodeID) *ua.MonitoredItemCreateRequest {
    handle := uint32(42)

    attributeID := ua.AttributeIDValue

    filter := ua.DataChangeFilter{}
    filter.Trigger = ua.DataChangeTriggerStatusValue
    filter.DeadbandType = uint32(ua.DeadbandTypeNone)
    filter.DeadbandValue = 0.0

    filterExtObj := ua.ExtensionObject{
        EncodingMask: ua.ExtensionObjectBinary,
        TypeID: &ua.ExpandedNodeID{
            NodeID: nodeID,
        },
        Value: filter,
    }


    request := &ua.MonitoredItemCreateRequest{
        ItemToMonitor: &ua.ReadValueID{
            NodeID:       nodeID,
            AttributeID:  attributeID,
            DataEncoding: &ua.QualifiedName{},
        },
        MonitoringMode: ua.MonitoringModeReporting,
        RequestedParameters: &ua.MonitoringParameters{
            ClientHandle:     handle,
            DiscardOldest:    true,
            Filter:           &filterExtObj,
            QueueSize:        10,
            SamplingInterval: 0.0,
        },
    }
    return request
}

发送监控请求时,出现如下错误。

The server does not support the requested monitored item filter. StatusBadMonitoredItemFilterUnsupported (0x80440000)

服务器为Kepware OPC UA服务器,服务器端日志如下,

服务器收到请求

4/15/2021  5:24:07.474 PM [gopcua-1618489446170158449] CreateMonitoredItemsRequest
0000000000: Event started
0000000000:    Request Header: 
0000000000:       authenticationToken: i=2991937600 
0000000000:       timestamp (UTC): 2021-04-15T12:24:06.180 
0000000000:       requestHandle: 5 
0000000000:       returnDiagnostics: 0 
0000000000:       auditEntryId: [empty] 
0000000000:       timeoutHint(ms): 10000 
0000000000:    Parameters: 
0000000000:       subscriptionId: 16 
0000000000:       timestampsToReturn: Both 
0000000000:       monitoredItemCreateRequests []: Size: 1 
0000000000:          monitoredItemCreateRequests [ 0 ]: 
0000000000:             itemToMonitor: 
0000000000:                nodeId: ns=2;s=HONDA.DEV1.T1 
0000000000:                attributeId: Value 
0000000000:                indexRange: [empty] 
0000000000:                dataEncoding: [empty] 
0000000000:             monitoringMode: Reporting 
0000000000:             requestedParameters: 
0000000000:                clientHandle: 42 
0000000000:                samplingInterval: 0 
0000000000:                filter: 
0000000000:                   parameterTypeId: 67800040 
0000000000:                queueSize: 10 
0000000000:                discardOldest: 1 
0000000000: Event complete

服务器发送的响应。

4/15/2021  5:24:07.474 PM [gopcua-1618489446170158449] CreateMonitoredItemsResponse
    0000000000: Event started
    0000000000:    Response Header: 
    0000000000:       timestamp (UTC): 2021-04-15T12:24:07.474 
    0000000000:       requestHandle: 5 
    0000000000:       serviceResult: 0x00000000 (Good) 
    0000000000:    Parameters: 
    0000000000:       monitoredItemCreateResults []: Size: 1 
    0000000000:          monitoredItemCreateResults [ 0 ]: 
    0000000000:             statusCode: 0x80440000 (BadMonitoredItemFilterUnsupported) 
    0000000000:             monitoredItemId: 0 
    0000000000:             revisedSamplingInterval: 0 
    0000000000:             revisedQueueSize: 0 
    0000000000:             filterResult: [empty] 
    0000000000: Event complete

在我看来,您正在尝试使用您正在为其创建 MonitoredItem 的节点的 NodeId 来构建包含您的过滤器结构的 ExtensionObject,而不是标识您的过滤器结构的 DataTypeEncoding 的 NodeId'正在使用。