From b7f038de3d579f9918c8c766e30c8733e1b4ec2a Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Fri, 10 Jun 2016 12:09:38 -0600 Subject: [PATCH] bacnetmstp: add new bacnetutil class to bacnetmstp UPM library There is some functionality that will always be needed for BACnet drivers. Here we create a new bacnetutil class, built as part of the bacnetmstp library that can handle much of the data handling and setup a BACnet driver will need. The idea is that any BACnet functionality needed, that is not device-specific, should be added to this class for all drivers to use. The intent is that all BACnet drivers will inherit from this class. Signed-off-by: Jon Trulson --- src/bacnetmstp/CMakeLists.txt | 4 +- src/bacnetmstp/bacnetutil.cxx | 925 ++++++++++++++++++++++++++++ src/bacnetmstp/bacnetutil.hpp | 559 +++++++++++++++++ src/bacnetmstp/javaupm_bacnetmstp.i | 6 +- 4 files changed, 1489 insertions(+), 5 deletions(-) create mode 100644 src/bacnetmstp/bacnetutil.cxx create mode 100644 src/bacnetmstp/bacnetutil.hpp diff --git a/src/bacnetmstp/CMakeLists.txt b/src/bacnetmstp/CMakeLists.txt index cf1a5d35..8f23f7e8 100644 --- a/src/bacnetmstp/CMakeLists.txt +++ b/src/bacnetmstp/CMakeLists.txt @@ -1,7 +1,7 @@ set (libname "bacnetmstp") set (libdescription "upm driver module for BACnet MS/TP devices") -set (module_src ${libname}.cxx device-client.c) -set (module_hpp ${libname}.hpp) +set (module_src ${libname}.cxx device-client.c bacnetutil.cxx) +set (module_hpp ${libname}.hpp bacnetutil.hpp) pkg_check_modules(BACNET libbacnet) if (BACNET_FOUND) diff --git a/src/bacnetmstp/bacnetutil.cxx b/src/bacnetmstp/bacnetutil.cxx new file mode 100644 index 00000000..17d555a4 --- /dev/null +++ b/src/bacnetmstp/bacnetutil.cxx @@ -0,0 +1,925 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include +#include +#include +#include + +#include "bacnetutil.hpp" + +using namespace upm; +using namespace std; + + +BACNETUTIL::BACNETUTIL(uint32_t targetDeviceObjectID) : + m_instance(0) +{ + // Save our device's ID + m_targetDeviceObjectID = targetDeviceObjectID; + + // create the BACNETMSTP instance here if it does not already exist, + // and store the pointer in our class to save on some typing. + + m_instance = BACNETMSTP::instance(); + + // now see if it has been initialized yet for init() + m_initialized = m_instance->isInitialized(); + + setDebug(false); + + // we disable this by default for performance reasons + checkReliability(false); + + // empty our unit caches + m_avUnitCache.clear(); + m_aiUnitCache.clear(); + + // empty our msv info store + m_msvInfo.clear(); + + // empty our binary info stores + m_bvInfo.clear(); + m_biInfo.clear(); +} + +BACNETUTIL::~BACNETUTIL() +{ +} + +void BACNETUTIL::initMaster(std::string port, int baudRate, + int deviceInstanceID, int macAddr, int maxMaster, + int maxInfoFrames) +{ + // first we check to see if the bacnetmstp instance has already been + // initialized (determined in the ctor). If not, we will do so here + // with the arguments specified. If it has already been + // initialized, then we do not bother calling bacnetmstp's init + // again as it will just be ignored. + + if (!m_initialized) + m_instance->initMaster(port, baudRate, deviceInstanceID, + macAddr, maxMaster, maxInfoFrames); + + // either it threw an exception, was already initialized or it's + // initialized now... + m_initialized = true; +} + +void BACNETUTIL::setDebug(bool enable) +{ + m_debugging = enable; + + // we also enable/disable debugging in BACNETMSTP + m_instance->setDebug(enable); +} + +float BACNETUTIL::getAnalogValue(uint32_t objInstance) +{ + // check reliability first, if enabled + if (m_checkReliability) + { + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE, + objInstance, PROP_RELIABILITY)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (reliability): " << getAllErrorString() + << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + getAllErrorString()); + } + + BACNET_RELIABILITY reliable = + static_cast(m_instance->getDataTypeEnum()); + + if (reliable != RELIABILITY_NO_FAULT_DETECTED) + { + if (m_debugging) + cerr << __FUNCTION__ << ": Reliability check failed" << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": Reliability check failed"); + } + } + + // now get the value + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE, + objInstance, PROP_PRESENT_VALUE)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (value): " << getAllErrorString() + << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + return m_instance->getDataTypeReal(); +} + +void BACNETUTIL::setAnalogValue(uint32_t objInstance, + float value) +{ + // Write the value + BACNET_APPLICATION_DATA_VALUE myData = + m_instance->createDataReal(value); + + // write it + if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE, + objInstance, PROP_PRESENT_VALUE, + &myData)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } +} + +string BACNETUTIL::getAnalogValueUnits(uint32_t objInstance) +{ + // see if it exists + if (m_avUnitCache.count(objInstance) == 0) + { + // then we need to fetch it + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE, + objInstance, PROP_UNITS)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + // set to empty string + m_avUnitCache[objInstance] = string(""); + } + else + { + // cache it for future calls + m_avUnitCache[objInstance] = + string(bactext_engineering_unit_name(m_instance->getDataTypeEnum())); + } + } + + return m_avUnitCache[objInstance]; +} + +float BACNETUTIL::getAnalogInput(uint32_t objInstance) +{ + // check reliability first, if enabled + if (m_checkReliability) + { + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_INPUT, + objInstance, PROP_RELIABILITY)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (reliability): " + << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + getAllErrorString()); + } + + BACNET_RELIABILITY reliable = + static_cast(m_instance->getDataTypeEnum()); + + if (reliable != RELIABILITY_NO_FAULT_DETECTED) + { + if (m_debugging) + cerr << __FUNCTION__ << ": Reliability check failed" << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": Reliability check failed"); + } + } + + // now get the value + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_INPUT, + objInstance, PROP_PRESENT_VALUE)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (value): " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + return m_instance->getDataTypeReal(); +} + +bool BACNETUTIL::getBinaryInput(uint32_t objInstance) +{ + // check the BV info, and update/cache the data if needed + updateBinaryInputInfo(objInstance); + + // check reliability first, if enabled + if (m_checkReliability) + { + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_BINARY_INPUT, + objInstance, PROP_RELIABILITY)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (reliability): " + << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + getAllErrorString()); + } + + BACNET_RELIABILITY reliable = + static_cast(m_instance->getDataTypeEnum()); + + if (reliable != RELIABILITY_NO_FAULT_DETECTED) + { + if (m_debugging) + cerr << __FUNCTION__ << ": Reliability check failed" << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": Reliability check failed"); + } + } + + // now get the value + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_BINARY_INPUT, + objInstance, PROP_PRESENT_VALUE)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (value): " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + BACNET_BINARY_PV bpv = + static_cast(m_instance->getDataTypeEnum()); + + return (bpv == BINARY_INACTIVE) ? false : true; +} + +bool BACNETUTIL::getBinaryValue(uint32_t objInstance) +{ + // check the BV info, and update/cache the data if needed + updateBinaryValueInfo(objInstance); + + // check reliability first, if enabled + if (m_checkReliability) + { + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_BINARY_VALUE, + objInstance, PROP_RELIABILITY)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (reliability): " + << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + getAllErrorString()); + } + + BACNET_RELIABILITY reliable = + static_cast(m_instance->getDataTypeEnum()); + + if (reliable != RELIABILITY_NO_FAULT_DETECTED) + { + if (m_debugging) + cerr << __FUNCTION__ << ": Reliability check failed" << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": Reliability check failed"); + } + } + + // now get the value + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_BINARY_VALUE, + objInstance, PROP_PRESENT_VALUE)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (value): " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + BACNET_BINARY_PV bpv = + static_cast(m_instance->getDataTypeEnum()); + + return (bpv == BINARY_INACTIVE) ? false : true; +} + +void BACNETUTIL::setBinaryValue(uint32_t objInstance, + bool value) +{ + BACNET_BINARY_PV bpv = (value) ? BINARY_ACTIVE : BINARY_INACTIVE; + + // Write the value + BACNET_APPLICATION_DATA_VALUE myData = + m_instance->createDataEnum(bpv); + + // write it + if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_BINARY_VALUE, + objInstance, PROP_PRESENT_VALUE, + &myData)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } +} + +string BACNETUTIL::getAnalogInputUnits(uint32_t objInstance) +{ + // see if it exists + if (m_aiUnitCache.count(objInstance) == 0) + { + // then we need to fetch it + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_ANALOG_INPUT, + objInstance, PROP_UNITS)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + // set to empty string + m_aiUnitCache[objInstance] = string(""); + } + else + { + // cache it for future calls + m_aiUnitCache[objInstance] = + string(bactext_engineering_unit_name(m_instance->getDataTypeEnum())); + } + } + + return m_aiUnitCache[objInstance]; +} + +unsigned int BACNETUTIL::getMultiStateValue(uint32_t objInstance) +{ + // check the MSV info, and update/cache the data if needed + updateMultiStateValueInfo(objInstance); + + // check reliability first, if enabled + if (m_checkReliability) + { + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_MULTI_STATE_VALUE, + objInstance, PROP_RELIABILITY)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (reliability): " + << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + getAllErrorString()); + } + + BACNET_RELIABILITY reliable = + static_cast(m_instance->getDataTypeEnum()); + + if (reliable != RELIABILITY_NO_FAULT_DETECTED) + { + if (m_debugging) + cerr << __FUNCTION__ << ": Reliability check failed" << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": Reliability check failed"); + } + } + + // now get the value + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_MULTI_STATE_VALUE, + objInstance, PROP_PRESENT_VALUE)) + { + if (m_debugging) + cerr << __FUNCTION__ << ": (value): " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + return m_instance->getDataTypeUnsignedInt(); +} + +void BACNETUTIL::updateMultiStateValueInfo(uint32_t objInstance) +{ + // bail if we already have information on this msv + if (m_msvInfo.count(objInstance) != 0) + return; + + // we need to fetch information on MSV's - number of states, and + // possibly the state-text, if present + + // get the number of values possible (required) + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_MULTI_STATE_VALUE, + objInstance, PROP_NUMBER_OF_STATES)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (number of states): " + << getAllErrorString() + << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } + + // Add the entry... + m_msvInfo[objInstance].numStates = m_instance->getDataTypeUnsignedInt(); + + if (m_debugging) + cerr << __FUNCTION__ + << ": number of states: " + << m_msvInfo[objInstance].numStates + << endl; + + // now get the state-text. This is optional, so we will not throw + // here. + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_MULTI_STATE_VALUE, + objInstance, PROP_STATE_TEXT)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (state-text): " + << getAllErrorString() + << endl; + } + + // store them + int numElements = m_instance->getDataNumElements(); + + if (m_debugging) + cerr << __FUNCTION__ << ": numElements: " << numElements << endl; + + if (numElements > 0) + { + for (int i=0; igetDataTypeString(i)); + + if (m_debugging) + cerr << __FUNCTION__ << ": " << int(objInstance) << ", " + << i << ": " + << "added state text string: " + << m_msvInfo[objInstance].stateList.at(i) + << endl; + } + } + + return; +} + +void BACNETUTIL::deleteMultiStateValueInfo(uint32_t objInstance) +{ + // if there is no data stored for this objInstance yet, then we do + // not need to do anything. + if (m_msvInfo.count(objInstance) == 0) + return; + + // Now, we just erase the entry, and it will be updated the next + // time the MSV is accessed. + m_msvInfo.erase(objInstance); + + return; +} + +string BACNETUTIL::lookupMultiStateValueText(uint32_t objInstance, + unsigned int value) +{ + // verify that we have the relevant object data cached. If not, go + // get it. + updateMultiStateValueInfo(objInstance); + + // verify that value is valid for this object + if (value == 0 || value > m_msvInfo[objInstance].numStates) + throw std::out_of_range(std::string(__FUNCTION__) + + ": value supplied is invalid. Maximum " + + "allowed values are 1 to " + + std::to_string(m_msvInfo[objInstance].numStates) + + " for this object"); + + + // at this point either it failed or suceeded. If it suceeded, then + // we will see if any state text was retrieved. If no text is + // available (it is an optional property), then we will simply + // return the value itself as a string. + + if (m_msvInfo[objInstance].stateList.size() > 0) + { + // we have state-text and a usable value. + + // value should never be 0 at this point, so compensate for + // indexing into stateList which is 0-based. + value--; + + return m_msvInfo[objInstance].stateList.at(value); + } + + // no stateList text available, so just return value as a string + return std::to_string(value); +} + +unsigned int BACNETUTIL::getMultiStateValueMaxStates(uint32_t objInstance) +{ + // check the MSV info, and update/cache the data if needed + updateMultiStateValueInfo(objInstance); + + return m_msvInfo[objInstance].numStates; +} + +string BACNETUTIL::getMultiStateValueText(uint32_t objInstance) +{ + unsigned int value = getMultiStateValue(objInstance); + + return lookupMultiStateValueText(objInstance, value); +} + +void BACNETUTIL::setMultiStateValue(uint32_t objInstance, + unsigned int value) +{ + // check the MSV info, and update/cache the data if needed + updateMultiStateValueInfo(objInstance); + + // Check value against the valid limits + + if (value == 0 || value > m_msvInfo[objInstance].numStates) + throw std::invalid_argument(std::string(__FUNCTION__) + + ": value supplied is invalid. Maximum " + + "allowed values are 1 to " + + std::to_string(m_msvInfo[objInstance].numStates) + + " for this object"); + + // Write the value + BACNET_APPLICATION_DATA_VALUE myData = + m_instance->createDataUnsignedInt(value); + + // write it + if (m_instance->writeProperty(m_targetDeviceObjectID, + OBJECT_MULTI_STATE_VALUE, + objInstance, PROP_PRESENT_VALUE, + &myData)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + throw std::runtime_error(std::string(__FUNCTION__) + + ": " + + getAllErrorString()); + } +} + +void BACNETUTIL::updateBinaryValueInfo(uint32_t objInstance) +{ + // bail if we already have information on this object + if (m_bvInfo.count(objInstance) != 0) + return; + + // fetch inactive/active text. These are optional accordingto the + // spec, so we will not throw if they do not exist. + + // get inactive text + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_BINARY_VALUE, + objInstance, PROP_INACTIVE_TEXT)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (inactive text): " + << getAllErrorString() + << endl; + + m_bvInfo[objInstance].inactiveText = "inactive"; + } + else + { + m_bvInfo[objInstance].inactiveText = m_instance->getDataTypeString(); + } + + // get active text + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_BINARY_VALUE, + objInstance, PROP_ACTIVE_TEXT)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (active text): " + << getAllErrorString() + << endl; + + m_bvInfo[objInstance].activeText = "active"; + } + else + { + m_bvInfo[objInstance].activeText = m_instance->getDataTypeString(); + } + + return; +} + +void BACNETUTIL::deleteBinaryValueInfo(uint32_t objInstance) +{ + // if there is no data stored for this objInstance yet, then we do + // not need to do anything. + if (m_bvInfo.count(objInstance) == 0) + return; + + // Now, we just erase the entry, and it will be updated the next + // time it is accessed. + m_bvInfo.erase(objInstance); + + return; +} + +void BACNETUTIL::updateBinaryInputInfo(uint32_t objInstance) +{ + // bail if we already have information on this object + if (m_biInfo.count(objInstance) != 0) + return; + + // fetch inactive/active text. These are optional accordingto the + // spec, so we will not throw if they do not exist. + + // get inactive text + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_BINARY_INPUT, + objInstance, PROP_INACTIVE_TEXT)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (inactive text): " + << getAllErrorString() + << endl; + + m_biInfo[objInstance].inactiveText = "inactive"; + } + else + { + m_biInfo[objInstance].inactiveText = m_instance->getDataTypeString(); + } + + // get active text + if (m_instance->readProperty(m_targetDeviceObjectID, + OBJECT_BINARY_INPUT, + objInstance, PROP_ACTIVE_TEXT)) + { + if (m_debugging) + cerr << __FUNCTION__ + << ": (active text): " + << getAllErrorString() + << endl; + + m_biInfo[objInstance].activeText = "active"; + } + else + { + m_biInfo[objInstance].activeText = m_instance->getDataTypeString(); + } + + return; +} + +void BACNETUTIL::deleteBinaryInputInfo(uint32_t objInstance) +{ + // if there is no data stored for this objInstance yet, then we do + // not need to do anything. + if (m_biInfo.count(objInstance) == 0) + return; + + // Now, we just erase the entry, and it will be updated the next + // time it is accessed. + m_biInfo.erase(objInstance); + + return; +} + +string BACNETUTIL::lookupBinaryInputText(uint32_t objInstance, bool value) +{ + // cache relevant data if necessary + updateBinaryInputInfo(objInstance); + + if (value) + return m_biInfo[objInstance].activeText; + else + return m_biInfo[objInstance].inactiveText; +} + +string BACNETUTIL::getBinaryInputText(uint32_t objInstance) +{ + bool value = getBinaryInput(objInstance); + + return lookupBinaryInputText(objInstance, value); +} + +string BACNETUTIL::lookupBinaryValueText(uint32_t objInstance, bool value) +{ + // cache relevant data if necessary + updateBinaryValueInfo(objInstance); + + if (value) + return m_bvInfo[objInstance].activeText; + else + return m_bvInfo[objInstance].inactiveText; +} + +string BACNETUTIL::getBinaryValueText(uint32_t objInstance) +{ + bool value = getBinaryValue(objInstance); + + return lookupBinaryValueText(objInstance, value); +} + +BACNETMSTP::BACERR_TYPE_T BACNETUTIL::getErrorType() +{ + return m_instance->getErrorType(); +} + +uint8_t BACNETUTIL::getRejectReason() +{ + return m_instance->getRejectReason(); +} + +std::string BACNETUTIL::getRejectString() +{ + return m_instance->getRejectString(); +} + +uint8_t BACNETUTIL::getAbortReason() +{ + return m_instance->getAbortReason(); +} + +std::string BACNETUTIL::getAbortString() +{ + return m_instance->getAbortString(); +} + +BACNET_ERROR_CLASS BACNETUTIL::getErrorClass() +{ + return m_instance->getErrorClass(); +} + +BACNET_ERROR_CODE BACNETUTIL::getErrorCode() +{ + return m_instance->getErrorCode(); +} + +std::string BACNETUTIL::getUPMErrorString() +{ + return m_instance->getUPMErrorString(); +} + +std::string BACNETUTIL::getErrorString() +{ + return m_instance->getErrorString(); +}; + +string BACNETUTIL::getAllErrorString() +{ + switch (m_instance->getErrorType()) + { + case BACNETMSTP::BACERR_TYPE_NONE: + return string("No Error"); + break; + + case BACNETMSTP::BACERR_TYPE_REJECT: + return string("Reject: ") + getRejectString(); + break; + + case BACNETMSTP::BACERR_TYPE_ABORT: + return string("Abort: ") + getAbortString(); + break; + + case BACNETMSTP::BACERR_TYPE_ERROR: + return string("Error: ") + getErrorString(); + break; + + case BACNETMSTP::BACERR_TYPE_UPM: + return string("UPM Error: ") + getUPMErrorString(); + break; + } +} + +string BACNETUTIL::getDeviceDescription() +{ + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_DEVICE, + m_targetDeviceObjectID, PROP_DESCRIPTION)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + return ""; + } + + return m_instance->getDataTypeString(); +} + +string BACNETUTIL::getDeviceLocation() +{ + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_DEVICE, + m_targetDeviceObjectID, PROP_LOCATION)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + return ""; + } + + return m_instance->getDataTypeString(); +} + +bool BACNETUTIL::setDeviceLocation(string location) +{ + BACNET_APPLICATION_DATA_VALUE myLocation = + m_instance->createDataString(location); + + // write the Device Object Location + if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_DEVICE, + m_targetDeviceObjectID, PROP_LOCATION, + &myLocation)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + return false; + } + + return true; +} + +string BACNETUTIL::getDeviceName() +{ + if (m_instance->readProperty(m_targetDeviceObjectID, OBJECT_DEVICE, + m_targetDeviceObjectID, PROP_OBJECT_NAME)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + return ""; + } + + return m_instance->getDataTypeString(); +} + +bool BACNETUTIL::setDeviceName(string name) +{ + if (name.size() < 1) + { + throw std::invalid_argument(std::string(__FUNCTION__) + + ": name must have at least one character"); + } + + BACNET_APPLICATION_DATA_VALUE myName = + m_instance->createDataString(name); + + // write the Device Object Location + if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_DEVICE, + m_targetDeviceObjectID, PROP_OBJECT_NAME, + &myName)) + { + // error occurred + if (m_debugging) + cerr << __FUNCTION__ << ": " << getAllErrorString() << endl; + + return false; + } + + return true; +} diff --git a/src/bacnetmstp/bacnetutil.hpp b/src/bacnetmstp/bacnetutil.hpp new file mode 100644 index 00000000..463d1d89 --- /dev/null +++ b/src/bacnetmstp/bacnetutil.hpp @@ -0,0 +1,559 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include + +#include "bacnetmstp.hpp" + +namespace upm { + + /** + * @library bacnetmstp + * @comname UPM Utility API for BACnet + * @con uart + * + * @brief UPM Utility API for BACnet + * + * This class implements some common access functions that are + * useful to any driver making use of the bacnetmstp driver. + * + * It provides some basic functionality for reading and writing a + * proprty (with and without relability checking) as well as access + * to error conditions. It is intended to be inherited by your + * driver class. + */ + + class BACNETUTIL { + public: + + /** + * BACNETUTIL constructor + * + */ + BACNETUTIL(uint32_t targetDeviceObjectID); + + /** + * BACNETUTIL Destructor + */ + virtual ~BACNETUTIL(); + + /** + * 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. + * + * @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. + */ + virtual void initMaster(std::string port, int baudRate, + int deviceInstanceNumber, + int macAddr, int maxMaster=DEFAULT_MAX_MASTER, + int maxInfoFrames=1); + + /** + * Enable some debugging output in this module as well as the + * BACNETMSTP module. Debugging is disabled by default. + * + * @param enable true to enable, false to disable. + */ + virtual void setDebug(bool enable); + + /** + * Retrieve the Present_Value property of an Analog Value object. + * If checkReliability() has been enabled, then the Reliability + * property of the object will be retrieved first. If the + * Reliability property is anything other than + * RELIABILITY_NO_FAULT_DETECTED, then the method will throw. + * Reliability checking is disabled by default for performance + * reasons. + * + * @param objInstance The Analog Value Object instance. + * @return The floating point value requested. + */ + virtual float getAnalogValue(uint32_t objInstance); + + /** + * Set the Present_Value property of an Analog Value object. This + * method will throw on an error. + * + * @param objInstance The Analog Value Object instance. + * @param value The data value to write. + */ + virtual void setAnalogValue(uint32_t objInstance, + float value); + + /** + * Retrieve the Present_Value property of an Analog Input object. + * If checkReliability() has been enabled, then the Reliability + * property of the object will be retrieved first. If the + * Reliability property is anything other than + * RELIABILITY_NO_FAULT_DETECTED, then the method will throw. + * Reliability checking is disabled by default for performance + * reasons. + * + * @param objInstance The Analog Input Object instance. + * @return the floating point value requested. + */ + virtual float getAnalogInput(uint32_t objInstance); + + /** + * Query an Analog Value object for the unit code, translate it + * into a string and cache the result for future use. Return the + * BACnet text for the Unit enumeration. Unit enumerations are + * things like 'kilowatt-hours', 'volts', etc. For Objects which + * do not have a Units property defined for them, or for which + * Units has no meaning, 'no-units' will typically be returned and + * cached for the object. + * + * @param objInstance The Analog Value Object instance. + * @return A string representing the Object's Unit property. + */ + virtual std::string getAnalogValueUnits(uint32_t objInstance); + + /** + * Query an Analog Input object for the unit code, translate it + * into a string and cache the result for future use. Return the + * BACnet text for the Unit enumeration. Unit enumerations are + * things like 'kilowatt-hours', 'volts', etc. For Objects which + * do not have a Units property defined for them, or for which + * Units has no meaning, 'no-units' will typically be returned and + * cached for the object. + * + * @param objInstance The Analog Input Object instance. + * @return A string representing the Object's Unit property. + */ + virtual std::string getAnalogInputUnits(uint32_t objInstance); + + /** + * Retrieve the Present_Value property of a Binary Input object. + * If checkReliability() has been enabled, then the Reliability + * property of the object will be retrieved first. If the + * Reliability property is anything other than + * RELIABILITY_NO_FAULT_DETECTED, then the method will throw. + * Reliability checking is disabled by default for performance + * reasons. + * + * @param objInstance The Object Instance number to query + * @return the boolean point value requested + */ + virtual bool getBinaryInput(uint32_t objInstance); + + /** + * Lookup (retrieve if necessary) the Inactive_Text and + * Active_Text properties of a Binary Input object. These text + * properties are optional and can provide a string representing a + * given state (true/false) that can be more informational than + * just the boolean value the object represents. This is useful + * in applications that display this data to a user for example. + * If this text is not present in the object (as it is not + * required), then a string representation of the value will be + * returned instead ("active" and "inactive"). + * + * @param objInstance The Object Instance number of the object + * @param value The value you want to lookup the text + * representation for. + * @return The string representing the value. + */ + virtual std::string lookupBinaryInputText(uint32_t objInstance, bool value); + + /** + * Return a string representation of the Present_Value property of + * a BinaryInput object. This method just calls getBinaryInput() + * on the object, uses lookupBinaryInputText() to lookup the + * corresponding text value, and returns the result. + * + * @param objInstance The Object Instance number of the object. + * @return The string representing the value. + */ + virtual std::string getBinaryInputText(uint32_t objInstance); + + /** + * Retrieve the Present_Value property of a Binary Value object. + * If checkReliability() has been enabled, then the Reliability + * property of the object will be retrieved first. If the + * Reliability property is anything other than + * RELIABILITY_NO_FAULT_DETECTED, then the method will throw. + * Reliability checking is disabled by default for performance + * reasons. + * + * @param objInstance The Object Instance number to query + * @return the boolean point value requested + */ + virtual bool getBinaryValue(uint32_t objInstance); + + /** + * Set the Present_Value property of a Binary Value object. This + * method will throw on an error. + * + * @param objInstance The Analog Value Object instance. + * @param value The data value to write + */ + virtual void setBinaryValue(uint32_t objInstance, + bool value); + + /** + * Lookup (retrieve if necessary) the Inactive_Text and + * Active_Text properties of a Binary Value object. These text + * properties are optional and can provide a string representing a + * given state (true/false) that can be more informational than + * just the boolean value the object represents. This is useful + * in applications that display this data to a user for example. + * If this text is not present in the object (as it is not + * required), then a string representation of the value will be + * returned instead ("active" and "inactive"). + * + * @param objInstance The Object Instance number of the object. + * @param value The value you want to lookup the text + * representation for. + * @return The string representing the value. + */ + virtual std::string lookupBinaryValueText(uint32_t objInstance, bool value); + + /** + * Return a string representation of the Present_Value property of + * a Binary Value object. This method just calls getBinaryValue() + * on the object, uses lookupBinaryValueText() to lookup the + * corresponding text value, and returns the result. + * + * @param objInstance The Object Instance number of the object. + * @return The string representing the value. + */ + virtual std::string getBinaryValueText(uint32_t objInstance); + + /** + * Retrieve the Present_Value property of a Multi State Value + * object. If checkReliability() has been enabled, then the + * Reliability property of the object will be retrieved first. If + * the Reliability property is anything other than + * RELIABILITY_NO_FAULT_DETECTED, then the method will throw. + * Reliability checking is disabled by default for performance + * reasons. + * + * @param objInstance The Object Instance number to query. + * @return The Present_Value property of the object. + */ + virtual unsigned int getMultiStateValue(uint32_t objInstance); + + /** + * Lookup (retrieve if necessary) the State_Text strings + * corresponding to the supplied value of a MultiStateValue + * object. State_Text is an optional property that can provide + * strings representing a given state that can be more + * informational than just the unsigned integer the object + * represents. This is useful in applications that display this + * data to a user for example. If this text is not present in the + * object (as it is not required), then a string representation of + * the integer value will be returned instead. + * + * @param objInstance The Object Instance number of the object. + * @param value The value you want to lookup the text + * representation for. + * @return The string representing the value. + */ + virtual std::string lookupMultiStateValueText(uint32_t objInstance, + unsigned int value); + + /** + * Return a string representation of the Present_Value property of + * a MultiStateValue object. This method just calls + * getMultiStateValue() on the object, uses + * lookupMultiStateValueText() to lookup the corresponding + * State_Text value, and returns the result. + * + * @param objInstance The Object Instance number of the object. + * @return The string representing the value. + */ + virtual std::string getMultiStateValueText(uint32_t objInstance); + + /** + * Return the maximum legal value of a Multi State Value Object. + * The value represents the highest value the Present_Value + * porperty of the object will allow. + * + * @param objInstance The Object Instance number of the object. + * @return The highest Present_Value the object supports. + */ + virtual unsigned int getMultiStateValueMaxStates(uint32_t objInstance); + + /** + * Set the Present_Value property of a Multi State Value object. + * The value provided must not be 0, and must not exceed the + * object's Number_Of_States property. Use + * getMultiStateValueMaxStates() to determine the maximum value + * the object supports. This method will throw on an error. + * + * @param objInstance The MultiStateValue Object instance. + * @param value The data value to write. + */ + virtual void setMultiStateValue(uint32_t objInstance, + unsigned int value); + + /** + * Enable or disable reliability checking. When retrieving data, + * the Present_Value property is returned. There is also an + * optional property called Reliability that can be checked to + * ensure that the Present_Value property is currently valid. + * + * Enabling Reliability Checking has the data retrieval functions + * check for a RELIABILITY_NO_FAULT_DETECTED value for the + * Reliability property before querying the Present_Value + * property. If anything other than RELIABILITY_NO_FAULT_DETECTED + * is set, then the method will throw. + * + * This checking is disabled by default since it will double the + * number of queries needed to retrieve a given value. In + * addition, since it is an optional property, calling it for an + * object that does not support it will also throw. However, if + * you need to ensure that the values returned are always + * completely valid as determined by the device firmware, and the + * objects you are querying support the reliability property, you + * can enable this. + * + * @param enable true to check Reliability before returning a + * value, false otherwise. + */ + virtual void checkReliability(bool enable) + { + m_checkReliability = enable; + }; + + /** + * Query the Device Object of the device and return it's + * Description property. This typically contains information like + * the Vendor, model and serial number of a device. + * + * @return A string containing the Device Object's Description property. + */ + virtual std::string getDeviceDescription(); + + /** + * Query the Device Object of the device and return it's Location + * property. This typically contains a string indication of a + * customer specific value. Use setLocation() to change. + * + * @return A string containing the Device Object's Location property. + */ + virtual std::string getDeviceLocation(); + + /** + * Set the Device Object's Location property. This must be a + * string containing no more than 63 characters. Not all devices + * allow setting the location. + * + * @param location The new location to set, if supported. + * @return true if the operation succeeded, otherwise false. + */ + virtual bool setDeviceLocation(std::string location); + + /** + * Query the Device Object of the device and return it's Name + * property. This should contain a unique string value. Use + * setName() to change. Note, according to the spec, the Device + * Object Name must be unique within a given BACnet network. + * + * @return A string containing the Object's Name property. + */ + virtual std::string getDeviceName(); + + /** + * Set the Device Object's Name property. This must be a string + * containing at least one character and no more than 63 + * characters. Note, according to the spec, the Device Object + * Name must be unique within a given BACnet network. + * + * @param name The name to set. + * @return true if the operation succeeded, false otherwise + */ + virtual bool setDeviceName(std::string name); + + /** + * This is a utility function that will return a string reporting + * on the various types of errors that can occur in BACnet + * operation. + * + * @return A string containing the last error message. + */ + virtual std::string getAllErrorString(); + + /** + * Return an enumration of the last error type to occur. The + * value returned will be one of the BACNETMSTP::BACERR_TYPE_T + * values. + * + * @return The last error type to occur, one of the + * BACNETMSTP::BACERR_TYPE_T values. + */ + virtual BACNETMSTP::BACERR_TYPE_T getErrorType(); + + /** + * In the event of a BACnet Reject error, return the error code. + * + * @return The Reject error code. + */ + virtual uint8_t getRejectReason(); + + /** + * In the event of a BACnet Reject error, return the error string. + * + * @return The Reject error string. + */ + virtual std::string getRejectString(); + + /** + * In the event of a BACnet Abort error, return the Abort reason code. + * + * @return The Abort reason code. + */ + virtual uint8_t getAbortReason(); + + /** + * In the event of a BACnet Abort error, return the Abort string. + * + * @return The Abort error string. + */ + virtual std::string getAbortString(); + + /** + * In the event of a general BACnet error, return the BACnet error class. + * + * @return One of the BACNET_ERROR_CLASS error class codes + */ + virtual BACNET_ERROR_CLASS getErrorClass(); + + /** + * In the event of a general BACnet error, return the BACnet error code. + * + * @return One of the BACNET_ERROR_CODE error codes + */ + virtual BACNET_ERROR_CODE getErrorCode(); + + /** + * In the event of a general BACnet error, return the BACnet error + * string. + * + * @return A string representing the BACnet error class and code. + */ + virtual std::string getErrorString(); + + /** + * In the event of a non-BACnet UPM error, return a string + * describing the error. + * + * @return A string representing the UPM error. + */ + virtual std::string getUPMErrorString(); + + protected: + // update our stored info for an MSV + virtual void updateMultiStateValueInfo(uint32_t objInstance); + // delete our stored info for an MSV + virtual void deleteMultiStateValueInfo(uint32_t objInstance); + + // update our stored info for a BV + virtual void updateBinaryValueInfo(uint32_t objInstance); + // delete our stored info for a BV + virtual void deleteBinaryValueInfo(uint32_t objInstance); + + // update our stored info for a BI + virtual void updateBinaryInputInfo(uint32_t objInstance); + // delete our stored info for a BI + virtual void deleteBinaryInputInfo(uint32_t objInstance); + + // also enable mstp debugging in BACNETMSTP + bool m_debugging; + + // whether or not to verify reliability before reading a value. + bool m_checkReliability; + + // our target Device Object ID + uint32_t m_targetDeviceObjectID; + + // a copy of the BACNETMSTP singleton instance pointer + BACNETMSTP* m_instance; + + // are we initialized? + bool m_initialized; + + // storage for Binary Input and Binary Value Data. This will + // generate SWIG warnings which can be ignored as we do not expose + // this struct outside the class. + typedef struct { + std::string inactiveText; + std::string activeText; + } binaryData_t; + + typedef std::map binaryInfo_t; + + // storage for binary input/value information + binaryInfo_t m_bvInfo; + binaryInfo_t m_biInfo; + + // storage for multi-state data. This will generate SWIG + // warnings which can be ignored as we do not expose this struct + // outside the class. + typedef struct { + unsigned int numStates; + std::vectorstateList; + } multiStateData_t; + + // our information storage for MSV's + typedef std::map multiStateInfo_t; + + multiStateInfo_t m_msvInfo; + + // Unit cache for AV + typedef std::map avCacheMap_t; + avCacheMap_t m_avUnitCache; + + // Unit cache for AI + typedef std::map aiCacheMap_t; + aiCacheMap_t m_aiUnitCache; + + private: + }; +} diff --git a/src/bacnetmstp/javaupm_bacnetmstp.i b/src/bacnetmstp/javaupm_bacnetmstp.i index c0b7f967..4902e73d 100644 --- a/src/bacnetmstp/javaupm_bacnetmstp.i +++ b/src/bacnetmstp/javaupm_bacnetmstp.i @@ -1,15 +1,15 @@ %module javaupm_bacnetmstp %include "../upm.i" %include "typemaps.i" -%include "cpointer.i" -%include "arrays_java.i"; -%include "../java_buffer.i" +%include "carrays_uint32_t.i" %{ #include "bacnetmstp.hpp" + #include "bacnetutil.hpp" %} %include "bacnetmstp.hpp" +%include "bacnetutil.hpp" %pragma(java) jniclasscode=%{ static {