Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server: use DataValue instead of Variant for node attributes #766

Merged
merged 3 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions examples/server/node_server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,12 @@ func main() {
// the parent node in the namespace if applicable.
var3 := server.NewNode(
ua.NewNumericNodeID(nodeNS.ID(), 12345), // you can use whatever node id you want here, whether it's numeric, string, guid, etc...
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("MyBrowseName")),
ua.AttributeIDNodeClass: ua.MustVariant(uint32(ua.NodeClassVariable)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDBrowseName: server.DataValueFromValue(attrs.BrowseName("MyBrowseName")),
ua.AttributeIDNodeClass: server.DataValueFromValue(uint32(ua.NodeClassVariable)),
},
nil,
func() *ua.Variant { return ua.MustVariant(12.34) },
func() *ua.DataValue { return server.DataValueFromValue(12.34) },
)
nodeNS.AddNode(var3)
nns_obj.AddRef(var3, id.HasComponent, true)
Expand All @@ -219,7 +219,7 @@ func main() {
num++

// get the current value of the variable
last_value := var1.Value().Value().(float32)
last_value := var1.Value().Value.Value().(float32)
// and change it
last_value += 1

Expand All @@ -229,7 +229,7 @@ func main() {
SourceTimestamp: time.Now(),
EncodingMask: ua.DataValueValue | ua.DataValueSourceTimestamp,
}
var1.SetAttribute(ua.AttributeIDValue, val)
var1.SetAttribute(ua.AttributeIDValue, &val)

// we also need to let the node namespace know that the value has changed so it can trigger the change notification
// and send the updated value to any subscribed clients.
Expand All @@ -246,7 +246,7 @@ func main() {
for {
changed_id := <-nodeNS.ExternalNotification
node := nodeNS.Node(changed_id)
value := node.Value().Value()
value := node.Value().Value.Value()
log.Printf("%s changed to %v", changed_id.String(), value)
}
}()
Expand Down
29 changes: 12 additions & 17 deletions server/namespace_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,7 @@ func (ns *MapNamespace) Attribute(n *ua.NodeID, a ua.AttributeID) *ua.DataValue
}
}

return &ua.DataValue{
EncodingMask: ua.DataValueServerTimestamp | ua.DataValueStatusCode | ua.DataValueValue,
ServerTimestamp: time.Now(),
Status: ua.StatusOK,
Value: attrval.Value,
}
return attrval.Value

}

Expand Down Expand Up @@ -385,13 +380,13 @@ func (ns *MapNamespace) Objects() *Node {
//reftype := ua.NewTwoByteNodeID(uint8(id.HasComponent)) // folder
n := NewNode(
oid,
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(int32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName(ns.name)),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName(ns.name, ns.name)),
ua.AttributeIDDescription: ua.MustVariant(uint32(ua.NodeClassObject)),
ua.AttributeIDDataType: ua.MustVariant(typedef),
ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(int32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName(ns.name)),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName(ns.name, ns.name)),
ua.AttributeIDDescription: DataValueFromValue(uint32(ua.NodeClassObject)),
ua.AttributeIDDataType: DataValueFromValue(typedef),
ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
},
[]*ua.ReferenceDescription{},
nil,
Expand All @@ -402,10 +397,10 @@ func (ns *MapNamespace) Objects() *Node {
func (ns *MapNamespace) Root() *Node {
n := NewNode(
ua.NewNumericNodeID(ns.ID(), id.RootFolder),
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(int32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName("Root")),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName("Root", "")),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(int32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName("Root")),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName("Root", "")),
},
[]*ua.ReferenceDescription{},
nil,
Expand Down
33 changes: 14 additions & 19 deletions server/namespace_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ func NewNodeNameSpace(srv *Server, name string) *NodeNameSpace {
//reftype := ua.NewTwoByteNodeID(uint8(id.HasComponent)) // folder
objectsNode := NewNode(
oid,
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(uint32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName(ns.name)),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName(ns.name, ns.name)),
ua.AttributeIDDescription: ua.MustVariant(uint32(ua.NodeClassObject)),
ua.AttributeIDDataType: ua.MustVariant(typedef),
ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(uint32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName(ns.name)),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName(ns.name, ns.name)),
ua.AttributeIDDescription: DataValueFromValue(uint32(ua.NodeClassObject)),
ua.AttributeIDDataType: DataValueFromValue(typedef),
ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
},
[]*ua.ReferenceDescription{},
nil,
Expand Down Expand Up @@ -126,12 +126,12 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV

