Compare commits

..

15 Commits

Author SHA1 Message Date
d355f76226 upm: v0.6.2
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-19 16:42:19 -07:00
4f6be750c7 vcap: Initial implementation
This UPM module captures a still frame from a Linux V4L device, such
as a USB webcam, and and then allows you to save it as a JPEG image
into a file.

The camera and driver in use must support streaming, mmap-able buffers
and must provide data in YUYV format.  This should encompass most
video cameras out there.  It has been tested with a few off the shelf
USB cameras without any problems.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-19 15:38:48 -07:00
bf7d7d1bda examples: changed default bus from 6 to 0 for SSD1306 examples
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-18 16:27:11 -07:00
9acc752074 java: add dependency on java targets for java examples
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 10:01:55 -07:00
b6b7d892c2 cmake: fix RPM generation when VERSION_COMMIT is empty string
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 10:01:27 -07:00
1ca8d100ea e50hx: add bacnetmstp includes so example can build
Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 10:01:02 -07:00
96f1afffe3 e50hx: remove add_dependency() call from CMakeLists.txt
Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 10:00:38 -07:00
899d873cbc 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>
2016-04-15 10:00:04 -07:00
dc8be495a9 docs: fix spelling errors
Signed-off-by: Fathi Boudra <fathi.boudra@linaro.org>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 09:59:29 -07:00
c7b5204fe4 bacnetmstp: initial BACnet MS/TP implementation
This driver is implemented as a singleton due to it's reliance on the
bacnet-stack implementation.  This implementation does not currently
support multiple BACnet networks at the same time, though in the
future it might, depending on the future of bacnet-stack development.
The version of bacnet-stack used in developing this driver was 0.8.3.

This driver is not intended to be used directly by end users, rather
it is intended for UPM drivers supporting specific BACnet devices,
such as the Veris E50H5 Energy Meter.

Unfortunately, this means that a process can only support a single
RS-485 BACnet network, though you can support multiple devices on that
network.

No examples are provided.  Please look at the E50HX driver for an
example of how to use this class in a BACnet MS/TP device driver if
you want to write one.

When initialized, the bacnet-stack library will attach to your RS-485
based BACnet network, and start a Master Finite State Machine (FSM) in
a separate thread.  This thread will handle the details of token
passing and locating other Masters in the network (Poll For Master).

This driver will appear as a BACnet Master device on the BACnet
network, which supports only the required Device Object and any
required services (readProp) and Device Object properties.

When initializing the driver, it is important to select a Device
Object Instance ID that is unique on your BACnet network.  This is the
unique identifier that will be used to identify your Master to the
rest of the BACnet network.

In addition, it may take some time after initialization before you
will be able to communicate on the network, as the first thing that
has to happen is that all Masters on the network need to be identified
(handled by the Master FSM) and a token needs to be received before
your Master can begin transmitting (making requests).  This may take a
couple of minutes on a large network.

You can speed this process up by specifying a maxMaster (to
initMaster()) that is smaller than the default (127) -- but only if
you are CERTAIN that there are no masters with a MAC address higher
than the value you choose.  If you fail to follow this rule, you may
introduce hard to identify token passing problems on the network for
yourself and other BACnet Masters.

Currently, this driver only supports the readProperty and
writeProperty requests to other BACnet devices.  In addition, array
property reading and writing is not currently supported.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-15 09:58:36 -07:00
527615758a cmake: Fixed builddoc flow for javascript documentation
Fixed a few small typos for handling node as well as a
small conditional for building PYTHON.

    * Fixed some NODE_EXECUTABLE->NODEJS_EXECUTABLE instances
    which must have been missed from a previous commit.

    * Added a qualifier for python documentation so both
    BUILDSWIGPYTHON AND BUILDSWIG must be set to add
    dependencies for pydoc.

Signed-off-by: Noel Eck <noel.eck@intel.com>
2016-04-12 16:56:55 -07:00
16b6fcf807 cmake: set CMAKE_INSTALL_LIBDIR before using it
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-06 12:51:20 -07:00
5a9f234a3e cmake: Solved issue with nodejs installation path
Changed ${LIB_INSTALL_DIR} with lib, because the variable expands to
/usr/lib, making the install path /usr/usr/lib/node_modules, which
is incorrect. Now the install path is /usr/lib/node_modules.

Signed-off-by: Andrei Vasiliu <andrei.vasiliu@intel.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-06 12:45:14 -07:00
c7bd37b322 docs: added new API compatibility section
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
2016-04-06 12:44:56 -07:00
edcaed90e6 extdep: Removed add_dependencies call from CMakeLists
Small change to get rid of a warning in newer cmake versions.

Versions of cmake (>= 3.0) throw a warning on the add_dependecy
method for non-existant dependencies (add_dependency call before
target_link_libraries call).

Removed the call to add_dependency since target_link_libraries should
provide the same functionality for ozw and modbus dependencies.

Signed-off-by: Noel Eck <noel.eck@intel.com>
2016-04-06 12:44:35 -07:00
73 changed files with 6033 additions and 66 deletions

View File

