- updated to version 0.8.3-dev

pull/6/head
Michael Zillgith 11 years ago
parent a0f67f9a5d
commit b5e319b904

@ -1,3 +1,8 @@
Changes to version 0.8.3
------------------------
- client: better support for optional report elements
- server: fixed some minor problems with reporting
Changes to version 0.8.2 Changes to version 0.8.2
------------------------ ------------------------
- Client: Added adjustable timeout to connect functions - Client: Added adjustable timeout to connect functions

@ -11,7 +11,7 @@ project(libiec61850)
set(LIB_VERSION_MAJOR "0") set(LIB_VERSION_MAJOR "0")
set(LIB_VERSION_MINOR "8") set(LIB_VERSION_MINOR "8")
set(LIB_VERSION_PATCH "2") set(LIB_VERSION_PATCH "3")
# feature checks # feature checks
include(CheckLibraryExists) include(CheckLibraryExists)

@ -131,7 +131,7 @@
/* default results for MMS identify service */ /* default results for MMS identify service */
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
#define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850" #define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850"
#define CONFIG_DEFAULT_MMS_REVISION "0.8.2" #define CONFIG_DEFAULT_MMS_REVISION "0.8.3"
/* MMS virtual file store base path - where file services are looking for files */ /* MMS virtual file store base path - where file services are looking for files */
#define CONFIG_VIRTUAL_FILESTORE_BASEPATH "./vmd-filestore/" #define CONFIG_VIRTUAL_FILESTORE_BASEPATH "./vmd-filestore/"

@ -1,121 +0,0 @@
/*
* byte_stream.c
*
* Some helper functions to read from sockets
*
* Copyright 2013 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.
*/
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "byte_stream.h"
#include "socket.h"
int
ByteStream_readUint8(Socket self, uint8_t* byte)
{
int bytesRead;
uint64_t start = Hal_getTimeInMs();
do {
bytesRead = Socket_read(self, byte, 1);
if (bytesRead == -1) break;
} while ((bytesRead < 1) && ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
if (bytesRead != 1) {
return -1;
}
return 1;
}
int
ByteStream_readUint16(Socket self, uint16_t* value)
{
uint8_t byte[2];
int bytes_read = 0;
uint64_t start = Hal_getTimeInMs();
do {
int readVal = Socket_read(self, byte + bytes_read, 2);
if (readVal == -1) break;
bytes_read += readVal;
} while ((bytes_read < 2)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
if (bytes_read != 2)
return -1;
*value = (byte[0] * 0x100) + byte[1];
return 2;
}
int
ByteStream_skipBytes(Socket self, int number)
{
int c = 0;
uint8_t byte;
uint64_t start = Hal_getTimeInMs();
do {
int readBytes = Socket_read(self, &byte, 1);
if (readBytes < 0)
return -1;
else
c = c + readBytes;
} while ((c < number)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
return c;
}
int
ByteStream_readOctets(Socket self, uint8_t* buffer, int size)
{
int readBytes = 0;
int remainingSize = size;
uint64_t start = Hal_getTimeInMs();
do {
int chunkSize = Socket_read(self, buffer + readBytes, remainingSize);
if (chunkSize < 0)
return -1;
else
{
readBytes += chunkSize;
remainingSize = size - readBytes;
}
} while ((readBytes < size)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
return readBytes;
}

@ -1,43 +0,0 @@
/*
* byte_stream.h
*
* Copyright 2013 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 BYTE_STREAM_H_
#define BYTE_STREAM_H_
#include "libiec61850_platform_includes.h"
#include "socket.h"
#include "byte_buffer.h"
int
ByteStream_readUint8(Socket self, uint8_t* byte);
int
ByteStream_readUint16(Socket self, uint16_t* value);
int
ByteStream_skipBytes(Socket self, int number);
int
ByteStream_readOctets(Socket self, uint8_t* targetBuffer, int size);
#endif /* BYTE_STREAM_H_ */

@ -0,0 +1,65 @@
/*
* memory.h
*
* Copyright 2014 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 MEMORY_H_
#define MEMORY_H_
#define CALLOC(nmemb, size) Memory_calloc(nmemb, size)
#define MALLOC(size) Memory_malloc(size)
#define REALLOC(oldptr, size) Memory_realloc(oldptr, size)
#define FREEMEM(ptr) Memory_free(ptr)
#define GLOBAL_CALLOC(nmemb, size) Memory_calloc(nmemb, size)
#define GLOBAL_MALLOC(size) Memory_malloc(size)
#define GLOBAL_REALLOC(oldptr, size) Memory_realloc(oldptr, size)
#define GLOBAL_FREEMEM(ptr) Memory_free(ptr)
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
typedef void
(*MemoryExceptionHandler) (void* parameter);
void
Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter);
void*
Memory_malloc(size_t size);
void*
Memory_calloc(size_t nmemb, size_t size);
void *
Memory_realloc(void *ptr, size_t size);
void
Memory_free(void* memb);
#ifdef __cplusplus
}
#endif
#endif /* MEMORY_H_ */

@ -8,7 +8,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -18,7 +17,7 @@
#define ATTRIBUTE_PACKED #define ATTRIBUTE_PACKED
#endif #endif
#include "time_hal.h" #include "hal_time.h"
#include "mms_value.h" #include "mms_value.h"
#endif /* LIBIEC61850_COMMON_API_INCLUDES_H_ */ #endif /* LIBIEC61850_COMMON_API_INCLUDES_H_ */

@ -19,4 +19,6 @@
#include <assert.h> #include <assert.h>
#include "lib_memory.h"
#endif /* LIBIEC61850_PLATFORM_INCLUDES_H_ */ #endif /* LIBIEC61850_PLATFORM_INCLUDES_H_ */

@ -30,7 +30,7 @@ char*
copyString(const char* string); copyString(const char* string);
char* char*
copyStringToBuffer(char* string, char* buffer); copyStringToBuffer(const char* string, char* buffer);
char* char*
copySubString(char* startPos, char* endPos); copySubString(char* startPos, char* endPos);

@ -1,7 +1,7 @@
/* /*
* ethernet.h * ethernet_hal.h
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -21,10 +21,11 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#ifndef ETHERNET_H_ #ifndef ETHERNET_HAL_H_
#define ETHERNET_H_ #define ETHERNET_HAL_H_
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -43,12 +44,12 @@ extern "C" {
/** /**
* Opaque handle that represents an Ethernet "socket". * \brief Opaque handle that represents an Ethernet "socket".
*/ */
typedef struct sEthernetSocket* EthernetSocket; typedef struct sEthernetSocket* EthernetSocket;
/** /**
* Return the MAC address of an Ethernet interface. * \brief Return the MAC address of an Ethernet interface.
* *
* The result are the six bytes that make up the Ethernet MAC address. * The result are the six bytes that make up the Ethernet MAC address.
* *
@ -59,7 +60,7 @@ void
Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr); Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr);
/** /**
* Create an Ethernet socket using the specified interface and * \brief Create an Ethernet socket using the specified interface and
* destination MAC address. * destination MAC address.
* *
* \param interfaceId the ID of the Ethernet interface * \param interfaceId the ID of the Ethernet interface
@ -68,17 +69,45 @@ Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr);
EthernetSocket EthernetSocket
Ethernet_createSocket(char* interfaceId, uint8_t* destAddress); Ethernet_createSocket(char* interfaceId, uint8_t* destAddress);
/**
* \brief destroy the ethernet socket
*
* \param ethSocket the ethernet socket handle
*/
void void
Ethernet_destroySocket(EthernetSocket ethSocket); Ethernet_destroySocket(EthernetSocket ethSocket);
void void
Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize); Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize);
/*
* \brief set a protocol filter for the specified etherType
*
* \param ethSocket the ethernet socket handle
* \param etherType the ether type of messages to accept
*/
void void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType); Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType);
/**
* \brief receive an ethernet packet (non-blocking)
*
* \param ethSocket the ethernet socket handle
* \param buffer the buffer to copy the message to
* \param the maximum size of the buffer
*
* \return size of message received in bytes
*/
int int
Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize); Ethernet_receivePacket(EthernetSocket ethSocket, uint8_t* buffer, int bufferSize);
/**
* \brief Indicates if runtime provides support for direct Ethernet access
*
* \return true if Ethernet support is available, false otherwise
*/
bool
Ethernet_isSupported(void);
/*! @} */ /*! @} */
@ -88,4 +117,4 @@ Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize);
} }
#endif #endif
#endif /* ETHERNET_H_ */ #endif /* ETHERNET_HAL_H_ */

@ -1,5 +1,5 @@
/* /*
* filesystem.h * filesystem_hal.h
* *
* Copyright 2014 Michael Zillgith * Copyright 2014 Michael Zillgith
* *
@ -21,8 +21,8 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#ifndef FILESYSTEM_H_ #ifndef FILESYSTEM_HAL_H_
#define FILESYSTEM_H_ #define FILESYSTEM_HAL_H_
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -170,4 +170,4 @@ FileSystem_setBasePath(char* basePath);
} }
#endif #endif
#endif /* FILESYSTEM_H_ */ #endif /* FILESYSTEM_HAL_H_ */

@ -0,0 +1,234 @@
/*
* socket_hal.h
*
* Copyright 2013, 2014 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 SOCKET_HAL_H_
#define SOCKET_HAL_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
*
* @{
*/
/**
* @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer)
*
* Thread and Socket abstraction layer. This functions have to be implemented to
* port libIEC61850 to a new hardware/OS platform.
*
* @{
*/
/** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket;
/** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket;
/** Opaque reference for a set of server and socket handles */
typedef struct sHandleSet* HandleSet;
HandleSet
Handleset_new(void);
void
Handleset_addSocket(HandleSet self, const Socket sock);
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
void
Handleset_destroy(HandleSet self);
/**
* \brief Create a new TcpServerSocket instance
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* \param address ip address or hostname to listen on
* \param port the TCP port to listen on
*
* \return the newly create TcpServerSocket instance
*/
ServerSocket
TcpServerSocket_create(const char* address, int port);
void
ServerSocket_listen(ServerSocket self);
/**
* \brief accept a new incoming connection (non-blocking)
*
* This function shall accept a new incoming connection. It is non-blocking and has to
* return NULL if no new connection is pending.
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* NOTE: The behaviour of this function changed with version 0.8!
*
* \param self server socket instance
*
* \return handle of the new connection socket or NULL if no new connection is available
*/
Socket
ServerSocket_accept(ServerSocket self);
/**
* \brief set the maximum number of pending connection in the queue
*
* Implementation of this function is OPTIONAL.
*
* \param self the server socket instance
* \param backlog the number of pending connections in the queue
*
*/
void
ServerSocket_setBacklog(ServerSocket self, int backlog);
/**
* \brief destroy a server socket instance
*
* Free all resources allocated by this server socket instance.
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* \param self server socket instance
*/
void
ServerSocket_destroy(ServerSocket self);
/**
* \brief create a TCP client socket
*
* Implementation of this function is MANDATORY if client functionality is required.
*
* \return a new client socket instance.
*/
Socket
TcpSocket_create(void);
/**
* \brief set the timeout to establish a new connection
*
* \param self the client socket instance
* \param timeoutInMs the timeout in ms
*/
void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs);
/**
* \brief connect to a server
*
* Connect to a server application identified by the address and port parameter.
*
* The "address" parameter may either be a hostname or an IP address. The IP address
* has to be provided as a C string (e.g. "10.0.2.1").
*
* Implementation of this function is MANDATORY if client functionality is required.
*
* NOTE: return type changed from int to bool with version 0.8
*
* \param self the client socket instance
* \param address the IP address or hostname as C string
* \param port the TCP port of the application to connect to
*
* \return a new client socket instance.
*/
bool
Socket_connect(Socket self, const char* address, int port);
/**
* \brief read from socket to local buffer (non-blocking)
*
* The function shall return immediately if no data is available. In this case
* the function returns 0. If an error happens the function shall return -1.
*
* Implementation of this function is MANDATORY
*
* NOTE: The behaviour of this function changed with version 0.8!
*
* \param self the client, connection or server socket instance
* \param buf the buffer where the read bytes are copied to
* \param size the maximum number of bytes to read (size of the provided buffer)
*
* \return the number of bytes read or -1 if an error occurred
*/
int
Socket_read(Socket self, uint8_t* buf, int size);
/**
* \brief send a message through the socket
*
* Implementation of this function is MANDATORY
*
* \param self client, connection or server socket instance
*
* \return number of bytes transmitted of -1 in case of an error
*/
int
Socket_write(Socket self, uint8_t* buf, int size);
/**
* \brief Get the address of the peer application (IP address and port number)
*
* The peer address has to be returned as
*
* Implementation of this function is MANDATORY
*
* \param self the client, connection or server socket instance
*
* \return the IP address and port number as strings separated by the ':' character.
*/
char*
Socket_getPeerAddress(Socket self);
/**
* \brief destroy a socket (close the socket if a connection is established)
*
* This function shall close the connection (if one is established) and free all
* resources allocated by the socket.
*
* Implementation of this function is MANDATORY
*
* \param self the client, connection or server socket instance
*/
void
Socket_destroy(Socket self);
/*! @} */
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* SOCKET_HAL_H_ */

@ -1,9 +1,9 @@
/* /*
* thread.h * thread_hal.h
* *
* Multi-threading abstraction layer * Multi-threading abstraction layer
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -23,8 +23,8 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#ifndef THREAD_H_ #ifndef THREAD_HAL_H_
#define THREAD_H_ #define THREAD_HAL_H_
#include <stdbool.h> #include <stdbool.h>
@ -112,4 +112,4 @@ Semaphore_destroy(Semaphore self);
#endif #endif
#endif /* THREAD_H_ */ #endif /* THREAD_HAL_H_ */

@ -1,104 +0,0 @@
/*
* socket.h
*
* Copyright 2013 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 SOCKET_H_
#define SOCKET_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup hal Hardware/OS abstraction layer
*
* @{
*/
/**
* @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer)
*
* Thread and Socket abstraction layer. This functions have to be implemented to
* port libIEC61850 to a new hardware/OS platform.
*
* @{
*/
/** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket;
/** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket;
/**
* Create a new TcpServerSocket instance
*
* \param address ip address or hostname to listen on
* \param port the TCP port to listen on
*
* \return the newly create TcpServerSocket instance
*/
ServerSocket
TcpServerSocket_create(char* address, int port);
void
ServerSocket_listen(ServerSocket self);
Socket
ServerSocket_accept(ServerSocket self);
void
ServerSocket_setBacklog(ServerSocket self, int backlog);
void
ServerSocket_destroy(ServerSocket self);
Socket
TcpSocket_create(void);
int
Socket_connect(Socket self, char* address, int port);
int
Socket_read(Socket self, uint8_t* buf, int size);
int
Socket_write(Socket self, uint8_t* buf, int size);
char*
Socket_getPeerAddress(Socket self);
void
Socket_destroy(Socket self);
/*! @} */
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* SOCKET_H_ */

@ -23,13 +23,10 @@
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
#include "iec61850_client.h"
#include "stack_config.h" #include "stack_config.h"
#include "iec61850_client.h"
#include "mms_client_connection.h" #include "mms_client_connection.h"
#include "mms_mapping.h"
#include "ied_connection_private.h" #include "ied_connection_private.h"
#include <stdio.h> #include <stdio.h>
@ -42,6 +39,9 @@
#define DEBUG_IED_CLIENT 0 #define DEBUG_IED_CLIENT 0
#endif #endif
char*
MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer);
struct sControlObjectClient struct sControlObjectClient
{ {
ControlModel ctlModel; ControlModel ctlModel;
@ -99,6 +99,14 @@ convertToMmsAndInsertFC(char* newItemId, char* originalObjectName, char* fc)
newItemId[dstIndex] = 0; newItemId[dstIndex] = 0;
} }
static void
resetLastApplError(ControlObjectClient self)
{
self->lastApplError.error = 0;
self->lastApplError.addCause = ADD_CAUSE_UNKNOWN;
self->lastApplError.ctlNum = 0;
}
ControlObjectClient ControlObjectClient
ControlObjectClient_create(char* objectReference, IedConnection connection) ControlObjectClient_create(char* objectReference, IedConnection connection)
{ {
@ -192,7 +200,7 @@ ControlObjectClient_create(char* objectReference, IedConnection connection)
return NULL; return NULL;
} }
ControlObjectClient self = (ControlObjectClient) calloc(1, sizeof(struct sControlObjectClient)); ControlObjectClient self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
self->objectReference = copyString(objectReference); self->objectReference = copyString(objectReference);
self->connection = connection; self->connection = connection;
@ -227,7 +235,7 @@ ControlObjectClient_create(char* objectReference, IedConnection connection)
void void
ControlObjectClient_destroy(ControlObjectClient self) ControlObjectClient_destroy(ControlObjectClient self)
{ {
free(self->objectReference); GLOBAL_FREEMEM(self->objectReference);
private_IedConnection_removeControlClient(self->connection, self); private_IedConnection_removeControlClient(self->connection, self);
@ -235,9 +243,9 @@ ControlObjectClient_destroy(ControlObjectClient self)
MmsValue_delete(self->ctlVal); MmsValue_delete(self->ctlVal);
if (self->orIdent != NULL) if (self->orIdent != NULL)
free(self->orIdent); GLOBAL_FREEMEM(self->orIdent);
free(self); GLOBAL_FREEMEM(self);
} }
void void
@ -261,10 +269,10 @@ ControlObjectClient_getControlModel(ControlObjectClient self)
} }
void void
ControlObjectClient_setOrigin(ControlObjectClient self, char* orIdent, int orCat) ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat)
{ {
if (self->orIdent != NULL) if (self->orIdent != NULL)
free(self->orIdent); GLOBAL_FREEMEM(self->orIdent);
self->orIdent = copyString(orIdent); self->orIdent = copyString(orIdent);
self->orCat = orCat; self->orCat = orCat;
@ -295,6 +303,8 @@ createOriginValue(ControlObjectClient self)
bool bool
ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime) ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
{ {
resetLastApplError(self);
MmsValue* operParameters; MmsValue* operParameters;
if (self->hasTimeActivatedMode) if (self->hasTimeActivatedMode)
@ -369,6 +379,8 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
bool bool
ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
{ {
resetLastApplError(self);
char domainId[65]; char domainId[65];
char itemId[130]; char itemId[130];
@ -444,6 +456,8 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
bool bool
ControlObjectClient_select(ControlObjectClient self) ControlObjectClient_select(ControlObjectClient self)
{ {
resetLastApplError(self);
char domainId[65]; char domainId[65];
char itemId[130]; char itemId[130];
@ -461,7 +475,7 @@ ControlObjectClient_select(ControlObjectClient self)
MmsValue* value = MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection), MmsValue* value = MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId); &mmsError, domainId, itemId);
int selected = false; bool selected = false;
if (value == NULL) { if (value == NULL) {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
@ -501,6 +515,8 @@ ControlObjectClient_select(ControlObjectClient self)
bool bool
ControlObjectClient_cancel(ControlObjectClient self) ControlObjectClient_cancel(ControlObjectClient self)
{ {
resetLastApplError(self);
MmsValue* cancelParameters; MmsValue* cancelParameters;
if (self->hasTimeActivatedMode) if (self->hasTimeActivatedMode)

@ -47,7 +47,7 @@ struct sClientGooseControlBlock {
ClientGooseControlBlock ClientGooseControlBlock
ClientGooseControlBlock_create(char* objectReference) ClientGooseControlBlock_create(char* objectReference)
{ {
ClientGooseControlBlock self = (ClientGooseControlBlock) calloc(1, sizeof(struct sClientGooseControlBlock)); ClientGooseControlBlock self = (ClientGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientGooseControlBlock));
self->objectReference = copyString(objectReference); self->objectReference = copyString(objectReference);
@ -57,7 +57,7 @@ ClientGooseControlBlock_create(char* objectReference)
void void
ClientGooseControlBlock_destroy(ClientGooseControlBlock self) ClientGooseControlBlock_destroy(ClientGooseControlBlock self)
{ {
free(self->objectReference); GLOBAL_FREEMEM(self->objectReference);
MmsValue_deleteIfNotNull(self->goEna); MmsValue_deleteIfNotNull(self->goEna);
MmsValue_deleteIfNotNull(self->goID); MmsValue_deleteIfNotNull(self->goID);
@ -69,7 +69,7 @@ ClientGooseControlBlock_destroy(ClientGooseControlBlock self)
MmsValue_deleteIfNotNull(self->maxTime); MmsValue_deleteIfNotNull(self->maxTime);
MmsValue_deleteIfNotNull(self->fixedOffs); MmsValue_deleteIfNotNull(self->fixedOffs);
free(self); GLOBAL_FREEMEM(self);
} }
bool bool

@ -38,10 +38,23 @@ struct sClientReport
char* rcbReference; char* rcbReference;
char* rptId; char* rptId;
MmsValue* entryId; MmsValue* entryId;
MmsValue* dataReferences;
MmsValue* dataSetValues; MmsValue* dataSetValues;
ReasonForInclusion* reasonForInclusion; ReasonForInclusion* reasonForInclusion;
/* Presence flags for optional elements */
bool hasDataSetName;
bool hasReasonForInclusion;
bool hasSequenceNumber;
bool hasDataReference;
bool hasConfRev;
bool hasTimestamp; bool hasTimestamp;
bool hasBufOverflow;
uint64_t timestamp; uint64_t timestamp;
uint16_t seqNum;
uint32_t confRev;
bool bufOverflow;
}; };
char* char*
@ -68,7 +81,7 @@ ReasonForInclusion_getValueAsString(ReasonForInclusion reasonCode)
ClientReport ClientReport
ClientReport_create() ClientReport_create()
{ {
ClientReport self = (ClientReport) calloc(1, sizeof(struct sClientReport)); ClientReport self = (ClientReport) GLOBAL_CALLOC(1, sizeof(struct sClientReport));
return self; return self;
} }
@ -79,18 +92,21 @@ ClientReport_destroy(ClientReport self)
if (self->entryId) if (self->entryId)
MmsValue_delete(self->entryId); MmsValue_delete(self->entryId);
free(self->rcbReference); GLOBAL_FREEMEM(self->rcbReference);
if (self->rptId != NULL) if (self->rptId != NULL)
free(self->rptId); GLOBAL_FREEMEM(self->rptId);
if (self->dataSetValues != NULL) if (self->dataSetValues != NULL)
MmsValue_delete(self->dataSetValues); MmsValue_delete(self->dataSetValues);
if (self->dataReferences != NULL)
MmsValue_delete(self->dataReferences);
if (self->reasonForInclusion != NULL) if (self->reasonForInclusion != NULL)
free(self->reasonForInclusion); GLOBAL_FREEMEM(self->reasonForInclusion);
free(self); GLOBAL_FREEMEM(self);
} }
char* char*
@ -132,6 +148,57 @@ ClientReport_getTimestamp(ClientReport self)
return self->timestamp; return self->timestamp;
} }
bool
ClientReport_hasSeqNum(ClientReport self)
{
return self->hasSequenceNumber;
}
uint16_t
ClientReport_getSeqNum(ClientReport self)
{
return self->seqNum;
}
bool
ClientReport_hasDataSetName(ClientReport self)
{
return self->hasDataSetName;
}
bool
ClientReport_hasReasonForInclusion(ClientReport self)
{
return self->hasReasonForInclusion;
}
bool
ClientReport_hasConfRev(ClientReport self)
{
return self->hasConfRev;
}
uint32_t
ClientReport_getConfRev(ClientReport self)
{
return self->confRev;
}
bool
ClientReport_hasBufOvfl(ClientReport self)
{
return self->hasBufOverflow;
}
bool
ClientReport_hasDataReference(ClientReport self)
{
return self->hasDataReference;
}
MmsValue* MmsValue*
ClientReport_getDataSetValues(ClientReport self) ClientReport_getDataSetValues(ClientReport self)
{ {
@ -238,26 +305,35 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
MmsValue* rptIdValue = MmsValue_getElement(value, 0); MmsValue* rptIdValue = MmsValue_getElement(value, 0);
LinkedList element = LinkedList_getNext(self->enabledReports); LinkedList element = LinkedList_getNext(self->enabledReports);
ClientReport report = NULL; ClientReport matchingReport = NULL;
while (element != NULL) { while (element != NULL) {
report = (ClientReport) element->data; ClientReport report = (ClientReport) element->data;
char* rptId =report->rptId; char* rptId = report->rptId;
if (rptId == NULL) if (rptId == NULL)
rptId = report->rcbReference; rptId = report->rcbReference;
if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0) { if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0) {
matchingReport = report;
break; break;
} }
element = LinkedList_getNext(element); element = LinkedList_getNext(element);
} }
if (report == NULL) if (matchingReport == NULL)
return; return;
matchingReport->hasSequenceNumber = false;
matchingReport->hasTimestamp = false;
matchingReport->hasReasonForInclusion = false;
matchingReport->hasDataReference = false;
matchingReport->hasConfRev = false;
matchingReport->hasDataSetName = false;
matchingReport->hasBufOverflow = false;
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue)); printf("DEBUG_IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue));
@ -266,52 +342,75 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
int inclusionIndex = 2; int inclusionIndex = 2;
/* has sequence-number */ /* has sequence-number */
if (MmsValue_getBitStringBit(optFlds, 1) == true) if (MmsValue_getBitStringBit(optFlds, 1) == true) {
MmsValue* seqNum = MmsValue_getElement(value, inclusionIndex);
if (MmsValue_getType(seqNum) == MMS_UNSIGNED) {
matchingReport->seqNum = (uint16_t) MmsValue_toUint32(seqNum);
matchingReport->hasSequenceNumber = true;
}
inclusionIndex++; inclusionIndex++;
}
/* has report-timestamp */ /* has report-timestamp */
if (MmsValue_getBitStringBit(optFlds, 2) == true) { if (MmsValue_getBitStringBit(optFlds, 2) == true) {
MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex); MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex);
if (MmsValue_getType(timeStampValue) == MMS_BINARY_TIME) { if (MmsValue_getType(timeStampValue) == MMS_BINARY_TIME) {
report->hasTimestamp = true; matchingReport->hasTimestamp = true;
report->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue); matchingReport->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue);
} }
inclusionIndex++; inclusionIndex++;
} }
if (MmsValue_getBitStringBit(optFlds, 4) == true) /* check if data set name is present */ /* check if data set name is present */
if (MmsValue_getBitStringBit(optFlds, 4) == true) {
matchingReport->hasDataSetName = true;
inclusionIndex++; inclusionIndex++;
}
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: Found enabled report!\n"); printf("DEBUG_IED_CLIENT: Found enabled report!\n");
/* skip bufOvfl */ /* skip bufOvfl */
if (MmsValue_getBitStringBit(optFlds, 6) == true) if (MmsValue_getBitStringBit(optFlds, 6) == true) {
matchingReport->hasBufOverflow = false;
inclusionIndex++; inclusionIndex++;
}
/* skip entryId */ /* check for entryId */
if (MmsValue_getBitStringBit(optFlds, 7) == true) { if (MmsValue_getBitStringBit(optFlds, 7) == true) {
MmsValue* entryId = MmsValue_getElement(value, inclusionIndex); MmsValue* entryId = MmsValue_getElement(value, inclusionIndex);
if (report->entryId != NULL) { if (matchingReport->entryId != NULL) {
if (!MmsValue_update(report->entryId, entryId)) { if (!MmsValue_update(matchingReport->entryId, entryId)) {
MmsValue_delete(report->entryId); MmsValue_delete(matchingReport->entryId);
report->entryId = MmsValue_clone(entryId); matchingReport->entryId = MmsValue_clone(entryId);
} }
} }
else { else {
report->entryId = MmsValue_clone(entryId); matchingReport->entryId = MmsValue_clone(entryId);
} }
inclusionIndex++; inclusionIndex++;
} }
/* skip confRev */ /* check for confRev */
if (MmsValue_getBitStringBit(optFlds, 8) == true) if (MmsValue_getBitStringBit(optFlds, 8) == true) {
MmsValue* confRev = MmsValue_getElement(value, inclusionIndex);
if (MmsValue_getType(confRev) == MMS_UNSIGNED) {
matchingReport->confRev = MmsValue_toUint32(confRev);
matchingReport->hasConfRev = true;
}
inclusionIndex++; inclusionIndex++;
}
/* skip segmentation fields */ /* skip segmentation fields */
if (MmsValue_getBitStringBit(optFlds, 9) == true) if (MmsValue_getBitStringBit(optFlds, 9) == true)
@ -329,26 +428,35 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
int valueIndex = inclusionIndex + 1; int valueIndex = inclusionIndex + 1;
/* skip data-reference fields */ /* skip data-reference fields */
if (MmsValue_getBitStringBit(optFlds, 5) == true) if (MmsValue_getBitStringBit(optFlds, 5) == true) {
// if (matchingReport->dataReferences == NULL)
// matchingReport->dataReferences = MmsValue_createEmptyArray(dataSetSize);
matchingReport->hasDataReference = true;
valueIndex += includedElements; valueIndex += includedElements;
}
int i; int i;
if (report->dataSetValues == NULL) { if (matchingReport->dataSetValues == NULL) {
report->dataSetValues = MmsValue_createEmtpyArray(dataSetSize); matchingReport->dataSetValues = MmsValue_createEmtpyArray(dataSetSize);
report->reasonForInclusion = (ReasonForInclusion*) matchingReport->reasonForInclusion = (ReasonForInclusion*)
malloc(sizeof(ReasonForInclusion) * dataSetSize); GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize);
int elementIndex; int elementIndex;
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++) for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
report->reasonForInclusion[elementIndex] = REASON_NOT_INCLUDED; matchingReport->reasonForInclusion[elementIndex] = REASON_NOT_INCLUDED;
} }
MmsValue* dataSetValues = report->dataSetValues; MmsValue* dataSetValues = matchingReport->dataSetValues;
bool hasReasonForInclusion = MmsValue_getBitStringBit(optFlds, 3); bool hasReasonForInclusion = MmsValue_getBitStringBit(optFlds, 3);
if (hasReasonForInclusion)
matchingReport->hasReasonForInclusion = true;
int reasonForInclusionIndex = valueIndex + includedElements; int reasonForInclusionIndex = valueIndex + includedElements;
for (i = 0; i < dataSetSize; i++) { for (i = 0; i < dataSetSize; i++) {
@ -372,26 +480,26 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
MmsValue* reasonForInclusion = MmsValue_getElement(value, reasonForInclusionIndex); MmsValue* reasonForInclusion = MmsValue_getElement(value, reasonForInclusionIndex);
if (MmsValue_getBitStringBit(reasonForInclusion, 1) == true) if (MmsValue_getBitStringBit(reasonForInclusion, 1) == true)
report->reasonForInclusion[i] = REASON_DATA_CHANGE; matchingReport->reasonForInclusion[i] = REASON_DATA_CHANGE;
else if (MmsValue_getBitStringBit(reasonForInclusion, 2) == true) else if (MmsValue_getBitStringBit(reasonForInclusion, 2) == true)
report->reasonForInclusion[i] = REASON_QUALITY_CHANGE; matchingReport->reasonForInclusion[i] = REASON_QUALITY_CHANGE;
else if (MmsValue_getBitStringBit(reasonForInclusion, 3) == true) else if (MmsValue_getBitStringBit(reasonForInclusion, 3) == true)
report->reasonForInclusion[i] = REASON_DATA_UPDATE; matchingReport->reasonForInclusion[i] = REASON_DATA_UPDATE;
else if (MmsValue_getBitStringBit(reasonForInclusion, 4) == true) else if (MmsValue_getBitStringBit(reasonForInclusion, 4) == true)
report->reasonForInclusion[i] = REASON_INTEGRITY; matchingReport->reasonForInclusion[i] = REASON_INTEGRITY;
else if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true) else if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true)
report->reasonForInclusion[i] = REASON_GI; matchingReport->reasonForInclusion[i] = REASON_GI;
} }
else { else {
report->reasonForInclusion[i] = REASON_UNKNOWN; matchingReport->reasonForInclusion[i] = REASON_UNKNOWN;
} }
} }
else { else {
report->reasonForInclusion[i] = REASON_NOT_INCLUDED; matchingReport->reasonForInclusion[i] = REASON_NOT_INCLUDED;
} }
} }
if (report->callback != NULL) { if (matchingReport->callback != NULL) {
report->callback(report->callbackParameter, report); matchingReport->callback(matchingReport->callbackParameter, matchingReport);
} }
} }

