Compare commits

...
Sign in to create a new pull request.

62 commits

Author SHA1 Message Date
Michael Zillgith
bfbae01a2d
Merge branch 'development' into pkgconfig 2017-11-21 22:37:39 +01:00
Michael Zillgith
5fbe455587
Merge branch 'development' into pkgconfig 2017-11-21 22:36:06 +01:00
Michael Zillgith
b673e1d28a
Merge pull request #36 from stv0g/ethernet-handleset
EthernetHandleSet
2017-11-21 22:32:19 +01:00
Michael Zillgith
eabba7e484
Merge pull request #35 from stv0g/sv-harmonization-documentation
Sampled Values API harmonization and documentation
2017-11-21 22:28:48 +01:00
a1654eef70 smaller fixes 2017-11-19 20:16:41 +01:00
0eaeb8c573 rephrased changelog entry 2017-11-19 20:14:43 +01:00
819bb67d6d fix examples with new API 2017-11-19 20:03:55 +01:00
73cdb5a0f7 install "sv_subscriber_deprecated.h" 2017-11-19 20:03:28 +01:00
d3ff0f25e5 add ABI compatability wrapper 2017-11-19 20:03:13 +01:00
12cf073f81 refactor "SVClientASDU" to "SVSubscriber_ASDU" 2017-11-19 20:02:53 +01:00
b4b69bf56d refactor "SV_ASDU" to "SVPublisher_ASDU" 2017-11-19 20:02:22 +01:00
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
5b29a95da5 install sv_publisher_deprecated.h 2017-11-19 14:02:46 +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
f4fea90ce3 fix install() 2017-11-19 13:58: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
8c1d9b1cbe
added entry to changeling 2017-11-19 12:44:58 +01:00
f5e21a41bf generate pkg-config file only on Unixes (Linux, BSD, Apple) 2017-11-19 12:39:48 +01:00
90f20bce43 generate and install pkg-config file 2017-11-19 12:37:13 +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
6d36d05ad6 add note to changelog about API change 2017-11-18 13:39:00 +01:00
31249fdce3 updated Doxygen configuration from 1.7 to 1.8 2017-11-18 13:14:58 +01:00
62e04cf0aa add Doxygen target to CMake 2017-11-18 13:05:34 +01:00
63cd4f6f00 added documentation for Sampled Values publisher 2017-11-18 12:53:06 +01:00
c4a8d90fda fix syntax error in Doxygen config 2017-11-18 12:52:34 +01:00
eb3be133f6 updated sampled values examples to use new naming 2017-11-18 11:46:49 +01:00
f1163e78db added deprecated wrappers for ABI compatability 2017-11-18 11:46:24 +01:00
4c5e1717d1 refactored "SampledValuesPublisher" into "SVPublisher" (closes #34) 2017-11-18 11:45:52 +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
34 changed files with 3946 additions and 1525 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

@ -1,3 +1,9 @@
Changes to version 1.2.0
------------------------
- Added pkg-config file
- The Sampled Values APIs have been renamed. The old version of the API is deprecated but still supported and will be removed in the next major version of the library.
Changes to version 1.1.0
------------------------

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)
@ -13,6 +13,7 @@ ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1")
set(LIB_VERSION_MINOR "1")
set(LIB_VERSION_PATCH "0")
set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
@ -32,20 +33,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 +59,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
@ -110,46 +107,51 @@ set(API_HEADERS
src/goose/goose_publisher.h
src/sampled_values/sv_subscriber.h
src/sampled_values/sv_publisher.h
src/sampled_values/sv_publisher_deprecated.h
src/sampled_values/sv_subscriber_deprecated.h
src/logging/logging_api.h
${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_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)
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

@ -53,27 +53,27 @@ static int vol2;
static int vol3;
static int vol4;
static SampledValuesPublisher svPublisher;
static SV_ASDU asdu;
static SVPublisher svPublisher;
static SVPublisher_ASDU asdu;
static void
setupSVPublisher(const char* svInterface)
{
svPublisher = SampledValuesPublisher_create(NULL, svInterface);
svPublisher = SVPublisher_create(NULL, svInterface);
asdu = SampledValuesPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1);
asdu = SVPublisher_addASDU(svPublisher, "xxxxMUnn01", NULL, 1);
amp1 = SV_ASDU_addINT32(asdu);
amp2 = SV_ASDU_addINT32(asdu);
amp3 = SV_ASDU_addINT32(asdu);
amp4 = SV_ASDU_addINT32(asdu);
amp1 = SVPublisher_ASDU_addINT32(asdu);
amp2 = SVPublisher_ASDU_addINT32(asdu);
amp3 = SVPublisher_ASDU_addINT32(asdu);
amp4 = SVPublisher_ASDU_addINT32(asdu);
vol1 = SV_ASDU_addINT32(asdu);
vol2 = SV_ASDU_addINT32(asdu);
vol3 = SV_ASDU_addINT32(asdu);
vol4 = SV_ASDU_addINT32(asdu);
vol1 = SVPublisher_ASDU_addINT32(asdu);
vol2 = SVPublisher_ASDU_addINT32(asdu);
vol3 = SVPublisher_ASDU_addINT32(asdu);
vol4 = SVPublisher_ASDU_addINT32(asdu);
SampledValuesPublisher_setupComplete(svPublisher);
SVPublisher_setupComplete(svPublisher);
}
static void sVCBEventHandler (SVControlBlock* svcb, int event, void* parameter)
@ -143,19 +143,19 @@ main(int argc, char** argv)
if (svcbEnabled) {
SV_ASDU_setINT32(asdu, amp1, current);
SV_ASDU_setINT32(asdu, amp2, current);
SV_ASDU_setINT32(asdu, amp3, current);
SV_ASDU_setINT32(asdu, amp4, current);
SVPublisher_ASDU_setINT32(asdu, amp1, current);
SVPublisher_ASDU_setINT32(asdu, amp2, current);
SVPublisher_ASDU_setINT32(asdu, amp3, current);
SVPublisher_ASDU_setINT32(asdu, amp4, current);
SV_ASDU_setINT32(asdu, vol1, voltage);
SV_ASDU_setINT32(asdu, vol2, voltage);
SV_ASDU_setINT32(asdu, vol3, voltage);
SV_ASDU_setINT32(asdu, vol4, voltage);
SVPublisher_ASDU_setINT32(asdu, vol1, voltage);
SVPublisher_ASDU_setINT32(asdu, vol2, voltage);
SVPublisher_ASDU_setINT32(asdu, vol3, voltage);
SVPublisher_ASDU_setINT32(asdu, vol4, voltage);
SV_ASDU_increaseSmpCnt(asdu);
SVPublisher_ASDU_increaseSmpCnt(asdu);
SampledValuesPublisher_publish(svPublisher);
SVPublisher_publish(svPublisher);
}
voltage++;
@ -168,7 +168,7 @@ main(int argc, char** argv)
IedServer_stop(iedServer);
/* Cleanup - free all resources */
SampledValuesPublisher_destroy(svPublisher);
SVPublisher_destroy(svPublisher);
IedServer_destroy(iedServer);
return 0;

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

