EPICS Home

Experimental Physics and Industrial Control System


 
1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  <20092010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  <20092010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: EPICS BACnet Support
From: Marty Kraimer <[email protected]>
To: [email protected]
Date: Thu, 29 Oct 2009 08:34:22 -0400
Just a few words about what I know about BACnet support for EPICS.
 
It has been somewhat over two years since I did the BACnet support for the JavaIOC so some of what I say may be out of date.

BACnet supports several different communication protocols. One is BACnetIP, which is actually UDP. This is what is used for both of the EPICS implementations.

The source forge BACnet support is a partial implementation of the BACnet standard. In particular it does not support segmented messages. If all you want is support for scalar IO (for example analog and digital IO) this does not matter. But if you want support for arrays or other messages that do not fix in a single network packet then it is a problem. Although the javaIOC BACnet support does not use source forge.bacnet it currently has the same limitations.

Let me try to state the main difference between the TRIUMF support and the javaIOC BACnet support. Rod or Keith can correct me if what I say is wrong.

For BACnet each device has a unique Device Identifier, which is just a number.

The TRIUMF implementation uses a BACnet Who-is request to discover all the devices. A Who-is request specifies a low and high device number. This is  sent as a broadcast. The BACnet  standard says that each device in the requested range must set an I-am response message, which must also be a broadcast message. Thus one Who-is broadcast can result in many I-am broadcasts.  If a Who-is has low and high set to include every device of the network, it can take a LONG time for the BACnet network to settle down. The TRIUMF implementation solves this problem by only doing this when absolutely necessary.

The javaIOC BACnet support requires that you supply a list of Device Identifiers to which you want to connect. It then issues Who-is requests to a single device at a time and throttles the rate at which it sends Who-is requests.

I have attached a document describing the state of the javaIOC BACnet support. As Keith mentioned it requires some changes to work with the latest version of the javaIOC. If someone is interested in further developing this support I will be happy to make the changes to make it work with the latest version of the javaIOC but I do not have time now to do much more.

I do think that the javaIOC version is a good base on which to implement more of the BACnet standard. The first thing is to implement support for segmented messages.

Marty



[email protected] wrote:
Hello,
I am currently working on this, initially just to read some BACnet values into an EPICS Linux IOC, though progress is slow as I'm just fitting it in around other jobs. When I inquired it seemed as if nobody had done much work on BACnet for a couple of years or more.
 