@ -48,7 +48,7 @@ isBufferedRcb(char* objectReference)
ClientReportControlBlock ClientReportControlBlock
ClientReportControlBlock_create(char* objectReference) ClientReportControlBlock_create(char* objectReference)
{ {
ClientReportControlBlock self = (ClientReportControlBlock) calloc(1, sizeof(struct sClientReportControlBlock)); ClientReportControlBlock self = (ClientReportControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientReportControlBlock));
self->objectReference = copyString(objectReference); self->objectReference = copyString(objectReference);
self->isBuffered = isBufferedRcb(objectReference); self->isBuffered = isBufferedRcb(objectReference);
@ -59,7 +59,7 @@ ClientReportControlBlock_create(char* objectReference)
void void
ClientReportControlBlock_destroy(ClientReportControlBlock self) ClientReportControlBlock_destroy(ClientReportControlBlock self)
{ {
free(self->objectReference); GLOBAL_FREEMEM(self->objectReference);
MmsValue_deleteIfNotNull(self->rptId); MmsValue_deleteIfNotNull(self->rptId);
MmsValue_deleteIfNotNull(self->rptEna); MmsValue_deleteIfNotNull(self->rptEna);
@ -78,7 +78,7 @@ ClientReportControlBlock_destroy(ClientReportControlBlock self)
MmsValue_deleteIfNotNull(self->resvTms); MmsValue_deleteIfNotNull(self->resvTms);
MmsValue_deleteIfNotNull(self->owner); MmsValue_deleteIfNotNull(self->owner);
free(self); GLOBAL_FREEMEM(self);
} }
char* char*
@ -180,13 +180,13 @@ ClientReportControlBlock_getConfRev(ClientReportControlBlock self)
int int
ClientReportControlBlock_getOptFlds(ClientReportControlBlock self) ClientReportControlBlock_getOptFlds(ClientReportControlBlock self)
{ {
return MmsValue_getBitStringAsInteger(self->optFlds); return (MmsValue_getBitStringAsInteger(self->optFlds) / 2);
} }
void void
ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, int optFlds) ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, int optFlds)
{ {
if (self->optFlds == 0) if (self->optFlds == NULL)
self->optFlds = MmsValue_newBitString(10); self->optFlds = MmsValue_newBitString(10);
MmsValue_setBitStringFromInteger(self->optFlds, optFlds * 2); /* bit 0 is reserved in MMS mapping */ MmsValue_setBitStringFromInteger(self->optFlds, optFlds * 2); /* bit 0 is reserved in MMS mapping */
@ -616,10 +616,26 @@ IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientRepo
MmsConnection_writeMultipleVariables(self->connection, &mmsError, domainId, itemIds, values, &accessResults); MmsConnection_writeMultipleVariables(self->connection, &mmsError, domainId, itemIds, values, &accessResults);
if (accessResults != NULL) if (accessResults != NULL) {
LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
LinkedList accessResult = LinkedList_getNext(accessResults);
while (accessResult != NULL) {
MmsValue* dataAccessError = (MmsValue*) accessResult->data;
if (MmsValue_getDataAccessError(dataAccessError) != DATA_ACCESS_ERROR_SUCCESS) {
*error = IED_ERROR_UNKNOWN;
break;
}
accessResult = LinkedList_getNext(accessResult);
}
LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
}
else
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
goto exit_function; goto exit_function;
} }
else { else {

@ -33,6 +33,8 @@
#include "ied_connection_private.h" #include "ied_connection_private.h"
#include "mms_value_internal.h" #include "mms_value_internal.h"
#define DEFAULT_CONNECTION_TIMEOUT 10000
typedef struct sICLogicalDevice typedef struct sICLogicalDevice
{ {
char* name; char* name;
@ -107,7 +109,7 @@ iedConnection_mapDataAccessErrorToIedError(MmsDataAccessError mmsError)
static ICLogicalDevice* static ICLogicalDevice*
ICLogicalDevice_create(char* name) ICLogicalDevice_create(char* name)
{ {
ICLogicalDevice* self = (ICLogicalDevice*) calloc(1, sizeof(struct sICLogicalDevice)); ICLogicalDevice* self = (ICLogicalDevice*) GLOBAL_CALLOC(1, sizeof(struct sICLogicalDevice));
self->name = copyString(name); self->name = copyString(name);
@ -129,7 +131,7 @@ ICLogicalDevice_setDataSetList(ICLogicalDevice* self, LinkedList dataSets)
static void static void
ICLogicalDevice_destroy(ICLogicalDevice* self) ICLogicalDevice_destroy(ICLogicalDevice* self)
{ {
free(self->name); GLOBAL_FREEMEM(self->name);
if (self->variables != NULL) if (self->variables != NULL)
LinkedList_destroy(self->variables); LinkedList_destroy(self->variables);
@ -137,13 +139,13 @@ ICLogicalDevice_destroy(ICLogicalDevice* self)
if (self->dataSets != NULL) if (self->dataSets != NULL)
LinkedList_destroy(self->dataSets); LinkedList_destroy(self->dataSets);
free(self); GLOBAL_FREEMEM(self);
} }
static ClientDataSet static ClientDataSet
ClientDataSet_create(char* dataSetReference) ClientDataSet_create(const char* dataSetReference)
{ {
ClientDataSet self = (ClientDataSet) calloc(1, sizeof(struct sClientDataSet)); ClientDataSet self = (ClientDataSet) GLOBAL_CALLOC(1, sizeof(struct sClientDataSet));
self->dataSetReference = copyString(dataSetReference); self->dataSetReference = copyString(dataSetReference);
StringUtils_replace(self->dataSetReference, '.', '$'); StringUtils_replace(self->dataSetReference, '.', '$');
@ -159,9 +161,9 @@ ClientDataSet_destroy(ClientDataSet self)
if (self->dataSetValues != NULL) if (self->dataSetValues != NULL)
MmsValue_delete(self->dataSetValues); MmsValue_delete(self->dataSetValues);
free(self->dataSetReference); GLOBAL_FREEMEM(self->dataSetReference);
free(self); GLOBAL_FREEMEM(self);
} }
static void static void
@ -315,31 +317,38 @@ doesReportMatchControlObject(char* domainName, char* itemName, char* objectRef)
} }
static void static void
handleLastApplErrorMessage(IedConnection self, MmsValue* value) handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError)
{ {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: received LastApplError\n"); printf("IED_CLIENT: received LastApplError\n");
if ((MmsValue_getType(lastApplError) != MMS_STRUCTURE) || (MmsValue_getArraySize(lastApplError) != 5))
{
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: LastApplError has wrong type!\n");
return;
}
MmsValue* lastApplError = value;
MmsValue* cntrlObj = MmsValue_getElement(lastApplError, 0); MmsValue* cntrlObj = MmsValue_getElement(lastApplError, 0);
MmsValue* error = MmsValue_getElement(lastApplError, 1); MmsValue* error = MmsValue_getElement(lastApplError, 1);
//MmsValue* origin = MmsValue_getElement(lastApplError, 2); //MmsValue* origin = MmsValue_getElement(lastApplError, 2);
MmsValue* ctlNum = MmsValue_getElement(lastApplError, 3); MmsValue* ctlNum = MmsValue_getElement(lastApplError, 3);
MmsValue* addCause = MmsValue_getElement(lastApplError, 4); MmsValue* addCause = MmsValue_getElement(lastApplError, 4);
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: CntrlObj: %s\n", MmsValue_toString(cntrlObj)); printf("IED_CLIENT: CntrlObj: %s\n", MmsValue_toString(cntrlObj));
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: ctlNum: %u\n", MmsValue_toUint32(ctlNum)); printf("IED_CLIENT: ctlNum: %u\n", MmsValue_toUint32(ctlNum));
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: addCause: %i\n", MmsValue_toInt32(addCause)); printf("IED_CLIENT: addCause: %i\n", MmsValue_toInt32(addCause));
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: error: %i\n", MmsValue_toInt32(error)); printf("IED_CLIENT: error: %i\n", MmsValue_toInt32(error));
self->lastApplError.ctlNum = MmsValue_toUint32(ctlNum); self->lastApplError.ctlNum = MmsValue_toUint32(ctlNum);
self->lastApplError.addCause = MmsValue_toInt32(addCause); self->lastApplError.addCause = (ControlAddCause) MmsValue_toInt32(addCause);
self->lastApplError.error = MmsValue_toInt32(error); self->lastApplError.error = MmsValue_toInt32(error);
LinkedList control = LinkedList_getNext(self->clientControls); LinkedList control = LinkedList_getNext(self->clientControls);
while (control != NULL) { while (control != NULL) {
@ -347,8 +356,9 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* value)
char* objectRef = ControlObjectClient_getObjectReference(object); char* objectRef = ControlObjectClient_getObjectReference(object);
if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) {
ControlObjectClient_setLastApplError(object, self->lastApplError); ControlObjectClient_setLastApplError(object, self->lastApplError);
}
control = LinkedList_getNext(control); control = LinkedList_getNext(control);
} }
@ -366,7 +376,6 @@ informationReportHandler(void* parameter, char* domainName,
if (domainName == NULL) { if (domainName == NULL) {
if (isVariableListName) { if (isVariableListName) {
private_IedConnection_handleReport(self, value); private_IedConnection_handleReport(self, value);
} }
else { else {
@ -402,7 +411,7 @@ informationReportHandler(void* parameter, char* domainName,
IedConnection IedConnection
IedConnection_create() IedConnection_create()
{ {
IedConnection self = (IedConnection) calloc(1, sizeof(struct sIedConnection)); IedConnection self = (IedConnection) GLOBAL_CALLOC(1, sizeof(struct sIedConnection));
self->enabledReports = LinkedList_create(); self->enabledReports = LinkedList_create();
self->logicalDevices = NULL; self->logicalDevices = NULL;
@ -414,9 +423,17 @@ IedConnection_create()
self->stateMutex = Semaphore_create(1); self->stateMutex = Semaphore_create(1);
self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
return self; return self;
} }
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{
self->connectionTimeout = timeoutInMs;
}
IedConnectionState IedConnectionState
IedConnection_getState(IedConnection self) IedConnection_getState(IedConnection self)
{ {
@ -469,15 +486,14 @@ IedConnection_connect(IedConnection self, IedClientError* error, char* hostname,
MmsConnection_setConnectionLostHandler(self->connection, connectionLostHandler, (void*) self); MmsConnection_setConnectionLostHandler(self->connection, connectionLostHandler, (void*) self);
MmsConnection_setInformationReportHandler(self->connection, informationReportHandler, self); MmsConnection_setInformationReportHandler(self->connection, informationReportHandler, self);
MmsConnection_setConnectTimeout(self->connection, self->connectionTimeout);
if (MmsConnection_connect(self->connection, &mmsError, hostname, tcpPort)) { if (MmsConnection_connect(self->connection, &mmsError, hostname, tcpPort)) {
*error = IED_ERROR_OK; *error = IED_ERROR_OK;
IedConnection_setState(self, IED_STATE_CONNECTED); IedConnection_setState(self, IED_STATE_CONNECTED);
} }
else { else
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
MmsConnection_destroy(self->connection);
self->connection = NULL;
}
} }
else else
*error = IED_ERROR_ALREADY_CONNECTED; *error = IED_ERROR_ALREADY_CONNECTED;
@ -493,9 +509,6 @@ IedConnection_abort(IedConnection self, IedClientError* error)
MmsConnection_abort(self->connection, &mmsError); MmsConnection_abort(self->connection, &mmsError);
MmsConnection_destroy(self->connection);
self->connection = NULL;
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
} }
else else
@ -520,13 +533,9 @@ void
IedConnection_close(IedConnection self) IedConnection_close(IedConnection self)
{ {
if (IedConnection_getState(self) == IED_STATE_CONNECTED) { if (IedConnection_getState(self) == IED_STATE_CONNECTED) {
MmsConnection_close(self->connection);
IedConnection_setState(self, IED_STATE_CLOSED); IedConnection_setState(self, IED_STATE_CLOSED);
} }
if (self->connection != NULL) {
MmsConnection_destroy(self->connection);
self->connection = NULL;
}
} }
void void
@ -534,6 +543,8 @@ IedConnection_destroy(IedConnection self)
{ {
IedConnection_close(self); IedConnection_close(self);
MmsConnection_destroy(self->connection);
if (self->logicalDevices != NULL) if (self->logicalDevices != NULL)
LinkedList_destroyDeep(self->logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy); LinkedList_destroyDeep(self->logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy);
@ -544,11 +555,11 @@ IedConnection_destroy(IedConnection self)
Semaphore_destroy(self->stateMutex); Semaphore_destroy(self->stateMutex);
free(self); GLOBAL_FREEMEM(self);
} }
MmsVariableSpecification* MmsVariableSpecification*
IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, char* objectReference, IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc) FunctionalConstraint fc)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
@ -582,11 +593,11 @@ IedConnection_getVariableSpecification(IedConnection self, IedClientError* error
} }
MmsValue* MmsValue*
IedConnection_readObject(IedConnection self, IedClientError* error, char* objectReference, IedConnection_readObject(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc) FunctionalConstraint fc)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[129]; char itemIdBuffer[65];
MmsValue* value = NULL; MmsValue* value = NULL;
char* domainId; char* domainId;
@ -613,7 +624,7 @@ IedConnection_readObject(IedConnection self, IedClientError* error, char* object
} }
bool bool
IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readBooleanValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
@ -636,7 +647,7 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char*
} }
float float
IedConnection_readFloatValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readFloatValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
@ -659,7 +670,7 @@ IedConnection_readFloatValue(IedConnection self, IedClientError* error, char* ob
} }
char* char*
IedConnection_readStringValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readStringValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
@ -682,7 +693,7 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, char* o
} }
int32_t int32_t
IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readInt32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
@ -705,7 +716,7 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* ob
} }
uint32_t uint32_t
IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
@ -728,18 +739,19 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, cha
} }
Timestamp* Timestamp*
IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc, IedConnection_readTimestampValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc,
Timestamp* timeStamp) Timestamp* timeStamp)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
Timestamp* retVal = timeStamp; Timestamp* retVal = timeStamp;
if (retVal == NULL)
retVal = (Timestamp*) malloc(sizeof(Timestamp));
if (value != NULL) { if (value != NULL) {
if (MmsValue_getType(value) == MMS_UTC_TIME) { if (MmsValue_getType(value) == MMS_UTC_TIME) {
if (retVal == NULL)
retVal = (Timestamp*) GLOBAL_MALLOC(sizeof(Timestamp));
memcpy(retVal->val, value->value.utcTime, 8); memcpy(retVal->val, value->value.utcTime, 8);
} }
else { else {
@ -757,12 +769,14 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char
} }
Quality Quality
IedConnection_readQualityValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc) IedConnection_readQualityValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc)
{ {
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc); MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
Quality quality = QUALITY_VALIDITY_GOOD; Quality quality = QUALITY_VALIDITY_GOOD;
if (value != NULL) {
if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) { if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) {
quality = Quality_fromMmsValue(value); quality = Quality_fromMmsValue(value);
} }
@ -775,15 +789,17 @@ IedConnection_readQualityValue(IedConnection self, IedClientError* error, char*
MmsValue_delete(value); MmsValue_delete(value);
}
return quality; return quality;
} }
void void
IedConnection_writeObject(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeObject(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, MmsValue* value) FunctionalConstraint fc, MmsValue* value)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[129]; char itemIdBuffer[65];
char* domainId; char* domainId;
char* itemId; char* itemId;
@ -804,7 +820,7 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, char* objec
} }
void void
IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, bool value) FunctionalConstraint fc, bool value)
{ {
MmsValue mmsValue; MmsValue mmsValue;
@ -816,7 +832,7 @@ IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char*
} }
void void
IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, int32_t value) FunctionalConstraint fc, int32_t value)
{ {
uint8_t valueBuffer[4]; uint8_t valueBuffer[4];
@ -838,7 +854,7 @@ IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* o
void void
IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint32_t value) FunctionalConstraint fc, uint32_t value)
{ {
uint8_t valueBuffer[4]; uint8_t valueBuffer[4];
@ -859,7 +875,7 @@ IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, ch
} }
void void
IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeFloatValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, float value) FunctionalConstraint fc, float value)
{ {
MmsValue mmsValue; MmsValue mmsValue;
@ -872,7 +888,7 @@ IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* o
} }
void void
IedConnection_writeOctetString(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeOctetString(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint8_t* value, int valueLength) FunctionalConstraint fc, uint8_t* value, int valueLength)
{ {
MmsValue mmsValue; MmsValue mmsValue;
@ -885,7 +901,7 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, char*
} }
void void
IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, char* value) FunctionalConstraint fc, char* value)
{ {
MmsValue mmsValue; MmsValue mmsValue;
@ -1002,7 +1018,7 @@ mmsFileDirectoryHandler(void* parameter, char* filename, uint32_t size, uint64_t
} }
LinkedList /*<FileDirectoryEntry>*/ LinkedList /*<FileDirectoryEntry>*/
IedConnection_getFileDirectory(IedConnection self, IedClientError* error, char* directoryName) IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* directoryName)
{ {
*error = IED_ERROR_OK; *error = IED_ERROR_OK;
@ -1058,7 +1074,7 @@ mmsFileReadHandler(void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t by
} }
uint32_t uint32_t
IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName, IedClientGetFileHandler handler, IedConnection_getFile(IedConnection self, IedClientError* error, const char* fileName, IedClientGetFileHandler handler,
void* handlerParameter) void* handlerParameter)
{ {
*error = IED_ERROR_OK; *error = IED_ERROR_OK;
@ -1093,7 +1109,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
return 0; return 0;
} }
if (clientFileReadHandler.retVal == true) { if (clientFileReadHandler.retVal == false) {
*error = IED_ERROR_UNKNOWN; *error = IED_ERROR_UNKNOWN;
break; break;
} }
@ -1110,7 +1126,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
} }
void void
IedConnection_deleteFile(IedConnection self, IedClientError* error, char* fileName) IedConnection_deleteFile(IedConnection self, IedClientError* error, const char* fileName)
{ {
*error = IED_ERROR_OK; *error = IED_ERROR_OK;
@ -1133,12 +1149,14 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error,
char* logicalDeviceName) const char* logicalDeviceName)
{ {
*error = IED_ERROR_OK;
if (self->logicalDevices == NULL) if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error); IedConnection_getDeviceModelFromServer(self, error);
if (self->logicalDevices == NULL) if (*error != IED_ERROR_OK)
return NULL; return NULL;
LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices); LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices);
@ -1223,15 +1241,25 @@ addVariablesWithFc(char* fc, char* lnName, LinkedList variables, LinkedList lnDi
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* logicalNodeReference, ACSIClass acsiClass) const char* logicalNodeReference, ACSIClass acsiClass)
{ {
*error = IED_ERROR_OK;
if (strlen(logicalNodeReference) > 129) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
if (self->logicalDevices == NULL) if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error); IedConnection_getDeviceModelFromServer(self, error);
char lnRefCopy[193]; if (*error != IED_ERROR_OK)
return NULL;
strncpy(lnRefCopy, logicalNodeReference, 192); char lnRefCopy[130];
lnRefCopy[192] = 0;
strncpy(lnRefCopy, logicalNodeReference, 129);
lnRefCopy[129] = 0;
char* ldSep = strchr(lnRefCopy, '/'); char* ldSep = strchr(lnRefCopy, '/');
@ -1246,7 +1274,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* logicalNodeName = ldSep + 1; char* logicalNodeName = ldSep + 1;
// search for logical device /* search for logical device */
LinkedList device = LinkedList_getNext(self->logicalDevices); LinkedList device = LinkedList_getNext(self->logicalDevices);
@ -1305,7 +1333,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* dataObjectName = copyString(fcEndPos + 1); char* dataObjectName = copyString(fcEndPos + 1);
if (!addToStringSet(lnDirectory, dataObjectName)) if (!addToStringSet(lnDirectory, dataObjectName))
free(dataObjectName); GLOBAL_FREEMEM(dataObjectName);
} }
} }
} }
@ -1318,6 +1346,21 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
} }
break; break;
case ACSI_CLASS_SGCB:
{
LinkedList variable = LinkedList_getNext(ld->variables);
while (variable != NULL) {
char* variableName = (char*) variable->data;
if (strcmp(variableName, "LLN0$SP$SGCB") == 0)
LinkedList_add(lnDirectory, (void*) copyString("SGCB"));
variable = LinkedList_getNext(variable);
}
}
break;
case ACSI_CLASS_BRCB: case ACSI_CLASS_BRCB:
addVariablesWithFc("BR", logicalNodeName, ld->variables, lnDirectory); addVariablesWithFc("BR", logicalNodeName, ld->variables, lnDirectory);
break; break;
@ -1360,7 +1403,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
break; break;
default: default:
printf("ACSI class not yet supported!\n"); if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ACSI class not yet supported!\n");
break; break;
} }
@ -1370,18 +1414,33 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
char* logicalNodeReference) const char* logicalNodeReference)
{ {
*error = IED_ERROR_OK;
if (strlen(logicalNodeReference) > 129) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
if (self->logicalDevices == NULL) if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error); IedConnection_getDeviceModelFromServer(self, error);
char lnRefCopy[193]; if (*error != IED_ERROR_OK)
return NULL;
strncpy(lnRefCopy, logicalNodeReference, 192); char lnRefCopy[130];
lnRefCopy[192] = 0;
strncpy(lnRefCopy, logicalNodeReference, 129);
lnRefCopy[129] = 0;
char* ldSep = strchr(lnRefCopy, '/'); char* ldSep = strchr(lnRefCopy, '/');
if (ldSep == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
*ldSep = 0; *ldSep = 0;
char* logicalDeviceName = lnRefCopy; char* logicalDeviceName = lnRefCopy;
@ -1441,15 +1500,25 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
static LinkedList static LinkedList
getDataDirectory(IedConnection self, IedClientError* error, getDataDirectory(IedConnection self, IedClientError* error,
char* dataReference, bool withFc) const char* dataReference, bool withFc)
{ {
*error = IED_ERROR_OK;
if (strlen(dataReference) > 129) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
if (self->logicalDevices == NULL) if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error); IedConnection_getDeviceModelFromServer(self, error);
char dataRefCopy[193]; if (*error != IED_ERROR_OK)
return NULL;
char dataRefCopy[130];
strncpy(dataRefCopy, dataReference, 192); strncpy(dataRefCopy, dataReference, 129);
dataRefCopy[192] = 0; dataRefCopy[129] = 0;
char* ldSep = strchr(dataRefCopy, '/'); char* ldSep = strchr(dataRefCopy, '/');
@ -1550,7 +1619,7 @@ getDataDirectory(IedConnection self, IedClientError* error,
if (withFc) { if (withFc) {
int elementNameLen = strlen(subElementName); int elementNameLen = strlen(subElementName);
elementName = (char*) malloc(elementNameLen + 5); elementName = (char*) GLOBAL_MALLOC(elementNameLen + 5);
memcpy(elementName, subElementName, elementNameLen); memcpy(elementName, subElementName, elementNameLen);
elementName[elementNameLen] = '['; elementName[elementNameLen] = '[';
elementName[elementNameLen + 1] = *(fcPos + 1); elementName[elementNameLen + 1] = *(fcPos + 1);
@ -1562,7 +1631,7 @@ getDataDirectory(IedConnection self, IedClientError* error,
elementName = copyString(subElementName); elementName = copyString(subElementName);
if (!addToStringSet(dataDirectory, elementName)) if (!addToStringSet(dataDirectory, elementName))
free(elementName); GLOBAL_FREEMEM(elementName);
} }
} }
} }
@ -1580,38 +1649,211 @@ getDataDirectory(IedConnection self, IedClientError* error,
} }
LinkedList LinkedList
IedConnection_getDataDirectory(IedConnection self, IedClientError* error, IedConnection_getDataDirectory(IedConnection self, IedClientError* error, const char* dataReference)
char* dataReference)
{ {
return getDataDirectory(self, error, dataReference, false); return getDataDirectory(self, error, dataReference, false);
} }
LinkedList LinkedList
IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, const char* dataReference)
char* dataReference)
{ {
return getDataDirectory(self, error, dataReference, true); return getDataDirectory(self, error, dataReference, true);
} }
static LinkedList
getDataDirectoryByFc(IedConnection self, IedClientError* error,
const char* dataReference, FunctionalConstraint fc)
{
*error = IED_ERROR_OK;
if (strlen(dataReference) > 129) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
char* fcString = FunctionalConstraint_toString(fc);
if (fcString == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error);
if (*error != IED_ERROR_OK)
return NULL;
char dataRefCopy[130];
strncpy(dataRefCopy, dataReference, 129);
dataRefCopy[129] = 0;
char* ldSep = strchr(dataRefCopy, '/');
*ldSep = 0;
char* logicalDeviceName = dataRefCopy;
char* logicalNodeName = ldSep + 1;
char* logicalNodeNameEnd = strchr(logicalNodeName, '.');
if (logicalNodeNameEnd == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
int logicalNodeNameLen = logicalNodeNameEnd - logicalNodeName;
char* dataNamePart = logicalNodeNameEnd + 1;
int dataNamePartLen = strlen(dataNamePart);
if (dataNamePartLen < 1) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
StringUtils_replace(dataNamePart, '.', '$');
// search for logical device
LinkedList device = LinkedList_getNext(self->logicalDevices);
bool deviceFound = false;
ICLogicalDevice* ld;
while (device != NULL) {
ld = (ICLogicalDevice*) device->data;
if (strcmp(logicalDeviceName, ld->name) == 0) {
deviceFound = true;
break;
}
device = LinkedList_getNext(device);
}
if (!deviceFound) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return NULL;
}
LinkedList variable = LinkedList_getNext(ld->variables);
LinkedList dataDirectory = LinkedList_create();
while (variable != NULL) {
char* variableName = (char*) variable->data;
char* fcPos = strchr(variableName, '$');
if (fcPos != NULL) {
int lnNameLen = fcPos - variableName;
if (logicalNodeNameLen == lnNameLen) {
if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) {
/* ok we are in the correct logical node */
/* skip FC */
char* fcEnd = strchr(fcPos + 1, '$');
if (fcEnd == NULL)
goto next_variable;
if ((fcPos[1] != fcString[0]) || (fcPos[2] != fcString[1]))
goto next_variable;
char* remainingPart = fcEnd + 1;
int remainingLen = strlen(remainingPart);
if (remainingLen <= dataNamePartLen)
goto next_variable;
if (remainingPart[dataNamePartLen] == '$') {
if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) {
char* subElementName = remainingPart + dataNamePartLen + 1;
char* subElementNameSep = strchr(subElementName, '$');
if (subElementNameSep != NULL)
goto next_variable;
int elementNameLen = strlen(subElementName);
char* elementName = (char*) GLOBAL_MALLOC(elementNameLen + 5);
memcpy(elementName, subElementName, elementNameLen);
elementName[elementNameLen] = '[';
elementName[elementNameLen + 1] = *(fcPos + 1);
elementName[elementNameLen + 2] = *(fcPos + 2);
elementName[elementNameLen + 3] = ']';
elementName[elementNameLen + 4] = 0;
if (!addToStringSet(dataDirectory, elementName))
GLOBAL_FREEMEM(elementName);
}
}
}
}
}
next_variable:
variable = LinkedList_getNext(variable);
}
*error = IED_ERROR_OK;
return dataDirectory;
}
LinkedList
IedConnection_getDataDirectoryByFC(IedConnection self, IedClientError* error, const char* dataReference, FunctionalConstraint fc)
{
return getDataDirectoryByFc(self, error, dataReference, fc);
}
void void
IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dataSetReference, IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList /* <char*> */dataSetElements) LinkedList /* <char*> */dataSetElements)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[129]; char itemIdBuffer[33]; /* maximum data set name = 32 chars */
char* domainId; const char* domainId;
char* itemId; const char* itemId;
bool isAssociationSpecific = false; bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') { if (dataSetReference[0] != '@') {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
itemId = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
StringUtils_replace(itemId, '.', '$'); if (domainId == NULL) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return;
}
int domainIdLength = strlen(domainId);
if ((strlen(dataSetReference) - domainIdLength - 1) > 32) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return;
}
char* itemIdRef = copyStringToBuffer(dataSetReference + domainIdLength + 1, itemIdBuffer);
StringUtils_replace(itemIdRef, '.', '$');
itemId = itemIdRef;
} }
else { else {
itemId = dataSetReference; itemId = dataSetReference + 1;
isAssociationSpecific = true; isAssociationSpecific = true;
} }
@ -1645,20 +1887,39 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dat
} }
void void
IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dataSetReference) IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference)
{ {
char domainId[65]; char domainId[65];
char itemId[129]; char itemId[33];
bool isAssociationSpecific = false; bool isAssociationSpecific = false;
int dataSetReferenceLength = strlen(dataSetReference);
if (dataSetReference[0] != '@') { if (dataSetReference[0] != '@') {
MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId); if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) {
copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemId); *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return;
}
const char* itemIdString = dataSetReference + strlen(domainId) + 1;
if (strlen(itemIdString) > 32) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return;
}
copyStringToBuffer(itemIdString, itemId);
StringUtils_replace(itemId, '.', '$'); StringUtils_replace(itemId, '.', '$');
} }
else { else {
strncpy(itemId, dataSetReference, 128); if (dataSetReferenceLength > 33) {
itemId[128] = 0; *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return;
}
strcpy(itemId, dataSetReference + 1);
isAssociationSpecific = true; isAssociationSpecific = true;
} }
@ -1673,7 +1934,7 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dat
} }
LinkedList /* <char*> */ LinkedList /* <char*> */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, char* dataSetReference, bool* isDeletable) IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable)
{ {
bool deletable = false; bool deletable = false;
@ -1682,18 +1943,19 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, cha
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[129]; char itemIdBuffer[129];
char* domainId = NULL; const char* domainId = NULL;
char* itemId = NULL; const char* itemId = NULL;
bool isAssociationSpecific = false; bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') { if (dataSetReference[0] != '@') {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
itemId = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer); char* itemIdRef = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
StringUtils_replace(itemId, '.', '$'); StringUtils_replace(itemIdRef, '.', '$');
itemId = itemIdRef;
} }
else { else {
itemId = dataSetReference; itemId = dataSetReference + 1;
isAssociationSpecific = true; isAssociationSpecific = true;
} }
@ -1736,25 +1998,25 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, cha
} }
ClientDataSet ClientDataSet
IedConnection_readDataSetValues(IedConnection self, IedClientError* error, char* dataSetReference, IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
ClientDataSet dataSet) ClientDataSet dataSet)
{ {
char domainIdBuffer[65]; char domainIdBuffer[65];
char itemIdBuffer[129]; char itemIdBuffer[129];
char* domainId = NULL; const char* domainId = NULL;
char* itemId = NULL; const char* itemId = NULL;
bool isAssociationSpecific = false; bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') { if (dataSetReference[0] != '@') {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
itemId = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer); char* itemIdRef = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
StringUtils_replace(itemId, '.', '$'); StringUtils_replace(itemIdRef, '.', '$');
itemId = itemIdRef;
} }
else { else {
itemId = dataSetReference; itemId = dataSetReference + 1;
isAssociationSpecific = true; isAssociationSpecific = true;
} }
@ -1815,9 +2077,9 @@ private_IedConnection_removeControlClient(IedConnection self, ControlObjectClien
} }
FileDirectoryEntry FileDirectoryEntry
FileDirectoryEntry_create(char* fileName, uint32_t fileSize, uint64_t lastModified) FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified)
{ {
FileDirectoryEntry self = (FileDirectoryEntry) calloc(1, sizeof(struct sFileDirectoryEntry)); FileDirectoryEntry self = (FileDirectoryEntry) GLOBAL_CALLOC(1, sizeof(struct sFileDirectoryEntry));
self->fileName = copyString(fileName); self->fileName = copyString(fileName);
self->fileSize = fileSize; self->fileSize = fileSize;
@ -1829,8 +2091,8 @@ FileDirectoryEntry_create(char* fileName, uint32_t fileSize, uint64_t lastModifi
void void
FileDirectoryEntry_destroy(FileDirectoryEntry self) FileDirectoryEntry_destroy(FileDirectoryEntry self)
{ {
free(self->fileName); GLOBAL_FREEMEM(self->fileName);
free(self); GLOBAL_FREEMEM(self);
} }
char* char*

@ -36,7 +36,7 @@ Quality_getValidity(Quality* self)
void void
Quality_setValidity(Quality* self, Validity validity) Quality_setValidity(Quality* self, Validity validity)
{ {
*self = *self & (0xfff8); *self = *self & (0xfffc);
*self = *self | validity; *self = *self | validity;
} }
@ -68,6 +68,34 @@ Quality_fromMmsValue(MmsValue* mmsValue)
return (Quality) MmsValue_getBitStringAsInteger(mmsValue); return (Quality) MmsValue_getBitStringAsInteger(mmsValue);
} }
Dbpos
Dbpos_fromMmsValue(MmsValue* mmsValue)
{
return (Dbpos) MmsValue_getBitStringAsIntegerBigEndian(mmsValue);
}
MmsValue*
Dbpos_toMmsValue(MmsValue* mmsValue, Dbpos dbpos)
{
if (mmsValue == NULL) {
mmsValue = MmsValue_newBitString(2);
}
else {
if (MmsValue_getType(mmsValue) != MMS_BIT_STRING)
return NULL;
if (MmsValue_getBitStringSize(mmsValue) != 2)
return NULL;
}
assert((int) dbpos >= 0);
assert((int) dbpos < 4);
MmsValue_setBitStringFromIntegerBigEndian(mmsValue, dbpos);
return mmsValue;
}
char* char*
FunctionalConstraint_toString(FunctionalConstraint fc) { FunctionalConstraint_toString(FunctionalConstraint fc) {
switch (fc) { switch (fc) {

@ -66,7 +66,7 @@ typedef struct
{ {
int ctlNum; int ctlNum;
int error; int error;
int addCause; ControlAddCause addCause;
} LastApplError; } LastApplError;
/** Connection state of the IedConnection instance (either idle, connected or closed) */ /** Connection state of the IedConnection instance (either idle, connected or closed) */
@ -160,6 +160,19 @@ IedConnection_create(void);
void void
IedConnection_destroy(IedConnection self); IedConnection_destroy(IedConnection self);
/**
* \brief set the connect timeout in ms
*
* Set the connect timeout for this connection. This function has to be called before IedConnection_connect
* is called.
*
* \param self the connection object
* \param timoutInMs the connection timeout in ms
*/
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs);
/************************************************** /**************************************************
* Association service * Association service
**************************************************/ **************************************************/
@ -693,6 +706,31 @@ ClientReport_getEntryId(ClientReport self);
bool bool
ClientReport_hasTimestamp(ClientReport self); ClientReport_hasTimestamp(ClientReport self);
bool
ClientReport_hasSeqNum(ClientReport self);
uint16_t
ClientReport_getSeqNum(ClientReport self);
bool
ClientReport_hasDataSetName(ClientReport self);
bool
ClientReport_hasReasonForInclusion(ClientReport self);
bool
ClientReport_hasConfRev(ClientReport self);
uint32_t
ClientReport_getConfRev(ClientReport self);
bool
ClientReport_hasBufOvfl(ClientReport self);
bool
ClientReport_hasDataReference(ClientReport self);
/** /**
* \brief get the timestamp of the report * \brief get the timestamp of the report
* *
@ -721,7 +759,7 @@ ReasonForInclusion_getValueAsString(ReasonForInclusion reasonCode);
**************************************************/ **************************************************/
ClientReportControlBlock ClientReportControlBlock
ClientReportControlBlock_create(char* dataAttributeReference); ClientReportControlBlock_create(char* rcbReference);
void void
ClientReportControlBlock_destroy(ClientReportControlBlock self); ClientReportControlBlock_destroy(ClientReportControlBlock self);
@ -856,7 +894,7 @@ ClientReportControlBlock_getOwner(ClientReportControlBlock self);
* \return the MmsValue instance of the received value or NULL if the request failed * \return the MmsValue instance of the received value or NULL if the request failed
*/ */
MmsValue* MmsValue*
IedConnection_readObject(IedConnection self, IedClientError* error, char* dataAttributeReference, FunctionalConstraint fc); IedConnection_readObject(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc);
/** /**
* \brief write a functional constrained data attribute (FCDA) or functional constrained data (FCD). * \brief write a functional constrained data attribute (FCDA) or functional constrained data (FCD).
@ -868,7 +906,7 @@ IedConnection_readObject(IedConnection self, IedClientError* error, char* dataAt
* \param value the MmsValue to write (has to be of the correct type - MMS_STRUCTURE for FCD) * \param value the MmsValue to write (has to be of the correct type - MMS_STRUCTURE for FCD)
*/ */
void void
IedConnection_writeObject(IedConnection self, IedClientError* error, char* dataAttributeReference, FunctionalConstraint fc, IedConnection_writeObject(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc,
MmsValue* value); MmsValue* value);
@ -881,7 +919,7 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, char* dataA
* \param fc the functional constraint of the data attribute to read * \param fc the functional constraint of the data attribute to read
*/ */
bool bool
IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readBooleanValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief read a functional constrained data attribute (FCDA) of type float * \brief read a functional constrained data attribute (FCDA) of type float
@ -892,7 +930,7 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char*
* \param fc the functional constraint of the data attribute to read * \param fc the functional constraint of the data attribute to read
*/ */
float float
IedConnection_readFloatValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readFloatValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief read a functional constrained data attribute (FCDA) of type VisibleString or MmsString * \brief read a functional constrained data attribute (FCDA) of type VisibleString or MmsString
@ -907,7 +945,7 @@ IedConnection_readFloatValue(IedConnection self, IedClientError* error, char* ob
* \return a C string representation of the value. Has to be freed by the caller! * \return a C string representation of the value. Has to be freed by the caller!
*/ */
char* char*
IedConnection_readStringValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readStringValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief read a functional constrained data attribute (FCDA) of type Integer or Unsigned and return the result as int32_t * \brief read a functional constrained data attribute (FCDA) of type Integer or Unsigned and return the result as int32_t
@ -920,7 +958,7 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, char* o
* \return an int32_t value of the read data attributes * \return an int32_t value of the read data attributes
*/ */
int32_t int32_t
IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readInt32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief read a functional constrained data attribute (FCDA) of type Integer or Unsigned and return the result as uint32_t * \brief read a functional constrained data attribute (FCDA) of type Integer or Unsigned and return the result as uint32_t
@ -933,7 +971,7 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* ob
* \return an uint32_t value of the read data attributes * \return an uint32_t value of the read data attributes
*/ */
uint32_t uint32_t
IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief read a functional constrained data attribute (FCDA) of type Timestamp (UTC Time) * \brief read a functional constrained data attribute (FCDA) of type Timestamp (UTC Time)
@ -950,7 +988,7 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, cha
* \return the timestamp value * \return the timestamp value
*/ */
Timestamp* Timestamp*
IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc, IedConnection_readTimestampValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc,
Timestamp* timeStamp); Timestamp* timeStamp);
/** /**
@ -964,7 +1002,7 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char
* \return the timestamp value * \return the timestamp value
*/ */
Quality Quality
IedConnection_readQualityValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc); IedConnection_readQualityValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
/** /**
* \brief write a functional constrained data attribute (FCDA) of type boolean * \brief write a functional constrained data attribute (FCDA) of type boolean
@ -976,7 +1014,7 @@ IedConnection_readQualityValue(IedConnection self, IedClientError* error, char*
* \param value the boolean value to write * \param value the boolean value to write
*/ */
void void
IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, bool value); FunctionalConstraint fc, bool value);
/** /**
@ -989,7 +1027,7 @@ IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char*
* \param value the int32_t value to write * \param value the int32_t value to write
*/ */
void void
IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, int32_t value); FunctionalConstraint fc, int32_t value);
/** /**
@ -1002,7 +1040,7 @@ IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* o
* \param value the uint32_t value to write * \param value the uint32_t value to write
*/ */
void void
IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint32_t value); FunctionalConstraint fc, uint32_t value);
/** /**
@ -1015,15 +1053,15 @@ IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, ch
* \param value the float value to write * \param value the float value to write
*/ */
void void
IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeFloatValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, float value); FunctionalConstraint fc, float value);
void void
IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, char* value); FunctionalConstraint fc, char* value);
void void
IedConnection_writeOctetString(IedConnection self, IedClientError* error, char* objectReference, IedConnection_writeOctetString(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint8_t* value, int valueLength); FunctionalConstraint fc, uint8_t* value, int valueLength);
/** @} */ /** @} */
@ -1050,7 +1088,7 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, char*
* \return data set instance with retrieved values of NULL if an error occurred. * \return data set instance with retrieved values of NULL if an error occurred.
*/ */
ClientDataSet ClientDataSet
IedConnection_readDataSetValues(IedConnection self, IedClientError* error, char* dataSetReference, ClientDataSet dataSet); IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet);
/** /**
* \brief create a new data set at the connected server device * \brief create a new data set at the connected server device
@ -1068,7 +1106,7 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, char*
* *
*/ */
void void
IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dataSetReference, LinkedList /* char* */ dataSetElements); IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements);
/** /**
* \brief delete a deletable data set at the connected server device * \brief delete a deletable data set at the connected server device
@ -1082,7 +1120,7 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dat
* \param dataSetReference object reference of the data set * \param dataSetReference object reference of the data set
*/ */
void void
IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dataSetReference); IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference);
/** /**
@ -1100,7 +1138,7 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dat
* \return LinkedList containing the data set elements as char* strings. * \return LinkedList containing the data set elements as char* strings.
*/ */
LinkedList /* <char*> */ LinkedList /* <char*> */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, char* dataSetReference, bool* isDeletable); IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable);
/******************************************************** /********************************************************
* Data set object (local representation of a data set) * Data set object (local representation of a data set)
@ -1212,7 +1250,7 @@ void
ControlObjectClient_setTestMode(ControlObjectClient self); ControlObjectClient_setTestMode(ControlObjectClient self);
void void
ControlObjectClient_setOrigin(ControlObjectClient self, char* orIdent, int orCat); ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat);
void void
ControlObjectClient_enableInterlockCheck(ControlObjectClient self); ControlObjectClient_enableInterlockCheck(ControlObjectClient self);
@ -1222,13 +1260,17 @@ ControlObjectClient_enableSynchroCheck(ControlObjectClient self);
/** /**
* \brief handler that is invoked when a command termination message is received * \brief Private a callback handler that is invoked when a command termination message is received.
*
* This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received.
* To distinguish between a CommandTermination+ and CommandTermination- please use the
* ControlObjectClient_getLastApplError function.
* *
* \param self the ControlObjectClient instance * \param self the ControlObjectClient instance
* \param handler the callback function to be used * \param handler the callback function to be used
* \param handlerParameter an arbitrary parameter that is passed to the handler * \param handlerParameter an arbitrary parameter that is passed to the handler
*/ */
typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient connection); typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient controlClient);
void void
ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, CommandTerminationHandler handler, ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, CommandTerminationHandler handler,
@ -1301,7 +1343,7 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool
* \return LinkedList with string elements representing the logical node names * \return LinkedList with string elements representing the logical node names
*/ */
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, char* logicalDeviceName); IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName);
typedef enum { typedef enum {
ACSI_CLASS_DATA_OBJECT, ACSI_CLASS_DATA_OBJECT,
@ -1332,7 +1374,7 @@ typedef enum {
*/ */
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
char* logicalNodeReference); const char* logicalNodeReference);
/** /**
* \brief returns the directory of the given logical node (LN) containing elements of the specified ACSI class * \brief returns the directory of the given logical node (LN) containing elements of the specified ACSI class
@ -1349,7 +1391,7 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
*/ */
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* logicalNodeReference, ACSIClass acsiClass); const char* logicalNodeReference, ACSIClass acsiClass);
/** /**
* \brief returns the directory of the given data object (DO) * \brief returns the directory of the given data object (DO)
@ -1365,8 +1407,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
* *
*/ */
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getDataDirectory(IedConnection self, IedClientError* error, IedConnection_getDataDirectory(IedConnection self, IedClientError* error, const char* dataReference);
char* dataReference);
/** /**
* \brief returns the directory of the given data object (DO) * \brief returns the directory of the given data object (DO)
@ -1383,8 +1424,25 @@ IedConnection_getDataDirectory(IedConnection self, IedClientError* error,
* *
*/ */
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, const char* dataReference);
char* dataReference);
/**
* \brief returns the directory of the given data object/data attribute with the given FC
*
* Implementation of the GetDataDirectory ACSI service. This will return the list of
* C strings with all data attributes or sub data objects as elements that have the given FX. The returned
* strings will contain the functional constraint appended in square brackets when appropriate.
*
* \param self the connection object
* \param error the error code if an error occurs
* \param dataReference string that represents the DO reference
* \param fc the functional constraint
*
* \return list of all data attributes or sub data objects as C strings in a LinkedList
*
*/
LinkedList
IedConnection_getDataDirectoryByFC(IedConnection self, IedClientError* error, const char* dataReference, FunctionalConstraint fc);
/** /**
* \brief return the MMS variable type specification of the data attribute referenced by dataAttributeReference and function constraint fc. * \brief return the MMS variable type specification of the data attribute referenced by dataAttributeReference and function constraint fc.
@ -1401,7 +1459,7 @@ IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error,
* *
*/ */
MmsVariableSpecification* MmsVariableSpecification*
IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, char* dataAttributeReference, IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* dataAttributeReference,
FunctionalConstraint fc); FunctionalConstraint fc);
/** @} */ /** @} */
@ -1415,7 +1473,7 @@ IedConnection_getVariableSpecification(IedConnection self, IedClientError* error
typedef struct sFileDirectoryEntry* FileDirectoryEntry; typedef struct sFileDirectoryEntry* FileDirectoryEntry;
FileDirectoryEntry FileDirectoryEntry
FileDirectoryEntry_create(char* fileName, uint32_t fileSize, uint64_t lastModified); FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified);
void void
FileDirectoryEntry_destroy(FileDirectoryEntry self); FileDirectoryEntry_destroy(FileDirectoryEntry self);
@ -1435,6 +1493,13 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self);
* *
* Requires the server to support file services. * Requires the server to support file services.
* *
* NOTE: the returned linked list has to be freed by the user. You can user the following statement
* to free the list of directory entries:
*
* LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy);
*
* where fileNames is the return value of this function.
*
* \param self the connection object * \param self the connection object
* \param error the error code if an error occurs * \param error the error code if an error occurs
* \param directoryName the name of the directory or NULL to get the entries of the root directory * \param directoryName the name of the directory or NULL to get the entries of the root directory
@ -1442,7 +1507,7 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self);
* \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements * \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements
*/ */
LinkedList /*<FileDirectoryEntry>*/ LinkedList /*<FileDirectoryEntry>*/
IedConnection_getFileDirectory(IedConnection self, IedClientError* error, char* directoryName); IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* directoryName);
/** /**
* \brief user provided handler to receive the data of the GetFile request * \brief user provided handler to receive the data of the GetFile request
@ -1473,7 +1538,7 @@ typedef bool
* \return number of bytes received * \return number of bytes received
*/ */
uint32_t uint32_t
IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName, IedClientGetFileHandler handler, IedConnection_getFile(IedConnection self, IedClientError* error, const char* fileName, IedClientGetFileHandler handler,
void* handlerParameter); void* handlerParameter);
/** /**
@ -1486,7 +1551,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
* \param fileName the name of the file to delete * \param fileName the name of the file to delete
*/ */
void void
IedConnection_deleteFile(IedConnection self, IedClientError* error, char* fileName); IedConnection_deleteFile(IedConnection self, IedClientError* error, const char* fileName);
/** @} */ /** @} */