@ -29,19 +29,19 @@ main(int argc, char** argv)
printf("Using interface %s\n", interface);
SampledValuesPublisher svPublisher = SampledValuesPublisher_create(NULL, interface);
SVPublisher svPublisher = SVPublisher_create(NULL, interface);
SV_ASDU asdu1 = SampledValuesPublisher_addASDU(svPublisher, "svpub1", NULL, 1);
SVPublisher_ASDU asdu1 = SVPublisher_addASDU(svPublisher, "svpub1", NULL, 1);
int float1 = SV_ASDU_addFLOAT(asdu1);
int float2 = SV_ASDU_addFLOAT(asdu1);
int float1 = SVPublisher_ASDU_addFLOAT(asdu1);
int float2 = SVPublisher_ASDU_addFLOAT(asdu1);
SV_ASDU asdu2 = SampledValuesPublisher_addASDU(svPublisher, "svpub2", NULL, 1);
SVPublisher_ASDU asdu2 = SVPublisher_addASDU(svPublisher, "svpub2", NULL, 1);
int float3 = SV_ASDU_addFLOAT(asdu2);
int float4 = SV_ASDU_addFLOAT(asdu2);
int float3 = SVPublisher_ASDU_addFLOAT(asdu2);
int float4 = SVPublisher_ASDU_addFLOAT(asdu2);
SampledValuesPublisher_setupComplete(svPublisher);
SVPublisher_setupComplete(svPublisher);
float fVal1 = 1234.5678f;
float fVal2 = 0.12345f;
@ -49,19 +49,19 @@ main(int argc, char** argv)
int i;
while (running) {
SV_ASDU_setFLOAT(asdu1, float1, fVal1);
SV_ASDU_setFLOAT(asdu1, float2, fVal2);
SVPublisher_ASDU_setFLOAT(asdu1, float1, fVal1);
SVPublisher_ASDU_setFLOAT(asdu1, float2, fVal2);
SV_ASDU_increaseSmpCnt(asdu1);
SV_ASDU_increaseSmpCnt(asdu2);
SVPublisher_ASDU_increaseSmpCnt(asdu1);
SVPublisher_ASDU_increaseSmpCnt(asdu2);
fVal1 += 1.1f;
fVal2 += 0.1f;
SampledValuesPublisher_publish(svPublisher);
SVPublisher_publish(svPublisher);
Thread_sleep(50);
}
SampledValuesPublisher_destroy(svPublisher);
SVPublisher_destroy(svPublisher);
}

View file

