fix: Add support for opcua datetime values (#12101)

This commit is contained in:
Viraj Sinha 2022-11-07 11:35:53 -08:00 committed by GitHub
parent e305973731
commit e42b0832e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 16 deletions

View File

@ -3,13 +3,14 @@ package opcua
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"strconv"
"time"
"github.com/gopcua/opcua" "github.com/gopcua/opcua"
"github.com/gopcua/opcua/ua" "github.com/gopcua/opcua/ua"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/config"
"net/url"
"strconv"
"time"
) )
type OpcUAWorkarounds struct { type OpcUAWorkarounds struct {
@ -56,6 +57,7 @@ func (o *OpcUAClientConfig) validateEndpoint() error {
default: default:
return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityMode, o.Endpoint) return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityMode, o.Endpoint)
} }
return nil return nil
} }

View File

@ -3,15 +3,16 @@ package input
import ( import (
"context" "context"
"fmt" "fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/gopcua/opcua/ua" "github.com/gopcua/opcua/ua"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal/choice" "github.com/influxdata/telegraf/internal/choice"
"github.com/influxdata/telegraf/metric" "github.com/influxdata/telegraf/metric"
"github.com/influxdata/telegraf/plugins/common/opcua" "github.com/influxdata/telegraf/plugins/common/opcua"
"sort"
"strconv"
"strings"
"time"
) )
// NodeSettings describes how to map from a OPC UA node to a Metric // NodeSettings describes how to map from a OPC UA node to a Metric
@ -52,10 +53,11 @@ const (
// InputClientConfig a configuration for the input client // InputClientConfig a configuration for the input client
type InputClientConfig struct { type InputClientConfig struct {
opcua.OpcUAClientConfig opcua.OpcUAClientConfig
MetricName string `toml:"name"` MetricName string `toml:"name"`
Timestamp TimestampSource `toml:"timestamp"` Timestamp TimestampSource `toml:"timestamp"`
RootNodes []NodeSettings `toml:"nodes"` TimestampFormat string `toml:"timestamp_format"`
Groups []NodeGroupSettings `toml:"group"` RootNodes []NodeSettings `toml:"nodes"`
Groups []NodeGroupSettings `toml:"group"`
} }
func (o *InputClientConfig) Validate() error { func (o *InputClientConfig) Validate() error {
@ -68,6 +70,10 @@ func (o *InputClientConfig) Validate() error {
return err return err
} }
if o.TimestampFormat == "" {
o.TimestampFormat = time.RFC3339Nano
}
return nil return nil
} }
@ -356,8 +362,14 @@ func (o *OpcUAInputClient) UpdateNodeValue(nodeIdx int, d *ua.DataValue) {
} }
if d.Value != nil { if d.Value != nil {
o.LastReceivedData[nodeIdx].Value = d.Value.Value()
o.LastReceivedData[nodeIdx].DataType = d.Value.Type() o.LastReceivedData[nodeIdx].DataType = d.Value.Type()
o.LastReceivedData[nodeIdx].Value = d.Value.Value()
if o.LastReceivedData[nodeIdx].DataType == ua.TypeIDDateTime {
if t, ok := d.Value.Value().(time.Time); ok {
o.LastReceivedData[nodeIdx].Value = t.Format(o.Config.TimestampFormat)
}
}
} }
o.LastReceivedData[nodeIdx].ServerTime = d.ServerTimestamp o.LastReceivedData[nodeIdx].ServerTime = d.ServerTimestamp
o.LastReceivedData[nodeIdx].SourceTime = d.SourceTimestamp o.LastReceivedData[nodeIdx].SourceTime = d.SourceTimestamp

View File