@ -124,6 +124,45 @@ extern "C" {
/** @} */ /** @} */
/**
* @defgroup CONTROL_ADD_CAUSE Definition for addCause type - used in control models
*
* @{
*/
/** AddCause - additional cause information for control model errors */
typedef enum {
ADD_CAUSE_UNKNOWN = 0,
ADD_CAUSE_NOT_SUPPORTED = 1,
ADD_CAUSE_BLOCKED_BY_SWITCHING_HIERARCHY = 2,
ADD_CAUSE_SELECT_FAILED = 3,
ADD_CAUSE_INVALID_POSITION = 4,
ADD_CAUSE_POSITION_REACHED = 5,
ADD_CAUSE_PARAMETER_CHANGE_IN_EXECUTION = 6,
ADD_CAUSE_STEP_LIMIT = 7,
ADD_CAUSE_BLOCKED_BY_MODE = 8,
ADD_CAUSE_BLOCKED_BY_PROCESS = 9,
ADD_CAUSE_BLOCKED_BY_INTERLOCKING = 10,
ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK = 11,
ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION = 12,
ADD_CAUSE_BLOCKED_BY_HEALTH = 13,
ADD_CAUSE_1_OF_N_CONTROL = 14,
ADD_CAUSE_ABORTION_BY_CANCEL = 15,
ADD_CAUSE_TIME_LIMIT_OVER = 16,
ADD_CAUSE_ABORTION_BY_TRIP = 17,
ADD_CAUSE_OBJECT_NOT_SELECTED = 18,
ADD_CAUSE_OBJECT_ALREADY_SELECTED = 19,
ADD_CAUSE_NO_ACCESS_AUTHORITY = 20,
ADD_CAUSE_ENDED_WITH_OVERSHOOT = 21,
ADD_CAUSE_ABORTION_DUE_TO_DEVIATION = 22,
ADD_CAUSE_ABORTION_BY_COMMUNICATION_LOSS = 23,
ADD_CAUSE_ABORTION_BY_COMMAND = 24,
ADD_CAUSE_NONE = 25,
ADD_CAUSE_INCONSISTENT_PARAMETERS = 26,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT = 27
} ControlAddCause;
/** @} */
/** /**
* @defgroup FUNCTIONAL_CONSTRAINTS Definitions and functions related to functional constraints (FCs) * @defgroup FUNCTIONAL_CONSTRAINTS Definitions and functions related to functional constraints (FCs)
@ -227,6 +266,43 @@ Quality_fromMmsValue(MmsValue* mmsValue);
/** @} */ /** @} */
/**
* @defgroup DBPOS Definitions and functions related to IEC 61850 Dbpos (a CODED ENUM) data type
*
* @{
*/
typedef enum {
DBPOS_INTERMEDIATE_STATE = 0,
DBPOS_OFF = 1,
DBPOS_ON = 2,
DBPOS_BAD_STATE = 3
} Dbpos;
/**
* \brief convert MMS bit string to Dbpos enumeration type
*
* \param mmsValue the MmsValue instance representing the Dbpos value
*
* \return the corresponding Dbpos value
*/
Dbpos
Dbpos_fromMmsValue(MmsValue* mmsValue);
/**
* \brief conver Dbpos to MMS bit string
*
* \param mmsValue the MmsValue instance representing a Dbpos value or NULL to create a new MmsValue instance
* \param a Dbpos value
*
* \return the corresponding MmsValue instance
*/
MmsValue*
Dbpos_toMmsValue(MmsValue* mmsValue, Dbpos dbpos);
/** @} */
/** /**
* @defgroup TIMESTAMP Definitions and functions related to IEC 61850 Timestamp (UTC Time) data type * @defgroup TIMESTAMP Definitions and functions related to IEC 61850 Timestamp (UTC Time) data type
* *

@ -28,6 +28,7 @@
extern "C" { extern "C" {
#endif #endif
#include "hal_filesystem.h"
/** \addtogroup server_api_group /** \addtogroup server_api_group
* @{ * @{

@ -24,8 +24,8 @@
#ifndef DYNAMIC_MODEL_H_ #ifndef DYNAMIC_MODEL_H_
#define DYNAMIC_MODEL_H_ #define DYNAMIC_MODEL_H_
#include "model.h" #include "iec61850_model.h"
#include "cdc.h" #include "iec61850_cdc.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -146,6 +146,20 @@ ReportControlBlock*
ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char* ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char*
dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd); dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd);
/**
* \brief create a setting group control block (SGCB)
*
* Create a new setting group control block (SGCB) and add it to the given logical node (LN).
*
* \param parent the parent LN.
* \param the active setting group on server startup (1..N)
* \param the number of setting groups (N)
*
* \return the new SGCB instance
*/
SettingGroupControlBlock*
SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numOfSGs);
/** /**
* \brief create a new GSE/GOOSE control block (GoCB) * \brief create a new GSE/GOOSE control block (GoCB)
* *

@ -1,7 +1,7 @@
/* /*
* model.h * model.h
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -72,6 +72,12 @@ typedef struct sIedModel IedModel;
typedef struct sDataSet DataSet; typedef struct sDataSet DataSet;
typedef struct sReportControlBlock ReportControlBlock; typedef struct sReportControlBlock ReportControlBlock;
/**
* \brief IEC 61850 data model of setting group control block (SGCB)
*/
typedef struct sSettingGroupControlBlock SettingGroupControlBlock;
typedef struct sGSEControlBlock GSEControlBlock; typedef struct sGSEControlBlock GSEControlBlock;
@ -121,6 +127,7 @@ struct sIedModel {
DataSet* dataSets; DataSet* dataSets;
ReportControlBlock* rcbs; ReportControlBlock* rcbs;
GSEControlBlock* gseCBs; GSEControlBlock* gseCBs;
SettingGroupControlBlock* sgcbs;
void (*initializer) (void); void (*initializer) (void);
}; };
@ -210,6 +217,20 @@ struct sReportControlBlock {
ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry */ ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry */
}; };
struct sSettingGroupControlBlock {
LogicalNode* parent;
uint8_t actSG; /* value from SCL file */
uint8_t numOfSGs; /* value from SCL file */
uint8_t editSG; /* 0 at power-up */
bool cnfEdit; /* false at power-up */
uint64_t timestamp;
uint16_t resvTms;
SettingGroupControlBlock* sibling; /* next control block in list or NULL if this is the last entry */
};
typedef struct { typedef struct {
uint8_t vlanPriority; uint8_t vlanPriority;
uint16_t vlanId; uint16_t vlanId;
@ -263,6 +284,18 @@ ModelNode_getChild(ModelNode* modelNode, const char* name);
char* char*
ModelNode_getObjectReference(ModelNode* node, char* objectReference); ModelNode_getObjectReference(ModelNode* node, char* objectReference);
/**
* \brief Set the name of the IED
*
* This will change the default name (usualy "TEMPLATE") to a user configured values.
* NOTE: This function has to be called before IedServer_create !
*
* \param model the IedModel instance
* \param the name of the configured IED
*/
void
IedModel_setIedName(IedModel* self, const char* iedName);
/** /**
* \brief Lookup a model node by its object reference * \brief Lookup a model node by its object reference
* *
@ -307,8 +340,26 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR
ModelNode* ModelNode*
IedModel_getModelNodeByShortAddress(IedModel* model, uint32_t shortAddress); IedModel_getModelNodeByShortAddress(IedModel* model, uint32_t shortAddress);
/**
* \brief Lookup a logical node by name that is part of the given logical device
*
* \param device the logical device instance
* \param lnName the logical node name
*
* \return the logical device instance or NULL if it does not exist
*/
LogicalNode* LogicalNode*
LogicalDevice_getLogicalNode(LogicalDevice* device, const char* nodeName); LogicalDevice_getLogicalNode(LogicalDevice* device, const char* lnName);
/**
* \brief Get the setting group control block (SGCB) of the logical device
*
* \param device the logical device instance
*
* \return the SGCB instance or NULL if no SGCB is available
*/
SettingGroupControlBlock*
LogicalDevice_getSettingGroupControlBlock(LogicalDevice* device);
/**@}*/ /**@}*/

@ -36,10 +36,10 @@ extern "C" {
*/ */
#include "mms_server.h" #include "mms_server.h"
#include "dynamic_model.h" #include "iec61850_dynamic_model.h"
#include "model.h" #include "iec61850_model.h"
#include "filesystem.h" #include "hal_filesystem.h"
#include "config_file_parser.h" #include "iec61850_config_file_parser.h"
/** /**
* An opaque handle for an IED server instance * An opaque handle for an IED server instance
@ -93,6 +93,53 @@ IedServer_start(IedServer self, int tcpPort);
void void
IedServer_stop(IedServer self); IedServer_stop(IedServer self);
/**
* \brief Start handling client connection for non-threaded mode
*
* This function will instruct the TCP(IP stack to listen for incoming connections.
* In order to accept new connection the function IedServer_processIncomingData has to
* be called periodically.
*
* \param self the instance of IedServer to operate on.
* \param tcpPort the TCP port the server is listening
*/
void
IedServer_startThreadless(IedServer self, int tcpPort);
int
IedServer_waitReady(IedServer self, unsigned int timeoutMs);
/**
* \brief handle incoming TCP data in non-threaded mode
*
* The function should be called periodically. If the function is called more often
* the response time for incoming messages will be faster. As an alternative the
* function may only be called if new TCP data is available.
*
* \param self the instance of IedServer to operate on.
*/
void
IedServer_processIncomingData(IedServer self);
/**
* \brief perform periodic background tasks in non-threaded mode
*
* The function should be called periodically. If the function is called more often
* the more accurate are internal timeouts (e.g. for reports).
*
* \param self the instance of IedServer to operate on.
*/
void
IedServer_performPeriodicTasks(IedServer self);
/**
* \brief Stop handling client connections for non-threaded mode
*
* \param self the instance of IedServer to operate on.
*/
void
IedServer_stopThreadless(IedServer self);
/** /**
* \brief Return the data model of the server * \brief Return the data model of the server
* *
@ -149,6 +196,17 @@ IedServer_getIsoServer(IedServer self);
void void
IedServer_enableGoosePublishing(IedServer self); IedServer_enableGoosePublishing(IedServer self);
/**
* \brief Disable all GOOSE control blocks.
*
* This will set the GoEna attribute of all configured GOOSE control blocks
* to false. This will stop GOOSE transmission.
*
* \param self the instance of IedServer to operate on.
*/
void
IedServer_disableGoosePublishing(IedServer self);
/**@}*/ /**@}*/
/** /**
@ -183,7 +241,7 @@ IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void
* \param self the ClientConnection instance * \param self the ClientConnection instance
* \return peer address as C string. * \return peer address as C string.
*/ */
char* const char*
ClientConnection_getPeerAddress(ClientConnection self); ClientConnection_getPeerAddress(ClientConnection self);
/** /**
@ -471,13 +529,130 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu
/**@}*/ /**@}*/
/** /**
* @defgroup IEC61850_SERVER_CONTROL Server side control model handling * @defgroup IEC61850_SERVER_SETTING_GROUPS Server side setting group handling
* *
* @{ * @{
*/ */
/**
* \brief Change active setting group
*
* Inform the IedServer that the active setting group has changed due to an internal event.
* Before calling this function the user should update the relevant data attributes with FC=SG!
*
* \param self the instance of IedServer to operate on.
* \param sgcb the handle of the setting group control block of the setting group
* \param newActiveSg the number of the new active setting group
*/
void
IedServer_changeActiveSettingGroup(IedServer self, SettingGroupControlBlock* sgcb, uint8_t newActiveSg);
/**
* \brief Get the active setting group number
*
* \param self the instance of IedServer to operate on.
* \param sgcb the handle of the setting group control block of the setting group
*
* \return the number of the active setting group
*/
uint8_t
IedServer_getActiveSettingGroup(IedServer self, SettingGroupControlBlock* sgcb);
/**
* \brief Callback handler that is invoked when the active setting group is about to be changed by an
* external client.
*
* This function is called BEFORE the active setting group is changed. The user can reject to change the
* active setting group by returning false.
*
* \param user provided parameter
* \param sgcb the setting group control block of the setting group that is about to be changed
* \param newActSg the new active setting group
* \param connection the client connection that requests the change
*
* \return true if the change is accepted, false otherwise.
*
*/
typedef bool (*ActiveSettingGroupChangedHandler) (void* parameter, SettingGroupControlBlock* sgcb,
uint8_t newActSg, ClientConnection connection);
/**
* \brief Set the callback handler for the SetActSG event
*
* \param self the instance of IedServer to operate on.
* \param sgcb the handle of the setting group control block of the setting group.
* \param handler the user provided callback handler.
* \param parameter a user provided parameter that is passed to the control handler.
*/
void
IedServer_setActiveSettingGroupChangedHandler(IedServer self, SettingGroupControlBlock* sgcb,
ActiveSettingGroupChangedHandler handler, void* parameter);
/**
* \brief Callback handler that is invoked when the edit setting group is about to be changed by an
* external client.
*
* In this function the user should update all SE data attributes
* associated with the given SettingGroupControlBlock.
* This function is called BEFORE the active setting group is changed. The user can reject to change the
* edit setting group by returning false. This can be used to implement RBAC.
*
* \param user provided parameter
* \param sgcb the setting group control block of the setting group that is about to be changed
* \param newEditSg the new edit setting group
* \param connection the client connection that requests the change
*
* \return true if the change is accepted, false otherwise.
*
*/
typedef bool (*EditSettingGroupChangedHandler) (void* parameter, SettingGroupControlBlock* sgcb,
uint8_t newEditSg, ClientConnection connection);
/**
* \brief Set the callback handler for the SetEditSG event
*
* \param self the instance of IedServer to operate on.
* \param sgcb the handle of the setting group control block of the setting group.
* \param handler the user provided callback handler.
* \param parameter a user provided parameter that is passed to the control handler.
*/
void
IedServer_setEditSettingGroupChangedHandler(IedServer self, SettingGroupControlBlock* sgcb,
EditSettingGroupChangedHandler handler, void* parameter);
/**
* \brief Callback handler that is invoked when the edit setting group has been confirmed by an
* external client.
*
* \param user provided parameter
* \param sgcb the setting group control block of the setting group that is about to be changed
* \param editSg the edit setting group that has been confirmed
*
*/
typedef void (*EditSettingGroupConfirmationHandler) (void* parameter, SettingGroupControlBlock* sgcb,
uint8_t editSg);
/**
* \brief Set the callback handler for the COnfEditSG event
*
* \param self the instance of IedServer to operate on.
* \param sgcb the handle of the setting group control block of the setting group.
* \param handler the user provided callback handler.
* \param parameter a user provided parameter that is passed to the control handler.
*/
void
IedServer_setEditSettingGroupConfirmationHandler(IedServer self, SettingGroupControlBlock* sgcb,
EditSettingGroupConfirmationHandler handler, void* parameter);
/**@}*/
/**
* @defgroup IEC61850_SERVER_CONTROL Server side control model handling
*
* @{
*/
/** /**
* \brief result code for ControlPerformCheckHandler * \brief result code for ControlPerformCheckHandler
@ -490,6 +665,15 @@ typedef enum {
CONTROL_OBJECT_UNDEFINED = 4 /** object not visible in this security context ??? */ CONTROL_OBJECT_UNDEFINED = 4 /** object not visible in this security context ??? */
} CheckHandlerResult; } CheckHandlerResult;
/**
* \brief result codes for control handler (ControlWaitForExecutionHandler and ControlHandler)
*/
typedef enum {
CONTROL_RESULT_FAILED = 0, /** check or operation failed */
CONTROL_RESULT_OK = 1, /** check or operation was successful */
CONTROL_RESULT_WAITING = 2 /** check or operation is in progress */
} ControlHandlerResult;
/** /**
* \brief Control model callback to perform the static tests (optional). * \brief Control model callback to perform the static tests (optional).
* *
@ -516,15 +700,19 @@ typedef CheckHandlerResult (*ControlPerformCheckHandler) (void* parameter, MmsVa
* a control operation has been invoked by the client. This callback function is * a control operation has been invoked by the client. This callback function is
* intended to perform the dynamic tests. It should check if the synchronization conditions * intended to perform the dynamic tests. It should check if the synchronization conditions
* are met if the synchroCheck parameter is set to true. * are met if the synchroCheck parameter is set to true.
* NOTE: Since version 0.7.9 this function is intended to return immediately. If the operation
* cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the
* handler will be invoked again later.
* *
* \param parameter the parameter that was specified when setting the control handler * \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation. * \param ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation * \param test indicates if the operate request is a test operation
* \param synchroCheck the synchroCheck parameter provided by the client * \param synchroCheck the synchroCheck parameter provided by the client
* *
* \return true if the dynamic tests had been successful, false otherwise * \return CONTROL_RESULT_OK if the dynamic tests had been successful, CONTROL_RESULT_FAILED otherwise,
* CONTROL_RESULT_WAITING if the test is not yet finished
*/ */
typedef bool (*ControlWaitForExecutionHandler) (void* parameter, MmsValue* ctlVal, bool test, bool synchroCheck); typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (void* parameter, MmsValue* ctlVal, bool test, bool synchroCheck);
/** /**
* \brief Control model callback to actually perform the control operation. * \brief Control model callback to actually perform the control operation.
@ -532,14 +720,18 @@ typedef bool (*ControlWaitForExecutionHandler) (void* parameter, MmsValue* ctlVa
* User provided callback function for the control model. It will be invoked when * User provided callback function for the control model. It will be invoked when
* a control operation happens (Oper). Here the user should perform the control operation * a control operation happens (Oper). Here the user should perform the control operation
* (e.g. by setting an digital output or switching a relay). * (e.g. by setting an digital output or switching a relay).
* NOTE: Since version 0.7.9 this function is intended to return immediately. If the operation
* cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the
* handler will be invoked again later.
* *
* \param parameter the parameter that was specified when setting the control handler * \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation. * \param ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation * \param test indicates if the operate request is a test operation
* *
* \return true if the control action bas been successful, false otherwise * \return CONTROL_RESULT_OK if the control action bas been successful, CONTROL_RESULT_FAILED otherwise,
* CONTROL_RESULT_WAITING if the test is not yet finished
*/ */
typedef bool (*ControlHandler) (void* parameter, MmsValue* ctlVal, bool test); typedef ControlHandlerResult (*ControlHandler) (void* parameter, MmsValue* ctlVal, bool test);
/** /**
* \brief Set control handler for controllable data object * \brief Set control handler for controllable data object

@ -24,7 +24,7 @@
#ifndef CONTROL_H_ #ifndef CONTROL_H_
#define CONTROL_H_ #define CONTROL_H_
#include "model.h" #include "iec61850_model.h"
#include "mms_server_connection.h" #include "mms_server_connection.h"
#include "mms_device_model.h" #include "mms_device_model.h"
#include "iec61850_server.h" #include "iec61850_server.h"
@ -91,9 +91,6 @@ ControlObject_select(ControlObject* self, MmsServerConnection* connection);
bool bool
ControlObject_unselect(ControlObject* self, MmsServerConnection* connection); ControlObject_unselect(ControlObject* self, MmsServerConnection* connection);
bool
ControlObject_operate(ControlObject* self, MmsValue* value, uint64_t currentTime, bool testCondition);
void void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter); ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter);

@ -28,7 +28,7 @@
#define DEBUG_IED_CLIENT 0 #define DEBUG_IED_CLIENT 0
#endif #endif
#include "thread.h" #include "hal_thread.h"
struct sIedConnection struct sIedConnection
{ {
@ -38,10 +38,12 @@ struct sIedConnection
LinkedList logicalDevices; LinkedList logicalDevices;
LinkedList clientControls; LinkedList clientControls;
LastApplError lastApplError; LastApplError lastApplError;
Semaphore stateMutex; Semaphore stateMutex;
IedConnectionClosedHandler connectionCloseHandler; IedConnectionClosedHandler connectionCloseHandler;
void* connectionClosedParameter; void* connectionClosedParameter;
uint32_t connectionTimeout;
}; };
struct sClientReportControlBlock { struct sClientReportControlBlock {

@ -31,6 +31,7 @@
#define ALLOW_WRITE_ACCESS_CF 2 #define ALLOW_WRITE_ACCESS_CF 2
#define ALLOW_WRITE_ACCESS_SP 4 #define ALLOW_WRITE_ACCESS_SP 4
#define ALLOW_WRITE_ACCESS_SV 8 #define ALLOW_WRITE_ACCESS_SV 8
#define ALLOW_WRITE_ACCESS_SE 16
struct sIedServer struct sIedServer
{ {

@ -1,7 +1,7 @@
/* /*
* mms_mapping.h * mms_mapping.h
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -24,7 +24,7 @@
#ifndef MMS_MAPPING_H_ #ifndef MMS_MAPPING_H_
#define MMS_MAPPING_H_ #define MMS_MAPPING_H_
#include "model.h" #include "iec61850_model.h"
#include "mms_server_connection.h" #include "mms_server_connection.h"
#include "mms_device_model.h" #include "mms_device_model.h"
#include "control.h" #include "control.h"
@ -44,6 +44,27 @@ MmsMapping_create(IedModel* model);
MmsDevice* MmsDevice*
MmsMapping_getMmsDeviceModel(MmsMapping* mapping); MmsMapping_getMmsDeviceModel(MmsMapping* mapping);
void
MmsMapping_configureSettingGroups(MmsMapping* self);
void
MmsMapping_checkForSettingGroupReservationTimeouts(MmsMapping* self, uint64_t currentTime);
void
MmsMapping_setSgChangedHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
ActiveSettingGroupChangedHandler handler, void* parameter);
void
MmsMapping_setEditSgChangedHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
EditSettingGroupChangedHandler handler, void* parameter);
void
MmsMapping_setConfirmEditSgHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
EditSettingGroupConfirmationHandler handler, void* parameter);
void
MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock* sgcb, uint8_t newActiveSg);
void void
MmsMapping_setMmsServer(MmsMapping* self, MmsServer server); MmsMapping_setMmsServer(MmsMapping* self, MmsServer server);
@ -68,18 +89,21 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value);
void void
MmsMapping_enableGoosePublishing(MmsMapping* self); MmsMapping_enableGoosePublishing(MmsMapping* self);
void
MmsMapping_disableGoosePublishing(MmsMapping* self);
char* char*
MmsMapping_getMmsDomainFromObjectReference(char* objectReference, char* buffer); MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer);
void void
MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject); MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject);
char* char*
MmsMapping_createMmsVariableNameFromObjectReference(char* objectReference, FunctionalConstraint fc, char* buffer); MmsMapping_createMmsVariableNameFromObjectReference(const char* objectReference, FunctionalConstraint fc, char* buffer);
void void
MmsMapping_addObservedAttribute(MmsMapping* self, DataAttribute* dataAttribute, MmsMapping_addObservedAttribute(MmsMapping* self, DataAttribute* dataAttribute,
void* handler); AttributeChangedHandler handler);
char* char*
MmsMapping_getNextNameElement(char* name); MmsMapping_getNextNameElement(char* name);

@ -24,7 +24,7 @@
#ifndef MMS_MAPPING_INTERNAL_H_ #ifndef MMS_MAPPING_INTERNAL_H_
#define MMS_MAPPING_INTERNAL_H_ #define MMS_MAPPING_INTERNAL_H_
#include "thread.h" #include "hal_thread.h"
#include "linked_list.h" #include "linked_list.h"
struct sMmsMapping { struct sMmsMapping {
@ -37,9 +37,15 @@ struct sMmsMapping {
LinkedList observedObjects; LinkedList observedObjects;
LinkedList attributeAccessHandlers; LinkedList attributeAccessHandlers;
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
LinkedList settingGroups;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
bool reportThreadRunning; bool reportThreadRunning;
bool reportThreadFinished; bool reportThreadFinished;
Thread reportWorkerThread; Thread reportWorkerThread;
#endif
IedServer iedServer; IedServer iedServer;

@ -74,7 +74,10 @@ typedef struct {
int triggerOps; int triggerOps;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore createNotificationsMutex; /* { covered by mutex } */ Semaphore createNotificationsMutex; /* { covered by mutex } */
#endif
ReportInclusionFlag* inclusionFlags; /* { covered by mutex } */ ReportInclusionFlag* inclusionFlags; /* { covered by mutex } */
bool triggered; /* { covered by mutex } */ bool triggered; /* { covered by mutex } */
uint64_t reportTime; /* { covered by mutex } */ uint64_t reportTime; /* { covered by mutex } */

@ -26,12 +26,16 @@
#include "mms_mapping.h" #include "mms_mapping.h"
#include "control.h" #include "control.h"
#include "stack_config.h" #include "stack_config.h"
#include "thread.h" #include "hal_thread.h"
#include "ied_server_private.h" #include "ied_server_private.h"
struct sClientConnection { struct sClientConnection {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore tasksCountMutex; Semaphore tasksCountMutex;
#endif
int tasksCount; int tasksCount;
void* serverConnectionHandle; void* serverConnectionHandle;
}; };
@ -39,9 +43,12 @@ struct sClientConnection {
ClientConnection ClientConnection
private_ClientConnection_create(void* serverConnectionHandle) private_ClientConnection_create(void* serverConnectionHandle)
{ {
ClientConnection self = (ClientConnection) malloc(sizeof(struct sClientConnection)); ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection));
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->tasksCountMutex = Semaphore_create(1); self->tasksCountMutex = Semaphore_create(1);
#endif
self->tasksCount = 0; self->tasksCount = 0;
self->serverConnectionHandle = serverConnectionHandle; self->serverConnectionHandle = serverConnectionHandle;
@ -51,8 +58,11 @@ private_ClientConnection_create(void* serverConnectionHandle)
void void
private_ClientConnection_destroy(ClientConnection self) private_ClientConnection_destroy(ClientConnection self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->tasksCountMutex); Semaphore_destroy(self->tasksCountMutex);
free(self); #endif
GLOBAL_FREEMEM(self);
} }
int int
@ -60,9 +70,15 @@ private_ClientConnection_getTasksCount(ClientConnection self)
{ {
int tasksCount; int tasksCount;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex); Semaphore_wait(self->tasksCountMutex);
#endif
tasksCount = self->tasksCount; tasksCount = self->tasksCount;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex); Semaphore_post(self->tasksCountMutex);
#endif
return tasksCount; return tasksCount;
} }
@ -70,17 +86,29 @@ private_ClientConnection_getTasksCount(ClientConnection self)
void void
private_ClientConnection_increaseTasksCount(ClientConnection self) private_ClientConnection_increaseTasksCount(ClientConnection self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex); Semaphore_wait(self->tasksCountMutex);
#endif
self->tasksCount++; self->tasksCount++;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex); Semaphore_post(self->tasksCountMutex);
#endif
} }
void void
private_ClientConnection_decreaseTasksCount(ClientConnection self) private_ClientConnection_decreaseTasksCount(ClientConnection self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex); Semaphore_wait(self->tasksCountMutex);
#endif
self->tasksCount--; self->tasksCount--;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex); Semaphore_post(self->tasksCountMutex);
#endif
} }
void* void*
@ -90,7 +118,7 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self)
} }
char* const char*
ClientConnection_getPeerAddress(ClientConnection self) ClientConnection_getPeerAddress(ClientConnection self)
{ {
MmsServerConnection* mmsConnection = (MmsServerConnection*) self->serverConnectionHandle; MmsServerConnection* mmsConnection = (MmsServerConnection*) self->serverConnectionHandle;

@ -23,10 +23,11 @@
#include "iec61850_server.h" #include "iec61850_server.h"
#include "mms_mapping.h" #include "mms_mapping.h"
#include "mms_mapping_internal.h"
#include "control.h" #include "control.h"
#include "stack_config.h" #include "stack_config.h"
#include "ied_server_private.h" #include "ied_server_private.h"
#include "thread.h" #include "hal_thread.h"
#include "reporting.h" #include "reporting.h"
#ifndef DEBUG_IED_SERVER #ifndef DEBUG_IED_SERVER
@ -178,7 +179,7 @@ createMmsServerCache(IedServer self)
MmsServer_insertIntoCache(self->mmsServer, logicalDevice, variableName, defaultValue); MmsServer_insertIntoCache(self->mmsServer, logicalDevice, variableName, defaultValue);
free(variableName); GLOBAL_FREEMEM(variableName);
} }
} }
} }
@ -199,13 +200,15 @@ installDefaultValuesForDataAttribute(IedServer self, DataAttribute* dataAttribut
char domainName[65]; char domainName[65];
MmsMapping_getMmsDomainFromObjectReference(objectReference, domainName); strncpy(domainName, self->model->name, 64);
MmsMapping_getMmsDomainFromObjectReference(objectReference, domainName + strlen(domainName));
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName); MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName);
if (domain == NULL) { if (domain == NULL) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Error domain (%s) not found!\n", domainName); printf("Error domain (%s) not found for %s!\n", domainName, objectReference);
return; return;
} }
@ -214,7 +217,19 @@ installDefaultValuesForDataAttribute(IedServer self, DataAttribute* dataAttribut
dataAttribute->mmsValue = cacheValue; dataAttribute->mmsValue = cacheValue;
if (value != NULL) { if (value != NULL) {
if (cacheValue != NULL)
MmsValue_update(cacheValue, value); MmsValue_update(cacheValue, value);
#if (DEBUG_IED_SERVER == 1)
if (cacheValue == NULL) {
printf("IED_SERVER: exception: invalid initializer for %s\n", mmsVariableName);
exit(-1);
//TODO else call exception handler
}
#endif
MmsValue_delete(value); MmsValue_delete(value);
} }
@ -304,7 +319,12 @@ updateDataSetsWithCachedValues(IedServer self)
while (dataSetEntry != NULL) { while (dataSetEntry != NULL) {
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, dataSetEntry->logicalDeviceName); char domainName[65];
strncpy(domainName, self->model->name, 64);
strncat(domainName, dataSetEntry->logicalDeviceName, 64);
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName);
MmsValue* value = MmsServer_getValueFromCache(self->mmsServer, domain, dataSetEntry->variableName); MmsValue* value = MmsServer_getValueFromCache(self->mmsServer, domain, dataSetEntry->variableName);
@ -329,7 +349,7 @@ updateDataSetsWithCachedValues(IedServer self)
IedServer IedServer
IedServer_create(IedModel* iedModel) IedServer_create(IedModel* iedModel)
{ {
IedServer self = (IedServer) calloc(1, sizeof(struct sIedServer)); IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer));
self->model = iedModel; self->model = iedModel;
@ -357,27 +377,29 @@ IedServer_create(IedModel* iedModel)
self->clientConnections = LinkedList_create(); self->clientConnections = LinkedList_create();
/* default write access policy allows access to SP and SV FCDAs but denies access to DC and CF FCDAs */ /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */
self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV; self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE;
#if (CONFIG_IEC61850_REPORT_SERVICE == 1) #if (CONFIG_IEC61850_REPORT_SERVICE == 1)
Reporting_activateBufferedReports(self->mmsMapping); Reporting_activateBufferedReports(self->mmsMapping);
#endif #endif
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
MmsMapping_configureSettingGroups(self->mmsMapping);
#endif
return self; return self;
} }
void void
IedServer_destroy(IedServer self) IedServer_destroy(IedServer self)
{ {
//TODO wait for control and other threads to finish??
MmsServer_destroy(self->mmsServer); MmsServer_destroy(self->mmsServer);
IsoServer_destroy(self->isoServer); IsoServer_destroy(self->isoServer);
MmsMapping_destroy(self->mmsMapping); MmsMapping_destroy(self->mmsMapping);
LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy); LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy);
free(self); GLOBAL_FREEMEM(self);
} }
void void
@ -398,12 +420,63 @@ IedServer_getIsoServer(IedServer self)
return self->isoServer; return self->isoServer;
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
#if (CONFIG_MMS_SINGLE_THREADED == 1)
static void
singleThreadedServerThread(void* parameter)
{
IedServer self = (IedServer) parameter;
MmsMapping* mmsMapping = self->mmsMapping;
bool running = true;
mmsMapping->reportThreadFinished = false;
mmsMapping->reportThreadRunning = true;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: server thread started!\n");
while (running) {
if (IedServer_waitReady(self, 100) > 0) {
MmsServer_handleIncomingMessages(self->mmsServer);
IedServer_performPeriodicTasks(self);
}
else {
IedServer_performPeriodicTasks(self);
}
Thread_sleep(1);
running = mmsMapping->reportThreadRunning;
}
if (DEBUG_IED_SERVER)
printf("IED_SERVER: server thread finished!\n");
mmsMapping->reportThreadFinished = true;
}
#endif /* (CONFIG_MMS_SINGLE_THREADED == 1) */
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
#if (CONFIG_MMS_THREADLESS_STACK != 1)
void void
IedServer_start(IedServer self, int tcpPort) IedServer_start(IedServer self, int tcpPort)
{ {
#if (CONFIG_MMS_SINGLE_THREADED == 1)
MmsServer_startListeningThreadless(self->mmsServer, tcpPort);
Thread serverThread = Thread_create((ThreadExecutionFunction) singleThreadedServerThread, (void*) self, true);
Thread_start(serverThread);
#else
MmsServer_startListening(self->mmsServer, tcpPort); MmsServer_startListening(self->mmsServer, tcpPort);
MmsMapping_startEventWorkerThread(self->mmsMapping); MmsMapping_startEventWorkerThread(self->mmsMapping);
#endif
} }
#endif
bool bool
IedServer_isRunning(IedServer self) IedServer_isRunning(IedServer self)
@ -420,12 +493,43 @@ IedServer_getDataModel(IedServer self)
return self->model; return self->model;
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
void void
IedServer_stop(IedServer self) IedServer_stop(IedServer self)
{ {
MmsMapping_stopEventWorkerThread(self->mmsMapping); MmsMapping_stopEventWorkerThread(self->mmsMapping);
#if (CONFIG_MMS_SINGLE_THREADED == 1)
MmsServer_stopListeningThreadless(self->mmsServer);
#else
MmsServer_stopListening(self->mmsServer); MmsServer_stopListening(self->mmsServer);
#endif
}
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
void
IedServer_startThreadless(IedServer self, int tcpPort)
{
MmsServer_startListeningThreadless(self->mmsServer, tcpPort);
}
int
IedServer_waitReady(IedServer self, unsigned int timeoutMs)
{
return MmsServer_waitReady(self->mmsServer, timeoutMs);
}
void
IedServer_processIncomingData(IedServer self)
{
MmsServer_handleIncomingMessages(self->mmsServer);
}
void
IedServer_stopThreadless(IedServer self)
{
MmsServer_stopListeningThreadless(self->mmsServer);
} }
void void
@ -626,18 +730,33 @@ checkForUpdateTrigger(IedServer self, DataAttribute* dataAttribute)
static inline void static inline void
checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute) checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute)
{ {
#if (CONFIG_IEC61850_REPORT_SERVICE== 1) #if (CONFIG_IEC61850_REPORT_SERVICE == 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
if (dataAttribute->triggerOptions & TRG_OPT_DATA_CHANGED) if (dataAttribute->triggerOptions & TRG_OPT_DATA_CHANGED) {
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue, MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue,
REPORT_CONTROL_VALUE_CHANGED); REPORT_CONTROL_VALUE_CHANGED);
else if (dataAttribute->triggerOptions & TRG_OPT_QUALITY_CHANGED) #endif
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue);
#endif
}
else if (dataAttribute->triggerOptions & TRG_OPT_QUALITY_CHANGED) {
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue, MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue,
REPORT_CONTROL_QUALITY_CHANGED); REPORT_CONTROL_QUALITY_CHANGED);
#endif /* (CONFIG_IEC61850_REPORT_SERVICE== 1) */ #endif
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue); MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue);
#endif #endif
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE== 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
} }
void void
@ -834,13 +953,22 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu
} }
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
void void
IedServer_enableGoosePublishing(IedServer self) IedServer_enableGoosePublishing(IedServer self)
{ {
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_enableGoosePublishing(self->mmsMapping); MmsMapping_enableGoosePublishing(self->mmsMapping);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
} }
void
IedServer_disableGoosePublishing(IedServer self)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_disableGoosePublishing(self->mmsMapping);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
}
void void
IedServer_observeDataAttribute(IedServer self, DataAttribute* dataAttribute, IedServer_observeDataAttribute(IedServer self, DataAttribute* dataAttribute,
@ -866,6 +994,9 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo
case SV: case SV:
self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_SV; self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_SV;
break; break;
case SE:
self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_SE;
break;
default: /* ignore - request is invalid */ default: /* ignore - request is invalid */
break; break;
} }
@ -884,6 +1015,9 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo
case SV: case SV:
self->writeAccessPolicies &= ~ALLOW_WRITE_ACCESS_SV; self->writeAccessPolicies &= ~ALLOW_WRITE_ACCESS_SV;
break; break;
case SE:
self->writeAccessPolicies &= ALLOW_WRITE_ACCESS_SE;
break;
default: /* ignore - request is invalid */ default: /* ignore - request is invalid */
break; break;
} }
@ -942,13 +1076,59 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F
LogicalDevice* ld = (LogicalDevice*) ln->parent; LogicalDevice* ld = (LogicalDevice*) ln->parent;
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, ld->name); char domainName[65];
strncpy(domainName, self->model->name, 64);
strncat(domainName, ld->name, 64);
MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName);
MmsValue* value = MmsServer_getValueFromCache(self->mmsServer, domain, currentStart); MmsValue* value = MmsServer_getValueFromCache(self->mmsServer, domain, currentStart);
return value; return value;
} }
void
IedServer_changeActiveSettingGroup(IedServer self, SettingGroupControlBlock* sgcb, uint8_t newActiveSg)
{
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
MmsMapping_changeActiveSettingGroup(self->mmsMapping, sgcb, newActiveSg);
#endif
}
uint8_t
IedServer_getActiveSettingGroup(IedServer self, SettingGroupControlBlock* sgcb)
{
return sgcb->actSG;
}
void
IedServer_setActiveSettingGroupChangedHandler(IedServer self, SettingGroupControlBlock* sgcb,
ActiveSettingGroupChangedHandler handler, void* parameter)
{
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
MmsMapping_setSgChangedHandler(self->mmsMapping, sgcb, handler, parameter);
#endif
}
void
IedServer_setEditSettingGroupChangedHandler(IedServer self, SettingGroupControlBlock* sgcb,
EditSettingGroupChangedHandler handler, void* parameter)
{
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
MmsMapping_setEditSgChangedHandler(self->mmsMapping, sgcb, handler, parameter);
#endif
}
void
IedServer_setEditSettingGroupConfirmationHandler(IedServer self, SettingGroupControlBlock* sgcb,
EditSettingGroupConfirmationHandler handler, void* parameter)
{
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
MmsMapping_setConfirmEditSgHandler(self->mmsMapping, sgcb, handler, parameter);
#endif
}
ClientConnection ClientConnection
private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnectionHandle) private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnectionHandle)
{ {

@ -42,16 +42,6 @@
#define CONTROL_ERROR_TIMEOUT_TEST 2 #define CONTROL_ERROR_TIMEOUT_TEST 2
#define CONTROL_ERROR_OPERATOR_TEST 3 #define CONTROL_ERROR_OPERATOR_TEST 3
#define CONTROL_ADD_CAUSE_UNKNOWN 0
#define CONTROL_ADD_CAUSE_SELECT_FAILED 3
#define CONTROL_ADD_CAUSE_BLOCKED_BY_INTERLOCKING 10
#define CONTROL_ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK 11
#define CONTROL_ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION 12
#define CONTROL_ADD_CAUSE_OBJECT_NOT_SELECTED 18
#define CONTROL_ADD_CAUSE_OBJECT_ALREADY_SELECTED 19
#define CONTROL_ADD_CAUSE_INCONSISTENT_PARAMETERS 26
#define CONTROL_ADD_CAUSE_LOCKED_BY_OTHER_CLIENT 27
#define STATE_UNSELECTED 0 #define STATE_UNSELECTED 0
#define STATE_READY 1 #define STATE_READY 1
#define STATE_WAIT_FOR_ACTICATION_TIME 2 #define STATE_WAIT_FOR_ACTICATION_TIME 2
@ -67,7 +57,10 @@ struct sControlObject
char* name; char* name;
int state; int state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore stateLock; Semaphore stateLock;
#endif
MmsValue* mmsValue; MmsValue* mmsValue;
MmsVariableSpecification* typeSpec; MmsVariableSpecification* typeSpec;
@ -98,7 +91,7 @@ struct sControlObject
uint64_t operateTime; uint64_t operateTime;
bool operateOnce; bool operateOnce;
MmsServerConnection* clientConnection; MmsServerConnection* mmsConnection;
MmsValue* emptyString; MmsValue* emptyString;
@ -111,8 +104,8 @@ struct sControlObject
uint32_t operateInvokeId; uint32_t operateInvokeId;
ControlHandler listener; ControlHandler operateHandler;
void* listenerParameter; void* operateHandlerParameter;
ControlPerformCheckHandler checkHandler; ControlPerformCheckHandler checkHandler;
void* checkHandlerParameter; void* checkHandlerParameter;
@ -123,10 +116,13 @@ struct sControlObject
void void
ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error, ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
int addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode); ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode);
void void
ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection* connection); ControlObject_sendCommandTerminationPositive(ControlObject* self);
void
ControlObject_sendCommandTerminationNegative(ControlObject* self);
MmsValue* MmsValue*
Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
@ -135,18 +131,31 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
static void static void
setState(ControlObject* self, int newState) setState(ControlObject* self, int newState)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock); Semaphore_wait(self->stateLock);
#endif
self->state = newState; self->state = newState;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock); Semaphore_post(self->stateLock);
#endif
} }
static int static int
getState(ControlObject* self) getState(ControlObject* self)
{ {
int state; int state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock); Semaphore_wait(self->stateLock);
#endif
state = self->state; state = self->state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock); Semaphore_post(self->stateLock);
#endif
return state; return state;
} }
@ -257,6 +266,47 @@ isSboClassOperateOnce(ControlObject* self)
return true; /* default is operate-once ! */ return true; /* default is operate-once ! */
} }
static MmsValue*
getOperParameterOperTime(MmsValue* operParameters)
{
if (MmsValue_getType(operParameters) == MMS_STRUCTURE) {
if (MmsValue_getArraySize(operParameters) == 7)
return MmsValue_getElement(operParameters, 1);
}
return NULL;
}
static bool
initiateControlTask(ControlObject* self)
{
ClientConnection iedClientConnection =
private_IedServer_getClientConnectionByHandle(self->iedServer, self->mmsConnection);
if (iedClientConnection == NULL)
return false;
private_ClientConnection_increaseTasksCount(iedClientConnection);
return true;
}
static void
exitControlTask(ControlObject* self)
{
ClientConnection iedClientConnection =
private_IedServer_getClientConnectionByHandle(self->iedServer, self->mmsConnection);
if (iedClientConnection == NULL)
return;
/* synchronize with connection management */
private_ClientConnection_decreaseTasksCount(iedClientConnection);
}
static void static void
abortControlOperation(ControlObject* self) abortControlOperation(ControlObject* self)
{ {
@ -269,61 +319,62 @@ abortControlOperation(ControlObject* self)
} }
else else
setState(self, STATE_READY); setState(self, STATE_READY);
exitControlTask(self);
} }
static MmsValue* static ControlHandlerResult
getOperParameterOperTime(MmsValue* operParameters) operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool testCondition)
{ {
if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { self->selectTime = currentTime;
if (MmsValue_getArraySize(operParameters) == 7) if (self->operateHandler != NULL)
return MmsValue_getElement(operParameters, 1); return self->operateHandler(self->operateHandlerParameter, value, testCondition);
}
return NULL; return CONTROL_RESULT_OK;
} }
static void static void
controlOperateThread(ControlObject* self) executeControlTask(ControlObject* self)
{ {
bool checkOk = true; int state;
bool isTimeActivatedControl = false; executeStateMachine:
ClientConnection iedClientConnection = state = getState(self);
private_IedServer_getClientConnectionByHandle(self->iedServer, self->clientConnection);
if (iedClientConnection == NULL) return; switch (state) {
private_ClientConnection_increaseTasksCount(iedClientConnection); case STATE_WAIT_FOR_ACTICATION_TIME:
case STATE_WAIT_FOR_EXECUTION:
{
ControlHandlerResult dynamicCheckResult = CONTROL_RESULT_OK;
bool isTimeActivatedControl = false;
if (getState(self) == STATE_WAIT_FOR_ACTICATION_TIME) if (state == STATE_WAIT_FOR_ACTICATION_TIME)
isTimeActivatedControl = true; isTimeActivatedControl = true;
setState(self, STATE_WAIT_FOR_EXECUTION);
if (self->waitForExecutionHandler != NULL) { if (self->waitForExecutionHandler != NULL) {
checkOk = self->waitForExecutionHandler(self->waitForExecutionHandlerParameter, self->ctlVal, dynamicCheckResult = self->waitForExecutionHandler(self->waitForExecutionHandlerParameter, self->ctlVal,
self->testMode, self->synchroCheck); self->testMode, self->synchroCheck);
} }
if (!checkOk) { if (dynamicCheckResult == CONTROL_RESULT_FAILED) {
if (isTimeActivatedControl) { if (isTimeActivatedControl) {
ControlObject_sendLastApplError(self, self->clientConnection, "Oper", ControlObject_sendLastApplError(self, self->mmsConnection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
self->ctlNum, self->origin, false); self->ctlNum, self->origin, false);
} }
else else
MmsServerConnection_sendWriteResponse(self->clientConnection, self->operateInvokeId, MmsServerConnection_sendWriteResponse(self->mmsConnection, self->operateInvokeId,
DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED); DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED, true);
abortControlOperation(self); abortControlOperation(self);
exitControlTask(self);
} }
else { else if (dynamicCheckResult == CONTROL_RESULT_OK) {
if (isTimeActivatedControl) { if (isTimeActivatedControl) {
ControlObject_sendCommandTerminationReq(self, self->clientConnection); ControlObject_sendCommandTerminationPositive(self);
MmsValue* operTm = getOperParameterOperTime(self->oper); MmsValue* operTm = getOperParameterOperTime(self->oper);
@ -331,54 +382,60 @@ controlOperateThread(ControlObject* self)
} }
else else
MmsServerConnection_sendWriteResponse(self->clientConnection, self->operateInvokeId, MmsServerConnection_sendWriteResponse(self->mmsConnection, self->operateInvokeId,
DATA_ACCESS_ERROR_SUCCESS); DATA_ACCESS_ERROR_SUCCESS, true);
setState(self, STATE_OPERATE);
goto executeStateMachine;
}
}
break;
case STATE_OPERATE:
{
uint64_t currentTime = Hal_getTimeInMs(); uint64_t currentTime = Hal_getTimeInMs();
setState(self, STATE_OPERATE); ControlHandlerResult result = operateControl(self, self->ctlVal, currentTime, self->testMode);
if (result != CONTROL_RESULT_WAITING) {
if (ControlObject_operate(self, self->ctlVal, currentTime, self->testMode)) { if (result == CONTROL_RESULT_OK) {
if ((self->ctlModel == 4) || (self->ctlModel == 3)) { if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
ControlObject_sendCommandTerminationReq(self, self->clientConnection); ControlObject_sendCommandTerminationPositive(self);
} }
} }
else { else {
if ((self->ctlModel == 4) || (self->ctlModel == 3)) { if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Oper: operate failed!\n"); printf("IED_SERVER: operate failed!\n");
ControlObject_sendLastApplError(self, self->clientConnection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_SELECT_FAILED, ControlObject_sendCommandTerminationNegative(self);
self->ctlNum, self->origin, false);
} }
} }
abortControlOperation(self); abortControlOperation(self);
exitControlTask(self);
} }
}
break;
/* synchronize with connection management */ }
private_ClientConnection_decreaseTasksCount(iedClientConnection);
}
static void
startControlOperateThread(ControlObject* self)
{
Thread thread = Thread_create((ThreadExecutionFunction) controlOperateThread, (void*) self, true);
Thread_start(thread);
} }
ControlObject* ControlObject*
ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name) ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* name)
{ {
ControlObject* self = (ControlObject*) calloc(1, sizeof(ControlObject)); ControlObject* self = (ControlObject*) GLOBAL_CALLOC(1, sizeof(ControlObject));
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("create control object for LD: %s, LN: %s, name: %s\n", domain->domainName, lnName, name); printf("create control object for LD: %s, LN: %s, name: %s\n", domain->domainName, lnName, name);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->stateLock = Semaphore_create(1); self->stateLock = Semaphore_create(1);
#endif
self->name = copyString(name); self->name = copyString(name);
self->lnName = lnName; self->lnName = lnName;
@ -420,7 +477,9 @@ ControlObject_destroy(ControlObject* self)
free(self->name); free(self->name);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->stateLock); Semaphore_destroy(self->stateLock);
#endif
free(self); free(self);
} }
@ -527,7 +586,7 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection* conn
updateSboTimeoutValue(self); updateSboTimeoutValue(self);
self->selected = true; self->selected = true;
self->selectTime = selectTime; self->selectTime = selectTime;
self->clientConnection = connection; self->mmsConnection = connection;
setState(self, STATE_READY); setState(self, STATE_READY);
} }
@ -551,7 +610,7 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
bool bool
ControlObject_unselect(ControlObject* self, MmsServerConnection* connection) ControlObject_unselect(ControlObject* self, MmsServerConnection* connection)
{ {
if (self->clientConnection == connection) { if (self->mmsConnection == connection) {
abortControlOperation(self); abortControlOperation(self);
return true; return true;
} }
@ -559,23 +618,11 @@ ControlObject_unselect(ControlObject* self, MmsServerConnection* connection)
return false; return false;
} }
bool
ControlObject_operate(ControlObject* self, MmsValue* value, uint64_t currentTime, bool testCondition)
{
self->selectTime = currentTime;
if (self->listener != NULL) {
self->listener(self->listenerParameter, value, testCondition);
}
return true;
}
void void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter) ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter)
{ {
self->listener = listener; self->operateHandler = listener;
self->listenerParameter = parameter; self->operateHandlerParameter = parameter;
} }
void void
@ -601,7 +648,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
while (element != NULL) { while (element != NULL) {
ControlObject* controlObject = (ControlObject*) element->data; ControlObject* controlObject = (ControlObject*) element->data;
if (controlObject->timeActivatedOperate) { if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) {
if (controlObject->operateTime <= currentTimeInMs) { if (controlObject->operateTime <= currentTimeInMs) {
@ -610,25 +657,29 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
controlObject->timeActivatedOperate = false; controlObject->timeActivatedOperate = false;
bool checkOk = true; CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (controlObject->checkHandler != NULL) { /* perform operative tests */ if (controlObject->checkHandler != NULL) { /* perform operative tests */
checkOk = controlObject->checkHandler( checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode, controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
controlObject->interlockCheck, (ClientConnection) controlObject->clientConnection); controlObject->interlockCheck, (ClientConnection) controlObject->mmsConnection);
} }
if (checkOk) if (checkResult == CONTROL_ACCEPTED) {
startControlOperateThread(controlObject); executeControlTask(controlObject);
}
else { else {
ControlObject_sendLastApplError(controlObject, controlObject->clientConnection, "Oper", ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_BLOCKED_BY_INTERLOCKING, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
controlObject->ctlNum, controlObject->origin, false); controlObject->ctlNum, controlObject->origin, false);
abortControlOperation(controlObject); abortControlOperation(controlObject);
} }
} }
} /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */
else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) {
executeControlTask(controlObject);
} }
element = LinkedList_getNext(element); element = LinkedList_getNext(element);
@ -767,14 +818,14 @@ getOperParameterTime(MmsValue* operParameters)
} }
void void
ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection* connection) ControlObject_sendCommandTerminationPositive(ControlObject* self)
{ {
char itemId[130]; char itemId[130];
createStringInBuffer(itemId, 4, self->lnName, "$CO$", self->name, "$Oper"); createStringInBuffer(itemId, 4, self->lnName, "$CO$", self->name, "$Oper");
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("CmdTermination: %s\n", itemId); printf("IED_SERVER: send CommandTermination+: %s\n", itemId);
char* domainId = MmsDomain_getName(self->mmsDomain); char* domainId = MmsDomain_getName(self->mmsDomain);
@ -789,15 +840,91 @@ ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection
LinkedList_add(varSpecList, &varSpec); LinkedList_add(varSpecList, &varSpec);
LinkedList_add(values, self->oper); LinkedList_add(values, self->oper);
MmsServerConnection_sendInformationReportListOfVariables(connection, varSpecList, values, false); MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, varSpecList, values, false);
LinkedList_destroyStatic(varSpecList); LinkedList_destroyStatic(varSpecList);
LinkedList_destroyStatic(values); LinkedList_destroyStatic(values);
} }
void
ControlObject_sendCommandTerminationNegative(ControlObject* self)
{
/* create LastApplError */
MmsValue lastApplErrorMemory;
MmsValue* lastApplError = &lastApplErrorMemory;
lastApplError->type = MMS_STRUCTURE;
lastApplError->value.structure.size = 5;
MmsValue* componentContainer[5];
lastApplError->value.structure.components = componentContainer;
char ctlObj[130];
createStringInBuffer(ctlObj, 2, self->ctlObjectName, "$Oper");
MmsValue ctlObjValueMemory;
MmsValue* ctlObjValue = &ctlObjValueMemory;
ctlObjValue->type = MMS_VISIBLE_STRING;
ctlObjValue->value.visibleString.buf = ctlObj;
ctlObjValue->value.visibleString.size = sizeof(ctlObj);
MmsValue_setElement(lastApplError, 0, ctlObjValue);
MmsValue_setInt32(self->error, CONTROL_ERROR_UNKOWN);
MmsValue_setInt32(self->addCause, ADD_CAUSE_UNKNOWN);
MmsValue_setElement(lastApplError, 1, self->error);
MmsValue_setElement(lastApplError, 2, self->origin);
MmsValue_setElement(lastApplError, 3, self->ctlNum);
MmsValue_setElement(lastApplError, 4, self->addCause);
MmsVariableAccessSpecification lastApplErrorVarSpec;
lastApplErrorVarSpec.itemId = "LastApplError";
lastApplErrorVarSpec.domainId = NULL;
/* create oper variable */
char itemId[130];
createStringInBuffer(itemId, 4, self->lnName, "$CO$", self->name, "$Oper");
char* domainId = MmsDomain_getName(self->mmsDomain);
MmsVariableAccessSpecification operVarSpec;
operVarSpec.itemId = itemId;
operVarSpec.domainId = domainId;
/* create response */
if (DEBUG_IED_SERVER)
printf("IED_SERVER: send CommandTermination-: %s\n", itemId);
LinkedList varSpecList = LinkedList_create();
LinkedList values = LinkedList_create();
LinkedList_add(varSpecList, &lastApplErrorVarSpec);
LinkedList_add(varSpecList, &operVarSpec);
LinkedList_add(values, lastApplError);
LinkedList_add(values, self->oper);
MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, varSpecList, values, false);
LinkedList_destroyStatic(varSpecList);
LinkedList_destroyStatic(values);
} /* ControlObject_sendCommandTerminationNegative() */
void void
ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error, ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
int addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode) ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode)
{ {
MmsValue lastApplErrorMemory; MmsValue lastApplErrorMemory;
@ -963,15 +1090,15 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
checkSelectTimeout(controlObject, currentTime); checkSelectTimeout(controlObject, currentTime);
if (getState(controlObject) == STATE_UNSELECTED) { if (getState(controlObject) == STATE_UNSELECTED) {
bool checkOk = true; CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (controlObject->checkHandler != NULL) { /* perform operative tests */ if (controlObject->checkHandler != NULL) { /* perform operative tests */
checkOk = controlObject->checkHandler( checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, NULL, false, false, controlObject->checkHandlerParameter, NULL, false, false,
(ClientConnection) connection); (ClientConnection) connection);
} }
if (checkOk) { if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection); selectObject(controlObject, currentTime, connection);
value = ControlObject_getSBO(controlObject); value = ControlObject_getSBO(controlObject);
} }
@ -980,7 +1107,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
} }
else { else {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("select not applicable for control model %i\n", controlObject->ctlModel); printf("IED_SERVER: select not applicable for control model %i\n", controlObject->ctlModel);
value = ControlObject_getSBO(controlObject); value = ControlObject_getSBO(controlObject);
} }
@ -1031,6 +1158,27 @@ checkValidityOfOriginParameter(MmsValue* origin)
return true; return true;
} }
static MmsDataAccessError
getDataAccessErrorFromCheckHandlerResult(CheckHandlerResult checkResult)
{
MmsDataAccessError indication;
if (checkResult == CONTROL_HARDWARE_FAULT)
indication = DATA_ACCESS_ERROR_HARDWARE_FAULT;
else
if (checkResult == CONTROL_TEMPORARILY_UNAVAILABLE)
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
else
if (checkResult == CONTROL_OBJECT_UNDEFINED)
indication = DATA_ACCESS_ERROR_OBJECT_UNDEFINED;
else if (checkResult == CONTROL_OBJECT_ACCESS_DENIED)
indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
else
indication = DATA_ACCESS_ERROR_SUCCESS;
return indication;
}
MmsDataAccessError MmsDataAccessError
Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
MmsValue* value, MmsServerConnection* connection) MmsValue* value, MmsServerConnection* connection)
@ -1138,19 +1286,19 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) { if (state != STATE_UNSELECTED) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (connection != controlObject->clientConnection) if (connection != controlObject->mmsConnection)
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
CONTROL_ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true); ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
else else
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
CONTROL_ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true); ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("SBOw: select failed!\n"); printf("SBOw: select failed!\n");
} }
else { else {
bool checkOk = true; CheckHandlerResult checkResult = CONTROL_ACCEPTED;
bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool interlockCheck = MmsValue_getBitStringBit(check, 1);
@ -1161,12 +1309,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection); connection);
checkOk = controlObject->checkHandler( checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck, controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck,
clientConnection); clientConnection);
} }
if (checkOk) { if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection); selectObject(controlObject, currentTime, connection);
updateControlParameters(controlObject, ctlVal, ctlNum, origin); updateControlParameters(controlObject, ctlVal, ctlNum, origin);
@ -1177,10 +1325,10 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
printf("SBOw: selected successful\n"); printf("SBOw: selected successful\n");
} }
else { else {
indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
CONTROL_ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("SBOw: select rejected by application!\n"); printf("SBOw: select rejected by application!\n");
@ -1225,7 +1373,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
ctlNum, origin, true); ctlNum, origin, true);
goto free_and_return; goto free_and_return;
@ -1240,7 +1388,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
controlObject->testMode = testCondition; controlObject->testMode = testCondition;
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
if (controlObject->clientConnection != connection) { if (controlObject->mmsConnection != connection) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Oper: operate from wrong client connection!\n"); printf("Oper: operate from wrong client connection!\n");
@ -1255,7 +1403,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true); ctlNum, origin, true);
goto free_and_return; goto free_and_return;
@ -1274,7 +1422,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
controlObject->timeActivatedOperate = true; controlObject->timeActivatedOperate = true;
controlObject->synchroCheck = synchroCheck; controlObject->synchroCheck = synchroCheck;
controlObject->interlockCheck = interlockCheck; controlObject->interlockCheck = interlockCheck;
controlObject->clientConnection = connection; controlObject->mmsConnection = connection;
initiateControlTask(controlObject);
setState(controlObject, STATE_WAIT_FOR_ACTICATION_TIME); setState(controlObject, STATE_WAIT_FOR_ACTICATION_TIME);
@ -1291,8 +1441,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CheckHandlerResult checkResult = CONTROL_ACCEPTED; CheckHandlerResult checkResult = CONTROL_ACCEPTED;
setState(controlObject, STATE_PERFORM_TEST);
if (controlObject->checkHandler != NULL) { /* perform operative tests */ if (controlObject->checkHandler != NULL) { /* perform operative tests */
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
@ -1307,20 +1455,22 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (checkResult == CONTROL_ACCEPTED) { if (checkResult == CONTROL_ACCEPTED) {
indication = DATA_ACCESS_ERROR_NO_RESPONSE; indication = DATA_ACCESS_ERROR_NO_RESPONSE;
controlObject->clientConnection = connection; controlObject->mmsConnection = connection;
controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection);
startControlOperateThread(controlObject);
setState(controlObject, STATE_WAIT_FOR_EXECUTION);
initiateControlTask(controlObject);
#if (CONFIG_MMS_THREADLESS_STACK == 1)
//TODO call this in single threaded version to increase response time!?
//executeControlTask(controlObject);
#endif
} }
else { else {
if (checkResult == CONTROL_HARDWARE_FAULT) indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
indication = DATA_ACCESS_ERROR_HARDWARE_FAULT;
else if (checkResult == CONTROL_TEMPORARILY_UNAVAILABLE)
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
else if (checkResult == CONTROL_OBJECT_UNDEFINED)
indication = DATA_ACCESS_ERROR_OBJECT_UNDEFINED;
else
indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
abortControlOperation(controlObject); abortControlOperation(controlObject);
} }
@ -1329,11 +1479,11 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
} }
else if (state == STATE_UNSELECTED) { else if (state == STATE_UNSELECTED) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Oper: not selected!\n"); printf("IED_SERVER: Oper failed - control not selected!\n");
indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_OBJECT_NOT_SELECTED, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_OBJECT_NOT_SELECTED,
ctlNum, origin, true); ctlNum, origin, true);
goto free_and_return; goto free_and_return;
@ -1341,7 +1491,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
} }
else if (strcmp(varName, "Cancel") == 0) { else if (strcmp(varName, "Cancel") == 0) {
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Received cancel!\n"); printf("IED_SERVER: control received cancel!\n");
int state = getState(controlObject); int state = getState(controlObject);
@ -1351,13 +1501,13 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if ((ctlNum == NULL) || (origin == NULL)) { if ((ctlNum == NULL) || (origin == NULL)) {
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("Invalid cancel message!\n"); printf("IED_SERVER: Invalid cancel message!\n");
goto free_and_return; goto free_and_return;
} }
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
if (state != STATE_UNSELECTED) { if (state != STATE_UNSELECTED) {
if (controlObject->clientConnection == connection) { if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;
setState(controlObject, STATE_UNSELECTED); setState(controlObject, STATE_UNSELECTED);
goto free_and_return; goto free_and_return;
@ -1365,7 +1515,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
else { else {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "Cancel", ControlObject_sendLastApplError(controlObject, connection, "Cancel",
CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT,
ctlNum, origin, true); ctlNum, origin, true);
} }
} }

@ -30,7 +30,7 @@
#include "linked_list.h" #include "linked_list.h"
#include "array_list.h" #include "array_list.h"
#include "thread.h" #include "hal_thread.h"
#include "reporting.h" #include "reporting.h"
#include "mms_mapping_internal.h" #include "mms_mapping_internal.h"
@ -56,7 +56,10 @@ struct sMmsGooseControlBlock {
LinkedList dataSetValues; LinkedList dataSetValues;
uint64_t nextPublishTime; uint64_t nextPublishTime;
int retransmissionsLeft; /* number of retransmissions left for the last event */ int retransmissionsLeft; /* number of retransmissions left for the last event */
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore publisherMutex; Semaphore publisherMutex;
#endif
MmsMapping* mmsMapping; MmsMapping* mmsMapping;
@ -68,9 +71,11 @@ struct sMmsGooseControlBlock {
MmsGooseControlBlock MmsGooseControlBlock
MmsGooseControlBlock_create() MmsGooseControlBlock_create()
{ {
MmsGooseControlBlock self = (MmsGooseControlBlock) calloc(1, sizeof(struct sMmsGooseControlBlock)); MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock));
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->publisherMutex = Semaphore_create(1); self->publisherMutex = Semaphore_create(1);
#endif
return self; return self;
} }
@ -78,7 +83,9 @@ MmsGooseControlBlock_create()
void void
MmsGooseControlBlock_destroy(MmsGooseControlBlock self) MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->publisherMutex); Semaphore_destroy(self->publisherMutex);
#endif
if (self->publisher != NULL) if (self->publisher != NULL)
GoosePublisher_destroy(self->publisher); GoosePublisher_destroy(self->publisher);
@ -87,13 +94,13 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
LinkedList_destroyStatic(self->dataSetValues); LinkedList_destroyStatic(self->dataSetValues);
if (self->goCBRef != NULL) if (self->goCBRef != NULL)
free(self->goCBRef); GLOBAL_FREEMEM(self->goCBRef);
if (self->goId != NULL) if (self->goId != NULL)
free(self->goId); GLOBAL_FREEMEM(self->goId);
if (self->dataSetRef != NULL) if (self->dataSetRef != NULL)
free(self->dataSetRef); GLOBAL_FREEMEM(self->dataSetRef);
if (self->dataSet != NULL) { if (self->dataSet != NULL) {
if (self->isDynamicDataSet) if (self->isDynamicDataSet)
@ -102,7 +109,7 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
MmsValue_delete(self->mmsValue); MmsValue_delete(self->mmsValue);
free(self); GLOBAL_FREEMEM(self);
} }
MmsDomain* MmsDomain*
@ -150,13 +157,15 @@ MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self)
void void
MmsGooseControlBlock_enable(MmsGooseControlBlock self) MmsGooseControlBlock_enable(MmsGooseControlBlock self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex); Semaphore_wait(self->publisherMutex);
#endif
if (!MmsGooseControlBlock_isEnabled(self)) { if (!MmsGooseControlBlock_isEnabled(self)) {
if (self->dataSetRef != NULL) { if (self->dataSetRef != NULL) {
free(self->dataSetRef); GLOBAL_FREEMEM(self->dataSetRef);
if (self->dataSet != NULL) if (self->dataSet != NULL)
if (self->isDynamicDataSet) if (self->isDynamicDataSet)
@ -240,7 +249,9 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex); Semaphore_post(self->publisherMutex);
#endif
} }
void void
@ -253,7 +264,9 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self)
self->goEna = false; self->goEna = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex); Semaphore_wait(self->publisherMutex);
#endif
if (self->publisher != NULL) { if (self->publisher != NULL) {
GoosePublisher_destroy(self->publisher); GoosePublisher_destroy(self->publisher);
@ -262,7 +275,9 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self)
self->dataSetValues = NULL; self->dataSetValues = NULL;
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex); Semaphore_post(self->publisherMutex);
#endif
} }
} }
@ -272,7 +287,9 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
{ {
if (currentTime >= self->nextPublishTime) { if (currentTime >= self->nextPublishTime) {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex); Semaphore_wait(self->publisherMutex);
#endif
GoosePublisher_publish(self->publisher, self->dataSetValues); GoosePublisher_publish(self->publisher, self->dataSetValues);
@ -297,14 +314,18 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL; CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL;
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex); Semaphore_post(self->publisherMutex);
#endif
} }
} }
void void
MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self) MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex); Semaphore_wait(self->publisherMutex);
#endif
uint64_t currentTime = GoosePublisher_increaseStNum(self->publisher); uint64_t currentTime = GoosePublisher_increaseStNum(self->publisher);
@ -327,74 +348,76 @@ MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
GoosePublisher_publish(self->publisher, self->dataSetValues); GoosePublisher_publish(self->publisher, self->dataSetValues);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex); Semaphore_post(self->publisherMutex);
#endif
} }
static MmsVariableSpecification* static MmsVariableSpecification*
createMmsGooseControlBlock(char* gcbName) createMmsGooseControlBlock(char* gcbName)
{ {
MmsVariableSpecification* gcb = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); MmsVariableSpecification* gcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
gcb->name = copyString(gcbName); gcb->name = copyString(gcbName);
gcb->type = MMS_STRUCTURE; gcb->type = MMS_STRUCTURE;
gcb->typeSpec.structure.elementCount = 9; gcb->typeSpec.structure.elementCount = 9;
gcb->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(9, sizeof(MmsVariableSpecification*)); gcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(9, sizeof(MmsVariableSpecification*));
MmsVariableSpecification* namedVariable; MmsVariableSpecification* namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GoEna"); namedVariable->name = copyString("GoEna");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
gcb->typeSpec.structure.elements[0] = namedVariable; gcb->typeSpec.structure.elements[0] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GoID"); namedVariable->name = copyString("GoID");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
gcb->typeSpec.structure.elements[1] = namedVariable; gcb->typeSpec.structure.elements[1] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DatSet"); namedVariable->name = copyString("DatSet");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
gcb->typeSpec.structure.elements[2] = namedVariable; gcb->typeSpec.structure.elements[2] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("ConfRev"); namedVariable->name = copyString("ConfRev");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[3] = namedVariable; gcb->typeSpec.structure.elements[3] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("NdsCom"); namedVariable->name = copyString("NdsCom");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
gcb->typeSpec.structure.elements[4] = namedVariable; gcb->typeSpec.structure.elements[4] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DstAddress"); namedVariable->name = copyString("DstAddress");
MmsMapping_createPhyComAddrStructure(namedVariable); MmsMapping_createPhyComAddrStructure(namedVariable);
gcb->typeSpec.structure.elements[5] = namedVariable; gcb->typeSpec.structure.elements[5] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("MinTime"); namedVariable->name = copyString("MinTime");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[6] = namedVariable; gcb->typeSpec.structure.elements[6] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("MaxTime"); namedVariable->name = copyString("MaxTime");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[7] = namedVariable; gcb->typeSpec.structure.elements[7] = namedVariable;
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("FixedOffs"); namedVariable->name = copyString("FixedOffs");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
@ -439,13 +462,13 @@ MmsVariableSpecification*
GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int gseCount) LogicalNode* logicalNode, int gseCount)
{ {
MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1, MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification)); sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GO"); namedVariable->name = copyString("GO");
namedVariable->type = MMS_STRUCTURE; namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = gseCount; namedVariable->typeSpec.structure.elementCount = gseCount;
namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(gseCount, namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(gseCount,
sizeof(MmsVariableSpecification*)); sizeof(MmsVariableSpecification*));
int currentGCB = 0; int currentGCB = 0;

