1
0
mirror of https://github.com/JoelBender/bacpypes synced 2025-10-05 22:18:16 +08:00

refactor: Remove tutorials/WhoIsIAm.py

This removes the Tutorial code and updates the old samples/WhoIsIAm.py to match it.
This commit is contained in:
Richard Littauer 2020-04-28 18:13:47 -04:00
parent d832f84319
commit 9ec89dbf11
4 changed files with 168 additions and 370 deletions

View File

@ -3,25 +3,25 @@
Getting Started
===============
Ah, so you are interested in getting started with BACnet and Python. Welcome
to BACpypes, I hope you enjoy your journey. This tutorial starts with
just enough of the basics of BACnet to get a workstation communicating with
another device. We will cover installing the library, downloading and
Ah, so you are interested in getting started with BACnet and Python. Welcome
to BACpypes, I hope you enjoy your journey. This tutorial starts with
just enough of the basics of BACnet to get a workstation communicating with
another device. We will cover installing the library, downloading and
configuring the samples applications.
Basic Assumptions
-----------------
I will assume you are a software developer and it is your job to communicate
with a device from another company that uses BACnet. Your employer has
given you a test device and purchased a copy of the BACnet standard. I will
I will assume you are a software developer and it is your job to communicate
with a device from another company that uses BACnet. Your employer has
given you a test device and purchased a copy of the BACnet standard. I will
need...
- a development workstation running some flavor of Linux or Windows, complete with
- a development workstation running some flavor of Linux or Windows, complete with
the latest version of Python (2.7 or >3.4) and
`setup tools <https://pypi.python.org/pypi/setuptools#unix-based-systems-including-mac-os-x>`_.
- a small Ethernet hub into which you can plug both your workstation and your
- a small Ethernet hub into which you can plug both your workstation and your
mysterious BACnet device, so you won't be distracted by lots of other network traffic.
- a BACnetIP/BACnet-MSTP Router if your mysterious device is an MSTP device (BACpypes is
@ -33,7 +33,7 @@ need...
`Anaconda <https://www.continuum.io/downloads>`_ or Enthought
`Canopy <https://www.enthought.com/products/canopy/>`_.
Before getting this test environment set up and while you are still connected
Before getting this test environment set up and while you are still connected
to the internet, install the BACpypes library::
$ sudo easy_install bacpypes
@ -42,7 +42,7 @@ or::
$ sudo pip install bacpypes
And while you are at it, get a copy of the BACpypes project from GitHub. It
And while you are at it, get a copy of the BACpypes project from GitHub. It
contains the library source code, sample code, and this documentation. Install
the `Git <https://en.wikipedia.org/wiki/Git>`_ software from
`here <https://git-scm.com/downloads>`_, then make a local copy of the
@ -50,11 +50,11 @@ repository by cloning it::
$ git clone https://github.com/JoelBender/bacpypes.git
No protocol analysis workbench would be complete without an installed
No protocol analysis workbench would be complete without an installed
copy of `Wireshark <http://www.wireshark.org/>`_::
$ sudo apt-get install wireshark
or if you use Windows, `download it here <https://www.wireshark.org/download.html>`_.
.. caution::
@ -67,15 +67,15 @@ or if you use Windows, `download it here <https://www.wireshark.org/download.htm
Configuring the Workstation
---------------------------
The mystery BACnet device you have is going to come with some configuration
information by default and sometimes it is easier to set up the test
The mystery BACnet device you have is going to come with some configuration
information by default and sometimes it is easier to set up the test
environment with my set of assumptions than come up with a fresh set
from scratch.
*IP Address*
The device will probably come with an IP address, let's assume that it
The device will probably come with an IP address, let's assume that it
is 192.168.0.10, subnet mask 255.255.0.0, gateway address 192.168.0.1.
You are going to be joining the same network, so pick 192.168.0.11
You are going to be joining the same network, so pick 192.168.0.11
for your workstation address and use the same subnet mask 255.255.0.0.
If working with MSTP devices, base your workstation address on the address
@ -84,45 +84,45 @@ from scratch.
*Network Number*
If working with a BACnetIP router and an MSTP device, you will need to know
the network number configured inside the router. Every BACnet network **must**
have a unique numeric identifier. You will often see the magical number **2000**
have a unique numeric identifier. You will often see the magical number **2000**
but you can choose anything between 1 to 0xFFFE.
*Device Identifier*
Every BACnet device on a BACnet network **must** have a unique numeric
identifier. This number is a 22-bit unsigned non-zero value.
It is critical this identifier be unique. Most large customers will have
Every BACnet device on a BACnet network **must** have a unique numeric
identifier. This number is a 22-bit unsigned non-zero value.
It is critical this identifier be unique. Most large customers will have
someone or some group responsible for maintaining device identifiers across the
site. Keep track of the device identifier for the test device. Let's
assume that this device is **1000** and you are going to pick **1001**
site. Keep track of the device identifier for the test device. Let's
assume that this device is **1000** and you are going to pick **1001**
for your workstation.
*Device Name*
Every BACnet device on a BACnet network should also have a unique name, which
is a character string. There is nothing on a BACnet network that
Every BACnet device on a BACnet network should also have a unique name, which
is a character string. There is nothing on a BACnet network that
enforces this uniqueness, but it is a real headache for integrators
when it isn't followed. You will need to pick a name for your
workstation. My collegues and I use star names, so in the sample
when it isn't followed. You will need to pick a name for your
workstation. My collegues and I use star names, so in the sample
configuration files you will see the name "Betelgeuse". An actual customer's
site will use a more formal (but less fun) naming convention.
site will use a more formal (but less fun) naming convention.
There are a few more configuration values that you will need, but
you won't need to change the values in the sample configuration file
you won't need to change the values in the sample configuration file
until you get deeper into the protocol.
*Maximum APDU Length Accepted*
BACnet works on lots of different types of networks, from high
speed Ethernet to "slower" and "cheaper" ARCNET or MS/TP (a
BACnet works on lots of different types of networks, from high
speed Ethernet to "slower" and "cheaper" ARCNET or MS/TP (a
serial bus protocol used for a field bus defined by BACnet).
For devices to exchange messages they need to know the maximum
size message the other device can handle.
*Segmentation Supported*
A vast majority of BACnet communications traffic fits in one
message, but there are times when larger messages are
convenient and more efficient. Segmentation allows larger
A vast majority of BACnet communications traffic fits in one
message, but there are times when larger messages are
convenient and more efficient. Segmentation allows larger
messages to be broken up into segments and spliced back together.
It is not unusual for "low power" field devices to not
It is not unusual for "low power" field devices to not
support segmentation.
There are other configuration parameters in the INI file that are
@ -132,24 +132,24 @@ also used by other applications, just leave them alone for now.
Updating the INI File
~~~~~~~~~~~~~~~~~~~~~
Now that you know what these values are going to be, you can
configure the BACnet portion of your workstation. Change into the
Now that you know what these values are going to be, you can
configure the BACnet portion of your workstation. Change into the
bacpypes directory that you checked out earlier, make a copy
of the sample configuration file, and edit it for your site::
$ cd bacpypes
$ cp BACpypes~.ini BACpypes.ini
.. tip::
.. tip::
The sample applications are going to look for this file.
You can direct the applications to use other INI files on the command line, so it is
You can direct the applications to use other INI files on the command line, so it is
simple to keep multiple configurations.
At some point you will probably running both "client" and "server"
applications on your workstation, so you will want separate
configuration files for them. Keep in mind that BACnet devices
communicate as peers, so it is not unusual for an application to
At some point you will probably running both "client" and "server"
applications on your workstation, so you will want separate
configuration files for them. Keep in mind that BACnet devices
communicate as peers, so it is not unusual for an application to
act as both a client and a server at the same time.
A typical BACpypes.ini file contains::
@ -170,41 +170,41 @@ A typical BACpypes.ini file contains::
UDP Communications Issues
-------------------------
BACnet devices communicate using UDP rather than TCP. This is so
devices do not need to implement a full IP stack (although
BACnet devices communicate using UDP rather than TCP. This is so
devices do not need to implement a full IP stack (although
many of them do because they support multiple protocols, including
having embedded web servers).
There are two types of UDP messages; *unicast* which is a message
from one specific IP address (and port) to another device's IP address
(and port); and *broadcast* messages which are sent by one device
and received and processed by all other devices that are listening
on that port. BACnet uses both types of messages and your workstation
There are two types of UDP messages; *unicast* which is a message
from one specific IP address (and port) to another device's IP address
(and port); and *broadcast* messages which are sent by one device
and received and processed by all other devices that are listening
on that port. BACnet uses both types of messages and your workstation
will need to receive both types.
The BACpypes.ini file has an *address* parameter which is an IP
The BACpypes.ini file has an *address* parameter which is an IP
address in CIDR notation and can be followed by a port number. For
example, **192.168.0.11/16** specifies both the IP address and the
number of bits in the network portion, which in turn implies a
subnet mask, in this case **255.255.0.0**. Unicast messages will
number of bits in the network portion, which in turn implies a
subnet mask, in this case **255.255.0.0**. Unicast messages will
be sent to the IP address, and broadcast messages will be sent to
the broadcast address **192.168.255.255** which is the network
portion of the address with all 1's in the host portion. In this example,
the broadcast address **192.168.255.255** which is the network
portion of the address with all 1's in the host portion. In this example,
the default port 47808 (0xBAC0) is used but you could provide and different
one, **192.168.0.11:47809/16**.
To receive both unicast and broadcast addresses, BACpypes
opens two sockets, one for unicast traffic and one that only listens
To receive both unicast and broadcast addresses, BACpypes
opens two sockets, one for unicast traffic and one that only listens
for broadcast messages. The operating system will typically not allow two
applications to open the same socket at the same time
so to run two BACnet applciations at
applications to open the same socket at the same time
so to run two BACnet applciations at
the same time they need to be configured with different ports.
.. note::
The BACnet protocol has been assigned port 47808 (hex 0xBAC0) by
by the `Internet Assigned Numbers Authority <https://www.iana.org/>`_, and sequentially
higher numbers are used in many applications (i.e. 47809, 47810,...).
The BACnet protocol has been assigned port 47808 (hex 0xBAC0) by
by the `Internet Assigned Numbers Authority <https://www.iana.org/>`_, and sequentially
higher numbers are used in many applications (i.e. 47809, 47810,...).
There are some BACnet routing and networking issues related to using these higher unoffical
ports, but that is a topic for another tutorial.
@ -213,38 +213,38 @@ Starting An Application
-----------------------
The simplest BACpypes sample application is the **WhoIsIAm.py**
application. It sends out Who-Is and I-Am messages and
application. It sends out Who-Is and I-Am messages and
displays the results it receives. What are these things?
As mentioned before, BACnet has unique device identifiers and
most applications use these identifiers in their configuration
As mentioned before, BACnet has unique device identifiers and
most applications use these identifiers in their configuration
to know who their peers are. Once these identifiers are given
to a device they typically do not change, even as the network
topology changes.
BACnet devices use the Who-Is request to translate device
identifiers into network addresses. This is very similar to
a decentralized DNS service, but the names are unsigned
integers. The request is broadcast on the network and the
BACnet devices use the Who-Is request to translate device
identifiers into network addresses. This is very similar to
a decentralized DNS service, but the names are unsigned
integers. The request is broadcast on the network and the
client waits around to listen for I-Am messages. The source
address of the I-Am response is "bound" to the device identifier
address of the I-Am response is "bound" to the device identifier
and most communications are unicast thereafter.
First, start up Wireshark on your workstation and a capture
First, start up Wireshark on your workstation and a capture
session with a BACnet capture filter::
udp and port 47808
You might start seeing BACnet traffic from your test device,
and if you wait to power it on after starting your capture
you should see at least a broadcast I-Am message. By looking
in the I-Am packet decoding you will see some of its
configuration parameters that should match what you expected
You might start seeing BACnet traffic from your test device,
and if you wait to power it on after starting your capture
you should see at least a broadcast I-Am message. By looking
in the I-Am packet decoding you will see some of its
configuration parameters that should match what you expected
them to be.
Now start the simplest tutorial application::
$ python samples/Tutorial/WhoIsIAm.py
$ python samples/WhoIsIAm.py
.. note::
@ -272,7 +272,7 @@ BACnet communications traffic. Generate the basic I-Am message::
> iam
You should see Wireshark capture your I-Am message containing your configuration
You should see Wireshark capture your I-Am message containing your configuration
parameters. This is a "global broadcast" message. Your test device will see
it but since your test device probably isn't looking for you, it will not
respond to the message.
@ -281,27 +281,27 @@ respond to the message.
Binding to the Test Device
--------------------------
Next we want to confirm that your workstation can receive the
messages the test device sends out. We do this by generating a
generic Who-Is request. The request will be "unconstrained", meaning
Next we want to confirm that your workstation can receive the
messages the test device sends out. We do this by generating a
generic Who-Is request. The request will be "unconstrained", meaning
every device that hears the message will respond with their corresponding
I-Am messages.
I-Am messages.
.. caution::
.. caution::
Generating **unconstrained** Who-Is requests on a large network will create
a LOT of traffic, which can lead to network problems caused by the resulting
Generating **unconstrained** Who-Is requests on a large network will create
a LOT of traffic, which can lead to network problems caused by the resulting
flood of messages.
To generate the Who-Is request::
> whois
You should see the Who-Is request captured in Wireshark along with the I-Am
response from your test device, and then the details of the response displayed
You should see the Who-Is request captured in Wireshark along with the I-Am
response from your test device, and then the details of the response displayed
on the workstation console.::
> whois
> whois
> pduSource = <RemoteStation 50009:9>
iAmDeviceIdentifier = ('device', 1000)
maxAPDULengthAccepted = 480
@ -309,13 +309,13 @@ on the workstation console.::
vendorID = 8
There are a few different forms of the *whois* command supported by this
There are a few different forms of the *whois* command supported by this
simple application. You can see these with the help command::
> help whois
whois [ <addr>] [ <lolimit> <hilimit> ]
This is like a BNF syntax, the **whois** command is optionally
This is like a BNF syntax, the **whois** command is optionally
followed by a BACnet device address, and then optionally followed by a
low (address) limit and high (address) limit. The most common use of the Who-Is
request is to look for a specific device given its device
@ -329,9 +329,9 @@ building as a group::
> whois 203000 203099
Every once in a while a contractor might install a BACnet
Every once in a while a contractor might install a BACnet
device that hasn't been properly configured. Assuming that
it has an IP address, you can send an **unconstrained Who-Is** request
it has an IP address, you can send an **unconstrained Who-Is** request
to the specific device and hope that it responds::
> whois 192.168.0.10
@ -349,9 +349,9 @@ but that is a subject of an other tutorial.
What's Next
-----------
The next tutorial describes the different ways this
The next tutorial describes the different ways this
application can be run, and what the commands can tell you
about how it is working. All of the "console" applications
(i.e. those that prompt for commands) use the same basic
about how it is working. All of the "console" applications
(i.e. those that prompt for commands) use the same basic
commands and work the same way.

View File

@ -3,9 +3,9 @@
Running BACpypes Applications
=============================
All BACpypes sample applications have the same basic set of command line
options so it is easy to move between applications, turn debugging on and
and use different configurations. There may be additional options and
All BACpypes sample applications have the same basic set of command line
options so it is easy to move between applications, turn debugging on and
and use different configurations. There may be additional options and
command parameters than just the ones described in this section.
Getting Help
@ -14,7 +14,7 @@ Getting Help
Whatever the command line parameters and additional options might be for
an application, you can start with help::
$ python Tutorial/WhoIsIAm.py --help
$ python samples/WhoIsIAm.py --help
usage: WhoIsIAm.py [-h] [--buggers] [--debug [DEBUG [DEBUG ...]]] [--color] [--ini INI]
This application presents a 'console' prompt to the user asking for Who-Is and
@ -33,22 +33,22 @@ an application, you can start with help::
Listing Debugging Loggers
-------------------------
The BACpypes library and sample applications make extensive use of the
built-in *logging* module in Python. Every module in the library, along
with every class and exported function, has a logging object associated
with it. By attaching a log handler to a logger, the log handler is given
The BACpypes library and sample applications make extensive use of the
built-in *logging* module in Python. Every module in the library, along
with every class and exported function, has a logging object associated
with it. By attaching a log handler to a logger, the log handler is given
a chance to output the progress of the application.
Because BACpypes modules are deeply interconnected, dumping a complete list
of all of the logger names is a long list. Start out focusing on the
Because BACpypes modules are deeply interconnected, dumping a complete list
of all of the logger names is a long list. Start out focusing on the
components of the WhoIsIAm.py application::
$ python Tutorial/WhoIsIAm.py --buggers | grep __main__
$ python samples/WhoIsIAm.py --buggers | grep __main__
__main__
__main__.WhoIsIAmApplication
__main__.WhoIsIAmConsoleCmd
In this sample, the entire application is called __main__ and it defines
In this sample, the entire application is called __main__ and it defines
two classes.
Debugging a Module
@ -61,12 +61,12 @@ Telling the application to debug a module is simple::
DEBUG:__main__: - args: Namespace(buggers=False, debug=['__main__'], ini=<class 'bacpypes.consolelogging.ini'>)
DEBUG:__main__.WhoIsIAmApplication:__init__ (<bacpypes.app.LocalDeviceObject object at 0xb6dd98cc>, '128.253.109.40/24:47808')
DEBUG:__main__:running
>
>
The output is the severity code of the logger (almost always DEBUG), the name
of the module, class, or function, then some message about the progress of the
application. From the output above you can see the application initializing,
setting the args variable, creating an instance of the WhoIsIAmApplication class
The output is the severity code of the logger (almost always DEBUG), the name
of the module, class, or function, then some message about the progress of the
application. From the output above you can see the application initializing,
setting the args variable, creating an instance of the WhoIsIAmApplication class
(with some parameters), and then declaring itself - running.
@ -76,52 +76,52 @@ Debugging a Class
Debugging all of the classes and functions can generate a lot of output,
so it is useful to focus on a specific function or class::
$ python Tutorial/WhoIsIAm.py --debug __main__.WhoIsIAmApplication
$ python samples/WhoIsIAm.py --debug __main__.WhoIsIAmApplication
DEBUG:__main__.WhoIsIAmApplication:__init__ (<bacpypes.app.LocalDeviceObject object at 0x9bca8ac>, '128.253.109.40/24:47808')
>
>
The same method is used to debug the activity of a BACpypes module, for
The same method is used to debug the activity of a BACpypes module, for
example, there is a class called UDPActor in the UDP module::
$ python Tutorial/WhoIsIAm.py --ini BAC0.ini --debug bacpypes.udp.UDPActor
$ python samples/WhoIsIAm.py --ini BAC0.ini --debug bacpypes.udp.UDPActor
> DEBUG:bacpypes.udp.UDPActor:__init__ <bacpypes.udp.UDPDirector 128.253.109.255:47808 at 0xb6d40d6c> ('128.253.109.254', 47808)
DEBUG:bacpypes.udp.UDPActor:response <bacpypes.comm.PDU object at 0xb6d433cc>
<bacpypes.comm.PDU object at 0xb6d433cc>
pduSource = ('128.253.109.254', 47808)
pduData = x'81.04.00.37.0A.10.6D.45.BA.C0.01.28.FF.FF.00.00.B6.01.05.FD...'
In this sample, an instance of a UDPActor is created and then its response
function is called with an instance of a PDU as a parameter. Following
In this sample, an instance of a UDPActor is created and then its response
function is called with an instance of a PDU as a parameter. Following
the function invocation description, the debugging output continues with the
contents of the PDU. Notice, the protocol data is printed as a hex
contents of the PDU. Notice, the protocol data is printed as a hex
encoded string (and restricted to just the first 20 bytes of the message).
You can debug a function just as easily. Specify as many different
combinations of logger names as necessary. Note, you cannot debug a
You can debug a function just as easily. Specify as many different
combinations of logger names as necessary. Note, you cannot debug a
specific function within a class.
Sending Debug Log to a file
----------------------------
The current --debug command line option takes a list of named debugging access
points and attaches a StreamHandler which sends the output to sys.stderr.
There is a way to send the debugging output to a
RotatingFileHandler by providing a file name, and optionally maxBytes and
backupCount. For example, this invocation sends the main application debugging
to standard error and the debugging output of the bacpypes.udp module to the
The current --debug command line option takes a list of named debugging access
points and attaches a StreamHandler which sends the output to sys.stderr.
There is a way to send the debugging output to a
RotatingFileHandler by providing a file name, and optionally maxBytes and
backupCount. For example, this invocation sends the main application debugging
to standard error and the debugging output of the bacpypes.udp module to the
traffic.txt file::
$ python Tutorial/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt
$ python samples/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt
By default the `maxBytes` is zero so there is no rotating file, but it can be
By default the `maxBytes` is zero so there is no rotating file, but it can be
provided, for example this limits the file size to 1MB::
$ python Tutorial/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt:1048576
$ python samples/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt:1048576
If `maxBytes` is provided, then by default the `backupCount` is 10, but it can also
If `maxBytes` is provided, then by default the `backupCount` is 10, but it can also
be specified, so this limits the output to one hundred files::
$ python Tutorial/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt:1048576:100
$ python samples/WhoIsIAm.py --debug __main__ bacpypes.udp:traffic.txt:1048576:100
.. caution::
@ -136,18 +136,18 @@ The definition of debug::
Changing INI Files
------------------
It is not unusual to have a variety of different INI files specifying
It is not unusual to have a variety of different INI files specifying
different port numbers or other BACnet communications paramters.
Rather than swapping INI files, you can simply provide the INI file on the
command line, overriding the default BACpypes.ini file. For example, I
command line, overriding the default BACpypes.ini file. For example, I
have an INI file for port 47808::
$ python Tutorial/WhoIsIAm.py --ini BAC0.ini
$ python samples/WhoIsIAm.py --ini BAC0.ini
And another one for port 47809::
$ python Tutorial/WhoIsIAm.py --ini BAC1.ini
$ python samples/WhoIsIAm.py --ini BAC1.ini
And I switch back and forth between them.

View File

@ -1,181 +0,0 @@
#!/usr/bin/env python
"""
This application presents a 'console' prompt to the user asking for Who-Is and I-Am
commands which create the related APDUs, then lines up the corresponding I-Am
for incoming traffic and prints out the contents.
"""
import sys
from bacpypes.debugging import bacpypes_debugging, ModuleLogger
from bacpypes.consolelogging import ConfigArgumentParser
from bacpypes.consolecmd import ConsoleCmd
from bacpypes.core import run, enable_sleeping
from bacpypes.pdu import Address, GlobalBroadcast
from bacpypes.apdu import WhoIsRequest, IAmRequest
from bacpypes.errors import DecodingError
from bacpypes.app import BIPSimpleApplication
from bacpypes.local.device import LocalDeviceObject
# some debugging
_debug = 1
_log = ModuleLogger(globals())
# globals
this_device = None
this_application = None
#
# WhoIsIAmApplication
#
@bacpypes_debugging
class WhoIsIAmApplication(BIPSimpleApplication):
def __init__(self, *args):
if _debug: WhoIsIAmApplication._debug("__init__ %r", args)
BIPSimpleApplication.__init__(self, *args)
# keep track of requests to line up responses
self._request = None
def request(self, apdu):
if _debug: WhoIsIAmApplication._debug("request %r", apdu)
# save a copy of the request
self._request = apdu
# forward it along
BIPSimpleApplication.request(self, apdu)
def confirmation(self, apdu):
if _debug: WhoIsIAmApplication._debug("confirmation %r", apdu)
# forward it along
BIPSimpleApplication.confirmation(self, apdu)
def indication(self, apdu):
if _debug: WhoIsIAmApplication._debug("indication %r", apdu)
if (isinstance(self._request, WhoIsRequest)) and (isinstance(apdu, IAmRequest)):
device_type, device_instance = apdu.iAmDeviceIdentifier
if device_type != 'device':
raise DecodingError("invalid object type")
if (self._request.deviceInstanceRangeLowLimit is not None) and \
(device_instance < self._request.deviceInstanceRangeLowLimit):
pass
elif (self._request.deviceInstanceRangeHighLimit is not None) and \
(device_instance > self._request.deviceInstanceRangeHighLimit):
pass
else:
# print out the contents
sys.stdout.write('pduSource = ' + repr(apdu.pduSource) + '\n')
sys.stdout.write('iAmDeviceIdentifier = ' + str(apdu.iAmDeviceIdentifier) + '\n')
sys.stdout.write('maxAPDULengthAccepted = ' + str(apdu.maxAPDULengthAccepted) + '\n')
sys.stdout.write('segmentationSupported = ' + str(apdu.segmentationSupported) + '\n')
sys.stdout.write('vendorID = ' + str(apdu.vendorID) + '\n')
sys.stdout.flush()
# forward it along
BIPSimpleApplication.indication(self, apdu)
#
# WhoIsIAmConsoleCmd
#
@bacpypes_debugging
class WhoIsIAmConsoleCmd(ConsoleCmd):
def do_whois(self, args):
"""whois [ <addr> ] [ <lolimit> <hilimit> ]"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_whois %r", args)
try:
# gather the parameters
if (len(args) == 1) or (len(args) == 3):
addr = Address(args[0])
del args[0]
else:
addr = GlobalBroadcast()
if len(args) == 2:
lolimit = int(args[0])
hilimit = int(args[1])
else:
lolimit = hilimit = None
# code lives in the device service
this_application.who_is(lolimit, hilimit, addr)
except Exception as error:
WhoIsIAmConsoleCmd._exception("exception: %r", error)
def do_iam(self, args):
"""iam"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_iam %r", args)
# code lives in the device service
this_application.i_am()
def do_rtn(self, args):
"""rtn <addr> <net> ... """
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_rtn %r", args)
# provide the address and a list of network numbers
router_address = Address(args[0])
network_list = [int(arg) for arg in args[1:]]
# pass along to the service access point
this_application.nsap.update_router_references(None, router_address, network_list)
#
# __main__
#
def main():
global this_device
global this_application
# parse the command line arguments
args = ConfigArgumentParser(description=__doc__).parse_args()
if _debug: _log.debug("initialization")
if _debug: _log.debug(" - args: %r", args)
# make a device object
this_device = LocalDeviceObject(
objectName=args.ini.objectname,
objectIdentifier=int(args.ini.objectidentifier),
maxApduLengthAccepted=int(args.ini.maxapdulengthaccepted),
segmentationSupported=args.ini.segmentationsupported,
vendorIdentifier=int(args.ini.vendoridentifier),
)
# make a simple application
this_application = WhoIsIAmApplication(this_device, args.ini.address)
# make a console
this_console = WhoIsIAmConsoleCmd()
if _debug: _log.debug(" - this_console: %r", this_console)
# enable sleeping will help with threads
enable_sleeping()
_log.debug("running")
run()
_log.debug("fini")
if __name__ == "__main__":
main()