My development is following as closely as possible the work done at TRIUMF in Canada, by Rod Nussbaumer. (Rod, I hope you don't mind me mentioning you.)
 
Marty Kraimer also developed BACnet support for the javaIOC, for DESY. I believe this needs some updating to work with the latest javaIOC, but that should not be too onerous. (Marty, sorry for another name drop.)
 
Regarding the TRIUMF work, Rod has been very helpful sending me large amounts of code and answering lots of questions but I have not yet got everything working. The route is a little tortuous due to all the different tools required and my lack of knowledge, but having gone through it recently it should be less painful again. The BACnet part of the code is a modified version (0.3.10) of bacnet4linux.(http://sourceforge.net/projects/bacnet4linux/) See also, http://bacnet4linux.sourceforge.net/.
However this is now obsolete regarding further development (even the later 0.3.12 version) and replaced by the BACnet Stack project, see http://bacnet.sourceforge.net/ and http://sourceforge.net/projects/bacnet/
 
I chose to stick with Rod's method however as it provides far more than just a BACnet stack. The bacnet4linux package includes a web server which was very useful initially for checking communications on the BACnet interface. Then the browsing and selection of PVs is web based, using a Perl script to talk to a PostgresQL database. This script also generates EPICS records and EDM panels, making it much easier to change the set of PVs we want to monitor.
Let me know if you want more detail of the whole system. It could be much more complex than you need.
 
If you wished to develop EPICS BACnet support yourself, I think there are several other BACnet stacks available, but mostly commercial and mostly geared up to adding BACnet support to the remote servers, interfacing to hardware and not specifically for the client (management) side.
It depends exactly what you want to do with the BACnet support as to whether these are any use.
 
Regards,
Keith Baker
Controls Group
Diamond Light Source Ltd
+44(0)1235 778054


From: [email protected] [mailto:[email protected]] On Behalf Of Debby Quock
Sent: 28 October 2009 13:03
To: [email protected]
Subject: EPICS BACnet Support

EPICS Colleagues,

 

Is any one currently using and/or developing EPICS support for BACnet data communication protocol? More specifically, EPICS-to-BACnet driver that is used with commercial heating and ventilation control systems.

 

Thanks,

 

Debby Quock

Controls Group

Advanced Photon Source

Argonne National Laboratory

 


 

-- 

This e-mail and any attachments may contain confidential, copyright and or privileged material, and are for the use of the intended addressee only. If you are not the intended addressee or an authorised recipient of the addressee please notify us of receipt by returning the e-mail and do not use, copy, retain, distribute or disclose the information in or attached to the e-mail.
Any opinions expressed within this e-mail are those of the individual and not necessarily of Diamond Light Source Ltd.
Diamond Light Source Ltd. cannot guarantee that this e-mail or any attachments are free from viruses and we cannot accept liability for any damage which you may sustain as a result of software viruses which may be transmitted in or with the message.
Diamond Light Source Limited (company no. 4375679). Registered in England and Wales with its registered office at Diamond House, Harwell Science and Innovation Campus, Didcot, Oxfordshire, OX11 0DE, United Kingdom
 



Title: EPICS JavaIOC: BACnetGateway

EPICS JavaIOC: BACnetGateway
package: org.epics.ioc.BACnetGateway
2007.11.19

CONTENTS

Overview


This is an early version of a JavaIOC BACnet Gateway. It can do the following:

The BACnet Gateway is an unfinished project. In particular the following are not implemented:


Setup and Usage


Four components are required in order to use the BACnetGateway.

  1. Eclipse and Java
    These must be versions that support Java 1.5 or later.
  2. JavaIOC
    Import this into eclipse.
  3. BACnetGateway
    Import this into eclipse. After importing look at properties =>Java Build Path=>Projects. Make sure it lists javaIOC.
  4. BACnet Database
    A JavaIOC database for your BACnet devices. Put this in your own area.

Build EncodeDecode

This is the C code used by BACnetGateway. It uses the EPICS build system, i.e. it requires EPICS base. It resides in BACnetGateway/jni/EncodeGateway. To build it for your site:

Configuring XMLToDatabase

This resides in the default package for javaIOC. It is the java application which loads the databases and start the ioc. The following must be configured:


JavaIOC BACnet Database


The JavaIOC BACnet database should be maintained in a private area. Three types of record instances are created: 1) A record that lists the BACnet devices, 2) a record for each BACnet analog object, and 3) a record for each BACnet binary or multi-state object. These records should all be included via a record named bacnetDB.xml which has a definition like:

<?xml version="1.0" ?>
<IOCDatabase>
<include addPath = "fullPathName" />
<include href = "" />
<include href = "" />
<include href = "" />
<include removePath = "fullPathName" />
</IOCDatabase>

The fullPathName is the pathname to the JavaIOC database. The names devicesDB, analogDB, and digitalDB are just examples. These can be anything as long as they reside in the directory specified by fullPathName.

BACnet Devices

An intArray record instance must be created that lists the BACnet devices to which the Gateway connects. For each device a JavaIOC portDriver is created. The portName is just the BACnet device number. This record is defined as follows:

<record name = "BACnetDevices" type = "intArray"
    supportName = "BACnetPorts" >
    <value>
        <value>deviceNum</value>
        <value>deviceNum</value>
        <!-- ... -->
    </value>
</record>

Analog Objects

