Compare commits

..

38 commits

Author SHA1 Message Date
694866cf02 includes without 'libiec61850' prefix wont compile installed versions of the library at '/usr/local/include/libiec61850' 2017-11-19 14:08:24 +01:00
dfd077c4c2 declarations in for loops are only allowed in C99 2017-11-19 14:01:41 +01:00
8a83b535a9 install hal_ethernet.h 2017-11-19 14:00:07 +01:00
979656094d add ability to remove sockets from EthernetHandleSet 2017-11-19 13:53:42 +01:00
967e6ef5b3 use poll() instead of select() 2017-11-19 13:53:37 +01:00
20f654bbc3 add documentation to SVReceiver_tick() 2017-11-19 11:40:25 +01:00
63b6da6ae7 improve documentation of HandleSet_waitReady() 2017-11-19 11:40:25 +01:00
646c9cf073 add support for new EthernetHandleSet to GOOSE subscriber 2017-11-19 11:40:25 +01:00
b6cf079a94 add support for new EthernetHandleSet to SV subscriber 2017-11-19 11:40:24 +01:00
dec12b61a8 refactor: declare private functions static 2017-11-19 11:40:24 +01:00
c082f09aef add EthernetHandleSet implementation for Windows 2017-11-19 11:40:24 +01:00
89661b471f add EthernetHandleSet implementation for BSD 2017-11-19 11:40:24 +01:00
d771444904 add EthernetHandleSet implementation for Linux 2017-11-19 11:40:24 +01:00
1adcd6bcfb fixed typo 2017-11-18 15:08:08 +01:00
Michael Zillgith
f7b04a02ed - MMS client: fixed bug in MmsConnection_connect (COTP payload buffer was not reset in case of an error during connect -> connection failed in case of reuse of MmsConnection object) 2017-11-17 17:29:48 +01:00
Michael Zillgith
87583f5859 - integrated travis status into README.MD 2017-11-12 18:52:14 +01:00
Michael Zillgith
bc6b7e68a8 - fixed typo in src/sampled_values/sv_publisher.c 2017-11-12 18:43:50 +01:00
Michael Zillgith
ba985ec2b0 - MMS client: delete named variable list service supports VMD specific lists 2017-11-12 18:34:13 +01:00
Michael Zillgith
3b941ca577
Merge pull request #31 from stv0g/travis
Travis-CI
2017-11-12 17:50:48 +01:00
ddae3c70ee use correct include path for generated configuration files 2017-11-12 11:13:18 +01:00
Michael Zillgith
5d03e77343
Merge pull request #33 from stv0g/sv-add-smprate
Sampled Values: add support for sample rate attribute
2017-11-10 19:12:08 +01:00
Michael Zillgith
4cbaa6a7c1
Merge pull request #32 from stv0g/sv-fix-smpmod
Sampled Values: fix encoding of optional smpMod attribute:
2017-11-10 19:10:26 +01:00
Michael Zillgith
197a0b853f
Merge pull request #30 from stv0g/sv-types
Sampled Values: add support for 64 bit integers
2017-11-10 18:54:06 +01:00
Michael Zillgith
180e5e7f26
Merge pull request #29 from stv0g/refactoring
Use spaces instead of tabs consistently
2017-11-10 18:48:26 +01:00
e95b321d12 sampled values: fix data type for smpRate 2017-11-10 16:50:05 +01:00
60c7d3a75f sampled values: fix encoding of optional smpMod attribute:
- The attribute must be encoded as an 16bit unsigned integer. The existing code encodes it as 32 bit integer.
- The attribute is encoded with the ASN.1 BER encoding Tag number 8. The existing code encodes it with tag number 4 which is used for the refrTm attribute.

See also: IEC 61850-9-2:2011 (page 20)
2017-11-10 15:50:23 +01:00
628641cabb sampled values: add support for sample rate attribute 2017-11-10 15:45:34 +01:00
1d8d6211ca remove OS X to cut build time 2017-11-10 14:55:14 +01:00
dffe219303 fix version incompatability in Swig 2017-11-10 14:54:11 +01:00
581bd6e9af run tests in Travis-CI 2017-11-10 14:54:10 +01:00
403c3a9e93 added missing case to switch statement in order to silence compiler warnings 2017-11-10 14:54:10 +01:00
064bfa902e mostly cosmetical cleanup of CMakeLists 2017-11-10 14:54:10 +01:00
4fb3141088 enable build on OS X and Python bindings 2017-11-10 14:54:10 +01:00
02bc54a8c2 fix wrong #cmakedefine01 statement 2017-11-10 11:51:22 +01:00
fc6e3892c6 added Travis-CI config 2017-11-10 11:43:14 +01:00
3047681854 refactoring: use spaces instead of tabs consistently 2017-11-10 11:26:20 +01:00
09d5daf0b5 sampled values: add support for 64 bit integers 2017-11-10 11:23:45 +01:00
Michael Zillgith
c291db5d95 - updated README file
- SV receiver: Added semaphore to make subscriber list thread-safe
2017-11-08 08:53:37 +01:00
24 changed files with 748 additions and 190 deletions

25
.travis.yml Normal file
View file

@ -0,0 +1,25 @@
language: c
compiler:
- clang
- gcc
addons:
apt:
packages:
- cmake
- swig
- libsqlite3-dev
- python-dev
before_install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
brew update;
brew install swig python sqlite;
fi
- mkdir -p build && cd build
script:
- cmake .. -DBUILD_PYTHON_BINDINGS=ON
- make
- make test

View file

