pull/182/head
Michael Zillgith 6 years ago
commit eaaaa2b0a5

@ -11,6 +11,17 @@ Changes to version 1.4.0
- common: MmsVariableSpecification_getChildValue now also accepts "." as separator
- .NET API: ReportControlBlock.GetOwner returns null when no owner available (#79)
- IEC 61850 client: IedConnection - added CONNECTING AND CLOSING states - removed IDLE state (CLOSED, CONNECTING, CONNECTED, CLOSING)
- now using mbedtls 2.16
- TLS renegotiation disabled by default
- fixed bug in BerInteger_setUint16
- IEC 61850 client: Added functions IedConnection_setRequestTimeout and IedConnection_getRequestTimeout to C API and IedConnection.RequestTimeout property to .NET API
- MMS client: fixed problem with obtain file timeout with large files
- IEC 61850 server: Control model callback signature changed. Added ControlAction object to access control related parameters in control callbacks
- SV subscriber: improved error handling when Ethernet access doesn't work; fixed potential memory leak
- GOOSE publisher: integrated error handling when Ethernet interface is not available
- GOOSE receiver: add support for operation without Ethernet HAL implementation
- MMS server: fixed memory access problem when client unexpectedly closed connection during file upload (set-file)
- static model generator: Initialize Dbpos value from Val element in ICD/CID file (see github #163)
Changes to version 1.3.3
------------------------

@ -117,9 +117,9 @@ if(MSVC AND MSVC_VERSION LESS 1800)
)
endif(MSVC AND MSVC_VERSION LESS 1800)
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0)
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
set(WITH_MBEDTLS 1)
endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0)
endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
if(WITH_MBEDTLS)

@ -71,9 +71,9 @@ LIB_INCLUDE_DIRS += third_party/winpcap/Include
endif
ifdef WITH_MBEDTLS
LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.6.0/library
LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.16/library
LIB_SOURCE_DIRS += hal/tls/mbedtls
LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.6.0/include
LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.16/include
LIB_INCLUDE_DIRS += hal/tls/mbedtls
CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"'
CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1'