For each BACnet object a record instance must be created. BACnet/test/aiTemplateDB.xml provides a template file for analog inputs. BACnet/test/aiDB.xml provides an example of how to create record instances from the template. Copy these files to your area and start with them. If a particular BACnet object does not provide the display units and/or limits then, if required, they must be configured in the record instances.

For each object the deviceNumber, objectNumber, and objectType must be provided. The deviceType for analog objects are (0,1,2) for (analog-input, analog-output, analog-value).

Binary and Multi-state Objects

BACnet/test/digitalTemplate.xml provides a template for binary and multi-state objects. Again copy them to your area and start with them. If a BACnet binary object does not provide the inactiveText or activeText then configure them manually. If a BACnet multi-state object does not provide the stateText via an unsegmented message they will need to be configured manually.

The deviceType for binary objects are (3,4,5) for (binary-input, binary-output, binary-value). The deviceType for multi-state objects are (13,14,19) for (multi-state-input, multi-state-output, multi-state-value).


Implementation Overview


When the BACnetGateway is started it creates threads for managing all BACnet network I/O. There is a single receive and a single send thread. All other threads do I/O via these threads.

A thread is provided which is resposible for locating BACnet devices. At initialization a whois BACnet broadcast message is issued for each BACnet device. When a iam broadcast message is received, it's network address is saved. Until a device sends an iam, the connection thread periodically sends a whois messages.

Another thread is responsible for timeouts for confirmed messages. It implements the recommended BACnet timeout protocol, i.e. a timeout occurs after 3 seconds and up to two additional messages are send before the message fails and the sender notified of a timeout.

The connection between the JavaIOC and BACnet is via a BACnet portDriver. A portDriver is created for each BACnet device.

given


Java Interfaces and Factories


BACnetIO

BACnetIO performs all BACnet network IO.

interface BACnetIO {
    void locateDeviceObject(DeviceObject deviceObject);
    void readConfirmedRequest(ReadConfirmedRequest request);
    Trace getTrace();
}

interface BACnetIORequestor {
    void requestDone(byte[] receiveBuffer, int offsetServiceData, int length);
    void requestFailed(String message);
}

interface ReadConfirmedRequest {
    DeviceObject getDeviceObject();
    byte[] getServiceRequest();
    int getServiceRequestLength();
    BACnetIORequestor getBACnetIORequestor();
    public int getNumberTimeouts();
    public void setNumberTimeouts(int number);
}

public class BACnetIOFactory {
    public static BACnetIO getBACnetIO();
}

BACnetIO provides methods:

locateDeviceObject
Locate a BACnet device. A whois BACnet broadcast is sent. A iam broadcast is expected from the device. Until an iam is received new whois requests are periodically sent.
readConfirmedRequest
Issue a BACnet readConfirmed request and listen for the response. If a reply is not received after a timout (3 seconds) up to two additional requests are sent. If a reply is never received then the requestor is notified that the request timed out. The BACnet device can respond with a simple or complex ACK, errior, or abort.
getTrace
Get the trace intercface for BACnetIO. This can be used to generate diagnostic messages for ALL BacnetIO activity.

BACnetIORequestor provides methods:

requestDone
A reply has been received. The arguments are the reply message.
requestFailed
A request has failed. The message provides the reason.

ReadConfirmedRequest provides methods:

getDeviceObject
Get the deviceObject.
getServiceRequest
Get the service request portion of the message to send to the device.
getServiceRequestLength
Get the length of the service request portion of the message to send to the device.
getBACnetIORequestor
Get the BACnetIORequestor for the request.
getNumberTimeouts
Get the number of times the last message was sent without receiving a reply.
setNumberTimeouts
Set the number of times the current message has been sent without a reply.

BACnetIOFactory provides method:

getBACnetIO
Get the single instance of BACnetIO.

ReadProperty

ReadProperty is for issuing BACnet readProperty requests.