@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 2.8)
# automagically detect if we should cross-compile
if(DEFINED ENV{TOOLCHAIN})
set(CMAKE_C_COMPILER $ENV{TOOLCHAIN}gcc)
set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN}g++)
set(CMAKE_AR "$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE)
set(CMAKE_C_COMPILER $ENV{TOOLCHAIN}gcc)
set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN}g++)
set(CMAKE_AR "$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE)
endif()
project(libiec61850)
@ -32,20 +32,15 @@ option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF)
option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" ON)
option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF)
# choose the library features which shall be included
option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON)
# choose the library features which shall be included
option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON)
option(CONFIG_IEC61850_REPORT_SERVICE "Build with support for IEC 61850 reporting services" ON)
option(CONFIG_IEC61850_LOG_SERVICE "Build with support for IEC 61850 logging services" ON)
option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON)
option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "8000" CACHE STRING "Default buffer size for buffered reports in byte" )
# advanced options
@ -63,22 +58,23 @@ option(DEBUG_MMS_CLIENT "Enable MMS CLIENT printf debugging" OFF)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/config
${CMAKE_CURRENT_LIST_DIR}/src/common/inc
${CMAKE_CURRENT_LIST_DIR}/src/goose
${CMAKE_CURRENT_LIST_DIR}/src/sampled_values
${CMAKE_CURRENT_LIST_DIR}/src/hal/inc
${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc
${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc_private
${CMAKE_CURRENT_LIST_DIR}/src/mms/inc
${CMAKE_CURRENT_LIST_DIR}/src/mms/inc_private
${CMAKE_CURRENT_LIST_DIR}/src/mms/iso_mms/asn1c
${CMAKE_CURRENT_LIST_DIR}/src/logging
src/common/inc
src/goose
src/sampled_values
src/hal/inc
src/iec61850/inc
src/iec61850/inc_private
src/mms/inc
src/mms/inc_private
src/mms/iso_mms/asn1c
src/logging
)
set(API_HEADERS
src/hal/inc/hal_time.h
src/hal/inc/hal_thread.h
src/hal/inc/hal_filesystem.h
src/hal/inc/hal_ethernet.h
src/hal/inc/platform_endian.h
src/common/inc/libiec61850_common_api.h
src/common/inc/libiec61850_platform_includes.h
@ -114,42 +110,45 @@ set(API_HEADERS
${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h
)
IF(MSVC)
include_directories(
src/vs
)
ENDIF(MSVC)
if(MSVC)
include_directories(
src/vs
)
endif(MSVC)
# write the detected stuff to this file
configure_file(${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h
)
if(BUILD_EXAMPLES)
add_subdirectory(examples)
add_subdirectory(examples)
endif(BUILD_EXAMPLES)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src)
INSTALL(FILES ${API_HEADERS} DESTINATION include/libiec61850 COMPONENT Development)
install(FILES ${API_HEADERS} DESTINATION include/libiec61850 COMPONENT Development)
IF(BUILD_PYTHON_BINDINGS)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/pyiec61850)
ENDIF(BUILD_PYTHON_BINDINGS)
if(BUILD_PYTHON_BINDINGS)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/pyiec61850)
endif(BUILD_PYTHON_BINDINGS)
IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
INCLUDE(InstallRequiredSystemLibraries)
SET(CPACK_PACKAGE_DESCRIPTION "IEC 61850 MMS/GOOSE client and server library")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IEC 61850 MMS/GOOSE client and server library")
SET(CPACK_PACKAGE_VENDOR "MZ Automation GmbH")
SET(CPACK_PACKAGE_CONTACT "info@libiec61850.com")
SET(CPACK_PACKAGE_VERSION_MAJOR "${LIB_VERSION_MAJOR}")
SET(CPACK_PACKAGE_VERSION_MINOR "${LIB_VERSION_MINOR}")
SET(CPACK_PACKAGE_VERSION_PATCH "${LIB_VERSION_PATCH}")
SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}")
SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
SET(CPACK_COMPONENTS_ALL Libraries Development Applications)
#set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
INCLUDE(CPack)
ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_DESCRIPTION "IEC 61850 MMS/GOOSE client and server library")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IEC 61850 MMS/GOOSE client and server library")
set(CPACK_PACKAGE_VENDOR "MZ Automation GmbH")
set(CPACK_PACKAGE_CONTACT "info@libiec61850.com")
set(CPACK_PACKAGE_VERSION_MAJOR "${LIB_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${LIB_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${LIB_VERSION_PATCH}")
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
set(CPACK_COMPONENTS_ALL Libraries Development Applications)
#set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
include(CPack)
endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

View file

