Skip to content

Commit

Permalink
Generalize type registry
Browse files Browse the repository at this point in the history
 * Allow for non-numeric type identifiers.
 * Loosen the restriction on registering a type only once.
  • Loading branch information
magiconair committed Jul 11, 2019
1 parent 32c4f51 commit 878f978
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 49 deletions.
21 changes: 4 additions & 17 deletions examples/udt/udt.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ func main() {
}
defer c.Close()

v, err := c.Node(ua.NewNumericNodeID(4, 2134)).Value()
// v, err := c.Node(ua.NewNumericNodeID(4, 2134)).Value()
v, err := c.Node(ua.NewStringNodeID(3, `"Unit"."Components"."PIT001"."Cmd"`)).Value()
switch {
case err != nil:
log.Fatal(err)
Expand All @@ -80,21 +81,7 @@ type MyUDT struct {
ManOut float32
}

// Only add these methods if you need custom logic for encoding and decoding.
// func (u *MyUDT) Decode(b []byte) (int, error) {
// buf := NewBuffer(b)
// u.AlarmAck = buf.ReadInt32()
// ...
// return buf.Pos(), body.Error()
// }

// func (u *MyUDT) Encode() ([]byte, error) {
// buf := NewBuffer(nil)
// buf.WriteInt32(u.AlarmAck)
// ...
// return buf.Bytes(), buf.Error()
// }

func init() {
ua.RegisterExtensionObject(2038, new(MyUDT))
ua.RegisterExtensionObject(ua.NewStringNodeID(3, `TE_"udtAnaIn_Cmd"`), new(MyUDT))
ua.RegisterExtensionObject(ua.NewNumericNodeID(4, 2038), new(MyUDT))
}
33 changes: 21 additions & 12 deletions ua/extension_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@ var eotypes = NewTypeRegistry()

// init registers known built-in extension objects.
func init() {
RegisterExtensionObject(id.AnonymousIdentityToken_Encoding_DefaultBinary, new(AnonymousIdentityToken))
RegisterExtensionObject(id.UserNameIdentityToken_Encoding_DefaultBinary, new(UserNameIdentityToken))
RegisterExtensionObject(id.X509IdentityToken_Encoding_DefaultBinary, new(X509IdentityToken))
RegisterExtensionObject(id.IssuedIdentityToken_Encoding_DefaultBinary, new(IssuedIdentityToken))
RegisterExtensionObject(id.ServerStatusDataType_Encoding_DefaultBinary, new(ServerStatusDataType))
RegisterExtensionObject(id.DataChangeNotification_Encoding_DefaultBinary, new(DataChangeNotification))
RegisterExtensionObject(id.ReadRawModifiedDetails_Encoding_DefaultBinary, new(ReadRawModifiedDetails))
RegisterExtensionObject(id.HistoryData_Encoding_DefaultBinary, new(HistoryData))
// RegisterExtensionObject(id.AnonymousIdentityToken_Encoding_DefaultBinary, new(AnonymousIdentityToken))
// RegisterExtensionObject(id.UserNameIdentityToken_Encoding_DefaultBinary, new(UserNameIdentityToken))
// RegisterExtensionObject(id.X509IdentityToken_Encoding_DefaultBinary, new(X509IdentityToken))
// RegisterExtensionObject(id.IssuedIdentityToken_Encoding_DefaultBinary, new(IssuedIdentityToken))
// RegisterExtensionObject(id.ServerStatusDataType_Encoding_DefaultBinary, new(ServerStatusDataType))
// RegisterExtensionObject(id.DataChangeNotification_Encoding_DefaultBinary, new(DataChangeNotification))
// RegisterExtensionObject(id.ReadRawModifiedDetails_Encoding_DefaultBinary, new(ReadRawModifiedDetails))
// RegisterExtensionObject(id.HistoryData_Encoding_DefaultBinary, new(HistoryData))
RegisterExtensionObject(NewNumericNodeID(0, id.AnonymousIdentityToken_Encoding_DefaultBinary), new(AnonymousIdentityToken))
RegisterExtensionObject(NewNumericNodeID(0, id.UserNameIdentityToken_Encoding_DefaultBinary), new(UserNameIdentityToken))
RegisterExtensionObject(NewNumericNodeID(0, id.X509IdentityToken_Encoding_DefaultBinary), new(X509IdentityToken))
RegisterExtensionObject(NewNumericNodeID(0, id.IssuedIdentityToken_Encoding_DefaultBinary), new(IssuedIdentityToken))
RegisterExtensionObject(NewNumericNodeID(0, id.ServerStatusDataType_Encoding_DefaultBinary), new(ServerStatusDataType))
RegisterExtensionObject(NewNumericNodeID(0, id.DataChangeNotification_Encoding_DefaultBinary), new(DataChangeNotification))
RegisterExtensionObject(NewNumericNodeID(0, id.ReadRawModifiedDetails_Encoding_DefaultBinary), new(ReadRawModifiedDetails))
RegisterExtensionObject(NewNumericNodeID(0, id.HistoryData_Encoding_DefaultBinary), new(HistoryData))

}

// RegisterExtensionObject registers a new extension object type.
// It panics if the type or the id is already registered.
func RegisterExtensionObject(typeID uint32, v interface{}) {
if err := eotypes.Register(typeID, v); err != nil {
func RegisterExtensionObject(typeID *NodeID, v interface{}) {
if err := eotypes.Register(typeID.String(), v); err != nil {
panic("Extension object " + err.Error())
}
}
Expand Down Expand Up @@ -86,10 +95,10 @@ func (e *ExtensionObject) Decode(b []byte) (int, error) {
return buf.Pos(), body.Error()
}

typeID := e.TypeID.NodeID.IntID()
typeID := e.TypeID.NodeID.String()
e.Value = eotypes.New(typeID)
if e.Value == nil {
return buf.Pos(), fmt.Errorf("invalid extension object with id %d", typeID)
return buf.Pos(), fmt.Errorf("invalid extension object with id %s", typeID)
}

body.ReadStruct(e.Value)
Expand Down
9 changes: 6 additions & 3 deletions ua/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package ua

import (
"fmt"
"strconv"

"github.com/gopcua/opcua/debug"
)
Expand All @@ -16,7 +17,8 @@ var svcreg = NewTypeRegistry()
// RegisterService registers a new service object type.
// It panics if the type or the id is already registered.
func RegisterService(typeID uint16, v interface{}) {
if err := svcreg.Register(uint32(typeID), v); err != nil {
id := strconv.Itoa(int(typeID))
if err := svcreg.Register(id, v); err != nil {
panic("Service " + err.Error())
}
}
Expand All @@ -25,7 +27,8 @@ func RegisterService(typeID uint16, v interface{}) {
// registered with RegisterService. If the service object is not
// known the function returns 0.
func ServiceTypeID(v interface{}) uint16 {
return uint16(svcreg.Lookup(v))
n, _ := strconv.Atoi(svcreg.Lookup(v))
return uint16(n)
}

func DecodeService(b []byte) (*ExpandedNodeID, interface{}, error) {
Expand All @@ -36,7 +39,7 @@ func DecodeService(b []byte) (*ExpandedNodeID, interface{}, error) {
}
b = b[n:]

v := svcreg.New(uint32(typeID.NodeID.IntID()))
v := svcreg.New(strconv.Itoa(int(typeID.NodeID.IntID())))
if v == nil {
return nil, nil, StatusBadServiceUnsupported
}
Expand Down
33 changes: 16 additions & 17 deletions ua/typereg.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ import (
"sync"
)

// TypeRegistry maps numeric ids to Go types.
// TypeRegistry provides a map of identifiers to Go types.
// The implementation is safe for concurrent use.
type TypeRegistry struct {
mu sync.RWMutex
types map[uint32]reflect.Type
ids map[reflect.Type]uint32
types map[string]reflect.Type
ids map[reflect.Type]string
}

// NewTypeRegistry returns a new type registry.
func NewTypeRegistry() *TypeRegistry {
return &TypeRegistry{
types: make(map[uint32]reflect.Type),
ids: make(map[reflect.Type]uint32),
types: make(map[string]reflect.Type),
ids: make(map[reflect.Type]string),
}
}

// New returns a new instance of the type with the given id.
// If the id is not known the function returns nil.
func (r *TypeRegistry) New(id uint32) interface{} {
func (r *TypeRegistry) New(id string) interface{} {
r.mu.RLock()
defer r.mu.RUnlock()

Expand All @@ -39,31 +39,30 @@ func (r *TypeRegistry) New(id uint32) interface{} {
return reflect.New(typ.Elem()).Interface()
}

// Lookup returns the id of the type of v or zero if the
// type is not registered.
func (r *TypeRegistry) Lookup(v interface{}) uint32 {
// Lookup returns the id of the type of v or an empty string if the type is not
// registered. If the type was registered multiple times the first
// registered id for this type is returned.
func (r *TypeRegistry) Lookup(v interface{}) string {
r.mu.RLock()
defer r.mu.RUnlock()

return r.ids[reflect.TypeOf(v)]
}

// Register adds a new type to the registry. If either the type
// or the id is already registered the function returns an error.
func (r *TypeRegistry) Register(id uint32, v interface{}) error {
// Register adds a new type to the registry.
// If the id is already registered the function returns an error.
func (r *TypeRegistry) Register(id string, v interface{}) error {
r.mu.Lock()
defer r.mu.Unlock()

typ := reflect.TypeOf(v)

if r.types[id] != nil {
return fmt.Errorf("%d is already registered", id)
return fmt.Errorf("%s is already registered", id)
}
r.types[id] = typ

if _, ok := r.ids[typ]; ok {
return fmt.Errorf("%T is already registered", v)
if _, exists := r.ids[typ]; !exists {
r.ids[typ] = id
}
r.ids[typ] = id
return nil
}

0 comments on commit 878f978

Please sign in to comment.