enum PropertyType {
    unknown,
    typeNull,
    bool,
    int32,
    float64,
    octetString,
    string,
    bitString,
    enumerated,
    objectID,
    error
}

interface ReadProperty {
    void request(int objectIdentifier, int[] propertyIDs);
    Trace getTrace();
    int getPropertyID();
    PropertyType getPropertyType();
    void setPropertyType(PropertyType value);
    boolean getBoolean();
    void setBoolean(boolean value);
    int getInt();
    void setInt(int value);
    double getDouble();
    void setDouble(double value);
    byte[] getOctetString();
    void setOctetString(byte[] value);
    String getString();
    void setString(String value);
    byte[] getBitString();
    int getBitStringLength();
    void setBitString(byte[] value,int length);
    int getEnumerated();
    void setEnumerated(int value);
    int getObjectIdentifier();
    void setObjectIdentifier(int value);
    int getErrorClass();
    void setErrorClass(int value);
    int getErrorCode();
    void setErrorCode(int value);
}

interface ReadPropertyRequestor {
    void startResult();
    void nextProperty();
    void endResult();
    void requestFailed(String message);
}

class ReadPropertyFactory {
    static ReadProperty createReadProperty(DeviceObject deviceObject,
               ReadPropertyRequestor readPropertyRequestor);
}

PropertyType defines the BACnet primitive data types. See clause 20.2 of the BACnet standard for details.

unknown
The types is not known
typeNull
A BACnet null.
bool
A BACnet boolean.
int32
A BACnet signed or unsigned integer.
float64
A BACnet real or double.
octetString
A BACnet octet string.
string
A BACnet character string.
bitString
A BACnet bit string.
enumerated
A BACnet enumerated.
objectID
A BACnetObjectIdentifier
error
A BACnet error was received. Note that this is NOT a BACnet primitive data type.

ReadProperty is the interface for a BACnet readProperty request.

request
Issue a readProperty request. The request is for an arbitrary number of properties of a single BACnet object.
getTrace
Get the trace interface for this ReadProperty
getPropertyID
Get a new propertyID. See the BACnet standard for details. Each message has an ID that is used to ignore duplicate messages.
getPropertyType
Get the propertyType. This is called by the ReadPropertyRequestor when nextProperty is called.
setPropertyType
This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty.
getBoolean
Get the boolean value. Called by ReadPropertyRequestor if the propertyType is boolean.
setBoolean
Set a boolean value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is boolean.
getInt
Get the integer value. Called by ReadPropertyRequestor if the propertyType is integer.
setInt
Set a integer value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is integer
getDouble
Get the double value. Called by ReadPropertyRequestor if the propertyType is double.
setDouble
Set a double value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is double
getOctetString
Get the octet string value. Called by ReadPropertyRequestor if the propertyType is octet string.
setOctetString
Set a octet string value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is octet string
getString
Get the string value. Called by ReadPropertyRequestor if the propertyType is string.
setString
Set a string value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is string
getBitString
Get the bit string value. Called by ReadPropertyRequestor if the propertyType is bit string.
getBitStringLength
Get the bit string length value. Called by ReadPropertyRequestor if the propertyType is bit string length. This is the number of bits. The bits arev numbered from left to right. This bit 0 is 0x80 of byte[0] etc.
setBitString
Set a bit string value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is bit string
getEnumerated
Get the enumerated value. Called by ReadPropertyRequestor if the propertyType is enumerated.
setEnumerated
Set a enumerated value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is enumerated
getObjectIdentifier
Get the objectID value. Called by ReadPropertyRequestor if the propertyType is objectID.
setObjectIdentifier
Set a objectID value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is objectID
getErrorClass
Get the error class value. Called by ReadPropertyRequestor if the propertyType is error class.
setErrorClass
Set a error class value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is error class
getErrorCode
Get the error code value. Called by ReadPropertyRequestor if the propertyType is error code.
setErrorCode
Set a error code value. This is called by ReadPropertyFactory before calling ReadPropertyRequestor.nextProperty if the propertyType is error code