@ -1,5 +1,7 @@
# README libIEC61850
[![Build Status](https://travis-ci.org/mz-automation/libiec61850.svg?branch=master)](https://travis-ci.org/mz-automation/libiec61850)
This file is part of the documentation of **libIEC61850**. More documentation can be found online at http://libiec61850.com or in the provided doxygen documentation. Also consider to review the examples to understand how to use the library
Content:
@ -73,15 +75,17 @@ To build the library and run libiec61850 applications with GOOSE support on Wind
## Building with the cmake build script
With the help of the cmake build script it is possible to create platform independet project descriptions and let cmake create specific project or build files for other tools like Make or Visual Studio.
With the help of the cmake build script it is possible to create platform independent project descriptions and let cmake create specific project or build files for other tools like Make or Visual Studio.
If you have cmake installed fire up a command line (cmd.exe) and create a new subdirectory in the libiec61850 folder. Change to this subdirectory. Then you can invoke cmake. As an command line argument you have to supply a "generator" that is used by cmake to create the project file for the actual build tool (in our case Visual Studio).
If you have cmake installed fire up a command line (cmd.exe) and create a new subdirectory in the libiec61850 folder. Change to this subdirectory. Then you can invoke cmake. As an command line argument you have to supply a "generator" that is used by cmake to create the project file for the actual build tool (in our case Visual Studio 2015).
`cmake -G "Visual Studio 11" ..`
`cmake -G "Visual Studio 14 2015" ..`
will instruct cmake to create a "solution" for Visual Studio 2012. To do the same thing for Visual Studio 2010 type
will instruct cmake to create a "solution" for Visual Studio 2015. The resulting project files will be 32 bit.
`cmake -G "Visual Studio 10" ..`
To build 64 bit libraries the "Win64" generator option has to be added.
`cmake -G "Visual Studio 14 2015 Win64" ..`
Note: The ".." at the end of the command line tells cmake where to find the main build script file (called CMakeLists.txt). This should point to the folder libiec61850 which is in our case the parent directory (..).

View file

@ -29,9 +29,9 @@
#cmakedefine01 DEBUG_MMS_SERVER
#cmakedefine01 DEBUG_GOOSE_SUBSCRIBER
#cmakedefine01 DEBUG_GOOSE_PUBLISHER
#cmakedefine01 DEBUG_SV_SUBSCRIBER 0
#cmakedefine01 DEBUG_SV_PUBLISHER 0
#cmakedefine01 DEBUG_HAL_ETHERNET 0
#cmakedefine01 DEBUG_SV_SUBSCRIBER
#cmakedefine01 DEBUG_SV_PUBLISHER
#cmakedefine01 DEBUG_HAL_ETHERNET
/* 1 ==> server runs in single threaded mode (one dedicated thread for the server)
* 0 ==> server runs in multi threaded mode (one thread for each connection and

View file

@ -11,35 +11,34 @@ add_subdirectory(server_example_61400_25)
add_subdirectory(server_example_setting_groups)
add_subdirectory(server_example_logging)
add_subdirectory(server_example_files)
add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2)
add_subdirectory(iec61850_client_example3)
add_subdirectory(iec61850_client_example4)
add_subdirectory(iec61850_client_example5)
IF(WIN32)
else()
add_subdirectory(iec61850_client_example_files)
endif()
add_subdirectory(iec61850_client_example_reporting)
add_subdirectory(iec61850_client_example_log)
IF(WIN32)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")
message("Found winpcap -> compile examples for GOOSE and SV")
add_subdirectory(server_example_goose)
add_subdirectory(goose_subscriber)
add_subdirectory(goose_publisher)
add_subdirectory(sv_subscriber)
add_subdirectory(iec61850_9_2_LE_example)
add_subdirectory(iec61850_sv_client_example)
add_subdirectory(sv_publisher)
if(NOT WIN32)
add_subdirectory(iec61850_client_example_files)
endif()
if(WIN32)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")
set(BUILD_SV_GOOSE_EXAMPLES ON)
message("Found winpcap -> compile examples for GOOSE and SV")
endif()
else()
add_subdirectory(server_example_goose)
add_subdirectory(goose_subscriber)
add_subdirectory(goose_publisher)
add_subdirectory(sv_subscriber)
add_subdirectory(iec61850_9_2_LE_example)
add_subdirectory(iec61850_sv_client_example)
add_subdirectory(sv_publisher)
set(BUILD_SV_GOOSE_EXAMPLES ON)
endif()
if(${BUILD_SV_GOOSE_EXAMPLES})
add_subdirectory(server_example_goose)
add_subdirectory(goose_subscriber)
add_subdirectory(goose_publisher)
add_subdirectory(sv_subscriber)
add_subdirectory(iec61850_9_2_LE_example)
add_subdirectory(iec61850_sv_client_example)
add_subdirectory(sv_publisher)
endif()

View file

@ -7,6 +7,7 @@
*/
#include "goose_receiver.h"
#include "goose_subscriber.h"
#include "hal_thread.h"
#include <stdlib.h>

View file

@ -248,6 +248,8 @@ main(int argc, char** argv)
case FileOperationType_Set:
setFile(con);
break;
case FileOperationType_None:
break;
}

View file

@ -81,7 +81,7 @@ printJournalEntries(LinkedList journalEntries)
}
}
void*
void
printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool received)
{
if (received)

View file

@ -1,23 +1,40 @@
FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})
FIND_PACKAGE(PythonLibs REQUIRED)
FIND_PACKAGE ( PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED )
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
SET(CMAKE_SWIG_FLAGS "")
SET_PROPERTY(SOURCE iec61850.i PROPERTY CPLUSPLUS ON)
SWIG_ADD_MODULE(iec61850 python iec61850.i)
IF(WIN32)
SWIG_LINK_LIBRARIES(iec61850 ${PYTHON_LIBRARIES} iec61850 ws2_32)
ELSE()
SWIG_LINK_LIBRARIES(iec61850 ${PYTHON_LIBRARIES} iec61850-shared)
ENDIF(WIN32)
EXECUTE_PROCESS ( #Finding python modules install path
COMMAND ${PYTHON_EXECUTABLE} -c
"import site, sys; sys.stdout.write(site.getsitepackages()[-1])"
OUTPUT_VARIABLE PYTHON_SITE_DIR
)
INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR})
INSTALL ( TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR})
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
find_package(PythonLibs REQUIRED)
find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED)
include_directories(${PYTHON_INCLUDE_PATH})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_SWIG_FLAGS "")
set_property(SOURCE iec61850.i PROPERTY CPLUSPLUS ON)
if(WIN32)
set(LIBS iec61850 ws2_32)
else()
set(LIBS iec61850-shared)
endif()
if(${SWIG_VERSION} VERSION_LESS 3.0)
swig_add_module(iec61850 python iec61850.i)
else()
swig_add_library(iec61850
LANGUAGE python
SOURCES iec61850.i
)
endif()
swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS})
# Finding python modules install path
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c
"import site, sys; sys.stdout.write(site.getsitepackages()[-1])"
OUTPUT_VARIABLE PYTHON_SITE_DIR
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR})
install(TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR})
add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py)