switch attr {
case ua.AttributeIDNodeID:
a = &AttrValue{Value: ua.MustVariant(id)}
a = &AttrValue{Value: DataValueFromValue(id)}
case ua.AttributeIDEventNotifier:
// TODO: this is a hack to force the EventNotifier to false for everything.
// If at some point someone or something needs to use this, this will have to go away and be
// fixed properly.
a = &AttrValue{Value: ua.MustVariant(byte(0))}
a = &AttrValue{Value: DataValueFromValue(byte(0))}
case ua.AttributeIDNodeClass:
a, err = n.Attribute(attr)
if err != nil {
Expand All @@ -142,9 +142,9 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV
}
}
// TODO: we need int32 instead of uint32 here. this isn't the right place to fix it, but it is a bandaid
x, ok := a.Value.Value().(uint32)
x, ok := a.Value.Value.Value().(uint32)
if ok {
a.Value = ua.MustVariant(int32(x))
a.Value.Value = ua.MustVariant(int32(x))
}
default:
a, err = n.Attribute(attr)
Expand All @@ -157,12 +157,7 @@ func (as *NodeNameSpace) Attribute(id *ua.NodeID, attr ua.AttributeID) *ua.DataV
Status: ua.StatusBadAttributeIDInvalid,
}
}
return &ua.DataValue{
EncodingMask: ua.DataValueServerTimestamp | ua.DataValueStatusCode | ua.DataValueValue,
ServerTimestamp: time.Now(),
Status: ua.StatusOK,
Value: a.Value,
}
return a.Value
}

