mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-14 19:29:11 +08:00

* NOISSUE- Add OPC-UA adapter Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * NOISSUE - Add opc-adapter PoC, docker and vendor Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Convert OPC messages to SenML Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add gopcua package Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * lora-adapter typo Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add OPC Reader Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Typo fix Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Typo fix Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Update copyright headers Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Fix reviews Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Fix reviews Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add opc config Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add all opc envars in the config Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Config typo Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add route map Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Use opcua package instead of opc Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Fix OPCUA typo Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Rm MQTT sub Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Move interefaces to root Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Fix revieews and typo Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Update Gopkg.toml Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com> * Add all envars into .env Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
420 lines
9.6 KiB
Go
420 lines
9.6 KiB
Go
// Copyright 2018-2019 opcua authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package ua
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gopcua/opcua/errors"
|
|
)
|
|
|
|
// todo(fs): fix mask
|
|
|
|
// NodeID is an identifier for a node in the address space of an OPC UA Server.
|
|
// The NodeID object encodes all different node id types.
|
|
type NodeID struct {
|
|
mask NodeIDType
|
|
ns uint16
|
|
nid uint32
|
|
bid []byte
|
|
gid *GUID
|
|
}
|
|
|
|
// NewTwoByteNodeID returns a new two byte node id.
|
|
func NewTwoByteNodeID(id uint8) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeTwoByte,
|
|
nid: uint32(id),
|
|
}
|
|
}
|
|
|
|
// NewFourByteNodeID returns a new four byte node id.
|
|
func NewFourByteNodeID(ns uint8, id uint16) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeFourByte,
|
|
ns: uint16(ns),
|
|
nid: uint32(id),
|
|
}
|
|
}
|
|
|
|
// NewNumericNodeID returns a new numeric node id.
|
|
func NewNumericNodeID(ns uint16, id uint32) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeNumeric,
|
|
ns: ns,
|
|
nid: id,
|
|
}
|
|
}
|
|
|
|
// NewStringNodeID returns a new string node id.
|
|
func NewStringNodeID(ns uint16, id string) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeString,
|
|
ns: ns,
|
|
bid: []byte(id),
|
|
}
|
|
}
|
|
|
|
// NewGUIDNodeID returns a new GUID node id.
|
|
func NewGUIDNodeID(ns uint16, id string) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeGUID,
|
|
ns: ns,
|
|
gid: NewGUID(id),
|
|
}
|
|
}
|
|
|
|
// NewByteStringNodeID returns a new byte string node id.
|
|
func NewByteStringNodeID(ns uint16, id []byte) *NodeID {
|
|
return &NodeID{
|
|
mask: NodeIDTypeByteString,
|
|
ns: ns,
|
|
bid: id,
|
|
}
|
|
}
|
|
|
|
// ParseNodeID returns a node id from a string definition of the format
|
|
// 'ns=<namespace>;{s,i,b,g}=<identifier>'.
|
|
//
|
|
// For string node ids the 's=' prefix can be omitted.
|
|
//
|
|
// For numeric ids the smallest possible type which can store the namespace
|
|
// and id value is returned.
|
|
//
|
|
// Namespace URLs 'nsu=' are not supported since they require a lookup.
|
|
//
|
|
func ParseNodeID(s string) (*NodeID, error) {
|
|
if s == "" {
|
|
return NewTwoByteNodeID(0), nil
|
|
}
|
|
|
|
var nsval, idval string
|
|
|
|
p := strings.SplitN(s, ";", 2)
|
|
switch len(p) {
|
|
case 1:
|
|
nsval, idval = "ns=0", p[0]
|
|
case 2:
|
|
nsval, idval = p[0], p[1]
|
|
default:
|
|
return nil, errors.Errorf("invalid node id: %s", s)
|
|
}
|
|
|
|
// parse namespace
|
|
var ns uint16
|
|
switch {
|
|
case strings.HasPrefix(nsval, "nsu="):
|
|
return nil, errors.Errorf("namespace urls are not supported: %s", s)
|
|
|
|
case strings.HasPrefix(nsval, "ns="):
|
|
n, err := strconv.Atoi(nsval[3:])
|
|
if err != nil {
|
|
return nil, errors.Errorf("invalid namespace id: %s", s)
|
|
}
|
|
if n < 0 || n > math.MaxUint16 {
|
|
return nil, errors.Errorf("namespace id out of range (0..65535): %s", s)
|
|
}
|
|
ns = uint16(n)
|
|
|
|
default:
|
|
return nil, errors.Errorf("invalid node id: %s", s)
|
|
}
|
|
|
|
// parse identifier
|
|
switch {
|
|
case strings.HasPrefix(idval, "i="):
|
|
id, err := strconv.ParseUint(idval[2:], 10, 64)
|
|
if err != nil {
|
|
return nil, errors.Errorf("invalid numeric id: %s", s)
|
|
}
|
|
switch {
|
|
case ns == 0 && id < 256:
|
|
return NewTwoByteNodeID(byte(id)), nil
|
|
case ns < 256 && id < math.MaxUint16:
|
|
return NewFourByteNodeID(byte(ns), uint16(id)), nil
|
|
case id < math.MaxUint32:
|
|
return NewNumericNodeID(ns, uint32(id)), nil
|
|
default:
|
|
return nil, errors.Errorf("numeric id out of range (0..2^32-1): %s", s)
|
|
}
|
|
|
|
case strings.HasPrefix(idval, "s="):
|
|
return NewStringNodeID(ns, idval[2:]), nil
|
|
|
|
case strings.HasPrefix(idval, "g="):
|
|
n := NewGUIDNodeID(ns, idval[2:])
|
|
if n == nil || n.StringID() == "" {
|
|
return nil, errors.Errorf("invalid guid node id: %s", s)
|
|
}
|
|
return n, nil
|
|
|
|
case strings.HasPrefix(idval, "b="):
|
|
b, err := base64.StdEncoding.DecodeString(idval[2:])
|
|
if err != nil {
|
|
return nil, errors.Errorf("invalid opaque node id: %s", s)
|
|
}
|
|
return NewByteStringNodeID(ns, b), nil
|
|
|
|
case strings.HasPrefix(idval, "ns="):
|
|
return nil, errors.Errorf("invalid node id: %s", s)
|
|
|
|
default:
|
|
return NewStringNodeID(ns, idval), nil
|
|
}
|
|
}
|
|
|
|
// EncodingMask returns the encoding mask field including the
|
|
// type information and additional flags.
|
|
func (n *NodeID) EncodingMask() NodeIDType {
|
|
return n.mask
|
|
}
|
|
|
|
// Type returns the node id type in EncodingMask.
|
|
func (n *NodeID) Type() NodeIDType {
|
|
return n.mask & NodeIDType(0xf)
|
|
}
|
|
|
|
// URIFlag returns whether the URI flag is set in EncodingMask.
|
|
func (n *NodeID) URIFlag() bool {
|
|
return n.mask&0x80 == 0x80
|
|
}
|
|
|
|
// SetURIFlag sets NamespaceURI flag in EncodingMask.
|
|
func (n *NodeID) SetURIFlag() {
|
|
n.mask |= 0x80
|
|
}
|
|
|
|
// IndexFlag returns whether the Index flag is set in EncodingMask.
|
|
func (n *NodeID) IndexFlag() bool {
|
|
return n.mask&0x40 == 0x40
|
|
}
|
|
|
|
// SetIndexFlag sets NamespaceURI flag in EncodingMask.
|
|
func (n *NodeID) SetIndexFlag() {
|
|
n.mask |= 0x40
|
|
}
|
|
|
|
// Namespace returns the namespace id. For two byte node ids
|
|
// this will always be zero.
|
|
func (n *NodeID) Namespace() uint16 {
|
|
return n.ns
|
|
}
|
|
|
|
// SetNamespace sets the namespace id. It returns an error
|
|
// if the id is not within the range of the node id type.
|
|
func (n *NodeID) SetNamespace(v uint16) error {
|
|
switch n.Type() {
|
|
case NodeIDTypeTwoByte:
|
|
if v != 0 {
|
|
return errors.Errorf("out of range [0..0]: %d", v)
|
|
}
|
|
return nil
|
|
|
|
case NodeIDTypeFourByte:
|
|
if max := uint16(math.MaxUint8); v > max {
|
|
return errors.Errorf("out of range [0..%d]: %d", max, v)
|
|
}
|
|
n.ns = uint16(v)
|
|
return nil
|
|
|
|
default:
|
|
if max := uint16(math.MaxUint16); v > max {
|
|
return errors.Errorf("out of range [0..%d]: %d", max, v)
|
|
}
|
|
n.ns = uint16(v)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// IntID returns the identifier value if the type is
|
|
// TwoByte, FourByte or Numeric. For all other types IntID
|
|
// returns 0.
|
|
func (n *NodeID) IntID() uint32 {
|
|
return n.nid
|
|
}
|
|
|
|
// SetIntID sets the identifier value for two byte, four byte and
|
|
// numeric node ids. It returns an error for other types.
|
|
func (n *NodeID) SetIntID(v uint32) error {
|
|
switch n.Type() {
|
|
case NodeIDTypeTwoByte:
|
|
if max := uint32(math.MaxUint8); v > max {
|
|
return errors.Errorf("out of range [0..%d]: %d", max, v)
|
|
}
|
|
n.nid = uint32(v)
|
|
return nil
|
|
|
|
case NodeIDTypeFourByte:
|
|
if max := uint32(math.MaxUint16); v > max {
|
|
return errors.Errorf("out of range [0..%d]: %d", max, v)
|
|
}
|
|
n.nid = uint32(v)
|
|
return nil
|
|
|
|
case NodeIDTypeNumeric:
|
|
if max := uint32(math.MaxUint32); v > max {
|
|
return errors.Errorf("out of range [0..%d]: %d", max, v)
|
|
}
|
|
n.nid = uint32(v)
|
|
return nil
|
|
|
|
default:
|
|
return errors.Errorf("incompatible node id type")
|
|
}
|
|
}
|
|
|
|
// StringID returns the string value of the identifier
|
|
// for String and GUID NodeIDs, and the base64 encoded
|
|
// value for Opaque types. For all other types StringID
|
|
// returns an empty string.
|
|
func (n *NodeID) StringID() string {
|
|
switch n.Type() {
|
|
case NodeIDTypeGUID:
|
|
if n.gid == nil {
|
|
return ""
|
|
}
|
|
return n.gid.String()
|
|
case NodeIDTypeString:
|
|
return string(n.bid)
|
|
case NodeIDTypeByteString:
|
|
return base64.StdEncoding.EncodeToString(n.bid)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// SetStringID sets the identifier value for string, guid and opaque
|
|
// node ids. It returns an error for other types.
|
|
func (n *NodeID) SetStringID(v string) error {
|
|
switch n.Type() {
|
|
case NodeIDTypeGUID:
|
|
n.gid = NewGUID(v)
|
|
return nil
|
|
|
|
case NodeIDTypeString:
|
|
n.bid = []byte(v)
|
|
return nil
|
|
|
|
case NodeIDTypeByteString:
|
|
b, err := base64.StdEncoding.DecodeString(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.bid = b
|
|
return nil
|
|
|
|
default:
|
|
return errors.Errorf("incompatible node id type")
|
|
}
|
|
}
|
|
|
|
// String returns the string representation of the NodeID
|
|
// in the format described by ParseNodeID.
|
|
func (n *NodeID) String() string {
|
|
switch n.Type() {
|
|
case NodeIDTypeTwoByte:
|
|
return fmt.Sprintf("i=%d", n.nid)
|
|
|
|
case NodeIDTypeFourByte:
|
|
if n.ns == 0 {
|
|
return fmt.Sprintf("i=%d", n.nid)
|
|
}
|
|
return fmt.Sprintf("ns=%d;i=%d", n.ns, n.nid)
|
|
|
|
case NodeIDTypeNumeric:
|
|
if n.ns == 0 {
|
|
return fmt.Sprintf("i=%d", n.nid)
|
|
}
|
|
return fmt.Sprintf("ns=%d;i=%d", n.ns, n.nid)
|
|
|
|
case NodeIDTypeString:
|
|
if n.ns == 0 {
|
|
return fmt.Sprintf("s=%s", n.StringID())
|
|
}
|
|
return fmt.Sprintf("ns=%d;s=%s", n.ns, n.StringID())
|
|
|
|
case NodeIDTypeGUID:
|
|
if n.ns == 0 {
|
|
return fmt.Sprintf("g=%s", n.StringID())
|
|
}
|
|
return fmt.Sprintf("ns=%d;g=%s", n.ns, n.StringID())
|
|
|
|
case NodeIDTypeByteString:
|
|
if n.ns == 0 {
|
|
return fmt.Sprintf("o=%s", n.StringID())
|
|
}
|
|
return fmt.Sprintf("ns=%d;o=%s", n.ns, n.StringID())
|
|
|
|
default:
|
|
panic(fmt.Sprintf("invalid node id type: %d", n.Type()))
|
|
}
|
|
}
|
|
|
|
func (n *NodeID) Decode(b []byte) (int, error) {
|
|
buf := NewBuffer(b)
|
|
|
|
n.mask = NodeIDType(buf.ReadByte())
|
|
typ := n.mask & 0xf
|
|
|
|
switch typ {
|
|
case NodeIDTypeTwoByte:
|
|
n.nid = uint32(buf.ReadByte())
|
|
return buf.Pos(), buf.Error()
|
|
|
|
case NodeIDTypeFourByte:
|
|
n.ns = uint16(buf.ReadByte())
|
|
n.nid = uint32(buf.ReadUint16())
|
|
return buf.Pos(), buf.Error()
|
|
|
|
case NodeIDTypeNumeric:
|
|
n.ns = buf.ReadUint16()
|
|
n.nid = buf.ReadUint32()
|
|
return buf.Pos(), buf.Error()
|
|
|
|
case NodeIDTypeGUID:
|
|
n.ns = buf.ReadUint16()
|
|
n.gid = &GUID{}
|
|
buf.ReadStruct(n.gid)
|
|
return buf.Pos(), buf.Error()
|
|
|
|
case NodeIDTypeByteString, NodeIDTypeString:
|
|
n.ns = buf.ReadUint16()
|
|
n.bid = buf.ReadBytes()
|
|
return buf.Pos(), buf.Error()
|
|
|
|
default:
|
|
return 0, errors.Errorf("invalid node id type %v", typ)
|
|
}
|
|
}
|
|
|
|
func (n *NodeID) Encode() ([]byte, error) {
|
|
buf := NewBuffer(nil)
|
|
buf.WriteByte(byte(n.mask))
|
|
|
|
switch n.Type() {
|
|
case NodeIDTypeTwoByte:
|
|
buf.WriteByte(byte(n.nid))
|
|
case NodeIDTypeFourByte:
|
|
buf.WriteByte(byte(n.ns))
|
|
buf.WriteUint16(uint16(n.nid))
|
|
case NodeIDTypeNumeric:
|
|
buf.WriteUint16(n.ns)
|
|
buf.WriteUint32(n.nid)
|
|
case NodeIDTypeGUID:
|
|
buf.WriteUint16(n.ns)
|
|
buf.WriteStruct(n.gid)
|
|
case NodeIDTypeByteString, NodeIDTypeString:
|
|
buf.WriteUint16(n.ns)
|
|
buf.WriteByteString(n.bid)
|
|
default:
|
|
return nil, errors.Errorf("invalid node id type %v", n.Type())
|
|
}
|
|
return buf.Bytes(), buf.Error()
|
|
}
|