@ -15,22 +15,17 @@ message (INFO " found mraa version: ${MRAA_VERSION}")
# Appends the cmake/modules path to MAKE_MODULE_PATH variable.
set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
# Older cmake might not pick CMAKE_INSTALL_LIBDIR right
if (CMAKE_INSTALL_LIBDIR)
set (LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Installation path for libraries")
else ()
set (LIB_INSTALL_DIR "lib" CACHE PATH "Installation path for libraries")
endif ()
# Set CMAKE_LIB_INSTALL_DIR if not defined
# Set CMAKE_INSTALL_LIBDIR if not defined
include(GNUInstallDirs)
set (LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Installation path for libraries")
# Make a version file containing the current version from git.
include (GetGitRevisionDescription)
git_describe (VERSION "--tags")
if ("x_${VERSION}" STREQUAL "x_GIT-NOTFOUND" OR "x_${VERSION}" STREQUAL "x_-128-NOTFOUND")
message (WARNING " - Install git to compile a production UPM!")
set (VERSION "v0.6.1-dirty")
set (VERSION "v0.6.2-dirty")
endif ()
message (INFO " - UPM Version ${VERSION}")
@ -157,9 +152,9 @@ if (BUILDDOC)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxy/node/${JSDOC_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${JSDOC_FILE} COPYONLY)
endforeach()
add_custom_target(jsdoc ALL
COMMAND ${NODE_EXECUTABLE} docgen -m upm -i xml -t ${CMAKE_CURRENT_SOURCE_DIR}/src -g ../../
COMMAND ${NODEJS_EXECUTABLE} docgen -m upm -i xml -t ${CMAKE_CURRENT_SOURCE_DIR}/src -g ../../
COMMAND ${YUIDOC_EXECUTABLE} -C --no-sort --helpers generators/yuidoc/helper.js --themedir generators/yuidoc/tmpl -o html/node jsdoc/yuidoc/upm
COMMAND ${NODE_EXECUTABLE} tolower -i html/node
COMMAND ${NODEJS_EXECUTABLE} tolower -i html/node
DEPENDS doc
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Yuidoc" VERBATIM
@ -210,12 +205,17 @@ if (RPM)
set(CPACK_PACKAGE_VERSION ${VERSION})
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_NAME "upm")
set(upm_PACKAGE_ON_TAG ".")
if ("${VERSION_COMMIT}" STREQUAL "")
set(upm_PACKAGE_ON_TAG "")
endif()
set(CPACK_PACKAGE_VERSION
"${upm_VERSION_MAJOR}.${upm_VERSION_MINOR}.${upm_VERSION_PATCH}.${upm_PACKAGE_ON_TAG}${VERSION_COMMIT}")
"${upm_VERSION_MAJOR}.${upm_VERSION_MINOR}.${upm_VERSION_PATCH}${upm_PACKAGE_ON_TAG}${VERSION_COMMIT}")
set(CPACK_PACKAGE_CONTACT "Intel IoT-Devkit")
set(CPACK_PACKAGE_VENDOR "Intel IoT-Devkit")
set(CPACK_RPM_PACKAGE_REQUIRES "mraa >= ${MRAA_VERSION}")
set(CPACK_RPM_PACKAGE_PROVIDES "${CPACK_PACKAGE_NAME}-devel")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
EXECUTE_PROCESS(COMMAND rpm --showrc
COMMAND grep -E "dist[[:space:]]*\\."
COMMAND sed -e "s/^.*dist\\s*\\.//"

View File

@ -114,6 +114,15 @@ API Documentation
<a href="http://iotdk.intel.com/docs/master/upm/python"><img src="docs/icons/python.png"/></a>
<a href="http://iotdk.intel.com/docs/master/upm/node"><img src="docs/icons/node.png"/></a>
### API Compatibility
Even if we try our best not to, every once in a while we are forced to modify
our API in a way that will break backwards compatibility. If you find yourself
unable to compile code that was working fine before a library update, make sure
you check the [API changes](docs/apichanges.md) section first.
**NOTE** - Our **C++ header files** will change their extension from *.h* to
*.hpp* in the upcoming version.
### Changelog
Version changelog [here](docs/changelog.md).

View File

@ -94,4 +94,4 @@ if (NODEJS_EXECUTABLE)
mark_as_advanced (NODEJS_EXECUTABLE)
endif ()
mark_as_advanced (NODE_EXECUTABLE)
mark_as_advanced (NODEJS_EXECUTABLE)

20
docs/apichanges.md Normal file
View File

@ -0,0 +1,20 @@
API Changes {#apichanges}
===============
**IMPORTANT NOTICE**
Our **C++ header files** will change their extension from *.h* to *.hpp*. This
change is anticipated in the upcoming version and unfortunately will break
source compatibility with previous versions of UPM for every library. You will
need to update your code and change to the new extension format in your
`#include` directives.
Here's a list of other API changes made to the library that break source/binary
compatibility between releases:
* **my9221**, **groveledbar** and **grovecircularled** are now all part of the
same library (my9221) and new functionality was added going to v.0.5.1
* **stepmotor** driver API was changed significantly from v.0.4.1 to v.0.5.0
* **eboled** library was greatly improved in version 0.4.0 and the `draw()`
function was removed in favor of a more complete GFX library implementation

View File

@ -4,6 +4,16 @@ Changelog {#changelog}
Here's a list summarizing some of the key undergoing changes to our library
from earlier versions:
### v0.6.2
* Added a generic driver for taking snapshots from an USB camera device
* New API changes section in documentation to let users know when the UPM API
gets modified
* Fixed some spelling errors and improved JavaScript documentation builds with
newer versions of YUI and Node
* Enhanced Cmake scripts and the build process
* New sensors: vcap, e50hx
### v0.6.1
* Fixed library build process for different configurations across multiple

View File

@ -258,6 +258,13 @@ add_example (smartdrive)
if (HAVE_FIRMATA)
add_example (curieimu)
endif ()
if (BACNET_FOUND)
include_directories(${BACNET_INCLUDE_DIRS})
# we need access to bacnetmstp headers too
include_directories(${PROJECT_SOURCE_DIR}/src/bacnetmstp)
add_example (e50hx)
endif()
add_example (vcap)
# 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
View 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;
}

View File

@ -135,7 +135,7 @@ int main (int argc, char **argv)
if (rv > 0)
cout << "Received: " << radioBuffer << endl;
if (rv < 0) // some sort of read error occured
if (rv < 0) // some sort of read error occurred
{
cerr << "Port read error." << endl;
break;

View File

@ -29,7 +29,7 @@
#include "ssd1306.h"
#define DEVICE_ADDRESS 0x3C
#define BUS_NUMBER 0x6
#define BUS_NUMBER 0x0
static uint8_t intel_logo[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

View File

@ -77,7 +77,7 @@ int main (int argc, char **argv)
if (rv > 0)
write(1, nmeaBuffer, rv);
if (rv < 0) // some sort of read error occured
if (rv < 0) // some sort of read error occurred
{
cerr << "Port read error." << endl;
break;

70
examples/c++/vcap.cxx Normal file
View File

@ -0,0 +1,70 @@
/*
* 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 "vcap.hpp"
using namespace std;
int main(int argc, char **argv)
{
//! [Interesting]
string defaultDev = "/dev/video0";
// 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 VCAP instance, using the specified video device
upm::VCAP *sensor = new upm::VCAP(defaultDev);
// enable some debug/verbose output
sensor->setDebug(true);
// This is just a hint. The kernel can change this to a lower
// resolution that the hardware supports. Use getWidth() and
// getHeight() methods to see what the kernel actually chose if you
// care.
sensor->setResolution(1920, 1080);
// capture an image
sensor->captureImage();
// convert and save it as a jpeg
sensor->saveImage("video-img1.jpg");
cout << "Exiting..." << endl;
delete sensor;
//! [Interesting]
return 0;
}

View File

@ -57,7 +57,7 @@ public class AM2315Example {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -42,7 +42,7 @@ public class Ad8232Example {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following error has occured: "+e.getMessage());
System.out.println("The following error has occurred: "+e.getMessage());
}
}
}

View File

@ -38,7 +38,7 @@ public class Apds9002 {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -6,13 +6,15 @@ macro(add_example example_name jar_name)
set(example_jar "${CMAKE_CURRENT_BINARY_DIR}/../../src/${jar_name}/upm_${jar_name}.jar")
add_jar(${example_name} SOURCES ${example_src} INCLUDE_JARS ${example_jar})
add_dependencies(${example_name} javaupm_${jar_name})
endmacro()
macro(add_example_with_path example_name jar_path)
macro(add_example_with_path example_name jar_path jar_name)
set(example_src "${example_name}.java")
set(example_jar "${CMAKE_CURRENT_BINARY_DIR}/../../src/${jar_path}")
set(example_jar "${CMAKE_CURRENT_BINARY_DIR}/../../src/${jar_path}/upm_${jar_name}.jar")
add_jar(${example_name} SOURCES ${example_src} INCLUDE_JARS ${example_jar})
add_dependencies(${example_name} javaupm_${jar_name})
endmacro()
add_example(A110X_intrSample a110x)
@ -115,11 +117,15 @@ add_example(AM2315Example am2315)
if (MODBUS_FOUND)
add_example(H803X_Example h803x)
endif()
if (BACNET_FOUND)
add_example(E50HX_Example e50hx)
endif()
add_example(VCAP_Example vcap)
add_example_with_path(Jhd1313m1_lcdSample lcd/upm_i2clcd.jar)
add_example_with_path(Jhd1313m1Sample lcd/upm_i2clcd.jar)
add_example_with_path(Lcm1602_i2cSample lcd/upm_i2clcd.jar)
add_example_with_path(Lcm1602_parallelSample lcd/upm_i2clcd.jar)
add_example_with_path(SSD1308_oledSample lcd/upm_i2clcd.jar)
add_example_with_path(SSD1327_oledSample lcd/upm_i2clcd.jar)
add_example_with_path(Jhd1313m1_lcdSample lcd i2clcd)
add_example_with_path(Jhd1313m1Sample lcd i2clcd)
add_example_with_path(Lcm1602_i2cSample lcd i2clcd)
add_example_with_path(Lcm1602_parallelSample lcd i2clcd)
add_example_with_path(SSD1308_oledSample lcd i2clcd)
add_example_with_path(SSD1327_oledSample lcd i2clcd)

View 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]
}
}

View File

@ -59,7 +59,7 @@ public class Gp2y0aExample {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -39,7 +39,7 @@ public class GroveEmg {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
}

View File

@ -41,7 +41,7 @@ public class GroveGsr {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -38,7 +38,7 @@ public class GroveO2Example {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception has occured: "+e.getMessage());
System.out.println("The following exception has occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -42,7 +42,7 @@ public class HP20xExample {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -39,7 +39,7 @@ public class Th02Example {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception has occured: "+e.getMessage());
System.out.println("The following exception has occurred: "+e.getMessage());
}
}
}

View File

@ -52,7 +52,7 @@ public class Tsl2561 {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View File

@ -0,0 +1,60 @@
/*
* 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_vcap.VCAP;
public class VCAP_Example
{
private static String defaultDev = "/dev/video0";
public static void main(String[] args) throws InterruptedException
{
// ! [Interesting]
if (args.length > 0)
defaultDev = args[0];
System.out.println("Using device " + defaultDev);
System.out.println("Initializing...");
// Instantiate an VCAP instance, using the specified video device
VCAP sensor = new VCAP(defaultDev);
// enable some debug/verbose output
sensor.setDebug(true);
// This is just a hint. The kernel can change this to a lower
// resolution that the hardware supports. Use getWidth() and
// getHeight() methods to see what the kernel actually chose if you
// care.
sensor.setResolution(1920, 1080);
// capture an image
sensor.captureImage();
// convert and save it as a jpeg
sensor.saveImage("video-img1.jpg");
// ! [Interesting]
}
}

View File

@ -43,7 +43,7 @@ public class WaterLevelSensor {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("The following exception occured: "+e.getMessage());
System.out.println("The following exception occurred: "+e.getMessage());
}
}
//! [Interesting]

View 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);
});

View File

@ -128,7 +128,7 @@ function runRadio()
console.log("Received: " + resultStr);
}
if (rv < 0) // some sort of read error occured
if (rv < 0) // some sort of read error occurred
{
console.log("Port read error.");
return;

View File

@ -90,7 +90,7 @@ function exit()
// Load i2clcd module
var lcdObj = require('jsupm_i2clcd');
var lcd = new lcdObj.SSD1306(6, 0x3c);
var lcd = new lcdObj.SSD1306(0, 0x3C);
var next = 0;
lcd.clear();

View File

@ -66,7 +66,7 @@ function getGPSInfo()
process.stdout.write(GPSData)
}
if (rv < 0) // some sort of read error occured
if (rv < 0) // some sort of read error occurred
{
console.log("Port read error.");
process.exit(0);

View File

@ -0,0 +1,69 @@
/*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_vcap');
/************** Main code **************/
var defaultDev = "/dev/video0";
// 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 VCAP instance, using the specified video device
var sensor = new sensorObj.VCAP(defaultDev);
// enable some debug/verbose output
sensor.setDebug(true);
// This is just a hint. The kernel can change this to a lower
// resolution that the hardware supports. Use getWidth() and
// getHeight() methods to see what the kernel actually chose if you
// care.
sensor.setResolution(1920, 1080);
// capture an image
sensor.captureImage();
// convert and save it as a jpeg
sensor.saveImage("video-img1.jpg");
// make sure we clean up
sensor = null;
sensorObj.cleanUp();
sensorObj = null;
console.log("Exiting...");
process.exit(0);

95
examples/python/e50hx.py Normal file
View 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)

View File

@ -123,7 +123,7 @@ else:
resultStr += radioBuffer.__getitem__(x)
print "Received:", resultStr
if (rv < 0): # some sort of read error occured
if (rv < 0): # some sort of read error occurred
print "Port read error."
sys.exit(0)
myCounter += 1

View File

@ -73,7 +73,7 @@ def getGPSInfo():
GPSData += nmeaBuffer.__getitem__(x)
sys.stdout.write(GPSData)
if (rv < 0): # some sort of read error occured
if (rv < 0): # some sort of read error occurred
print "Port read error."
sys.exit(0)

67
examples/python/vcap.py Normal file
View File

@ -0,0 +1,67 @@
#!/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_vcap 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)
defaultDev = "/dev/video0"
# 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 VCAP instance, using the specified video device
sensor = sensorObj.VCAP(defaultDev)
# enable some debug/verbose output
sensor.setDebug(True);
# This is just a hint. The kernel can change this to a lower
# resolution that the hardware supports. Use getWidth() and
# getHeight() methods to see what the kernel actually chose if you
# care.
sensor.setResolution(1920, 1080);
# capture an image
sensor.captureImage();
# convert and save it as a jpeg
sensor.saveImage("video-img1.jpg");

View File

@ -166,7 +166,7 @@ macro(upm_doxygen)
set (classname ${libname})
endif()
set (CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND})
if (BUILDSWIG)
if (BUILDSWIGPYTHON AND BUILDSWIG)
add_dependencies (_pyupm_${libname} pyupm_doxy2swig)
add_dependencies (pydoc _pyupm_${libname})
else ()
@ -203,11 +203,11 @@ if (SWIG_FOUND)
# If a CMAKE_INSTALL_PREFIX has NOT been provided, set NODE_MODULE_INSTALL_PATH
# base on the NODE_ROOT_DIR.
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set (NODE_MODULE_INSTALL_PATH ${NODE_ROOT_DIR}/${LIB_INSTALL_DIR}/node_modules/jsupm_${libname}/)
set (NODE_MODULE_INSTALL_PATH ${NODE_ROOT_DIR}/lib/node_modules/jsupm_${libname}/)
# If a CMAKE_INSTALL_PREFIX has been provided, set NODE_MODULE_INSTALL_PATH
# relative to the provided install directory.
else ()
set (NODE_MODULE_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/node_modules/jsupm_${libname}/)
set (NODE_MODULE_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/lib/node_modules/jsupm_${libname}/)
endif ()
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/package.json
DESTINATION ${NODE_MODULE_INSTALL_PATH} COMPONENT ${libname})

View File

@ -113,7 +113,7 @@ namespace upm {
/**
* Set the zero point. This is the point measured and averaged
* when the sensor is not moving. It is set at construction time
* (averaged over a number of samples), but can be overriden here.
* (averaged over a number of samples), but can be overridden here.
*
* @param zeroPoint The averaged zero point of the sensor at rest
*/

View File

@ -62,7 +62,7 @@ class APA102
*
* @param ledCount Number of APA102 leds in the strip
* @param spiBus SPI Bus number
* @param batchMode (optional) Immediatly write to SPI (false, default) or wait for a pushState
* @param batchMode (optional) Immediately write to SPI (false, default) or wait for a pushState
* call (true)
* @param csn (optional) Chip Select Pin
*/

View File

@ -0,0 +1,24 @@
set (libname "bacnetmstp")
set (libdescription "upm driver module for BACnet MS/TP devices")
set (module_src ${libname}.cxx device-client.c)
set (module_h ${libname}.h)
pkg_search_module(BACNET libbacnet)
if (BACNET_FOUND)
set (reqlibname "libbacnet")
include_directories(${BACNET_INCLUDE_DIRS})
upm_module_init()
add_dependencies(${libname} ${BACNET_LIBRARIES})
target_link_libraries(${libname} ${BACNET_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
if (BUILDSWIG)
if (BUILDSWIGNODE)
swig_link_libraries (jsupm_${libname} ${BACNET_LIBRARIES} ${MRAA_LIBRARIES} ${NODE_LIBRARIES})
endif()
if (BUILDSWIGPYTHON)
swig_link_libraries (pyupm_${libname} ${BACNET_LIBRARIES} ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES})
endif()
if (BUILDSWIGJAVA)
swig_link_libraries (javaupm_${libname} ${BACNET_LIBRARIES} ${MRAAJAVA_LDFLAGS} ${JAVA_LDFLAGS})
endif()
endif()
endif ()

View File

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

716
src/bacnetmstp/bacnetmstp.h Normal file
View File

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

File diff suppressed because it is too large Load Diff

465
src/bacnetmstp/device.h Normal file
View File

@ -0,0 +1,465 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/** @file device.h Defines functions for handling all BACnet objects belonging
* to a BACnet device, as well as Device-specific properties. */
#ifndef DEVICE_H
#define DEVICE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacdef.h"
#include "bacenum.h"
#include "wp.h"
#include "rd.h"
#include "rp.h"
#include "rpm.h"
#include "readrange.h"
/** Called so a BACnet object can perform any necessary initialization.
* @ingroup ObjHelpers
*/
typedef void (
*object_init_function) (
void);
/** Counts the number of objects of this type.
* @ingroup ObjHelpers
* @return Count of implemented objects of this type.
*/
typedef unsigned (
*object_count_function) (
void);
/** Maps an object index position to its corresponding BACnet object instance number.
* @ingroup ObjHelpers
* @param index [in] The index of the object, in the array of objects of its type.
* @return The BACnet object instance number to be used in a BACNET_OBJECT_ID.
*/
typedef uint32_t(
*object_index_to_instance_function)
(
unsigned index);
/** Provides the BACnet Object_Name for a given object instance of this type.
* @ingroup ObjHelpers
* @param object_instance [in] The object instance number to be looked up.
* @param object_name [in,out] Pointer to a character_string structure that
* will hold a copy of the object name if this is a valid object_instance.
* @return True if the object_instance is valid and object_name has been
* filled with a copy of the Object's name.
*/
typedef bool(
*object_name_function)
(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
/** Look in the table of objects of this type, and see if this is a valid
* instance number.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @return True if the object instance refers to a valid object of this type.
*/
typedef bool(
*object_valid_instance_function) (
uint32_t object_instance);
/** Helper function to step through an array of objects and find either the
* first one or the next one of a given type. Used to step through an array
* of objects which is not necessarily contiguious for each type i.e. the
* index for the 'n'th object of a given type is not necessarily 'n'.
* @ingroup ObjHelpers
* @param [in] The index of the current object or a value of ~0 to indicate
* start at the beginning.
* @return The index of the next object of the required type or ~0 (all bits
* == 1) to indicate no more objects found.
*/
typedef unsigned (
*object_iterate_function) (
unsigned current_index);
/** Look in the table of objects of this type, and get the COV Value List.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @param [out] The value list
* @return True if the object instance supports this feature, and has changed.
*/
typedef bool(
*object_value_list_function) (
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
/** Look in the table of objects for this instance to see if value changed.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @return True if the object instance has changed.
*/
typedef bool(
*object_cov_function) (
uint32_t object_instance);
/** Look in the table of objects for this instance to clear the changed flag.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
*/
typedef void (
*object_cov_clear_function) (
uint32_t object_instance);
/** Intrinsic Reporting funcionality.
* @ingroup ObjHelpers
* @param [in] Object instance.
*/
typedef void (
*object_intrinsic_reporting_function) (
uint32_t object_instance);
/** Defines the group of object helper functions for any supported Object.
* @ingroup ObjHelpers
* Each Object must provide some implementation of each of these helpers
* in order to properly support the handlers. Eg, the ReadProperty handler
* handler_read_property() relies on the instance of Object_Read_Property
* for each Object type, or configure the function as NULL.
* In both appearance and operation, this group of functions acts like
* they are member functions of a C++ Object base class.
*/
typedef struct object_functions {
BACNET_OBJECT_TYPE Object_Type;
object_init_function Object_Init;
object_count_function Object_Count;
object_index_to_instance_function Object_Index_To_Instance;
object_valid_instance_function Object_Valid_Instance;
object_name_function Object_Name;
read_property_function Object_Read_Property;
write_property_function Object_Write_Property;
rpm_property_lists_function Object_RPM_List;
rr_info_function Object_RR_Info;
object_iterate_function Object_Iterator;
object_value_list_function Object_Value_List;
object_cov_function Object_COV;
object_cov_clear_function Object_COV_Clear;
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
} object_functions_t;
/* String Lengths - excluding any nul terminator */
#define MAX_DEV_NAME_LEN 32
#define MAX_DEV_LOC_LEN 64
#define MAX_DEV_MOD_LEN 32
#define MAX_DEV_VER_LEN 16
#define MAX_DEV_DESC_LEN 64
/** Structure to define the Object Properties common to all Objects. */
typedef struct commonBacObj_s {
/** The BACnet type of this object (ie, what class is this object from?).
* This property, of type BACnetObjectType, indicates membership in a
* particular object type class. Each inherited class will be of one type.
*/
BACNET_OBJECT_TYPE mObject_Type;
/** The instance number for this class instance. */
uint32_t Object_Instance_Number;
/** Object Name; must be unique.
* This property, of type CharacterString, shall represent a name for
* the object that is unique within the BACnet Device that maintains it.
*/
char Object_Name[MAX_DEV_NAME_LEN];
} COMMON_BAC_OBJECT;
/** Structure to define the Properties of Device Objects which distinguish
* one instance from another.
* This structure only defines fields for properties that are unique to
* a given Device object. The rest may be fixed in device.c or hard-coded
* into the read-property encoding.
* This may be useful for implementations which manage multiple Devices,
* eg, a Gateway.
*/
typedef struct devObj_s {
/** The BACnet Device Address for this device; ->len depends on DLL type. */
BACNET_ADDRESS bacDevAddr;
/** Structure for the Object Properties common to all Objects. */
COMMON_BAC_OBJECT bacObj;
/** Device Description. */
char Description[MAX_DEV_DESC_LEN];
/** The upcounter that shows if the Device ID or object structure has changed. */
uint32_t Database_Revision;
} DEVICE_OBJECT_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Device_Init(
object_functions_t * object_table);
bool Device_Reinitialize(
BACNET_REINITIALIZE_DEVICE_DATA * rd_data);
BACNET_REINITIALIZED_STATE Device_Reinitialized_State(
void);
rr_info_function Device_Objects_RR_Info(
BACNET_OBJECT_TYPE object_type);
void Device_getCurrentDateTime(
BACNET_DATE_TIME * DateTime);
int32_t Device_UTC_Offset(void);
bool Device_Daylight_Savings_Status(void);
void Device_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
void Device_Objects_Property_List(
BACNET_OBJECT_TYPE object_type,
struct special_property_list_t *pPropertyList);
/* functions to support COV */
bool Device_Encode_Value_List(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Device_Value_List_Supported(
BACNET_OBJECT_TYPE object_type);
bool Device_COV(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
void Device_COV_Clear(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
uint32_t Device_Object_Instance_Number(
void);
bool Device_Set_Object_Instance_Number(
uint32_t object_id);
bool Device_Valid_Object_Instance_Number(
uint32_t object_id);
unsigned Device_Object_List_Count(
void);
bool Device_Object_List_Identifier(
unsigned array_index,
int *object_type,
uint32_t * instance);
unsigned Device_Count(
void);
uint32_t Device_Index_To_Instance(
unsigned index);
bool Device_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Device_Set_Object_Name(
BACNET_CHARACTER_STRING * object_name);
/* Copy a child object name, given its ID. */
bool Device_Object_Name_Copy(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Device_Object_Name_ANSI_Init(const char * value);
BACNET_DEVICE_STATUS Device_System_Status(
void);
int Device_Set_System_Status(
BACNET_DEVICE_STATUS status,
bool local);
const char *Device_Vendor_Name(
void);
uint16_t Device_Vendor_Identifier(
void);
void Device_Set_Vendor_Identifier(
uint16_t vendor_id);
const char *Device_Model_Name(
void);
bool Device_Set_Model_Name(
const char *name,
size_t length);
const char *Device_Firmware_Revision(
void);
const char *Device_Application_Software_Version(
void);
bool Device_Set_Application_Software_Version(
const char *name,
size_t length);
const char *Device_Description(
void);
bool Device_Set_Description(
const char *name,
size_t length);
const char *Device_Location(
void);
bool Device_Set_Location(
const char *name,
size_t length);
/* some stack-centric constant values - no set methods */
uint8_t Device_Protocol_Version(
void);
uint8_t Device_Protocol_Revision(
void);
BACNET_SEGMENTATION Device_Segmentation_Supported(
void);
uint32_t Device_Database_Revision(
void);
void Device_Set_Database_Revision(
uint32_t revision);
void Device_Inc_Database_Revision(
void);
bool Device_Valid_Object_Name(
BACNET_CHARACTER_STRING * object_name,
int *object_type,
uint32_t * object_instance);
bool Device_Valid_Object_Id(
int object_type,
uint32_t object_instance);
int Device_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Device_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool DeviceGetRRInfo(
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
RR_PROP_INFO * pInfo); /* Where to put the information */
int Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#if defined(INTRINSIC_REPORTING)
void Device_local_reporting(
void);
#endif
/* Prototypes for Routing functionality in the Device Object.
* Enable by defining BAC_ROUTING in config.h and including gw_device.c
* in the build (lib/Makefile).
*/
void Routing_Device_Init(
uint32_t first_object_instance);
uint16_t Add_Routed_Device(
uint32_t Object_Instance,
BACNET_CHARACTER_STRING * Object_Name,
const char *Description);
DEVICE_OBJECT_DATA *Get_Routed_Device_Object(
int idx);
BACNET_ADDRESS *Get_Routed_Device_Address(
int idx);
void routed_get_my_address(
BACNET_ADDRESS * my_address);
bool Routed_Device_Address_Lookup(
int idx,
uint8_t address_len,
uint8_t * mac_adress);
bool Routed_Device_GetNext(
BACNET_ADDRESS * dest,
int *DNET_list,
int *cursor);
bool Routed_Device_Is_Valid_Network(
uint16_t dest_net,
int *DNET_list);
uint32_t Routed_Device_Index_To_Instance(
unsigned index);
bool Routed_Device_Valid_Object_Instance_Number(
uint32_t object_id);
bool Routed_Device_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
uint32_t Routed_Device_Object_Instance_Number(
void);
bool Routed_Device_Set_Object_Instance_Number(
uint32_t object_id);
bool Routed_Device_Set_Object_Name(
uint8_t encoding,
const char *value,
size_t length);
bool Routed_Device_Set_Description(
const char *name,
size_t length);
void Routed_Device_Inc_Database_Revision(
void);
int Routed_Device_Service_Approval(
BACNET_CONFIRMED_SERVICE service,
int service_argument,
uint8_t * apdu_buff,
uint8_t invoke_id);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/** @defgroup ObjFrmwk Object Framework
* The modules in this section describe the BACnet-stack's framework for
* BACnet-defined Objects (Device, Analog Input, etc). There are two submodules
* to describe this arrangement:
* - The "object helper functions" which provide C++-like common functionality
* to all supported object types.
* - The interface between the implemented Objects and the BAC-stack services,
* specifically the handlers, which are mediated through function calls to
* the Device object.
*//** @defgroup ObjHelpers Object Helper Functions
* @ingroup ObjFrmwk
* This section describes the function templates for the helper functions that
* provide common object support.
*//** @defgroup ObjIntf Handler-to-Object Interface Functions
* @ingroup ObjFrmwk
* This section describes the fairly limited set of functions that link the
* BAC-stack handlers to the BACnet Object instances. All of these calls are
* situated in the Device Object, which "knows" how to reach its child Objects.
*
* Most of these calls have a common operation:
* -# Call Device_Objects_Find_Functions( for the desired Object_Type )
* - Gets a pointer to the object_functions for this Type of Object.
* -# Call the Object's Object_Valid_Instance( for the desired object_instance )
* to make sure there is such an instance.
* -# Call the Object helper function needed by the handler,
* eg Object_Read_Property() for the RP handler.
*
*/
#endif

View File

@ -0,0 +1,23 @@
%module javaupm_bacnetmstp
%include "../upm.i"
%include "typemaps.i"
%include "cpointer.i"
%include "arrays_java.i";
%include "../java_buffer.i"
%{
#include "bacnetmstp.h"
%}
%include "bacnetmstp.h"
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("javaupm_bacnetmstp");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}

View File

@ -0,0 +1,11 @@
%module jsupm_bacnetmstp
%include "../upm.i"
%include "stdint.i"
%include "cpointer.i"
%pointer_functions(float, floatp);
%include "bacnetmstp.h"
%{
#include "bacnetmstp.h"
%}

View File

@ -0,0 +1,15 @@
// Include doxygen-generated documentation
%include "pyupm_doxy2swig.i"
%module pyupm_bacnetmstp
%include "../upm.i"
%include "stdint.i"
%include "cpointer.i"
%feature("autodoc", "3");
%pointer_functions(float, floatp);
%include "bacnetmstp.h"
%{
#include "bacnetmstp.h"
%}

65
src/bacnetmstp/timer.h Normal file
View File

@ -0,0 +1,65 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*********************************************************************/
#ifndef TIMER_H
#define TIMER_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h> /* for timeval */
/* Timer Module */
#ifndef MAX_MILLISECOND_TIMERS
#define TIMER_SILENCE 0
#define MAX_MILLISECOND_TIMERS 1
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
uint32_t timeGetTime(
void);
void timer_init(
void);
uint32_t timer_milliseconds(
unsigned index);
bool timer_elapsed_milliseconds(
unsigned index,
uint32_t value);
bool timer_elapsed_seconds(
unsigned index,
uint32_t value);
bool timer_elapsed_minutes(
unsigned index,
uint32_t seconds);
uint32_t timer_milliseconds_set(
unsigned index,
uint32_t value);
uint32_t timer_reset(
unsigned index);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

28
src/e50hx/CMakeLists.txt Normal file
View File

@ -0,0 +1,28 @@
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()
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
View 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
View 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
View 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
View 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
View 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"
%}

View File

@ -522,7 +522,7 @@ namespace upm {
/**
* Sets up the interrupt 1 threshold register
*
* @param val Threshhold to set
* @param val Threshold to set
* @return True if successful
*/
bool setInterrupt1Threshold(uint8_t val);
@ -530,7 +530,7 @@ namespace upm {
/**
* Sets up the interrupt 2 threshold register
*
* @param val Threshhold to set
* @param val Threshold to set
* @return True if successful
*/
bool setInterrupt2Threshold(uint8_t val);

View File

@ -8,7 +8,6 @@ if (MODBUS_FOUND)
set (reqlibname "libmodbus")
include_directories(${MODBUS_INCLUDE_DIRS})
upm_module_init()
add_dependencies(${libname} ${MODBUS_LIBRARIES})
target_link_libraries(${libname} ${MODBUS_LIBRARIES})
if (BUILDSWIG)
if (BUILDSWIGNODE)

View File

@ -8,7 +8,6 @@ if (MODBUS_FOUND)
set (reqlibname "libmodbus")
include_directories(${MODBUS_INCLUDE_DIRS})
upm_module_init()
add_dependencies(${libname} ${MODBUS_LIBRARIES})
target_link_libraries(${libname} ${MODBUS_LIBRARIES})
if (BUILDSWIG)
if (BUILDSWIGNODE)

View File

@ -851,7 +851,7 @@ namespace upm {
/**
* enable I2C Bypass. Enabling this feature allows devices on the
* MPU60X0 auxillary I2C bus to be visible on the MCU's I2C bus.
* MPU60X0 auxiliary I2C bus to be visible on the MCU's I2C bus.
*
* @param enable true to I2C bypass
* @return true if successful, false otherwise

View File

@ -177,8 +177,8 @@ namespace upm {
/**
* Set the driection mode (input or output) for all gpios enabled
* by gpioSetIOMask(). A 0 in a given bit postion (LSB = gpio0)
* configures the gpio as an ouput, and a 1 bit configures the
* by gpioSetIOMask(). A 0 in a given bit position (LSB = gpio0)
* configures the gpio as an output, and a 1 bit configures the
* gpio as an input. Only the gpios enabled by gpioSetMask() are
* affected by this call.
*
@ -211,7 +211,7 @@ namespace upm {
/**
* Read the raw analog input value present at the given gpio and
* return the coresponding voltage value at the pin. The gpio is
* return the corresponding voltage value at the pin. The gpio is
* switched to analog input mode by this call, regardless of any
* previous mode. The returned value will be a number between
* 0.0-3.3, depending on the voltage present at the pin. Only the

View File

@ -371,17 +371,17 @@ void hal_aci_tl_init(aci_pins_t *a_pins, bool debug)
error = mraa_gpio_dir (a_pins->m_rdy_ctx, MRAA_GPIO_IN);
if (error != MRAA_SUCCESS) {
printf ("[ERROR] GPIO failed to initilize \n");
printf ("[ERROR] GPIO failed to initialize \n");
}
error = mraa_gpio_dir (a_pins->m_req_ctx, MRAA_GPIO_OUT);
if (error != MRAA_SUCCESS) {
printf ("[ERROR] GPIO failed to initilize \n");
printf ("[ERROR] GPIO failed to initialize \n");
}
error = mraa_gpio_dir (a_pins->m_rst_ctx, MRAA_GPIO_OUT);
if (error != MRAA_SUCCESS) {
printf ("[ERROR] GPIO failed to initilize \n");
printf ("[ERROR] GPIO failed to initialize \n");
}
if (UNUSED != a_pins->active_pin) {

View File

@ -8,7 +8,6 @@ if (OPENZWAVE_FOUND)
set (reqlibname "libopenzwave")
include_directories(${OPENZWAVE_INCLUDE_DIRS})
upm_module_init()
add_dependencies(${libname} ${OPENZWAVE_LIBRARIES})
target_link_libraries(${libname} ${OPENZWAVE_LIBRARIES})
if (BUILDSWIG)
if (BUILDSWIGNODE)

View File

@ -398,7 +398,7 @@ namespace upm {
* library. Most modern devices are never polled, rather they are
* configured to report changing values to the controller on their
* own at device specific intervals or when appropriate events
* (depending the device) have occured.
* (depending the device) have occurred.
*
* @param nodeId The node ID
* @param index The value index (see dumpNodes()) of the value to query.

View File

@ -192,8 +192,8 @@ uint32_t PN532::getFirmwareVersion()
@param cmdlen The size of the command in bytes
@param timeout timeout before giving up
@returns 1 if everything is OK, 0 if timeout occured before an
ACK was recieved
@returns 1 if everything is OK, 0 if timeout occurred before an
ACK was received
*/
/**************************************************************************/
// default timeout of one second

View File

@ -232,8 +232,8 @@ namespace upm {
* @param cmdlen the size of the command in bytes
* @param timeout timeout before giving up (in ms)
*
* @return true if everything is OK, false if timeout occured
* before an ACK was recieved
* @return true if everything is OK, false if timeout occurred
* before an ACK was received
*/
bool sendCommandCheckAck(uint8_t *cmd, uint8_t cmdlen,
uint16_t timeout=1000);

View File

@ -189,7 +189,7 @@ mraa::Result SI1132::runCommand(uint8_t command)
}
if (response == 0) {
status = mraa::ERROR_UNSPECIFIED;
fprintf(stderr, "Comand %d failed\n", command);
fprintf(stderr, "Command %d failed\n", command);
}
return status;
}

View File

@ -397,7 +397,7 @@ SmartDrive::GetMotorStatus(int motor_id) {
if (motor_id == SmartDrive_Motor_ID_2)
status = readByte(SmartDrive_STATUS_M1);
if (motor_id == SmartDrive_Motor_ID_BOTH) {
std::cout << "Please specifiy which motor's status you want to fetch !" << std::endl;
std::cout << "Please specify which motor's status you want to fetch !" << std::endl;
}
return status;
}
@ -425,6 +425,6 @@ SmartDrive::PrintMotorStatus(int motor_id) {
std::cout << "Motor " << motor_id+1 << " is " << ((is_stalled == 0) ? "NOT" : "") << " stalled" << std::endl;
} else {
std::cout << "Please specifiy which motor's status you want to fetch !" << std::endl;
std::cout << "Please specify which motor's status you want to fetch !" << std::endl;
}
}

View File

@ -1740,7 +1740,7 @@ namespace upm {
* FSK : N/A ( set to 0 )
* LoRa: timeout in symbols
* @param fixLen Fixed length packets [false: variable, true: fixed]
* @param payloadLen Sets payload length when fixed lenght is used
* @param payloadLen Sets payload length when fixed length is used
* @param crcOn Enables/Disables the CRC [false: OFF, true: ON]
* @param FreqHopOn Enables disables the intra-packet frequency hopping
* FSK : N/A ( set to 0 )

View File

@ -8,7 +8,6 @@ if (MODBUS_FOUND)
set (reqlibname "libmodbus")
include_directories(${MODBUS_INCLUDE_DIRS})
upm_module_init()
add_dependencies(${libname} ${MODBUS_LIBRARIES})
target_link_libraries(${libname} ${MODBUS_LIBRARIES})
if (BUILDSWIG)
if (BUILDSWIGNODE)

View File

@ -221,6 +221,12 @@
* @ingroup bycat
*/
/**
* @brief Provide video or video camera access
* @defgroup video Video
* @ingroup bycat
*/
/**
* @brief Provide WiFi, Bluetooth, RF communication
* @defgroup wifi Wireless Communication

18
src/vcap/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
set (libname "vcap")
set (libdescription "upm Video Frame Capture and image save utility")
set (module_src ${libname}.cxx)
set (module_h ${libname}.hpp)
set (reqlibname "jpeg")
upm_module_init()
target_link_libraries(${libname} jpeg)
if (BUILDSWIG)
if (BUILDSWIGNODE)
swig_link_libraries (jsupm_${libname} jpeg ${MRAA_LIBRARIES} ${NODE_LIBRARIES})
endif()
if (BUILDSWIGPYTHON)
swig_link_libraries (pyupm_${libname} jpeg ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES})
endif()
if (BUILDSWIGJAVA)
swig_link_libraries (javaupm_${libname} jpeg ${MRAAJAVA_LDFLAGS} ${JAVA_LDFLAGS})
endif()
endif()

19
src/vcap/javaupm_vcap.i Normal file
View File

@ -0,0 +1,19 @@
%module javaupm_vcap
%include "../upm.i"
%include "std_string.i"
%include "vcap.hpp"
%{
#include "vcap.hpp"
%}
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("javaupm_vcap");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}

10
src/vcap/jsupm_vcap.i Normal file
View File

@ -0,0 +1,10 @@
%module jsupm_vcap
%include "../upm.i"
%include "std_string.i"
%include "vcap.hpp"
%{
#include "vcap.hpp"
%}

14
src/vcap/pyupm_vcap.i Normal file
View File

@ -0,0 +1,14 @@
// Include doxygen-generated documentation
%include "pyupm_doxy2swig.i"
%module pyupm_vcap
%include "../upm.i"
%include "std_string.i"
%feature("autodoc", "3");
%include "vcap.hpp"
%{
#include "vcap.hpp"
%}

524
src/vcap/vcap.cxx Normal file
View File

@ -0,0 +1,524 @@
/*
* 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 <iostream>
#include <stdexcept>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "vcap.hpp"
using namespace upm;
using namespace std;
#define CLAMP(_val, _min, _max) \
(((_val) < (_min)) ? (_min) : (((_val) > (_max)) ? (_max) : (_val)))
VCAP::VCAP(string videoDev) :
m_buffer(0), m_fd(-1)
{
memset(&m_caps, 0, sizeof(struct v4l2_capability));
memset(&m_format, 0, sizeof(struct v4l2_format));
m_debugging = false;
m_bufferLen = 0;
m_videoDevice = videoDev;
setJPGQuality(VCAP_DEFAULT_JPEG_QUALITY);
// try to open the video device, and set a default format.
if (!initVideoDevice())
throw std::runtime_error(std::string(__FUNCTION__) +
": initVideoDevice() failed");
m_height = 0;
m_width = 0;
m_imageCaptured = false;
}
VCAP::~VCAP()
{
releaseBuffer();
if (m_fd >= 0)
close(m_fd);
m_fd = -1;
}
bool VCAP::initVideoDevice()
{
if (m_videoDevice.empty())
return false;
if ((m_fd = open(m_videoDevice.c_str(), O_RDWR)) < 0)
{
cerr << __FUNCTION__ << ": open failed: " << strerror(errno) << endl;
return false;
}
if (!checkCapabilities())
{
close(m_fd);
m_fd = -1;
return false;
}
return true;
}
// This seems... odd, but appears to be necessary.
// Ignore error and retry if the ioctl fails due to EINTR
int VCAP::xioctl(int fd, int request, void* argp)
{
int r;
do {
r = ioctl(fd, request, argp);
}
while (r == -1 && errno == EINTR);
return r;
}
bool VCAP::checkCapabilities()
{
if (xioctl(m_fd, VIDIOC_QUERYCAP, &m_caps) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_QUERYCAP) failed: "
<< strerror(errno) << endl;
return false;
}
if (m_debugging)
{
cerr << "Driver: " << m_caps.driver << endl;
cerr << "Device: " << m_caps.card << endl;
cerr << "Caps : 0x" << std::hex << m_caps.capabilities << std::dec
<< endl;
}
// see if capturing is supported
if (!(m_caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
cerr << __FUNCTION__ << ": Device does not support video capture"
<< endl;
return false;
}
if (!(m_caps.capabilities & V4L2_CAP_STREAMING))
{
cerr << __FUNCTION__ << ": Device does not support streaming I/O"
<< endl;
return false;
}
return true;
}
bool VCAP::setResolution(int width, int height)
{
// in case we already created one
releaseBuffer();
m_width = width;
m_height = height;
m_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// initialize with the current format
if (xioctl(m_fd, VIDIOC_G_FMT, &m_format) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_G_FMT) failed: "
<< strerror(errno) << endl;
return false;
}
// make our changes...
m_format.fmt.pix.width = m_width;
m_format.fmt.pix.height = m_height;
m_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
m_format.fmt.pix.field = V4L2_FIELD_ANY;
if (xioctl(m_fd, VIDIOC_S_FMT, &m_format) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_S_FMT) failed: "
<< strerror(errno) << endl;
// If it's just busy, then this still might work, so don't fail here
if (errno != EBUSY)
return false;
}
// Now retrieve the driver's selected format and check it -
// specifically, the width and height might change, causing
// coredumps if we don't adjust them accordingly.
if (xioctl(m_fd, VIDIOC_G_FMT, &m_format) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_G_FMT) failed: "
<< strerror(errno) << endl;
return false;
}
// G_FMT will have adjusted these if neccessary, so verify
if (m_format.fmt.pix.width != m_width)
{
if (m_debugging)
cerr << __FUNCTION__ << ": Warning: Selected width "
<< std::to_string(m_width)
<< " adjusted by driver to "
<< std::to_string(m_format.fmt.pix.width)
<< endl;
m_width = m_format.fmt.pix.width;
}
if (m_format.fmt.pix.height != m_height)
{
if (m_debugging)
cerr << __FUNCTION__ << ": Warning: Selected height "
<< std::to_string(m_height)
<< " adjusted by driver to "
<< std::to_string(m_format.fmt.pix.height)
<< endl;
m_height = m_format.fmt.pix.height;
}
// now alloc the buffers here
if (!allocBuffer())
return false;
return true;
}
bool VCAP::allocBuffer()
{
struct v4l2_requestbuffers rb;
memset(&rb, 0, sizeof(rb));
// we just want one buffer, and we only support mmap().
rb.count = 1;
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
rb.memory = V4L2_MEMORY_MMAP;
if (xioctl(m_fd, VIDIOC_REQBUFS, &rb) < 0)
{
if (errno == EINVAL)
{
cerr << __FUNCTION__ << ": Capture device does not support mmapped "
<< "buffers"
<< endl;
}
cerr << __FUNCTION__ << ": ioctl(VIDIOC_REQBUFS) failed: "
<< strerror(errno) << endl;
return false;
}
// get the buffer and mmap it
struct v4l2_buffer mbuf;
memset(&mbuf, 0, sizeof(mbuf));
mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mbuf.memory = V4L2_MEMORY_MMAP;
mbuf.index = 0;
if (xioctl(m_fd, VIDIOC_QUERYBUF, &mbuf) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_QUERYBUF) failed: "
<< strerror(errno) << endl;
return false;
}
// map it
m_buffer = (unsigned char *)mmap(NULL, mbuf.length,
PROT_READ | PROT_WRITE, MAP_SHARED,
m_fd, mbuf.m.offset);
if (m_buffer == MAP_FAILED)
{
cerr << __FUNCTION__ << ": mmap() failed: "
<< strerror(errno) << endl;
return false;
}
// we'll need this when unmapping
m_bufferLen = mbuf.length;
return true;
}
void VCAP::releaseBuffer()
{
// first unmap any buffers
if (m_buffer)
munmap(m_buffer, m_bufferLen);
m_buffer = 0;
m_bufferLen = 0;
// then, tell the kernel driver to free any allocated buffer(s)...
struct v4l2_requestbuffers rb;
memset(&rb, 0, sizeof(rb));
rb.count = 0;
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
rb.memory = V4L2_MEMORY_MMAP;
if (xioctl(m_fd, VIDIOC_REQBUFS, &rb) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_REQBUFS) failed while freeing: "
<< strerror(errno) << endl;
}
// reset captured flag
m_imageCaptured = false;
}
bool VCAP::YUYV2JPEG(FILE *file)
{
struct jpeg_compress_struct jpgInfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
unsigned char *row_buffer = NULL;
unsigned char *yuyv = NULL;
int z;
row_buffer = (unsigned char *)calloc(m_width * 3, 1);
if (!row_buffer)
{
cerr << __FUNCTION__ << ": allocation of line buffer failed."
<< endl;
return false;
}
yuyv = m_buffer;
jpgInfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&jpgInfo);
jpeg_stdio_dest(&jpgInfo, file);
jpgInfo.image_width = m_width;
jpgInfo.image_height = m_height;
// components R, G, B
jpgInfo.input_components = 3;
jpgInfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&jpgInfo);
jpeg_set_quality(&jpgInfo, m_jpgQuality, TRUE);
jpeg_start_compress(&jpgInfo, TRUE);
z = 0;
while (jpgInfo.next_scanline < jpgInfo.image_height)
{
int x;
unsigned char *ptr = row_buffer;
for (x = 0; x < m_width; x++)
{
int r, g, b;
int y, u, v;
if (!z)
y = yuyv[0] << 8;
else
y = yuyv[2] << 8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
r = (y + (359 * v)) >> 8;
g = (y - (88 * u) - (183 * v)) >> 8;
b = (y + (454 * u)) >> 8;
*(ptr++) = CLAMP(r, 0, 255);
*(ptr++) = CLAMP(g, 0, 255);
*(ptr++) = CLAMP(b, 0, 255);
if (z++)
{
z = 0;
yuyv += 4;
}
}
row_pointer[0] = row_buffer;
jpeg_write_scanlines(&jpgInfo, row_pointer, 1);
}
jpeg_finish_compress(&jpgInfo);
jpeg_destroy_compress(&jpgInfo);
free(row_buffer);
return true;
}
bool VCAP::saveImage(string filename)
{
// check m_buffer to make sure we have an actual buffer... If not,
// we throw here.
if (!m_buffer)
{
throw std::runtime_error(std::string(__FUNCTION__) +
": no buffer. Call setResolution() first");
}
// if we haven't done at least one capture yet...
if (!m_imageCaptured)
{
throw std::runtime_error(std::string(__FUNCTION__) +
": No data, call captureImage() first");
}
FILE *file;
if ((file = fopen(filename.c_str(), "wb")) == NULL)
{
cerr << __FUNCTION__ << ": fopen() failed: "
<< strerror(errno) << endl;
return false;
}
YUYV2JPEG(file);
fclose(file);
if (m_debugging)
cerr << __FUNCTION__ << ": Saved image to " << filename << endl;
return true;
}
bool VCAP::captureImage()
{
// first, make sure a resolution was specified. If not, set the
// default
if (m_width == 0 || m_height == 0)
{
if (!setResolution(VCAP_DEFAULT_WIDTH, VCAP_DEFAULT_HEIGHT))
throw std::runtime_error(std::string(__FUNCTION__) +
": setResolution() failed");
}
// we basically just call doCaptureImage() twice - once to grab and
// discard the first frame (which is usually a remnent of a previous
// capture), and another to grab the real frame we are interesed in.
if (!doCaptureImage())
{
cerr << __FUNCTION__ << ": capture of first frame failed"
<< endl;
}
return doCaptureImage();
}
// the real workhorse
bool VCAP::doCaptureImage()
{
struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;
// queue our buffer
if (xioctl(m_fd, VIDIOC_QBUF, &buf) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_QBUF) failed: "
<< strerror(errno) << endl;
return false;
}
// enable streaming
if (xioctl(m_fd, VIDIOC_STREAMON, &buf.type) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_STREAMON) failed: "
<< strerror(errno) << endl;
return false;
}
// use select to wait for a complete frame.
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_fd, &fds);
struct timeval tv;
memset(&tv, 0, sizeof(tv));
// 5 seconds should be more than enough
tv.tv_sec = 5;
int rv;
if ((rv = select(m_fd + 1, &fds, NULL, NULL, &tv)) < 0)
{
cerr << __FUNCTION__ << ": select() failed: "
<< strerror(errno) << endl;
return false;
}
if (!rv)
{
// timed out
cerr << __FUNCTION__ << ": select() timed out waiting for frame"
<< endl;
return false;
}
// de-queue the buffer, we're now free to access it via the mmapped
// ptr (m_buffer)
if (xioctl(m_fd, VIDIOC_DQBUF, &buf) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_DQBUF) failed: "
<< strerror(errno) << endl;
return false;
}
// turn off streaming
if (xioctl(m_fd, VIDIOC_STREAMOFF, &buf.type) < 0)
{
cerr << __FUNCTION__ << ": ioctl(VIDIOC_STREAMOFF) failed: "
<< strerror(errno) << endl;
return false;
}
m_imageCaptured = true;
return true;
}
void VCAP::setJPGQuality(unsigned int qual)
{
m_jpgQuality = CLAMP(qual, 0, 100);
}

214
src/vcap/vcap.hpp Normal file
View File

@ -0,0 +1,214 @@
/*
* 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 <iostream>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/videodev2.h>
#define VCAP_DEFAULT_VIDEODEV "/dev/video0"
#define VCAP_DEFAULT_OUTPUTFILE "vcap.jpg"
#define VCAP_DEFAULT_WIDTH 640
#define VCAP_DEFAULT_HEIGHT 480
#define VCAP_DEFAULT_JPEG_QUALITY 99
namespace upm {
/**
* @brief Take a snapshot from a video camera and save as a JPEG
* @defgroup vcap libupm-vcap
* @ingroup video
*/
/**
* @library vcap
* @sensor vcap
* @comname Video Capture
* @type video
*
* @brief API for the Video Capture driver
*
* This UPM module captures a still frame from a Linux V4L device,
* such as a USB webcam, and and then allows you to save it as a
* JPEG image into a file.
*
* The camera and driver in use must support streaming, mmap-able
* buffers and must provide data in YUYV format. This should
* encompass most video cameras out there. It has been tested
* with a few off the shelf cameras without any problems.
*
* @snippet vcap.cxx Interesting
*/
class VCAP {
public:
/**
* VCAP object constructor
*
* @param videoDev The path to the video device, default is /dev/video0.
*/
VCAP(std::string videoDev=VCAP_DEFAULT_VIDEODEV);
/**
* VCAP object destructor
*/
~VCAP();
/**
* Set the desired resolution of the output image. Note, this is
* a hint to the underlying video driver. The video driver is
* free to lower the specified resolution if the hardware cannot
* support it. You can use getHeight() and getWidth() after
* calling this method to see what the video driver chose.
*
* @param width The desired width of the image.
* @param width The desired height of the image.
* @return true if the operation succeeded, false otherwise.
*/
bool setResolution(int width, int height);
/**
* Capture an image from the camera.
*
* @return true if the operation succeeded, false otherwise.
*/
bool captureImage();
/**
* Save the captured image (created with captureImage()) to a file
* in JPEG format. The file will be overwritten if it already
* exists.
*
* @param filename The name of the file in which to store the image.
* @return true if the operation succeeded, false otherwise.
*/
bool saveImage(std::string filename=VCAP_DEFAULT_OUTPUTFILE);
/**
* Return the current width of the image. You can use this method
* to determine if the video driver downgraded it after a call to
* setResolution().
*
* @return true Current width of capture.
*/
int getWidth() const
{
return m_width;
};
/**
* Return the current height of the image. You can use this method
* to determine if the video driver downgraded it after a call to
* setResolution().
*
* @return true Current height of capture.
*/
int getHeight() const
{
return m_height;
};
/**
* Set the JPEG quality.
*
* @param quality A number between 0-100, with higher numbers
* meaning higher quality. Numbers less than 0 will be clamped to
* 0, numbers higher than 100 will be clamped to 100.
*/
void setJPGQuality(unsigned int quality);
/**
* Get the current JPEG quality setting.
*
* @return the current JPEG quality setting.
*/
int getJPGQuality() const
{
return m_jpgQuality;
};
/**
* Enable or disable debugging output.
*
* @param enable true to enable debugging, false otherwise
*/
void setDebug(bool enable)
{
m_debugging = enable;
};
protected:
// open the device and check that it meats minimum requirements
bool initVideoDevice();
// make sure device is streamable, supports mmap and capture
bool checkCapabilities();
// read the mmapped buffer in YUYV format and create a jpeg image
bool YUYV2JPEG(FILE *file);
// buffer management
bool allocBuffer();
void releaseBuffer();
// does the actual capture
bool doCaptureImage();
private:
// internal ioctl
int xioctl(int fd, int request, void* argp);
std::string m_videoDevice;
// our file descriptor to the video device
int m_fd;
// v4l info
struct v4l2_capability m_caps;
struct v4l2_format m_format;
// our mmaped buffer
unsigned char *m_buffer;
size_t m_bufferLen;
// the resolution and quality
int m_width;
int m_height;
int m_jpgQuality;
// at least one image captured with current settings?
bool m_imageCaptured;
// are we debugging?
bool m_debugging;
};
}