View file

@ -811,3 +811,10 @@ GooseReceiver_tick(GooseReceiver self)
else
return false;
}
void
GooseReceiver_addHandleSet(GooseReceiver self, EthernetHandleSet handles)
{
return EthernetHandleSet_addSocket(handles, self->ethSocket);
}

View file

@ -24,18 +24,19 @@
#ifndef GOOSE_RECEIVER_H_
#define GOOSE_RECEIVER_H_
#include <goose_subscriber.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
/**
* \addtogroup goose_api_group
*/
/**@{*/
typedef struct sGooseSubscriber* GooseSubscriber;
typedef struct sGooseReceiver* GooseReceiver;
@ -133,6 +134,20 @@ GooseReceiver_stopThreadless(GooseReceiver self);
bool
GooseReceiver_tick(GooseReceiver self);
/* Forward declaration */
typedef struct sEthernetHandleSet* EthernetHandleSet;
/**
* \brief Add the receiver to a handleset for multiplexed asynchronous IO.
*
* Note: This function must only be called after GooseReceiver_startThreadless().
*
* \param[in] self The SVReceiver instance.
* \param[inout] handles The EthernetHandleSet to which the EthernetSocket of this receiver should be added.
*/
void
GooseReceiver_addHandleSet(GooseReceiver self, EthernetHandleSet handles);
/**@}*/
#ifdef __cplusplus

View file