func (as *NodeNameSpace) Node(id *ua.NodeID) *Node {
Expand Down Expand Up @@ -258,12 +253,12 @@ func (as *NodeNameSpace) SetAttribute(id *ua.NodeID, attr ua.AttributeID, val *u

access, err := n.Attribute(ua.AttributeIDUserAccessLevel)
if err == nil {
x := access.Value.Value()
x := access.Value.Value.Value()
_ = x
return ua.StatusBadUserAccessDenied
}

err = n.SetAttribute(attr, *val)
err = n.SetAttribute(attr, val)
if err != nil {
return ua.StatusBadAttributeIDInvalid
}
Expand Down
104 changes: 57 additions & 47 deletions server/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,31 @@ import (
"github.com/gopcua/opcua/ua"
)

type Attributes map[ua.AttributeID]*ua.Variant
type Attributes map[ua.AttributeID]*ua.DataValue

type References []*ua.ReferenceDescription

type ValueFunc func() *ua.Variant
type ValueFunc func() *ua.DataValue

type AttrValue struct {
Value *ua.Variant
Value *ua.DataValue
SourceTimestamp time.Time
}

func NewAttrValue(v *ua.Variant) *AttrValue {
func NewAttrValue(v *ua.DataValue) *AttrValue {
return &AttrValue{Value: v}
}

func DataValueFromValue(val any) *ua.DataValue {

v := ua.MustVariant(val)
return &ua.DataValue{
EncodingMask: ua.DataValueValue,
Value: v,
SourceTimestamp: time.Now(),
}
}

type Node struct {
id *ua.NodeID
attr Attributes
Expand All @@ -48,12 +58,12 @@ func NewFolderNode(nodeID *ua.NodeID, name string) *Node {
typedef := ua.NewNumericExpandedNodeID(0, id.ObjectsFolder)
n := NewNode(
nodeID,
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(uint32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: ua.MustVariant(ua.NodeClassObject),
ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(uint32(ua.NodeClassObject)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: DataValueFromValue(ua.NodeClassObject),
ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
},
[]*ua.ReferenceDescription{{
ReferenceTypeID: reftype,
Expand All @@ -71,36 +81,36 @@ func NewFolderNode(nodeID *ua.NodeID, name string) *Node {

func NewVariableNode(nodeID *ua.NodeID, name string, value any) *Node {
//eoid := ua.NewNumericExpandedNodeID(nodeID.Namespace(), nodeID.IntID())
vf, ok := value.(func() *ua.Variant)
vf, ok := value.(func() *ua.DataValue)
if !ok {
typedef := ua.NewNumericExpandedNodeID(0, id.VariableNode)
n := NewNode(
nodeID,
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(uint32(ua.NodeClassVariable)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: ua.MustVariant(uint32(ua.NodeClassVariable)),
ua.AttributeIDDataType: ua.MustVariant(typedef),
ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(uint32(ua.NodeClassVariable)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: DataValueFromValue(uint32(ua.NodeClassVariable)),
ua.AttributeIDDataType: DataValueFromValue(typedef),
ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
},
[]*ua.ReferenceDescription{},
func() *ua.Variant {
return ua.MustVariant(value)
func() *ua.DataValue {
return DataValueFromValue(value)
},
)
return n
}
typedef := ua.NewNumericExpandedNodeID(0, id.VariableNode)
n := NewNode(
nodeID,
map[ua.AttributeID]*ua.Variant{
ua.AttributeIDNodeClass: ua.MustVariant(uint32(ua.NodeClassVariable)),
ua.AttributeIDBrowseName: ua.MustVariant(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: ua.MustVariant(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: ua.MustVariant(uint32(ua.NodeClassVariable)),
ua.AttributeIDDataType: ua.MustVariant(typedef),
ua.AttributeIDEventNotifier: ua.MustVariant(int16(0)),
map[ua.AttributeID]*ua.DataValue{
ua.AttributeIDNodeClass: DataValueFromValue(uint32(ua.NodeClassVariable)),
ua.AttributeIDBrowseName: DataValueFromValue(attrs.BrowseName(name)),
ua.AttributeIDDisplayName: DataValueFromValue(attrs.DisplayName(name, name)),
ua.AttributeIDDescription: DataValueFromValue(uint32(ua.NodeClassVariable)),
ua.AttributeIDDataType: DataValueFromValue(typedef),
ua.AttributeIDEventNotifier: DataValueFromValue(int16(0)),
},
[]*ua.ReferenceDescription{},
vf,
Expand Down Expand Up @@ -133,7 +143,7 @@ func (n *Node) ID() *ua.NodeID {
return n.id
}

func (n *Node) Value() *ua.Variant {
func (n *Node) Value() *ua.DataValue {
if n.val == nil {
return nil
}
Expand All @@ -156,61 +166,61 @@ func (n *Node) Attribute(id ua.AttributeID) (*AttrValue, error) {
return nil, ua.StatusBadAttributeIDInvalid
}
}
func (n *Node) SetAttribute(id ua.AttributeID, val ua.DataValue) error {
func (n *Node) SetAttribute(id ua.AttributeID, val *ua.DataValue) error {
switch {
case id == ua.AttributeIDValue:

// TODO: probably need to do some type checking here.
// And some permissions tests
n.val = func() *ua.Variant {
return ua.MustVariant(val.Value.Value())
n.val = func() *ua.DataValue {
return val
}

return nil
default:
n.attr[id] = val.Value
n.attr[id] = val
}
return ua.StatusBadNodeAttributesInvalid
}

func (n *Node) BrowseName() *ua.QualifiedName {
v := n.attr[ua.AttributeIDBrowseName]
if v == nil || v.Value() == nil {
if v == nil || v.Value.Value() == nil {
return &ua.QualifiedName{}
}
return v.Value().(*ua.QualifiedName)
return v.Value.Value().(*ua.QualifiedName)
}

func (n *Node) SetBrowseName(s string) {
n.attr[ua.AttributeIDBrowseName] = ua.MustVariant(&ua.QualifiedName{Name: s})
n.attr[ua.AttributeIDBrowseName] = DataValueFromValue(&ua.QualifiedName{Name: s})
}

func (n *Node) DisplayName() *ua.LocalizedText {
v := n.attr[ua.AttributeIDDisplayName]
if v == nil || v.Value() == nil {
if v == nil || v.Value.Value() == nil {
return &ua.LocalizedText{}
}
val := v.Value().(*ua.LocalizedText)
val := v.Value.Value().(*ua.LocalizedText)
val.UpdateMask()
return val
}

func (n *Node) SetDisplayName(text, locale string) {
lt := &ua.LocalizedText{Text: text, Locale: locale}
lt.UpdateMask()
n.attr[ua.AttributeIDDisplayName] = ua.MustVariant(lt)
n.attr[ua.AttributeIDDisplayName] = DataValueFromValue(lt)
}

func (n *Node) Description() *ua.LocalizedText {
v := n.attr[ua.AttributeIDDescription]
if v == nil || v.Value() == nil {
if v == nil || v.Value.Value() == nil {
return &ua.LocalizedText{}
}
return v.Value().(*ua.LocalizedText)
return v.Value.Value().(*ua.LocalizedText)
}

func (n *Node) SetDescription(text, locale string) {
n.attr[ua.AttributeIDDescription] = ua.MustVariant(&ua.LocalizedText{Text: text, Locale: locale})
n.attr[ua.AttributeIDDescription] = DataValueFromValue(&ua.LocalizedText{Text: text, Locale: locale})
}

func (n *Node) DataType() *ua.ExpandedNodeID {
Expand All @@ -219,7 +229,7 @@ func (n *Node) DataType() *ua.ExpandedNodeID {
return ua.NewTwoByteExpandedNodeID(0)
}
v := n.attr[ua.AttributeIDDataType]
if v == nil || v.Value() == nil {
if v == nil || v.Value.Value() == nil {
// if we have a type definition, return that?
for i := range n.refs {
r := n.refs[i]
Expand All @@ -232,21 +242,21 @@ func (n *Node) DataType() *ua.ExpandedNodeID {
}
return ua.NewTwoByteExpandedNodeID(0)
}
return v.Value().(*ua.ExpandedNodeID)
return v.Value.Value().(*ua.ExpandedNodeID)
}

func (n *Node) SetNodeClass(nc ua.NodeClass) {
n.attr[ua.AttributeIDNodeClass] = ua.MustVariant(uint32(nc))
n.attr[ua.AttributeIDNodeClass] = DataValueFromValue(uint32(nc))
}

func (n *Node) NodeClass() ua.NodeClass {
v := n.attr[ua.AttributeIDNodeClass]
if v == nil || v.Value() == nil {
if v == nil || v.Value.Value() == nil {
return ua.NodeClassObject
}
vi32, ok := v.Value().(int32)
vi32, ok := v.Value.Value().(int32)
if !ok {
vui32, ok := v.Value().(uint32)
vui32, ok := v.Value.Value().(uint32)
if !ok {
return ua.NodeClassObject
}
Expand Down
Loading