@ -21,17 +21,17 @@ void sigint_handler(int signalId)
/* Callback handler for received SV messages */
static void
svUpdateListener (SVSubscriber subscriber, void* parameter, SVClientASDU asdu)
svUpdateListener (SVSubscriber subscriber, void* parameter, SVSubscriber_ASDU asdu)
{
printf("svUpdateListener called\n");
const char* svID = SVClientASDU_getSvId(asdu);
const char* svID = SVSubscriber_ASDU_getSvId(asdu);
if (svID != NULL)
printf(" svID=(%s)\n", svID);
printf(" smpCnt: %i\n", SVClientASDU_getSmpCnt(asdu));
printf(" confRev: %u\n", SVClientASDU_getConfRev(asdu));
printf(" smpCnt: %i\n", SVSubscriber_ASDU_getSmpCnt(asdu));
printf(" confRev: %u\n", SVSubscriber_ASDU_getConfRev(asdu));
/*
* Access to the data requires a priori knowledge of the data set.
@ -43,9 +43,9 @@ svUpdateListener (SVSubscriber subscriber, void* parameter, SVClientASDU asdu)
* To prevent damages due configuration, please check the length of the
* data block of the SV message before accessing the data.
*/
if (SVClientASDU_getDataSize(asdu) >= 8) {
printf(" DATA[0]: %f\n", SVClientASDU_getFLOAT32(asdu, 0));
printf(" DATA[1]: %f\n", SVClientASDU_getFLOAT32(asdu, 4));
if (SVSubscriber_ASDU_getDataSize(asdu) >= 8) {
printf(" DATA[0]: %f\n", SVSubscriber_ASDU_getFLOAT32(asdu, 0));
printf(" DATA[1]: %f\n", SVSubscriber_ASDU_getFLOAT32(asdu, 4));
}
}

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

@ -367,10 +367,22 @@ if(MSVC)
endif()
ENDIF(WITH_WPCAP)
if(UNIX)
configure_file(
${CMAKE_CURRENT_LIST_DIR}/libiec61850.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc @ONLY
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig")
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif(DOXYGEN_FOUND)
install (TARGETS iec61850 iec61850-shared
RUNTIME DESTINATION bin COMPONENT Applications
ARCHIVE DESTINATION lib COMPONENT Libraries
LIBRARY DESTINATION lib COMPONENT Libraries
)

2450
src/Doxyfile.in Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

13
src/libiec61850.pc.in Normal file
View file

@ -0,0 +1,13 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@/bin
libdir=@CMAKE_INSTALL_PREFIX@/lib
sharedlibdir=@CMAKE_INSTALL_PREFIX@/lib
includedir=@CMAKE_INSTALL_PREFIX@/include
Name: @PROJECT_NAME@
Description: @CPACK_PACKAGE_DESCRIPTION@
Version: @LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@
Requires:
Libs: -L${libdir} -L${sharedlibdir} -liec61850
Cflags: -I${includedir}

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

@ -42,7 +42,7 @@
#define SV_MAX_MESSAGE_SIZE 1518
struct sSV_ASDU {
struct sSVPublisher_ASDU {
char* svID;
char* datset;
int dataSize;
@ -59,15 +59,16 @@ struct sSV_ASDU {
uint64_t refrTm;
uint8_t smpMod;
uint16_t smpRate;
uint8_t* smpCntBuf;
SV_ASDU _next;
SVPublisher_ASDU _next;
};
struct sSampledValuesPublisher {
struct sSVPublisher {
uint8_t* buffer;
uint16_t appId;
EthernetSocket ethernetSocket;
@ -78,14 +79,14 @@ struct sSampledValuesPublisher {
int payloadLength; /* length of payload buffer */
int asduCount; /* number of ASDUs in the APDU */
SV_ASDU asduLIst;
SVPublisher_ASDU asduLIst;
};
static void
preparePacketBuffer(SampledValuesPublisher self, CommParameters* parameters, const char* interfaceID)
preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceID)
{
uint8_t srcAddr[6];
@ -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)
{
@ -254,22 +283,22 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
return bufPos + 8;
}
SampledValuesPublisher
SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId)
SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId)
{
SampledValuesPublisher self = (SampledValuesPublisher) GLOBAL_CALLOC(1, sizeof(struct sSampledValuesPublisher));
SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher));
self->asduLIst = NULL;
self->asduLIst = NULL;
preparePacketBuffer(self, parameters, interfaceId);
preparePacketBuffer(self, parameters, interfaceId);
return self;
return self;
}
SV_ASDU
SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* datset, uint32_t confRev)
SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev)
{
SV_ASDU newAsdu = (SV_ASDU) GLOBAL_CALLOC(1, sizeof(struct sSV_ASDU));
SVPublisher_ASDU newAsdu = (SVPublisher_ASDU) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU));
newAsdu->svID = svID;
newAsdu->datset = datset;
@ -281,7 +310,7 @@ SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* da
if (self->asduLIst == NULL)
self->asduLIst = newAsdu;
else {
SV_ASDU lastAsdu = self->asduLIst;
SVPublisher_ASDU lastAsdu = self->asduLIst;
while (lastAsdu->_next != NULL)
lastAsdu = lastAsdu->_next;
@ -293,7 +322,7 @@ SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* da
}
static int
SV_ASDU_getEncodedSize(SV_ASDU self)
SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self)
{
int encodedSize = 0;
@ -333,9 +362,9 @@ SV_ASDU_getEncodedSize(SV_ASDU self)
}
static int
SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos)
SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufPos)
{
int encodedSize = SV_ASDU_getEncodedSize(self);
int encodedSize = SVPublisher_ASDU_getEncodedSize(self);
/* tag and length field */
bufPos = BerEncoder_encodeTL(0x30, encodedSize, buffer, bufPos);
@ -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,32 +407,28 @@ 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;
}
void
SampledValuesPublisher_setupComplete(SampledValuesPublisher self)
SVPublisher_setupComplete(SVPublisher self)
{
int numberOfAsdu = 0;
/* determine number of ASDUs and length of all ASDUs */
SV_ASDU nextAsdu = self->asduLIst;
SVPublisher_ASDU nextAsdu = self->asduLIst;
int totalASDULength = 0;
while (nextAsdu != NULL) {
numberOfAsdu++;
int asduLength = SV_ASDU_getEncodedSize(nextAsdu);
int asduLength = SVPublisher_ASDU_getEncodedSize(nextAsdu);
/* tag and length field */
asduLength += BerEncoder_determineLengthSize(asduLength);
@ -431,7 +457,7 @@ SampledValuesPublisher_setupComplete(SampledValuesPublisher self)
nextAsdu = self->asduLIst;
while (nextAsdu != NULL) {
bufPos = SV_ASDU_encodeToBuffer(nextAsdu, buffer, bufPos);
bufPos = SVPublisher_ASDU_encodeToBuffer(nextAsdu, buffer, bufPos);
nextAsdu = nextAsdu->_next;
}
@ -452,7 +478,7 @@ SampledValuesPublisher_setupComplete(SampledValuesPublisher self)
void
SampledValuesPublisher_publish(SampledValuesPublisher self)
SVPublisher_publish(SVPublisher self)
{
if (DEBUG_SV_PUBLISHER)
printf("SV_PUBLISHER: send SV message\n");
@ -463,21 +489,21 @@ SampledValuesPublisher_publish(SampledValuesPublisher self)
void
SampledValuesPublisher_destroy(SampledValuesPublisher self)
SVPublisher_destroy(SVPublisher self)
{
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self->buffer);
}
void
SV_ASDU_resetBuffer(SV_ASDU self)
SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self)
{
self->dataSize = 0;
}
int
SV_ASDU_addINT8(SV_ASDU self)
SVPublisher_ASDU_addINT8(SVPublisher_ASDU self)
{
int index = self->dataSize;
@ -487,13 +513,13 @@ SV_ASDU_addINT8(SV_ASDU self)
}
void
SV_ASDU_setINT8(SV_ASDU self, int index, int8_t value)
SVPublisher_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value)
{
self->_dataBuffer[index] = value;
}
int
SV_ASDU_addINT32(SV_ASDU self)
SVPublisher_ASDU_addINT32(SVPublisher_ASDU self)
{
int index = self->dataSize;
@ -503,13 +529,29 @@ SV_ASDU_addINT32(SV_ASDU self)
}
void
SV_ASDU_setINT32(SV_ASDU self, int index, int32_t value)
SVPublisher_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value)
{
encodeInt32FixedSize(value, self->_dataBuffer, index);
}
int
SV_ASDU_addFLOAT(SV_ASDU self)
SVPublisher_ASDU_addINT64(SVPublisher_ASDU self)
{
int index = self->dataSize;
self->dataSize += 8;
return index;
}
void
SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value)
{
encodeInt64FixedSize(value, self->_dataBuffer, index);
}
int
SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self)
{
int index = self->dataSize;
@ -519,11 +561,10 @@ SV_ASDU_addFLOAT(SV_ASDU self)
}
void
SV_ASDU_setFLOAT(SV_ASDU self, int index, float value)
SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value)
{
uint8_t* buf = (uint8_t*) &value;
#if (ORDER_LITTLE_ENDIAN == 1)
BerEncoder_revertByteOrder(buf, 4);
#endif
@ -539,7 +580,7 @@ SV_ASDU_setFLOAT(SV_ASDU self, int index, float value)
int
SV_ASDU_addFLOAT64(SV_ASDU self)
SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self)
{
int index = self->dataSize;
self->dataSize += 8;
@ -547,27 +588,31 @@ SV_ASDU_addFLOAT64(SV_ASDU self)
}
void
SV_ASDU_setFLOAT64(SV_ASDU self, int index, double value)
SVPublisher_ASDU_setFLOAT64(SVPublisher_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];
}
}
uint16_t
SV_ASDU_getSmpCnt(SV_ASDU self)
SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self)
{
return self->smpCnt;
}
void
SV_ASDU_setSmpCnt(SV_ASDU self, uint16_t value)
SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value)
{
self->smpCnt = value;
@ -575,7 +620,7 @@ SV_ASDU_setSmpCnt(SV_ASDU self, uint16_t value)
}
void
SV_ASDU_increaseSmpCnt(SV_ASDU self)
SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self)
{
self->smpCnt++;
@ -583,16 +628,22 @@ SV_ASDU_increaseSmpCnt(SV_ASDU self)
}
void
SV_ASDU_setRefrTm(SV_ASDU self, uint64_t refrTm)
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm)
{
self->hasRefrTm = true;
self->refrTm = refrTm;
}
void
SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod)
SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod)
{
self->hasSmpMod = true;
self->smpMod = smpMod;
}
void
SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate)
{
self->hasSmpRate = true;
self->smpRate = smpRate;
}

