upm/src/bacnetmstp/bacnetmstp.hpp
Noel Eck 922e0cc26b cpp_headers: Renamed C++ headers from .h -> .hpp
To make room for UPM C and C++ sensor code to coexist, all UPM
C++ headers have been renamed from h -> hpp.  This commit contains
updates to documentation, includes, cmake collateral, examples, and
swig interface files.

    * Renamed all cxx/cpp header files which contain the string
    'copyright intel' from .h -> .hpp (if not already hpp).

    * Replaced all references to .h with .hpp in documentation,
    source files, cmake collateral, example code, and swig interface
    files.

    * Replaced cmake variable module_h with module_hpp.

    * Intentionally left upm.h since this file currently does not
    contain code (documentation only).

Signed-off-by: Noel Eck <noel.eck@intel.com>
2016-04-28 14:00:54 -07:00

717 lines
24 KiB
C++

/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <string>
// we only support a BACnet RS-485 MS/TP datalink
#define BACDL_MSTP 1
#undef BACDL_ALL
// get a variety of bacnet-stack includes...
#include "bacdef.h"
#include "config.h"
#include "bactext.h"
#include "bacerror.h"
#include "iam.h"
#include "arf.h"
#include "tsm.h"
#include "address.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "whois.h"
#include "mstpdef.h"
#include "dlmstp.h"
namespace upm {
/**
* @brief BACNETMSTP base class
* @defgroup bacnetmstp libupm-bacnetmstp
* @ingroup uart
*/
/**
* @library bacnetmstp
* @sensor bacnetmstp
* @comname UPM API for BACNET MS/TP communications
* @con uart
* @web http://bacnet.sourceforge.net/
* @brief UPM API for BACNETMSTP
*
* This is a singleton class that provides services to UPM BACnet
* drivers (like E50HX) based on the bacnet-stack at
* http://bacnet.sourceforge.net . This class is implemented as a
* singleton due to the fact that the bacnet-stack implementation
* does not currently allow multiple simultaneous datalinks. We are
* using 0.8.3 of bacnet-stack. In the future this restriction may
* be lifted depending on bacnet-stack, but for now, you are
* stuck with only a single BACnet MS/TP datalink.
*
* This driver is not intended to be used by end users. It is
* intended for use with other UPM drivers that require access to a
* BACnet MS/TP (Master Slave/Token Passing) network over RS-485.
*
* For this reason, no examples are provided. If you wish to
* implement your own BACnet MS/TP driver, please look at the E50HX
* driver to see how this class can be used.
*
* Currently, only readProperty and writeProperty BACnet requests
* are supported. In the future, any other BACnet requests could be
* supported as well. readProperty and writeProperty should provide
* most of what you will need when communicating with BACnet
* devices. Since the source code is open, feel free to add other
* services as you see fit :)
*
* In order to make requests over an MS/TP network, you must be a
* BACnet master. initMaster() is responsible for configuring your
* underlying RS-485 network and starting a Master FSM (finite state
* machine) thread that will be responsible for identifying other
* Masters on the network and negotiating token passing. Your
* master can only transmit when it has the token.
*
* Fortunately, all of these messy details are handled for you by
* this class, or the underlying bacnet-stack library this class
* relies on.
*/
class BACNETMSTP {
// Constructor and destructor are protected
public:
// error types
typedef enum {
BACERR_TYPE_NONE = 0,
BACERR_TYPE_REJECT,
BACERR_TYPE_ABORT,
BACERR_TYPE_ERROR,
BACERR_TYPE_UPM
} BACERR_TYPE_T;
// command types we currently support
typedef enum {
BACCMD_NONE = 0,
BACCMD_READ_PROPERTY,
BACCMD_WRITE_PROPERTY
} BACCMD_TYPE_T;
/**
* Get our singleton instance, initializing it if neccessary. All
* requests to this class should be done through this instance
* accessor.
*
* @return static pointer to our class instance
*/
static BACNETMSTP* instance();
/**
* This function initializes the underlying BACNETMSTP Master
* singleton in the event it has not already been initialized. If
* the BACNETMSTP Master singleton has already been initialized,
* then this call will be ignored. There can be only one.
*
* @param port The serial port that the RS-485 interface is
* connected to.
* @param baudRate The baudrate of the RS-485 interface. All
* devices on a BACnet RS-485 bus must run at the same baudrate.
* Valid values are 9600, 19200, 38400, 57600, 76800, and 115200.
* @param deviceInstanceNumber This is the unique Device Object
* Instance number that will be used for our BACnet Master in
* order to communicate over the BACnet interface. This number
* must be between 1-4194302 and must be unique on the BACnet
* network.
* @param macAddr This is the MAC address of our BACnet Master.
* It must be unique on the BACnet segment, and must be between
* 1-127.
* @param maxMaster This specifies to our Master the maximum MAC
* address used by any other Masters on the BACnet network. This
* must be between 1-127, the default is 127. Do not change this
* unless you know what you are doing or you could introduce
* token passing errors on the BACnet network.
* @param maxInfoFrames This number specifies the maximum number
* of transmissions (like requests for data) our Master is allowed
* to make before passing the token to the next Master. The
* default is 1.
*/
void initMaster(std::string port, int baudRate, int deviceInstanceNumber,
int macAddr, int maxMaster=DEFAULT_MAX_MASTER,
int maxInfoFrames=1);
/**
* Perform a BACnet readProperty transaction. This function will
* return when either the transaction has completed, or an error
* has occurred. It requests the value of a property, belonging
* to a specific object instance on a specific device.
*
* @param targetDeviceInstanceID This is the Device Object
* Instance ID of the device to send the request to. This number
* will be unique for every device on the network. An address
* lookup will be performed the first time a request is made to a
* device using the WhoHas BACnet service. The result will be
* cached for further use.
* @param objType This is the BACnet object type of the object you
* wish to query. It should be one of the BACNET_OBJECT_TYPE
* values.
* @param objInstance This is the instance number of the Object
* you wish to access. It is an integer starting from 1.
* @param objProperty This is the property of the Object and
* instance you wish to access. It should be one of the
* BACNET_PROPERTY_ID values.
* @param arrayIndex This specifies the index number of an array
* property. This is not currently supported. Until it is, leave
* the default at BACNET_ARRAY_ALL.
* @return true if an error occurred, false otherwise.
*/
bool readProperty(uint32_t targetDeviceInstanceID,
BACNET_OBJECT_TYPE objType,
uint32_t objInstance,
BACNET_PROPERTY_ID objProperty,
uint32_t arrayIndex=BACNET_ARRAY_ALL);
/**
* Perform a BACnet writeProperty transaction. This function will
* return when either the transaction has completed, or an error
* has occurred. It writes the supplied value to a property,
* belonging to a specific object instance on a specific device.
*
* @param targetDeviceInstanceID This is the Device Object
* Instance ID of the device to send the request to. This number
* will be unique for every device on the network. An address
* lookup will be performed the first time a request is made to a
* device using the WhoHas BACnet service. The result will be
* cached for further use.
* @param objType This is the BACnet object type of the object you
* wish to query. It should be one of the BACNET_OBJECT_TYPE
* values.
* @param objInstance This is the instance number of the Object
* you wish to access. It is an integer starting from 1.
* @param objProperty This is the property of the Object and
* instance you wish to access. It should be one of the
* BACNET_PROPERTY_ID values.
* @param propValue This is a pointer to a
* BACNET_APPLICATION_DATA_VALUE structure containg the data value
* to write to the property. Use the createData*() methods to
* properly create these structures.
* @param propPriority This specifies the priority of a
* commandable property. Leave it at the default unless you know
* what you are doing. In addition, there is conflicting
* information in the bacnet-stack documentation as to whether
* this is even supported.
* @param arrayIndex This specifies the index number of an array
* property. This is not currently supported. Until it is, leave
* the default at BACNET_ARRAY_ALL.
* @return true if an error occurred, false otherwise.
*/
bool writeProperty(uint32_t targetDeviceInstanceID,
BACNET_OBJECT_TYPE objType,
uint32_t objInstance,
BACNET_PROPERTY_ID objProperty,
BACNET_APPLICATION_DATA_VALUE* propValue,
uint8_t propPriority=BACNET_NO_PRIORITY,
int32_t arrayIndex=BACNET_ARRAY_ALL);
/**
* After a successful readProperty request, this method can be used
* to return a BACNET_APPLICATION_DATA_VALUE structure containing
* the returned data.
*
* @return a BACNET_APPLICATION_DATA_VALUE structure containing
* the returned data.
*/
BACNET_APPLICATION_DATA_VALUE getData();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet data type of the returned data. It
* will be one of the BACNET_APPLICATION_TAG_* values.
*
* @return A BACNET_APPLICATION_TAG_* value
*/
uint8_t getDataType();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* Real. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_REAL, and the value returned cannot be
* safely converted, an exception is thrown.
*
* @return A floating point value representing the returned data
*/
float getDataTypeReal();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* Boolean. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_BOOLEAN, and the value returned cannot
* be safely converted, an exception is thrown.
*
* @return A boolean value representing the returned data
*/
bool getDataTypeBoolean();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* unsigned int. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_UNSIGNED_INT, and the value returned
* cannot be safely converted, an exception is thrown.
*
* @return An unsigned int value representing the returned data
*/
unsigned int getDataTypeUnsignedInt();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* signed int. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_SIGNED_INT, and the value returned
* cannot be safely converted, an exception is thrown.
*
* @return A signed int value representing the returned data
*/
int getDataTypeSignedInt();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* string. Most of the data types except Enum can be converted to
* a string. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_CHARACTER_STRING, and the value returned
* cannot be safely converted, an exception is thrown.
*
* @return A string value representing the returned data
*/
std::string getDataTypeString();
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as an
* enumeration. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_ENUMERATED an exception is thrown.
*
* @return An unsigned int representing a BACnet enumerant
*/
unsigned int getDataTypeEnum();
#if defined(BACAPP_DOUBLE)
/**
* After a successful readProperty request, this method can be
* used to return the BACnet dataype of the returned data as a
* double. If the data type (getDataType()) is not a
* BACNET_APPLICATION_TAG_DOUBLE, and the value returned cannot be
* safely converted, an exception is thrown.
*
* @return A double floating point value representing the returned data
*/
double getDataTypeDouble();
#endif // BACAPP_DOUBLE
/**
* This method is used to create and return an initialized
* BACNET_APPLICATION_DATA_VALUE containing a real (floating point
* value). A pointer to this returned structure can then be used
* with writeProperty().
*
* @param value The floating point value to initialize the structure to.
* @return An initialized structure containing the value
*/
BACNET_APPLICATION_DATA_VALUE createDataReal(float Real);
/**
* This method is used to create and return an initialized
* BACNET_APPLICATION_DATA_VALUE containing a boolean. A pointer
* to this returned structure can then be used with
* writeProperty().
*
* @param value The boolean value to initialize the structure to.
* @return An initialized structure containing the value
*/
BACNET_APPLICATION_DATA_VALUE createDataBool(bool value);
/**
* This method is used to create and return an initialized
* BACNET_APPLICATION_DATA_VALUE containing a signed integer. A
* pointer to this returned structure can then be used with
* writeProperty().
*
* @param value The signed integer value to initialize the structure to.
* @return An initialized structure containing the value
*/
BACNET_APPLICATION_DATA_VALUE createDataSignedInt(int value);
/**
* This method is used to create and return an initialized
* BACNET_APPLICATION_DATA_VALUE containing a unsigned integer. A
* pointer to this returned structure can then be used with
* writeProperty().
*
* @param value The unsigned integer value to initialize the
* structure to.
* @return An initialized structure containing the value
*/
BACNET_APPLICATION_DATA_VALUE createDataUnsignedInt(unsigned int value);
/**
* This method is used to create and return an initialized
* BACNET_APPLICATION_DATA_VALUE containing a character string. A
* pointer to this returned structure can then be used with
* writeProperty(). Strings are typically limited to 63 characters.
*
* @param value The character string value to initialize the
* structure to.
* @return An initialized structure containing the value
*/
BACNET_APPLICATION_DATA_VALUE createDataString(std::string value);
/**
* Return an enumration of the last error type to occur. The
* value returned will be one of the BACERR_TYPE_T values.
*
* @return The last error type to occur, one of the BACERR_TYPE_T
* values.
*/
BACERR_TYPE_T getErrorType()
{
return m_errorType;
};
/**
* In the event of a BACnet Reject error, return the error code.
*
* @return The Reject error code.
*/
uint8_t getRejectReason()
{
return m_rejectReason;
};
/**
* In the event of a BACnet Reject error, return the error string.
*
* @return The Reject error string.
*/
std::string getRejectString()
{
return m_rejectString;
};
/**
* In the event of a BACnet Abort error, return the Abort reason code.
*
* @return The Abort reason code.
*/
uint8_t getAbortReason()
{
return m_abortReason;
};
/**
* In the event of a BACnet Abort error, return the Abort string.
*
* @return The Abort error string.
*/
std::string getAbortString()
{
return m_abortString;
};
/**
* In the event of a general BACnet error, return the BACnet error class.
*
* @return One of the BACNET_ERROR_CLASS error class codes
*/
BACNET_ERROR_CLASS getErrorClass()
{
return m_errorClass;
};
/**
* In the event of a general BACnet error, return the BACnet error code.
*
* @return One of the BACNET_ERROR_CODE error codes
*/
BACNET_ERROR_CODE getErrorCode()
{
return m_errorCode;
};
/**
* In the event of a general BACnet error, return the BACnet error
* string.
*
* @return A string representing the BACnet error class and code.
*/
std::string getErrorString()
{
return m_errorString;
};
/**
* In the event of a non-BACnet UPM error, return a string
* describing the error.
*
* @return A string representing the UPM error.
*/
std::string getUPMErrorString()
{
return m_upmErrorString;
};
/**
* Check to see if initMaster) has already been called, and out
* master is initialized.
*
* @return true if the master is initialized, false otherwise
*/
bool isInitialized()
{
return m_initialized;
};
/**
* Return the port that was specified to initMaster().
*
* @return The port specified to initMaster().
*/
std::string getPort()
{
return m_port;
};
/**
* Return the Object Device Instance ID for our Master was
* specified to initMaster().
*
* @return The Object Device Instance ID for our Master.
*/
uint32_t getDeviceInstanceID()
{
return m_deviceInstanceID;
};
/**
* Return the maxInfoFrames parameter that was specified to initMaster().
*
* @return The maxInfoFrames parameter specified to initMaster().
*/
int getMaxInfoFrames()
{
return m_maxInfoFrames;
};
/**
* Return the maxMaster parameter that was specified to initMaster().
*
* @return The maxMaster parameter specified to initMaster().
*/
int getMaxMaster()
{
return m_maxMaster;
};
/**
* Return the baud rate parameter that was specified to initMaster().
*
* @return The baud rate parameter specified to initMaster().
*/
int getBaudRate()
{
return m_baudRate;
};
/**
* Return the MAC address parameter that was specified to initMaster().
*
* @return The MAC address parameter specified to initMaster().
*/
int getMACAddress()
{
return m_macAddr;
};
/**
* Enable or disable debugging output.
*
* @param enable true to enable debugging, false otherwise
*/
void setDebug(bool enable);
protected:
/**
* BACNETMSTP constructor
*/
BACNETMSTP();
/**
* BACNETMSTP Destructor
*/
~BACNETMSTP();
// clear/reset error states
void clearErrors();
// error handler
static void handlerError(BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code);
// abort handler
static void handlerAbort(BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t abort_reason,
bool server);
// reject handler
static void handlerReject(BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t reject_reason);
// our handler for dealing with return data from a ReadProperty call
static void handlerReadPropertyAck(uint8_t* service_request,
uint16_t service_len,
BACNET_ADDRESS* src,
BACNET_CONFIRMED_SERVICE_ACK_DATA* service_data);
// our handler for writeProp acks
static void handlerWritePropertyAck(BACNET_ADDRESS* src,
uint8_t invoke_id);
// initialize our service handlers
void initServiceHandlers();
// utility function
std::string string2HexString(std::string input);
// responsible for dispatching a request to the BACnet network
bool dispatchRequest();
private:
// prevent copying and assignment
BACNETMSTP(BACNETMSTP const &) {};
BACNETMSTP& operator=(BACNETMSTP const&) {};
// our class instance
static BACNETMSTP* m_instance;
// has the class been created yet?
bool m_initialized;
// Some items we can set for our master
std::string m_port;
int m_maxInfoFrames;
int m_maxMaster;
int m_baudRate;
int m_macAddr;
// the unique Instance Number of our Master Device Object
uint32_t m_deviceInstanceID;
// adpu timeout in milliseconds
uint16_t m_adpuTimeoutMS;
// buffer used for receiving data
uint8_t m_rxBuffer[MAX_MPDU];
// our error classfication
BACERR_TYPE_T m_errorType;
// BACnet reject info
uint8_t m_rejectReason;
std::string m_rejectString;
// BACnet abort info
uint8_t m_abortReason;
std::string m_abortString;
// BACnet error info
BACNET_ERROR_CLASS m_errorClass;
BACNET_ERROR_CODE m_errorCode;
std::string m_errorString;
// generic UPM related errors - we just set the error text to
// something informative.
std::string m_upmErrorString;
// our returned data from readProperty()
BACNET_APPLICATION_DATA_VALUE m_returnedValue;
// current bound target address of dispatched service request
// (read/write prop, etc)
BACNET_ADDRESS m_targetAddress;
// current invokeID (for transaction handling)
uint8_t m_invokeID;
// error detected flag
bool m_errorDetected;
// Commands - we create a struct to hold the arguments for each
// command type we support. Then, we create a command struct
// which contains the command type and a union containing the
// relevant arguments. This is used by dispatchRequest() to issue
// the correct request.
// these may generate SWIG warnings, but they can be ignored as we
// do not expose these outside the class
typedef struct {
uint32_t targetDeviceInstanceID;
BACNET_OBJECT_TYPE objType;
uint32_t objInstance;
BACNET_PROPERTY_ID objProperty;
uint32_t arrayIndex;
} READ_PROPERTY_ARGS_T;
typedef struct {
uint32_t targetDeviceInstanceID;
BACNET_OBJECT_TYPE objType;
uint32_t objInstance;
BACNET_PROPERTY_ID objProperty;
BACNET_APPLICATION_DATA_VALUE* propValue;
uint8_t propPriority;
int32_t arrayIndex;
} WRITE_PROPERTY_ARGS_T;
struct {
BACCMD_TYPE_T cmd;
union {
READ_PROPERTY_ARGS_T readPropArgs;
WRITE_PROPERTY_ARGS_T writePropArgs;
};
} m_command;
bool m_debugging;
};
}