ReadPropertyRequestor is implemented by the requestor.

startResult
A new replay has arrived
nextProperty
The nextProperty is available. The requestor can call readProperty.getPropertyType and then the corresponding readProperty.getXXX method to get the property data.
endResult
The last property has been decoded.
requestFailed
A request has failed. The message provides the reason.

ReadPropertyFactory provides the method:

createReadProperty
Create a new ReadProperty.

DeviceObject

Each BACnet device known to the gateway is referenced via a DeviceObject.

interface DeviceObject {
    int getdeviceNumber();
    InetAddress getInetAddress();
    void setInetAddress(InetAddress inetAddress);
    byte[] getNPCI();
    void setNPCI(byte[] buffer, int offset, int length);
    int getMaxAPDU();
    void setMaxAPDU(int maxAPDU);
    boolean requestInvokeID(ReadConfirmedRequest request);
    ReadConfirmedRequest completeRequest(int invokeID);
    int getInvokeID();
    Trace getTrace();
}

interface DeviceObjectDB {
    DeviceObject getDeviceObject(int deviceNumber);
    DeviceObject getDeviceObject(
        InetAddress inetAddress,byte[] buffer, int offsetNPCI, int lengthNPCI);
    void addDevice(DeviceObject deviceObject);
}

class DeviceObjectFactory {
    public static DeviceObject getDeviceObject(int deviceNumber);
    public static DeviceObjectDB getDeviceObjectDB();
}

DeviceObject is created for each BACnet device.

getdeviceNumber
Get the deviceNumber
getInetAddress
Get the InetAddress for this device. Until am iam is received from the device this is null.
setInetAddress
Set the InetAddress for this device. Called as a result of an iam message from the device.
getNPCI
Get the Network Protocol Control Information for the deviced. Until an iam is received from the device this is null.
setNPCI
Set the NPCI for this device. Called as a result of an iam message from the device.
getMaxAPDU
Get the maximum size for the application portion of message for the device.
setMaxAPDU
Set the MaxAPDUI for this device. Called as a result of an iam message from the device.
requestInvokeID
Request a new invokeID. Only one request at a time to a particulat DeviceObject is allowed.
completeRequest
Called when a reply is received for a request. The invokeID in the response is provided. If this corresponds to an outstanding request the ReadConfirmedRequest is returned.
getInvokeID
Get the current invokeID.
getTrace
Get the trace interface for this DeviceObject.

DeviceObjectDB is a database holding all the device objects.

getDeviceObject(int deviceNumber)
Get the DeviceObject with deviceNumber..
getDeviceObject(InetAddress ...)
Get the DeviceObject located at the specified InetAddress and NPCI.
addDevice
Add a new DeviceObject.

DeviceObjectFactory implements DeviceObjects and the DeviceObjectDB

getDeviceObject
Get a DeviceObject. If it is not in the database a new DeviceObject is created and bacnetIO.locateDeviceObject(deviceObject) is called.
getDeviceObjectDB
Get the single instance of the DeviceObjectDB.

EncodeDecode

EncodeDecode Encode and decode BACnet primitive data.

public interface EncodeDecode {
    int tag(byte[] buffer,int offset,
       int tagNumber,boolean contextSpecific, int lengthValueType);
    int objectID(byte[] buffer,int offset,int objectType,int objectNumber);
    int objectIDTagged(byte[] buffer,int offset,
       int objectType,int objectNumber);
    int objectIDContext(byte[] buffer,int offset,
       int objectType,int objectNumber,int tagNumber);
    int integerTagged(byte[] buffer,int offset,int value);
    int integerContext(byte[] buffer,int offset,int value,int tagNumber);
    int unsignedIntegerTagged(byte[] buffer,int offset,int value);
    int unsignedIntegerContext(byte[] buffer,int offset,
       int value,int tagNumber);
    int realTagged(byte[] buffer,int offset,float value);
    int realContext(byte[] buffer,int offset,float value,int tagNumber);
    int realTagged(byte[] buffer,int offset,double value);
    int realContext(byte[] buffer,int offset,double value,int tagNumber);
    int decode(byte[] buffer,int offset, ReadProperty readProperty);
}

