I'm using GOPCUA v0.2.0-rc2 library to connect to an OPC UA server. Everything works fine if I don't use any filter in the monitoring items. But I want to use datachange filter to be able to receive values only when they change by some percentage or absolute value. Following is the complete code.
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
}
When the monitoring request is sent, I get following error.
The server does not support the requested monitored item filter. StatusBadMonitoredItemFilterUnsupported (0x80440000)
The server is Kepware OPC UA server, and the logs on the server side are below,
The request received by server
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
The response sent by server.
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
It looks to me like you're trying to build the ExtensionObject that contains your filter structure using the NodeId of the Node you're creating a MonitoredItem for instead of the NodeId that identifies the DataTypeEncoding of the filter structure you're using.