@ -22,6 +22,7 @@
*/
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h>
@ -46,12 +47,82 @@ struct sEthernetSocket {
struct bpf_program bpfProgram; // BPF filter machine code program.
};
int _Ethernet_activateBpdFilter(EthernetSocket self)
struct sEthernetHandleSet {
struct pollfd *handles;
int nhandles;
};
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd));
self->handles[i].fd = sock->bpf;
self->handles[i].events = POLLIN;
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
unsigned i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i].fd == sock->bpf) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles >= 0)) {
result = poll(self->handles, self->nhandles, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->nhandles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
int
activateBpdFilter(EthernetSocket self)
{
return ioctl(self->bpf, BIOCSETF, &self->bpfProgram);
}
int _Ethernet_setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
static int
setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
{
if (addr)
{
@ -62,18 +133,19 @@ int _Ethernet_setBpfEthernetAddressFilter(EthernetSocket self, uint8_t *addr)
memcpy((void *)&self->bpfProgram.bf_insns[3].k, &addr[2], 4);
memcpy((void *)&self->bpfProgram.bf_insns[5].k, &addr, 2);
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
else
{
// Disable Ethernet address filter.
self->bpfProgram.bf_insns[0].k = 0;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
}
int _Ethernet_setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
static int
setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
{
if (etherType)
{
@ -83,14 +155,14 @@ int _Ethernet_setBpfEthertypeFilter(EthernetSocket self, uint16_t etherType)
// Set protocol.
self->bpfProgram.bf_insns[9].k = etherType;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
else
{
// Disable Ethertype filter.
self->bpfProgram.bf_insns[6].k = 0;
return _Ethernet_activateBpdFilter(self);
return activateBpdFilter(self);
}
}
@ -283,13 +355,15 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
return self;
}
void Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
void
Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
{
if (!self || !self->bpfProgram.bf_insns || _Ethernet_setBpfEthertypeFilter(self, etherType))
if (!self || !self->bpfProgram.bf_insns || setBpfEthertypeFilter(self, etherType))
printf("Unable to set ethertype filter!\n");
}
int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
int
Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
{
// If the actual buffer is empty, make a read call to the BSP device in order to get new data.
if (self->bpfEnd - self->bpfPositon < 4)
@ -335,13 +409,15 @@ int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
return 0;
}
void Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize)
void
Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize)
{
// Just send the packet as it is.
write(self->bpf, buffer, packetSize);
}
void Ethernet_destroySocket(EthernetSocket self)
void
Ethernet_destroySocket(EthernetSocket self)
{
// Close the BPF device.
close(self->bpf);

View file

@ -23,6 +23,7 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
@ -40,6 +41,75 @@ struct sEthernetSocket {
struct sockaddr_ll socketAddress;
};
struct sEthernetHandleSet {
struct pollfd *handles;
int nhandles;
};
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd));
self->handles[i].fd = sock->rawSocket;
self->handles[i].events = POLLIN;
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
unsigned i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i].fd == sock->rawSocket) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles >= 0)) {
result = poll(self->handles, self->nhandles, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->nhandles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
static int
getInterfaceIndex(int sock, const char* deviceName)
{

View file

@ -47,6 +47,8 @@
#define HAVE_REMOTE
// Enable WinPcap specific extension: pcap_getevent()
#define WPCAP
#include "pcap.h"
struct sEthernetSocket {
@ -54,6 +56,11 @@ struct sEthernetSocket {
struct bpf_program etherTypeFilter;
};
struct sEthernetHandleSet {
HANDLE *handles;
int nhandles;
};
#ifdef __GNUC__ /* detect MINGW */
#ifndef __MINGW64_VERSION_MAJOR
@ -90,7 +97,6 @@ typedef ULONG (WINAPI* pgetadaptersaddresses)(ULONG family, ULONG flags, PVOID r
static pgetadaptersaddresses GetAdaptersAddresses;
static bool dllLoaded = false;
static void
@ -115,6 +121,71 @@ loadDLLs(void)
#endif /* __GNUC__ */
EthernetHandleSet
EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
result->handles = NULL;
result->nhandles = 0;
}
return result;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
int i = self->nhandles++;
self->handles = (HANDLE *) realloc(self->handles, self->nhandles * sizeof(HANDLE));
self->handles[i] = pcap_getevent(sock->rawSocket);
}
}
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
HANDLE h = pcap_getevent(socket->rawSocket);
unsigned i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i] == h) {
memmove(&self->handles[i], &self->handles[i+1], sizeof(HANDLE) * (self->nhandles - i - 1));
self->nhandles--;
return;
}
}
}
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
int result;
if ((self != NULL) && (self->nhandles > 0)) {
result = WaitForMultipleObjects(self->nhandles, self->handles, 0, timeoutMs);
}
else {
result = -1;
}
return result;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
if (self->handles)
free(self->handles);
GLOBAL_FREEMEM(self);
}
static char*
getInterfaceName(int interfaceIndex)
{
@ -215,8 +286,6 @@ getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress)
}
}
void
Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
{
@ -338,6 +407,28 @@ Ethernet_isSupported()
return false;
}
EthernetHandleSet
EthernetHandleSet_new(void)
{
return NULL;
}
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
}
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs)
{
return 0;
}
void
EthernetHandleSet_destroy(EthernetHandleSet self)
{
}
void
Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
{

View file

@ -48,6 +48,58 @@ extern "C" {
*/
typedef struct sEthernetSocket* EthernetSocket;
/** Opaque reference for a set of ethernet socket handles */
typedef struct sEthernetHandleSet* EthernetHandleSet;
/**
* \brief Create a new connection handle set (EthernetHandleSet)
*
* \return new EthernetHandleSet instance
*/
EthernetHandleSet
EthernetHandleSet_new(void);
/**
* \brief add a socket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock);
/**
* \brief remove a socket from an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock);
/**
* \brief wait for a socket to become ready
*
* This function is corresponding to the BSD socket select function.
* The function will return after \p timeoutMs ms if no data is pending.
*
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \return It returns the number of sockets on which data is pending
* or 0 if no data is pending on any of the monitored connections.
* The function shall return -1 if a socket error occures.
*/
int
EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs);
/**
* \brief destroy the EthernetHandleSet instance
*
* \param self the HandleSet instance to destroy
*/
void
EthernetHandleSet_destroy(EthernetHandleSet self);
/**
* \brief Return the MAC address of an Ethernet interface.
*

View file

@ -63,7 +63,7 @@ HandleSet
Handleset_new(void);
/**
* \brief add a soecket to an existing handle set
* \brief add a socket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
@ -71,18 +71,17 @@ Handleset_new(void);
void
Handleset_addSocket(HandleSet self, const Socket sock);
/**
* \brief wait for a socket to become ready
*
* This function is corresponding to the BSD socket select function.
* It returns the number of sockets on which data is pending or 0 if no data is pending
* on any of the monitored connections. The function will return after "timeout" ms if no
* data is pending.
* The function shall return -1 if a socket error occures.
* The function will return after \p timeoutMs ms if no data is pending.
*
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
* \return It returns the number of sockets on which data is pending
* or 0 if no data is pending on any of the monitored connections.
* The function shall return -1 if a socket error occures.
*/
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs);

View file

@ -351,7 +351,6 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters
goto returnError;
}
ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer,
self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize);

View file