View file

@ -43,6 +43,11 @@ typedef struct sCommParameters {
#endif
/**
* \defgroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API
*/
/**@{*/
#define IEC61850_SV_SMPSYNC_NOT_SYNCHRONIZED 0
#define IEC61850_SV_SMPSYNC_SYNCED_UNSPEC_LOCAL_CLOCK 1
#define IEC61850_SV_SMPSYNC_SYNCED_GLOBAL_CLOCK 2
@ -51,79 +56,241 @@ typedef struct sCommParameters {
#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1
#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2
typedef struct sSampledValuesPublisher* SampledValuesPublisher;
typedef struct sSV_ASDU* SV_ASDU;
SampledValuesPublisher
SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId);
SV_ASDU
SampledValuesPublisher_addASDU(SampledValuesPublisher self, char* svID, char* datset, uint32_t confRev);
void
SampledValuesPublisher_setupComplete(SampledValuesPublisher self);
void
SampledValuesPublisher_publish(SampledValuesPublisher self);
void
SampledValuesPublisher_destroy(SampledValuesPublisher self);
void
SV_ASDU_resetBuffer(SV_ASDU self);
int
SV_ASDU_addINT8(SV_ASDU self);
void
SV_ASDU_setINT8(SV_ASDU self, int index, int8_t value);
int
SV_ASDU_addINT32(SV_ASDU self);
void
SV_ASDU_setINT32(SV_ASDU self, int index, int32_t value);
int
SV_ASDU_addFLOAT(SV_ASDU self);
void
SV_ASDU_setFLOAT(SV_ASDU self, int index, float value);
int
SV_ASDU_addFLOAT64(SV_ASDU self);
void
SV_ASDU_setFLOAT64(SV_ASDU self, int index, double value);
void
SV_ASDU_setSmpCnt(SV_ASDU self, uint16_t value);
uint16_t
SV_ASDU_getSmpCnt(SV_ASDU self);
void
SV_ASDU_increaseSmpCnt(SV_ASDU self);
void
SV_ASDU_setRefrTm(SV_ASDU self, uint64_t refrTm);
/**
* \brief An opaque type representing an IEC 61850-9-2 Sampled Values publisher.
*/
typedef struct sSVPublisher* SVPublisher;
/**
* \brief Set the sample mode of the ASDU
* \brief An opaque type representing an IEC 61850-9-2 Sampled Values Application Service Data Unit (ASDU).
*/
typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
/**
* \brief Create a new IEC61850-9-2 Sampled Values publisher.
*
* If not set the transmitted ASDU will not contain an sampleMod value
* \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets.
* \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
* \return the new SV publisher instance.
*/
SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId);
/**
* \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher.
*
* \param self the SV_ASDU
* \param[in] svID
* \param[in] datset
* \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the logical device changes.
* \return the new ASDU instance.
*/
SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev);
/**
* \brief Prepare the publisher for publishing.
*
* This method must be called before SVPublisher_publish().
*
* \param[in] self the Sampled Values publisher instance.
*/
void
SVPublisher_setupComplete(SVPublisher self);
/**
* \brief Publish all registered ASDUs
*
* \param[in] self the Sampled Values publisher instance.
*/
void
SVPublisher_publish(SVPublisher self);
/**
* \brief Destroy an IEC61850-9-2 Sampled Values instance.
*
* \param[in] self the Sampled Values publisher instance.
*/
void
SVPublisher_destroy(SVPublisher self);
/**
* \addtogroup sv_publisher_asdu_group Values Application Service Data Unit (ASDU)
* @{
*/
/**
* \brief Reset the internal data buffer of an ASDU.
*
* All data elements added by SVPublisher_ASDU_add*() functions are removed.
* SVPublisher_setupComplete() must be called afterwards.
*
* \param[in] self the Sampled Values ASDU instance.
*/
void
SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self);
/**
* \brief Reserve memory for a signed 8-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \return the offset in bytes within the ASDU data block.
*/
int
SVPublisher_ASDU_addINT8(SVPublisher_ASDU self);
/**
* \brief Set the value of a 8-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] index The offset within the data block of the ASDU in bytes.
* \param[in] value The value which should be set.
*/
void
SVPublisher_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value);
/**
* \brief Reserve memory for a signed 32-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \return the offset in bytes within the ASDU data block.
*/
int
SVPublisher_ASDU_addINT32(SVPublisher_ASDU self);
/**
* \brief Set the value of a 32-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] index The offset within the data block of the ASDU in bytes.
* \param[in] value The value which should be set.
*/
void
SVPublisher_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value);
/**
* \brief Reserve memory for a signed 64-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \return the offset in bytes of the new element within the ASDU data block.
*/
int
SVPublisher_ASDU_addINT64(SVPublisher_ASDU self);
/**
* \brief Set the value of a 64-bit integer in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] index The offset within the data block of the ASDU in bytes.
* \param[in] value The value which should be set.
*/
void
SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value);
/**
* \brief Reserve memory for a single precission floating point number in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \return the offset in bytes of the new element within the ASDU data block.
*/
int
SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self);
/**
* \brief Set the value of a single precission floating point number in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] index The offset within the data block of the ASDU in bytes.
* \param[in] value The value which should be set.
*/
void
SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value);
/**
* \brief Reserve memory for a double precission floating point number in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \return the offset in bytes of the new element within the ASDU data block.
*/
int
SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self);
/**
* \brief Set the value of a double precission floating pointer number in the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] index The offset within the data block of the ASDU in bytes.
* \param[in] value The value which should be set.
*/
void
SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value);
/**
* \brief Set the sample count attribute of the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] value the new value of the attribute.
*/
void
SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value);
/**
* \brief Get the sample count attribute of the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
*/
uint16_t
SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self);
/**
* \brief Increment the sample count attribute of the ASDU.
*
* The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the analogue value is taken.
* The sample values shall be kept in the right order.
* If the counter is used to indicate time consistency of various sampled values, the counter shall be reset by an external synchronization event.
*
* \param[in] self the Sampled Values ASDU instance.
*/
void
SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self);
/**
* \brief Set the refresh time attribute of the ASDU.
*
* \param[in] self the Sampled Values ASDU instance.
*/
void
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm);
/**
* \brief Set the sample mode attribute of the ASDU.
*
* The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per second or seconds per sample.
* If it is missing, the default value is samples per period.
*
* \param[in] self the Sampled Values ASDU instance.
* \param smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE
*/
void
SV_ASDU_setSmpMod(SV_ASDU self, uint8_t smpMod);
SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod);
/**
* \brief Set the sample rate attribute of the ASDU.
*
* The attribute SmpRate shall specify the sample rate.
* The value shall be interpreted depending on the value of the SmpMod attribute.
*
* \param[in] self the Sampled Values ASDU instance.
* \param smpRate Amount of samples (default per nominal period, see SmpMod).
*/
void
SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate);
/**@} @}*/
#ifdef __cplusplus
}
#endif
#include "sv_publisher_deprecated.h"
#endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ */

View file

@ -0,0 +1,211 @@
/*
* sv_publisher.h
*
* Copyright 2016 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_
#define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_
#include "libiec61850_platform_includes.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#else
#define DEPRECATED
#endif
/**
* \addtogroup sv_publisher_deprecated_api_group Deprecated API
* \ingroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API
* \deprecated
* @{
*/
typedef DEPRECATED struct sSVPublisher* SampledValuesPublisher;
typedef DEPRECATED struct sSV_ASDU* SV_ASDU;
static DEPRECATED
SVPublisher
SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId)
{
return SVPublisher_create(parameters, interfaceId);
}
static DEPRECATED
SVPublisher_ASDU
SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev)
{
return SVPublisher_addASDU(self, svID, datset, confRev);
}
static DEPRECATED
void
SampledValuesPublisher_setupComplete(SVPublisher self)
{
SVPublisher_setupComplete(self);
}
static DEPRECATED
void
SampledValuesPublisher_publish(SVPublisher self)
{
SVPublisher_publish(self);
}
static DEPRECATED
void
SampledValuesPublisher_destroy(SVPublisher self)
{
SVPublisher_destroy(self);
}
static DEPRECATED
void
SV_ASDU_resetBuffer(SVPublisher_ASDU self)
{
SVPublisher_ASDU_resetBuffer(self);
}
static DEPRECATED
int
SV_ASDU_addINT8(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_addINT8(self);
}
static DEPRECATED
void
SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value)
{
SVPublisher_ASDU_setINT8(self, index, value);
}
static DEPRECATED
int
SV_ASDU_addINT32(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_addINT32(self);
}
static DEPRECATED
void
SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value)
{
SVPublisher_ASDU_setINT32(self, index, value);
}
static DEPRECATED
int
SV_ASDU_addINT64(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_addINT64(self);
}
static DEPRECATED
void
SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value)
{
SVPublisher_ASDU_setINT64(self, index, value);
}
static DEPRECATED
int
SV_ASDU_addFLOAT(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_addFLOAT(self);
}
static DEPRECATED
void
SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value)
{
SVPublisher_ASDU_setFLOAT(self, index, value);
}
static DEPRECATED
int
SV_ASDU_addFLOAT64(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_addFLOAT64(self);
}
static DEPRECATED
void
SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value)
{
SVPublisher_ASDU_setFLOAT64(self, index, value);
}
static DEPRECATED
void
SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value)
{
SVPublisher_ASDU_setSmpCnt(self, value);
}
static DEPRECATED
uint16_t
SV_ASDU_getSmpCnt(SVPublisher_ASDU self)
{
return SVPublisher_ASDU_getSmpCnt(self);
}
static DEPRECATED
void
SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self)
{
SVPublisher_ASDU_increaseSmpCnt(self);
}
static DEPRECATED
void
SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm)
{
SVPublisher_ASDU_setRefrTm(self, refrTm);
}
static DEPRECATED
void
SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod)
{
SVPublisher_ASDU_setSmpMod(self, smpMod);
}
static DEPRECATED
void
SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate)
{
SVPublisher_ASDU_setSmpRate(self, smpRate);
}
/**@}*/
#ifdef __cplusplus
}
#endif
#endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_DEPRECATED_H_ */

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 {
@ -61,7 +67,7 @@ struct sSVSubscriber {
void* listenerParameter;
};
struct sSVClientASDU {
struct sSVSubscriber_ASDU {
char* svId;
@ -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,14 +229,19 @@ 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)
{
int bufPos = 0;
struct sSVClientASDU asdu;
memset(&asdu, 0, sizeof(struct sSVClientASDU));
struct sSVSubscriber_ASDU asdu;
memset(&asdu, 0, sizeof(struct sSVSubscriber_ASDU));
int svIdLength = 0;
@ -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);
@ -480,7 +524,7 @@ SVSubscriber_setListener(SVSubscriber self, SVUpdateListener listener, void* pa
}
uint16_t
SVClientASDU_getSmpCnt(SVClientASDU self)
SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self)
{
uint16_t retVal;
uint8_t* valBytes = (uint8_t*) &retVal;
@ -525,7 +569,7 @@ decodeUtcTime(uint8_t* buffer, uint8_t* timeQuality)
}
uint64_t
SVClientASDU_getRefrTmAsMs(SVClientASDU self)
SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self)
{
uint64_t msTime = 0;
@ -536,20 +580,20 @@ SVClientASDU_getRefrTmAsMs(SVClientASDU self)
}
bool
SVClientASDU_hasRefrTm(SVClientASDU self)
SVSubscriber_ASDU_hasRefrTm(SVSubscriber_ASDU self)
{
return (self->refrTm != NULL);
}
const char*
SVClientASDU_getSvId(SVClientASDU self)
SVSubscriber_ASDU_getSvId(SVSubscriber_ASDU self)
{
return self->svId;
}
uint32_t
SVClientASDU_getConfRev(SVClientASDU self)
SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU self)
{
uint32_t retVal = *((uint32_t*) (self->confRev));
@ -563,7 +607,7 @@ SVClientASDU_getConfRev(SVClientASDU self)
}
int8_t
SVClientASDU_getINT8(SVClientASDU self, int index)
SVSubscriber_ASDU_getINT8(SVSubscriber_ASDU self, int index)
{
int8_t retVal = *((int8_t*) (self->dataBuffer + index));
@ -571,7 +615,7 @@ SVClientASDU_getINT8(SVClientASDU self, int index)
}
int16_t
SVClientASDU_getINT16(SVClientASDU self, int index)
SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index)
{
int16_t retVal = *((int16_t*) (self->dataBuffer + index));
@ -585,7 +629,7 @@ SVClientASDU_getINT16(SVClientASDU self, int index)
}
int32_t
SVClientASDU_getINT32(SVClientASDU self, int index)
SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU self, int index)
{
int32_t retVal = *((int32_t*) (self->dataBuffer + index));
@ -598,8 +642,22 @@ SVClientASDU_getINT32(SVClientASDU self, int index)
return retVal;
}
int64_t
SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU 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)
SVSubscriber_ASDU_getINT8U(SVSubscriber_ASDU self, int index)
{
uint8_t retVal = *((uint8_t*) (self->dataBuffer + index));
@ -607,7 +665,7 @@ SVClientASDU_getINT8U(SVClientASDU self, int index)
}
uint16_t
SVClientASDU_getINT16U(SVClientASDU self, int index)
SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index)
{
uint16_t retVal = *((uint16_t*) (self->dataBuffer + index));
@ -621,7 +679,7 @@ SVClientASDU_getINT16U(SVClientASDU self, int index)
}
uint32_t
SVClientASDU_getINT32U(SVClientASDU self, int index)
SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU self, int index)
{
uint32_t retVal = *((uint32_t*) (self->dataBuffer + index));
@ -634,9 +692,22 @@ SVClientASDU_getINT32U(SVClientASDU self, int index)
return retVal;
}
uint64_t
SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU 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)
SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index)
{
float retVal = *((float*) (self->dataBuffer + index));
@ -650,7 +721,7 @@ SVClientASDU_getFLOAT32(SVClientASDU self, int index)
}
double
SVClientASDU_getFLOAT64(SVClientASDU self, int index)
SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index)
{
double retVal = *((double*) (self->dataBuffer + index));
@ -665,7 +736,7 @@ SVClientASDU_getFLOAT64(SVClientASDU self, int index)
int
SVClientASDU_getDataSize(SVClientASDU self)
SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self)
{
return self->dataBufferLength;
}