@ -26,7 +26,7 @@
#include "linked_list.h" #include "linked_list.h"
#include "array_list.h" #include "array_list.h"
#include "stack_config.h" #include "stack_config.h"
#include "thread.h" #include "hal_thread.h"
#include "simple_allocator.h" #include "simple_allocator.h"
#include "mem_alloc_linked_list.h" #include "mem_alloc_linked_list.h"
@ -35,6 +35,7 @@
#include "mms_mapping_internal.h" #include "mms_mapping_internal.h"
#include "mms_value_internal.h" #include "mms_value_internal.h"
#include "conversions.h" #include "conversions.h"
#include <string.h>
#ifndef DEBUG_IED_SERVER #ifndef DEBUG_IED_SERVER
#define DEBUG_IED_SERVER 0 #define DEBUG_IED_SERVER 0
@ -45,12 +46,12 @@
static ReportBuffer* static ReportBuffer*
ReportBuffer_create(void) ReportBuffer_create(void)
{ {
ReportBuffer* self = (ReportBuffer*) malloc(sizeof(ReportBuffer)); ReportBuffer* self = (ReportBuffer*) GLOBAL_MALLOC(sizeof(ReportBuffer));
self->lastEnqueuedReport = NULL; self->lastEnqueuedReport = NULL;
self->oldestReport = NULL; self->oldestReport = NULL;
self->nextToTransmit = NULL; self->nextToTransmit = NULL;
self->memoryBlockSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->memoryBlockSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->memoryBlock = (uint8_t*) malloc(self->memoryBlockSize); self->memoryBlock = (uint8_t*) GLOBAL_MALLOC(self->memoryBlockSize);
self->reportsCount = 0; self->reportsCount = 0;
return self; return self;
@ -59,14 +60,14 @@ ReportBuffer_create(void)
static void static void
ReportBuffer_destroy(ReportBuffer* self) ReportBuffer_destroy(ReportBuffer* self)
{ {
free(self->memoryBlock); GLOBAL_FREEMEM(self->memoryBlock);
free(self); GLOBAL_FREEMEM(self);
} }
ReportControl* ReportControl*
ReportControl_create(bool buffered, LogicalNode* parentLN) ReportControl_create(bool buffered, LogicalNode* parentLN)
{ {
ReportControl* self = (ReportControl*) malloc(sizeof(ReportControl)); ReportControl* self = (ReportControl*) GLOBAL_MALLOC(sizeof(ReportControl));
self->name = NULL; self->name = NULL;
self->domain = NULL; self->domain = NULL;
self->parentLN = parentLN; self->parentLN = parentLN;
@ -90,7 +91,11 @@ ReportControl_create(bool buffered, LogicalNode* parentLN)
self->timeOfEntry = NULL; self->timeOfEntry = NULL;
self->reservationTimeout = 0; self->reservationTimeout = 0;
self->triggerOps = 0; self->triggerOps = 0;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->createNotificationsMutex = Semaphore_create(1); self->createNotificationsMutex = Semaphore_create(1);
#endif
self->bufferedDataSetValues = NULL; self->bufferedDataSetValues = NULL;
self->valueReferences = NULL; self->valueReferences = NULL;
self->lastEntryId = 0; self->lastEntryId = 0;
@ -105,13 +110,17 @@ ReportControl_create(bool buffered, LogicalNode* parentLN)
static void static void
ReportControl_lockNotify(ReportControl* self) ReportControl_lockNotify(ReportControl* self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->createNotificationsMutex); Semaphore_wait(self->createNotificationsMutex);
#endif
} }
static void static void
ReportControl_unlockNotify(ReportControl* self) ReportControl_unlockNotify(ReportControl* self)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->createNotificationsMutex); Semaphore_post(self->createNotificationsMutex);
#endif
} }
@ -130,11 +139,11 @@ deleteDataSetValuesShadowBuffer(ReportControl* self)
MmsValue_delete(self->bufferedDataSetValues[i]); MmsValue_delete(self->bufferedDataSetValues[i]);
} }
free(self->bufferedDataSetValues); GLOBAL_FREEMEM(self->bufferedDataSetValues);
} }
if (self->valueReferences != NULL) if (self->valueReferences != NULL)
free(self->valueReferences); GLOBAL_FREEMEM(self->valueReferences);
} }
void void
@ -144,7 +153,7 @@ ReportControl_destroy(ReportControl* self)
MmsValue_delete(self->rcbValues); MmsValue_delete(self->rcbValues);
if (self->inclusionFlags != NULL) if (self->inclusionFlags != NULL)
free(self->inclusionFlags); GLOBAL_FREEMEM(self->inclusionFlags);
if (self->inclusionField != NULL) if (self->inclusionField != NULL)
MmsValue_delete(self->inclusionField); MmsValue_delete(self->inclusionField);
@ -162,11 +171,13 @@ ReportControl_destroy(ReportControl* self)
if (self->buffered) if (self->buffered)
ReportBuffer_destroy(self->reportBuffer); ReportBuffer_destroy(self->reportBuffer);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->createNotificationsMutex); Semaphore_destroy(self->createNotificationsMutex);
#endif
free(self->name); GLOBAL_FREEMEM(self->name);
free(self); GLOBAL_FREEMEM(self);
} }
MmsValue* MmsValue*
@ -255,7 +266,6 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
LinkedList_add(reportElements, optFlds); LinkedList_add(reportElements, optFlds);
/* delete option fields for unsupported options */ /* delete option fields for unsupported options */
MmsValue_setBitStringBit(optFlds, 5, false); /* data-reference */
MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */ MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */
MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */ MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */
@ -287,6 +297,69 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
LinkedList_add(reportElements, self->inclusionField); LinkedList_add(reportElements, self->inclusionField);
/* add data references if selected */
if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */
DataSetEntry* dataSetEntry = self->dataSet->fcdas;
LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent;
IedModel* iedModel = (IedModel*) ld->parent;
char* iedName = iedModel->name;
int iedNameLength = strlen(iedName);
int i = 0;
for (i = 0; i < self->dataSet->elementCount; i++) {
assert(dataSetEntry->value != NULL);
bool addReferenceForEntry = false;
if (isGI || isIntegrity)
addReferenceForEntry = true;
else
if (self->inclusionFlags[i] != REPORT_CONTROL_NONE)
addReferenceForEntry = true;
if (addReferenceForEntry) {
char dataReference[130];
int currentPos = 0;
int j;
for (j = 0; j < iedNameLength; j++) {
dataReference[currentPos++] = iedName[j];
}
int ldNameLength = strlen(dataSetEntry->logicalDeviceName);
for (j = 0; j < ldNameLength; j++) {
dataReference[currentPos] = dataSetEntry->logicalDeviceName[j];
currentPos++;
}
dataReference[currentPos++] = '/';
for (j = 0; j < (int) strlen(dataSetEntry->variableName); j++) {
dataReference[currentPos++] = dataSetEntry->variableName[j];
}
dataReference[currentPos] = 0;
MmsValue* dataRef = MmsValue_newVisibleString(dataReference);
LinkedList_add(reportElements, dataRef);
LinkedList_add(deletableElements, dataRef);
}
dataSetEntry = dataSetEntry->sibling;
}
}
/* add data set value elements */ /* add data set value elements */
DataSetEntry* dataSetEntry = self->dataSet->fcdas; DataSetEntry* dataSetEntry = self->dataSet->fcdas;
@ -362,11 +435,11 @@ createDataSetValuesShadowBuffer(ReportControl* rc)
{ {
int dataSetSize = DataSet_getSize(rc->dataSet); int dataSetSize = DataSet_getSize(rc->dataSet);
MmsValue** dataSetValues = (MmsValue**) calloc(dataSetSize, sizeof(MmsValue*)); MmsValue** dataSetValues = (MmsValue**) GLOBAL_CALLOC(dataSetSize, sizeof(MmsValue*));
rc->bufferedDataSetValues = dataSetValues; rc->bufferedDataSetValues = dataSetValues;
rc->valueReferences = (MmsValue**) malloc(dataSetSize * sizeof(MmsValue*)); rc->valueReferences = (MmsValue**) GLOBAL_MALLOC(dataSetSize * sizeof(MmsValue*));
DataSetEntry* dataSetEntry = rc->dataSet->fcdas; DataSetEntry* dataSetEntry = rc->dataSet->fcdas;
@ -424,9 +497,9 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet)
rc->inclusionField = MmsValue_newBitString(dataSet->elementCount); rc->inclusionField = MmsValue_newBitString(dataSet->elementCount);
if (rc->inclusionFlags != NULL) if (rc->inclusionFlags != NULL)
free(rc->inclusionFlags); GLOBAL_FREEMEM(rc->inclusionFlags);
rc->inclusionFlags = (ReportInclusionFlag*) calloc(dataSet->elementCount, sizeof(ReportInclusionFlag)); rc->inclusionFlags = (ReportInclusionFlag*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(ReportInclusionFlag));
return true; return true;
} }
@ -545,47 +618,88 @@ refreshBufferTime(ReportControl* rc)
rc->bufTm = MmsValue_toUint32(bufTm); rc->bufTm = MmsValue_toUint32(bufTm);
} }
static void
composeDefaultRptIdString(char* rptIdString, ReportControl* reportControl)
{
int bufPos = 0;
while (reportControl->domain->domainName[bufPos] != 0) {
rptIdString[bufPos] = reportControl->domain->domainName[bufPos];
bufPos++;
}
rptIdString[bufPos++] = '/';
int i = 0;
while (reportControl->name[i] != 0) {
rptIdString[bufPos] = reportControl->name[i];
bufPos++;
i++;
}
rptIdString[bufPos] = 0;
}
static MmsValue*
createDefaultRptId(ReportControl* reportControl)
{
char rptIdString[130]; /* maximum length 129 chars */
composeDefaultRptIdString(rptIdString, reportControl);
return MmsValue_newVisibleString(rptIdString);
}
static void
updateWithDefaultRptId(ReportControl* reportControl, MmsValue* rptId)
{
char rptIdString[130]; /* maximum length 129 chars */
composeDefaultRptIdString(rptIdString, reportControl);
MmsValue_setVisibleString(rptId, rptIdString);
}
static MmsVariableSpecification* static MmsVariableSpecification*
createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
ReportControl* reportControl) ReportControl* reportControl)
{ {
MmsVariableSpecification* rcb = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); MmsVariableSpecification* rcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
rcb->name = copyString(reportControlBlock->name); rcb->name = copyString(reportControlBlock->name);
rcb->type = MMS_STRUCTURE; rcb->type = MMS_STRUCTURE;
MmsValue* mmsValue = (MmsValue*) calloc(1, sizeof(MmsValue)); MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
mmsValue->deleteValue = false; mmsValue->deleteValue = false;
mmsValue->type = MMS_STRUCTURE; mmsValue->type = MMS_STRUCTURE;
mmsValue->value.structure.size = 12; mmsValue->value.structure.size = 12;
mmsValue->value.structure.components = (MmsValue**) calloc(12, sizeof(MmsValue*)); mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(12, sizeof(MmsValue*));
rcb->typeSpec.structure.elementCount = 12; rcb->typeSpec.structure.elementCount = 12;
rcb->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(12, rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(12,
sizeof(MmsVariableSpecification*)); sizeof(MmsVariableSpecification*));
MmsVariableSpecification* namedVariable = MmsVariableSpecification* namedVariable =
(MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptID"); namedVariable->name = copyString("RptID");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
rcb->typeSpec.structure.elements[0] = namedVariable; rcb->typeSpec.structure.elements[0] = namedVariable;
if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0))
mmsValue->value.structure.components[0] = MmsValue_newVisibleString( mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
reportControlBlock->rptId); reportControlBlock->rptId);
else
mmsValue->value.structure.components[0] = createDefaultRptId(reportControl);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptEna"); namedVariable->name = copyString("RptEna");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[1] = namedVariable; rcb->typeSpec.structure.elements[1] = namedVariable;
mmsValue->value.structure.components[1] = MmsValue_newBoolean(false); mmsValue->value.structure.components[1] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("Resv"); namedVariable->name = copyString("Resv");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[2] = namedVariable; rcb->typeSpec.structure.elements[2] = namedVariable;
mmsValue->value.structure.components[2] = MmsValue_newBoolean(false); mmsValue->value.structure.components[2] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DatSet"); namedVariable->name = copyString("DatSet");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
@ -595,12 +709,12 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock, char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock,
reportControl); reportControl);
mmsValue->value.structure.components[3] = MmsValue_newVisibleString(dataSetReference); mmsValue->value.structure.components[3] = MmsValue_newVisibleString(dataSetReference);
free(dataSetReference); GLOBAL_FREEMEM(dataSetReference);
} }
else else
mmsValue->value.structure.components[3] = MmsValue_newVisibleString(""); mmsValue->value.structure.components[3] = MmsValue_newVisibleString("");
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("ConfRev"); namedVariable->name = copyString("ConfRev");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -610,14 +724,14 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
reportControl->confRev = mmsValue->value.structure.components[4]; reportControl->confRev = mmsValue->value.structure.components[4];
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("OptFlds"); namedVariable->name = copyString("OptFlds");
namedVariable->type = MMS_BIT_STRING; namedVariable->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 10; namedVariable->typeSpec.bitString = 10;
rcb->typeSpec.structure.elements[5] = namedVariable; rcb->typeSpec.structure.elements[5] = namedVariable;
mmsValue->value.structure.components[5] = createOptFlds(reportControlBlock); mmsValue->value.structure.components[5] = createOptFlds(reportControlBlock);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("BufTm"); namedVariable->name = copyString("BufTm");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -625,21 +739,21 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[6] = mmsValue->value.structure.components[6] =
MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("SqNum"); namedVariable->name = copyString("SqNum");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 16; namedVariable->typeSpec.unsignedInteger = 16;
rcb->typeSpec.structure.elements[7] = namedVariable; rcb->typeSpec.structure.elements[7] = namedVariable;
mmsValue->value.structure.components[7] = MmsValue_newUnsigned(16); mmsValue->value.structure.components[7] = MmsValue_newUnsigned(16);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("TrgOps"); namedVariable->name = copyString("TrgOps");
namedVariable->type = MMS_BIT_STRING; namedVariable->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 6; namedVariable->typeSpec.bitString = 6;
rcb->typeSpec.structure.elements[8] = namedVariable; rcb->typeSpec.structure.elements[8] = namedVariable;
mmsValue->value.structure.components[8] = createTrgOps(reportControlBlock); mmsValue->value.structure.components[8] = createTrgOps(reportControlBlock);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("IntgPd"); namedVariable->name = copyString("IntgPd");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -647,13 +761,13 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[9] = mmsValue->value.structure.components[9] =
MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GI"); namedVariable->name = copyString("GI");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[10] = namedVariable; rcb->typeSpec.structure.elements[10] = namedVariable;
mmsValue->value.structure.components[10] = MmsValue_newBoolean(false); mmsValue->value.structure.components[10] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("Owner"); namedVariable->name = copyString("Owner");
namedVariable->type = MMS_OCTET_STRING; namedVariable->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = -128; namedVariable->typeSpec.octetString = -128;
@ -675,37 +789,41 @@ static MmsVariableSpecification*
createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
ReportControl* reportControl, MmsMapping* mmsMapping) ReportControl* reportControl, MmsMapping* mmsMapping)
{ {
MmsVariableSpecification* rcb = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); MmsVariableSpecification* rcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
rcb->name = copyString(reportControlBlock->name); rcb->name = copyString(reportControlBlock->name);
rcb->type = MMS_STRUCTURE; rcb->type = MMS_STRUCTURE;
MmsValue* mmsValue = (MmsValue*) calloc(1, sizeof(MmsValue)); MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
mmsValue->deleteValue = false; mmsValue->deleteValue = false;
mmsValue->type = MMS_STRUCTURE; mmsValue->type = MMS_STRUCTURE;
mmsValue->value.structure.size = 15; mmsValue->value.structure.size = 15;
mmsValue->value.structure.components = (MmsValue**) calloc(15, sizeof(MmsValue*)); mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(15, sizeof(MmsValue*));
rcb->typeSpec.structure.elementCount = 15; rcb->typeSpec.structure.elementCount = 15;
rcb->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(15, rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(15,
sizeof(MmsVariableSpecification*)); sizeof(MmsVariableSpecification*));
MmsVariableSpecification* namedVariable = MmsVariableSpecification* namedVariable =
(MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptID"); namedVariable->name = copyString("RptID");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
rcb->typeSpec.structure.elements[0] = namedVariable; rcb->typeSpec.structure.elements[0] = namedVariable;
if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0))
mmsValue->value.structure.components[0] = MmsValue_newVisibleString( mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
reportControlBlock->rptId); reportControlBlock->rptId);
else
mmsValue->value.structure.components[0] = createDefaultRptId(reportControl);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptEna"); namedVariable->name = copyString("RptEna");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[1] = namedVariable; rcb->typeSpec.structure.elements[1] = namedVariable;
mmsValue->value.structure.components[1] = MmsValue_newBoolean(false); mmsValue->value.structure.components[1] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DatSet"); namedVariable->name = copyString("DatSet");
namedVariable->typeSpec.visibleString = -129; namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING; namedVariable->type = MMS_VISIBLE_STRING;
@ -716,12 +834,12 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
reportControl); reportControl);
mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference); mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference);
free(dataSetReference); GLOBAL_FREEMEM(dataSetReference);
} }
else else
mmsValue->value.structure.components[2] = MmsValue_newVisibleString(""); mmsValue->value.structure.components[2] = MmsValue_newVisibleString("");
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("ConfRev"); namedVariable->name = copyString("ConfRev");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -731,14 +849,14 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
reportControl->confRev = mmsValue->value.structure.components[3]; reportControl->confRev = mmsValue->value.structure.components[3];
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("OptFlds"); namedVariable->name = copyString("OptFlds");
namedVariable->type = MMS_BIT_STRING; namedVariable->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 10; namedVariable->typeSpec.bitString = 10;
rcb->typeSpec.structure.elements[4] = namedVariable; rcb->typeSpec.structure.elements[4] = namedVariable;
mmsValue->value.structure.components[4] = createOptFlds(reportControlBlock); mmsValue->value.structure.components[4] = createOptFlds(reportControlBlock);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("BufTm"); namedVariable->name = copyString("BufTm");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -746,21 +864,21 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[5] = mmsValue->value.structure.components[5] =
MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("SqNum"); namedVariable->name = copyString("SqNum");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 16; namedVariable->typeSpec.unsignedInteger = 16;
rcb->typeSpec.structure.elements[6] = namedVariable; rcb->typeSpec.structure.elements[6] = namedVariable;
mmsValue->value.structure.components[6] = MmsValue_newUnsigned(16); mmsValue->value.structure.components[6] = MmsValue_newUnsigned(16);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("TrgOps"); namedVariable->name = copyString("TrgOps");
namedVariable->type = MMS_BIT_STRING; namedVariable->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 6; namedVariable->typeSpec.bitString = 6;
rcb->typeSpec.structure.elements[7] = namedVariable; rcb->typeSpec.structure.elements[7] = namedVariable;
mmsValue->value.structure.components[7] = createTrgOps(reportControlBlock); mmsValue->value.structure.components[7] = createTrgOps(reportControlBlock);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("IntgPd"); namedVariable->name = copyString("IntgPd");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
@ -768,26 +886,26 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[8] = mmsValue->value.structure.components[8] =
MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GI"); namedVariable->name = copyString("GI");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[9] = namedVariable; rcb->typeSpec.structure.elements[9] = namedVariable;
mmsValue->value.structure.components[9] = MmsValue_newBoolean(false); mmsValue->value.structure.components[9] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("PurgeBuf"); namedVariable->name = copyString("PurgeBuf");
namedVariable->type = MMS_BOOLEAN; namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[10] = namedVariable; rcb->typeSpec.structure.elements[10] = namedVariable;
mmsValue->value.structure.components[10] = MmsValue_newBoolean(false); mmsValue->value.structure.components[10] = MmsValue_newBoolean(false);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("EntryID"); namedVariable->name = copyString("EntryID");
namedVariable->type = MMS_OCTET_STRING; namedVariable->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = 8; namedVariable->typeSpec.octetString = 8;
rcb->typeSpec.structure.elements[11] = namedVariable; rcb->typeSpec.structure.elements[11] = namedVariable;
mmsValue->value.structure.components[11] = MmsValue_newOctetString(8, 8); mmsValue->value.structure.components[11] = MmsValue_newOctetString(8, 8);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("TimeOfEntry"); namedVariable->name = copyString("TimeOfEntry");
namedVariable->type = MMS_BINARY_TIME; namedVariable->type = MMS_BINARY_TIME;
rcb->typeSpec.structure.elements[12] = namedVariable; rcb->typeSpec.structure.elements[12] = namedVariable;
@ -795,14 +913,14 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
reportControl->timeOfEntry = mmsValue->value.structure.components[12]; reportControl->timeOfEntry = mmsValue->value.structure.components[12];
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("ResvTms"); namedVariable->name = copyString("ResvTms");
namedVariable->type = MMS_UNSIGNED; namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32; namedVariable->typeSpec.unsignedInteger = 32;
rcb->typeSpec.structure.elements[13] = namedVariable; rcb->typeSpec.structure.elements[13] = namedVariable;
mmsValue->value.structure.components[13] = MmsValue_newUnsigned(32); mmsValue->value.structure.components[13] = MmsValue_newUnsigned(32);
namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification)); namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("Owner"); namedVariable->name = copyString("Owner");
namedVariable->type = MMS_OCTET_STRING; namedVariable->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = -128; namedVariable->typeSpec.octetString = -128;
@ -849,13 +967,13 @@ MmsVariableSpecification*
Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int reportsCount) LogicalNode* logicalNode, int reportsCount)
{ {
MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1, MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification)); sizeof(MmsVariableSpecification));
namedVariable->name = copyString("BR"); namedVariable->name = copyString("BR");
namedVariable->type = MMS_STRUCTURE; namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = reportsCount; namedVariable->typeSpec.structure.elementCount = reportsCount;
namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(reportsCount, namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount,
sizeof(MmsVariableSpecification*)); sizeof(MmsVariableSpecification*));
int currentReport = 0; int currentReport = 0;
@ -886,13 +1004,13 @@ MmsVariableSpecification*
Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int reportsCount) LogicalNode* logicalNode, int reportsCount)
{ {
MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1, MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification)); sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RP"); namedVariable->name = copyString("RP");
namedVariable->type = MMS_STRUCTURE; namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = reportsCount; namedVariable->typeSpec.structure.elementCount = reportsCount;
namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(reportsCount, namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount,
sizeof(MmsVariableSpecification*)); sizeof(MmsVariableSpecification*));
int currentReport = 0; int currentReport = 0;
@ -1057,6 +1175,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
rc->sqNum = 0; rc->sqNum = 0;
MmsValue* sqNum = ReportControl_getRCBValue(rc, "SqNum");
MmsValue_setUint16(sqNum, 0U);
retVal = DATA_ACCESS_ERROR_SUCCESS; retVal = DATA_ACCESS_ERROR_SUCCESS;
goto exit_function; goto exit_function;
} }
@ -1076,7 +1198,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
MmsServerConnection_getClientAddress(connection)); MmsServerConnection_getClientAddress(connection));
if (rc->buffered == false) { if (rc->buffered == false) {
free(rc->inclusionFlags); GLOBAL_FREEMEM(rc->inclusionFlags);
rc->inclusionFlags = NULL; rc->inclusionFlags = NULL;
MmsValue* resv = ReportControl_getRCBValue(rc, "Resv"); MmsValue* resv = ReportControl_getRCBValue(rc, "Resv");
@ -1218,6 +1340,16 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function; goto exit_function;
} }
else if (strcmp(elementName, "RptID") == 0) {
MmsValue* rptId = ReportControl_getRCBValue(rc, elementName);
if (strlen(MmsValue_toString(value)) == 0)
updateWithDefaultRptId(rc, rptId);
else
MmsValue_update(rptId, value);
goto exit_function;
}
MmsValue* rcbValue = ReportControl_getRCBValue(rc, elementName); MmsValue* rcbValue = ReportControl_getRCBValue(rc, elementName);
@ -1653,7 +1785,7 @@ sendNextReportEntry(ReportControl* self)
if (self->reportBuffer->nextToTransmit == NULL) if (self->reportBuffer->nextToTransmit == NULL)
return; return;
char* localStorage = (char*) malloc(LOCAL_STORAGE_MEMORY_SIZE); /* reserve 4k for dynamic memory allocation - char* localStorage = (char*) GLOBAL_MALLOC(LOCAL_STORAGE_MEMORY_SIZE); /* reserve 64k for dynamic memory allocation -
this can be optimized - maybe there is a good guess for the this can be optimized - maybe there is a good guess for the
required memory size */ required memory size */
@ -1857,7 +1989,7 @@ sendNextReportEntry(ReportControl* self)
case REPORT_CONTROL_VALUE_UPDATE: case REPORT_CONTROL_VALUE_UPDATE:
MmsValue_setBitStringBit(reason, 3, true); MmsValue_setBitStringBit(reason, 3, true);
break; break;
case REPORT_CONTROL_NONE: default:
break; break;
} }
@ -1905,7 +2037,7 @@ return_out_of_memory:
cleanup_and_return: cleanup_and_return:
if (localStorage != NULL) if (localStorage != NULL)
free(localStorage); GLOBAL_FREEMEM(localStorage);
} }
void void