@ -449,6 +449,8 @@ CotpConnection_init(CotpConnection* self, Socket socket,
self->options.tSelDst = tsel;
self->payload = payloadBuffer;
CotpConnection_resetPayload(self);
/* default TPDU size is maximum size */
CotpConnection_setTpduSize(self, COTP_MAX_TPDU_SIZE);

View file

@ -35,45 +35,48 @@
void
mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeBuffer,
const char* domainId, const char* listNameId)
const char* domainId, const char* listNameId)
{
MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
ConfirmedServiceRequest_PR_deleteNamedVariableList;
mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
ConfirmedServiceRequest_PR_deleteNamedVariableList;
DeleteNamedVariableListRequest_t* request =
&(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
DeleteNamedVariableListRequest_t* request =
&(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
request->listOfVariableListName = (struct DeleteNamedVariableListRequest__listOfVariableListName*) GLOBAL_CALLOC(1,
sizeof(struct DeleteNamedVariableListRequest__listOfVariableListName));
request->listOfVariableListName = (struct DeleteNamedVariableListRequest__listOfVariableListName*) GLOBAL_CALLOC(1,
sizeof(struct DeleteNamedVariableListRequest__listOfVariableListName));
request->listOfVariableListName->list.count = 1;
request->listOfVariableListName->list.size = 1;
request->listOfVariableListName->list.count = 1;
request->listOfVariableListName->list.size = 1;
request->listOfVariableListName->list.array = (ObjectName_t**) GLOBAL_CALLOC(1, sizeof(ObjectName_t*));
request->listOfVariableListName->list.array[0] = (ObjectName_t*) GLOBAL_CALLOC(1, sizeof(ObjectName_t));
request->listOfVariableListName->list.array = (ObjectName_t**) GLOBAL_CALLOC(1, sizeof(ObjectName_t*));
request->listOfVariableListName->list.array[0] = (ObjectName_t*) GLOBAL_CALLOC(1, sizeof(ObjectName_t));
if (domainId != NULL) {
if (domainId != NULL) {
request->listOfVariableListName->list.array[0]->present = ObjectName_PR_domainspecific;
request->listOfVariableListName->list.array[0]->choice.domainspecific.domainId.size = strlen(domainId);
request->listOfVariableListName->list.array[0]->choice.domainspecific.domainId.buf = (uint8_t*) StringUtils_copyString(domainId);
request->listOfVariableListName->list.array[0]->choice.domainspecific.domainId.buf =
(uint8_t*) StringUtils_copyString(domainId);
request->listOfVariableListName->list.array[0]->choice.domainspecific.itemId.size = strlen(listNameId);
request->listOfVariableListName->list.array[0]->choice.domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(listNameId);
}
else {
request->listOfVariableListName->list.array[0]->present = ObjectName_PR_vmdspecific;
request->listOfVariableListName->list.array[0]->choice.vmdspecific.size = strlen(listNameId);
request->listOfVariableListName->list.array[0]->choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(listNameId);
}
request->listOfVariableListName->list.array[0]->choice.domainspecific.itemId.buf =
(uint8_t*) StringUtils_copyString(listNameId);
}
else {
request->listOfVariableListName->list.array[0]->present = ObjectName_PR_vmdspecific;
request->listOfVariableListName->list.array[0]->choice.vmdspecific.size = strlen(listNameId);
request->listOfVariableListName->list.array[0]->choice.vmdspecific.buf =
(uint8_t*) StringUtils_copyString(listNameId);
}
request->scopeOfDelete = (INTEGER_t*) GLOBAL_CALLOC(1, sizeof(INTEGER_t));
asn_long2INTEGER(request->scopeOfDelete, DeleteNamedVariableListRequest__scopeOfDelete_specific);
request->scopeOfDelete = (INTEGER_t*) GLOBAL_CALLOC(1, sizeof(INTEGER_t));
asn_long2INTEGER(request->scopeOfDelete, DeleteNamedVariableListRequest__scopeOfDelete_specific);
der_encode(&asn_DEF_MmsPdu, mmsPdu,
(asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
(asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
}
void
@ -220,11 +223,25 @@ parseNamedVariableAttributes(GetNamedVariableListAttributesResponse_t* response,
LinkedList attributes = LinkedList_create();
for (i = 0; i < attributesCount; i++) {
char* domainId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]->
char* domainId;
char* itemId;
if (response->listOfVariable.list.array[i]->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) {
domainId = NULL;
itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]->
variableSpecification.choice.name.choice.vmdspecific);
}
else {
domainId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]->
variableSpecification.choice.name.choice.domainspecific.domainId);
char* itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]->
itemId = mmsMsg_createStringFromAsnIdentifier(response->listOfVariable.list.array[i]->
variableSpecification.choice.name.choice.domainspecific.itemId);
}
MmsVariableAccessSpecification* listEntry = MmsVariableAccessSpecification_create(domainId, itemId);

View file

@ -498,8 +498,7 @@ mmsServer_handleDefineNamedVariableListRequest(
char variableListName[65];
if (request->variableListName.choice.aaspecific.size > 64) {
//TODO send reject PDU instead?
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
goto exit_free_struct;
}
@ -543,8 +542,7 @@ mmsServer_handleDefineNamedVariableListRequest(
char variableListName[65];
if (request->variableListName.choice.vmdspecific.size > 64) {
//TODO send reject PDU instead?
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
goto exit_free_struct;
}
@ -583,7 +581,6 @@ mmsServer_handleDefineNamedVariableListRequest(
exit_free_struct:
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
exit_function:
return;
}

View file

@ -59,6 +59,7 @@ struct sSV_ASDU {
uint64_t refrTm;
uint8_t smpMod;
uint16_t smpRate;
uint8_t* smpCntBuf;
@ -220,6 +221,34 @@ encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos)
return bufPos;
}
static int
encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos)
{
uint8_t* valueArray = (uint8_t*) &value;
#if (ORDER_LITTLE_ENDIAN == 1)
buffer[bufPos++] = valueArray[7];
buffer[bufPos++] = valueArray[6];
buffer[bufPos++] = valueArray[5];
buffer[bufPos++] = valueArray[4];
buffer[bufPos++] = valueArray[3];
buffer[bufPos++] = valueArray[2];
buffer[bufPos++] = valueArray[1];
buffer[bufPos++] = valueArray[0];
#else
buffer[bufPos++] = valueArray[0];
buffer[bufPos++] = valueArray[1];
buffer[bufPos++] = valueArray[2];
buffer[bufPos++] = valueArray[3];
buffer[bufPos++] = valueArray[4];
buffer[bufPos++] = valueArray[5];
buffer[bufPos++] = valueArray[6];
buffer[bufPos++] = valueArray[7];
#endif
return bufPos;
}
static int
encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
{
@ -257,13 +286,13 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
SampledValuesPublisher
SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId)
{
SampledValuesPublisher self = (SampledValuesPublisher) GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher));
SampledValuesPublisher self = (SampledValuesPublisher) GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher));
self->asduLIst = NULL;
self->asduLIst = NULL;
preparePacketBuffer(self, parameters, interfaceId);
preparePacketBuffer(self, parameters, interfaceId);
return self;
return self;
}
SV_ASDU
@ -369,7 +398,8 @@ SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos)
buffer[bufPos++] = self->smpSynch;
/* SmpRate */
//TODO implement me
bufPos = BerEncoder_encodeTL(0x86, 2, buffer, bufPos);
bufPos = encodeUInt16FixedSize(self->smpRate, buffer, bufPos);
/* Sample */
bufPos = BerEncoder_encodeTL(0x87, self->dataSize, buffer, bufPos);
@ -377,15 +407,11 @@ SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos)
self->_dataBuffer = buffer + bufPos;
bufPos += self->dataSize; /* data has to inserted by user before sending message */
/* SmpMod */
if (self->hasSmpMod) {
bufPos = BerEncoder_encodeTL(0x84, 4, buffer, bufPos);
buffer[bufPos++] = 0;
buffer[bufPos++] = 0;
buffer[bufPos++] = 0;
buffer[bufPos++] = self->smpMod;
bufPos = BerEncoder_encodeTL(0x88, 4, buffer, bufPos);
bufPos = encodeUInt16FixedSize(self->smpMod, buffer, bufPos);
}
return bufPos;
@ -465,7 +491,7 @@ SampledValuesPublisher_publish(SampledValuesPublisher self)
void
SampledValuesPublisher_destroy(SampledValuesPublisher self)
{
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self->buffer);
}
@ -508,6 +534,22 @@ SV_ASDU_setINT32(SV_ASDU self, int index, int32_t value)
encodeInt32FixedSize(value, self->_dataBuffer, index);
}
int
SV_ASDU_addINT64(SV_ASDU self)
{
int index = self->dataSize;
self->dataSize += 8;
return index;
}
void
SV_ASDU_setINT64(SV_ASDU self, int index, int64_t value)
{
encodeInt64FixedSize(value, self->_dataBuffer, index);
}
int
SV_ASDU_addFLOAT(SV_ASDU self)
{
@ -523,7 +565,6 @@ SV_ASDU_setFLOAT(SV_ASDU self, int index, float value)
{
uint8_t* buf = (uint8_t*) &value;
#if (ORDER_LITTLE_ENDIAN == 1)
BerEncoder_revertByteOrder(buf, 4);
#endif
@ -550,11 +591,15 @@ void
SV_ASDU_setFLOAT64(SV_ASDU self, int index, double value)
{
uint8_t* buf = (uint8_t*) &value;
#if (ORDER_LITTLE_ENDIAN == 1)
BerEncoder_revertByteOrder(buf, 8);
#endif
int i;
uint8_t* buffer = self->_dataBuffer + index;
for (i = 0; i < 8; i++) {
buffer[i] = buf[i];
}
@ -596,3 +641,9 @@ SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod)
self->smpMod = smpMod;
}
void
SV_ASDU_setSmpRate(SV_ASDU self, uint16_t smpRate)
{
self->hasSmpRate = true;
self->smpRate = smpRate;
}

View file

@ -85,6 +85,12 @@ SV_ASDU_addINT32(SV_ASDU self);
void
SV_ASDU_setINT32(SV_ASDU self, int index, int32_t value);
int
SV_ASDU_addINT64(SV_ASDU self);
void
SV_ASDU_setINT64(SV_ASDU self, int index, int64_t value);
int
SV_ASDU_addFLOAT(SV_ASDU self);
@ -121,6 +127,18 @@ SV_ASDU_setRefrTm(SV_ASDU self, uint64_t refrTm);
void
SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod);
/**
* \brief Set the sample rate of the ASDU.
*
* If not set the transmitted ASDU will not contain an smpRate value.
*
* \param self the SV_ASDU
*
* \param smpRate Amount of samples (default per nominal period, see SmpMod).
*/
void
SV_ASDU_setSmpRate(SV_ASDU self, uint16_t smpRate);
#ifdef __cplusplus
}
#endif

