blob: 0658803e1f68903f3a3a0aa1b0e19df131ded2f0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package test_network
import (
"fmt"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/apache/plc4x/plc4go/internal/bacnetip"
. "github.com/apache/plc4x/plc4go/internal/bacnetip/constructors"
"github.com/apache/plc4x/plc4go/internal/bacnetip/tests"
)
type _NetworkServiceElement struct {
*bacnetip.NetworkServiceElement
}
func new_NetworkServiceElement(localLog zerolog.Logger) (*_NetworkServiceElement, error) {
i := &_NetworkServiceElement{}
// This class turns off the deferred startup function call that broadcasts
// I-Am-Router-To-Network and Network-Number-Is messages.
var err error
i.NetworkServiceElement, err = bacnetip.NewNetworkServiceElement(localLog, bacnetip.WithNetworkServiceElementStartupDisabled(true))
if err != nil {
return nil, errors.Wrap(err, "error creating network service element")
}
return i, nil
}
type NPDUCodec struct {
*bacnetip.Client
*bacnetip.Server
log zerolog.Logger
}
func NewNPDUCodec(localLog zerolog.Logger) (*NPDUCodec, error) {
n := &NPDUCodec{
log: localLog,
}
var err error
n.Client, err = bacnetip.NewClient(localLog, n)
if err != nil {
return nil, errors.Wrap(err, "error creating client")
}
n.Server, err = bacnetip.NewServer(localLog, n)
if err != nil {
return nil, errors.Wrap(err, "error creating client")
}
return n, nil
}
func (n *NPDUCodec) Indication(args bacnetip.Args, kwargs bacnetip.KWArgs) error {
n.log.Debug().Stringer("Args", args).Stringer("KWArgs", kwargs).Msg("Indication")
npdu := args.Get0NPDU()
// first a generic _NPDU
xpdu, err := bacnetip.NewNPDU(nil, nil)
if err != nil {
return errors.Wrap(err, "error creating NPDU")
}
if err := npdu.Encode(xpdu); err != nil {
return errors.Wrap(err, "error encoding xpdu")
}
// Now as a vanilla PDU
ypdu := bacnetip.NewPDU(bacnetip.NewMessageBridge())
if err := xpdu.Encode(ypdu); err != nil {
return errors.Wrap(err, "error decoding xpdu")
}
n.log.Debug().Stringer("ypdu", ypdu).Msg("encoded")
// send it downstream
return n.Request(bacnetip.NewArgs(ypdu), bacnetip.NoKWArgs)
}
func (n *NPDUCodec) Confirmation(args bacnetip.Args, kwargs bacnetip.KWArgs) error {
n.log.Debug().Stringer("Args", args).Stringer("KWArgs", kwargs).Msg("Indication")
pdu := args.Get0PDU()
// decode as generic _NPDU
xpdu, err := bacnetip.NewNPDU(nil, nil)
if err != nil {
return errors.Wrap(err, "error creating NPDU")
}
if err := xpdu.Decode(pdu); err != nil {
return errors.Wrap(err, "error decoding xpdu")
}
// drop application layer message
if xpdu.GetNPDUNetMessage() == nil {
n.log.Trace().Msg("drop message")
return nil
}
// do a deeper decode of the _NPDU
ypdu := bacnetip.NPDUTypes[*xpdu.GetNPDUNetMessage()]()
if err := ypdu.Decode(xpdu); err != nil {
return errors.Wrap(err, "error decoding ypdu")
}
return n.Response(bacnetip.NewArgs(ypdu), bacnetip.NoKWArgs)
}
func (n *NPDUCodec) String() string {
return "NPDUCodec"
}
type SnifferStateMachine struct {
*tests.ClientStateMachine
name string
address *bacnetip.Address
node *bacnetip.Node
log zerolog.Logger
}
func NewSnifferStateMachine(localLog zerolog.Logger, address string, vlan *bacnetip.Network) (*SnifferStateMachine, error) {
s := &SnifferStateMachine{
log: localLog,
}
machine, err := tests.NewClientStateMachine(localLog, tests.WithClientStateMachineName(address))
if err != nil {
return nil, errors.Wrap(err, "error building client state machine")
}
s.ClientStateMachine = machine
// save the name and address
s.name = address
s.address, err = bacnetip.NewAddress(localLog, address)
if err != nil {
return nil, errors.Wrap(err, "error creating address")
}
// create a promiscuous node, added to the network
s.node, err = bacnetip.NewNode(s.log, s.address, bacnetip.WithNodePromiscuous(true), bacnetip.WithNodeLan(vlan))
if err != nil {
return nil, errors.Wrap(err, "error creating node")
}
s.log.Debug().Stringer("node", s.node).Msg("node")
// bind the stack together
if err := bacnetip.Bind(localLog, s, s.node); err != nil {
return nil, errors.Wrap(err, "error binding")
}
return s, nil
}
func (s *SnifferStateMachine) String() string {
return fmt.Sprintf("SnifferStateMachine(%s)", s.name)
}
type NetworkLayerStateMachine struct {
*tests.ClientStateMachine
name string
address *bacnetip.Address
log zerolog.Logger
codec *NPDUCodec
node *bacnetip.Node
}
func NewNetworkLayerStateMachine(localLog zerolog.Logger, address string, vlan *bacnetip.Network) (*NetworkLayerStateMachine, error) {
n := &NetworkLayerStateMachine{
log: localLog,
}
// save the name and address
n.name = fmt.Sprintf("app @ %s", address)
n.address = Address(address)
var err error
n.ClientStateMachine, err = tests.NewClientStateMachine(localLog, tests.WithClientStateMachineName(n.name))
if err != nil {
return nil, errors.Wrap(err, "error building client state machine")
}
// create a network layer encoder/decoder
n.codec, err = NewNPDUCodec(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating codec")
}
n.log.Debug().Stringer("codec", n.codec).Msg("codec")
// create a node, added to the network
n.node, err = bacnetip.NewNode(localLog, n.address, bacnetip.WithNodeLan(vlan))
if err != nil {
return nil, errors.Wrap(err, "error creating node")
}
n.log.Debug().Stringer("node", n.node).Msg("node")
// bind this to the node
if err := bacnetip.Bind(localLog, n, n.codec, n.node); err != nil {
return nil, errors.Wrap(err, "error binding")
}
return n, nil
}
type RouterNode struct {
nsap *bacnetip.NetworkServiceAccessPoint
nse *_NetworkServiceElement
log zerolog.Logger
}
func NewRouterNode(localLog zerolog.Logger) (*RouterNode, error) {
r := &RouterNode{log: localLog}
var err error
// a network service access point will be needed
r.nsap, err = bacnetip.NewNetworkServiceAccessPoint(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating network service access point")
}
// give the NSAP a generic network layer service element
r.nse, err = new_NetworkServiceElement(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating network service element")
}
err = bacnetip.Bind(localLog, r.nse, r.nsap)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
return r, nil
}
func (r *RouterNode) AddNetwork(address string, vlan *bacnetip.Network, net uint16) error {
r.log.Debug().Str("address", address).Stringer("vlan", vlan).Uint16("net", net).Msg("AddNetwork")
// convert the address to an Address
addr := Address(address)
// create a node, add to the network
node, err := bacnetip.NewNode(r.log, addr, bacnetip.WithNodeLan(vlan))
if err != nil {
return errors.Wrap(err, "error creating node")
}
// bind the BIP stack to the local network
return r.nsap.Bind(node, &net, addr)
}
func (r *RouterNode) String() string {
return fmt.Sprintf("RouterNode")
}
type RouterStateMachine struct {
*RouterNode
tests.StateMachine
}
func NewRouterStateMachine(localLog zerolog.Logger) (*RouterStateMachine, error) {
r := &RouterStateMachine{}
var err error
r.RouterNode, err = NewRouterNode(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating router node")
}
var initFunc func()
r.StateMachine, initFunc = tests.NewStateMachine(localLog, r)
initFunc()
return r, nil
}
type TestDeviceObject struct {
*bacnetip.LocalDeviceObject
}
type ApplicationLayerStateMachine struct {
*bacnetip.ApplicationServiceElement
*tests.ClientStateMachine
name string
address *bacnetip.Address
asap *bacnetip.ApplicationServiceAccessPoint
smap *bacnetip.StateMachineAccessPoint
nsap *bacnetip.NetworkServiceAccessPoint
nse *_NetworkServiceElement
node *bacnetip.Node
log zerolog.Logger
}
func NewApplicationLayerStateMachine(localLog zerolog.Logger, address string, vlan *bacnetip.IPNetwork) (*ApplicationLayerStateMachine, error) {
a := &ApplicationLayerStateMachine{
log: localLog,
}
// save the name and address
a.name = fmt.Sprintf("app @ %s", address)
a.address = Address(address)
// build a local device object
localDevice := TestDeviceObject{
&bacnetip.LocalDeviceObject{
ObjectName: a.name,
ObjectIdentifier: "device:" + address,
VendorIdentifier: 999,
},
}
a.log.Debug().Stringer("address", a.address).Msg("address")
var err error
// continue with initialization
a.ApplicationServiceElement, err = bacnetip.NewApplicationServiceElement(a.log, a)
if err != nil {
return nil, errors.Wrap(err, "error creating application service")
}
a.ClientStateMachine, err = tests.NewClientStateMachine(a.log, tests.WithClientStateMachineName(localDevice.ObjectName))
if err != nil {
return nil, errors.Wrap(err, "error building client state machine")
}
// include a application decoder
a.asap, err = bacnetip.NewApplicationServiceAccessPoint(a.log)
if err != nil {
return nil, errors.Wrap(err, "error creating application service access point")
}
// pass the device object to the state machine access point so it
// can know if it should support segmentation
// the segmentation state machines need access to some device
// information cache, usually shared with the application
a.smap, err = bacnetip.NewStateMachineAccessPoint(a.log, localDevice.LocalDeviceObject, bacnetip.WithStateMachineAccessPointDeviceInfoCache(bacnetip.NewDeviceInfoCache(a.log))) // TODO: this is not quite right as we unwrap here
if err != nil {
return nil, errors.Wrap(err, "error creating state machine access point")
}
// a network service access point will be needed
a.nsap, err = bacnetip.NewNetworkServiceAccessPoint(a.log)
if err != nil {
return nil, errors.Wrap(err, "error creating network service access point")
}
// give the NSAP a generic network layer service element
a.nse, err = new_NetworkServiceElement(a.log)
if err != nil {
return nil, errors.Wrap(err, "error creating network service element")
}
err = bacnetip.Bind(a.log, a.nse, a.nsap)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
// bind the top layers
err = bacnetip.Bind(a.log, a, a.asap, a.smap, a.nsap)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
// create a node, added to the network
a.node, err = bacnetip.NewNode(a.log, a.address, bacnetip.WithNodeLan(vlan))
if err != nil {
return nil, errors.Wrap(err, "error creating node")
}
a.log.Debug().Stringer("node", a.node).Msg("node")
// bind the stack to the local network
err = a.nsap.Bind(a.node, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
return a, nil
}
func (a *ApplicationLayerStateMachine) Indication(args bacnetip.Args, kwargs bacnetip.KWArgs) error {
a.log.Debug().Stringer("Args", args).Stringer("KWArgs", kwargs).Msg("Indication")
return a.Receive(args, bacnetip.NoKWArgs)
}
func (a *ApplicationLayerStateMachine) Confirmation(args bacnetip.Args, kwargs bacnetip.KWArgs) error {
a.log.Debug().Stringer("Args", args).Stringer("KWArgs", kwargs).Msg("Confirmation")
return a.Receive(args, bacnetip.NoKWArgs)
}
type ApplicationNode struct {
*bacnetip.Application
*bacnetip.WhoIsIAmServices
*bacnetip.ReadWritePropertyServices
name string
address *bacnetip.Address
asap *bacnetip.ApplicationServiceAccessPoint
smap *bacnetip.StateMachineAccessPoint
nsap *bacnetip.NetworkServiceAccessPoint
nse *_NetworkServiceElement
node *bacnetip.Node
log zerolog.Logger
}
func NewApplicationNode(localLog zerolog.Logger, address string, vlan *bacnetip.IPNetwork) (*ApplicationNode, error) {
a := &ApplicationNode{
log: localLog,
}
// build a name, save the address
a.name = fmt.Sprintf("app @ %s", address)
a.address = Address(address)
// build a local device object
localDevice := &TestDeviceObject{
LocalDeviceObject: &bacnetip.LocalDeviceObject{
ObjectName: a.name,
ObjectIdentifier: "device:999",
VendorIdentifier: 999,
},
}
var err error
// continue with initialization
a.Application, err = bacnetip.NewApplication(localLog, localDevice.LocalDeviceObject) //TODO: this is a indirection that wasn't intended... we don't use the annotation yet so that might be fine
if err != nil {
return nil, errors.Wrap(err, "error building application")
}
// include a application decoder
a.asap, err = bacnetip.NewApplicationServiceAccessPoint(localLog)
if err != nil {
return nil, errors.Wrap(err, "error building application service access point")
}
// pass the device object to the state machine access point so it
// can know if it should support segmentation
// the segmentation state machines need access to the same device
// information cache as the application
a.smap, err = bacnetip.NewStateMachineAccessPoint(localLog, localDevice.LocalDeviceObject, bacnetip.WithStateMachineAccessPointDeviceInfoCache(a.GetDeviceInfoCache())) //TODO: this is a indirection that wasn't intended... we don't use the annotation yet so that might be fine
if err != nil {
return nil, errors.Wrap(err, "error building state machine access point")
}
// a network service access point will be needed
a.nsap, err = bacnetip.NewNetworkServiceAccessPoint(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating network service access point")
}
// give the NSAP a generic network layer service element
a.nse, err = new_NetworkServiceElement(localLog)
if err != nil {
return nil, errors.Wrap(err, "error creating network service element")
}
err = bacnetip.Bind(localLog, a.nse, a.nsap)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
// bind the top layers
err = bacnetip.Bind(localLog, a, a.asap, a.smap, a.nsap)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
// create a node, added to the network
a.node, err = bacnetip.NewNode(a.log, a.address, bacnetip.WithNodeLan(vlan))
if err != nil {
return nil, errors.Wrap(err, "error creating node")
}
// bind the stack to the local network
err = a.nsap.Bind(a.node, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "error binding")
}
return a, nil
}
func xtob(s string) []byte {
bytes, err := bacnetip.Xtob(s)
if err != nil {
panic(err)
}
return bytes
}