@ -23,8 +23,8 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "dynamic_model.h" #include "iec61850_dynamic_model.h"
#include "cdc.h" #include "iec61850_cdc.h"
/************************************************ /************************************************
* Constructed Attribute Classes * Constructed Attribute Classes

@ -22,9 +22,11 @@
*/ */
#include "iec61850_server.h" #include "iec61850_server.h"
#include "filesystem.h" #include "iec61850_dynamic_model.h"
#include "dynamic_model.h" #include "iec61850_config_file_parser.h"
#include "config_file_parser.h"
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "string_utilities.h" #include "string_utilities.h"
#include <stdio.h> #include <stdio.h>
@ -233,6 +235,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
indendation = 4; indendation = 4;
} }
else if (StringUtils_startsWith((char*) lineBuffer, "SG")) {
if (strcmp(currentLN->name, "LLN0") != 0)
goto exit_error;
int actSG;
int numOfSGs;
int matchedItems = sscanf((char*) lineBuffer, "SG(%i %i)", &actSG, &numOfSGs);
if (matchedItems < 2)
goto exit_error;
SettingGroupControlBlock_create(currentLN, actSG, numOfSGs);
}
else else
goto exit_error; goto exit_error;

@ -24,6 +24,8 @@
#include "iec61850_server.h" #include "iec61850_server.h"
#include "string_utilities.h" #include "string_utilities.h"
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
static void static void
iedModel_emptyVariableInitializer(void) iedModel_emptyVariableInitializer(void)
@ -34,7 +36,7 @@ iedModel_emptyVariableInitializer(void)
IedModel* IedModel*
IedModel_create(const char* name/*, MemoryAllocator allocator*/) IedModel_create(const char* name/*, MemoryAllocator allocator*/)
{ {
IedModel* self = (IedModel*) calloc(1, sizeof(IedModel)); IedModel* self = (IedModel*) GLOBAL_CALLOC(1, sizeof(IedModel));
if (name) if (name)
self->name = copyString(name); self->name = copyString(name);
@ -47,6 +49,8 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/)
self->gseCBs = NULL; self->gseCBs = NULL;
self->sgcbs = NULL;
self->initializer = iedModel_emptyVariableInitializer; self->initializer = iedModel_emptyVariableInitializer;
return self; return self;
@ -101,6 +105,24 @@ IedModel_addReportControlBlock(IedModel* self, ReportControlBlock* rcb)
} }
} }
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
static void
IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* sgcb)
{
if (self->sgcbs == NULL)
self->sgcbs = sgcb;
else {
SettingGroupControlBlock* lastSgcb = self->sgcbs;
while (lastSgcb->sibling != NULL)
lastSgcb = lastSgcb->sibling;
lastSgcb->sibling = sgcb;
}
}
#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
static void static void
IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb) IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
{ {
@ -119,7 +141,7 @@ IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
LogicalDevice* LogicalDevice*
LogicalDevice_create(const char* name, IedModel* parent) LogicalDevice_create(const char* name, IedModel* parent)
{ {
LogicalDevice* self = (LogicalDevice*) calloc(1, sizeof(LogicalDevice)); LogicalDevice* self = (LogicalDevice*) GLOBAL_CALLOC(1, sizeof(LogicalDevice));
self->name = copyString(name); self->name = copyString(name);
self->modelType = LogicalDeviceModelType; self->modelType = LogicalDeviceModelType;
@ -161,7 +183,7 @@ LogicalDevice_addLogicalNode(LogicalDevice* self, LogicalNode* lNode)
LogicalNode* LogicalNode*
LogicalNode_create(const char* name, LogicalDevice* parent) LogicalNode_create(const char* name, LogicalDevice* parent)
{ {
LogicalNode* self = (LogicalNode*) malloc(sizeof(LogicalNode)); LogicalNode* self = (LogicalNode*) GLOBAL_MALLOC(sizeof(LogicalNode));
self->name = copyString(name); self->name = copyString(name);
self->parent = (ModelNode*) parent; self->parent = (ModelNode*) parent;
@ -214,7 +236,7 @@ ReportControlBlock*
ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char* ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bool isBuffered, char*
dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd) dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd)
{ {
ReportControlBlock* self = (ReportControlBlock*) malloc(sizeof(ReportControlBlock)); ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
self->name = copyString(name); self->name = copyString(name);
self->parent = parent; self->parent = parent;
@ -243,6 +265,34 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bo
return self; return self;
} }
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
static void
LogicalNode_addSettingGroupControlBlock(LogicalNode* self, SettingGroupControlBlock* sgcb)
{
IedModel* model = (IedModel*) self->parent->parent;
IedModel_addSettingGroupControlBlock(model, sgcb);
}
SettingGroupControlBlock*
SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numOfSGs)
{
assert(actSG <= numOfSGs); /* actSG starting with 1 */
assert(strcmp(parent->name, "LLN0") == 0);
SettingGroupControlBlock* self = (SettingGroupControlBlock*) GLOBAL_MALLOC(sizeof(SettingGroupControlBlock));
self->parent = parent;
self->actSG = actSG;
self->numOfSGs = numOfSGs;
self->sibling = NULL;
LogicalNode_addSettingGroupControlBlock(parent, self);
return self;
}
#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
static void static void
LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb) LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
{ {
@ -254,7 +304,7 @@ LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
GSEControlBlock* GSEControlBlock*
GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs) GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs)
{ {
GSEControlBlock* self = (GSEControlBlock*) malloc(sizeof(GSEControlBlock)); GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock));
self->name = copyString(name); self->name = copyString(name);
self->parent = parent; self->parent = parent;
@ -291,7 +341,7 @@ GSEControlBlock_addPhyComAddress(GSEControlBlock* self, PhyComAddress* phyComAdd
PhyComAddress* PhyComAddress*
PhyComAddress_create(GSEControlBlock* parent, uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint8_t dstAddress[]) PhyComAddress_create(GSEControlBlock* parent, uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint8_t dstAddress[])
{ {
PhyComAddress* self = (PhyComAddress*) malloc(sizeof(PhyComAddress)); PhyComAddress* self = (PhyComAddress*) GLOBAL_MALLOC(sizeof(PhyComAddress));
self->vlanPriority = vlanPriority; self->vlanPriority = vlanPriority;
self->vlanId = vlanId; self->vlanId = vlanId;
@ -335,7 +385,7 @@ DataObject_addChild(DataObject* self, ModelNode* child)
DataObject* DataObject*
DataObject_create(const char* name, ModelNode* parent, int arrayElements) DataObject_create(const char* name, ModelNode* parent, int arrayElements)
{ {
DataObject* self = (DataObject*) malloc(sizeof(DataObject)); DataObject* self = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject));
self->name = copyString(name); self->name = copyString(name);
self->modelType = DataObjectModelType; self->modelType = DataObjectModelType;
@ -383,7 +433,7 @@ DataAttribute*
DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type, FunctionalConstraint fc, DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type, FunctionalConstraint fc,
uint8_t triggerOptions, int arrayElements, uint32_t sAddr) uint8_t triggerOptions, int arrayElements, uint32_t sAddr)
{ {
DataAttribute* self = (DataAttribute*) malloc(sizeof(DataAttribute)); DataAttribute* self = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute));
self->name = copyString(name); self->name = copyString(name);
self->elementCount = arrayElements; self->elementCount = arrayElements;
@ -408,7 +458,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
DataSet* DataSet*
DataSet_create(const char* name, LogicalNode* parent) DataSet_create(const char* name, LogicalNode* parent)
{ {
DataSet* self = (DataSet*) malloc(sizeof(DataSet)); DataSet* self = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet));
LogicalDevice* ld = (LogicalDevice*) parent->parent; LogicalDevice* ld = (LogicalDevice*) parent->parent;
@ -466,7 +516,7 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry)
DataSetEntry* DataSetEntry*
DataSetEntry_create(DataSet* dataSet, char* variable, int index, char* component) DataSetEntry_create(DataSet* dataSet, char* variable, int index, char* component)
{ {
DataSetEntry* self = (DataSetEntry*) malloc(sizeof(DataSetEntry)); DataSetEntry* self = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry));
self->variableName = copyString(variable); self->variableName = copyString(variable);
@ -487,7 +537,7 @@ DataSetEntry_create(DataSet* dataSet, char* variable, int index, char* component
static void static void
ModelNode_destroy(ModelNode* modelNode) ModelNode_destroy(ModelNode* modelNode)
{ {
free(modelNode->name); GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild; ModelNode* currentChild = modelNode->firstChild;
@ -508,7 +558,7 @@ ModelNode_destroy(ModelNode* modelNode)
} }
} }
free(modelNode); GLOBAL_FREEMEM(modelNode);
} }
void void
@ -521,12 +571,12 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild; LogicalDevice* ld = model->firstChild;
while (ld != NULL) { while (ld != NULL) {
free (ld->name); GLOBAL_FREEMEM (ld->name);
LogicalNode* ln = (LogicalNode*) ld->firstChild; LogicalNode* ln = (LogicalNode*) ld->firstChild;
while (ln != NULL) { while (ln != NULL) {
free(ln->name); GLOBAL_FREEMEM(ln->name);
/* delete all data objects */ /* delete all data objects */
@ -543,14 +593,14 @@ IedModel_destroy(IedModel* model)
LogicalNode* currentLn = ln; LogicalNode* currentLn = ln;
ln = (LogicalNode*) ln->sibling; ln = (LogicalNode*) ln->sibling;
free(currentLn); GLOBAL_FREEMEM(currentLn);
} }
LogicalDevice* currentLd = ld; LogicalDevice* currentLd = ld;
ld = (LogicalDevice*) ld->sibling; ld = (LogicalDevice*) ld->sibling;
free(currentLd); GLOBAL_FREEMEM(currentLd);
} }
/* delete all data sets */ /* delete all data sets */
@ -560,7 +610,7 @@ IedModel_destroy(IedModel* model)
while (dataSet != NULL) { while (dataSet != NULL) {
DataSet* nextDataSet = dataSet->sibling; DataSet* nextDataSet = dataSet->sibling;
free(dataSet->name); GLOBAL_FREEMEM(dataSet->name);
DataSetEntry* dse = dataSet->fcdas; DataSetEntry* dse = dataSet->fcdas;
@ -568,16 +618,16 @@ IedModel_destroy(IedModel* model)
DataSetEntry* nextDse = dse->sibling; DataSetEntry* nextDse = dse->sibling;
if (dse->componentName != NULL) if (dse->componentName != NULL)
free(dse->componentName); GLOBAL_FREEMEM(dse->componentName);
free(dse->variableName); GLOBAL_FREEMEM(dse->variableName);
free(dse); GLOBAL_FREEMEM(dse);
dse = nextDse; dse = nextDse;
} }
free(dataSet); GLOBAL_FREEMEM(dataSet);
dataSet = nextDataSet; dataSet = nextDataSet;
} }
@ -589,15 +639,15 @@ IedModel_destroy(IedModel* model)
while (rcb != NULL) { while (rcb != NULL) {
ReportControlBlock* nextRcb = rcb->sibling; ReportControlBlock* nextRcb = rcb->sibling;
free(rcb->name); GLOBAL_FREEMEM(rcb->name);
if (rcb->rptId) if (rcb->rptId)
free(rcb->rptId); GLOBAL_FREEMEM(rcb->rptId);
if (rcb->dataSetName) if (rcb->dataSetName)
free(rcb->dataSetName); GLOBAL_FREEMEM(rcb->dataSetName);
free(rcb); GLOBAL_FREEMEM(rcb);
rcb = nextRcb; rcb = nextRcb;
} }
@ -609,22 +659,37 @@ IedModel_destroy(IedModel* model)
while (gcb != NULL) { while (gcb != NULL) {
GSEControlBlock* nextGcb = gcb->sibling; GSEControlBlock* nextGcb = gcb->sibling;
free(gcb->name); GLOBAL_FREEMEM(gcb->name);
free(gcb->appId); GLOBAL_FREEMEM(gcb->appId);
free(gcb->dataSetName); GLOBAL_FREEMEM(gcb->dataSetName);
if (gcb->address) if (gcb->address)
free(gcb->address); GLOBAL_FREEMEM(gcb->address);
free(gcb); GLOBAL_FREEMEM(gcb);
gcb = nextGcb; gcb = nextGcb;
} }
/* delete setting controls */
SettingGroupControlBlock* sgcb = model->sgcbs;
while (sgcb != NULL) {
SettingGroupControlBlock* nextSgcb = sgcb->sibling;
GLOBAL_FREEMEM(sgcb);
sgcb = nextSgcb;
}
/* delete generic model parts */
if (model->name) if (model->name)
free(model->name); GLOBAL_FREEMEM(model->name);
free(model); GLOBAL_FREEMEM(model);
} }