View file

@ -50,7 +50,13 @@ struct sSVReceiver {
uint8_t* buffer;
EthernetSocket ethSocket;
LinkedList subscriberList;
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore subscriberListLock;
#endif
};
struct sSVSubscriber {
@ -88,6 +94,10 @@ SVReceiver_create(void)
self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH);
self->checkDestAddr = false;
#if (CONFIG_MMS_THREADLESS_STACK == 0)
self->subscriberListLock = Semaphore_create(1);
#endif
}
return self;
@ -111,13 +121,29 @@ SVReceiver_disableDestAddrCheck(SVReceiver self)
void
SVReceiver_addSubscriber(SVReceiver self, SVSubscriber subscriber)
{
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_wait(self->subscriberListLock);
#endif
LinkedList_add(self->subscriberList, (void*) subscriber);
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_post(self->subscriberListLock);
#endif
}
void
SVReceiver_removeSubscriber(SVReceiver self, SVSubscriber subscriber)
{
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_wait(self->subscriberListLock);
#endif
LinkedList_remove(self->subscriberList, (void*) subscriber);
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_post(self->subscriberListLock);
#endif
}
static void
@ -174,6 +200,10 @@ SVReceiver_destroy(SVReceiver self)
LinkedList_destroyDeep(self->subscriberList,
(LinkedListValueDeleteFunction) SVSubscriber_destroy);
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_destroy(self->subscriberListLock);
#endif
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
@ -199,6 +229,11 @@ SVReceiver_stopThreadless(SVReceiver self)
self->running = false;
}
void
SVReceiver_addHandleSet(SVReceiver self, EthernetHandleSet handles)
{
return EthernetHandleSet_addSocket(handles, self->ethSocket);
}
static void
parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length)
@ -398,6 +433,11 @@ parseSVMessage(SVReceiver self, int numbytes)
/* check if there is a matching subscriber */
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_wait(self->subscriberListLock);
#endif
LinkedList element = LinkedList_getNext(self->subscriberList);
SVSubscriber subscriber;
@ -427,6 +467,10 @@ parseSVMessage(SVReceiver self, int numbytes)
element = LinkedList_getNext(element);
}
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_post(self->subscriberListLock);
#endif
if (subscriberFound)
parseSVPayload(self, subscriber, buffer + bufPos, apduLength);
@ -598,6 +642,20 @@ SVClientASDU_getINT32(SVClientASDU self, int index)
return retVal;
}
int64_t
SVClientASDU_getINT64(SVClientASDU self, int index)
{
int64_t retVal = *((int64_t*) (self->dataBuffer + index));
#if (ORDER_LITTLE_ENDIAN == 1)
uint8_t* buf = (uint8_t*) (&retVal);
BerEncoder_revertByteOrder(buf, 8);
#endif
return retVal;
}
uint8_t
SVClientASDU_getINT8U(SVClientASDU self, int index)
{
@ -634,6 +692,19 @@ SVClientASDU_getINT32U(SVClientASDU self, int index)
return retVal;
}
uint64_t
SVClientASDU_getINT64U(SVClientASDU self, int index)
{
uint64_t retVal = *((uint64_t*) (self->dataBuffer + index));
#if (ORDER_LITTLE_ENDIAN == 1)
uint8_t* buf = (uint8_t*) (&retVal);
BerEncoder_revertByteOrder(buf, 8);
#endif
return retVal;
}
float
SVClientASDU_getFLOAT32(SVClientASDU self, int index)

