gopcua / opcua

Native Go OPC-UA library
MIT License
871 stars 264 forks source link

Proposal: support additional time encoding formats #574

Open magiconair opened 2 years ago

magiconair commented 2 years ago

Some PLCs do not support encoding timestamps with millisecond precision or higher precision in DateTime variables. The workaround is to either send RFC3339Nano formatted strings or to encode an LTIME in an int64 or uint64.

We could support this in gopcua through struct tags, e.g.

type Data struct {
    T1 time.Time `opcua:"type=int64,format=epoch-ms"`  // int64 with unix epoch ms
    T2 time.Time `opcua:"type=uint64,format=ltime-ns"` // uint64 with LTIME ns
    T2 time.Time `opcua:"type=string,format=2006-01-02T15:04:05.999999999Z07:00"` // string with RFC 3339Nano format
}

We could also auto-detect the type and only specify the format.

type Data struct {
    T1 time.Time `opcua:"format=epoch-ms"` // int64 with unix epoch ms
    T2 time.Time `opcua:"format=ltime-ns"` // uint64 with LTIME ns
    T2 time.Time `opcua:"format=2006-01-02T15:04:05.999999999Z07:00"` // string with RFC 3339Nano format
}

Would this be useful?

magiconair commented 2 years ago

@kung-foo ^^

kung-foo commented 2 years ago

where are we using struct tags? Internally at Intelecy are doing something similar. Example (structs defined via protobufs but same output as above):

message KepwareTagProperties {
  string name                     = 1 [(gogoproto.moretags) = "opc-ua:\"_Name\""];
  string address                  = 2 [(gogoproto.moretags) = "opc-ua:\"_Address\""];
  string description              = 3 [(gogoproto.moretags) = "opc-ua:\"_Description\""];
  string raw_data_type            = 4 [(gogoproto.moretags) = "opc-ua:\"_RawDataType\""];
  bool scaling_clamp_high         = 5 [(gogoproto.moretags) = "opc-ua:\"_ScalingClampHigh\""];
  bool scaling_clamp_low          = 6 [(gogoproto.moretags) = "opc-ua:\"_ScalingClampLow\""];
  double scaling_raw_high         = 7 [(gogoproto.moretags) = "opc-ua:\"_ScalingRawHigh\""];
  double scaling_raw_low          = 8 [(gogoproto.moretags) = "opc-ua:\"_ScalingRawLow\""];
  string scaling_scaled_data_type = 9 [(gogoproto.moretags) = "opc-ua:\"_ScalingScaledDataType\""];
  double scaling_scaled_high      = 10 [(gogoproto.moretags) = "opc-ua:\"_ScalingScaledHigh\""];
  double scaling_scaled_low       = 11 [(gogoproto.moretags) = "opc-ua:\"_ScalingScaledLow\""];
  string scaling_type             = 12 [(gogoproto.moretags) = "opc-ua:\"_ScalingType\""];
  string scaling_units            = 13 [(gogoproto.moretags) = "opc-ua:\"_ScalingUnits\""];
}
thetooth commented 2 months ago

Sorry to bump this issue but I'm working on a OPC UA to websocket translation middleware for a HMI project and am wondering if we should add support for IEC TIME (specifically duration) types? I've not looked closely at this but it would be nice to have access to elapsed time from TON function blocks and changing their set points.

Are there any edge case issues you can think of? Afaik they are just millisecond precision integers so encoding shouldn't be an issue, let me know and I'll start work on a PR to add this type, it's currently coming back with a type id of 3005 which seems a bit odd.