@ -21,7 +21,7 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "model.h" #include "iec61850_model.h"
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
@ -42,6 +42,12 @@ setAttributeValuesToNull(ModelNode* node)
} }
} }
void
IedModel_setIedName(IedModel* self, const char* name)
{
self->name = (char*) name;
};
void void
IedModel_setAttributeValuesToNull(IedModel* iedModel) IedModel_setAttributeValuesToNull(IedModel* iedModel)
{ {
@ -99,6 +105,9 @@ IedModel_lookupDataSet(IedModel* model, const char* dataSetReference /* e.g. ie
int ldNameLen = separator - dataSetReference; int ldNameLen = separator - dataSetReference;
while (dataSet != NULL) { while (dataSet != NULL) {
//TODO use domain name instead of dataSet->logicalDeviceName !?
if (strncmp(dataSet->logicalDeviceName, dataSetReference, ldNameLen) == 0) { if (strncmp(dataSet->logicalDeviceName, dataSetReference, ldNameLen) == 0) {
if (strcmp(dataSet->name, separator + 1) == 0) { if (strcmp(dataSet->name, separator + 1) == 0) {
return dataSet; return dataSet;
@ -117,7 +126,13 @@ IedModel_getDevice(IedModel* model, const char* deviceName)
LogicalDevice* device = model->firstChild; LogicalDevice* device = model->firstChild;
while (device != NULL) { while (device != NULL) {
if (strcmp(device->name, deviceName) == 0)
char domainName[65];
strncpy(domainName, model->name, 64);
strncat(domainName, device->name, 64);
if (strcmp(domainName, deviceName) == 0)
return device; return device;
device = (LogicalDevice*) device->sibling; device = (LogicalDevice*) device->sibling;
@ -352,14 +367,16 @@ createObjectReference(ModelNode* node, char* objectReference)
LogicalDevice* lDevice = (LogicalDevice*) lNode->parent; LogicalDevice* lDevice = (LogicalDevice*) lNode->parent;
IedModel* iedModel = (IedModel*) lDevice->parent;
bufPos = 0; bufPos = 0;
int nameLength = strlen(lDevice->name); int nameLength = strlen (iedModel->name) + strlen(lDevice->name);
int i; strncpy(objectReference, iedModel->name, 64);
for (i = 0; i < nameLength; i++) { strncat(objectReference, lDevice->name, 64);
objectReference[bufPos++] = lDevice->name[i];
} bufPos += nameLength;
objectReference[bufPos++] = '/'; objectReference[bufPos++] = '/';
} }
@ -379,7 +396,7 @@ char*
ModelNode_getObjectReference(ModelNode* node, char* objectReference) ModelNode_getObjectReference(ModelNode* node, char* objectReference)
{ {
if (objectReference == NULL) if (objectReference == NULL)
objectReference = (char*) malloc(130); objectReference = (char*) GLOBAL_MALLOC(130);
int bufPos = createObjectReference(node, objectReference); int bufPos = createObjectReference(node, objectReference);
@ -443,7 +460,36 @@ ModelNode_getChild(ModelNode* self, const char* name)
inline inline
LogicalNode* LogicalNode*
LogicalDevice_getLogicalNode(LogicalDevice* device, const char* nodeName) LogicalDevice_getLogicalNode(LogicalDevice* self, const char* nodeName)
{ {
return (LogicalNode*) ModelNode_getChild((ModelNode*) device, nodeName); return (LogicalNode*) ModelNode_getChild((ModelNode*) self, nodeName);
}
SettingGroupControlBlock*
LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self)
{
IedModel* model = (IedModel*) self->parent;
if (model == NULL)
return NULL;
LogicalNode* ln = LogicalDevice_getLogicalNode(self, "LLN0");
if (ln == NULL) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: logical node LLN0 not found!\n");
return NULL;
}
SettingGroupControlBlock* sgcb = model->sgcbs;
while (sgcb != NULL) {
if (sgcb->parent == ln)
return sgcb;
sgcb = sgcb->sibling;
}
return NULL;
} }

@ -91,11 +91,21 @@ AcseAuthenticationParameter_setPassword(AcseAuthenticationParameter self, char*
typedef bool typedef bool
(*AcseAuthenticator)(void* parameter, AcseAuthenticationParameter authParameter, void** securityToken); (*AcseAuthenticator)(void* parameter, AcseAuthenticationParameter authParameter, void** securityToken);
/**
* \brief COTP T selector
*
* To not use T SEL set size to 0.
*/
typedef struct {
uint8_t size; /** 0 .. 4 - 0 means T-selector is not present */
uint8_t value[4]; /** T-selector value - value[0] */
} TSelector;
struct sIsoConnectionParameters struct sIsoConnectionParameters
{ {
AcseAuthenticationParameter acseAuthParameter; AcseAuthenticationParameter acseAuthParameter;
char* hostname; const char* hostname;
int tcpPort; int tcpPort;
uint8_t remoteApTitle[10]; uint8_t remoteApTitle[10];
@ -103,14 +113,14 @@ struct sIsoConnectionParameters
int remoteAEQualifier; int remoteAEQualifier;
uint32_t remotePSelector; uint32_t remotePSelector;
uint16_t remoteSSelector; uint16_t remoteSSelector;
uint16_t remoteTSelector; TSelector remoteTSelector;
uint8_t localApTitle[10]; uint8_t localApTitle[10];
int localApTitleLen; int localApTitleLen;
int localAEQualifier; int localAEQualifier;
uint32_t localPSelector; uint32_t localPSelector;
uint16_t localSSelector; uint16_t localSSelector;
uint16_t localTSelector; TSelector localTSelector;
}; };
@ -161,7 +171,7 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s
* \param tcpPort the TCP port number of the server * \param tcpPort the TCP port number of the server
*/ */
void void
IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, char* hostname, int tcpPort); IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort);
/** /**
* \brief set the remote AP-Title and AE-Qualifier * \brief set the remote AP-Title and AE-Qualifier
@ -176,7 +186,7 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, char* hos
* \param aeQualifier the AP-qualifier * \param aeQualifier the AP-qualifier
*/ */
void void
IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, char* apTitle, int aeQualifier); IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier);
/** /**
* \brief set remote addresses for the lower layers * \brief set remote addresses for the lower layers
@ -188,10 +198,10 @@ IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, char* apT
* \param self the IsoConnectionParameters instance * \param self the IsoConnectionParameters instance
* \param pSelector the P-Selector (presentation layer address) * \param pSelector the P-Selector (presentation layer address)
* \param sSelector the S-Selector (session layer address) * \param sSelector the S-Selector (session layer address)
* \param tSelector the T-Selector (transpport layer address) * \param tSelector the T-Selector (ISO transport layer address)
*/ */
void void
IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, uint32_t pSelector, uint16_t sSelector, uint16_t tSelector); IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, uint32_t pSelector, uint16_t sSelector, TSelector tSelector);
/** /**
* \brief set the local AP-Title and AE-Qualifier * \brief set the local AP-Title and AE-Qualifier
@ -218,10 +228,10 @@ IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, char* apTi
* \param self the IsoConnectionParameters instance * \param self the IsoConnectionParameters instance
* \param pSelector the P-Selector (presentation layer address) * \param pSelector the P-Selector (presentation layer address)
* \param sSelector the S-Selector (session layer address) * \param sSelector the S-Selector (session layer address)
* \param tSelector the T-Selector (transpport layer address) * \param tSelector the T-Selector (ISO transport layer address)
*/ */
void void
IsoConnectionParameters_setLocalAddresses(IsoConnectionParameters self, uint32_t pSelector, uint16_t sSelector, uint16_t tSelector); IsoConnectionParameters_setLocalAddresses(IsoConnectionParameters self, uint32_t pSelector, uint16_t sSelector, TSelector tSelector);
/**@}*/ /**@}*/