View file

@ -52,24 +52,25 @@ extern "C" {
*
* | IEC 61850 type | required bytes |
* | -------------- | -------------- |
* | BOOLEAN | 1 byte |
* | INT8 | 1 byte |
* | INT16 | 2 byte |
* | INT32 | 4 byte |
* | INT64 | 8 byte |
* | INT8U | 1 byte |
* | INT16U | 2 byte |
* | INT24U | 3 byte |
* | INT32U | 4 byte |
* | FLOAT32 | 4 byte |
* | FLOAT64 | 8 byte |
* | ENUMERATED | 4 byte |
* | CODED ENUM | 4 byte |
* | OCTET STRING | 20 byte |
* | VISIBLE STRING | 35 byte |
* | TimeStamp | 8 byte |
* | EntryTime | 6 byte |
* | BITSTRING | 4 byte |
* | BOOLEAN | 1 byte |
* | INT8 | 1 byte |
* | INT16 | 2 byte |
* | INT32 | 4 byte |
* | INT64 | 8 byte |
* | INT8U | 1 byte |
* | INT16U | 2 byte |
* | INT24U | 3 byte |
* | INT32U | 4 byte |
* | INT64U | 8 byte |
* | FLOAT32 | 4 byte |
* | FLOAT64 | 8 byte |
* | ENUMERATED | 4 byte |
* | CODED ENUM | 4 byte |
* | OCTET STRING | 20 byte |
* | VISIBLE STRING | 35 byte |
* | TimeStamp | 8 byte |
* | EntryTime | 6 byte |
* | BITSTRING | 4 byte |
*
* The SV subscriber API can be used independent of the IEC 61850 client API. In order to access the SVCB via MMS you
* have to use the IEC 61850 client API. Please see \ref ClientSVControlBlock object in section \ref IEC61850_CLIENT_SV.
@ -209,9 +210,32 @@ SVReceiver_startThreadless(SVReceiver self);
void
SVReceiver_stopThreadless(SVReceiver self);
/**
* \brief Parse SV messages if they are available.
*
* Call after reception of ethernet frame and periodically to to house keeping tasks
*
* \param self the receiver object
*
* \return true if a message was available and has been parsed, false otherwise
*/
bool
SVReceiver_tick(SVReceiver self);
/* Forward declaration */
typedef struct sEthernetHandleSet* EthernetHandleSet;
/**
* \brief Add the receiver to a handleset for multiplexed asynchronous IO.
*
* Note: This function must only be called after SVReceiver_startThreadless().
*
* \param[in] self The SVReceiver instance.
* \param[inout] handles The EthernetHandleSet to which the EthernetSocket of this receiver should be added.
*/
void
SVReceiver_addHandleSet(SVReceiver self, EthernetHandleSet handles);
/*
* Subscriber
*/
@ -321,6 +345,17 @@ SVClientASDU_getINT16(SVClientASDU self, int index);
int32_t
SVClientASDU_getINT32(SVClientASDU self, int index);
/**
* \brief Get an INT64 data value in the data part of the ASDU
*
* \param self ASDU object instance
* \param index the index (byte position of the start) of the data in the data part
*
* \return SV data
*/
int64_t
SVClientASDU_getINT64(SVClientASDU self, int index);
/**
* \brief Get an INT8U data value in the data part of the ASDU
*
@ -354,6 +389,17 @@ SVClientASDU_getINT16U(SVClientASDU self, int index);
uint32_t
SVClientASDU_getINT32U(SVClientASDU self, int index);
/**
* \brief Get an INT64U data value in the data part of the ASDU
*
* \param self ASDU object instance
* \param index the index (byte position of the start) of the data in the data part
*
* \return SV data
*/
uint64_t
SVClientASDU_getINT64U(SVClientASDU self, int index);
/**
* \brief Get an FLOAT32 data value in the data part of the ASDU
*