@ -2,6 +2,9 @@ package opcua
import ( import (
"fmt" "fmt"
"testing"
"time"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/common/opcua" "github.com/influxdata/telegraf/plugins/common/opcua"
@ -9,8 +12,6 @@ import (
"github.com/influxdata/telegraf/testutil" "github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go/wait" "github.com/testcontainers/testcontainers-go/wait"
"testing"
"time"
) )
const servicePort = "4840" const servicePort = "4840"
@ -119,6 +120,7 @@ func TestReadClientIntegration(t *testing.T) {
{"ManufacturerName", "0", "i", "2263", "open62541"}, {"ManufacturerName", "0", "i", "2263", "open62541"},
{"badnode", "1", "i", "1337", nil}, {"badnode", "1", "i", "1337", nil},
{"goodnode", "1", "s", "the.answer", int32(42)}, {"goodnode", "1", "s", "the.answer", int32(42)},
{"DateTime", "1", "i", "51037", "0001-01-01T00:00:00Z"},
} }
readConfig := ReadClientConfig{ readConfig := ReadClientConfig{

View File

@ -65,6 +65,12 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## "source" -- uses the timestamp provided by the source ## "source" -- uses the timestamp provided by the source
# timestamp = "gather" # timestamp = "gather"
# #
## The default timetsamp format is RFC3339Nano
# Other timestamp layouts can be configured using the Go language time
# layout specification from https://golang.org/pkg/time/#Time.Format
# e.g.: json_timestamp_format = "2006-01-02T15:04:05Z07:00"
#timestamp_format = ""
#
## Node ID configuration ## Node ID configuration
## name - field name to use in the output ## name - field name to use in the output
## namespace - OPC UA namespace of the node (integer value 0 thru 3) ## namespace - OPC UA namespace of the node (integer value 0 thru 3)

View File

@ -3,6 +3,9 @@ package opcua_listener
import ( import (
"context" "context"
"fmt" "fmt"
"testing"
"time"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/common/opcua" "github.com/influxdata/telegraf/plugins/common/opcua"
@ -10,8 +13,6 @@ import (
"github.com/influxdata/telegraf/testutil" "github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go/wait" "github.com/testcontainers/testcontainers-go/wait"
"testing"
"time"
) )
const servicePort = "4840" const servicePort = "4840"
@ -54,6 +55,7 @@ func TestSubscribeClientIntegration(t *testing.T) {
{"ManufacturerName", "0", "i", "2263", "open62541"}, {"ManufacturerName", "0", "i", "2263", "open62541"},
{"badnode", "1", "i", "1337", nil}, {"badnode", "1", "i", "1337", nil},
{"goodnode", "1", "s", "the.answer", int32(42)}, {"goodnode", "1", "s", "the.answer", int32(42)},
{"DateTime", "1", "i", "51037", "0001-01-01T00:00:00Z"},
} }
var tagsRemaining = make([]string, 0, len(testopctags)) var tagsRemaining = make([]string, 0, len(testopctags))
for i, tag := range testopctags { for i, tag := range testopctags {
@ -152,6 +154,7 @@ security_mode = "auto"
certificate = "/etc/telegraf/cert.pem" certificate = "/etc/telegraf/cert.pem"
private_key = "/etc/telegraf/key.pem" private_key = "/etc/telegraf/key.pem"
auth_method = "Anonymous" auth_method = "Anonymous"
timestamp_format = "2006-01-02T15:04:05Z07:00"
username = "" username = ""
password = "" password = ""
nodes = [ nodes = [

View File

@ -46,6 +46,12 @@
## "source" -- uses the timestamp provided by the source ## "source" -- uses the timestamp provided by the source
# timestamp = "gather" # timestamp = "gather"
# #
## The default timetsamp format is RFC3339Nano
# Other timestamp layouts can be configured using the Go language time
# layout specification from https://golang.org/pkg/time/#Time.Format
# e.g.: json_timestamp_format = "2006-01-02T15:04:05Z07:00"
#timestamp_format = ""
#
## Node ID configuration ## Node ID configuration
## name - field name to use in the output ## name - field name to use in the output
## namespace - OPC UA namespace of the node (integer value 0 thru 3) ## namespace - OPC UA namespace of the node (integer value 0 thru 3)