83
samples/WhoIsIAm.py Executable file → Normal file
View File

@ -2,7 +2,7 @@
"""
This application presents a 'console' prompt to the user asking for Who-Is and I-Am
commands which create the related APDUs, then lines up the coorresponding I-Am
commands which create the related APDUs, then lines up the corresponding I-Am
for incoming traffic and prints out the contents.
"""
@ -12,8 +12,7 @@ from bacpypes.debugging import bacpypes_debugging, ModuleLogger
from bacpypes.consolelogging import ConfigArgumentParser
from bacpypes.consolecmd import ConsoleCmd
from bacpypes.core import run, deferred, enable_sleeping
from bacpypes.iocb import IOCB
from bacpypes.core import run, enable_sleeping
from bacpypes.pdu import Address, GlobalBroadcast
from bacpypes.apdu import WhoIsRequest, IAmRequest
@ -23,7 +22,7 @@ from bacpypes.app import BIPSimpleApplication
from bacpypes.local.device import LocalDeviceObject
# some debugging
_debug = 0
_debug = 1
_log = ModuleLogger(globals())
# globals
@ -68,10 +67,10 @@ class WhoIsIAmApplication(BIPSimpleApplication):
raise DecodingError("invalid object type")
if (self._request.deviceInstanceRangeLowLimit is not None) and \
(device_instance < self._request.deviceInstanceRangeLowLimit):
(device_instance < self._request.deviceInstanceRangeLowLimit):
pass
elif (self._request.deviceInstanceRangeHighLimit is not None) and \
(device_instance > self._request.deviceInstanceRangeHighLimit):
(device_instance > self._request.deviceInstanceRangeHighLimit):
pass
else:
# print out the contents
@ -85,6 +84,7 @@ class WhoIsIAmApplication(BIPSimpleApplication):
# forward it along
BIPSimpleApplication.indication(self, apdu)
#
# WhoIsIAmConsoleCmd
#
@ -93,60 +93,37 @@ class WhoIsIAmApplication(BIPSimpleApplication):
class WhoIsIAmConsoleCmd(ConsoleCmd):
def do_whois(self, args):
"""whois [ <addr>] [ <lolimit> <hilimit> ]"""
"""whois [ <addr> ] [ <lolimit> <hilimit> ]"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_whois %r", args)
try:
# build a request
request = WhoIsRequest()
# gather the parameters
if (len(args) == 1) or (len(args) == 3):
request.pduDestination = Address(args[0])
addr = Address(args[0])
del args[0]
else:
request.pduDestination = GlobalBroadcast()
addr = GlobalBroadcast()
if len(args) == 2:
request.deviceInstanceRangeLowLimit = int(args[0])
request.deviceInstanceRangeHighLimit = int(args[1])
if _debug: WhoIsIAmConsoleCmd._debug(" - request: %r", request)
lolimit = int(args[0])
hilimit = int(args[1])
else:
lolimit = hilimit = None
# make an IOCB
iocb = IOCB(request)
if _debug: WhoIsIAmConsoleCmd._debug(" - iocb: %r", iocb)
# code lives in the device service
this_application.who_is(lolimit, hilimit, addr)
# give it to the application
this_application.request_io(iocb)
except Exception as err:
WhoIsIAmConsoleCmd._exception("exception: %r", err)
except Exception as error:
WhoIsIAmConsoleCmd._exception("exception: %r", error)
def do_iam(self, args):
"""iam"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_iam %r", args)
try:
# build a request
request = IAmRequest()
request.pduDestination = GlobalBroadcast()
# set the parameters from the device object
request.iAmDeviceIdentifier = this_device.objectIdentifier
request.maxAPDULengthAccepted = this_device.maxApduLengthAccepted
request.segmentationSupported = this_device.segmentationSupported
request.vendorID = this_device.vendorIdentifier
if _debug: WhoIsIAmConsoleCmd._debug(" - request: %r", request)
# make an IOCB
iocb = IOCB(request)
if _debug: WhoIsIAmConsoleCmd._debug(" - iocb: %r", iocb)
# give it to the application
this_application.request_io(iocb)
except Exception as err:
WhoIsIAmConsoleCmd._exception("exception: %r", err)
# code lives in the device service
this_application.i_am()
def do_rtn(self, args):
"""rtn <addr> <net> ... """
@ -162,11 +139,12 @@ class WhoIsIAmConsoleCmd(ConsoleCmd):
#
# main
# __main__
#
def main():
global this_device, this_application
global this_device
global this_application
# parse the command line arguments
args = ConfigArgumentParser(description=__doc__).parse_args()
@ -175,14 +153,16 @@ def main():
if _debug: _log.debug(" - args: %r", args)
# make a device object
this_device = LocalDeviceObject(ini=args.ini)
if _debug: _log.debug(" - this_device: %r", this_device)
this_device = LocalDeviceObject(
objectName=args.ini.objectname,
objectIdentifier=int(args.ini.objectidentifier),
maxApduLengthAccepted=int(args.ini.maxapdulengthaccepted),
segmentationSupported=args.ini.segmentationsupported,
vendorIdentifier=int(args.ini.vendoridentifier),
)
# make a simple application
this_application = WhoIsIAmApplication(
this_device, args.ini.address,
)
if _debug: _log.debug(" - this_application: %r", this_application)
this_application = WhoIsIAmApplication(this_device, args.ini.address)
# make a console
this_console = WhoIsIAmConsoleCmd()
@ -197,6 +177,5 @@ def main():
_log.debug("fini")
if __name__ == "__main__":
main()