@ -119,6 +119,22 @@ IsoServer_startListening(IsoServer self);
void void
IsoServer_stopListening(IsoServer self); IsoServer_stopListening(IsoServer self);
void
IsoServer_startListeningThreadless(IsoServer self);
/**
* for non-threaded operation
*/
void
IsoServer_processIncomingMessages(IsoServer self);
int
IsoServer_waitReady(IsoServer self, unsigned int timeoutMs);
void
IsoServer_stopListeningThreadless(IsoServer self);
void void
IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection); IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection);

@ -38,7 +38,7 @@ extern "C" {
#include "mms_common.h" #include "mms_common.h"
#include "mms_type_spec.h" #include "mms_type_spec.h"
#include "mms_value.h" #include "mms_value.h"
#include "iso_client_connection.h" #include "iso_connection_parameters.h"
#include "linked_list.h" #include "linked_list.h"
/** /**
@ -79,7 +79,7 @@ MmsConnection
MmsConnection_create(void); MmsConnection_create(void);
/** /**
* \brief Set the request timeout for this connection * \brief Set the request timeout in ms for this connection
* *
* \param self MmsConnection instance to operate on * \param self MmsConnection instance to operate on
* \param timeoutInMs request timeout in milliseconds * \param timeoutInMs request timeout in milliseconds
@ -87,6 +87,15 @@ MmsConnection_create(void);
void void
MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs); MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs);
/**
* \brief Set the connect timeout in ms for this connection instance
*
* \param self MmsConnection instance to operate on
* \param timeoutInMs connect timeout in milliseconds
*/
void
MmsConnection_setConnectTimeout(MmsConnection self, uint32_t timeoutInMs);
/** /**
* \brief Install a handler function for MMS information reports (unsolicited messages from the server). * \brief Install a handler function for MMS information reports (unsolicited messages from the server).
* *
@ -163,7 +172,18 @@ MmsConnection_destroy(MmsConnection self);
* \return true on success. false if the connection attempt failed. * \return true on success. false if the connection attempt failed.
*/ */
bool bool
MmsConnection_connect(MmsConnection self, MmsError* mmsError, char* serverName, int serverPort); MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort);
/**
* \brief Close the connection - not recommended
*
* This service simply closes the TCP socket without any hand-shaking with the server.
* This behavior is not specified. Use with care!
*
* \param self MmsConnection instance to operate on
*/
void
MmsConnection_close(MmsConnection self);
/** /**
* \brief Uses the MMS/ACSE abort service to close the connection to the server * \brief Uses the MMS/ACSE abort service to close the connection to the server
@ -233,7 +253,7 @@ MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError);
* \return the of domain specific variable names or NULL if the request failed. * \return the of domain specific variable names or NULL if the request failed.
*/ */
LinkedList /* <char*> */ LinkedList /* <char*> */
MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, char* domainId); MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, const char* domainId);
/** /**
* \brief Get the names of all named variable lists present in a MMS domain of the server. * \brief Get the names of all named variable lists present in a MMS domain of the server.
@ -247,7 +267,7 @@ MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, cha
* \return the domain specific named variable list names or NULL if the request failed. * \return the domain specific named variable list names or NULL if the request failed.
*/ */
LinkedList /* <char*> */ LinkedList /* <char*> */
MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* mmsError, char* domainId); MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* mmsError, const char* domainId);
/** /**
* \brief Get the names of all named variable lists associated with this client connection. * \brief Get the names of all named variable lists associated with this client connection.
@ -275,7 +295,7 @@ MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsErr
* either be a simple value or a complex value or array. * either be a simple value or a complex value or array.
*/ */
MmsValue* MmsValue*
MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, char* domainId, char* itemId); MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId);
/** /**
* \brief Read an element of a single array variable from the server. * \brief Read an element of a single array variable from the server.
@ -292,7 +312,7 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, char* domainI
* array elements of numberOfElements > 0. * array elements of numberOfElements > 0.
*/ */
MmsValue* MmsValue*
MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, char* domainId, char* itemId, MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
uint32_t startIndex, uint32_t numberOfElements); uint32_t startIndex, uint32_t numberOfElements);
/** /**
@ -308,7 +328,7 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, char* do
* in the order as they appeared in the item ID list. * in the order as they appeared in the item ID list.
*/ */
MmsValue* MmsValue*
MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, char* domainId, MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
LinkedList /*<char*>*/ items); LinkedList /*<char*>*/ items);
/** /**
@ -322,10 +342,10 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, char
*/ */
void void
MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
char* domainId, char* itemId, MmsValue* value); const char* domainId, const char* itemId, MmsValue* value);
/** /**
* \brief Write multiple variables at the server (NOT YET IMPLEMENTED). * \brief Write multiple variables to the server.
* *
* This function will write multiple variables at the server. * This function will write multiple variables at the server.
* *
@ -342,7 +362,7 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
* write. * write.
*/ */
void void
MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, char* domainId, MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values, LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
LinkedList* /* <MmsValue*> */ accessResults); LinkedList* /* <MmsValue*> */ accessResults);
@ -358,7 +378,7 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, cha
*/ */
MmsVariableSpecification* MmsVariableSpecification*
MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError, MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError,
char* domainId, char* itemId); const char* domainId, const char* itemId);
/** /**
* \brief Read the values of a domain specific named variable list * \brief Read the values of a domain specific named variable list
@ -374,8 +394,8 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError
* in the order as they appeared in named variable list definition. * in the order as they appeared in named variable list definition.
*/ */
MmsValue* MmsValue*
MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, char* domainId, MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId,
char* listName, bool specWithResult); const char* listName, bool specWithResult);
/** /**
@ -392,7 +412,7 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError
*/ */
MmsValue* MmsValue*
MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError,
char* listName, bool specWithResult); const char* listName, bool specWithResult);
/** /**
* \brief Define a new named variable list at the server. * \brief Define a new named variable list at the server.
@ -405,8 +425,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self,
* elements have to be of type MmsVariableAccessSpecification*. * elements have to be of type MmsVariableAccessSpecification*.
*/ */
void void
MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, char* domainId, MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId,
char* listName, LinkedList variableSpecs); const char* listName, LinkedList variableSpecs);
/** /**
@ -420,7 +440,7 @@ MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, ch
*/ */
void void
MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError,
char* listName, LinkedList variableSpecs); const char* listName, LinkedList variableSpecs);
/** /**
* \brief Read the entry list of a named variable list at the server. * \brief Read the entry list of a named variable list at the server.
@ -436,7 +456,7 @@ MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, Mms
*/ */
LinkedList /* <MmsVariableAccessSpecification*> */ LinkedList /* <MmsVariableAccessSpecification*> */
MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsError,
char* domainId, char* listName, bool* deletable); const char* domainId, const char* listName, bool* deletable);
@ -451,7 +471,7 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr
*/ */
LinkedList /* <MmsVariableAccessSpecification*> */ LinkedList /* <MmsVariableAccessSpecification*> */
MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError, MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError,
char* listName, bool* deletable); const char* listName, bool* deletable);
/** /**
* \brief Delete a named variable list at the server. * \brief Delete a named variable list at the server.
@ -462,7 +482,7 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se
* \param listName the name of the named variable list * \param listName the name of the named variable list
*/ */
void void
MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, char* domainId, char* listName); MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName);
/** /**
* \brief Delete an association specific named variable list at the server. * \brief Delete an association specific named variable list at the server.
@ -473,7 +493,7 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, ch
*/ */
void void
MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, MmsError* mmsError, MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, MmsError* mmsError,
char* listName); const char* listName);
/** /**
* \brief Create a new MmsVariableSpecification that can be used to define named variable lists. * \brief Create a new MmsVariableSpecification that can be used to define named variable lists.
@ -579,7 +599,7 @@ typedef void
* \return the FRSM ID (file read state machine) handle of the opened file * \return the FRSM ID (file read state machine) handle of the opened file
*/ */
int32_t int32_t
MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, char* filename, uint32_t initialPosition, MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filename, uint32_t initialPosition,
uint32_t* fileSize, uint64_t* lastModified); uint32_t* fileSize, uint64_t* lastModified);
/** /**
@ -614,7 +634,7 @@ MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId);
* \param fileName name of the file to delete * \param fileName name of the file to delete
*/ */
void void
MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, char* fileName); MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fileName);
/** /**
* \brief rename the file with the specified name * \brief rename the file with the specified name
@ -625,7 +645,7 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, char* fileName)
* \param newFileName new name of the file * \param newFileName new name of the file
*/ */
void void
MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, char* currentFileName, char* newFileName); MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName);
/** /**
* \brief get the file directory of the server. * \brief get the file directory of the server.
@ -644,7 +664,7 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, char* currentFi
* \return (more follows) true if more data is available * \return (more follows) true if more data is available
*/ */
bool bool
MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, char* fileSpecification, char* continueAfter, MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter,
MmsFileDirectoryHandler handler, void* handlerParameter); MmsFileDirectoryHandler handler, void* handlerParameter);

@ -68,6 +68,7 @@ typedef enum
MMS_ERROR_DEFINITION_OBJECT_ATTRIBUTE_INCONSISTENT = 36, MMS_ERROR_DEFINITION_OBJECT_ATTRIBUTE_INCONSISTENT = 36,
MMS_ERROR_RESOURCE_OTHER = 40, MMS_ERROR_RESOURCE_OTHER = 40,
MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE = 41,
MMS_ERROR_SERVICE_OTHER = 50, MMS_ERROR_SERVICE_OTHER = 50,

