From 041f1bf67a300afc8120adad1f370fe00cdd4a48 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Tue, 25 Oct 2022 11:01:36 -0700 Subject: [PATCH 1/7] fix: Add support for opcua datetime values --- plugins/common/opcua/input/input_client.go | 16 +++++++++++----- plugins/inputs/opcua/opcua_test.go | 6 ++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/common/opcua/input/input_client.go b/plugins/common/opcua/input/input_client.go index d8c9005451f2e..206bfead27211 100644 --- a/plugins/common/opcua/input/input_client.go +++ b/plugins/common/opcua/input/input_client.go @@ -3,15 +3,16 @@ package input import ( "context" "fmt" + "sort" + "strconv" + "strings" + "time" + "github.com/gopcua/opcua/ua" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal/choice" "github.com/influxdata/telegraf/metric" "github.com/influxdata/telegraf/plugins/common/opcua" - "sort" - "strconv" - "strings" - "time" ) // NodeSettings describes how to map from a OPC UA node to a Metric @@ -356,8 +357,13 @@ func (o *OpcUAInputClient) UpdateNodeValue(nodeIdx int, d *ua.DataValue) { } if d.Value != nil { - o.LastReceivedData[nodeIdx].Value = d.Value.Value() o.LastReceivedData[nodeIdx].DataType = d.Value.Type() + + if o.LastReceivedData[nodeIdx].DataType == ua.TypeIDDateTime { + o.LastReceivedData[nodeIdx].Value = d.Value.Value().(time.Time).Format(time.RFC3339Nano) + } else { + o.LastReceivedData[nodeIdx].Value = d.Value.Value() + } } o.LastReceivedData[nodeIdx].ServerTime = d.ServerTimestamp o.LastReceivedData[nodeIdx].SourceTime = d.SourceTimestamp diff --git a/plugins/inputs/opcua/opcua_test.go b/plugins/inputs/opcua/opcua_test.go index 16e763e9a80bf..bf858e286bdd8 100644 --- a/plugins/inputs/opcua/opcua_test.go +++ b/plugins/inputs/opcua/opcua_test.go @@ -2,6 +2,9 @@ package opcua import ( "fmt" + "testing" + "time" + "github.com/docker/go-connections/nat" "github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/plugins/common/opcua" @@ -9,8 +12,6 @@ import ( "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go/wait" - "testing" - "time" ) const servicePort = "4840" @@ -119,6 +120,7 @@ func TestReadClientIntegration(t *testing.T) { {"ManufacturerName", "0", "i", "2263", "open62541"}, {"badnode", "1", "i", "1337", nil}, {"goodnode", "1", "s", "the.answer", int32(42)}, + {"DateTime", "1", "s", "51037", "0001-01-01T00:00:00Z"}, } readConfig := ReadClientConfig{ From 3c5d22a06bfe4ff429c01cb36eed54470da88417 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Tue, 25 Oct 2022 16:27:32 -0700 Subject: [PATCH 2/7] fix: fixed broken test --- plugins/inputs/opcua/opcua_test.go | 2 +- plugins/inputs/opcua_listener/opcua_listener_test.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/inputs/opcua/opcua_test.go b/plugins/inputs/opcua/opcua_test.go index bf858e286bdd8..ee03d07e77649 100644 --- a/plugins/inputs/opcua/opcua_test.go +++ b/plugins/inputs/opcua/opcua_test.go @@ -120,7 +120,7 @@ func TestReadClientIntegration(t *testing.T) { {"ManufacturerName", "0", "i", "2263", "open62541"}, {"badnode", "1", "i", "1337", nil}, {"goodnode", "1", "s", "the.answer", int32(42)}, - {"DateTime", "1", "s", "51037", "0001-01-01T00:00:00Z"}, + {"DateTime", "1", "i", "51037", "0001-01-01T00:00:00Z"}, } readConfig := ReadClientConfig{ diff --git a/plugins/inputs/opcua_listener/opcua_listener_test.go b/plugins/inputs/opcua_listener/opcua_listener_test.go index 50f7bdcb36c48..85b547f4e860c 100644 --- a/plugins/inputs/opcua_listener/opcua_listener_test.go +++ b/plugins/inputs/opcua_listener/opcua_listener_test.go @@ -3,6 +3,9 @@ package opcua_listener import ( "context" "fmt" + "testing" + "time" + "github.com/docker/go-connections/nat" "github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/plugins/common/opcua" @@ -10,8 +13,6 @@ import ( "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go/wait" - "testing" - "time" ) const servicePort = "4840" @@ -54,6 +55,8 @@ func TestSubscribeClientIntegration(t *testing.T) { {"ManufacturerName", "0", "i", "2263", "open62541"}, {"badnode", "1", "i", "1337", nil}, {"goodnode", "1", "s", "the.answer", int32(42)}, + {"DateTime2", "1", "s", "some_date_one", "hello"}, + {"DateTime", "1", "s", "some_date", "0001-01-01T00:00:00Z"}, } var tagsRemaining = make([]string, 0, len(testopctags)) for i, tag := range testopctags { From 15ec6ac749f691eecb1f64b4650826e4a9a621f7 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Wed, 26 Oct 2022 16:40:48 -0700 Subject: [PATCH 3/7] fix: opcua listener client integration test --- plugins/inputs/opcua_listener/opcua_listener_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/inputs/opcua_listener/opcua_listener_test.go b/plugins/inputs/opcua_listener/opcua_listener_test.go index 85b547f4e860c..e2a35e1b1b212 100644 --- a/plugins/inputs/opcua_listener/opcua_listener_test.go +++ b/plugins/inputs/opcua_listener/opcua_listener_test.go @@ -55,8 +55,7 @@ func TestSubscribeClientIntegration(t *testing.T) { {"ManufacturerName", "0", "i", "2263", "open62541"}, {"badnode", "1", "i", "1337", nil}, {"goodnode", "1", "s", "the.answer", int32(42)}, - {"DateTime2", "1", "s", "some_date_one", "hello"}, - {"DateTime", "1", "s", "some_date", "0001-01-01T00:00:00Z"}, + {"DateTime", "1", "i", "51037", "0001-01-01T00:00:00Z"}, } var tagsRemaining = make([]string, 0, len(testopctags)) for i, tag := range testopctags { From a0433f2fb9d9ae74a7be38acef7c1a5cd98aff49 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Tue, 1 Nov 2022 09:46:08 -0700 Subject: [PATCH 4/7] Update plugins/common/opcua/input/input_client.go add type assertion so we don't panic if we fail to convert to a time string Co-authored-by: Sven Rebhan <36194019+srebhan@users.noreply.github.com> --- plugins/common/opcua/input/input_client.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/common/opcua/input/input_client.go b/plugins/common/opcua/input/input_client.go index 206bfead27211..f889afe5f3c33 100644 --- a/plugins/common/opcua/input/input_client.go +++ b/plugins/common/opcua/input/input_client.go @@ -359,10 +359,11 @@ func (o *OpcUAInputClient) UpdateNodeValue(nodeIdx int, d *ua.DataValue) { if d.Value != nil { o.LastReceivedData[nodeIdx].DataType = d.Value.Type() + o.LastReceivedData[nodeIdx].Value = d.Value.Value() if o.LastReceivedData[nodeIdx].DataType == ua.TypeIDDateTime { - o.LastReceivedData[nodeIdx].Value = d.Value.Value().(time.Time).Format(time.RFC3339Nano) - } else { - o.LastReceivedData[nodeIdx].Value = d.Value.Value() + if t, ok := d.Value.Value().(time.Time); ok { + o.LastReceivedData[nodeIdx].Value = t.Format(time.RFC3339Nano) + } } } o.LastReceivedData[nodeIdx].ServerTime = d.ServerTimestamp From 804957aab7530f66060edce12795e2d0762fa6d1 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Wed, 2 Nov 2022 16:38:36 -0700 Subject: [PATCH 5/7] feat(inputs.opcua): added time format option --- plugins/common/opcua/client.go | 32 +++++++++++-------- plugins/common/opcua/input/input_client.go | 2 +- plugins/inputs/opcua_listener/README.md | 6 ++++ .../opcua_listener/opcua_listener_test.go | 1 + plugins/inputs/opcua_listener/sample.conf | 6 ++++ 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/plugins/common/opcua/client.go b/plugins/common/opcua/client.go index ea838b50ec6d4..3e822c0b5ad18 100644 --- a/plugins/common/opcua/client.go +++ b/plugins/common/opcua/client.go @@ -3,13 +3,14 @@ package opcua import ( "context" "fmt" + "net/url" + "strconv" + "time" + "github.com/gopcua/opcua" "github.com/gopcua/opcua/ua" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/config" - "net/url" - "strconv" - "time" ) type OpcUAWorkarounds struct { @@ -17,16 +18,17 @@ type OpcUAWorkarounds struct { } type OpcUAClientConfig struct { - Endpoint string `toml:"endpoint"` - SecurityPolicy string `toml:"security_policy"` - SecurityMode string `toml:"security_mode"` - Certificate string `toml:"certificate"` - PrivateKey string `toml:"private_key"` - Username string `toml:"username"` - Password string `toml:"password"` - AuthMethod string `toml:"auth_method"` - ConnectTimeout config.Duration `toml:"connect_timeout"` - RequestTimeout config.Duration `toml:"request_timeout"` + Endpoint string `toml:"endpoint"` + SecurityPolicy string `toml:"security_policy"` + SecurityMode string `toml:"security_mode"` + Certificate string `toml:"certificate"` + PrivateKey string `toml:"private_key"` + Username string `toml:"username"` + Password string `toml:"password"` + AuthMethod string `toml:"auth_method"` + ConnectTimeout config.Duration `toml:"connect_timeout"` + RequestTimeout config.Duration `toml:"request_timeout"` + TimestampFormat string `toml:"timestamp_format"` Workarounds OpcUAWorkarounds `toml:"workarounds"` } @@ -56,6 +58,10 @@ func (o *OpcUAClientConfig) validateEndpoint() error { default: return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityMode, o.Endpoint) } + + if o.TimestampFormat == "" { + o.TimestampFormat = time.RFC3339Nano + } return nil } diff --git a/plugins/common/opcua/input/input_client.go b/plugins/common/opcua/input/input_client.go index 206bfead27211..b977eccd0a9fa 100644 --- a/plugins/common/opcua/input/input_client.go +++ b/plugins/common/opcua/input/input_client.go @@ -360,7 +360,7 @@ func (o *OpcUAInputClient) UpdateNodeValue(nodeIdx int, d *ua.DataValue) { o.LastReceivedData[nodeIdx].DataType = d.Value.Type() if o.LastReceivedData[nodeIdx].DataType == ua.TypeIDDateTime { - o.LastReceivedData[nodeIdx].Value = d.Value.Value().(time.Time).Format(time.RFC3339Nano) + o.LastReceivedData[nodeIdx].Value = d.Value.Value().(time.Time).Format(o.Config.TimestampFormat) } else { o.LastReceivedData[nodeIdx].Value = d.Value.Value() } diff --git a/plugins/inputs/opcua_listener/README.md b/plugins/inputs/opcua_listener/README.md index 5b275d5e13559..447cdf63f6a0f 100644 --- a/plugins/inputs/opcua_listener/README.md +++ b/plugins/inputs/opcua_listener/README.md @@ -56,6 +56,12 @@ Plugin minimum tested version: 1.25 ## "source" -- uses the timestamp provided by the source # 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 ## name - field name to use in the output ## namespace - OPC UA namespace of the node (integer value 0 thru 3) diff --git a/plugins/inputs/opcua_listener/opcua_listener_test.go b/plugins/inputs/opcua_listener/opcua_listener_test.go index e2a35e1b1b212..9c2817b04cd62 100644 --- a/plugins/inputs/opcua_listener/opcua_listener_test.go +++ b/plugins/inputs/opcua_listener/opcua_listener_test.go @@ -154,6 +154,7 @@ security_mode = "auto" certificate = "/etc/telegraf/cert.pem" private_key = "/etc/telegraf/key.pem" auth_method = "Anonymous" +timestamp_format = "2006-01-02T15:04:05Z07:00" username = "" password = "" nodes = [ diff --git a/plugins/inputs/opcua_listener/sample.conf b/plugins/inputs/opcua_listener/sample.conf index ed8245abda047..fb9f7487c3a40 100644 --- a/plugins/inputs/opcua_listener/sample.conf +++ b/plugins/inputs/opcua_listener/sample.conf @@ -46,6 +46,12 @@ ## "source" -- uses the timestamp provided by the source # 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 ## name - field name to use in the output ## namespace - OPC UA namespace of the node (integer value 0 thru 3) From 66e8c405b767acf32d8b83c045344e73d6140e38 Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Thu, 3 Nov 2022 09:18:25 -0700 Subject: [PATCH 6/7] chore: move timestamp format to inputclient config --- plugins/common/opcua/input/input_client.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/common/opcua/input/input_client.go b/plugins/common/opcua/input/input_client.go index c859a906041c3..20e8896fafdd5 100644 --- a/plugins/common/opcua/input/input_client.go +++ b/plugins/common/opcua/input/input_client.go @@ -53,10 +53,11 @@ const ( // InputClientConfig a configuration for the input client type InputClientConfig struct { opcua.OpcUAClientConfig - MetricName string `toml:"name"` - Timestamp TimestampSource `toml:"timestamp"` - RootNodes []NodeSettings `toml:"nodes"` - Groups []NodeGroupSettings `toml:"group"` + MetricName string `toml:"name"` + Timestamp TimestampSource `toml:"timestamp"` + TimestampFormat string `toml:"timestamp_format"` + RootNodes []NodeSettings `toml:"nodes"` + Groups []NodeGroupSettings `toml:"group"` } func (o *InputClientConfig) Validate() error { @@ -69,6 +70,10 @@ func (o *InputClientConfig) Validate() error { return err } + if o.TimestampFormat == "" { + o.TimestampFormat = time.RFC3339Nano + } + return nil } From 336664b32d728802ff920d1251807d55c7b9344a Mon Sep 17 00:00:00 2001 From: Viraj Sinha Date: Thu, 3 Nov 2022 09:26:29 -0700 Subject: [PATCH 7/7] fix: remove timestamp format from ClientConfig --- plugins/common/opcua/client.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/plugins/common/opcua/client.go b/plugins/common/opcua/client.go index 3e822c0b5ad18..728bd755a8b03 100644 --- a/plugins/common/opcua/client.go +++ b/plugins/common/opcua/client.go @@ -18,17 +18,16 @@ type OpcUAWorkarounds struct { } type OpcUAClientConfig struct { - Endpoint string `toml:"endpoint"` - SecurityPolicy string `toml:"security_policy"` - SecurityMode string `toml:"security_mode"` - Certificate string `toml:"certificate"` - PrivateKey string `toml:"private_key"` - Username string `toml:"username"` - Password string `toml:"password"` - AuthMethod string `toml:"auth_method"` - ConnectTimeout config.Duration `toml:"connect_timeout"` - RequestTimeout config.Duration `toml:"request_timeout"` - TimestampFormat string `toml:"timestamp_format"` + Endpoint string `toml:"endpoint"` + SecurityPolicy string `toml:"security_policy"` + SecurityMode string `toml:"security_mode"` + Certificate string `toml:"certificate"` + PrivateKey string `toml:"private_key"` + Username string `toml:"username"` + Password string `toml:"password"` + AuthMethod string `toml:"auth_method"` + ConnectTimeout config.Duration `toml:"connect_timeout"` + RequestTimeout config.Duration `toml:"request_timeout"` Workarounds OpcUAWorkarounds `toml:"workarounds"` } @@ -59,9 +58,6 @@ func (o *OpcUAClientConfig) validateEndpoint() error { return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityMode, o.Endpoint) } - if o.TimestampFormat == "" { - o.TimestampFormat = time.RFC3339Nano - } return nil }