View file

@ -31,7 +31,7 @@ extern "C" {
#endif
/**
* \defgroup sv_subscriber_api_group IEC 61850 sampled values (SV) subscriber API
* \defgroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) subscriber API
*
* The sampled values (SV) subscriber API consists of three different objects.
* The \ref SVReceiver object is responsible for handling all SV Ethernet messages
@ -40,36 +40,37 @@ extern "C" {
* An \ref SVSubscriber object is associated to a SV data stream that is identified by its appID
* and destination Ethernet address. The \reg SVSubscriber object is used to install a callback
* handler \ref SVUpdateListener that is invoked for each ASDU (application service data unit) received for the
* associated stream. An \ref SVClientASDU is an object that represents a single ASDU. Each ASDU contains
* associated stream. An \ref SVSubscriber_ASDU is an object that represents a single ASDU. Each ASDU contains
* some meta information that can be obtained by specific access functions like e.g.
* \ref SVClientASDU_getSmpCnt to access the "SmpCnt" (sample count) attribute of the ASDU. The actual
* \ref SVSubscriber_ASDU_getSmpCnt to access the "SmpCnt" (sample count) attribute of the ASDU. The actual
* measurement data contained in the ASDU does not consist of structured ASN.1 data but stored as raw binary
* data. Without a priori knowledge of the dataset associated with the ASDU data stream it is not
* possible to interpret the received data correctly. Therefore you have to provide the data access functions
* with an index value to indicate the data type and the start of the data in the data block of the ASDU.
* E.g. reading a data set consisting of two FLOAT32 values you can use two subsequent calls of
* \ref SVClientASDU_getFLOAT32 one with index = 0 and the second one with index = 4.
* \ref SVSubscriber_ASDU_getFLOAT32 one with index = 0 and the second one with index = 4.
*
* | 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.
@ -85,18 +86,18 @@ extern "C" {
* sampled value data. Each ASDU represents a single sample consisting of multiple measurement
* values with a single dedicated timestamp.
*
* NOTE: SVClientASDU are statically allocated and are only valid inside of the SVUpdateListener
* NOTE: SVSubscriber_ASDU are statically allocated and are only valid inside of the SVUpdateListener
* function when called by the library. If you need the data contained in the ASDU elsewhere
* you have to copy and store the data by yourself!
*/
typedef struct sSVClientASDU* SVClientASDU;
typedef struct sSVSubscriber_ASDU* SVSubscriber_ASDU;
/**
* \brief opaque handle to a SV subscriber instance
*
* A subscriber is an instance associated with a single stream of measurement data. It is identified
* by the Ethernet destination address, the appID value (both are on SV message level) and the svID value
* that is part of each ASDU (SVClientASDU object).
* that is part of each ASDU (SVSubscriber_ASDU object).
*/
typedef struct sSVSubscriber* SVSubscriber;
@ -109,7 +110,7 @@ typedef struct sSVSubscriber* SVSubscriber;
* \param parameter a user provided parameter that is simply passed to the callback
* \param asdu SV ASDU data structure. This structure is only valid inside of the callback function
*/
typedef void (*SVUpdateListener)(SVSubscriber subscriber, void* parameter, SVClientASDU asdu);
typedef void (*SVUpdateListener)(SVSubscriber subscriber, void* parameter, SVSubscriber_ASDU asdu);
/**
* \brief opaque handle to a SV receiver instance
@ -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
*/
@ -238,9 +262,14 @@ void
SVSubscriber_destroy(SVSubscriber self);
/*************************************************************************
* SVClientASDU object methods
* SVSubscriber_ASDU object methods
**************************************************************************/
/**
* \addtogroup sv_subscriber_asdu_group Values Application Service Data Unit (ASDU)
* @{
*/
/**
* \brief return the SmpCnt value included in the SV ASDU
*
@ -250,7 +279,7 @@ SVSubscriber_destroy(SVSubscriber self);
* \param self ASDU object instance
*/
uint16_t
SVClientASDU_getSmpCnt(SVClientASDU self);
SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self);
/**
* \brief return the SvID value included in the SV ASDU
@ -258,7 +287,7 @@ SVClientASDU_getSmpCnt(SVClientASDU self);
* \param self ASDU object instance
*/
const char*
SVClientASDU_getSvId(SVClientASDU self);
SVSubscriber_ASDU_getSvId(SVSubscriber_ASDU self);
/**
* \brief return the ConfRev value included in the SV ASDU
@ -266,7 +295,7 @@ SVClientASDU_getSvId(SVClientASDU self);
* \param self ASDU object instance
*/
uint32_t
SVClientASDU_getConfRev(SVClientASDU self);
SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU self);
/**
* \brief Check if RefrTm value is included in the SV ASDU
@ -276,7 +305,7 @@ SVClientASDU_getConfRev(SVClientASDU self);
* \return true if RefrTm value is present, false otherwise
*/
bool
SVClientASDU_hasRefrTm(SVClientASDU self);
SVSubscriber_ASDU_hasRefrTm(SVSubscriber_ASDU self);
/**
* \brief Get the RefrTim value included in SV ASDU as ms timestamp
@ -286,7 +315,7 @@ SVClientASDU_hasRefrTm(SVClientASDU self);
* \return the time value as ms timestamp or 0 if RefrTm is not present in the SV ASDU
*/
uint64_t
SVClientASDU_getRefrTmAsMs(SVClientASDU self);
SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self);
/**
* \brief Get an INT8 data value in the data part of the ASDU
@ -297,7 +326,7 @@ SVClientASDU_getRefrTmAsMs(SVClientASDU self);
* \return SV data
*/
int8_t
SVClientASDU_getINT8(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT8(SVSubscriber_ASDU self, int index);
/**
* \brief Get an INT16 data value in the data part of the ASDU
@ -308,7 +337,7 @@ SVClientASDU_getINT8(SVClientASDU self, int index);
* \return SV data
*/
int16_t
SVClientASDU_getINT16(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index);
/**
* \brief Get an INT32 data value in the data part of the ASDU
@ -319,7 +348,18 @@ SVClientASDU_getINT16(SVClientASDU self, int index);
* \return SV data
*/
int32_t
SVClientASDU_getINT32(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU 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
SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU self, int index);
/**
* \brief Get an INT8U data value in the data part of the ASDU
@ -330,7 +370,7 @@ SVClientASDU_getINT32(SVClientASDU self, int index);
* \return SV data
*/
uint8_t
SVClientASDU_getINT8U(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT8U(SVSubscriber_ASDU self, int index);
/**
* \brief Get an INT16U data value in the data part of the ASDU
@ -341,7 +381,7 @@ SVClientASDU_getINT8U(SVClientASDU self, int index);
* \return SV data
*/
uint16_t
SVClientASDU_getINT16U(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index);
/**
* \brief Get an INT32U data value in the data part of the ASDU
@ -352,7 +392,18 @@ SVClientASDU_getINT16U(SVClientASDU self, int index);
* \return SV data
*/
uint32_t
SVClientASDU_getINT32U(SVClientASDU self, int index);
SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU 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
SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU self, int index);
/**
* \brief Get an FLOAT32 data value in the data part of the ASDU
@ -363,7 +414,7 @@ SVClientASDU_getINT32U(SVClientASDU self, int index);
* \return SV data
*/
float
SVClientASDU_getFLOAT32(SVClientASDU self, int index);
SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index);
/**
* \brief Get an FLOAT64 data value in the data part of the ASDU
@ -374,7 +425,7 @@ SVClientASDU_getFLOAT32(SVClientASDU self, int index);
* \return SV data
*/
double
SVClientASDU_getFLOAT64(SVClientASDU self, int index);
SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index);
/**
* \brief Returns the size of the data part of the ASDU
@ -384,13 +435,14 @@ SVClientASDU_getFLOAT64(SVClientASDU self, int index);
* \return size of the ASDU data part in bytes.
*/
int
SVClientASDU_getDataSize(SVClientASDU self);
SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self);
/**@}*/
/**@} @}*/
#ifdef __cplusplus
}
#endif
#include "sv_subscriber_deprecated.h"
#endif /* SAMPLED_VALUES_SV_SUBSCRIBER_ */

