mirror of
https://github.com/eclipse/upm.git
synced 2025-03-15 04:57:30 +03:00
e50hx: Initial implementation
This driver requires the UPM BACNETMSTP driver (PR #385) to be merged first. This module implements support for the Veris E50H2 and E50H5 BACnet Energy Meters. From the datasheet: The E50H5 BACnet MS/TP DIN Rail Meter with Data Logging combines exceptional performance and easy installation to deliver a cost-effective solution for power monitoring applications. Native serial communication via BACnet MS/TP provides complete accessibility of all measurements to your Building Automation System. The data logging capability protects data in the event of a power failure. The E50H5 can be easily installed on standard DIN rail, surface mounted or contained in an optional NEMA 4 enclosure, as needed. The front-panel LCD display makes device installation and setup easy and provides local access to the full set of detailed measurements. This module was developed using the upm::BACNETMSTP module, based on libbacnet-stack 0.8.3. Both libbacnet 0.8.3 and the upm::BACNETMSTP libraries must be present in order to build this module. This driver was developed on the E50H5. The Trend Log functionality is not currently supported. The Binary Input Objects are also not supported as these are only used for the Alarm bits which are already available from Analog Input Object 52 as an alarm bitfield incorporating all of the supported alarm indicators. It was connected using an RS232->RS485 interface. You cannot use the built in MCU TTL UART pins for accessing this device -- you must use a full Serial RS232->RS485 or USB-RS485 interface connected via USB. Signed-off-by: Jon Trulson <jtrulson@ics.com> Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
This commit is contained in:
parent
dc8be495a9
commit
899d873cbc
@ -258,6 +258,10 @@ add_example (smartdrive)
|
||||
if (HAVE_FIRMATA)
|
||||
add_example (curieimu)
|
||||
endif ()
|
||||
if (BACNET_FOUND)
|
||||
include_directories(${BACNET_INCLUDE_DIRS})
|
||||
add_example (e50hx)
|
||||
endif()
|
||||
|
||||
# These are special cases where you specify example binary, source file and module(s)
|
||||
include_directories (${PROJECT_SOURCE_DIR}/src)
|
||||
|
112
examples/c++/e50hx.cxx
Normal file
112
examples/c++/e50hx.cxx
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 <signal.h>
|
||||
|
||||
#include "e50hx.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace upm;
|
||||
|
||||
bool shouldRun = true;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
if (signo == SIGINT)
|
||||
shouldRun = false;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
//! [Interesting]
|
||||
// You will need to edit this example to conform to your site and your
|
||||
// devices, specifically the Device Object Instance ID passed to the
|
||||
// constructor, and the arguments to initMaster() that are
|
||||
// appropriate for your BACnet network.
|
||||
|
||||
string defaultDev = "/dev/ttyUSB0";
|
||||
|
||||
// if an argument was specified, use it as the device instead
|
||||
if (argc > 1)
|
||||
defaultDev = string(argv[1]);
|
||||
|
||||
cout << "Using device " << defaultDev << endl;
|
||||
cout << "Initializing..." << endl;
|
||||
|
||||
// Instantiate an E50HX object for an E50HX device that has 1075425
|
||||
// as it's unique Device Object Instance ID. NOTE: You will
|
||||
// certainly want to change this to the correct value for your
|
||||
// device(s).
|
||||
E50HX *sensor = new E50HX(1075425);
|
||||
|
||||
// Initialize our BACnet master, if it has not already been
|
||||
// initialized, with the device and baudrate, choosing 1000001 as
|
||||
// our unique Device Object Instance ID, 2 as our MAC address and
|
||||
// using default values for maxMaster and maxInfoFrames
|
||||
sensor->initMaster(defaultDev, 38400, 1000001, 2);
|
||||
|
||||
// Uncomment to enable debugging output
|
||||
// sensor->setDebug(true);
|
||||
|
||||
cout << endl;
|
||||
cout << "Device Description: " << sensor->getDescription() << endl;
|
||||
cout << "Device Location: " << sensor->getLocation() << endl;
|
||||
cout << endl;
|
||||
|
||||
// update and print a few values every 5 seconds
|
||||
while (shouldRun)
|
||||
{
|
||||
cout << "System Voltage: "
|
||||
<< sensor->getAnalogValue(E50HX::AV_System_Voltage)
|
||||
<< " " << sensor->getAnalogValueUnits(E50HX::AV_System_Voltage)
|
||||
<< endl;
|
||||
|
||||
cout << "System Type: "
|
||||
<< sensor->getAnalogValue(E50HX::AV_System_Type)
|
||||
<< endl;
|
||||
|
||||
cout << "Energy Consumption: " << sensor->getAnalogInput(E50HX::AI_Energy)
|
||||
<< " " << sensor->getAnalogInputUnits(E50HX::AI_Energy)
|
||||
<< endl;
|
||||
|
||||
cout << "Power Up Counter: "
|
||||
<< sensor->getAnalogInput(E50HX::AI_Power_Up_Count)
|
||||
<< endl;
|
||||
|
||||
cout << endl;
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
cout << "Exiting..." << endl;
|
||||
|
||||
delete sensor;
|
||||
|
||||
//! [Interesting]
|
||||
|
||||
return 0;
|
||||
}
|
@ -115,6 +115,9 @@ add_example(AM2315Example am2315)
|
||||
if (MODBUS_FOUND)
|
||||
add_example(H803X_Example h803x)
|
||||
endif()
|
||||
if (BACNET_FOUND)
|
||||
add_example(E50HX_Example e50hx)
|
||||
endif()
|
||||
|
||||
add_example_with_path(Jhd1313m1_lcdSample lcd/upm_i2clcd.jar)
|
||||
add_example_with_path(Jhd1313m1Sample lcd/upm_i2clcd.jar)
|
||||
|
90
examples/java/E50HX_Example.java
Normal file
90
examples/java/E50HX_Example.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import upm_e50hx.E50HX;
|
||||
|
||||
public class E50HX_Example
|
||||
{
|
||||
private static String defaultDev = "/dev/ttyUSB0";
|
||||
|
||||
public static void main(String[] args) throws InterruptedException
|
||||
{
|
||||
// ! [Interesting]
|
||||
|
||||
// You will need to edit this example to conform to your site
|
||||
// and your devices, specifically the Device Object Instance
|
||||
// ID passed to the constructor, and the arguments to
|
||||
// initMaster() that are appropriate for your BACnet network.
|
||||
|
||||
if (args.length > 0)
|
||||
defaultDev = args[0];
|
||||
System.out.println("Using device " + defaultDev);
|
||||
System.out.println("Initializing...");
|
||||
|
||||
// Instantiate an E50HX object for an E50HX device that has
|
||||
// 1075425 as it's unique Device Object Instance ID. NOTE:
|
||||
// You will certainly want to change this to the correct value
|
||||
// for your device(s).
|
||||
E50HX sensor = new E50HX(1075425);
|
||||
|
||||
// Initialize our BACnet master, if it has not already been
|
||||
// initialized, with the device and baudrate, choosing 1000001 as
|
||||
// our unique Device Object Instance ID, 2 as our MAC address and
|
||||
// using default values for maxMaster and maxInfoFrames
|
||||
sensor.initMaster(defaultDev, 38400, 1000001, 2);
|
||||
|
||||
// Uncomment to enable debugging output
|
||||
// sensor.setDebug(true);
|
||||
|
||||
System.out.println();
|
||||
System.out.println("Device Description: " + sensor.getDescription());
|
||||
System.out.println("Device Location: " + sensor.getLocation());
|
||||
System.out.println();
|
||||
|
||||
// update and print a few values every 5 seconds
|
||||
while (true)
|
||||
{
|
||||
System.out.println("System Voltage: "
|
||||
+ sensor.getAnalogValue(E50HX.ANALOG_VALUES_T.AV_System_Voltage)
|
||||
+ " "
|
||||
+ sensor.getAnalogValueUnits(E50HX.ANALOG_VALUES_T.AV_System_Voltage));
|
||||
|
||||
System.out.println("System Type: "
|
||||
+ sensor.getAnalogValue(E50HX.ANALOG_VALUES_T.AV_System_Type));
|
||||
|
||||
System.out.println("Energy Consumption: "
|
||||
+ sensor.getAnalogInput(E50HX.ANALOG_INPUTS_T.AI_Energy)
|
||||
+ " "
|
||||
+ sensor.getAnalogInputUnits(E50HX.ANALOG_INPUTS_T.AI_Energy));
|
||||
|
||||
System.out.println("Power Up Counter: "
|
||||
+ sensor.getAnalogInput(E50HX.ANALOG_INPUTS_T.AI_Power_Up_Count));
|
||||
|
||||
System.out.println();
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
// ! [Interesting]
|
||||
}
|
||||
}
|
101
examples/javascript/e50hx.js
Normal file
101
examples/javascript/e50hx.js
Normal file
@ -0,0 +1,101 @@
|
||||
/*jslint node:true, vars:true, bitwise:true, unparam:true */
|
||||
/*jshint unused:true */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
var sensorObj = require('jsupm_e50hx');
|
||||
|
||||
|
||||
/************** Main code **************/
|
||||
|
||||
// You will need to edit this example to conform to your site and your
|
||||
// devices, specifically the Device Object Instance ID passed to the
|
||||
// constructor, and the arguments to initMaster() that are
|
||||
// appropriate for your BACnet network.
|
||||
|
||||
var defaultDev = "/dev/ttyUSB0";
|
||||
|
||||
// if an argument was specified, use it as the device instead
|
||||
if (process.argv.length > 2)
|
||||
{
|
||||
defaultDev = process.argv[2];
|
||||
}
|
||||
|
||||
console.log("Using device " + defaultDev);
|
||||
console.log("Initializing...");
|
||||
|
||||
// Instantiate an E50HX object for an E50HX device that has 1075425
|
||||
// as it's unique Device Object Instance ID. NOTE: You will
|
||||
// certainly want to change this to the correct value for your
|
||||
// device(s).
|
||||
var sensor = new sensorObj.E50HX(1075425);
|
||||
|
||||
// Initialize our BACnet master, if it has not already been
|
||||
// initialized, with the device and baudrate, choosing 1000001 as
|
||||
// our unique Device Object Instance ID, 2 as our MAC address and
|
||||
// using default values for maxMaster and maxInfoFrames
|
||||
sensor.initMaster(defaultDev, 38400, 1000001, 2);
|
||||
|
||||
// Uncomment to enable debugging output
|
||||
// sensor.setDebug(true);
|
||||
|
||||
console.log("");
|
||||
console.log("Device Description:", sensor.getDescription());
|
||||
console.log("Device Location:", sensor.getLocation());
|
||||
console.log("");
|
||||
|
||||
// update and print a few values every 5 seconds
|
||||
setInterval(function()
|
||||
{
|
||||
console.log("System Voltage:",
|
||||
sensor.getAnalogValue(sensorObj.E50HX.AV_System_Voltage),
|
||||
" ",
|
||||
sensor.getAnalogValueUnits(sensorObj.E50HX.AV_System_Voltage));
|
||||
|
||||
console.log("System Type:",
|
||||
sensor.getAnalogValue(sensorObj.E50HX.AV_System_Type));
|
||||
|
||||
console.log("Energy Consumption:",
|
||||
sensor.getAnalogInput(sensorObj.E50HX.AI_Energy),
|
||||
" ",
|
||||
sensor.getAnalogInputUnits(sensorObj.E50HX.AI_Energy));
|
||||
|
||||
console.log("Power Up Counter:",
|
||||
sensor.getAnalogInput(sensorObj.E50HX.AI_Power_Up_Count));
|
||||
|
||||
console.log("");
|
||||
|
||||
}, 5000);
|
||||
|
||||
|
||||
process.on('SIGINT', function()
|
||||
{
|
||||
sensor = null;
|
||||
sensorObj.cleanUp();
|
||||
sensorObj = null;
|
||||
console.log("Exiting...");
|
||||
process.exit(0);
|
||||
});
|
95
examples/python/e50hx.py
Normal file
95
examples/python/e50hx.py
Normal file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/python
|
||||
# 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.
|
||||
|
||||
import time, sys, signal, atexit
|
||||
import pyupm_e50hx as sensorObj
|
||||
|
||||
## Exit handlers ##
|
||||
# This function stops python from printing a stacktrace when you hit control-C
|
||||
def SIGINTHandler(signum, frame):
|
||||
raise SystemExit
|
||||
|
||||
# This function lets you run code on exit
|
||||
def exitHandler():
|
||||
print "Exiting..."
|
||||
sys.exit(0)
|
||||
|
||||
# Register exit handlers
|
||||
atexit.register(exitHandler)
|
||||
signal.signal(signal.SIGINT, SIGINTHandler)
|
||||
|
||||
# You will need to edit this example to conform to your site and your
|
||||
# devices, specifically the Device Object Instance ID passed to the
|
||||
# constructor, and the arguments to initMaster() that are
|
||||
# appropriate for your BACnet network.
|
||||
|
||||
defaultDev = "/dev/ttyUSB0"
|
||||
|
||||
# if an argument was specified, use it as the device instead
|
||||
if (len(sys.argv) > 1):
|
||||
defaultDev = sys.argv[1]
|
||||
|
||||
print "Using device", defaultDev
|
||||
print "Initializing..."
|
||||
|
||||
# Instantiate an E50HX object for an E50HX device that has 1075425
|
||||
# as it's unique Device Object Instance ID. NOTE: You will
|
||||
# certainly want to change this to the correct value for your
|
||||
# device(s).
|
||||
sensor = sensorObj.E50HX(1075425)
|
||||
|
||||
# Initialize our BACnet master, if it has not already been
|
||||
# initialized, with the device and baudrate, choosing 1000001 as
|
||||
# our unique Device Object Instance ID, 2 as our MAC address and
|
||||
# using default values for maxMaster and maxInfoFrames
|
||||
sensor.initMaster(defaultDev, 38400, 1000001, 2)
|
||||
|
||||
# Uncomment to enable debugging output
|
||||
# sensor.setDebug(True);
|
||||
|
||||
# output the serial number and firmware revision
|
||||
print
|
||||
print "Device Description:", sensor.getDescription()
|
||||
print "Device Location:", sensor.getLocation()
|
||||
print
|
||||
|
||||
# update and print available values every second
|
||||
while (1):
|
||||
print "System Voltage:",
|
||||
print sensor.getAnalogValue(sensorObj.E50HX.AV_System_Voltage),
|
||||
print " ",
|
||||
print sensor.getAnalogValueUnits(sensorObj.E50HX.AV_System_Voltage)
|
||||
|
||||
print "System Type:",
|
||||
print sensor.getAnalogValue(sensorObj.E50HX.AV_System_Type)
|
||||
|
||||
print "Energy Consumption:",
|
||||
print sensor.getAnalogInput(sensorObj.E50HX.AI_Energy),
|
||||
print " ",
|
||||
print sensor.getAnalogInputUnits(sensorObj.E50HX.AI_Energy)
|
||||
|
||||
print "Power Up Counter:",
|
||||
print sensor.getAnalogInput(sensorObj.E50HX.AI_Power_Up_Count)
|
||||
|
||||
print
|
||||
time.sleep(5)
|
29
src/e50hx/CMakeLists.txt
Normal file
29
src/e50hx/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
set (libname "e50hx")
|
||||
set (libdescription "upm module for the Veris E50HX (E50H2/E50H5)Energy Meters")
|
||||
set (module_src ${libname}.cxx)
|
||||
set (module_h ${libname}.h)
|
||||
|
||||
pkg_search_module(BACNET libbacnet)
|
||||
if (BACNET_FOUND)
|
||||
# upm-libbacnetmstp will bring in libbacnet, I hope
|
||||
set (reqlibname "upm-bacnetmstp")
|
||||
include_directories(${BACNET_INCLUDE_DIRS})
|
||||
include_directories("../bacnetmstp")
|
||||
upm_module_init()
|
||||
add_dependencies(${libname} bacnetmstp)
|
||||
target_link_libraries(${libname} bacnetmstp)
|
||||
if (BUILDSWIG)
|
||||
if (BUILDSWIGNODE)
|
||||
set_target_properties(${SWIG_MODULE_jsupm_${libname}_REAL_NAME} PROPERTIES SKIP_BUILD_RPATH TRUE)
|
||||
swig_link_libraries (jsupm_${libname} bacnetmstp ${MRAA_LIBRARIES} ${NODE_LIBRARIES})
|
||||
endif()
|
||||
if (BUILDSWIGPYTHON)
|
||||
set_target_properties(${SWIG_MODULE_pyupm_${libname}_REAL_NAME} PROPERTIES SKIP_BUILD_RPATH TRUE)
|
||||
swig_link_libraries (pyupm_${libname} bacnetmstp ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES})
|
||||
endif()
|
||||
if (BUILDSWIGJAVA)
|
||||
set_target_properties(${SWIG_MODULE_javaupm_${libname}_REAL_NAME} PROPERTIES SKIP_BUILD_RPATH TRUE)
|
||||
swig_link_libraries (javaupm_${libname} bacnetmstp ${MRAAJAVA_LDFLAGS} ${JAVA_LDFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
566
src/e50hx/e50hx.cxx
Normal file
566
src/e50hx/e50hx.cxx
Normal file
@ -0,0 +1,566 @@
|
||||
/*
|
||||
* 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "e50hx.h"
|
||||
|
||||
using namespace upm;
|
||||
using namespace std;
|
||||
|
||||
|
||||
E50HX::E50HX(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();
|
||||
}
|
||||
|
||||
E50HX::~E50HX()
|
||||
{
|
||||
}
|
||||
|
||||
void E50HX::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 E50HX::setDebug(bool enable)
|
||||
{
|
||||
m_debugging = enable;
|
||||
|
||||
// we also enable/disable debugging in BACNETMSTP
|
||||
m_instance->setDebug(enable);
|
||||
}
|
||||
|
||||
float E50HX::getAnalogValue(ANALOG_VALUES_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;
|
||||
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
BACNET_RELIABILITY reliable =
|
||||
static_cast<BACNET_RELIABILITY>(m_instance->getDataTypeEnum());
|
||||
|
||||
if (reliable != RELIABILITY_NO_FAULT_DETECTED)
|
||||
{
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": Reliability check failed" << endl;
|
||||
|
||||
return RETURN_UNRELIABLE;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
return m_instance->getDataTypeReal();
|
||||
}
|
||||
|
||||
string E50HX::getAnalogValueUnits(ANALOG_VALUES_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 E50HX::getAnalogInput(ANALOG_INPUTS_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;
|
||||
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
BACNET_RELIABILITY reliable =
|
||||
static_cast<BACNET_RELIABILITY>(m_instance->getDataTypeEnum());
|
||||
|
||||
if (reliable != RELIABILITY_NO_FAULT_DETECTED)
|
||||
{
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": Reliability check failed" << endl;
|
||||
|
||||
return RETURN_UNRELIABLE;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
return m_instance->getDataTypeReal();
|
||||
}
|
||||
|
||||
string E50HX::getAnalogInputUnits(ANALOG_INPUTS_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];
|
||||
}
|
||||
|
||||
uint16_t E50HX::getAlarmBits()
|
||||
{
|
||||
return uint16_t(getAnalogInput(AI_Alarm_Bitmap));
|
||||
}
|
||||
|
||||
BACNETMSTP::BACERR_TYPE_T E50HX::getErrorType()
|
||||
{
|
||||
return m_instance->getErrorType();
|
||||
}
|
||||
|
||||
uint8_t E50HX::getRejectReason()
|
||||
{
|
||||
return m_instance->getRejectReason();
|
||||
}
|
||||
|
||||
std::string E50HX::getRejectString()
|
||||
{
|
||||
return m_instance->getRejectString();
|
||||
}
|
||||
|
||||
uint8_t E50HX::getAbortReason()
|
||||
{
|
||||
return m_instance->getAbortReason();
|
||||
}
|
||||
|
||||
std::string E50HX::getAbortString()
|
||||
{
|
||||
return m_instance->getAbortString();
|
||||
}
|
||||
|
||||
BACNET_ERROR_CLASS E50HX::getErrorClass()
|
||||
{
|
||||
return m_instance->getErrorClass();
|
||||
}
|
||||
|
||||
BACNET_ERROR_CODE E50HX::getErrorCode()
|
||||
{
|
||||
return m_instance->getErrorCode();
|
||||
}
|
||||
|
||||
std::string E50HX::getUPMErrorString()
|
||||
{
|
||||
return m_instance->getUPMErrorString();
|
||||
}
|
||||
|
||||
std::string E50HX::getErrorString()
|
||||
{
|
||||
return m_instance->getErrorString();
|
||||
};
|
||||
|
||||
|
||||
string E50HX::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 E50HX::getDescription()
|
||||
{
|
||||
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 E50HX::getLocation()
|
||||
{
|
||||
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 E50HX::setLocation(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;
|
||||
}
|
||||
|
||||
bool E50HX::writeConfig(CFG_VALUES_T config)
|
||||
{
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(float(config));
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_Config, PROP_PRESENT_VALUE, &myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writeSystemType(SYSTEM_TYPES_T systype)
|
||||
{
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(float(systype));
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_System_Type, PROP_PRESENT_VALUE, &myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writeCTRatioPrimary(float ctRatio)
|
||||
{
|
||||
if (ctRatio < 5 || ctRatio > 32000)
|
||||
{
|
||||
throw std::out_of_range(std::string(__FUNCTION__)
|
||||
+ ": ctRatio must be between 5-32000");
|
||||
}
|
||||
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(ctRatio);
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_CT_Ratio_Primary, PROP_PRESENT_VALUE,
|
||||
&myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writeCTRatioSecondary(CT_SECONDARY_T ctRatio)
|
||||
{
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(float(ctRatio));
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_CT_Ratio_Secondary, PROP_PRESENT_VALUE,
|
||||
&myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writePTRatio(float ptRatio)
|
||||
{
|
||||
if (ptRatio < 0.01 || ptRatio > 320.0)
|
||||
{
|
||||
throw std::out_of_range(std::string(__FUNCTION__)
|
||||
+ ": ptRatio must be between 0.01-320.0");
|
||||
}
|
||||
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(ptRatio);
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_PT_Ratio, PROP_PRESENT_VALUE,
|
||||
&myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writeSystemVoltage(float sysVolts)
|
||||
{
|
||||
if (sysVolts < 82.0 || sysVolts > 32000.0)
|
||||
{
|
||||
throw std::out_of_range(std::string(__FUNCTION__)
|
||||
+ ": sysVolts must be between 82.0-32000.0");
|
||||
}
|
||||
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(sysVolts);
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_System_Voltage, PROP_PRESENT_VALUE,
|
||||
&myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writeDisplayUnits(DISP_UNITS_T dispUnits)
|
||||
{
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(float(dispUnits));
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_Display_Units, PROP_PRESENT_VALUE,
|
||||
&myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writePhaseLossVT(float phaseLoss)
|
||||
{
|
||||
if (phaseLoss < 1.0 || phaseLoss > 99.0)
|
||||
{
|
||||
throw std::out_of_range(std::string(__FUNCTION__)
|
||||
+ ": phaseLoss must be between 1.0-99.0");
|
||||
}
|
||||
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(phaseLoss);
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_Phase_Loss_Voltage_Threshold,
|
||||
PROP_PRESENT_VALUE, &myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E50HX::writePhaseLossIT(float phaseLoss)
|
||||
{
|
||||
if (phaseLoss < 1.0 || phaseLoss > 99.0)
|
||||
{
|
||||
throw std::out_of_range(std::string(__FUNCTION__)
|
||||
+ ": phaseLoss must be between 1.0-99.0");
|
||||
}
|
||||
|
||||
// Write the value
|
||||
BACNET_APPLICATION_DATA_VALUE myData =
|
||||
m_instance->createDataReal(phaseLoss);
|
||||
|
||||
// write it
|
||||
if (m_instance->writeProperty(m_targetDeviceObjectID, OBJECT_ANALOG_VALUE,
|
||||
AV_Phase_Loss_Imbalance_Threshold,
|
||||
PROP_PRESENT_VALUE, &myData))
|
||||
{
|
||||
// error occurred
|
||||
if (m_debugging)
|
||||
cerr << __FUNCTION__ << ": " << getAllErrorString() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
588
src/e50hx/e50hx.h
Normal file
588
src/e50hx/e50hx.h
Normal file
@ -0,0 +1,588 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <map>
|
||||
|
||||
#include "bacnetmstp.h"
|
||||
|
||||
namespace upm {
|
||||
|
||||
/**
|
||||
* @brief E50HX Energy Meter
|
||||
* @defgroup e50hx libupm-e50hx
|
||||
* @ingroup uart electric
|
||||
*/
|
||||
|
||||
/**
|
||||
* @library e50hx
|
||||
* @sensor e50hx
|
||||
* @comname UPM API for the Veris E50HX Energy Meters
|
||||
* @type electic
|
||||
* @man veris
|
||||
* @con uart
|
||||
* @web http://www.veris.com/Item/E50H5.aspx
|
||||
*
|
||||
* @brief UPM API for the Veris E50HX Energy Meter
|
||||
*
|
||||
* This module implements support for the Veris E50H2 and E50H5
|
||||
* BACnet Energy Meters.
|
||||
*
|
||||
* From the datasheet: The E50H5 BACnet MS/TP DIN Rail Meter with
|
||||
* Data Logging combines exceptional performance and easy
|
||||
* installation to deliver a cost-effective solution for power
|
||||
* monitoring applications. Native serial communication via BACnet
|
||||
* MS/TP provides complete accessibility of all measurements to your
|
||||
* Building Automation System The data logging capability protects
|
||||
* data in the event of a power failure. The E50H5 can be easily
|
||||
* installed on standard DIN rail, surface mounted or contained in
|
||||
* an optional NEMA 4 enclosure, as needed. The front-panel LCD
|
||||
* display makes device installation and setup easy and provides
|
||||
* local access to the full set of detailed measurements.
|
||||
*
|
||||
* This module was developed using the upm::BACNETMSTP module, based
|
||||
* on libbacnet-stack 0.8.3. Both libbacnet 0.8.3 and the
|
||||
* upm::BACNETMSTP libraries must be present in order to build this
|
||||
* module. This driver was developed on the E50H5. The Trend Log
|
||||
* functionality is not currently supported.
|
||||
*
|
||||
* The Binary Input Objects are also not supported as these are only
|
||||
* used for the Alarm bits which are already available from Analog
|
||||
* Input Object 52 as an alarm bitfield incorporating all of the
|
||||
* supported alarm indicators.
|
||||
*
|
||||
* It was connected using an RS232->RS485 interface. You cannot use
|
||||
* the built in MCU TTL UART pins for accessing this device -- you
|
||||
* must use a full Serial RS232->RS485 or USB-RS485 interface
|
||||
* connected via USB.
|
||||
*
|
||||
* @snippet e50hx.cxx Interesting
|
||||
*/
|
||||
|
||||
class E50HX {
|
||||
public:
|
||||
|
||||
// Supported Analog Value Objects. These are readable and writable.
|
||||
typedef enum : uint32_t {
|
||||
AV_Config = 1, // always returns 0 on read
|
||||
AV_System_Type = 2,
|
||||
AV_CT_Ratio_Primary = 3,
|
||||
AV_CT_Ratio_Secondary = 4,
|
||||
AV_PT_Ratio = 5,
|
||||
AV_System_Voltage = 6,
|
||||
AV_Display_Units = 7,
|
||||
AV_Phase_Loss_Voltage_Threshold = 8,
|
||||
AV_Phase_Loss_Imbalance_Threshold = 9,
|
||||
AV_Subintervals = 10,
|
||||
AV_Subinterval_Length = 11
|
||||
} ANALOG_VALUES_T;
|
||||
|
||||
// Supported Analog Input Objects. These are read only.
|
||||
typedef enum : uint32_t {
|
||||
AI_Energy = 1,
|
||||
AI_kW_Total = 2,
|
||||
AI_kVAR_Total = 3,
|
||||
AI_kVA_Total = 4,
|
||||
AI_PF_Total = 5,
|
||||
AI_Volts_LL_Avg = 6,
|
||||
AI_Volts_LN_Avg = 7,
|
||||
AI_Current_Avg = 8,
|
||||
AI_kW_A = 9,
|
||||
AI_kW_B = 10,
|
||||
AI_kW_C = 11,
|
||||
AI_PF_A = 12,
|
||||
AI_PF_B = 13,
|
||||
AI_PF_C = 14,
|
||||
AI_Volts_AB = 15,
|
||||
AI_Volts_BC = 16,
|
||||
AI_Volts_AC = 17,
|
||||
AI_Volts_AN = 18,
|
||||
AI_Volts_BN = 19,
|
||||
AI_Volts_CN = 20,
|
||||
AI_Current_A = 21,
|
||||
AI_Current_B = 22,
|
||||
AI_Current_C = 23,
|
||||
// AI24 is reserved
|
||||
AI_Frequency = 25,
|
||||
AI_kVAh = 26, // units = kVAh, not kWH
|
||||
AI_kVARh = 27, // units = kVAh, not kWH
|
||||
AI_kVA_A = 28,
|
||||
AI_kVA_B = 29,
|
||||
AI_kVA_C = 30,
|
||||
AI_kVAR_A = 31,
|
||||
AI_kVAR_B = 32,
|
||||
AI_kVAR_C = 33,
|
||||
AI_KW_Present_Demand = 34,
|
||||
AI_KVAR_Present_Demand = 35,
|
||||
AI_KWA_Present_Demand = 36,
|
||||
AI_KW_Max_Demand = 37,
|
||||
AI_KVAR_Max_Demand = 38,
|
||||
AI_KVA_Max_Demand = 39,
|
||||
AI_Pulse_Count_1 = 40, // H2 & H5
|
||||
// AI41 is reserved on H2 variant
|
||||
AI_Pulse_Count_2 = 41, // only on H5 variant
|
||||
AI_KWH_A = 42,
|
||||
AI_KWH_B = 43,
|
||||
AI_KWH_C = 44,
|
||||
AI_Max_Power = 45, // theoretical max power
|
||||
// AI46 reserved
|
||||
AI_Energy_Resets = 47,
|
||||
// AI48 and AI49 reserved
|
||||
AI_Power_Up_Count = 50,
|
||||
AI_Output_Config = 51, // H2 = 11, H5 = 10
|
||||
AI_Alarm_Bitmap = 52
|
||||
} ANALOG_INPUTS_T;
|
||||
|
||||
// Alarm bits (AI52)
|
||||
typedef enum : uint16_t {
|
||||
ALARM_Volts_Error_A = 0x0001,
|
||||
ALARM_Volts_Error_B = 0x0002,
|
||||
ALARM_Volts_Error_C = 0x0004,
|
||||
|
||||
ALARM_Current_Error_A = 0x0008,
|
||||
ALARM_Current_Error_B = 0x0010,
|
||||
ALARM_Current_Error_C = 0x0020,
|
||||
|
||||
ALARM_Frequency_Error = 0x0040,
|
||||
|
||||
ALARM_Reserved_0 = 0x0080, // reserved
|
||||
|
||||
ALARM_Phase_Loss_A = 0x0100,
|
||||
ALARM_Phase_Loss_B = 0x0200,
|
||||
ALARM_Phase_Loss_C = 0x0400,
|
||||
|
||||
ALARM_Power_Factor_A = 0x0800,
|
||||
ALARM_Power_Factor_B = 0x1000,
|
||||
ALARM_Power_Factor_C = 0x2000,
|
||||
|
||||
ALARM_RTC_RESET = 0x4000 // H5 only
|
||||
} ALARM_BITS_T;
|
||||
|
||||
// valid config values to write to AV1
|
||||
typedef enum {
|
||||
CFG_CLR_ENERGY_ACCUM = 30078, // clear energy accumulators
|
||||
CFG_NEW_DSI = 21211, // begin new demand subinterval
|
||||
CFG_RESET_MAX_TO_PRESENT = 21212, // reset max vals to present vals
|
||||
CFG_CLEAR_PULSE_COUNTERS = 16498 // clear the pulse counters
|
||||
} CFG_VALUES_T;
|
||||
|
||||
// system type configuration
|
||||
typedef enum {
|
||||
SYSTYPE_SINGLE_PHASE_AN = 10,
|
||||
SYSTYPE_SINGLE_PHASE_AB = 11,
|
||||
SYSTYPE_SPLIT_PHASE_ABN = 12,
|
||||
SYSTYPE_3PHASE_ABC = 31,
|
||||
SYSTYPE_3PHASE_ABCN = 40
|
||||
} SYSTEM_TYPES_T;
|
||||
|
||||
// CT input ratio
|
||||
typedef enum {
|
||||
CT_RATIO_SECONDARY_1 = 1, // CT's w/ 1v outputs
|
||||
CT_RATIO_SECONDARY_3 = 3 // CT's w/ 0.3v outputs
|
||||
} CT_SECONDARY_T;
|
||||
|
||||
// LCD display units
|
||||
typedef enum {
|
||||
DISP_UNITS_IEC = 0, // IEC display units
|
||||
DISP_UNITS_IEEE = 1 // IEEE display units
|
||||
} DISP_UNITS_T;
|
||||
|
||||
// Since none of the legal values returned by getAnalogValue() or
|
||||
// getAnalogInput() will ever be negative, we use these two values
|
||||
// to indicate either an error (BACnet or UPM), or to indicate
|
||||
// that the value is unreliable if checkReliability() has been
|
||||
// enabled.
|
||||
const float RETURN_ERROR = -1.0;
|
||||
const float RETURN_UNRELIABLE = -2.0;
|
||||
|
||||
/**
|
||||
* E50HX constructor
|
||||
*
|
||||
* @param targetDeviceObjectID the unique Instance ID of the
|
||||
* Device Object. This number is used to uniquely identify
|
||||
* devices on the BACnet network, and ranges from 1 to 4194302.
|
||||
* This is not the device's MAC address, though on some devices,
|
||||
* the MAC address may be used as part of this number. On the
|
||||
* E50HX, this number is randomly generated per device, and you
|
||||
* can see this number (or change it) on the BACnet config screens
|
||||
* on the LCD.
|
||||
*/
|
||||
E50HX(uint32_t targetDeviceObjectID);
|
||||
|
||||
/**
|
||||
* E50HX Destructor
|
||||
*/
|
||||
~E50HX();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 RETURN_UNRELIABLE value
|
||||
* will be returned. Reliability checking is disabled by default
|
||||
* for performance reasons.
|
||||
*
|
||||
* @param objInstance One of the ANALOG_VALUES_T values.
|
||||
* @return the floating point value requested
|
||||
*/
|
||||
float getAnalogValue(ANALOG_VALUES_T objInstance);
|
||||
|
||||
/**
|
||||
* 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 RETURN_UNRELIABLE value
|
||||
* will be returned. Reliability checking is disabled by default
|
||||
* for performance reasons.
|
||||
*
|
||||
* @param objInstance One of the ANALOG_INPUTS_T values.
|
||||
* @return the floating point value requested
|
||||
*/
|
||||
float getAnalogInput(ANALOG_INPUTS_T objInstance);
|
||||
|
||||
/**
|
||||
* Write one of several 'magic' numbers to the configuration
|
||||
* object (AV1). This is used to clear certain counters, reset
|
||||
* the accumulated Energy consumption values, etc.
|
||||
*
|
||||
* @param config One of the CFG_VALUES_T values
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeConfig(CFG_VALUES_T config);
|
||||
|
||||
/**
|
||||
* Set the System Type of the device. This defines the voltage
|
||||
* lines you have connected.
|
||||
*
|
||||
* @param systype One of the SYSTEM_TYPES_T values.
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeSystemType(SYSTEM_TYPES_T systype);
|
||||
|
||||
/**
|
||||
* Set the Primary CT ratio. See the datasheet for details.
|
||||
*
|
||||
* @param ctRatio A floating point value between 5-32000
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeCTRatioPrimary(float ctRatio);
|
||||
|
||||
/**
|
||||
* Set the Secondary CT ratio. See the datasheet for details.
|
||||
*
|
||||
* @param ctRatio One of the CT_SECONDARY_T values.
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeCTRatioSecondary(CT_SECONDARY_T ctRatio);
|
||||
|
||||
/**
|
||||
* Set the PT ratio. See the datasheet for details.
|
||||
*
|
||||
* @param ptRatio A floating point value between 0.01-320.0
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writePTRatio(float ptRatio);
|
||||
|
||||
/**
|
||||
* Set the System Voltage parmeter. See the datasheet for details.
|
||||
*
|
||||
* @param sysVolts A floating point value between 82.0-32000.0
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeSystemVoltage(float sysVolts);
|
||||
|
||||
/**
|
||||
* Set the LCD Display Units in IEC or IEEE format.
|
||||
*
|
||||
* @param dispUnits One of the DISP_UNITS_T values.
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writeDisplayUnits(DISP_UNITS_T dispUnits);
|
||||
|
||||
/**
|
||||
* Set the Phase Loss Voltage Threshold. See the datasheet for
|
||||
* details.
|
||||
*
|
||||
* @param dispUnits A floating point value between 1.0-99.0
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writePhaseLossVT(float phaseLoss);
|
||||
|
||||
/**
|
||||
* Set the Phase Loss Imbalance Threshold. See the datasheet for
|
||||
* details.
|
||||
*
|
||||
* @param dispUnits A floating point value between 1.0-99.0
|
||||
* @return true if the operation suceeded, false if there was an
|
||||
* error.
|
||||
*/
|
||||
bool writePhaseLossIT(float phaseLoss);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param objInstance One of the ANALOG_VALUES_T values.
|
||||
* @return A string representing the Object's Unit property.
|
||||
*/
|
||||
std::string getAnalogValueUnits(ANALOG_VALUES_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.
|
||||
*
|
||||
* @param objInstance One of the ANALOG_INPUTS_T values.
|
||||
* @return A string representing the Object's Unit property.
|
||||
*/
|
||||
std::string getAnalogInputUnits(ANALOG_INPUTS_T objInstance);
|
||||
|
||||
/**
|
||||
* Query the AI52 Object and return a bitmask of current Alarms.
|
||||
* Compare against ALARM_BITS_T to determine what conditions are
|
||||
* signaling an alarm. Alarm conditions will clear on their own
|
||||
* as soon as the cause is rectified.
|
||||
*
|
||||
* @return A bitmask of values from ALARM_BITS_T indicating
|
||||
* current alarm conditions.
|
||||
*/
|
||||
uint16_t getAlarmBits();
|
||||
|
||||
/**
|
||||
* Enable or disable reliability checking. By default, when using
|
||||
* getAnalogValue() or getAnalogInput() the Present_Value property
|
||||
* is returned. There is also a property called Reliability that
|
||||
* can be checked to ensure that the Present_Value property is
|
||||
* currently valid.
|
||||
*
|
||||
* Enabling Reliability Checking has these 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
|
||||
* these functions will return RETURN_UNRELIABLE rather than the
|
||||
* Present_Value.
|
||||
*
|
||||
* This checking is disabled by default since it will double the
|
||||
* number of queries needed to retrieve a given value. However,
|
||||
* if you need to ensure that the values returned are always
|
||||
* completely valid as determined by the device firmware, you
|
||||
* should enable this.
|
||||
*
|
||||
* @param enable true to check Reliability before returning a
|
||||
* value, false otherwise.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
std::string getDescription();
|
||||
|
||||
/**
|
||||
* Query the Device Object of the device and return it's Location
|
||||
* property. This typically contains a string indication a
|
||||
* customer specific value. Use setLocation() to change.
|
||||
*
|
||||
* @return A string containing the Device Object's Location property.
|
||||
*/
|
||||
std::string getLocation();
|
||||
|
||||
/**
|
||||
* Set the Device Object's Location property. This must be a
|
||||
* string containing no more than 63 characters.
|
||||
*
|
||||
* @return true if the operation succeeded, false otherwise
|
||||
*/
|
||||
bool setLocation(std::string location);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
BACNETMSTP::BACERR_TYPE_T getErrorType();
|
||||
|
||||
/**
|
||||
* In the event of a BACnet Reject error, return the error code.
|
||||
*
|
||||
* @return The Reject error code.
|
||||
*/
|
||||
uint8_t getRejectReason();
|
||||
|
||||
/**
|
||||
* In the event of a BACnet Reject error, return the error string.
|
||||
*
|
||||
* @return The Reject error string.
|
||||
*/
|
||||
std::string getRejectString();
|
||||
|
||||
/**
|
||||
* In the event of a BACnet Abort error, return the Abort reason code.
|
||||
*
|
||||
* @return The Abort reason code.
|
||||
*/
|
||||
uint8_t getAbortReason();
|
||||
|
||||
/**
|
||||
* In the event of a BACnet Abort error, return the Abort string.
|
||||
*
|
||||
* @return The Abort error string.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
std::string getUPMErrorString();
|
||||
|
||||
protected:
|
||||
// a copy of the BACNETMSTP singleton instance pointer
|
||||
BACNETMSTP* m_instance;
|
||||
|
||||
// unique device object ID of e50hx
|
||||
uint32_t m_targetDeviceObjectID;
|
||||
|
||||
// are we initialized?
|
||||
bool m_initialized;
|
||||
|
||||
private:
|
||||
bool m_debugging;
|
||||
|
||||
// whether or not to verify reliability before reading a value.
|
||||
bool m_checkReliability;
|
||||
|
||||
// Unit cache for AV
|
||||
typedef std::map<ANALOG_VALUES_T, std::string> avCacheMap_t;
|
||||
avCacheMap_t m_avUnitCache;
|
||||
|
||||
// Unit cache for AI
|
||||
typedef std::map<ANALOG_INPUTS_T, std::string> aiCacheMap_t;
|
||||
aiCacheMap_t m_aiUnitCache;
|
||||
};
|
||||
}
|
21
src/e50hx/javaupm_e50hx.i
Normal file
21
src/e50hx/javaupm_e50hx.i
Normal file
@ -0,0 +1,21 @@
|
||||
%module javaupm_e50hx
|
||||
%include "../upm.i"
|
||||
%include "typemaps.i"
|
||||
|
||||
%include "bacnetmstp.h"
|
||||
%include "e50hx.h"
|
||||
%{
|
||||
#include "e50hx.h"
|
||||
%}
|
||||
|
||||
|
||||
%pragma(java) jniclasscode=%{
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("javaupm_e50hx");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.err.println("Native code library failed to load. \n" + e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
%}
|
9
src/e50hx/jsupm_e50hx.i
Normal file
9
src/e50hx/jsupm_e50hx.i
Normal file
@ -0,0 +1,9 @@
|
||||
%module jsupm_e50hx
|
||||
%include "../upm.i"
|
||||
%include "stdint.i"
|
||||
|
||||
%include "bacnetmstp.h"
|
||||
%include "e50hx.h"
|
||||
%{
|
||||
#include "e50hx.h"
|
||||
%}
|
13
src/e50hx/pyupm_e50hx.i
Normal file
13
src/e50hx/pyupm_e50hx.i
Normal file
@ -0,0 +1,13 @@
|
||||
// Include doxygen-generated documentation
|
||||
%include "pyupm_doxy2swig.i"
|
||||
%module pyupm_e50hx
|
||||
%include "../upm.i"
|
||||
%include "stdint.i"
|
||||
|
||||
%feature("autodoc", "3");
|
||||
|
||||
%include "bacnetmstp.h"
|
||||
%include "e50hx.h"
|
||||
%{
|
||||
#include "e50hx.h"
|
||||
%}
|
Loading…
x
Reference in New Issue
Block a user