class EncodeDecodeFactory { 
    static EncodeDecode getEncode();
}

class EncodeDecodeJNI {
    native void encodeFloat(byte[] buffer, int offset, float value);
    native void encodeDouble(byte[] buffer, int offset, double value);
    native  float decodeFloat(byte[] buffer, int offset);
    native double decodeDouble(byte[] buffer, int offset);

     static { System.loadLibrary("EncodeDecodeJNI");}
}

EncodeDecode Encode or decode BACnet data. In all cases that int return value is new offset into byte[].

tag
Encode a BACnet tag.
objectID
Encode an objectID, i.e. the object type (upper 10 bits) and object number (lower 22 bits).
objectIDTagged
Encode an objectID with an application tag.
objectIDContext
Encode an objectID with a context tag.
integerTagged
Encode an integer with an application tag.
integerContext
Encode an integer with a context tag.
unsignedIntegerTagged
Encode an unsigned integer with an application tag.
unsignedIntegerContext
Encode an unsigned integer with a context tag.
realTagged
Encode a float or double into a application tagged BACnet real or double
realContext
Encode a float or double into a context tagged BACnet real or double
decode
Decode the next BACnet data and put the result into readProperty.

EncodeDecodeFactory gets the single instance of EncodeDecode.

getEncode
Get the single instance of EncodeDecode.

EncodeDecodeJNI Java Native Interface code to encode and decode float and double data.

encodeFloat
Encode a float into four bytes.
encodeDouble
Encode a double into four bytes.
decodeFloat
Device the next four bytes into a float.
decodeDouble
Decode the next 8 bytes into a double.

BACnetString

provides the string values for various BACnet enumnerated things.

interface BACnetString {
    String getErrorClass(byte index);
    String getErrorCode(byte index);
    String getAbortReason(byte index);
    String getRejectReason(byte index);
    String getUnits(byte index);
    String getEventState(byte index);
}

class BACnetStringFactory {
    static BACnetString getBACnetString();
}

BACnetString gets the string values for various BACnet data.

getErrorClass
Get the error class. A typical use is bacnetString.getErrorClass(readProperty.getErrorClass());
getErrorCode
Get the error class. A typical use is bacnetString.getErrorCode(readProperty.getErrorCode());
getAbortReason
Get abort reason.
getRejectReason
Get reject reason.
getUnits
Get units.
getEventState
Get event state.

BACnetStringFactory gets the single instance of BACnetString.

getBACnetString
Get the single instance of BACnetString.

JavaIOC PortDriver <=> BACnet

Two factorys are provide the interface between the JavaIOC portDriver and BACnet. BACnetPortDriverFactory implements a portDriver. BACnetSupportFactory implements the support for an intArray record, which defines the set of BACnet device objects to which the gateway communicates.

class BACnetPortDriverFactory {
    PortDriver create(int deviceNumber);
}

class BACnetSupportFactory {
    static Support create(DBStructure dbStructure);
}

BACnetPortDriverFactory creates a BACnet portDriver. One is created for each BACnet device.

create
Create a portDriver

BACnetSupportFactory creates the support for an intArray record that defines the set of BACnet devices.

create
Create the support.

References:
EPICS BACnet Support Debby Quock
RE: EPICS BACnet Support keith.baker

Navigate by Date:
Prev: Re: macLib/msi changes with 3.14.10 Andrew Johnson
Next: Re: ICALEPCS 2009 Lifetime Achievement Award John Maclean
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  <20092010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: RE: EPICS BACnet Support keith.baker
Next: File I/O from genSub Process function Bruce Hill
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  <20092010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024