upm/src/bacnetmstp/bacnetmstp.cxx
Noel Eck 0bd68e4e2b ANDROID: CMake/src changes to build on android-24
* Updated pom file generation: Generate pom files after all sensor
      library targets have been created - allows for dependencies
    * Changes for compiling on Android
    * Check for mraa build options: Look at symbols in mraa library to
      determine UPM build options (example: mraa_iio_init, mraa_firmata_init)
    * Add per target summary for C/C++/java/nodejs/python
    * Added hierarchy to fti include directory...
        old: #include "upm_voltage.h"
        new: #include "fti/upm_voltage.h"
    * Removed unimplemented methods from mpu9150 library and java example
    * Add utilities-c target for all c examples.  Most of the C examples
      rely on the upm_delay methods.  Add a dependency on the utilities-c
      target for all c examples.
    * Updated the examples/CMakeLists.txt to add dependencies passed via
      TARGETS to the target name parsed from the example name.  Also updated
      the interface example names to start with 'interfaces'.
    * Updated src/examples/CMakeLists.txt to ALWAYS remove examples from the
      example_src_list (moved this from end of function to beginning).

Signed-off-by: Noel Eck <noel.eck@intel.com>
2017-04-24 10:27:35 -07:00

940 lines
28 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.
*/
#include <unistd.h>
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <syslog.h>
#include "bacnetmstp.hpp"
#include "handlers.h"
#include "client.h"
#include "txbuf.h"
#include "mstpdef.h"
using namespace upm;
using namespace std;
// our singleton instance
BACNETMSTP* BACNETMSTP::m_instance = 0;
BACNETMSTP::BACNETMSTP()
{
// set defaults here
m_maxInfoFrames = DEFAULT_MAX_INFO_FRAMES;
m_maxMaster = DEFAULT_MAX_MASTER;
m_baudRate = 38400;
m_macAddr = DEFAULT_MAX_MASTER;
m_initialized = false;
// 60 sec, for MS/TP
m_adpuTimeoutMS = 60000;
m_deviceInstanceID = BACNET_MAX_INSTANCE;
memset(m_rxBuffer, 0, MAX_MPDU);
m_returnedValue.clear();
m_targetAddress = {0};
m_invokeID = 0;
m_errorDetected = false;
setDebug(false);
}
BACNETMSTP::~BACNETMSTP()
{
if (m_initialized)
datalink_cleanup();
}
void BACNETMSTP::setDebug(bool enable)
{
m_debugging = enable;
}
void BACNETMSTP::clearErrors()
{
m_errorType = BACERR_TYPE_NONE;
// empty out all of our error/reject/abort info
m_rejectReason = REJECT_REASON_OTHER;
m_rejectString.clear();
m_abortReason = ABORT_REASON_OTHER;
m_abortString.clear();
m_errorClass = ERROR_CLASS_DEVICE;
m_errorCode = ERROR_CODE_OTHER;
m_errorString.clear();
m_upmErrorString.clear();
}
void BACNETMSTP::handlerError(BACNET_ADDRESS* src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": entered" << endl;
if (address_match(&(instance()->m_targetAddress), src) &&
(invoke_id == instance()->m_invokeID))
{
instance()->m_errorType = BACERR_TYPE_ERROR;
instance()->m_errorClass = error_class;
instance()->m_errorCode = error_code;
instance()->m_errorString =
bactext_error_class_name((int)error_class)
+ string(": ") + bactext_error_code_name((int)error_code);
instance()->m_errorDetected = true;
}
}
void BACNETMSTP::handlerAbort(BACNET_ADDRESS* src,
uint8_t invoke_id,
uint8_t abort_reason,
bool server)
{
(void)server; // not used
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": entered" << endl;
if (address_match(&(instance()->m_targetAddress), src) &&
(invoke_id == instance()->m_invokeID))
{
instance()->m_errorType = BACERR_TYPE_ABORT;
instance()->m_abortReason = abort_reason;
instance()->m_abortString =
bactext_abort_reason_name((int)abort_reason);
instance()->m_errorDetected = true;
}
}
void BACNETMSTP::handlerReject(BACNET_ADDRESS* src,
uint8_t invoke_id,
uint8_t reject_reason)
{
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": entered" << endl;
if (address_match(&(instance()->m_targetAddress), src) &&
(invoke_id == instance()->m_invokeID))
{
instance()->m_errorType = BACERR_TYPE_REJECT;
instance()->m_rejectReason = reject_reason;
instance()->m_rejectString =
bactext_reject_reason_name((int)reject_reason);
instance()->m_errorDetected = true;
}
}
void BACNETMSTP::handlerReadPropertyAck(uint8_t* service_request,
uint16_t service_len,
BACNET_ADDRESS* src,
BACNET_CONFIRMED_SERVICE_ACK_DATA* service_data)
{
int len = 0;
BACNET_READ_PROPERTY_DATA data;
// clear our stored data
instance()->m_returnedValue.clear();
BACNET_APPLICATION_DATA_VALUE value;
memset((void *)&value, 0, sizeof(value));
uint8_t *application_data = 0;
int application_data_len = 0;
if (address_match(&(instance()->m_targetAddress), src) &&
(service_data->invoke_id == instance()->m_invokeID))
{
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": got readProp ack" << endl;
len = rp_ack_decode_service_request(service_request, service_len,
&data);
// store any delivered data elements
if (len > 0)
{
application_data_len = data.application_data_len;
application_data = data.application_data;
while (true)
{
len = bacapp_decode_application_data(application_data,
application_data_len,
&value);
if (len > 0)
{
// store a copy
instance()->m_returnedValue.push_back(value);
if (len < application_data_len)
{
// there is more data
application_data += len;
application_data_len -= len;
}
else
{
// we are done
break;
}
}
else
{
// shouldn't happen?
cerr << __FUNCTION__ << ": decode app data failed" << endl;
break;
}
}
}
}
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": STORED "
<< instance()->m_returnedValue.size()
<< " data elements." << endl;
}
void BACNETMSTP::handlerWritePropertyAck(BACNET_ADDRESS* src,
uint8_t invoke_id)
{
if (address_match(&(instance()->m_targetAddress), src) &&
(invoke_id == instance()->m_invokeID))
{
if (instance()->m_debugging)
cerr << __FUNCTION__ << ": got writeProp ack" << endl;
}
}
void BACNETMSTP::initServiceHandlers()
{
// this is in device-client.c
Device_Init(NULL);
// These are service requests we must handle from other masters
// we need to handle who-is to support dynamic device binding to us
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
// handle i-am to support binding to other devices
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
// set the handler for all the services we don't implement
// It is required to send the proper reject message...
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
// we must implement read property (it's required)
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
// These are related to requests we make
// handle the data coming back from confirmed readProp requests
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handlerReadPropertyAck);
// handle the simple ack for confirmed writeProp requests
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
handlerWritePropertyAck);
// handle any errors coming back
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, handlerError);
apdu_set_abort_handler(handlerAbort);
apdu_set_reject_handler(handlerReject);
}
BACNETMSTP* BACNETMSTP::instance()
{
if (!m_instance)
m_instance = new BACNETMSTP;
return m_instance;
}
void BACNETMSTP::initMaster(string port, int baudRate,
int deviceInstanceID, int macAddr, int maxMaster,
int maxInfoFrames)
{
// first some checking
// if we are already initialized, then it's too late to change things now
if (m_initialized)
{
if (m_debugging)
cerr << __FUNCTION__ << ": Instance is already initialized, ignored."
<< endl;
return;
}
// baudrate
// The standard allows (as of at least 2010) the following baud rates
if ( !(baudRate == 9600 || baudRate == 19200 || baudRate == 38400
|| baudRate == 57600 || baudRate == 76800 || baudRate == 115200) )
{
throw invalid_argument(string(__FUNCTION__)
+ ": baudRate must be 9600, 19200, 38400, "
+ " 57600, 76800, or 115200");
}
// maxMaster
// maxMaster must be less than or equal to 127
if (maxMaster < 0 || maxMaster > DEFAULT_MAX_MASTER)
{
throw out_of_range(string(__FUNCTION__)
+ ": maxMaster must be between 0 and "
+ to_string(DEFAULT_MAX_MASTER));
}
// As a master ourselves, we must have a MAC address also within the
// constraints of maxMaster
if (macAddr < 0 || macAddr > DEFAULT_MAX_MASTER)
{
throw out_of_range(string(__FUNCTION__)
+ ": macAddr must be between 0 and "
+ to_string(DEFAULT_MAX_MASTER));
}
// this should be unique on the network
if (deviceInstanceID >= BACNET_MAX_INSTANCE)
{
throw out_of_range(string(__FUNCTION__)
+ ": deviceInstanceID must be less than "
+ to_string(BACNET_MAX_INSTANCE)
+ ", and must be unique on the network");
}
m_port = port;
m_baudRate = baudRate;
m_maxInfoFrames = maxInfoFrames;
m_macAddr = macAddr;
m_maxMaster = maxMaster;
m_deviceInstanceID = deviceInstanceID;
// Let the fun begin...
// setup our info
Device_Set_Object_Instance_Number(m_deviceInstanceID);
address_init();
initServiceHandlers();
dlmstp_set_max_info_frames(m_maxInfoFrames);
dlmstp_set_max_master(m_maxMaster);
dlmstp_set_baud_rate(m_baudRate);
dlmstp_set_mac_address(m_macAddr);
// FIXME - allow to change?
apdu_timeout_set(m_adpuTimeoutMS);
// Ordinarily, I'd like to check the return value of this function,
// but even in the face of errors, it always returns true :( This
// function starts the ball rolling, and initializes the Master FSM
// thread. Unfortunately, it doesn't appear this can be turned back
// off without exiting the application.
datalink_init((char *)port.c_str());
m_initialized = true;
}
bool BACNETMSTP::dispatchRequest()
{
uint16_t pdu_len = 0;
unsigned timeout = 100; // milliseconds
unsigned max_apdu = 0;
time_t elapsed_seconds = 0;
time_t last_seconds = 0;
time_t current_seconds = 0;
time_t timeout_seconds = 0;
bool found = false;
// address where message came from
BACNET_ADDRESS src = {0};
clearErrors();
m_errorDetected = false;
uint32_t targetDeviceInstanceID = BACNET_MAX_INSTANCE;
switch (m_command.cmd)
{
case BACCMD_READ_PROPERTY:
targetDeviceInstanceID = m_command.readPropArgs.targetDeviceInstanceID;
break;
case BACCMD_WRITE_PROPERTY:
targetDeviceInstanceID = m_command.writePropArgs.targetDeviceInstanceID;
break;
case BACCMD_NONE:
{
m_errorType = BACERR_TYPE_UPM;
m_upmErrorString = string(__FUNCTION__) +
": called with BACCMD_NONE, ignoring";
return true; // error
}
break;
default:
{
// should this throw?
m_errorType = BACERR_TYPE_UPM;
m_upmErrorString = string(__FUNCTION__) +
": internal error, called with unknown command, ignoring";
return true; // error
}
break;
}
// timeouts
last_seconds = time(NULL);
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
// we use 0 to indicate that request hasn't been made yet, so that
// it will be made once the address is bound.
m_invokeID = 0;
// bind to the device first.
found = address_bind_request(targetDeviceInstanceID, &max_apdu,
&(instance()->m_targetAddress));
if (!found)
{
if (m_debugging)
cerr << __FUNCTION__ << ": Address not found, Sending WhoIs..." << endl;
Send_WhoIs(targetDeviceInstanceID, targetDeviceInstanceID);
}
else
{
if (m_debugging)
cerr << __FUNCTION__ << ": Address was found" << endl;
}
// loop until either we get our data, an error occurs, or we timeout
while (true)
{
current_seconds = time(NULL);
// at least one second has passed
if (current_seconds != last_seconds)
tsm_timer_milliseconds((uint16_t) ((current_seconds -
last_seconds) * 1000));
if (m_errorDetected)
break;
// we have to wait until the address is bound before proceeding
if (!found)
{
found =
address_bind_request(targetDeviceInstanceID, &max_apdu,
&(instance()->m_targetAddress));
}
if (found)
{
// address is bound, and we have not sent our request yet. Make it so.
if (m_invokeID == 0)
{
switch (m_command.cmd)
{
case BACCMD_READ_PROPERTY:
m_invokeID =
Send_Read_Property_Request(targetDeviceInstanceID,
m_command.readPropArgs.objType,
m_command.readPropArgs.objInstance,
m_command.readPropArgs.objProperty,
m_command.readPropArgs.arrayIndex);
if (m_debugging)
cerr << __FUNCTION__
<< ": Called Send_Read_Property_Request(), m_invokeID = "
<< (int)m_invokeID << endl;
break;
case BACCMD_WRITE_PROPERTY:
m_invokeID =
Send_Write_Property_Request(targetDeviceInstanceID,
m_command.writePropArgs.objType,
m_command.writePropArgs.objInstance,
m_command.writePropArgs.objProperty,
m_command.writePropArgs.propValue,
m_command.writePropArgs.propPriority,
m_command.writePropArgs.arrayIndex);
if (m_debugging)
cerr << __FUNCTION__
<< ": Called Send_Write_Property_Request(), m_invokeID = "
<< (int)m_invokeID << endl;
break;
default:
syslog(LOG_WARNING, "%s: switch case not defined",
string(__FUNCTION__).c_str());
}
}
else if (tsm_invoke_id_free(m_invokeID))
{
// transaction completed successfully
if (m_debugging)
cerr << __FUNCTION__ << ": Success, m_invokeID = "
<< (int)m_invokeID << endl;
break;
}
else if (tsm_invoke_id_failed(m_invokeID))
{
// transaction state machine failed, most likely timeout
tsm_free_invoke_id(m_invokeID);
m_errorType = BACERR_TYPE_UPM;
m_upmErrorString = string(__FUNCTION__) +
": TSM Timed Out.";
if (m_debugging)
cerr << m_upmErrorString << endl;
m_errorDetected = true;
break;
}
}
else
{
// still waiting to bind. timeout if we've waited too long.
elapsed_seconds += (current_seconds - last_seconds);
if (elapsed_seconds > timeout_seconds)
{
m_errorType = BACERR_TYPE_UPM;
m_upmErrorString = string(__FUNCTION__) +
": Timed out waiting to bind address.";
// We output this error unconditionally as this is an
// error you will get if you supply a non-existant
// Device Obeject Instance ID.
cerr << m_upmErrorString << endl;
cerr << __FUNCTION__
<< ": Did you supply the correct Device Object Instance ID "
<< "for your device?"
<< endl;
m_errorDetected = true;
break;
}
}
// returns 0 bytes on timeout
pdu_len = datalink_receive(&src, m_rxBuffer, MAX_MPDU, timeout);
// process the packet if valid. This will call our handlers as needed.
if (pdu_len)
npdu_handler(&src, m_rxBuffer, pdu_len);
// keep track of time for next check
last_seconds = current_seconds;
}
return m_errorDetected;
}
bool BACNETMSTP::readProperty(uint32_t targetDeviceInstanceID,
BACNET_OBJECT_TYPE objType,
uint32_t objInstance,
BACNET_PROPERTY_ID objProperty,
uint32_t arrayIndex)
{
// some sanity checking...
if (objInstance >= BACNET_MAX_INSTANCE)
{
throw out_of_range(string(__FUNCTION__)
+ ": objInstance must be less than "
+ to_string(BACNET_MAX_INSTANCE));
}
// fill in the command structure and dispatch
m_command.cmd = BACCMD_READ_PROPERTY;
m_command.readPropArgs.targetDeviceInstanceID = targetDeviceInstanceID;
m_command.readPropArgs.objType = objType;
m_command.readPropArgs.objInstance = objInstance;
m_command.readPropArgs.objProperty = objProperty;
m_command.readPropArgs.arrayIndex = arrayIndex;
if (m_debugging)
cerr << __FUNCTION__ << ": calling dispatchRequest()..." << endl;
// send it off
bool error = dispatchRequest();
// clear the command to avoid accidental re-calls
m_command.cmd = BACCMD_NONE;
return error;
}
bool BACNETMSTP::writeProperty(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)
{
// some sanity checking...
if (objInstance >= BACNET_MAX_INSTANCE)
{
throw out_of_range(string(__FUNCTION__)
+ ": objInstance must be less than "
+ to_string(BACNET_MAX_INSTANCE));
}
// fill in the command structure and dispatch
m_command.cmd = BACCMD_WRITE_PROPERTY;
m_command.writePropArgs.targetDeviceInstanceID = targetDeviceInstanceID;
m_command.writePropArgs.objType = objType;
m_command.writePropArgs.objInstance = objInstance;
m_command.writePropArgs.objProperty = objProperty;
m_command.writePropArgs.propValue = propValue;
m_command.writePropArgs.propPriority = propPriority;
m_command.writePropArgs.arrayIndex = arrayIndex;
if (m_debugging)
cerr << __FUNCTION__ << ": calling dispatchRequest()..." << endl;
// send it off
bool error = dispatchRequest();
// clear the command to avoid accidental re-calls
m_command.cmd = BACCMD_NONE;
return error;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::getData(int index)
{
return m_returnedValue.at(index);
}
int BACNETMSTP::getDataNumElements()
{
return m_returnedValue.size();
}
uint8_t BACNETMSTP::getDataType(int index)
{
return m_returnedValue.at(index).tag;
}
float BACNETMSTP::getDataTypeReal(int index)
{
if (getDataType(index) == BACNET_APPLICATION_TAG_REAL)
return m_returnedValue.at(index).type.Real;
else
{
if (m_debugging)
cerr << __FUNCTION__ << ": Not of Real type, trying to convert..." << endl;
// try to convert or throw
switch (getDataType(index))
{
case BACNET_APPLICATION_TAG_BOOLEAN:
return (getDataTypeBoolean(index) ? 1.0 : 0.0);
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
return float(getDataTypeUnsignedInt(index));
case BACNET_APPLICATION_TAG_SIGNED_INT:
return float(getDataTypeSignedInt(index));
default:
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to Real");
}
}
}
bool BACNETMSTP::getDataTypeBoolean(int index)
{
if (getDataType(index) == BACNET_APPLICATION_TAG_BOOLEAN)
return ((m_returnedValue.at(index).type.Boolean) ? true : false);
else
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to Bool");
}
unsigned int BACNETMSTP::getDataTypeUnsignedInt(int index)
{
if (getDataType(index) == BACNET_APPLICATION_TAG_UNSIGNED_INT)
return m_returnedValue.at(index).type.Unsigned_Int;
else
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to UnsignedInt");
}
int BACNETMSTP::getDataTypeSignedInt(int index)
{
if (getDataType(index) == BACNET_APPLICATION_TAG_SIGNED_INT)
return m_returnedValue.at(index).type.Signed_Int;
else
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to SignedInt");
}
#if defined(BACAPP_DOUBLE)
double BACNETMSTP::getDataTypeDouble(int index)
{
if (getDataType() == BACNET_APPLICATION_TAG_DOUBLE)
return m_returnedValue.at(index).type.Double;
else
{
if (m_debugging)
cerr << __FUNCTION__ << ": Not of Double type, trying to convert..." << endl;
// try to convert or throw
switch (getDataType(index))
{
case BACNET_APPLICATION_TAG_REAL:
return double(getDataTypeReal(index));
case BACNET_APPLICATION_TAG_BOOLEAN:
return (getDataTypeBoolean(index) ? 1.0 : 0.0);
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
return double(getDataTypeUnsignedInt(index));
case BACNET_APPLICATION_TAG_SIGNED_INT:
return double(getDataTypeSignedInt(index));
default:
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to Double");
}
}
}
#endif // BACAPP_DOUBLE
unsigned int BACNETMSTP::getDataTypeEnum(int index)
{
if (getDataType(index) == BACNET_APPLICATION_TAG_ENUMERATED)
return m_returnedValue.at(index).type.Enumerated;
else
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to Enum");
}
string BACNETMSTP::getDataTypeString(int index)
{
string retval;
// Here, we can try to accomodate all the types
switch(getDataType(index))
{
case BACNET_APPLICATION_TAG_REAL:
retval = to_string(getDataTypeReal(index));
break;
#if defined(BACAPP_DOUBLE)
case BACNET_APPLICATION_TAG_DOUBLE:
retval = to_string(getDataTypeDouble(index));
break;
#endif // BACAPP_DOUBLE
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
retval = to_string(getDataTypeUnsignedInt(index));
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
retval = to_string(getDataTypeSignedInt(index));
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
retval = (getDataTypeBoolean(index) ? string("true") : string("false"));
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
retval = string(characterstring_value(&m_returnedValue.at(index).type.Character_String),
characterstring_length(&m_returnedValue.at(index).type.Character_String));
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
{
string tmpstr((char *)octetstring_value(&m_returnedValue.at(index).type.Octet_String),
octetstring_length(&m_returnedValue.at(index).type.Octet_String));
retval = string2HexString(tmpstr);
}
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
{
int len = bitstring_bits_used(&m_returnedValue.at(index).type.Bit_String);
for (int i=0; i<len; i++)
{
if (bitstring_bit(&m_returnedValue.at(index).type.Bit_String,
uint8_t(i)))
retval += "1";
else
retval += "0";
if (i != 0 && ((i % 8) == 0))
retval += " ";
}
}
break;
default:
throw invalid_argument(string(__FUNCTION__)
+ ": data type ("
+ to_string(int(getDataType(index)))
+ ") is not convertible to String");
break;
}
return retval;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataReal(float value)
{
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_REAL;
data.type.Real = value;
return data;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataBool(bool value)
{
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_BOOLEAN;
data.type.Boolean = value;
return data;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataSignedInt(int value)
{
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_SIGNED_INT;
data.type.Signed_Int = value;
return data;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataUnsignedInt(unsigned int value)
{
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
data.type.Unsigned_Int = value;
return data;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataString(string value)
{
if (value.size() > (MAX_CHARACTER_STRING_BYTES - 1))
{
throw invalid_argument(string(__FUNCTION__)
+ ": value must be less than or equal to "
+ to_string(MAX_CHARACTER_STRING_BYTES - 1)
+ " characters long");
}
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_CHARACTER_STRING;
characterstring_init_ansi(&data.type.Character_String, value.c_str());
return data;
}
BACNET_APPLICATION_DATA_VALUE BACNETMSTP::createDataEnum(uint32_t value)
{
BACNET_APPLICATION_DATA_VALUE data;
memset(&data, 0, sizeof(BACNET_APPLICATION_DATA_VALUE));
data.tag = BACNET_APPLICATION_TAG_ENUMERATED;
data.type.Enumerated = value;
return data;
}
string BACNETMSTP::string2HexString(string input)
{
static const char* const lut = "0123456789abcdef";
size_t len = input.size();
string output;
output.reserve(3 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
output.push_back(' ');
}
return output;
}