@ -2,12 +2,19 @@
[![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
This file is part of the documentation of **libIEC61850**. More documentation can be found online at http://libiec61850.com.
The API documentation can be found here:
* C API: https://support.mz-automation.de/doc/libiec61850/c/latest/
* .NET API: https://support.mz-automation.de/doc/libiec61850/net/latest/
Also consider to review the examples to understand how to use the library.
Content:
* [Overview](#overview)
* [Features](#features)
* [Examples](#examples)
* [Building and running the examples](#building-and-running-the-examples-with-the-provided-makefiles)
* [Building the library with TLS support](#building-the-library-with-tls-support)
* [Installing the library and the API headers](#installing-the-library-and-the-api-headers)
@ -52,6 +59,12 @@ The library support the following IEC 61850 protocol features:
* C and C#/.NET API
## Examples
The examples are built automatically when CMake is used to build the library.
NOTE: Most examples are intended to show a specific function of the library. They are designed to show this function as simple as possible and may miss error handling that has to be present in real applications!
## Building and running the examples with the provided makefiles
In the project root directory type
@ -75,7 +88,9 @@ You can test the server examples by using a generic client or the provided clien
## Building the library with TLS support
Download, unpack, and copy mbedtls-2.6.0 into the third_party/mbedtls folder.
Download, unpack, and copy mbedtls-2.16 into the third_party/mbedtls folder.
NOTE: The current version support mbedtls version 2.16. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.16".
In the main libiec61850 folder run
@ -83,6 +98,8 @@ In the main libiec61850 folder run
make WITH_MBEDTLS=1
```
When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.16 is present.
## Installing the library and the API headers
The make and cmake build scripts provide an install target. This target copies the API header files and the static library to a single directory for the headers (INSTALL_PREFIX/include) and the static library (INSTALL_PREFIX/lib). With this feature it is more easy to integrate libiec61850 in an external application since you only have to add a simple include directory to the build tool of your choice.
@ -151,6 +168,8 @@ The experimental Python binding can be created using SWIG with cmake.
To enable the bindings you have to select the phyton configuration option with ccmake of cmake-gui.
We don't provide any support for the Python bindings!
## Commercial licenses and support
Support and commercial license options are provided by MZ Automation GmbH. Please contact info@mz-automation.de for more details.
@ -159,6 +178,7 @@ Support and commercial license options are provided by MZ Automation GmbH. Pleas
If you want to contribute to the improvement and development of the library please send me comments, feature requests, bug reports, or patches. For more than trivial contributions I require you to sign a Contributor License Agreement. Please contact info@libiec61850.com.
Please don't send pull requests before signing the Contributor License Agreement! Such pull requests may be silently ignored.
## Third-party contributions

@ -425,6 +425,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setConnectTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setRequestTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getRequestTimeout(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
@ -769,6 +775,22 @@ namespace IEC61850
}
}
/// <summary>
/// Gets or sets the request timeout for this connection
/// </summary>
/// <value>The request timeout in milliseconds</value>
public UInt32 RequestTimeout
{
get
{
return IedConnection_getRequestTimeout(connection);
}
set
{
IedConnection_setRequestTimeout(connection, value);
}
}
/// <summary>
/// Gets or sets the maximum size if a PDU (has to be set before calling connect!).
/// </summary>

@ -517,8 +517,11 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ControlAction_getOrIdent (IntPtr self, ref int size);
[DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ControlAction_getClientConnection (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ControlAction_getClientConnection(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int ControlAction_getCtlNum(IntPtr self);
private IntPtr self;
private IedServer.ControlHandlerInfo info;
@ -569,6 +572,15 @@ namespace IEC61850
return orIdent;
}
/// <summary>
/// Gets the ctlNum attribute of the control action
/// </summary>
/// <returns>The ctlNum value. Valid values are restricted from 0 to 255, -1 means not present</returns>
public int GetCtlNum()
{
return ControlAction_getCtlNum(self);
}
/// <summary>
/// Gets the control object that is subject to this action
/// </summary>

@ -59,20 +59,28 @@ namespace IEC61850
[MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] value;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[StructLayout(LayoutKind.Sequential)]
private struct NativePSelector
{
public byte size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] value;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter);
@ -130,7 +138,7 @@ namespace IEC61850
/// <param name='tSelector'>
/// ISO COTP transport layer address
/// </param>
public void SetRemoteAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector)
public void SetRemoteAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@ -152,7 +160,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; i++)
nativeSSelector.value [i] = sSelector [i];
IsoConnectionParameters_setRemoteAddresses(self, pSelector, nativeSSelector, nativeTSelector);
if (pSelector.Length > 16)
throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded");
NativePSelector nativePSelector;
nativePSelector.size = (byte)pSelector.Length;
nativePSelector.value = new byte[16];
for (int i = 0; i < pSelector.Length; i++)
nativePSelector.value[i] = pSelector[i];
IsoConnectionParameters_setRemoteAddresses(self, nativePSelector, nativeSSelector, nativeTSelector);
}
/// <summary>
@ -181,7 +199,7 @@ namespace IEC61850
/// <param name='tSelector'>
/// ISO COTP transport layer address
/// </param>
public void SetLocalAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector)
public void SetLocalAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@ -203,7 +221,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; i++)
nativeSSelector.value [i] = sSelector [i];
IsoConnectionParameters_setLocalAddresses(self, pSelector, nativeSSelector, nativeTSelector);
if (pSelector.Length > 16)
throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded");
NativePSelector nativePSelector;
nativePSelector.size = (byte)pSelector.Length;
nativePSelector.value = new byte[16];
for (int i = 0; i < pSelector.Length; i++)
nativePSelector.value[i] = pSelector[i];
IsoConnectionParameters_setLocalAddresses(self, nativePSelector, nativeSSelector, nativeTSelector);
}
/// <summary>

@ -24,7 +24,7 @@ namespace example3
{
IsoConnectionParameters parameters = con.GetConnectionParameters();
parameters.SetRemoteAddresses(1, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03});
parameters.SetRemoteAddresses(new byte[] { 0x00, 0x01 }, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03});
con.ConnectTimeout = 10000;

@ -24,7 +24,10 @@ add_subdirectory(iec61850_client_example_array)
add_subdirectory(iec61850_client_example_files)
add_subdirectory(iec61850_client_example_async)
add_subdirectory(iec61850_client_file_async)
add_subdirectory(mms_utility)
if (NOT WIN32)
add_subdirectory(mms_utility)
endif(NOT WIN32)
if(WIN32)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")

@ -51,21 +51,26 @@ main(int argc, char** argv)
*/
GoosePublisher publisher = GoosePublisher_create(&gooseCommParameters, interface);
GoosePublisher_setGoCbRef(publisher, "simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
GoosePublisher_setConfRev(publisher, 1);
GoosePublisher_setDataSetRef(publisher, "simpleIOGenericIO/LLN0$AnalogValues");
if (publisher) {
GoosePublisher_setGoCbRef(publisher, "simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
GoosePublisher_setConfRev(publisher, 1);
GoosePublisher_setDataSetRef(publisher, "simpleIOGenericIO/LLN0$AnalogValues");
int i = 0;
int i = 0;
for (i = 0; i < 3; i++) {
Thread_sleep(1000);
for (i = 0; i < 3; i++) {
Thread_sleep(1000);
if (GoosePublisher_publish(publisher, dataSetValues) == -1) {
printf("Error sending message!\n");
}
}
if (GoosePublisher_publish(publisher, dataSetValues) == -1) {
printf("Error sending message!\n");
}
}
GoosePublisher_destroy(publisher);
GoosePublisher_destroy(publisher);
}
else {
printf("Failed to create GOOSE publisher. Reason can be that the Ethernet interface doesn't exist or root permission are required.\n");
}
LinkedList_destroyDeep(dataSetValues, (LinkedListValueDeleteFunction) MmsValue_delete);
}

@ -66,10 +66,15 @@ main(int argc, char** argv)
GooseReceiver_start(receiver);
signal(SIGINT, sigint_handler);
if (GooseReceiver_isRunning(receiver)) {
signal(SIGINT, sigint_handler);
while (running) {
Thread_sleep(100);
while (running) {
Thread_sleep(100);
}
}
else {
printf("Failed to start GOOSE subscriber. Reason can be that the Ethernet interface doesn't exist or root permission are required.\n");
}
GooseReceiver_stop(receiver);

@ -30,17 +30,17 @@ print_help()
}
static void
mmsFileDirectoryHandler (void* parameter, char* filename, uint32_t size, uint64_t lastModified)
mmsFileDirectoryHandler(void* parameter, char* filename, uint32_t size, uint64_t lastModified)
{
char* lastName = (char*) parameter;
strcpy (lastName, filename);
strcpy(lastName, filename);
printf("%s\n", filename);
}
static void
mmsGetFileAttributeHandler (void* parameter, char* filename, uint32_t size, uint64_t lastModified)
mmsGetFileAttributeHandler(void* parameter, char* filename, uint32_t size, uint64_t lastModified)
{
char gtString[30];
Conversions_msTimeToGeneralizedTime(lastModified, (uint8_t*) gtString);
@ -99,142 +99,144 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re
printf("\n");
}
int main(int argc, char** argv) {
char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102;
int maxPduSize = 65000;
int arrayIndex = -1;
char* domainName = NULL;
char* variableName = NULL;
char* componentName = NULL;
char* filename = NULL;
char* journalName = NULL;
int readDeviceList = 0;
int getDeviceDirectory = 0;
int identifyDevice = 0;
int readWriteHasDomain = 0;
int readVariable = 0;
int showFileList = 0;
int getFileAttributes = 0;
int readJournal = 0;
int printRawMmsMessages = 0;
int deleteFile = 0;
int readVariableList = 0;
int c;
while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:")) != -1)
switch (c) {
case 'm':
printRawMmsMessages = 1;
break;
case 'h':
hostname = StringUtils_copyString(optarg);
break;
case 'p':
tcpPort = atoi(optarg);
break;
case 'l':
maxPduSize = atoi(optarg);
break;
case 'd':
readDeviceList = 1;
break;
case 'i':
identifyDevice = 1;
break;
case 't':
getDeviceDirectory = 1;
domainName = StringUtils_copyString(optarg);
break;
case 'a':
readWriteHasDomain = 1;
domainName = StringUtils_copyString(optarg);
break;
case 'r':
readVariable = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'c':
componentName = StringUtils_copyString(optarg);
break;
case 'v':
readVariableList = 1;
variableName = StringUtils_copyString(optarg);
int main(int argc, char** argv)
{
char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102;
int maxPduSize = 65000;
int arrayIndex = -1;
char* domainName = NULL;
char* variableName = NULL;
char* componentName = NULL;
char* filename = NULL;
char* journalName = NULL;
int readDeviceList = 0;
int getDeviceDirectory = 0;
int identifyDevice = 0;
int readWriteHasDomain = 0;
int readVariable = 0;
int showFileList = 0;
int getFileAttributes = 0;
int readJournal = 0;
int printRawMmsMessages = 0;
int deleteFile = 0;
int readVariableList = 0;
int c;
while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:")) != -1)
switch (c) {
case 'm':
printRawMmsMessages = 1;
break;
case 'h':
free(hostname);
hostname = StringUtils_copyString(optarg);
break;
case 'p':
tcpPort = atoi(optarg);
break;
case 'l':
maxPduSize = atoi(optarg);
break;
case 'd':
readDeviceList = 1;
break;
case 'i':
identifyDevice = 1;
break;
case 't':
getDeviceDirectory = 1;
domainName = StringUtils_copyString(optarg);
break;
case 'a':
readWriteHasDomain = 1;
domainName = StringUtils_copyString(optarg);
break;
case 'r':
readVariable = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'c':
componentName = StringUtils_copyString(optarg);
break;
case 'v':
readVariableList = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'f':
showFileList = 1;
break;
case 'g':
getFileAttributes = 1;
filename = StringUtils_copyString(optarg);
break;
case 'x':
deleteFile = 1;
filename = StringUtils_copyString(optarg);
break;
case 'j':
readJournal = 1;
journalName = StringUtils_copyString(optarg);
break;
case 'f':
showFileList = 1;
break;
case 'g':
getFileAttributes = 1;
filename = StringUtils_copyString(optarg);
break;
case 'x':
deleteFile = 1;
filename = StringUtils_copyString(optarg);
break;
case 'j':
readJournal = 1;
journalName = StringUtils_copyString(optarg);
break;
case 'y':
arrayIndex = atoi(optarg);
break;
default:
print_help();
return 0;
}
MmsConnection con = MmsConnection_create();
MmsError error;
/* Set maximum MMS PDU size (local detail) to 2000 byte */
MmsConnection_setLocalDetail(con, maxPduSize);
if (printRawMmsMessages)
MmsConnection_setRawMessageHandler(con, (MmsRawMessageHandler) printRawMmsMessage, NULL);
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n");
goto exit;
}
else
printf("MMS connected.\n");
if (identifyDevice) {
MmsServerIdentity* identity =
MmsConnection_identify(con, &error);
if (identity != NULL) {
printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName);
printf(" model:\t%s\n", identity->modelName);
printf(" revision:\t%s\n", identity->revision);
}
else
printf("Reading server identity failed!\n");
}
if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error);
LinkedList_printStringList(nameList);
LinkedList_destroy(nameList);
}
if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName);
if (variableList) {
case 'y':
arrayIndex = atoi(optarg);
break;
default:
print_help();
return 0;
}
MmsConnection con = MmsConnection_create();
MmsError error;
/* Set maximum MMS PDU size (local detail) to 2000 byte */
MmsConnection_setLocalDetail(con, maxPduSize);
if (printRawMmsMessages)
MmsConnection_setRawMessageHandler(con, (MmsRawMessageHandler) printRawMmsMessage, NULL);
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n");
goto exit;
}
else
printf("MMS connected.\n");
if (identifyDevice) {
MmsServerIdentity* identity =
MmsConnection_identify(con, &error);
if (identity != NULL) {
printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName);
printf(" model:\t%s\n", identity->modelName);
printf(" revision:\t%s\n", identity->revision);
}
else
printf("Reading server identity failed!\n");
}
if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error);
LinkedList_printStringList(nameList);
LinkedList_destroy(nameList);
}
if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName);
if (variableList) {
LinkedList element = LinkedList_getNext(variableList);
printf("\nMMS domain variables for domain %s\n", domainName);
@ -248,14 +250,14 @@ int main(int argc, char** argv) {
}
LinkedList_destroy(variableList);
}
else {
printf("\nFailed to read domain directory (error=%d)\n", error);
}
}
else {
printf("\nFailed to read domain directory (error=%d)\n", error);
}
variableList = MmsConnection_getDomainJournals(con, &error, domainName);
variableList = MmsConnection_getDomainJournals(con, &error, domainName);
if (variableList) {
if (variableList) {
LinkedList element = variableList;
@ -268,21 +270,21 @@ int main(int argc, char** argv) {
}
LinkedList_destroy(variableList);
}
}
else {
printf("\nFailed to read domain journals (error=%d)\n", error);
}
}
}
if (readJournal) {
if (readJournal) {
printf(" read journal %s...\n", journalName);
printf(" read journal %s...\n", journalName);
char* logDomain = journalName;
char* logName = strchr(journalName, '/');
char* logDomain = journalName;
char* logName = strchr(journalName, '/');
if (logName != NULL) {
if (logName != NULL) {
logName[0] = 0;
logName++;
@ -334,44 +336,43 @@ int main(int argc, char** argv) {
readNext = true;
}
} while ((moreFollows == true) || (readNext == true));
}
while ((moreFollows == true) || (readNext == true));
}
}
else
printf(" Invalid log name!\n");
}
if (readVariable) {
if (readWriteHasDomain) {
MmsValue* result;
if (componentName == NULL) {
if (arrayIndex == -1) {
result = MmsConnection_readVariable(con, &error, domainName, variableName);
}
else {
result = MmsConnection_readSingleArrayElementWithComponent(con, &error, domainName, variableName, arrayIndex, NULL);
}
}
else {
if (arrayIndex == -1) {
result = MmsConnection_readVariableComponent(con, &error, domainName, variableName, componentName);
}
else {
result = MmsConnection_readSingleArrayElementWithComponent(con, &error, domainName, variableName, arrayIndex, componentName);
}
}
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
}
}
else
printf(" Invalid log name!\n");
}
if (readVariable) {
if (readWriteHasDomain) {
MmsValue* result;
if (componentName == NULL) {
if (arrayIndex == -1) {
result = MmsConnection_readVariable(con, &error, domainName, variableName);
}
else {
result = MmsConnection_readSingleArrayElementWithComponent(con, &error, domainName, variableName, arrayIndex, NULL);
}
}
else {
if (arrayIndex == -1) {
result = MmsConnection_readVariableComponent(con, &error, domainName, variableName, componentName);
}
else {
result = MmsConnection_readSingleArrayElementWithComponent(con, &error, domainName, variableName, arrayIndex, componentName);
}
}
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
}
else {
printf("Read SUCCESS\n");
if (result != NULL) {
else {
printf("Read SUCCESS\n");
if (result != NULL) {
char outbuf[1024];
MmsValue_printToBuffer(result, outbuf, 1024);
@ -379,17 +380,17 @@ int main(int argc, char** argv) {
printf("%s\n", outbuf);
MmsValue_delete(result);
}
else
printf("result: NULL\n");
}
}
else
printf("result: NULL\n");
}
}
else
printf("Reading VMD scope variable not yet supported!\n");
}
}
else
printf("Reading VMD scope variable not yet supported!\n");
}
if (readVariableList) {
if (readVariableList) {
if (readWriteHasDomain) {
MmsValue* variables = MmsConnection_readNamedVariableListValues(con, &error, domainName, variableName, true);
@ -402,25 +403,25 @@ int main(int argc, char** argv) {
}
else
printf("Reading VMD scope variable list not yet supported!\n");
}
}
if (showFileList) {
char lastName[300];
lastName[0] = 0;
if (showFileList) {
char lastName[300];
lastName[0] = 0;
char* continueAfter = NULL;
char* continueAfter = NULL;
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
continueAfter = lastName;
}
}
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
continueAfter = lastName;
}
}
if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
}
if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
}
if (deleteFile) {
MmsConnection_fileDelete(con, &error, filename);
if (deleteFile) {
MmsConnection_fileDelete(con, &error, filename);
if (error != MMS_ERROR_NONE) {
printf("Delete file failed: (ERROR %i)\n", error);
@ -428,15 +429,15 @@ int main(int argc, char** argv) {
else {
printf("File deleted\n");
}
}
}
exit:
free(hostname);
free(domainName);
free(variableName);
free(journalName);
free(componentName);
exit:
free(hostname);
free(domainName);
free(variableName);
free(journalName);
free(componentName);
MmsConnection_destroy(con);
MmsConnection_destroy(con);
}

@ -59,9 +59,21 @@ int main(int argc, char** argv) {
char* ethernetIfcID = argv[1];
printf("Using GOOSE interface: %s\n", ethernetIfcID);
/* set GOOSE interface for all GOOSE publishers (GCBs) */
IedServer_setGooseInterfaceId(iedServer, ethernetIfcID);
}
if (argc > 2) {
char* ethernetIfcID = argv[2];
printf("Using GOOSE interface for GenericIO/LLN0.gcbAnalogValues: %s\n", ethernetIfcID);
/* set GOOSE interface for a particular GOOSE publisher (GCB) */
IedServer_setGooseInterfaceIdEx(iedServer, IEDMODEL_GenericIO_LLN0, "gcbAnalogValues", ethernetIfcID);
}
/* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102);

@ -25,6 +25,10 @@ static IedServer iedServer = NULL;
static bool subsAnIn1 = false;
static bool subsInd1 = false;
static float an1 = 0.f;
static uint64_t timestamp = 0;
static bool ind1 = true;
void
sigint_handler(int signalId)
{
@ -41,6 +45,28 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
printf("Connection closed\n");
}
static void
updateProcessValues()
{
Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp);
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
if (subsAnIn1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1);
}
if (subsInd1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1);
}
}
static MmsDataAccessError
writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter)
{
@ -64,7 +90,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f));
}
else {
IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subID, "");
subsAnIn1 = false;
updateProcessValues();
}
}
@ -105,7 +135,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal));
}
else {
IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subID, "");
subsInd1 = false;
updateProcessValues();
}
}
else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subVal) {
@ -135,9 +169,6 @@ main(int argc, char** argv)
{
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create a new IEC 61850 server instance */
iedServer = IedServer_create(&iedModel);
@ -167,44 +198,22 @@ main(int argc, char** argv)
signal(SIGINT, sigint_handler);
float t = 0.f;
bool ind1 = true;
while (running) {
uint64_t timestamp = Hal_getTimeInMs();
timestamp = Hal_getTimeInMs();
t += 0.1f;
float an1 = sinf(t);
an1 = sinf(t);
if (ind1)
ind1 = false;
else
ind1 = true;
Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp);
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
/* toggle clock-not-synchronized flag in timestamp */
if (((int) t % 2) == 0)
Timestamp_setClockNotSynchronized(&iecTimestamp, true);
IedServer_lockDataModel(iedServer);
if (subsAnIn1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1);
}
if (subsInd1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1);
}
updateProcessValues();
IedServer_unlockDataModel(iedServer);

@ -75,13 +75,18 @@ main(int argc, char** argv)
/* Start listening to SV messages - starts a new receiver background thread */
SVReceiver_start(receiver);
signal(SIGINT, sigint_handler);
if (SVReceiver_isRunning(receiver)) {
signal(SIGINT, sigint_handler);
while (running)
Thread_sleep(1);
while (running)
Thread_sleep(1);
/* Stop listening to SV messages */
SVReceiver_stop(receiver);
/* Stop listening to SV messages */
SVReceiver_stop(receiver);
}
else {
printf("Failed to start SV subscriber. Reason can be that the Ethernet interface doesn't exist or root permission are required.\n");
}
/* Cleanup and free resources */
SVReceiver_destroy(receiver);

@ -8,6 +8,10 @@ set_source_files_properties(${example_SRCS}
PROPERTIES LANGUAGE CXX)
ENDIF(WIN32)
configure_file(client1-key.pem client1-key.pem COPYONLY)
configure_file(client1.cer client1.cer COPYONLY)
configure_file(root.cer root.cer COPYONLY)
add_executable(tls_client_example
${example_SRCS}
)

@ -12,6 +12,12 @@ set_source_files_properties(${example_SRCS}
PROPERTIES LANGUAGE CXX)
ENDIF(WIN32)
configure_file(server-key.pem server-key.pem COPYONLY)
configure_file(server.cer server.cer COPYONLY)
configure_file(client1.cer client1.cer COPYONLY)
configure_file(client2.cer client2.cer COPYONLY)
configure_file(root.cer root.cer COPYONLY)
add_executable(tls_server_example
${example_SRCS}
)

@ -106,18 +106,18 @@ ENDIF(WIN32)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC" )
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" )
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0)
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16)
message("Found mbedtls -> can compile with TLS support")
set(WITH_MBEDTLS 1)
endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0)
endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16)
if(WITH_MBEDTLS)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls
${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0/include
${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/include
)
file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.6.0/library/*.c)
file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/library/*.c)
add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")

@ -36,6 +36,10 @@
#include "lib_memory.h"
#include "hal_ethernet.h"
#ifndef DEBUG_SOCKET
#define DEBUG_SOCKET 0
#endif
struct sEthernetSocket {
int rawSocket;
bool isBind;
@ -123,23 +127,26 @@ getInterfaceIndex(int sock, const char* deviceName)
strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) {
perror("ETHERNET_LINUX: Failed to get interface index -> exit");
exit(1);
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Failed to get interface index");
return -1;
}
int interfaceIndex = ifr.ifr_ifindex;
if (ioctl (sock, SIOCGIFFLAGS, &ifr) == -1)
{
perror ("ETHERNET_LINUX: Problem getting device flags -> exit");
exit (1);
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return -1;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1)
{
perror ("ETHERNET_LINUX: Setting device to promiscuous mode failed -> exit");
exit (1);
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return -1;
}
return interfaceIndex;
@ -178,7 +185,8 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
ethernetSocket->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (ethernetSocket->rawSocket == -1) {
printf("Error creating raw socket!\n");
if (DEBUG_SOCKET)
printf("Error creating raw socket!\n");
GLOBAL_FREEMEM(ethernetSocket);
return NULL;
}
@ -186,7 +194,14 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
ethernetSocket->socketAddress.sll_family = PF_PACKET;
ethernetSocket->socketAddress.sll_protocol = htons(ETH_P_IP);
ethernetSocket->socketAddress.sll_ifindex = getInterfaceIndex(ethernetSocket->rawSocket, interfaceId);
int ifcIdx = getInterfaceIndex(ethernetSocket->rawSocket, interfaceId);
if (ifcIdx == -1) {
Ethernet_destroySocket(ethernetSocket);
return NULL;
}
ethernetSocket->socketAddress.sll_ifindex = ifcIdx;
ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER;
ethernetSocket->socketAddress.sll_pkttype = PACKET_OTHERHOST;

@ -33,6 +33,7 @@
#include <stdio.h>
#include <fcntl.h>
#include <netinet/tcp.h> /* required for TCP keepalive */
#include <linux/version.h>
#include "hal_thread.h"
#include "lib_memory.h"
@ -214,6 +215,7 @@ TcpServerSocket_create(const char* address, int port)
int optionReuseAddr = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000;
int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@ -221,6 +223,9 @@ TcpServerSocket_create(const char* address, int port)
if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
}
#else
#warning "TCP_USER_TIMEOUT not supported by linux kernel"
#endif
if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
@ -312,6 +317,7 @@ TcpSocket_create()
self->fd = sock;
self->connectTimeout = 5000;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000;
int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@ -319,6 +325,8 @@ TcpSocket_create()
if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
}
#endif
}
else {
if (DEBUG_SOCKET)

@ -171,7 +171,7 @@ TLSConfiguration_create()
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED);
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_DISABLED);
/* static int hashes[] = {3,4,5,6,7,8,0}; */
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */

@ -35,8 +35,8 @@
#define GOOSE_MAX_MESSAGE_SIZE 1518
static void
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID);
static bool
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags);
struct sGoosePublisher {
uint8_t* buffer;
@ -63,25 +63,40 @@ struct sGoosePublisher {
MmsValue* timestamp; /* time when stNum is increased */
};
GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag)
{
GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher));
prepareGooseBuffer(self, parameters, interfaceID);
if (self) {
self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) {
self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
GoosePublisher_reset(self);
GoosePublisher_reset(self);
}
else {
GoosePublisher_destroy(self);
self = NULL;
}
}
return self;
}
GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
{
return GoosePublisher_createEx(parameters, interfaceID, true);
}
void
GoosePublisher_destroy(GoosePublisher self)
{
Ethernet_destroySocket(self->ethernetSocket);
if (self->ethernetSocket) {
Ethernet_destroySocket(self->ethernetSocket);
}
MmsValue_delete(self->timestamp);
@ -94,7 +109,9 @@ GoosePublisher_destroy(GoosePublisher self)
if (self->dataSetRef != NULL)
GLOBAL_FREEMEM(self->dataSetRef);
GLOBAL_FREEMEM(self->buffer);
if (self->buffer)
GLOBAL_FREEMEM(self->buffer);
GLOBAL_FREEMEM(self);
}
@ -159,8 +176,8 @@ GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToL
self->timeAllowedToLive = timeAllowedToLive;
}
static void
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID)
static bool
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags)
{
uint8_t srcAddr[6];
@ -194,50 +211,57 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
else
self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr);
self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE);
if (self->ethernetSocket) {
self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE);
memcpy(self->buffer, dstAddr, 6);
memcpy(self->buffer + 6, srcAddr, 6);
memcpy(self->buffer, dstAddr, 6);
memcpy(self->buffer + 6, srcAddr, 6);
int bufPos = 12;
int bufPos = 12;
#if 1
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
if (useVlanTags) {
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci2 = vlanId % 256;
uint8_t tci2 = vlanId % 256;
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
#endif
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
}
/* EtherType GOOSE */
self->buffer[bufPos++] = 0x88;
self->buffer[bufPos++] = 0xB8;
/* EtherType GOOSE */
self->buffer[bufPos++] = 0x88;
self->buffer[bufPos++] = 0xB8;
/* APPID */
self->buffer[bufPos++] = appId / 256;
self->buffer[bufPos++] = appId % 256;
/* APPID */
self->buffer[bufPos++] = appId / 256;
self->buffer[bufPos++] = appId % 256;
self->lengthField = bufPos;
self->lengthField = bufPos;
/* Length */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x08;
/* Length */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x08;
/* Reserved1 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved1 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved2 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved2 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
self->payloadStart = bufPos;
self->payloadStart = bufPos;
return true;
}
else {
return false;
}
}
static int32_t

@ -49,6 +49,9 @@ typedef struct sGoosePublisher* GoosePublisher;
LIB61850_API GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID);
LIB61850_API GoosePublisher
GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag);
LIB61850_API void
GoosePublisher_destroy(GoosePublisher self);

@ -111,6 +111,15 @@ GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId)
self->interfaceId = StringUtils_copyString(interfaceId);
}
const char*
GooseReceiver_getInterfaceId(GooseReceiver self)
{
if (self->interfaceId)
return self->interfaceId;
else
return CONFIG_ETHERNET_INTERFACE_ID;
}
static void
createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength)
{
@ -784,8 +793,6 @@ gooseReceiverLoop(void* threadParameter)
{
GooseReceiver self = (GooseReceiver) threadParameter;
GooseReceiver_startThreadless(self);
if (self->running) {
while (self->running) {
@ -807,17 +814,19 @@ void
GooseReceiver_start(GooseReceiver self)
{
#if (CONFIG_MMS_THREADLESS_STACK == 0)
self->thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, false);
if (GooseReceiver_startThreadless(self)) {
self->thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, false);
if (self->thread != NULL) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE receiver started for interface %s\n", self->interfaceId);
if (self->thread != NULL) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE receiver started for interface %s\n", self->interfaceId);
Thread_start(self->thread);
}
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Starting GOOSE receiver failed for interface %s\n", self->interfaceId);
Thread_start(self->thread);
}
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Starting GOOSE receiver failed for interface %s\n", self->interfaceId);
}
}
#endif
}
@ -835,7 +844,9 @@ GooseReceiver_stop(GooseReceiver self)
self->stop = true;
self->running = false;
Thread_destroy(self->thread);
if (self->thread)
Thread_destroy(self->thread);
self->stop = false;
#endif
}

@ -73,6 +73,16 @@ GooseReceiver_createEx(uint8_t* buffer);
LIB61850_API void
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId);
/**
* \brief return the interface ID used by the GOOSE receiver
*
* \param self the GosseReceiver instance
*
* \return the Ethernet interface ID string
*/
LIB61850_API const char*
GooseReceiver_getInterfaceId(GooseReceiver self);
/**
* \brief Add a subscriber to this receiver instance
*

@ -57,6 +57,7 @@ struct sControlObjectClient
LastApplError lastApplError;
MmsError lastMmsError;
MmsDataAccessError lastAccessError; /* last error of read or write command */
CommandTerminationHandler commandTerminationHandler;
void* commandTerminaionHandlerParameter;
@ -316,7 +317,10 @@ ControlObjectClient_getCtlValType(ControlObjectClient self)
IedClientError
ControlObjectClient_getLastError(ControlObjectClient self)
{
return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
if (self->lastAccessError != DATA_ACCESS_ERROR_SUCCESS)
return iedConnection_mapDataAccessErrorToIedError(self->lastAccessError);
else
return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
}
void
@ -501,13 +505,14 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
MmsError mmsError;
MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, operParameters);
MmsValue_setElement(operParameters, 0, NULL);
MmsValue_delete(operParameters);
self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
@ -544,6 +549,9 @@ internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsData
bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@ -728,6 +736,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_delete(selValParameters);
self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
@ -765,6 +774,9 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@ -881,6 +893,7 @@ ControlObjectClient_select(ControlObjectClient self)
self->ctlNum++;
self->lastMmsError = mmsError;
self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
if (value == NULL) {
if (DEBUG_IED_CLIENT)
@ -899,6 +912,12 @@ ControlObjectClient_select(ControlObjectClient self)
selected = true;
}
}
else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
self->lastAccessError = MmsValue_getDataAccessError(value);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: unexpected response from server!\n");
@ -925,12 +944,17 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
bool success = false;
self->lastMmsError = err;
self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
self->ctlNum++;
if (iedError == IED_ERROR_OK) {
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
iedError = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value));
MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value);
self->lastAccessError = dataAccessError;
iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError);
}
else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
@ -1082,6 +1106,7 @@ ControlObjectClient_cancel(ControlObjectClient self)
&mmsError, domainId, itemId, cancelParameters);
self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters);
@ -1117,6 +1142,9 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA
bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);

@ -102,6 +102,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError)
case MMS_ERROR_OUTSTANDING_CALL_LIMIT:
return IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED:
return IED_ERROR_OBJECT_UNDEFINED;
default:
return IED_ERROR_UNKNOWN;
}
@ -638,6 +641,24 @@ IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
self->connectionTimeout = timeoutInMs;
}
void
IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs)
{
if (self->connection) {
MmsConnection_setRequestTimeout(self->connection, timeoutInMs);
}
}
uint32_t
IedConnection_getRequestTimeout(IedConnection self)
{
if (self->connection) {
return MmsConnection_getRequestTimeout(self->connection);
}
else
return 0;
}
IedConnectionState
IedConnection_getState(IedConnection self)
{

@ -244,6 +244,28 @@ IedConnection_destroy(IedConnection self);
LIB61850_API void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs);
/**
* \brief set the request timeout in ms
*
* Set the request timeout for this connection. You can call this function any time to adjust
* timeout behavior.
*
* \param self the connection object
* \param timoutInMs the connection timeout in ms
*/
LIB61850_API void
IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs);
/**
* \brief get the request timeout in ms for this connection
*
* \param self the connection object
*
* \return request timeout in milliseconds
*/
LIB61850_API uint32_t
IedConnection_getRequestTimeout(IedConnection self);
/**
* \brief Perform MMS message handling and house-keeping tasks (for non-thread mode only)
*
@ -1102,7 +1124,7 @@ typedef enum {
/** the element is included due to a general interrogation by the client */
IEC61850_REASON_GI = 5,
/** the reason for inclusion is unknown */
/** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */
IEC61850_REASON_UNKNOWN = 6
} ReasonForInclusion;

@ -459,6 +459,8 @@ IedServer_getMmsServer(IedServer self);
* then configured GOOSE control blocks keep inactive until a MMS client enables
* them by writing to the GOOSE control block.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
*/
LIB61850_API void
@ -470,6 +472,8 @@ IedServer_enableGoosePublishing(IedServer self);
* This will set the GoEna attribute of all configured GOOSE control blocks
* to false. This will stop GOOSE transmission.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
*/
LIB61850_API void
@ -482,12 +486,46 @@ IedServer_disableGoosePublishing(IedServer self);
* default interface ID from stack_config.h is used. Note the interface ID is operating system
* specific!
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
* \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/
LIB61850_API void
IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
/**
* \brief Set the Ethernet interface to be used by GOOSE publishing
*
* This function can be used to set the GOOSE interface ID forG all CBs (parameter ln = NULL) or for
* a specific GCB specified by the logical node instance and the GCB name.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
* \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
* \param gcbName the name (not object reference!) of the GCB
* \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/
LIB61850_API void
IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId);
/**
* \brief Enable/disable the use of VLAN tags in GOOSE messages
*
* This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = NULL) or for
* a specific GCB specified by the logical node instance and the GCB name.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on
* \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
* \param gcbName the name (not object reference!) of the GCB
* \param useVlanTag true to enable VLAN tagging, false otherwise
*/
LIB61850_API void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
/**@}*/
/**
@ -1126,6 +1164,16 @@ ControlAction_getOrCat(ControlAction self);
LIB61850_API uint8_t*
ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
/**
* \brief Get the ctlNum attribute send by the client
*
* \param self the control action instance
*
* \return the ctlNum value
*/
LIB61850_API int
ControlAction_getCtlNum(ControlAction self);
/**
* \brief Gets the client object associated with the client that caused the control action
*

@ -1,7 +1,7 @@
/*
* mms_goose.h
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -35,6 +35,15 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self);
LIB61850_INTERNAL MmsDomain*
MmsGooseControlBlock_getDomain(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag);
LIB61850_INTERNAL void
MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId);
LIB61850_INTERNAL LogicalNode*
MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self);
LIB61850_INTERNAL char*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self);

@ -106,6 +106,12 @@ MmsMapping_enableGoosePublishing(MmsMapping* self);
LIB61850_INTERNAL void
MmsMapping_disableGoosePublishing(MmsMapping* self);
LIB61850_INTERNAL void
MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
LIB61850_INTERNAL void
MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId);
LIB61850_INTERNAL void
MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject);

@ -1287,6 +1287,21 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu
}
void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_useGooseVlanTag(self->mmsMapping, ln, gcbName, useVlanTag);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
}
void
IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_setGooseInterfaceId(self->mmsMapping, ln, gcbName, interfaceId);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
}
void
IedServer_enableGoosePublishing(IedServer self)

@ -1734,6 +1734,18 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize)
return NULL;
}
int
ControlAction_getCtlNum(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
if (controlObject->ctlNum) {
return MmsValue_toInt32(controlObject->ctlNum);
}
return -1;
}
ClientConnection
ControlAction_getClientConnection(ControlAction self)
{

@ -39,7 +39,9 @@
struct sMmsGooseControlBlock {
char* name;
bool goEna;
int goEna:1;
int isDynamicDataSet:1;
int useVlanTag:1;
char* dstAddress;
@ -50,7 +52,6 @@ struct sMmsGooseControlBlock {
GoosePublisher publisher;
DataSet* dataSet;
bool isDynamicDataSet;
LinkedList dataSetValues;
uint64_t nextPublishTime;
@ -68,6 +69,8 @@ struct sMmsGooseControlBlock {
char* goCBRef;
char* goId;
char* dataSetRef;
char* gooseInterfaceId;
};
MmsGooseControlBlock
@ -75,9 +78,12 @@ MmsGooseControlBlock_create()
{
MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock));
if (self) {
self->useVlanTag = true;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->publisherMutex = Semaphore_create(1);
self->publisherMutex = Semaphore_create(1);
#endif
}
return self;
}
@ -112,11 +118,29 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
}
}
if (self->gooseInterfaceId != NULL)
GLOBAL_FREEMEM(self->gooseInterfaceId);
MmsValue_delete(self->mmsValue);
GLOBAL_FREEMEM(self);
}
void
MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag)
{
self->useVlanTag = useVlanTag;
}
void
MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId)
{
if (self->gooseInterfaceId != NULL)
GLOBAL_FREEMEM(self->gooseInterfaceId);
self->gooseInterfaceId = StringUtils_copyString(interfaceId);
}
MmsDomain*
MmsGooseControlBlock_getDomain(MmsGooseControlBlock self)
{
@ -129,6 +153,12 @@ MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self)
return self->dataSet;
}
LogicalNode*
MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self)
{
return self->logicalNode;
}
char*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self)
{
@ -222,7 +252,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6);
self->publisher = GoosePublisher_create(&commParameters, self->mmsMapping->gooseInterfaceId);
if (self->gooseInterfaceId)
self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag);
else
self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag);
self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));

@ -2914,6 +2914,44 @@ MmsMapping_enableGoosePublishing(MmsMapping* self)
}
void
MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag)
{
LinkedList element = self->gseControls;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
if (ln == NULL) {
MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag);
}
else {
if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) {
MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag);
}
}
}
}
void
MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId)
{
LinkedList element = self->gseControls;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
if (ln == NULL) {
MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId);
}
else {
if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) {
MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId);
}
}
}
}
void
MmsMapping_disableGoosePublishing(MmsMapping* self)
{

@ -194,7 +194,6 @@ BerEncoder_revertByteOrder(uint8_t* octets, const int size)
int
BerEncoder_compressInteger(uint8_t* integer, int originalSize)
{
uint8_t* integerEnd = integer + originalSize - 1;
uint8_t* bytePosition;
@ -205,7 +204,7 @@ BerEncoder_compressInteger(uint8_t* integer, int originalSize)
continue;
}
else if (bytePosition[0] == 0xff) {
if (bytePosition[1] & 0x80)
if ((bytePosition[1] & 0x80) == 0x80)
continue;
}

@ -1,7 +1,7 @@
/*
* ber_integer.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -106,7 +106,23 @@ BerInteger_setUint16(Asn1PrimitiveValue* self, uint16_t value)
uint16_t valueCopy = value;
uint8_t* valueBuffer = (uint8_t*) &valueCopy;
return setIntegerValue(self, valueBuffer, sizeof(value));
uint8_t byteBuffer[3];
int i;
#if (ORDER_LITTLE_ENDIAN == 1)
byteBuffer[2] = 0;
for (i = 0; i < 2; i++)
byteBuffer[i] = valueBuffer[i];
#else
byteBuffer[0] = 0;
for (i = 0; i < 2; i++)
byteBuffer[i + 1] = valueBuffer[i];
#endif /* (ORDER_LITTLE_ENDIAN == 1) */
return setIntegerValue(self, byteBuffer, sizeof(byteBuffer));
}
int

@ -160,6 +160,16 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath);
LIB61850_API void
MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs);
/**
* \brief Get the request timeout in ms for this connection
*
* \param self MmsConnection instance to operate on
*
* \return request timeout in milliseconds
*/
LIB61850_API uint32_t
MmsConnection_getRequestTimeout(MmsConnection self);
/**
* \brief Set the connect timeout in ms for this connection instance
*

@ -544,7 +544,7 @@ MmsValue_getBinaryTimeAsUtcMs(const MmsValue* self);
*
* This method will copy the provided buffer to the internal buffer of the
* MmsValue instance. This will only happen if the internal buffer size is large
* enough for the new value.
* enough for the new value. Otherwise the object value is not changed.
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
* \param buf the buffer that contains the new value
@ -556,6 +556,9 @@ MmsValue_setOctetString(MmsValue* self, uint8_t* buf, int size);
/**
* \brief Returns the size in bytes of an MmsValue object of type MMS_OCTET_STRING.
*
* NOTE: To access the byte in the buffer the function \ref MmsValue_getOctetStringBuffer
* has to be used.
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
*
* \return size in bytes
@ -579,6 +582,8 @@ MmsValue_getOctetStringMaxSize(MmsValue* self);
/**
* \brief Returns the reference to the internally hold buffer of an MmsValue object of type MMS_OCTET_STRING.
*
* NOTE: The size of the buffer can be requested with the \ref MmsValue_getOctetStringSize function.
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
*
* \return reference to the buffer

@ -88,8 +88,6 @@ struct sMmsOutstandingCall
uint64_t timeout;
};
typedef struct sMmsOutstandingCall* MmsOutstandingCall;
/* private instance variables */
struct sMmsConnection {
Semaphore lastInvokeIdLock;
@ -374,5 +372,8 @@ uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response);
LIB61850_INTERNAL MmsOutstandingCall
mmsClient_getMatchingObtainFileRequest(MmsConnection self, const char* filename);
#endif /* MMS_MSG_INTERNAL_H_ */

@ -1,7 +1,7 @@
/*
* mms_common_internal.h
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -42,11 +42,17 @@
#include "hal_filesystem.h"
typedef struct sMmsOutstandingCall* MmsOutstandingCall;
typedef struct {
int32_t frsmId;
uint32_t readPosition;
uint32_t fileSize;
FileHandle fileHandle;
#if (MMS_OBTAIN_FILE_SERVICE == 1)
MmsOutstandingCall obtainRequest;
#endif
} MmsFileReadStateMachine;
/* include for MmsFileReadHandler definition */

@ -361,6 +361,9 @@ mmsServer_handleObtainFileRequest(
uint32_t invokeId,
ByteBuffer* response);
LIB61850_INTERNAL void
mmsServerConnection_stopFileUploadTasks(MmsServerConnection self);
LIB61850_INTERNAL bool
mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess);

@ -314,6 +314,36 @@ removeFromOutstandingCalls(MmsConnection self, uint32_t invokeId)
Semaphore_post(self->outstandingCallsLock);
}
MmsOutstandingCall
mmsClient_getMatchingObtainFileRequest(MmsConnection self, const char* filename)
{
int i = 0;
Semaphore_wait(self->outstandingCallsLock);
for (i = 0; i < OUTSTANDING_CALLS; i++) {
if (self->outstandingCalls[i].isUsed) {
if (self->outstandingCalls[i].type == MMS_CALL_TYPE_OBTAIN_FILE) {
char* storedFilename = (char*) self->outstandingCalls[i].internalParameter.ptr;
if (storedFilename) {
if (!strcmp(filename, storedFilename)) {
Semaphore_post(self->outstandingCallsLock);
return &(self->outstandingCalls[i]);
}
}
}
}
}
Semaphore_post(self->outstandingCallsLock);
return NULL;
}
static void
sendMessage(MmsConnection self, ByteBuffer* message)
{
@ -927,6 +957,11 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M
MmsConnection_GenericServiceHandler handler =
(MmsConnection_GenericServiceHandler) outstandingCall->userCallback;
if (outstandingCall->type == MMS_CALL_TYPE_OBTAIN_FILE) {
if (outstandingCall->internalParameter.ptr)
GLOBAL_FREEMEM(outstandingCall->internalParameter.ptr);
}
if (err != MMS_ERROR_NONE) {
handler(outstandingCall->invokeId, outstandingCall->userParameter, err, false);
}
@ -1555,6 +1590,12 @@ MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs)
self->requestTimeout = timeoutInMs;
}
uint32_t
MmsConnection_getRequestTimeout(MmsConnection self)
{
return self->requestTimeout;
}
void
MmsConnection_setConnectTimeout(MmsConnection self, uint32_t timeoutInMs)
{
@ -4044,7 +4085,11 @@ MmsConnection_obtainFileAsync(MmsConnection self, MmsError* mmsError, const char
mmsClient_createObtainFileRequest(invokeId, payload, sourceFile, destinationFile);
MmsClientInternalParameter intParam;
intParam.ptr = NULL;
if (sourceFile)
intParam.ptr = StringUtils_copyString(sourceFile);
else
intParam.ptr = NULL;
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_OBTAIN_FILE, handler, parameter, intParam);

@ -127,12 +127,25 @@ mmsClient_handleFileOpenRequest(
MmsFileReadStateMachine* frsm = getFreeFrsm(connection);
if (frsm != NULL) {
MmsOutstandingCall obtainFileCall = mmsClient_getMatchingObtainFileRequest(connection, filename);
if (obtainFileCall) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: file open is matching obtain file request for file %s\n", filename);
obtainFileCall->timeout = Hal_getTimeInMs() + connection->requestTimeout;
}
FileHandle fileHandle = mmsMsg_openFile(MmsConnection_getFilestoreBasepath(connection), filename, false);
if (fileHandle != NULL) {
frsm->fileHandle = fileHandle;
frsm->readPosition = filePosition;
frsm->frsmId = getNextFrsmId(connection);
frsm->obtainRequest = obtainFileCall;
mmsMsg_createFileOpenResponse(MmsConnection_getFilestoreBasepath(connection),
invokeId, response, filename, frsm);
@ -172,8 +185,12 @@ mmsClient_handleFileReadRequest(
MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId);
if (frsm != NULL)
if (frsm) {
if (frsm->obtainRequest)
frsm->obtainRequest->timeout = Hal_getTimeInMs() + connection->requestTimeout;
mmsMsg_createFileReadResponse(connection->parameters.maxPduSize, invokeId, response, frsm);
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER);
}
@ -189,11 +206,19 @@ mmsClient_handleFileCloseRequest(
MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId);
FileSystem_closeFile(frsm->fileHandle);
frsm->fileHandle = NULL;
frsm->frsmId = 0;
if (frsm) {
if (frsm->obtainRequest)
frsm->obtainRequest->timeout = Hal_getTimeInMs() + connection->requestTimeout;
FileSystem_closeFile(frsm->fileHandle);
frsm->fileHandle = NULL;
frsm->frsmId = 0;
frsm->obtainRequest = NULL;
mmsMsg_createFileCloseResponse(invokeId, response);
mmsMsg_createFileCloseResponse(invokeId, response);
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER);
}

@ -588,6 +588,33 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
}
}
#if (MMS_OBTAIN_FILE_SERVICE == 1)
void
mmsServerConnection_stopFileUploadTasks(MmsServerConnection self)
{
MmsServer server = self->server;
int i;
for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) {
if (server->fileUploadTasks[i].state != 0) {
if (server->fileUploadTasks[i].connection == self) {
/* stop file upload task */
FileSystem_closeFile(server->fileUploadTasks[i].fileHandle);
deleteFile(MmsServerConnection_getFilesystemBasepath(self), server->fileUploadTasks[i].destinationFilename);
server->fileUploadTasks[i].state = 0;
}
}
}
}
#endif /*(MMS_OBTAIN_FILE_SERVICE == 1) */
void
mmsServer_handleObtainFileRequest(
MmsServerConnection connection,

@ -740,6 +740,8 @@ MmsServerConnection_destroy(MmsServerConnection self)
for (frsmIndex = 0; frsmIndex < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; frsmIndex++)
if (self->frsms[frsmIndex].fileHandle != NULL)
FileSystem_closeFile(self->frsms[frsmIndex].fileHandle);
mmsServerConnection_stopFileUploadTasks(self);
#endif
#if (MMS_DYNAMIC_DATA_SETS == 1)

@ -85,7 +85,7 @@ struct sSVPublisher {
static bool
preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId)
preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId, bool useVlanTags)
{
uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS;
@ -134,17 +134,19 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
int bufPos = 12;
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
if (useVlanTags) {
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci2 = vlanId % 256;
uint8_t tci2 = vlanId % 256;
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
}
/* EtherType Sampled Values */
self->buffer[bufPos++] = 0x88;
@ -293,14 +295,14 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
}
SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId)
SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag)
{
SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher));
if (self) {
self->asduList = NULL;
if (preparePacketBuffer(self, parameters, interfaceId) == false) {
if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) {
GLOBAL_FREEMEM(self);
self = NULL;
}
@ -310,6 +312,12 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId)
return self;
}
SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId)
{
return SVPublisher_createEx(parameters, interfaceId, true);
}
SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev)
{

@ -70,6 +70,9 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
/**
* \brief Create a new IEC61850-9-2 Sampled Values publisher.
*
* NOTE: VLAN tagging is enabled when calling this constructor. To disable VLAN tagging
* use \ref SVPublisher_createEx instead.
*
* \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.
@ -77,6 +80,17 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
LIB61850_API SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId);
/**
* \brief Create a new IEC61850-9-2 Sampled Values publisher.
*
* \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.
* \param[in] useVlanTags enable(true)/disable(false) VLAN tagging
* \return the new SV publisher instance.
*/
LIB61850_API SVPublisher
SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag);
/**
* \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher.
*

@ -157,38 +157,34 @@ svReceiverLoop(void* threadParameter)
{
SVReceiver self = (SVReceiver) threadParameter;
self->running = true;
self->stopped = false;
if (SVReceiver_startThreadless(self)) {
while (self->running) {
while (self->running) {
if (SVReceiver_tick(self) == false)
Thread_sleep(1);
}
SVReceiver_stopThreadless(self);
}
else {
if (DEBUG_SV_SUBSCRIBER)
printf("SV_SUBSCRIBER: Failed to start SV receiver\n");
if (SVReceiver_tick(self) == false)
Thread_sleep(1);
}
self->stopped = true;
}
void
SVReceiver_start(SVReceiver self)
{
Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true);
if (SVReceiver_startThreadless(self)) {
if (thread != NULL) {
if (DEBUG_SV_SUBSCRIBER)
printf("SV_SUBSCRIBER: SV receiver started for interface %s\n", self->interfaceId);
Thread_start(thread);
Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true);
if (thread) {
Thread_start(thread);
}
else {
if (DEBUG_SV_SUBSCRIBER)
printf("SV_SUBSCRIBER: Failed to start thread\n");
}
}
else {
if (DEBUG_SV_SUBSCRIBER)
@ -206,10 +202,12 @@ SVReceiver_isRunning(SVReceiver self)
void
SVReceiver_stop(SVReceiver self)
{
self->running = false;
if (self->running) {
SVReceiver_stopThreadless(self);
while (self->stopped == false)
Thread_sleep(1);
while (self->stopped == false)
Thread_sleep(1);
}
}
void
@ -218,6 +216,9 @@ SVReceiver_destroy(SVReceiver self)
LinkedList_destroyDeep(self->subscriberList,
(LinkedListValueDeleteFunction) SVSubscriber_destroy);
if (self->interfaceId != NULL)
GLOBAL_FREEMEM(self->interfaceId);
#if (CONFIG_MMS_THREADLESS_STACK == 0)
Semaphore_destroy(self->subscriberListLock);
#endif
@ -247,7 +248,8 @@ SVReceiver_startThreadless(SVReceiver self)
void
SVReceiver_stopThreadless(SVReceiver self)
{
Ethernet_destroySocket(self->ethSocket);
if (self->ethSocket)
Ethernet_destroySocket(self->ethSocket);
self->running = false;
}

@ -1,6 +1,9 @@
README
------
For TLS support with mbedtls download the source tarball of version 2.6.0 and extract here in the subfolder
For TLS support with mbedtls download the source tarball of version 2.16.x and extract here in the subfolder
mbedtls-2.16
After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.16". Otherwise the build system will not find the library.
mbedtls-2.6.0

@ -160,7 +160,26 @@ public class DataModelValue {
System.out.println("Warning: Initialization of CHECK is unsupported!");
case CODEDENUM:
this.value = null;
System.out.println("Warning: Initialization of CODEDENUM is unsupported!");
if (value.equals("intermediate-state"))
this.value = new Integer(0);
else if (value.equals("off"))
this.value = new Integer(1);
else if (value.equals("on"))
this.value = new Integer(2);
else if (value.equals("bad-state"))
this.value = new Integer(4);
else if (value.equals("stop"))
this.value = new Integer(0);
else if (value.equals("lower"))
this.value = new Integer(1);
else if (value.equals("higher"))
this.value = new Integer(2);
else if (value.equals("reserved"))
this.value = new Integer(4);
else
System.out.println("Warning: CODEDENUM is initialized with unsupported value " + value.toString());
break;
case QUALITY:
this.value = null;

@ -798,6 +798,17 @@ public class StaticModelGenerator {
}
break;
case CODEDENUM:
{
buffer.append("MmsValue_newBitString(2);\n");
buffer.append("MmsValue_setBitStringFromIntegerBigEndian(");
buffer.append(daName);
buffer.append(".mmsValue, ");
buffer.append(value.getValue().toString());
buffer.append(");\n");
}
break;
case UNICODE_STRING_255:
buffer.append("MmsValue_newMmsString(\"" + value.getValue() + "\");");
break;

Loading…
Cancel
Save