View file

@ -0,0 +1,167 @@
/*
* sv_subscriber_deprecated.h
*
* Copyright 2015 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_
#define SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_
#include "libiec61850_common_api.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#else
#define DEPRECATED
#endif
/**
* \addtogroup sv_subscriber_deprecated_api_group Deprecated API
* \ingroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) publisher API
* \deprecated
* @{
*/
typedef struct sSVSubscriberASDU* SVClientASDU;
static DEPRECATED
uint16_t
SVClientASDU_getSmpCnt(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_getSmpCnt(self);
}
static DEPRECATED
const char*
SVClientASDU_getSvId(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_getSvId(self);
}
static DEPRECATED
uint32_t
SVClientASDU_getConfRev(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_getConfRev(self);
}
static DEPRECATED
bool
SVClientASDU_hasRefrTm(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_hasRefrTm(self);
}
static DEPRECATED
uint64_t
SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_getRefrTmAsMs(self);
}
static DEPRECATED
int8_t
SVClientASDU_getINT8(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT8(self, index);
}
static DEPRECATED
int16_t
SVClientASDU_getINT16(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT16(self, index);
}
static DEPRECATED
int32_t
SVClientASDU_getINT32(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT32(self, index);
}
static DEPRECATED
int64_t
SVClientASDU_getINT64(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT64(self, index);
}
static DEPRECATED
uint8_t
SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT8U(self, index);
}
static DEPRECATED
uint16_t
SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT16U(self, index);
}
static DEPRECATED
uint32_t
SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT32U(self, index);
}
static DEPRECATED
uint64_t
SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getINT64U(self, index);
}
static DEPRECATED
float
SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getFLOAT32(self, index);
}
static DEPRECATED
double
SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index)
{
return SVSubscriber_ASDU_getFLOAT64(self, index);
}
static DEPRECATED
int
SVClientASDU_getDataSize(SVSubscriber_ASDU self)
{
return SVSubscriber_ASDU_getDataSize(self);
}
/**@}*/
#ifdef __cplusplus
}
#endif
#endif /* SAMPLED_VALUES_SV_SUBSCRIBER_DEPRECATED_H_ */