@ -1,7 +1,7 @@
/* /*
* mms_server.h * mms_server.h
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -49,7 +49,7 @@ typedef struct sMmsServer* MmsServer;
#define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5 #define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5
#endif #endif
#include "filesystem.h" #include "hal_filesystem.h"
typedef struct { typedef struct {
int32_t frsmId; int32_t frsmId;
@ -78,10 +78,13 @@ typedef struct sMmsServerConnection {
} MmsServerConnection; } MmsServerConnection;
typedef MmsValue* (*ReadVariableHandler)(void* parameter, MmsDomain* domain, typedef MmsValue* (*MmsReadVariableHandler)(void* parameter, MmsDomain* domain,
char* variableId, MmsServerConnection* connection); char* variableId, MmsServerConnection* connection);
typedef MmsDataAccessError (*WriteVariableHandler)(void* parameter, typedef MmsDataAccessError (*MmsReadAccessHandler) (void* parameter, MmsDomain* domain,
char* variableId, MmsServerConnection* connection);
typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter,
MmsDomain* domain, char* variableId, MmsValue* value, MmsDomain* domain, char* variableId, MmsValue* value,
MmsServerConnection* connection); MmsServerConnection* connection);
@ -92,11 +95,17 @@ MmsServer
MmsServer_create(IsoServer isoServer, MmsDevice* device); MmsServer_create(IsoServer isoServer, MmsDevice* device);
void void
MmsServer_installReadHandler(MmsServer self, ReadVariableHandler, MmsServer_destroy(MmsServer self);
void
MmsServer_installReadHandler(MmsServer self, MmsReadVariableHandler,
void* parameter); void* parameter);
void void
MmsServer_installWriteHandler(MmsServer self, WriteVariableHandler, MmsServer_installReadAccessHandler(MmsServer self, MmsReadAccessHandler, void* parameter);
void
MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler,
void* parameter); void* parameter);
/** /**
@ -148,17 +157,68 @@ MmsServer_insertIntoCache(MmsServer self, MmsDomain* domain, char* itemId,
void void
MmsServer_setDevice(MmsServer self, MmsDevice* device); MmsServer_setDevice(MmsServer self, MmsDevice* device);
/* Start a new server thread and listen for incoming connections */ /***************************************************
* Functions for multi-threaded operation mode
***************************************************/
/**
* \brief Start a new server thread and listen for incoming connections
*
* \param self the MmsServer instance to operate on
* \param tcpPort the TCP port the server is listening on.
*/
void void
MmsServer_startListening(MmsServer self, int tcpPort); MmsServer_startListening(MmsServer self, int tcpPort);
/* Stop server thread an all open connection threads */ /**
* \brief Stop server thread an all open connection threads
*
* \param self the MmsServer instance to operate on
*/
void void
MmsServer_stopListening(MmsServer self); MmsServer_stopListening(MmsServer self);
/***************************************************
* Functions for threadless operation mode
***************************************************/
/**
* \brief Start a new server in non-threaded operation mode
*
* \param self the MmsServer instance to operate on
* \param tcpPort the TCP port the server is listening on.
*/
void
MmsServer_startListeningThreadless(MmsServer self, int tcpPort);
/**
* \brief Wait for the server to come ready in non-threaded operation mode
*
* \param self the MmsServer instance to operate on
* \param timeoutMs maximum number of milliseconds to wait
* \return 1 if the server is ready, 0 if not or -1 on error
*/
int
MmsServer_waitReady(MmsServer self, unsigned int timeoutMs);
/**
* \brief Handle client connections (for non-threaded operation mode)
*
* This function is listening for new client connections and handles incoming
* requests for existing client connections.
*
* \param self the MmsServer instance to operate on
*/
void void
MmsServer_destroy(MmsServer self); MmsServer_handleIncomingMessages(MmsServer self);
/**
* \brief Stop the server (for non-threaded operation mode)
*
* \param self the MmsServer instance to operate on
*/
void
MmsServer_stopListeningThreadless(MmsServer self);
/*************************************************** /***************************************************

@ -5,7 +5,7 @@
* Complex types are arrays or structures of simple and complex types. * Complex types are arrays or structures of simple and complex types.
* They also represent MMS NamedVariables. * They also represent MMS NamedVariables.
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -36,31 +36,80 @@
extern "C" { extern "C" {
#endif #endif
/**
* \addtogroup common_api_group
*/
/**@{*/
/**
* \defgroup MMS_VAR_SPEC MmsVariableSpecification data type specifications
*/
/**@{*/
/** /**
* \brief Delete MmsTypeSpecification object (recursive). * \brief Delete MmsTypeSpecification object (recursive).
* *
* \param self the MmsVariableSpecification object * \param self the MmsVariableSpecification instance
*/ */
void void
MmsVariableSpecification_destroy(MmsVariableSpecification* self); MmsVariableSpecification_destroy(MmsVariableSpecification* self);
/**
* \brief Get the corresponding child of value according to childId.
*
* This function assumes that value is the corresponding value of the MMS variable self.
* Given the relative name of a child of self this function returns the corresponding child
* of the value object. Note: the child name has to be provided in MMS mapping syntax (with
* "$" sign as separator between path name elements!
*
* \param self the MmsVariableSpecification instance
* \param value the MmsValue instance
* \param childId the relative MMS name to the child MMS variable (with "$" separators!)
*
*/
MmsValue* MmsValue*
MmsVariableSpecification_getChildValue(MmsVariableSpecification* self, MmsValue* value, char* childId); MmsVariableSpecification_getChildValue(MmsVariableSpecification* self, MmsValue* value, const char* childId);
/**
* \brief Get the child of self specified by its relative name
*
* \param self the MmsVariableSpecification instance
* \param nameId the relative MMS name to the child MMS variable (with "$" separators!)
*
* \return the variable specification of the child or NULL if not existing.
*/
MmsVariableSpecification* MmsVariableSpecification*
MmsVariableSpecification_getNamedVariableRecursive(MmsVariableSpecification* variable, char* nameId); MmsVariableSpecification_getNamedVariableRecursive(MmsVariableSpecification* self, const char* nameId);
/**
* \brief get the MMS type of the variable
*
* \param self the MmsVariableSpecification instance
*
* \return the MMS type of the variable
*/
MmsType MmsType
MmsVariableSpecification_getType(MmsVariableSpecification* self); MmsVariableSpecification_getType(MmsVariableSpecification* self);
char* /**
* \brief get the name of the variable
*
* Note: the return string is only valid as long as the MmsVariableSpecification
* instance exists!
*
* \param self the MmsVariableSpecification instance
*
* \return the name of the variable
*/
const char*
MmsVariableSpecification_getName(MmsVariableSpecification* self); MmsVariableSpecification_getName(MmsVariableSpecification* self);
LinkedList /* <char*> */ LinkedList /* <char*> */
MmsVariableSpecification_getStructureElements(MmsVariableSpecification* self); MmsVariableSpecification_getStructureElements(MmsVariableSpecification* self);
/** /**
* \brief returns the number of elements if the type is a complex type (structure, array) * \brief returns the number of elements if the type is a complex type (structure, array) or the
* bit size of integers, unsigned integers, floats, bit strings, visible and MMS strings and octet strings.
* *
* \param self the MmsVariableSpecification object * \param self the MmsVariableSpecification object
* \return the number of elements or -1 if not applicable * \return the number of elements or -1 if not applicable
@ -77,6 +126,10 @@ MmsVariableSpecification_getArrayElementSpecification(MmsVariableSpecification*
int int
MmsVariableSpecification_getExponentWidth(MmsVariableSpecification* self); MmsVariableSpecification_getExponentWidth(MmsVariableSpecification* self);
/**@}*/
/**@}*/
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -35,11 +35,18 @@ typedef enum ATTRIBUTE_PACKED {
MMS_VALUE_OBJECT_ACCESS_UNSUPPORTED MMS_VALUE_OBJECT_ACCESS_UNSUPPORTED
} MmsValueIndication; } MmsValueIndication;
/**
* \addtogroup MMS_VAR_SPEC
*/
/**@{*/
/** /**
* Type definition for MMS Named Variables * Type definition for MMS Named Variables
*/ */
typedef struct sMmsVariableSpecification MmsVariableSpecification; typedef struct sMmsVariableSpecification MmsVariableSpecification;
/**@}*/
struct ATTRIBUTE_PACKED sMmsVariableSpecification { struct ATTRIBUTE_PACKED sMmsVariableSpecification {
MmsType type; MmsType type;
char* name; char* name;

@ -355,6 +355,9 @@ MmsValue_setAllBitStringBits(MmsValue* self);
/** /**
* \brief Convert a bit string to an unsigned integer * \brief Convert a bit string to an unsigned integer
* *
* This function assumes that the first bit in the bit string is the
* least significant bit (little endian bit order).
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING. * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
*/ */
uint32_t uint32_t
@ -363,12 +366,38 @@ MmsValue_getBitStringAsInteger(MmsValue* self);
/** /**
* \brief Convert an unsigned integer to a bit string * \brief Convert an unsigned integer to a bit string
* *
* The integer representation in the bit string assumes the first bit is the
* least significant bit (little endian bit order).
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING. * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
* \param intValue the integer value that is used to set the bit string * \param intValue the integer value that is used to set the bit string
*/ */
void void
MmsValue_setBitStringFromInteger(MmsValue* self, uint32_t intValue); MmsValue_setBitStringFromInteger(MmsValue* self, uint32_t intValue);
/**
* \brief Convert a bit string to an unsigned integer (big endian bit order)
*
* This function assumes that the first bit in the bit string is the
* most significant bit (big endian bit order).
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
*/
uint32_t
MmsValue_getBitStringAsIntegerBigEndian(MmsValue* self);
/**
* \brief Convert an unsigned integer to a bit string (big endian bit order)
*
* The integer representation in the bit string assumes the first bit is the
* most significant bit (big endian bit order).
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
* \param intValue the integer value that is used to set the bit string
*/
void
MmsValue_setBitStringFromIntegerBigEndian(MmsValue* self, uint32_t intValue);
/** /**
* \brief Update an MmsValue object of UtcTime type with a timestamp in s * \brief Update an MmsValue object of UtcTime type with a timestamp in s
* *
@ -638,7 +667,7 @@ MmsValue_clone(MmsValue* self);
* \param self the MmsValue instance that will be cloned * \param self the MmsValue instance that will be cloned
* \param destinationAddress the start address of the user provided buffer * \param destinationAddress the start address of the user provided buffer
* *
* \return an MmsValue instance that is an exact copy of the given instance. * \return a pointer to the position in the buffer just after the last byte written.
*/ */
uint8_t* uint8_t*
MmsValue_cloneToBuffer(MmsValue* self, uint8_t* destinationAddress); MmsValue_cloneToBuffer(MmsValue* self, uint8_t* destinationAddress);

@ -44,7 +44,7 @@ int
BerEncoder_encodeBoolean(uint8_t tag, bool value, uint8_t* buffer, int bufPos); BerEncoder_encodeBoolean(uint8_t tag, bool value, uint8_t* buffer, int bufPos);
int int
BerEncoder_encodeStringWithTag(uint8_t tag, char* string, uint8_t* buffer, int bufPos); BerEncoder_encodeStringWithTag(uint8_t tag, const char* string, uint8_t* buffer, int bufPos);
int int
BerEncoder_encodeOctetString(uint8_t tag, uint8_t* octetString, uint32_t octetStringSize, uint8_t* buffer, int bufPos); BerEncoder_encodeOctetString(uint8_t tag, uint8_t* octetString, uint32_t octetStringSize, uint8_t* buffer, int bufPos);
@ -79,7 +79,7 @@ int
BerEncoder_determineLengthSize(uint32_t length); BerEncoder_determineLengthSize(uint32_t length);
int int
BerEncoder_determineEncodedStringSize(char* string); BerEncoder_determineEncodedStringSize(const char* string);
int int
BerEncoder_determineEncodedBitStringSize(int bitStringSize); BerEncoder_determineEncodedBitStringSize(int bitStringSize);
@ -89,7 +89,7 @@ BerEncoder_determineEncodedBitStringSize(int bitStringSize);
*/ */
int int
BerEncoder_encodeOIDToBuffer(char* oidString, uint8_t* buffer, int maxBufLen); BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufLen);
void void
BerEncoder_revertByteOrder(uint8_t* octets, const int size); BerEncoder_revertByteOrder(uint8_t* octets, const int size);

@ -1,7 +1,7 @@
/* /*
* cotp.h * cotp.h
* *
* Copyright 2013 Michael Zillgith * Copyright 2013, 2014 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -26,15 +26,14 @@
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
#include "byte_buffer.h" #include "byte_buffer.h"
#include "byte_stream.h"
#include "buffer_chain.h" #include "buffer_chain.h"
#include "socket.h" #include "hal_socket.h"
#include "iso_connection_parameters.h" #include "iso_connection_parameters.h"
typedef struct { typedef struct {
int32_t tsap_id_src; TSelector tSelSrc;
int32_t tsap_id_dst; TSelector tSelDst;
uint8_t tpdu_size; uint8_t tpduSize;
} CotpOptions; } CotpOptions;
typedef struct { typedef struct {
@ -46,13 +45,26 @@ typedef struct {
CotpOptions options; CotpOptions options;
bool isLastDataUnit; bool isLastDataUnit;
ByteBuffer* payload; ByteBuffer* payload;
ByteBuffer* writeBuffer; ByteBuffer* writeBuffer; /* buffer to store TPKT packet to send */
ByteBuffer* readBuffer; /* buffer to store received TPKT packet */
uint16_t packetSize; /* size of the packet currently received */
} CotpConnection; } CotpConnection;
typedef enum { typedef enum {
OK, ERROR, CONNECT_INDICATION, DATA_INDICATION, DISCONNECT_INDICATION COTP_OK,
COTP_ERROR,
COTP_CONNECT_INDICATION,
COTP_DATA_INDICATION,
COTP_DISCONNECT_INDICATION,
COTP_MORE_FRAGMENTS_FOLLOW
} CotpIndication; } CotpIndication;
typedef enum {
TPKT_PACKET_COMPLETE = 0,
TPKT_WAITING = 1,
TPKT_ERROR = 2
} TpktState;
int /* in byte */ int /* in byte */
CotpConnection_getTpduSize(CotpConnection* self); CotpConnection_getTpduSize(CotpConnection* self);
@ -60,14 +72,18 @@ void
CotpConnection_setTpduSize(CotpConnection* self, int tpduSize /* in byte */); CotpConnection_setTpduSize(CotpConnection* self, int tpduSize /* in byte */);
void void
CotpConnection_init(CotpConnection* self, Socket socket, ByteBuffer* payloadBuffer); CotpConnection_init(CotpConnection* self, Socket socket,
ByteBuffer* payloadBuffer, ByteBuffer* readBuffer, ByteBuffer* writeBuffer);
void
CotpConnection_destroy(CotpConnection* self);
CotpIndication CotpIndication
CotpConnection_parseIncomingMessage(CotpConnection* self); CotpConnection_parseIncomingMessage(CotpConnection* self);
void
CotpConnection_resetPayload(CotpConnection* self);
TpktState
CotpConnection_readToTpktBuffer(CotpConnection* self);
CotpIndication CotpIndication
CotpConnection_sendConnectionRequestMessage(CotpConnection* self, IsoConnectionParameters isoParameters); CotpConnection_sendConnectionRequestMessage(CotpConnection* self, IsoConnectionParameters isoParameters);

@ -59,7 +59,7 @@ IsoClientConnection_destroy(IsoClientConnection self);
void void
IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params, IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params,
ByteBuffer* payload); ByteBuffer* payload, uint32_t connectTimeoutInMs);
void void
IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payload); IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payload);

@ -24,11 +24,20 @@
#ifndef ISO_SERVER_PRIVATE_H_ #ifndef ISO_SERVER_PRIVATE_H_
#define ISO_SERVER_PRIVATE_H_ #define ISO_SERVER_PRIVATE_H_
#include "socket.h" #include "hal_socket.h"
IsoConnection IsoConnection
IsoConnection_create(Socket socket, IsoServer isoServer); IsoConnection_create(Socket socket, IsoServer isoServer);
void
IsoConnection_destroy(IsoConnection self);
void
IsoConnection_handleTcpConnection(IsoConnection self);
void
IsoConnection_addHandleSet(const IsoConnection self, HandleSet handles);
void void
private_IsoServer_increaseConnectionCounter(IsoServer self); private_IsoServer_increaseConnectionCounter(IsoServer self);
@ -50,4 +59,7 @@ IsoServer_userLock(IsoServer self);
void void
IsoServer_userUnlock(IsoServer self); IsoServer_userUnlock(IsoServer self);
bool
IsoConnection_isRunning(IsoConnection self);
#endif /* ISO_SERVER_PRIVATE_H_ */ #endif /* ISO_SERVER_PRIVATE_H_ */

@ -27,9 +27,10 @@
#include "MmsPdu.h" #include "MmsPdu.h"
#include "linked_list.h" #include "linked_list.h"
#include "mms_client_connection.h" #include "mms_client_connection.h"
#include "iso_client_connection.h"
#include "ber_decode.h" #include "ber_decode.h"
#include "thread.h" #include "hal_thread.h"
#ifndef DEBUG_MMS_CLIENT #ifndef DEBUG_MMS_CLIENT
#define DEBUG_MMS_CLIENT 0 #define DEBUG_MMS_CLIENT 0
@ -60,20 +61,20 @@ struct sMmsConnection {
uint32_t lastInvokeId; uint32_t lastInvokeId;
Semaphore lastResponseLock; Semaphore lastResponseLock;
uint32_t responseInvokeId; volatile uint32_t responseInvokeId;
ByteBuffer* lastResponse; ByteBuffer* lastResponse;
uint32_t lastResponseBufPos; volatile uint32_t lastResponseBufPos;
MmsError lastResponseError; volatile MmsError lastResponseError;
Semaphore outstandingCallsLock; Semaphore outstandingCallsLock;
uint32_t* outstandingCalls; uint32_t* outstandingCalls;
uint32_t requestTimeout; uint32_t requestTimeout;
uint32_t connectTimeout;
IsoClientConnection isoClient; IsoClientConnection isoClient;
AssociationState associationState; AssociationState associationState;
ConnectionState connectionState; ConnectionState connectionState;
//uint8_t* buffer;
MmsConnectionParameters parameters; MmsConnectionParameters parameters;
IsoConnectionParameters isoParameters; IsoConnectionParameters isoParameters;
@ -114,47 +115,47 @@ MmsPdu_t*
mmsClient_createConfirmedRequestPdu(uint32_t invokeId); mmsClient_createConfirmedRequestPdu(uint32_t invokeId);
int int
mmsClient_createMmsGetNameListRequestVMDspecific(long invokeId, ByteBuffer* writeBuffer, char* continueAfter); mmsClient_createMmsGetNameListRequestVMDspecific(long invokeId, ByteBuffer* writeBuffer, const char* continueAfter);
bool bool
mmsClient_parseGetNameListResponse(LinkedList* nameList, ByteBuffer* message, uint32_t* invokeId); mmsClient_parseGetNameListResponse(LinkedList* nameList, ByteBuffer* message, uint32_t* invokeId);
int int
mmsClient_createGetNameListRequestDomainOrVMDSpecific(long invokeId, char* domainName, mmsClient_createGetNameListRequestDomainOrVMDSpecific(long invokeId, const char* domainName,
ByteBuffer* writeBuffer, MmsObjectClass objectClass, char* continueAfter); ByteBuffer* writeBuffer, MmsObjectClass objectClass, const char* continueAfter);
MmsValue* MmsValue*
mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool createArray); mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool createArray);
int int
mmsClient_createReadRequest(uint32_t invokeId, char* domainId, char* itemId, ByteBuffer* writeBuffer); mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char* itemId, ByteBuffer* writeBuffer);
int int
mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, char* domainId, char* itemId, mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer); uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer);
int int
mmsClient_createReadRequestMultipleValues(uint32_t invokeId, char* domainId, LinkedList /*<char*>*/ items, mmsClient_createReadRequestMultipleValues(uint32_t invokeId, const char* domainId, LinkedList /*<char*>*/ items,
ByteBuffer* writeBuffer); ByteBuffer* writeBuffer);
int int
mmsClient_createReadNamedVariableListRequest(uint32_t invokeId, char* domainId, char* itemId, mmsClient_createReadNamedVariableListRequest(uint32_t invokeId, const char* domainId, const char* itemId,
ByteBuffer* writeBuffer, bool specWithResult); ByteBuffer* writeBuffer, bool specWithResult);
int int
mmsClient_createReadAssociationSpecificNamedVariableListRequest( mmsClient_createReadAssociationSpecificNamedVariableListRequest(
uint32_t invokeId, uint32_t invokeId,
char* itemId, const char* itemId,
ByteBuffer* writeBuffer, ByteBuffer* writeBuffer,
bool specWithResult); bool specWithResult);
void void
mmsClient_createGetNamedVariableListAttributesRequest(uint32_t invokeId, ByteBuffer* writeBuffer, mmsClient_createGetNamedVariableListAttributesRequest(uint32_t invokeId, ByteBuffer* writeBuffer,
char* domainId, char* listNameId); const char* domainId, const char* listNameId);
void void
mmsClient_createGetNamedVariableListAttributesRequestAssociationSpecific(uint32_t invokeId, mmsClient_createGetNamedVariableListAttributesRequestAssociationSpecific(uint32_t invokeId,
ByteBuffer* writeBuffer, char* listNameId); ByteBuffer* writeBuffer, const char* listNameId);
LinkedList LinkedList
mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint32_t* invokeId, mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint32_t* invokeId,
@ -163,7 +164,7 @@ mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint3
int int
mmsClient_createGetVariableAccessAttributesRequest( mmsClient_createGetVariableAccessAttributesRequest(
uint32_t invokeId, uint32_t invokeId,
char* domainId, char* itemId, const char* domainId, const char* itemId,
ByteBuffer* writeBuffer); ByteBuffer* writeBuffer);
MmsVariableSpecification* MmsVariableSpecification*
@ -177,16 +178,16 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M
int itemCount, LinkedList* accessResults); int itemCount, LinkedList* accessResults);
int int
mmsClient_createWriteRequest(uint32_t invokeId, char* domainId, char* itemId, MmsValue* value, mmsClient_createWriteRequest(uint32_t invokeId, const char* domainId, const char* itemId, MmsValue* value,
ByteBuffer* writeBuffer); ByteBuffer* writeBuffer);
int int
mmsClient_createWriteMultipleItemsRequest(uint32_t invokeId, char* domainId, LinkedList itemIds, LinkedList values, mmsClient_createWriteMultipleItemsRequest(uint32_t invokeId, const char* domainId, LinkedList itemIds, LinkedList values,
ByteBuffer* writeBuffer); ByteBuffer* writeBuffer);
void void
mmsClient_createDefineNamedVariableListRequest(uint32_t invokeId, ByteBuffer* writeBuffer, mmsClient_createDefineNamedVariableListRequest(uint32_t invokeId, ByteBuffer* writeBuffer,
char* domainId, char* listNameId, LinkedList /*<char*>*/ listOfVariables, const char* domainId, const char* listNameId, LinkedList /*<char*>*/ listOfVariables,
bool associationSpecific); bool associationSpecific);
bool bool
@ -194,7 +195,7 @@ mmsClient_parseDefineNamedVariableResponse(ByteBuffer* message, uint32_t* invoke
void void
mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeBuffer, mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeBuffer,
char* domainId, char* listNameId); const char* domainId, const char* listNameId);
bool bool
mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId); mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId);
@ -203,7 +204,7 @@ void
mmsClient_createDeleteAssociationSpecificNamedVariableListRequest( mmsClient_createDeleteAssociationSpecificNamedVariableListRequest(
long invokeId, long invokeId,
ByteBuffer* writeBuffer, ByteBuffer* writeBuffer,
char* listNameId); const char* listNameId);
void void
mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request); mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request);
@ -218,7 +219,7 @@ bool
mmsClient_parseStatusResponse(MmsConnection self, int* vmdLogicalStatus, int* vmdPhysicalStatus); mmsClient_parseStatusResponse(MmsConnection self, int* vmdLogicalStatus, int* vmdPhysicalStatus);
void void
mmsClient_createFileOpenRequest(uint32_t invokeId, ByteBuffer* request, char* fileName, uint32_t initialPosition); mmsClient_createFileOpenRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName, uint32_t initialPosition);
bool bool
mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified); mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified);
@ -233,13 +234,13 @@ void
mmsClient_createFileCloseRequest(uint32_t invokeId, ByteBuffer* request, int32_t frsmId); mmsClient_createFileCloseRequest(uint32_t invokeId, ByteBuffer* request, int32_t frsmId);
void void
mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, char* currentFileName, char* newFileName); mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const char* currentFileName, const char* newFileName);
void void
mmsClient_createFileDeleteRequest(uint32_t invokeId, ByteBuffer* request, char* fileName); mmsClient_createFileDeleteRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName);
void void
mmsClient_createFileDirectoryRequest(uint32_t invokeId, ByteBuffer* request, char* fileSpecification, char* continueAfter); mmsClient_createFileDirectoryRequest(uint32_t invokeId, ByteBuffer* request, const char* fileSpecification, const char* continueAfter);
bool bool
mmsClient_parseFileDirectoryResponse(MmsConnection self, MmsFileDirectoryHandler handler, void* handlerParameter, mmsClient_parseFileDirectoryResponse(MmsConnection self, MmsFileDirectoryHandler handler, void* handlerParameter,
@ -253,6 +254,6 @@ mmsClient_createConcludeRequest(MmsConnection self, ByteBuffer* message);
int int
mmsClient_createMmsGetNameListRequestAssociationSpecific(long invokeId, ByteBuffer* writeBuffer, mmsClient_createMmsGetNameListRequestAssociationSpecific(long invokeId, ByteBuffer* writeBuffer,
char* continueAfter); const char* continueAfter);
#endif /* MMS_MSG_INTERNAL_H_ */ #endif /* MMS_MSG_INTERNAL_H_ */

@ -94,7 +94,8 @@ MmsServerConnection_sendInformationReportListOfVariables(
); );
void void
MmsServerConnection_sendWriteResponse(MmsServerConnection* self, uint32_t invokeId, MmsDataAccessError indication); MmsServerConnection_sendWriteResponse(MmsServerConnection* self, uint32_t invokeId, MmsDataAccessError indication,
bool handlerMode);
uint32_t uint32_t

@ -32,11 +32,12 @@
#include "mms_device_model.h" #include "mms_device_model.h"
#include "mms_common_internal.h" #include "mms_common_internal.h"
#include "stack_config.h" #include "stack_config.h"
#include "mms_server.h"
#include "byte_buffer.h" #include "byte_buffer.h"
#include "string_utilities.h" #include "string_utilities.h"
#include "map.h" #include "map.h"
#include "thread.h" #include "hal_thread.h"
#include "ber_encoder.h" #include "ber_encoder.h"
#include "ber_decode.h" #include "ber_decode.h"
@ -61,10 +62,13 @@ struct sMmsServer {
IsoServer isoServer; IsoServer isoServer;
MmsDevice* device; MmsDevice* device;
ReadVariableHandler readHandler; MmsReadVariableHandler readHandler;
void* readHandlerParameter; void* readHandlerParameter;
WriteVariableHandler writeHandler; MmsReadAccessHandler readAccessHandler;
void* readAccessHandlerParameter;
MmsWriteVariableHandler writeHandler;
void* writeHandlerParameter; void* writeHandlerParameter;
MmsConnectionHandler connectionHandler; MmsConnectionHandler connectionHandler;
@ -73,7 +77,10 @@ struct sMmsServer {
Map openConnections; Map openConnections;
Map valueCaches; Map valueCaches;
bool isLocked; bool isLocked;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore modelMutex; Semaphore modelMutex;
#endif
#if MMS_STATUS_SERVICE == 1 #if MMS_STATUS_SERVICE == 1
int vmdLogicalStatus; int vmdLogicalStatus;

@ -1,453 +0,0 @@
/*
* iso_client_connection.c
*
* Client side representation of the ISO stack (COTP, session, presentation, ACSE)
*
* Copyright 2013 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.
*/
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "socket.h"
#include "thread.h"
#include "cotp.h"
#include "iso_session.h"
#include "iso_presentation.h"
#include "iso_client_connection.h"
#include "acse.h"
#ifndef DEBUG_ISO_CLIENT
#ifdef DEBUG
#define DEBUG_ISO_CLIENT 1
#else
#define DEBUG_ISO_CLIENT 0
#endif /*DEBUG */
#endif /* DEBUG_ISO_SERVER */
#define STATE_IDLE 0
#define STATE_ASSOCIATED 1
#define STATE_ERROR 2
#define ISO_CLIENT_BUFFER_SIZE CONFIG_MMS_MAXIMUM_PDU_SIZE + 100
struct sIsoClientConnection
{
IsoIndicationCallback callback;
void* callbackParameter;
int state;
Socket socket;
CotpConnection* cotpConnection;
IsoPresentation* presentation;
IsoSession* session;
AcseConnection acseConnection;
uint8_t* sendBuffer; /* send buffer */
uint8_t* cotpBuf; /* receive buffer */
ByteBuffer* cotpBuffer;
ByteBuffer* transmitPayloadBuffer;
Semaphore transmitBufferMutex;
ByteBuffer* receivePayloadBuffer;
Semaphore receiveBufferMutex;
Thread thread;
};
static void*
connectionHandlingThread(void* threadParameter)
{
IsoClientConnection self = (IsoClientConnection) threadParameter;
IsoSessionIndication sessionIndication;
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: new connection\n");
Semaphore_wait(self->receiveBufferMutex);
while (CotpConnection_parseIncomingMessage(self->cotpConnection) == DATA_INDICATION) {
sessionIndication =
IsoSession_parseMessage(self->session,
CotpConnection_getPayload(self->cotpConnection));
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: parse message\n");
if (sessionIndication != SESSION_DATA) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid session message\n");
break;
}
if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n");
break;
}
self->callback(ISO_IND_DATA, self->callbackParameter,
&(self->presentation->nextPayload));
/* wait for user to release the buffer */
Semaphore_wait(self->receiveBufferMutex);
}
self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: exit connection\n");
return NULL;
}
IsoClientConnection
IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParameter)
{
IsoClientConnection self = (IsoClientConnection) calloc(1, sizeof(struct sIsoClientConnection));
self->callback = callback;
self->callbackParameter = callbackParameter;
self->state = STATE_IDLE;
self->sendBuffer = (uint8_t*) malloc(ISO_CLIENT_BUFFER_SIZE);
self->transmitPayloadBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
self->transmitPayloadBuffer->buffer = self->sendBuffer;
self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE;
self->receivePayloadBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
self->transmitBufferMutex = Semaphore_create(1);
self->receiveBufferMutex = Semaphore_create(1);
return self;
}
void
IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer)
{
struct sBufferChain payloadBCMemory;
BufferChain payload = &payloadBCMemory;
BufferChain_init(payload, payloadBuffer->size, payloadBuffer->size, NULL, payloadBuffer->buffer);
struct sBufferChain presentationBCMemory;
BufferChain presentationBuffer = &presentationBCMemory;
presentationBuffer->buffer = self->sendBuffer + payload->length;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
IsoPresentation_createUserData(self->presentation, presentationBuffer, payload);
struct sBufferChain sessionBufferBCMemory;
BufferChain sessionBuffer = &sessionBufferBCMemory;
IsoSession_createDataSpdu(self->session, sessionBuffer, presentationBuffer);
CotpIndication indication = CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
/* release transmit buffer for use by API client */
Semaphore_post(self->transmitBufferMutex);
if (indication != OK)
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n");
}
void
IsoClientConnection_close(IsoClientConnection self)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_close\n");
if (self->socket != NULL) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call socket close\n");
Socket_destroy(self->socket);
}
self->state = STATE_IDLE;
}
void
IsoClientConnection_destroy(IsoClientConnection self)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_destroy\n");
if (self->state == STATE_ASSOCIATED) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call IsoClientConnection_close\n");
IsoClientConnection_close(self);
}
if (self->thread != NULL)
Thread_destroy(self->thread);
if (self->cotpBuf != NULL)
free(self->cotpBuf);
if (self->cotpBuffer != NULL)
free(self->cotpBuffer);
if (self->cotpConnection != NULL) {
CotpConnection_destroy(self->cotpConnection);
free(self->cotpConnection);
}
if (self->session != NULL)
free(self->session);
if (self->presentation != NULL)
free(self->presentation);
free(self->transmitPayloadBuffer);
free(self->receivePayloadBuffer);
Semaphore_destroy(self->receiveBufferMutex);
Semaphore_destroy(self->transmitBufferMutex);
free(self->sendBuffer);
free(self);
}
void
IsoClientConnection_abort(IsoClientConnection self)
{
//TODO block other messages from being sent
IsoClientConnection_allocateTransmitBuffer(self);
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
acseBuffer->buffer = self->sendBuffer;
acseBuffer->nextPart = NULL;
AcseConnection_createAbortMessage(NULL, acseBuffer, false);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->nextPart = acseBuffer;
IsoPresentation_createAbortUserMessage(self->presentation, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - presentationBuffer->length;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
sessionBuffer->nextPart = presentationBuffer;
IsoSession_createAbortSpdu(self->session, sessionBuffer, presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
}
void
IsoClientConnection_release(IsoClientConnection self)
{
//TODO block other messages from being sent
IsoClientConnection_allocateTransmitBuffer(self);
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
acseBuffer->buffer = self->sendBuffer;
acseBuffer->nextPart = NULL;
AcseConnection_createReleaseRequestMessage(NULL, acseBuffer);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->nextPart = acseBuffer;
IsoPresentation_createUserDataACSE(self->presentation, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - presentationBuffer->length;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
sessionBuffer->nextPart = presentationBuffer;
IsoSession_createFinishSpdu(NULL, sessionBuffer, presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
}
void
IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params,
ByteBuffer* payload)
{
Socket socket = TcpSocket_create();
self->socket = socket;
if (!Socket_connect(socket, params->hostname, params->tcpPort))
goto returnError;
self->cotpBuf = (uint8_t*) malloc(ISO_CLIENT_BUFFER_SIZE);
self->cotpBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
ByteBuffer_wrap(self->cotpBuffer, self->cotpBuf, 0, ISO_CLIENT_BUFFER_SIZE);
self->cotpConnection = (CotpConnection*) calloc(1, sizeof(CotpConnection));
CotpConnection_init(self->cotpConnection, socket, self->cotpBuffer);
/* COTP (ISO transport) handshake */
CotpIndication cotpIndication =
CotpConnection_sendConnectionRequestMessage(self->cotpConnection, params);
cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != CONNECT_INDICATION)
goto returnError;
/* Upper layers handshake */
struct sBufferChain sAcsePayload;
BufferChain acsePayload = &sAcsePayload;
acsePayload->buffer = payload->buffer;
acsePayload->partLength = payload->size;
acsePayload->length = payload->size;
acsePayload->nextPart = NULL;
AcseConnection_init(&(self->acseConnection), NULL, NULL);
AcseAuthenticationParameter authParameter = NULL;
if (params != NULL)
authParameter = params->acseAuthParameter;
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->buffer = self->sendBuffer + payload->size;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acsePayload->length;
AcseConnection_createAssociateRequestMessage(&(self->acseConnection), params, acseBuffer, acsePayload,
authParameter);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
self->presentation = (IsoPresentation*) calloc(1, sizeof(IsoPresentation));
IsoPresentation_init(self->presentation);
IsoPresentation_createConnectPdu(self->presentation, params, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
self->session = (IsoSession*) calloc(1, sizeof(IsoSession));
IsoSession_init(self->session);
IsoSession_createConnectSpdu(self->session, params, sessionBuffer,
presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != DATA_INDICATION)
goto returnError;
IsoSessionIndication sessionIndication;
sessionIndication =
IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection));
if (sessionIndication != SESSION_CONNECT) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no session connect indication\n");
goto returnError;
}
if (!IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session))) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no presentation ok indication\n");
goto returnError;
}
AcseIndication acseIndication;
acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload);
if (acseIndication != ACSE_ASSOCIATE) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n");
goto returnError;
}
ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer,
self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize);
Semaphore_wait(self->receiveBufferMutex);
self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer);
self->state = STATE_ASSOCIATED;
self->thread = Thread_create(connectionHandlingThread, self, false);
Thread_start(self->thread);
return;
returnError:
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
self->state = STATE_ERROR;
Socket_destroy(self->socket);
self->socket = NULL;
return;
}
ByteBuffer*
IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self)
{
Semaphore_wait(self->transmitBufferMutex);
self->transmitPayloadBuffer->size = 0;
self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE;
return self->transmitPayloadBuffer;
}
void
IsoClientConnection_releaseReceiveBuffer(IsoClientConnection self)
{
Semaphore_post(self->receiveBufferMutex);
}
Loading…
Cancel
Save