Skip to content

Commit

Permalink
feat: more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyTheocharis committed Nov 29, 2023
1 parent 7d15b3d commit d222fdb
Show file tree
Hide file tree
Showing 4 changed files with 766 additions and 121 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,51 @@ spec:
name: benthos-1-config
```

### Capabilities
The plugin is designed to browse and subscribe to all child nodes within a folder for each configured NodeID, provided that the NodeID represents a folder. It features a recursion depth of up to 10 levels, enabling thorough exploration of nested folder structures. The browsing specifically targets nodes organized under the OPC UA 'Organizes' relationship type, intentionally excluding nodes under 'HasProperty' and 'HasComponent' relationships. Additionally, the plugin does not browse Objects represented by red, blue, or green cube icons in UAExpert.

Subscriptions are selectively managed, with tags having a DataType of null being excluded from subscription. Also, by default, the plugin does not subscribe to the properties of a tag, such as minimum and maximum values.

#### Datatypes
The plugin has been rigorously tested with an array of datatypes, both as single values and as arrays. The following datatypes have been verified for compatibility:

- `Boolean`
- `Byte`
- `DateTime`
- `Double`
- `Enumeration`
- `ExpandedNodeId`
- `Float`
- `Guid`
- `Int16`
- `Int32`
- `Int64`
- `Integer`
- `LocalizedText`
- `NodeId`
- `Number`
- `QualifiedName`
- `SByte`
- `StatusCode`
- `String`
- `UInt16`
- `UInt32`
- `UInt64`
- `UInteger`
- `ByteArray`
- `ByteString`
- `Duration`
- `LocaleId`
- `UtcTime`
- `Variant`
- `XmlElement`

There are specific datatypes which are currently not supported by the plugin and attempting to use them will result in errors. These include:

- Two-dimensional arrays
- UA Extension Objects


### Authentication and Security

In benthos-umh, security and authentication are designed to be as robust as possible while maintaining flexibility. The software automates the process of selecting the highest level of security offered by an OPC-UA server for the selected Authentication Method, but the user can specify their own Security Policy / Security Mode if they want (see further below at Configuration options)
Expand Down
24 changes: 24 additions & 0 deletions plugin/opcua.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,17 @@ func browse(ctx context.Context, n *opcua.Node, path string, level int, logger *
switch err := attrs[0].Status; err {
case ua.StatusOK:
def.NodeClass = ua.NodeClass(attrs[0].Value.Int())
case ua.StatusBadSecurityModeInsufficient:
return nil, nil
default:
return nil, err
}

switch err := attrs[1].Status; err {
case ua.StatusOK:
def.BrowseName = attrs[1].Value.String()
case ua.StatusBadSecurityModeInsufficient:
return nil, nil
default:
return nil, err
}
Expand All @@ -96,6 +100,8 @@ func browse(ctx context.Context, n *opcua.Node, path string, level int, logger *
def.Description = attrs[2].Value.String()
case ua.StatusBadAttributeIDInvalid:
// ignore
case ua.StatusBadSecurityModeInsufficient:
return nil, nil
default:
return nil, err
}
Expand All @@ -106,6 +112,8 @@ func browse(ctx context.Context, n *opcua.Node, path string, level int, logger *
def.Writable = def.AccessLevel&ua.AccessLevelTypeCurrentWrite == ua.AccessLevelTypeCurrentWrite
case ua.StatusBadAttributeIDInvalid:
// ignore
case ua.StatusBadSecurityModeInsufficient:
return nil, nil
default:
return nil, err
}
Expand Down Expand Up @@ -142,6 +150,8 @@ func browse(ctx context.Context, n *opcua.Node, path string, level int, logger *
}
case ua.StatusBadAttributeIDInvalid:
// ignore
case ua.StatusBadSecurityModeInsufficient:
return nil, nil
default:
return nil, err
}
Expand Down Expand Up @@ -501,6 +511,11 @@ func (g *OPCUAInput) Connect(ctx context.Context) error {
monitoredRequests = append(monitoredRequests, miCreateRequest)
}

if len(nodeList) == 0 {
g.log.Errorf("Did not subscribe to any nodes. This can happen if the nodes that are selected are incompatible with this benthos version. Aborting...")
return fmt.Errorf("no valid nodes selected")
}

res, err := sub.Monitor(ctx, ua.TimestampsToReturnBoth, monitoredRequests...)
if err != nil {
panic(err)
Expand Down Expand Up @@ -569,6 +584,11 @@ func (g *OPCUAInput) createMessageFromValue(variant *ua.Variant, nodeID string)
b = append(b, jsonBytes...)
}

if b == nil {
g.log.Errorf("Could not create benthos message as payload is empty for node %s: %v", nodeID, b)
return nil
}

message := service.NewMessage(b)

re := regexp.MustCompile(`[^a-zA-Z0-9_-]`)
Expand All @@ -590,6 +610,10 @@ func (g *OPCUAInput) ReadBatchPull(ctx context.Context) (service.MessageBatch, s
})
}

if len(g.nodeList) > 100 {
g.log.Warnf("Reading more than 100 nodes with pull method. The request might fail as it can take too much time. Recommendation: use subscribeEnabled: true instead for better performance")
}

req := &ua.ReadRequest{
MaxAge: 2000,
NodesToRead: nodesToRead,
Expand Down
Loading

0 comments on commit d222fdb

Please sign in to comment.