diff --git a/CHANGELOG b/CHANGELOG
index 2019cf3b..9ff17fb5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
------------------------
- Client: Added adjustable timeout to connect functions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4c597cc4..a7aad2d4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,7 +11,7 @@ project(libiec61850)
set(LIB_VERSION_MAJOR "0")
set(LIB_VERSION_MINOR "8")
-set(LIB_VERSION_PATCH "2")
+set(LIB_VERSION_PATCH "3")
# feature checks
include(CheckLibraryExists)
diff --git a/config/stack_config.h b/config/stack_config.h
index e995ab0d..31a6678a 100644
--- a/config/stack_config.h
+++ b/config/stack_config.h
@@ -131,7 +131,7 @@
/* default results for MMS identify service */
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
#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 */
#define CONFIG_VIRTUAL_FILESTORE_BASEPATH "./vmd-filestore/"
diff --git a/src/common/byte_stream.c b/src/common/byte_stream.c
deleted file mode 100644
index e0677bdf..00000000
--- a/src/common/byte_stream.c
+++ /dev/null
@@ -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 .
- *
- * 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;
-}
-
diff --git a/src/common/byte_stream.h b/src/common/byte_stream.h
deleted file mode 100644
index 0728b604..00000000
--- a/src/common/byte_stream.h
+++ /dev/null
@@ -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 .
- *
- * 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_ */
diff --git a/src/common/array_list.h b/src/common/inc/array_list.h
similarity index 100%
rename from src/common/array_list.h
rename to src/common/inc/array_list.h
diff --git a/src/common/buffer_chain.h b/src/common/inc/buffer_chain.h
similarity index 100%
rename from src/common/buffer_chain.h
rename to src/common/inc/buffer_chain.h
diff --git a/src/common/byte_buffer.h b/src/common/inc/byte_buffer.h
similarity index 100%
rename from src/common/byte_buffer.h
rename to src/common/inc/byte_buffer.h
diff --git a/src/common/conversions.h b/src/common/inc/conversions.h
similarity index 100%
rename from src/common/conversions.h
rename to src/common/inc/conversions.h
diff --git a/src/common/inc/lib_memory.h b/src/common/inc/lib_memory.h
new file mode 100644
index 00000000..506a24ca
--- /dev/null
+++ b/src/common/inc/lib_memory.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 .
+ *
+ * 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
+
+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_ */
diff --git a/src/common/libiec61850_common_api.h b/src/common/inc/libiec61850_common_api.h
similarity index 90%
rename from src/common/libiec61850_common_api.h
rename to src/common/inc/libiec61850_common_api.h
index 5121d8be..eb828656 100644
--- a/src/common/libiec61850_common_api.h
+++ b/src/common/inc/libiec61850_common_api.h
@@ -8,7 +8,6 @@
#include
#include
#include
-#include
#include
#include
@@ -18,7 +17,7 @@
#define ATTRIBUTE_PACKED
#endif
-#include "time_hal.h"
+#include "hal_time.h"
#include "mms_value.h"
#endif /* LIBIEC61850_COMMON_API_INCLUDES_H_ */
diff --git a/src/common/libiec61850_platform_includes.h b/src/common/inc/libiec61850_platform_includes.h
similarity index 93%
rename from src/common/libiec61850_platform_includes.h
rename to src/common/inc/libiec61850_platform_includes.h
index 252c1011..6a73b3ef 100644
--- a/src/common/libiec61850_platform_includes.h
+++ b/src/common/inc/libiec61850_platform_includes.h
@@ -19,4 +19,6 @@
#include
+#include "lib_memory.h"
+
#endif /* LIBIEC61850_PLATFORM_INCLUDES_H_ */
diff --git a/src/common/linked_list.h b/src/common/inc/linked_list.h
similarity index 100%
rename from src/common/linked_list.h
rename to src/common/inc/linked_list.h
diff --git a/src/common/map.h b/src/common/inc/map.h
similarity index 100%
rename from src/common/map.h
rename to src/common/inc/map.h
diff --git a/src/common/mem_alloc_linked_list.h b/src/common/inc/mem_alloc_linked_list.h
similarity index 100%
rename from src/common/mem_alloc_linked_list.h
rename to src/common/inc/mem_alloc_linked_list.h
diff --git a/src/common/simple_allocator.h b/src/common/inc/simple_allocator.h
similarity index 100%
rename from src/common/simple_allocator.h
rename to src/common/inc/simple_allocator.h
diff --git a/src/common/string_map.h b/src/common/inc/string_map.h
similarity index 100%
rename from src/common/string_map.h
rename to src/common/inc/string_map.h
diff --git a/src/common/string_utilities.h b/src/common/inc/string_utilities.h
similarity index 97%
rename from src/common/string_utilities.h
rename to src/common/inc/string_utilities.h
index eeeec621..72f44bf8 100644
--- a/src/common/string_utilities.h
+++ b/src/common/inc/string_utilities.h
@@ -30,7 +30,7 @@ char*
copyString(const char* string);
char*
-copyStringToBuffer(char* string, char* buffer);
+copyStringToBuffer(const char* string, char* buffer);
char*
copySubString(char* startPos, char* endPos);
diff --git a/src/hal/ethernet/ethernet.h b/src/hal/inc/hal_ethernet.h
similarity index 61%
rename from src/hal/ethernet/ethernet.h
rename to src/hal/inc/hal_ethernet.h
index 65f72bce..7287e30a 100644
--- a/src/hal/ethernet/ethernet.h
+++ b/src/hal/inc/hal_ethernet.h
@@ -1,7 +1,7 @@
/*
- * ethernet.h
+ * ethernet_hal.h
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -21,10 +21,11 @@
* See COPYING file for the complete license text.
*/
-#ifndef ETHERNET_H_
-#define ETHERNET_H_
+#ifndef ETHERNET_HAL_H_
+#define ETHERNET_HAL_H_
#include
+#include
#ifdef __cplusplus
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;
/**
- * 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.
*
@@ -59,7 +60,7 @@ void
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.
*
* \param interfaceId the ID of the Ethernet interface
@@ -68,17 +69,45 @@ Ethernet_getInterfaceMACAddress(char* interfaceId, uint8_t* addr);
EthernetSocket
Ethernet_createSocket(char* interfaceId, uint8_t* destAddress);
+/**
+ * \brief destroy the ethernet socket
+ *
+ * \param ethSocket the ethernet socket handle
+ */
void
Ethernet_destroySocket(EthernetSocket ethSocket);
void
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
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
-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 /* ETHERNET_H_ */
+#endif /* ETHERNET_HAL_H_ */
diff --git a/src/hal/filesystem/filesystem.h b/src/hal/inc/hal_filesystem.h
similarity index 97%
rename from src/hal/filesystem/filesystem.h
rename to src/hal/inc/hal_filesystem.h
index af7c2429..10272958 100644
--- a/src/hal/filesystem/filesystem.h
+++ b/src/hal/inc/hal_filesystem.h
@@ -1,5 +1,5 @@
/*
- * filesystem.h
+ * filesystem_hal.h
*
* Copyright 2014 Michael Zillgith
*
@@ -21,8 +21,8 @@
* See COPYING file for the complete license text.
*/
-#ifndef FILESYSTEM_H_
-#define FILESYSTEM_H_
+#ifndef FILESYSTEM_HAL_H_
+#define FILESYSTEM_HAL_H_
#ifdef __cplusplus
extern "C" {
@@ -170,4 +170,4 @@ FileSystem_setBasePath(char* basePath);
}
#endif
-#endif /* FILESYSTEM_H_ */
+#endif /* FILESYSTEM_HAL_H_ */
diff --git a/src/hal/inc/hal_socket.h b/src/hal/inc/hal_socket.h
new file mode 100644
index 00000000..ce950d4a
--- /dev/null
+++ b/src/hal/inc/hal_socket.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 .
+ *
+ * See COPYING file for the complete license text.
+ */
+
+#ifndef SOCKET_HAL_H_
+#define SOCKET_HAL_H_
+
+#include
+#include
+
+#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_ */
diff --git a/src/hal/thread/thread.h b/src/hal/inc/hal_thread.h
similarity index 95%
rename from src/hal/thread/thread.h
rename to src/hal/inc/hal_thread.h
index 977d6c5e..7d719f95 100644
--- a/src/hal/thread/thread.h
+++ b/src/hal/inc/hal_thread.h
@@ -1,9 +1,9 @@
/*
- * thread.h
+ * thread_hal.h
*
* Multi-threading abstraction layer
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -23,8 +23,8 @@
* See COPYING file for the complete license text.
*/
-#ifndef THREAD_H_
-#define THREAD_H_
+#ifndef THREAD_HAL_H_
+#define THREAD_HAL_H_
#include
@@ -112,4 +112,4 @@ Semaphore_destroy(Semaphore self);
#endif
-#endif /* THREAD_H_ */
+#endif /* THREAD_HAL_H_ */
diff --git a/src/hal/time/time_hal.h b/src/hal/inc/hal_time.h
similarity index 100%
rename from src/hal/time/time_hal.h
rename to src/hal/inc/hal_time.h
diff --git a/src/hal/platform_endian.h b/src/hal/inc/platform_endian.h
similarity index 100%
rename from src/hal/platform_endian.h
rename to src/hal/inc/platform_endian.h
diff --git a/src/hal/socket/socket.h b/src/hal/socket/socket.h
deleted file mode 100644
index 41e84e43..00000000
--- a/src/hal/socket/socket.h
+++ /dev/null
@@ -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 .
- *
- * See COPYING file for the complete license text.
- */
-
-#ifndef SOCKET_H_
-#define SOCKET_H_
-
-#include
-
-#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_ */
diff --git a/src/iedclient/impl/client_control.c b/src/iec61850/client/client_control.c
similarity index 95%
rename from src/iedclient/impl/client_control.c
rename to src/iec61850/client/client_control.c
index 75e3dfb7..80576830 100644
--- a/src/iedclient/impl/client_control.c
+++ b/src/iec61850/client/client_control.c
@@ -23,13 +23,10 @@
#include "libiec61850_platform_includes.h"
-#include "iec61850_client.h"
-
#include "stack_config.h"
+#include "iec61850_client.h"
#include "mms_client_connection.h"
-#include "mms_mapping.h"
-
#include "ied_connection_private.h"
#include
@@ -42,6 +39,9 @@
#define DEBUG_IED_CLIENT 0
#endif
+char*
+MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer);
+
struct sControlObjectClient
{
ControlModel ctlModel;
@@ -99,6 +99,14 @@ convertToMmsAndInsertFC(char* newItemId, char* originalObjectName, char* fc)
newItemId[dstIndex] = 0;
}
+static void
+resetLastApplError(ControlObjectClient self)
+{
+ self->lastApplError.error = 0;
+ self->lastApplError.addCause = ADD_CAUSE_UNKNOWN;
+ self->lastApplError.ctlNum = 0;
+}
+
ControlObjectClient
ControlObjectClient_create(char* objectReference, IedConnection connection)
{
@@ -192,7 +200,7 @@ ControlObjectClient_create(char* objectReference, IedConnection connection)
return NULL;
}
- ControlObjectClient self = (ControlObjectClient) calloc(1, sizeof(struct sControlObjectClient));
+ ControlObjectClient self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
self->objectReference = copyString(objectReference);
self->connection = connection;
@@ -227,7 +235,7 @@ ControlObjectClient_create(char* objectReference, IedConnection connection)
void
ControlObjectClient_destroy(ControlObjectClient self)
{
- free(self->objectReference);
+ GLOBAL_FREEMEM(self->objectReference);
private_IedConnection_removeControlClient(self->connection, self);
@@ -235,9 +243,9 @@ ControlObjectClient_destroy(ControlObjectClient self)
MmsValue_delete(self->ctlVal);
if (self->orIdent != NULL)
- free(self->orIdent);
+ GLOBAL_FREEMEM(self->orIdent);
- free(self);
+ GLOBAL_FREEMEM(self);
}
void
@@ -261,10 +269,10 @@ ControlObjectClient_getControlModel(ControlObjectClient self)
}
void
-ControlObjectClient_setOrigin(ControlObjectClient self, char* orIdent, int orCat)
+ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat)
{
if (self->orIdent != NULL)
- free(self->orIdent);
+ GLOBAL_FREEMEM(self->orIdent);
self->orIdent = copyString(orIdent);
self->orCat = orCat;
@@ -295,6 +303,8 @@ createOriginValue(ControlObjectClient self)
bool
ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
{
+ resetLastApplError(self);
+
MmsValue* operParameters;
if (self->hasTimeActivatedMode)
@@ -369,6 +379,8 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
bool
ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
{
+ resetLastApplError(self);
+
char domainId[65];
char itemId[130];
@@ -444,6 +456,8 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
bool
ControlObjectClient_select(ControlObjectClient self)
{
+ resetLastApplError(self);
+
char domainId[65];
char itemId[130];
@@ -461,7 +475,7 @@ ControlObjectClient_select(ControlObjectClient self)
MmsValue* value = MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId);
- int selected = false;
+ bool selected = false;
if (value == NULL) {
if (DEBUG_IED_CLIENT)
@@ -501,6 +515,8 @@ ControlObjectClient_select(ControlObjectClient self)
bool
ControlObjectClient_cancel(ControlObjectClient self)
{
+ resetLastApplError(self);
+
MmsValue* cancelParameters;
if (self->hasTimeActivatedMode)
diff --git a/src/iedclient/impl/client_goose_control.c b/src/iec61850/client/client_goose_control.c
similarity index 98%
rename from src/iedclient/impl/client_goose_control.c
rename to src/iec61850/client/client_goose_control.c
index 24ba4b39..ead96722 100644
--- a/src/iedclient/impl/client_goose_control.c
+++ b/src/iec61850/client/client_goose_control.c
@@ -47,7 +47,7 @@ struct sClientGooseControlBlock {
ClientGooseControlBlock
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);
@@ -57,7 +57,7 @@ ClientGooseControlBlock_create(char* objectReference)
void
ClientGooseControlBlock_destroy(ClientGooseControlBlock self)
{
- free(self->objectReference);
+ GLOBAL_FREEMEM(self->objectReference);
MmsValue_deleteIfNotNull(self->goEna);
MmsValue_deleteIfNotNull(self->goID);
@@ -69,7 +69,7 @@ ClientGooseControlBlock_destroy(ClientGooseControlBlock self)
MmsValue_deleteIfNotNull(self->maxTime);
MmsValue_deleteIfNotNull(self->fixedOffs);
- free(self);
+ GLOBAL_FREEMEM(self);
}
bool
diff --git a/src/iedclient/impl/client_report.c b/src/iec61850/client/client_report.c
similarity index 65%
rename from src/iedclient/impl/client_report.c
rename to src/iec61850/client/client_report.c
index 747bd1cd..c02aa3c3 100644
--- a/src/iedclient/impl/client_report.c
+++ b/src/iec61850/client/client_report.c
@@ -38,10 +38,23 @@ struct sClientReport
char* rcbReference;
char* rptId;
MmsValue* entryId;
+ MmsValue* dataReferences;
MmsValue* dataSetValues;
ReasonForInclusion* reasonForInclusion;
+
+ /* Presence flags for optional elements */
+ bool hasDataSetName;
+ bool hasReasonForInclusion;
+ bool hasSequenceNumber;
+ bool hasDataReference;
+ bool hasConfRev;
bool hasTimestamp;
+ bool hasBufOverflow;
+
uint64_t timestamp;
+ uint16_t seqNum;
+ uint32_t confRev;
+ bool bufOverflow;
};
char*
@@ -68,7 +81,7 @@ ReasonForInclusion_getValueAsString(ReasonForInclusion reasonCode)
ClientReport
ClientReport_create()
{
- ClientReport self = (ClientReport) calloc(1, sizeof(struct sClientReport));
+ ClientReport self = (ClientReport) GLOBAL_CALLOC(1, sizeof(struct sClientReport));
return self;
}
@@ -79,18 +92,21 @@ ClientReport_destroy(ClientReport self)
if (self->entryId)
MmsValue_delete(self->entryId);
- free(self->rcbReference);
+ GLOBAL_FREEMEM(self->rcbReference);
if (self->rptId != NULL)
- free(self->rptId);
+ GLOBAL_FREEMEM(self->rptId);
if (self->dataSetValues != NULL)
MmsValue_delete(self->dataSetValues);
+ if (self->dataReferences != NULL)
+ MmsValue_delete(self->dataReferences);
+
if (self->reasonForInclusion != NULL)
- free(self->reasonForInclusion);
+ GLOBAL_FREEMEM(self->reasonForInclusion);
- free(self);
+ GLOBAL_FREEMEM(self);
}
char*
@@ -132,6 +148,57 @@ ClientReport_getTimestamp(ClientReport self)
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*
ClientReport_getDataSetValues(ClientReport self)
{
@@ -238,27 +305,36 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
MmsValue* rptIdValue = MmsValue_getElement(value, 0);
LinkedList element = LinkedList_getNext(self->enabledReports);
- ClientReport report = NULL;
+ ClientReport matchingReport = NULL;
while (element != NULL) {
- report = (ClientReport) element->data;
+ ClientReport report = (ClientReport) element->data;
- char* rptId =report->rptId;
+ char* rptId = report->rptId;
if (rptId == NULL)
rptId = report->rcbReference;
if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0) {
+ matchingReport = report;
break;
}
element = LinkedList_getNext(element);
}
- if (report == NULL)
+ if (matchingReport == NULL)
return;
- if (DEBUG_IED_CLIENT)
+ 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)
printf("DEBUG_IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue));
MmsValue* optFlds = MmsValue_getElement(value, 1);
@@ -266,52 +342,75 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
int inclusionIndex = 2;
/* 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++;
+ }
/* has report-timestamp */
if (MmsValue_getBitStringBit(optFlds, 2) == true) {
MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex);
if (MmsValue_getType(timeStampValue) == MMS_BINARY_TIME) {
- report->hasTimestamp = true;
- report->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue);
+ matchingReport->hasTimestamp = true;
+ matchingReport->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue);
}
inclusionIndex++;
}
- if (MmsValue_getBitStringBit(optFlds, 4) == true) /* check if data set name is present */
- inclusionIndex++;
+ /* check if data set name is present */
+ if (MmsValue_getBitStringBit(optFlds, 4) == true) {
+ matchingReport->hasDataSetName = true;
+ inclusionIndex++;
+ }
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: Found enabled report!\n");
/* skip bufOvfl */
- if (MmsValue_getBitStringBit(optFlds, 6) == true)
+ if (MmsValue_getBitStringBit(optFlds, 6) == true) {
+ matchingReport->hasBufOverflow = false;
inclusionIndex++;
+ }
- /* skip entryId */
+ /* check for entryId */
if (MmsValue_getBitStringBit(optFlds, 7) == true) {
MmsValue* entryId = MmsValue_getElement(value, inclusionIndex);
- if (report->entryId != NULL) {
+ if (matchingReport->entryId != NULL) {
- if (!MmsValue_update(report->entryId, entryId)) {
- MmsValue_delete(report->entryId);
- report->entryId = MmsValue_clone(entryId);
+ if (!MmsValue_update(matchingReport->entryId, entryId)) {
+ MmsValue_delete(matchingReport->entryId);
+ matchingReport->entryId = MmsValue_clone(entryId);
}
}
else {
- report->entryId = MmsValue_clone(entryId);
+ matchingReport->entryId = MmsValue_clone(entryId);
}
inclusionIndex++;
}
- /* skip confRev */
- if (MmsValue_getBitStringBit(optFlds, 8) == true)
+ /* check for confRev */
+ 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++;
+ }
+
/* skip segmentation fields */
if (MmsValue_getBitStringBit(optFlds, 9) == true)
@@ -329,26 +428,35 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
int valueIndex = inclusionIndex + 1;
/* 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;
+ }
int i;
- if (report->dataSetValues == NULL) {
- report->dataSetValues = MmsValue_createEmtpyArray(dataSetSize);
- report->reasonForInclusion = (ReasonForInclusion*)
- malloc(sizeof(ReasonForInclusion) * dataSetSize);
+ if (matchingReport->dataSetValues == NULL) {
+ matchingReport->dataSetValues = MmsValue_createEmtpyArray(dataSetSize);
+ matchingReport->reasonForInclusion = (ReasonForInclusion*)
+ GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize);
int 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);
+
+ if (hasReasonForInclusion)
+ matchingReport->hasReasonForInclusion = true;
+
int reasonForInclusionIndex = valueIndex + includedElements;
for (i = 0; i < dataSetSize; i++) {
@@ -372,26 +480,26 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
MmsValue* reasonForInclusion = MmsValue_getElement(value, reasonForInclusionIndex);
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)
- report->reasonForInclusion[i] = REASON_QUALITY_CHANGE;
+ matchingReport->reasonForInclusion[i] = REASON_QUALITY_CHANGE;
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)
- report->reasonForInclusion[i] = REASON_INTEGRITY;
+ matchingReport->reasonForInclusion[i] = REASON_INTEGRITY;
else if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true)
- report->reasonForInclusion[i] = REASON_GI;
+ matchingReport->reasonForInclusion[i] = REASON_GI;
}
else {
- report->reasonForInclusion[i] = REASON_UNKNOWN;
+ matchingReport->reasonForInclusion[i] = REASON_UNKNOWN;
}
}
else {
- report->reasonForInclusion[i] = REASON_NOT_INCLUDED;
+ matchingReport->reasonForInclusion[i] = REASON_NOT_INCLUDED;
}
}
- if (report->callback != NULL) {
- report->callback(report->callbackParameter, report);
+ if (matchingReport->callback != NULL) {
+ matchingReport->callback(matchingReport->callbackParameter, matchingReport);
}
}
diff --git a/src/iedclient/impl/client_report_control.c b/src/iec61850/client/client_report_control.c
similarity index 95%
rename from src/iedclient/impl/client_report_control.c
rename to src/iec61850/client/client_report_control.c
index 7178bc20..daac8336 100644
--- a/src/iedclient/impl/client_report_control.c
+++ b/src/iec61850/client/client_report_control.c
@@ -48,7 +48,7 @@ isBufferedRcb(char* objectReference)
ClientReportControlBlock
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->isBuffered = isBufferedRcb(objectReference);
@@ -59,7 +59,7 @@ ClientReportControlBlock_create(char* objectReference)
void
ClientReportControlBlock_destroy(ClientReportControlBlock self)
{
- free(self->objectReference);
+ GLOBAL_FREEMEM(self->objectReference);
MmsValue_deleteIfNotNull(self->rptId);
MmsValue_deleteIfNotNull(self->rptEna);
@@ -78,7 +78,7 @@ ClientReportControlBlock_destroy(ClientReportControlBlock self)
MmsValue_deleteIfNotNull(self->resvTms);
MmsValue_deleteIfNotNull(self->owner);
- free(self);
+ GLOBAL_FREEMEM(self);
}
char*
@@ -180,13 +180,13 @@ ClientReportControlBlock_getConfRev(ClientReportControlBlock self)
int
ClientReportControlBlock_getOptFlds(ClientReportControlBlock self)
{
- return MmsValue_getBitStringAsInteger(self->optFlds);
+ return (MmsValue_getBitStringAsInteger(self->optFlds) / 2);
}
void
ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, int optFlds)
{
- if (self->optFlds == 0)
+ if (self->optFlds == NULL)
self->optFlds = MmsValue_newBitString(10);
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);
- if (accessResults != NULL)
+ if (accessResults != NULL) {
+
+ 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;
}
else {
diff --git a/src/iedclient/impl/ied_connection.c b/src/iec61850/client/ied_connection.c
similarity index 79%
rename from src/iedclient/impl/ied_connection.c
rename to src/iec61850/client/ied_connection.c
index d953b0bd..b97434fb 100644
--- a/src/iedclient/impl/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -33,6 +33,8 @@
#include "ied_connection_private.h"
#include "mms_value_internal.h"
+#define DEFAULT_CONNECTION_TIMEOUT 10000
+
typedef struct sICLogicalDevice
{
char* name;
@@ -107,7 +109,7 @@ iedConnection_mapDataAccessErrorToIedError(MmsDataAccessError mmsError)
static ICLogicalDevice*
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);
@@ -129,7 +131,7 @@ ICLogicalDevice_setDataSetList(ICLogicalDevice* self, LinkedList dataSets)
static void
ICLogicalDevice_destroy(ICLogicalDevice* self)
{
- free(self->name);
+ GLOBAL_FREEMEM(self->name);
if (self->variables != NULL)
LinkedList_destroy(self->variables);
@@ -137,13 +139,13 @@ ICLogicalDevice_destroy(ICLogicalDevice* self)
if (self->dataSets != NULL)
LinkedList_destroy(self->dataSets);
- free(self);
+ GLOBAL_FREEMEM(self);
}
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);
StringUtils_replace(self->dataSetReference, '.', '$');
@@ -159,9 +161,9 @@ ClientDataSet_destroy(ClientDataSet self)
if (self->dataSetValues != NULL)
MmsValue_delete(self->dataSetValues);
- free(self->dataSetReference);
+ GLOBAL_FREEMEM(self->dataSetReference);
- free(self);
+ GLOBAL_FREEMEM(self);
}
static void
@@ -315,31 +317,38 @@ doesReportMatchControlObject(char* domainName, char* itemName, char* objectRef)
}
static void
-handleLastApplErrorMessage(IedConnection self, MmsValue* value)
+handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError)
{
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* error = MmsValue_getElement(lastApplError, 1);
//MmsValue* origin = MmsValue_getElement(lastApplError, 2);
MmsValue* ctlNum = MmsValue_getElement(lastApplError, 3);
MmsValue* addCause = MmsValue_getElement(lastApplError, 4);
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)
- printf("DEBUG_IED_CLIENT: ctlNum: %u\n", MmsValue_toUint32(ctlNum));
+ printf("IED_CLIENT: ctlNum: %u\n", MmsValue_toUint32(ctlNum));
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)
- 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.addCause = MmsValue_toInt32(addCause);
+ self->lastApplError.addCause = (ControlAddCause) MmsValue_toInt32(addCause);
self->lastApplError.error = MmsValue_toInt32(error);
LinkedList control = LinkedList_getNext(self->clientControls);
while (control != NULL) {
@@ -347,8 +356,9 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* value)
char* objectRef = ControlObjectClient_getObjectReference(object);
- if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj)))
+ if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) {
ControlObjectClient_setLastApplError(object, self->lastApplError);
+ }
control = LinkedList_getNext(control);
}
@@ -366,7 +376,6 @@ informationReportHandler(void* parameter, char* domainName,
if (domainName == NULL) {
if (isVariableListName) {
-
private_IedConnection_handleReport(self, value);
}
else {
@@ -402,7 +411,7 @@ informationReportHandler(void* parameter, char* domainName,
IedConnection
IedConnection_create()
{
- IedConnection self = (IedConnection) calloc(1, sizeof(struct sIedConnection));
+ IedConnection self = (IedConnection) GLOBAL_CALLOC(1, sizeof(struct sIedConnection));
self->enabledReports = LinkedList_create();
self->logicalDevices = NULL;
@@ -414,9 +423,17 @@ IedConnection_create()
self->stateMutex = Semaphore_create(1);
+ self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
+
return self;
}
+void
+IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
+{
+ self->connectionTimeout = timeoutInMs;
+}
+
IedConnectionState
IedConnection_getState(IedConnection self)
{
@@ -469,15 +486,14 @@ IedConnection_connect(IedConnection self, IedClientError* error, char* hostname,
MmsConnection_setConnectionLostHandler(self->connection, connectionLostHandler, (void*) self);
MmsConnection_setInformationReportHandler(self->connection, informationReportHandler, self);
+ MmsConnection_setConnectTimeout(self->connection, self->connectionTimeout);
+
if (MmsConnection_connect(self->connection, &mmsError, hostname, tcpPort)) {
*error = IED_ERROR_OK;
IedConnection_setState(self, IED_STATE_CONNECTED);
}
- else {
+ else
*error = iedConnection_mapMmsErrorToIedError(mmsError);
- MmsConnection_destroy(self->connection);
- self->connection = NULL;
- }
}
else
*error = IED_ERROR_ALREADY_CONNECTED;
@@ -493,9 +509,6 @@ IedConnection_abort(IedConnection self, IedClientError* error)
MmsConnection_abort(self->connection, &mmsError);
- MmsConnection_destroy(self->connection);
- self->connection = NULL;
-
*error = iedConnection_mapMmsErrorToIedError(mmsError);
}
else
@@ -520,13 +533,9 @@ void
IedConnection_close(IedConnection self)
{
if (IedConnection_getState(self) == IED_STATE_CONNECTED) {
+ MmsConnection_close(self->connection);
IedConnection_setState(self, IED_STATE_CLOSED);
}
-
- if (self->connection != NULL) {
- MmsConnection_destroy(self->connection);
- self->connection = NULL;
- }
}
void
@@ -534,6 +543,8 @@ IedConnection_destroy(IedConnection self)
{
IedConnection_close(self);
+ MmsConnection_destroy(self->connection);
+
if (self->logicalDevices != NULL)
LinkedList_destroyDeep(self->logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy);
@@ -544,11 +555,11 @@ IedConnection_destroy(IedConnection self)
Semaphore_destroy(self->stateMutex);
- free(self);
+ GLOBAL_FREEMEM(self);
}
MmsVariableSpecification*
-IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc)
{
char domainIdBuffer[65];
@@ -582,11 +593,11 @@ IedConnection_getVariableSpecification(IedConnection self, IedClientError* error
}
MmsValue*
-IedConnection_readObject(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_readObject(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc)
{
char domainIdBuffer[65];
- char itemIdBuffer[129];
+ char itemIdBuffer[65];
MmsValue* value = NULL;
char* domainId;
@@ -613,7 +624,7 @@ IedConnection_readObject(IedConnection self, IedClientError* error, char* object
}
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);
@@ -636,7 +647,7 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char*
}
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);
@@ -659,7 +670,7 @@ IedConnection_readFloatValue(IedConnection self, IedClientError* error, char* ob
}
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);
@@ -682,7 +693,7 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, char* o
}
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);
@@ -705,7 +716,7 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* ob
}
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);
@@ -728,18 +739,19 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, cha
}
Timestamp*
-IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc,
+IedConnection_readTimestampValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc,
Timestamp* timeStamp)
{
MmsValue* value = IedConnection_readObject(self, error, objectReference, fc);
Timestamp* retVal = timeStamp;
- if (retVal == NULL)
- retVal = (Timestamp*) malloc(sizeof(Timestamp));
-
if (value != NULL) {
if (MmsValue_getType(value) == MMS_UTC_TIME) {
+
+ if (retVal == NULL)
+ retVal = (Timestamp*) GLOBAL_MALLOC(sizeof(Timestamp));
+
memcpy(retVal->val, value->value.utcTime, 8);
}
else {
@@ -757,33 +769,37 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char
}
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);
Quality quality = QUALITY_VALIDITY_GOOD;
- if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) {
- quality = Quality_fromMmsValue(value);
- }
- else {
- if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
- *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value));
- else
- *error = IED_ERROR_UNEXPECTED_VALUE_RECEIVED;
- }
+ if (value != NULL) {
- MmsValue_delete(value);
+ if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) {
+ quality = Quality_fromMmsValue(value);
+ }
+ else {
+ if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
+ *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value));
+ else
+ *error = IED_ERROR_UNEXPECTED_VALUE_RECEIVED;
+ }
+
+ MmsValue_delete(value);
+
+ }
return quality;
}
void
-IedConnection_writeObject(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeObject(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, MmsValue* value)
{
char domainIdBuffer[65];
- char itemIdBuffer[129];
+ char itemIdBuffer[65];
char* domainId;
char* itemId;
@@ -804,7 +820,7 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, char* objec
}
void
-IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, bool value)
{
MmsValue mmsValue;
@@ -816,7 +832,7 @@ IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char*
}
void
-IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, int32_t value)
{
uint8_t valueBuffer[4];
@@ -838,7 +854,7 @@ IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* o
void
-IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint32_t value)
{
uint8_t valueBuffer[4];
@@ -859,7 +875,7 @@ IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, ch
}
void
-IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeFloatValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, float value)
{
MmsValue mmsValue;
@@ -872,7 +888,7 @@ IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* o
}
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)
{
MmsValue mmsValue;
@@ -885,7 +901,7 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, char*
}
void
-IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, char* value)
{
MmsValue mmsValue;
@@ -1002,7 +1018,7 @@ mmsFileDirectoryHandler(void* parameter, char* filename, uint32_t size, uint64_t
}
LinkedList /**/
-IedConnection_getFileDirectory(IedConnection self, IedClientError* error, char* directoryName)
+IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* directoryName)
{
*error = IED_ERROR_OK;
@@ -1058,7 +1074,7 @@ mmsFileReadHandler(void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t by
}
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)
{
*error = IED_ERROR_OK;
@@ -1093,7 +1109,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
return 0;
}
- if (clientFileReadHandler.retVal == true) {
+ if (clientFileReadHandler.retVal == false) {
*error = IED_ERROR_UNKNOWN;
break;
}
@@ -1110,7 +1126,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
}
void
-IedConnection_deleteFile(IedConnection self, IedClientError* error, char* fileName)
+IedConnection_deleteFile(IedConnection self, IedClientError* error, const char* fileName)
{
*error = IED_ERROR_OK;
@@ -1133,12 +1149,14 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool
LinkedList /**/
IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error,
- char* logicalDeviceName)
+ const char* logicalDeviceName)
{
+ *error = IED_ERROR_OK;
+
if (self->logicalDevices == NULL)
IedConnection_getDeviceModelFromServer(self, error);
- if (self->logicalDevices == NULL)
+ if (*error != IED_ERROR_OK)
return NULL;
LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices);
@@ -1223,15 +1241,25 @@ addVariablesWithFc(char* fc, char* lnName, LinkedList variables, LinkedList lnDi
LinkedList /**/
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)
IedConnection_getDeviceModelFromServer(self, error);
- char lnRefCopy[193];
+ if (*error != IED_ERROR_OK)
+ return NULL;
+
+ char lnRefCopy[130];
- strncpy(lnRefCopy, logicalNodeReference, 192);
- lnRefCopy[192] = 0;
+ strncpy(lnRefCopy, logicalNodeReference, 129);
+ lnRefCopy[129] = 0;
char* ldSep = strchr(lnRefCopy, '/');
@@ -1246,7 +1274,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* logicalNodeName = ldSep + 1;
- // search for logical device
+ /* search for logical device */
LinkedList device = LinkedList_getNext(self->logicalDevices);
@@ -1305,7 +1333,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
char* dataObjectName = copyString(fcEndPos + 1);
if (!addToStringSet(lnDirectory, dataObjectName))
- free(dataObjectName);
+ GLOBAL_FREEMEM(dataObjectName);
}
}
}
@@ -1318,6 +1346,21 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
}
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:
addVariablesWithFc("BR", logicalNodeName, ld->variables, lnDirectory);
break;
@@ -1360,7 +1403,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
break;
default:
- printf("ACSI class not yet supported!\n");
+ if (DEBUG_IED_CLIENT)
+ printf("IED_CLIENT: ACSI class not yet supported!\n");
break;
}
@@ -1370,18 +1414,33 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
LinkedList /**/
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)
IedConnection_getDeviceModelFromServer(self, error);
- char lnRefCopy[193];
+ if (*error != IED_ERROR_OK)
+ return NULL;
+
+ char lnRefCopy[130];
- strncpy(lnRefCopy, logicalNodeReference, 192);
- lnRefCopy[192] = 0;
+ strncpy(lnRefCopy, logicalNodeReference, 129);
+ lnRefCopy[129] = 0;
char* ldSep = strchr(lnRefCopy, '/');
+ if (ldSep == NULL) {
+ *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
+ return NULL;
+ }
+
*ldSep = 0;
char* logicalDeviceName = lnRefCopy;
@@ -1441,15 +1500,25 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
static LinkedList
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)
IedConnection_getDeviceModelFromServer(self, error);
- char dataRefCopy[193];
+ if (*error != IED_ERROR_OK)
+ return NULL;
+
+ char dataRefCopy[130];
- strncpy(dataRefCopy, dataReference, 192);
- dataRefCopy[192] = 0;
+ strncpy(dataRefCopy, dataReference, 129);
+ dataRefCopy[129] = 0;
char* ldSep = strchr(dataRefCopy, '/');
@@ -1550,7 +1619,7 @@ getDataDirectory(IedConnection self, IedClientError* error,
if (withFc) {
int elementNameLen = strlen(subElementName);
- elementName = (char*) malloc(elementNameLen + 5);
+ elementName = (char*) GLOBAL_MALLOC(elementNameLen + 5);
memcpy(elementName, subElementName, elementNameLen);
elementName[elementNameLen] = '[';
elementName[elementNameLen + 1] = *(fcPos + 1);
@@ -1562,7 +1631,7 @@ getDataDirectory(IedConnection self, IedClientError* error,
elementName = copyString(subElementName);
if (!addToStringSet(dataDirectory, elementName))
- free(elementName);
+ GLOBAL_FREEMEM(elementName);
}
}
}
@@ -1580,38 +1649,211 @@ getDataDirectory(IedConnection self, IedClientError* error,
}
LinkedList
-IedConnection_getDataDirectory(IedConnection self, IedClientError* error,
- char* dataReference)
+IedConnection_getDataDirectory(IedConnection self, IedClientError* error, const char* dataReference)
{
return getDataDirectory(self, error, dataReference, false);
}
LinkedList
-IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error,
- char* dataReference)
+IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, const char* dataReference)
{
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
-IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dataSetReference,
+IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList /* */dataSetElements)
{
char domainIdBuffer[65];
- char itemIdBuffer[129];
+ char itemIdBuffer[33]; /* maximum data set name = 32 chars */
- char* domainId;
- char* itemId;
+ const char* domainId;
+ const char* itemId;
bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') {
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 {
- itemId = dataSetReference;
+ itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
@@ -1645,20 +1887,39 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dat
}
void
-IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dataSetReference)
+IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference)
{
char domainId[65];
- char itemId[129];
+ char itemId[33];
bool isAssociationSpecific = false;
+ int dataSetReferenceLength = strlen(dataSetReference);
+
if (dataSetReference[0] != '@') {
- MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId);
- copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemId);
+ if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) {
+ *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, '.', '$');
}
else {
- strncpy(itemId, dataSetReference, 128);
- itemId[128] = 0;
+ if (dataSetReferenceLength > 33) {
+ *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
+ return;
+ }
+
+ strcpy(itemId, dataSetReference + 1);
+
isAssociationSpecific = true;
}
@@ -1673,7 +1934,7 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, char* dat
}
LinkedList /* */
-IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, char* dataSetReference, bool* isDeletable)
+IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable)
{
bool deletable = false;
@@ -1682,18 +1943,19 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, cha
char domainIdBuffer[65];
char itemIdBuffer[129];
- char* domainId = NULL;
- char* itemId = NULL;
+ const char* domainId = NULL;
+ const char* itemId = NULL;
bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
- itemId = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
- StringUtils_replace(itemId, '.', '$');
+ char* itemIdRef = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
+ StringUtils_replace(itemIdRef, '.', '$');
+ itemId = itemIdRef;
}
else {
- itemId = dataSetReference;
+ itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
@@ -1736,25 +1998,25 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, cha
}
ClientDataSet
-IedConnection_readDataSetValues(IedConnection self, IedClientError* error, char* dataSetReference,
+IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
ClientDataSet dataSet)
{
char domainIdBuffer[65];
char itemIdBuffer[129];
- char* domainId = NULL;
- char* itemId = NULL;
-
+ const char* domainId = NULL;
+ const char* itemId = NULL;
bool isAssociationSpecific = false;
if (dataSetReference[0] != '@') {
domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
- itemId = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
- StringUtils_replace(itemId, '.', '$');
+ char* itemIdRef = copyStringToBuffer(dataSetReference + strlen(domainId) + 1, itemIdBuffer);
+ StringUtils_replace(itemIdRef, '.', '$');
+ itemId = itemIdRef;
}
else {
- itemId = dataSetReference;
+ itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
@@ -1815,9 +2077,9 @@ private_IedConnection_removeControlClient(IedConnection self, ControlObjectClien
}
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->fileSize = fileSize;
@@ -1829,8 +2091,8 @@ FileDirectoryEntry_create(char* fileName, uint32_t fileSize, uint64_t lastModifi
void
FileDirectoryEntry_destroy(FileDirectoryEntry self)
{
- free(self->fileName);
- free(self);
+ GLOBAL_FREEMEM(self->fileName);
+ GLOBAL_FREEMEM(self);
}
char*
diff --git a/src/iedcommon/iec61850_common.c b/src/iec61850/common/iec61850_common.c
similarity index 91%
rename from src/iedcommon/iec61850_common.c
rename to src/iec61850/common/iec61850_common.c
index 6e854fdc..eb7dc848 100644
--- a/src/iedcommon/iec61850_common.c
+++ b/src/iec61850/common/iec61850_common.c
@@ -36,7 +36,7 @@ Quality_getValidity(Quality* self)
void
Quality_setValidity(Quality* self, Validity validity)
{
- *self = *self & (0xfff8);
+ *self = *self & (0xfffc);
*self = *self | validity;
}
@@ -68,6 +68,34 @@ Quality_fromMmsValue(MmsValue* 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*
FunctionalConstraint_toString(FunctionalConstraint fc) {
switch (fc) {
diff --git a/src/iedserver/model/cdc.h b/src/iec61850/inc/iec61850_cdc.h
similarity index 100%
rename from src/iedserver/model/cdc.h
rename to src/iec61850/inc/iec61850_cdc.h
diff --git a/src/iedclient/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
similarity index 92%
rename from src/iedclient/iec61850_client.h
rename to src/iec61850/inc/iec61850_client.h
index 53eb5f3f..37d2b8fd 100644
--- a/src/iedclient/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -66,7 +66,7 @@ typedef struct
{
int ctlNum;
int error;
- int addCause;
+ ControlAddCause addCause;
} LastApplError;
/** Connection state of the IedConnection instance (either idle, connected or closed) */
@@ -160,6 +160,19 @@ IedConnection_create(void);
void
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
**************************************************/
@@ -693,6 +706,31 @@ ClientReport_getEntryId(ClientReport self);
bool
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
*
@@ -721,7 +759,7 @@ ReasonForInclusion_getValueAsString(ReasonForInclusion reasonCode);
**************************************************/
ClientReportControlBlock
-ClientReportControlBlock_create(char* dataAttributeReference);
+ClientReportControlBlock_create(char* rcbReference);
void
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
*/
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).
@@ -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)
*/
void
-IedConnection_writeObject(IedConnection self, IedClientError* error, char* dataAttributeReference, FunctionalConstraint fc,
+IedConnection_writeObject(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc,
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
*/
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
@@ -892,7 +930,7 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, char*
* \param fc the functional constraint of the data attribute to read
*/
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
@@ -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!
*/
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
@@ -920,7 +958,7 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, char* o
* \return an int32_t value of the read data attributes
*/
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
@@ -933,7 +971,7 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, char* ob
* \return an uint32_t value of the read data attributes
*/
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)
@@ -950,7 +988,7 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, cha
* \return the timestamp value
*/
Timestamp*
-IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char* objectReference, FunctionalConstraint fc,
+IedConnection_readTimestampValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc,
Timestamp* timeStamp);
/**
@@ -964,7 +1002,7 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, char
* \return the timestamp value
*/
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
@@ -976,7 +1014,7 @@ IedConnection_readQualityValue(IedConnection self, IedClientError* error, char*
* \param value the boolean value to write
*/
void
-IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, bool value);
/**
@@ -989,7 +1027,7 @@ IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, char*
* \param value the int32_t value to write
*/
void
-IedConnection_writeInt32Value(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const char* objectReference,
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
*/
void
-IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, uint32_t value);
/**
@@ -1015,15 +1053,15 @@ IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, ch
* \param value the float value to write
*/
void
-IedConnection_writeFloatValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeFloatValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, float value);
void
-IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, char* objectReference,
+IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, char* value);
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);
/** @} */
@@ -1050,7 +1088,7 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, char*
* \return data set instance with retrieved values of NULL if an error occurred.
*/
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
@@ -1068,7 +1106,7 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, char*
*
*/
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
@@ -1082,7 +1120,7 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, char* dat
* \param dataSetReference object reference of the data set
*/
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.
*/
LinkedList /* */
-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)
@@ -1212,7 +1250,7 @@ void
ControlObjectClient_setTestMode(ControlObjectClient self);
void
-ControlObjectClient_setOrigin(ControlObjectClient self, char* orIdent, int orCat);
+ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat);
void
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 handler the callback function to be used
* \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
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
*/
LinkedList /**/
-IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, char* logicalDeviceName);
+IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName);
typedef enum {
ACSI_CLASS_DATA_OBJECT,
@@ -1332,7 +1374,7 @@ typedef enum {
*/
LinkedList /**/
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
@@ -1349,7 +1391,7 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
*/
LinkedList /**/
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)
@@ -1365,8 +1407,7 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
*
*/
LinkedList /**/
-IedConnection_getDataDirectory(IedConnection self, IedClientError* error,
- char* dataReference);
+IedConnection_getDataDirectory(IedConnection self, IedClientError* error, const char* dataReference);
/**
* \brief returns the directory of the given data object (DO)
@@ -1383,8 +1424,25 @@ IedConnection_getDataDirectory(IedConnection self, IedClientError* error,
*
*/
LinkedList /**/
-IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error,
- char* dataReference);
+IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error, const 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.
@@ -1401,7 +1459,7 @@ IedConnection_getDataDirectoryFC(IedConnection self, IedClientError* error,
*
*/
MmsVariableSpecification*
-IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, char* dataAttributeReference,
+IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* dataAttributeReference,
FunctionalConstraint fc);
/** @} */
@@ -1415,7 +1473,7 @@ IedConnection_getVariableSpecification(IedConnection self, IedClientError* error
typedef struct sFileDirectoryEntry* FileDirectoryEntry;
FileDirectoryEntry
-FileDirectoryEntry_create(char* fileName, uint32_t fileSize, uint64_t lastModified);
+FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified);
void
FileDirectoryEntry_destroy(FileDirectoryEntry self);
@@ -1435,6 +1493,13 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self);
*
* 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 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
@@ -1442,7 +1507,7 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self);
* \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements
*/
LinkedList /**/
-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
@@ -1473,7 +1538,7 @@ typedef bool
* \return number of bytes received
*/
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);
/**
@@ -1486,7 +1551,7 @@ IedConnection_getFile(IedConnection self, IedClientError* error, char* fileName,
* \param fileName the name of the file to delete
*/
void
-IedConnection_deleteFile(IedConnection self, IedClientError* error, char* fileName);
+IedConnection_deleteFile(IedConnection self, IedClientError* error, const char* fileName);
/** @} */
diff --git a/src/iedcommon/iec61850_common.h b/src/iec61850/inc/iec61850_common.h
similarity index 76%
rename from src/iedcommon/iec61850_common.h
rename to src/iec61850/inc/iec61850_common.h
index dea10c19..51564f5a 100644
--- a/src/iedcommon/iec61850_common.h
+++ b/src/iec61850/inc/iec61850_common.h
@@ -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)
@@ -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
*
diff --git a/src/iedserver/model/config_file_parser.h b/src/iec61850/inc/iec61850_config_file_parser.h
similarity index 97%
rename from src/iedserver/model/config_file_parser.h
rename to src/iec61850/inc/iec61850_config_file_parser.h
index 79943f4e..e595b11c 100644
--- a/src/iedserver/model/config_file_parser.h
+++ b/src/iec61850/inc/iec61850_config_file_parser.h
@@ -28,6 +28,7 @@
extern "C" {
#endif
+#include "hal_filesystem.h"
/** \addtogroup server_api_group
* @{
diff --git a/src/iedserver/model/dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h
similarity index 93%
rename from src/iedserver/model/dynamic_model.h
rename to src/iec61850/inc/iec61850_dynamic_model.h
index 8969cad7..88701588 100644
--- a/src/iedserver/model/dynamic_model.h
+++ b/src/iec61850/inc/iec61850_dynamic_model.h
@@ -24,8 +24,8 @@
#ifndef DYNAMIC_MODEL_H_
#define DYNAMIC_MODEL_H_
-#include "model.h"
-#include "cdc.h"
+#include "iec61850_model.h"
+#include "iec61850_cdc.h"
#ifdef __cplusplus
extern "C" {
@@ -146,6 +146,20 @@ ReportControlBlock*
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);
+/**
+ * \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)
*
diff --git a/src/iedserver/model/model.h b/src/iec61850/inc/iec61850_model.h
similarity index 85%
rename from src/iedserver/model/model.h
rename to src/iec61850/inc/iec61850_model.h
index 18e21a99..b44378d5 100644
--- a/src/iedserver/model/model.h
+++ b/src/iec61850/inc/iec61850_model.h
@@ -1,7 +1,7 @@
/*
* model.h
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -72,6 +72,12 @@ typedef struct sIedModel IedModel;
typedef struct sDataSet DataSet;
typedef struct sReportControlBlock ReportControlBlock;
+
+/**
+ * \brief IEC 61850 data model of setting group control block (SGCB)
+ */
+typedef struct sSettingGroupControlBlock SettingGroupControlBlock;
+
typedef struct sGSEControlBlock GSEControlBlock;
@@ -121,6 +127,7 @@ struct sIedModel {
DataSet* dataSets;
ReportControlBlock* rcbs;
GSEControlBlock* gseCBs;
+ SettingGroupControlBlock* sgcbs;
void (*initializer) (void);
};
@@ -210,6 +217,20 @@ struct sReportControlBlock {
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 {
uint8_t vlanPriority;
uint16_t vlanId;
@@ -263,6 +284,18 @@ ModelNode_getChild(ModelNode* modelNode, const char* name);
char*
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
*
@@ -307,8 +340,26 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR
ModelNode*
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*
-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);
/**@}*/
diff --git a/src/iedserver/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
similarity index 76%
rename from src/iedserver/iec61850_server.h
rename to src/iec61850/inc/iec61850_server.h
index ac304929..d7ecb624 100644
--- a/src/iedserver/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -36,10 +36,10 @@ extern "C" {
*/
#include "mms_server.h"
-#include "dynamic_model.h"
-#include "model.h"
-#include "filesystem.h"
-#include "config_file_parser.h"
+#include "iec61850_dynamic_model.h"
+#include "iec61850_model.h"
+#include "hal_filesystem.h"
+#include "iec61850_config_file_parser.h"
/**
* An opaque handle for an IED server instance
@@ -93,6 +93,53 @@ IedServer_start(IedServer self, int tcpPort);
void
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
*
@@ -149,6 +196,17 @@ IedServer_getIsoServer(IedServer self);
void
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
* \return peer address as C string.
*/
-char*
+const char*
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
@@ -490,6 +665,15 @@ typedef enum {
CONTROL_OBJECT_UNDEFINED = 4 /** object not visible in this security context ??? */
} 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).
*
@@ -516,15 +700,19 @@ typedef CheckHandlerResult (*ControlPerformCheckHandler) (void* parameter, MmsVa
* 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
* 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 ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation
* \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.
@@ -532,14 +720,18 @@ typedef bool (*ControlWaitForExecutionHandler) (void* parameter, MmsValue* ctlVa
* 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
* (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 ctlVal the control value of the control 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
diff --git a/src/iedserver/mms_mapping/control.h b/src/iec61850/inc_private/control.h
similarity index 95%
rename from src/iedserver/mms_mapping/control.h
rename to src/iec61850/inc_private/control.h
index ac3505f6..5b26df47 100644
--- a/src/iedserver/mms_mapping/control.h
+++ b/src/iec61850/inc_private/control.h
@@ -24,7 +24,7 @@
#ifndef CONTROL_H_
#define CONTROL_H_
-#include "model.h"
+#include "iec61850_model.h"
#include "mms_server_connection.h"
#include "mms_device_model.h"
#include "iec61850_server.h"
@@ -91,9 +91,6 @@ ControlObject_select(ControlObject* self, MmsServerConnection* connection);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection* connection);
-bool
-ControlObject_operate(ControlObject* self, MmsValue* value, uint64_t currentTime, bool testCondition);
-
void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter);
diff --git a/src/iedclient/impl/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h
similarity index 97%
rename from src/iedclient/impl/ied_connection_private.h
rename to src/iec61850/inc_private/ied_connection_private.h
index 752d552f..6955300f 100644
--- a/src/iedclient/impl/ied_connection_private.h
+++ b/src/iec61850/inc_private/ied_connection_private.h
@@ -28,7 +28,7 @@
#define DEBUG_IED_CLIENT 0
#endif
-#include "thread.h"
+#include "hal_thread.h"
struct sIedConnection
{
@@ -38,10 +38,12 @@ struct sIedConnection
LinkedList logicalDevices;
LinkedList clientControls;
LastApplError lastApplError;
+
Semaphore stateMutex;
IedConnectionClosedHandler connectionCloseHandler;
void* connectionClosedParameter;
+ uint32_t connectionTimeout;
};
struct sClientReportControlBlock {
diff --git a/src/iedserver/impl/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h
similarity index 98%
rename from src/iedserver/impl/ied_server_private.h
rename to src/iec61850/inc_private/ied_server_private.h
index 907beeeb..d2ae3ae0 100644
--- a/src/iedserver/impl/ied_server_private.h
+++ b/src/iec61850/inc_private/ied_server_private.h
@@ -31,6 +31,7 @@
#define ALLOW_WRITE_ACCESS_CF 2
#define ALLOW_WRITE_ACCESS_SP 4
#define ALLOW_WRITE_ACCESS_SV 8
+#define ALLOW_WRITE_ACCESS_SE 16
struct sIedServer
{
diff --git a/src/iedserver/mms_mapping/mms_goose.h b/src/iec61850/inc_private/mms_goose.h
similarity index 100%
rename from src/iedserver/mms_mapping/mms_goose.h
rename to src/iec61850/inc_private/mms_goose.h
diff --git a/src/iedserver/mms_mapping/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h
similarity index 75%
rename from src/iedserver/mms_mapping/mms_mapping.h
rename to src/iec61850/inc_private/mms_mapping.h
index 37ac4ca4..7ffcaf46 100644
--- a/src/iedserver/mms_mapping/mms_mapping.h
+++ b/src/iec61850/inc_private/mms_mapping.h
@@ -1,7 +1,7 @@
/*
* mms_mapping.h
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -24,7 +24,7 @@
#ifndef MMS_MAPPING_H_
#define MMS_MAPPING_H_
-#include "model.h"
+#include "iec61850_model.h"
#include "mms_server_connection.h"
#include "mms_device_model.h"
#include "control.h"
@@ -44,6 +44,27 @@ MmsMapping_create(IedModel* model);
MmsDevice*
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
MmsMapping_setMmsServer(MmsMapping* self, MmsServer server);
@@ -68,18 +89,21 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value);
void
MmsMapping_enableGoosePublishing(MmsMapping* self);
+void
+MmsMapping_disableGoosePublishing(MmsMapping* self);
+
char*
-MmsMapping_getMmsDomainFromObjectReference(char* objectReference, char* buffer);
+MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer);
void
MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject);
char*
-MmsMapping_createMmsVariableNameFromObjectReference(char* objectReference, FunctionalConstraint fc, char* buffer);
+MmsMapping_createMmsVariableNameFromObjectReference(const char* objectReference, FunctionalConstraint fc, char* buffer);
void
MmsMapping_addObservedAttribute(MmsMapping* self, DataAttribute* dataAttribute,
- void* handler);
+ AttributeChangedHandler handler);
char*
MmsMapping_getNextNameElement(char* name);
diff --git a/src/iedserver/mms_mapping/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h
similarity index 87%
rename from src/iedserver/mms_mapping/mms_mapping_internal.h
rename to src/iec61850/inc_private/mms_mapping_internal.h
index 37681d62..c2c7f841 100644
--- a/src/iedserver/mms_mapping/mms_mapping_internal.h
+++ b/src/iec61850/inc_private/mms_mapping_internal.h
@@ -1,50 +1,56 @@
-/*
- * mms_mapping_internal.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 .
- *
- * See COPYING file for the complete license text.
- */
-
-#ifndef MMS_MAPPING_INTERNAL_H_
-#define MMS_MAPPING_INTERNAL_H_
-
-#include "thread.h"
-#include "linked_list.h"
-
-struct sMmsMapping {
- IedModel* model;
- MmsDevice* mmsDevice;
- MmsServer mmsServer;
- LinkedList reportControls;
- LinkedList gseControls;
- LinkedList controlObjects;
- LinkedList observedObjects;
- LinkedList attributeAccessHandlers;
-
- bool reportThreadRunning;
- bool reportThreadFinished;
- Thread reportWorkerThread;
-
- IedServer iedServer;
-
- IedConnectionIndicationHandler connectionIndicationHandler;
- void* connectionIndicationHandlerParameter;
-};
-
-#endif /* MMS_MAPPING_INTERNAL_H_ */
+/*
+ * mms_mapping_internal.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 .
+ *
+ * See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_MAPPING_INTERNAL_H_
+#define MMS_MAPPING_INTERNAL_H_
+
+#include "hal_thread.h"
+#include "linked_list.h"
+
+struct sMmsMapping {
+ IedModel* model;
+ MmsDevice* mmsDevice;
+ MmsServer mmsServer;
+ LinkedList reportControls;
+ LinkedList gseControls;
+ LinkedList controlObjects;
+ LinkedList observedObjects;
+ LinkedList attributeAccessHandlers;
+
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ LinkedList settingGroups;
+#endif
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
+ bool reportThreadRunning;
+ bool reportThreadFinished;
+ Thread reportWorkerThread;
+#endif
+
+ IedServer iedServer;
+
+ IedConnectionIndicationHandler connectionIndicationHandler;
+ void* connectionIndicationHandlerParameter;
+};
+
+#endif /* MMS_MAPPING_INTERNAL_H_ */
diff --git a/src/iedserver/mms_mapping/reporting.h b/src/iec61850/inc_private/reporting.h
similarity index 98%
rename from src/iedserver/mms_mapping/reporting.h
rename to src/iec61850/inc_private/reporting.h
index f20339a1..448f1b81 100644
--- a/src/iedserver/mms_mapping/reporting.h
+++ b/src/iec61850/inc_private/reporting.h
@@ -74,7 +74,10 @@ typedef struct {
int triggerOps;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore createNotificationsMutex; /* { covered by mutex } */
+#endif
+
ReportInclusionFlag* inclusionFlags; /* { covered by mutex } */
bool triggered; /* { covered by mutex } */
uint64_t reportTime; /* { covered by mutex } */
diff --git a/src/iedserver/impl/client_connection.c b/src/iec61850/server/impl/client_connection.c
similarity index 81%
rename from src/iedserver/impl/client_connection.c
rename to src/iec61850/server/impl/client_connection.c
index a1198363..2e02db5e 100644
--- a/src/iedserver/impl/client_connection.c
+++ b/src/iec61850/server/impl/client_connection.c
@@ -26,12 +26,16 @@
#include "mms_mapping.h"
#include "control.h"
#include "stack_config.h"
-#include "thread.h"
+#include "hal_thread.h"
#include "ied_server_private.h"
struct sClientConnection {
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore tasksCountMutex;
+#endif
+
int tasksCount;
void* serverConnectionHandle;
};
@@ -39,9 +43,12 @@ struct sClientConnection {
ClientConnection
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);
+#endif
+
self->tasksCount = 0;
self->serverConnectionHandle = serverConnectionHandle;
@@ -51,8 +58,11 @@ private_ClientConnection_create(void* serverConnectionHandle)
void
private_ClientConnection_destroy(ClientConnection self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->tasksCountMutex);
- free(self);
+#endif
+
+ GLOBAL_FREEMEM(self);
}
int
@@ -60,9 +70,15 @@ private_ClientConnection_getTasksCount(ClientConnection self)
{
int tasksCount;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex);
+#endif
+
tasksCount = self->tasksCount;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex);
+#endif
return tasksCount;
}
@@ -70,17 +86,29 @@ private_ClientConnection_getTasksCount(ClientConnection self)
void
private_ClientConnection_increaseTasksCount(ClientConnection self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex);
+#endif
+
self->tasksCount++;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex);
+#endif
}
void
private_ClientConnection_decreaseTasksCount(ClientConnection self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->tasksCountMutex);
+#endif
+
self->tasksCount--;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->tasksCountMutex);
+#endif
}
void*
@@ -90,7 +118,7 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self)
}
-char*
+const char*
ClientConnection_getPeerAddress(ClientConnection self)
{
MmsServerConnection* mmsConnection = (MmsServerConnection*) self->serverConnectionHandle;
diff --git a/src/iedserver/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
similarity index 83%
rename from src/iedserver/impl/ied_server.c
rename to src/iec61850/server/impl/ied_server.c
index d5d1f779..17f21b4b 100644
--- a/src/iedserver/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -23,10 +23,11 @@
#include "iec61850_server.h"
#include "mms_mapping.h"
+#include "mms_mapping_internal.h"
#include "control.h"
#include "stack_config.h"
#include "ied_server_private.h"
-#include "thread.h"
+#include "hal_thread.h"
#include "reporting.h"
#ifndef DEBUG_IED_SERVER
@@ -178,7 +179,7 @@ createMmsServerCache(IedServer self)
MmsServer_insertIntoCache(self->mmsServer, logicalDevice, variableName, defaultValue);
- free(variableName);
+ GLOBAL_FREEMEM(variableName);
}
}
}
@@ -199,13 +200,15 @@ installDefaultValuesForDataAttribute(IedServer self, DataAttribute* dataAttribut
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);
if (domain == NULL) {
if (DEBUG_IED_SERVER)
- printf("Error domain (%s) not found!\n", domainName);
+ printf("Error domain (%s) not found for %s!\n", domainName, objectReference);
return;
}
@@ -214,7 +217,19 @@ installDefaultValuesForDataAttribute(IedServer self, DataAttribute* dataAttribut
dataAttribute->mmsValue = cacheValue;
if (value != NULL) {
- MmsValue_update(cacheValue, value);
+
+ if (cacheValue != NULL)
+ 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);
}
@@ -304,7 +319,12 @@ updateDataSetsWithCachedValues(IedServer self)
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);
@@ -329,7 +349,7 @@ updateDataSetsWithCachedValues(IedServer self)
IedServer
IedServer_create(IedModel* iedModel)
{
- IedServer self = (IedServer) calloc(1, sizeof(struct sIedServer));
+ IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer));
self->model = iedModel;
@@ -357,27 +377,29 @@ IedServer_create(IedModel* iedModel)
self->clientConnections = LinkedList_create();
- /* default write access policy allows access to SP and SV FCDAs but denies access to DC and CF FCDAs */
- self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV;
+ /* 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 | ALLOW_WRITE_ACCESS_SE;
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
Reporting_activateBufferedReports(self->mmsMapping);
#endif
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ MmsMapping_configureSettingGroups(self->mmsMapping);
+#endif
+
return self;
}
void
IedServer_destroy(IedServer self)
{
- //TODO wait for control and other threads to finish??
-
MmsServer_destroy(self->mmsServer);
IsoServer_destroy(self->isoServer);
MmsMapping_destroy(self->mmsMapping);
LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy);
- free(self);
+ GLOBAL_FREEMEM(self);
}
void
@@ -398,12 +420,63 @@ IedServer_getIsoServer(IedServer self)
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
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);
MmsMapping_startEventWorkerThread(self->mmsMapping);
+#endif
}
+#endif
bool
IedServer_isRunning(IedServer self)
@@ -420,12 +493,43 @@ IedServer_getDataModel(IedServer self)
return self->model;
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
void
IedServer_stop(IedServer self)
{
MmsMapping_stopEventWorkerThread(self->mmsMapping);
+#if (CONFIG_MMS_SINGLE_THREADED == 1)
+ MmsServer_stopListeningThreadless(self->mmsServer);
+#else
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
@@ -626,18 +730,33 @@ checkForUpdateTrigger(IedServer self, DataAttribute* dataAttribute)
static inline void
checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute)
{
-#if (CONFIG_IEC61850_REPORT_SERVICE== 1)
- if (dataAttribute->triggerOptions & TRG_OPT_DATA_CHANGED)
+#if (CONFIG_IEC61850_REPORT_SERVICE == 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
+ if (dataAttribute->triggerOptions & TRG_OPT_DATA_CHANGED) {
+
+#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue,
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,
REPORT_CONTROL_QUALITY_CHANGED);
-#endif /* (CONFIG_IEC61850_REPORT_SERVICE== 1) */
+#endif
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
- MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue);
+ MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue);
#endif
+ }
+#endif /* (CONFIG_IEC61850_REPORT_SERVICE== 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
+
+
}
void
@@ -834,13 +953,22 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu
}
-#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
+
void
IedServer_enableGoosePublishing(IedServer self)
{
+#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
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) */
+}
void
IedServer_observeDataAttribute(IedServer self, DataAttribute* dataAttribute,
@@ -866,6 +994,9 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo
case SV:
self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_SV;
break;
+ case SE:
+ self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_SE;
+ break;
default: /* ignore - request is invalid */
break;
}
@@ -884,6 +1015,9 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo
case SV:
self->writeAccessPolicies &= ~ALLOW_WRITE_ACCESS_SV;
break;
+ case SE:
+ self->writeAccessPolicies &= ALLOW_WRITE_ACCESS_SE;
+ break;
default: /* ignore - request is invalid */
break;
}
@@ -942,13 +1076,59 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F
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);
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
private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnectionHandle)
{
diff --git a/src/iedserver/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
similarity index 77%
rename from src/iedserver/mms_mapping/control.c
rename to src/iec61850/server/mms_mapping/control.c
index afae0141..529085fc 100644
--- a/src/iedserver/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -42,16 +42,6 @@
#define CONTROL_ERROR_TIMEOUT_TEST 2
#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_READY 1
#define STATE_WAIT_FOR_ACTICATION_TIME 2
@@ -67,7 +57,10 @@ struct sControlObject
char* name;
int state;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore stateLock;
+#endif
MmsValue* mmsValue;
MmsVariableSpecification* typeSpec;
@@ -98,7 +91,7 @@ struct sControlObject
uint64_t operateTime;
bool operateOnce;
- MmsServerConnection* clientConnection;
+ MmsServerConnection* mmsConnection;
MmsValue* emptyString;
@@ -111,8 +104,8 @@ struct sControlObject
uint32_t operateInvokeId;
- ControlHandler listener;
- void* listenerParameter;
+ ControlHandler operateHandler;
+ void* operateHandlerParameter;
ControlPerformCheckHandler checkHandler;
void* checkHandlerParameter;
@@ -123,10 +116,13 @@ struct sControlObject
void
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
-ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection* connection);
+ControlObject_sendCommandTerminationPositive(ControlObject* self);
+
+void
+ControlObject_sendCommandTerminationNegative(ControlObject* self);
MmsValue*
Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
@@ -135,18 +131,31 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
static void
setState(ControlObject* self, int newState)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock);
+#endif
+
self->state = newState;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock);
+#endif
}
static int
getState(ControlObject* self)
{
int state;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock);
+#endif
+
state = self->state;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock);
+#endif
return state;
}
@@ -257,6 +266,47 @@ isSboClassOperateOnce(ControlObject* self)
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
abortControlOperation(ControlObject* self)
{
@@ -269,116 +319,123 @@ abortControlOperation(ControlObject* self)
}
else
setState(self, STATE_READY);
+
+ exitControlTask(self);
}
-static MmsValue*
-getOperParameterOperTime(MmsValue* operParameters)
+static ControlHandlerResult
+operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool testCondition)
{
- if (MmsValue_getType(operParameters) == MMS_STRUCTURE) {
+ self->selectTime = currentTime;
- if (MmsValue_getArraySize(operParameters) == 7)
- return MmsValue_getElement(operParameters, 1);
- }
+ if (self->operateHandler != NULL)
+ return self->operateHandler(self->operateHandlerParameter, value, testCondition);
- return NULL;
+ return CONTROL_RESULT_OK;
}
static void
-controlOperateThread(ControlObject* self)
+executeControlTask(ControlObject* self)
{
- bool checkOk = true;
-
- bool isTimeActivatedControl = false;
+ int state;
- ClientConnection iedClientConnection =
- private_IedServer_getClientConnectionByHandle(self->iedServer, self->clientConnection);
+executeStateMachine:
- if (iedClientConnection == NULL) return;
+ state = getState(self);
- private_ClientConnection_increaseTasksCount(iedClientConnection);
+ switch (state) {
- if (getState(self) == STATE_WAIT_FOR_ACTICATION_TIME)
- isTimeActivatedControl = true;
+ case STATE_WAIT_FOR_ACTICATION_TIME:
+ case STATE_WAIT_FOR_EXECUTION:
+ {
+ ControlHandlerResult dynamicCheckResult = CONTROL_RESULT_OK;
+ bool isTimeActivatedControl = false;
- setState(self, STATE_WAIT_FOR_EXECUTION);
+ if (state == STATE_WAIT_FOR_ACTICATION_TIME)
+ isTimeActivatedControl = true;
- if (self->waitForExecutionHandler != NULL) {
- checkOk = self->waitForExecutionHandler(self->waitForExecutionHandlerParameter, self->ctlVal,
- self->testMode, self->synchroCheck);
- }
+ if (self->waitForExecutionHandler != NULL) {
+ dynamicCheckResult = self->waitForExecutionHandler(self->waitForExecutionHandlerParameter, self->ctlVal,
+ self->testMode, self->synchroCheck);
+ }
- if (!checkOk) {
+ if (dynamicCheckResult == CONTROL_RESULT_FAILED) {
+ if (isTimeActivatedControl) {
+ ControlObject_sendLastApplError(self, self->mmsConnection, "Oper",
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
+ self->ctlNum, self->origin, false);
+ }
+ else
+ MmsServerConnection_sendWriteResponse(self->mmsConnection, self->operateInvokeId,
+ DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED, true);
- if (isTimeActivatedControl) {
- ControlObject_sendLastApplError(self, self->clientConnection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
- self->ctlNum, self->origin, false);
+ abortControlOperation(self);
+ exitControlTask(self);
}
- else
- MmsServerConnection_sendWriteResponse(self->clientConnection, self->operateInvokeId,
- DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED);
+ else if (dynamicCheckResult == CONTROL_RESULT_OK) {
+ if (isTimeActivatedControl) {
+ ControlObject_sendCommandTerminationPositive(self);
- abortControlOperation(self);
+ MmsValue* operTm = getOperParameterOperTime(self->oper);
- }
- else {
- if (isTimeActivatedControl) {
- ControlObject_sendCommandTerminationReq(self, self->clientConnection);
+ MmsValue_setUtcTime(operTm, 0);
- MmsValue* operTm = getOperParameterOperTime(self->oper);
+ }
+ else
+ MmsServerConnection_sendWriteResponse(self->mmsConnection, self->operateInvokeId,
+ DATA_ACCESS_ERROR_SUCCESS, true);
- MmsValue_setUtcTime(operTm, 0);
+ setState(self, STATE_OPERATE);
+ goto executeStateMachine;
}
- else
- MmsServerConnection_sendWriteResponse(self->clientConnection, self->operateInvokeId,
- DATA_ACCESS_ERROR_SUCCESS);
+ }
+ break;
+ case STATE_OPERATE:
+ {
uint64_t currentTime = Hal_getTimeInMs();
- setState(self, STATE_OPERATE);
+ ControlHandlerResult result = operateControl(self, self->ctlVal, currentTime, self->testMode);
- if (ControlObject_operate(self, self->ctlVal, currentTime, self->testMode)) {
+ if (result != CONTROL_RESULT_WAITING) {
- if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
- ControlObject_sendCommandTerminationReq(self, self->clientConnection);
- }
- }
- else {
+ if (result == CONTROL_RESULT_OK) {
- if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
- if (DEBUG_IED_SERVER)
- printf("Oper: operate failed!\n");
- ControlObject_sendLastApplError(self, self->clientConnection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_SELECT_FAILED,
- self->ctlNum, self->origin, false);
+ if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
+ ControlObject_sendCommandTerminationPositive(self);
+ }
}
- }
+ else {
- abortControlOperation(self);
- }
+ if ((self->ctlModel == 4) || (self->ctlModel == 3)) {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: operate failed!\n");
- /* synchronize with connection management */
- private_ClientConnection_decreaseTasksCount(iedClientConnection);
-}
+ ControlObject_sendCommandTerminationNegative(self);
+ }
+ }
-static void
-startControlOperateThread(ControlObject* self)
-{
- Thread thread = Thread_create((ThreadExecutionFunction) controlOperateThread, (void*) self, true);
+ abortControlOperation(self);
+ exitControlTask(self);
+ }
+ }
+ break;
- Thread_start(thread);
+ }
}
ControlObject*
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)
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);
+#endif
self->name = copyString(name);
self->lnName = lnName;
@@ -420,7 +477,9 @@ ControlObject_destroy(ControlObject* self)
free(self->name);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->stateLock);
+#endif
free(self);
}
@@ -527,7 +586,7 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection* conn
updateSboTimeoutValue(self);
self->selected = true;
self->selectTime = selectTime;
- self->clientConnection = connection;
+ self->mmsConnection = connection;
setState(self, STATE_READY);
}
@@ -551,7 +610,7 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection* connection)
{
- if (self->clientConnection == connection) {
+ if (self->mmsConnection == connection) {
abortControlOperation(self);
return true;
}
@@ -559,23 +618,11 @@ ControlObject_unselect(ControlObject* self, MmsServerConnection* connection)
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
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter)
{
- self->listener = listener;
- self->listenerParameter = parameter;
+ self->operateHandler = listener;
+ self->operateHandlerParameter = parameter;
}
void
@@ -601,7 +648,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
while (element != NULL) {
ControlObject* controlObject = (ControlObject*) element->data;
- if (controlObject->timeActivatedOperate) {
+ if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) {
if (controlObject->operateTime <= currentTimeInMs) {
@@ -610,25 +657,29 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
controlObject->timeActivatedOperate = false;
- bool checkOk = true;
+ CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (controlObject->checkHandler != NULL) { /* perform operative tests */
- checkOk = controlObject->checkHandler(
+ checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
- controlObject->interlockCheck, (ClientConnection) controlObject->clientConnection);
+ controlObject->interlockCheck, (ClientConnection) controlObject->mmsConnection);
}
- if (checkOk)
- startControlOperateThread(controlObject);
+ if (checkResult == CONTROL_ACCEPTED) {
+ executeControlTask(controlObject);
+ }
else {
- ControlObject_sendLastApplError(controlObject, controlObject->clientConnection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
+ ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
controlObject->ctlNum, controlObject->origin, false);
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);
@@ -767,14 +818,14 @@ getOperParameterTime(MmsValue* operParameters)
}
void
-ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection* connection)
+ControlObject_sendCommandTerminationPositive(ControlObject* self)
{
char itemId[130];
createStringInBuffer(itemId, 4, self->lnName, "$CO$", self->name, "$Oper");
if (DEBUG_IED_SERVER)
- printf("CmdTermination: %s\n", itemId);
+ printf("IED_SERVER: send CommandTermination+: %s\n", itemId);
char* domainId = MmsDomain_getName(self->mmsDomain);
@@ -789,15 +840,91 @@ ControlObject_sendCommandTerminationReq(ControlObject* self, MmsServerConnection
LinkedList_add(varSpecList, &varSpec);
LinkedList_add(values, self->oper);
- MmsServerConnection_sendInformationReportListOfVariables(connection, varSpecList, values, false);
+ MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, varSpecList, values, false);
LinkedList_destroyStatic(varSpecList);
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
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;
@@ -963,15 +1090,15 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
checkSelectTimeout(controlObject, currentTime);
if (getState(controlObject) == STATE_UNSELECTED) {
- bool checkOk = true;
+ CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (controlObject->checkHandler != NULL) { /* perform operative tests */
- checkOk = controlObject->checkHandler(
+ checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, NULL, false, false,
(ClientConnection) connection);
}
- if (checkOk) {
+ if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
value = ControlObject_getSBO(controlObject);
}
@@ -980,7 +1107,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
}
else {
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);
}
@@ -1031,6 +1158,27 @@ checkValidityOfOriginParameter(MmsValue* origin)
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
Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
MmsValue* value, MmsServerConnection* connection)
@@ -1138,19 +1286,19 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
- if (connection != controlObject->clientConnection)
+ if (connection != controlObject->mmsConnection)
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
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)
printf("SBOw: select failed!\n");
}
else {
- bool checkOk = true;
+ CheckHandlerResult checkResult = CONTROL_ACCEPTED;
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,
connection);
- checkOk = controlObject->checkHandler(
+ checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck,
clientConnection);
}
- if (checkOk) {
+ if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
updateControlParameters(controlObject, ctlVal, ctlNum, origin);
@@ -1177,10 +1325,10 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
printf("SBOw: selected successful\n");
}
else {
- indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
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)
printf("SBOw: select rejected by application!\n");
@@ -1225,8 +1373,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
- ctlNum, origin, true);
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
+ ctlNum, origin, true);
goto free_and_return;
}
@@ -1240,7 +1388,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
controlObject->testMode = testCondition;
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
- if (controlObject->clientConnection != connection) {
+ if (controlObject->mmsConnection != connection) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (DEBUG_IED_SERVER)
printf("Oper: operate from wrong client connection!\n");
@@ -1255,8 +1403,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
ControlObject_sendLastApplError(controlObject, connection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_INCONSISTENT_PARAMETERS,
- ctlNum, origin, true);
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
+ ctlNum, origin, true);
goto free_and_return;
}
@@ -1274,7 +1422,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
controlObject->timeActivatedOperate = true;
controlObject->synchroCheck = synchroCheck;
controlObject->interlockCheck = interlockCheck;
- controlObject->clientConnection = connection;
+ controlObject->mmsConnection = connection;
+
+ initiateControlTask(controlObject);
setState(controlObject, STATE_WAIT_FOR_ACTICATION_TIME);
@@ -1291,8 +1441,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CheckHandlerResult checkResult = CONTROL_ACCEPTED;
- setState(controlObject, STATE_PERFORM_TEST);
-
if (controlObject->checkHandler != NULL) { /* perform operative tests */
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
@@ -1307,20 +1455,22 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (checkResult == CONTROL_ACCEPTED) {
indication = DATA_ACCESS_ERROR_NO_RESPONSE;
- controlObject->clientConnection = connection;
+ controlObject->mmsConnection = 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 {
- 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
- indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
abortControlOperation(controlObject);
}
@@ -1329,19 +1479,19 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
else if (state == STATE_UNSELECTED) {
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;
ControlObject_sendLastApplError(controlObject, connection, "Oper",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_OBJECT_NOT_SELECTED,
- ctlNum, origin, true);
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_OBJECT_NOT_SELECTED,
+ ctlNum, origin, true);
goto free_and_return;
}
}
else if (strcmp(varName, "Cancel") == 0) {
if (DEBUG_IED_SERVER)
- printf("Received cancel!\n");
+ printf("IED_SERVER: control received cancel!\n");
int state = getState(controlObject);
@@ -1351,13 +1501,13 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if ((ctlNum == NULL) || (origin == NULL)) {
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
if (DEBUG_IED_SERVER)
- printf("Invalid cancel message!\n");
+ printf("IED_SERVER: Invalid cancel message!\n");
goto free_and_return;
}
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
if (state != STATE_UNSELECTED) {
- if (controlObject->clientConnection == connection) {
+ if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS;
setState(controlObject, STATE_UNSELECTED);
goto free_and_return;
@@ -1365,8 +1515,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
else {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
ControlObject_sendLastApplError(controlObject, connection, "Cancel",
- CONTROL_ERROR_NO_ERROR, CONTROL_ADD_CAUSE_LOCKED_BY_OTHER_CLIENT,
- ctlNum, origin, true);
+ CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT,
+ ctlNum, origin, true);
}
}
}
diff --git a/src/iedserver/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c
similarity index 88%
rename from src/iedserver/mms_mapping/mms_goose.c
rename to src/iec61850/server/mms_mapping/mms_goose.c
index dcf8a763..256a38d8 100644
--- a/src/iedserver/mms_mapping/mms_goose.c
+++ b/src/iec61850/server/mms_mapping/mms_goose.c
@@ -30,7 +30,7 @@
#include "linked_list.h"
#include "array_list.h"
-#include "thread.h"
+#include "hal_thread.h"
#include "reporting.h"
#include "mms_mapping_internal.h"
@@ -56,7 +56,10 @@ struct sMmsGooseControlBlock {
LinkedList dataSetValues;
uint64_t nextPublishTime;
int retransmissionsLeft; /* number of retransmissions left for the last event */
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore publisherMutex;
+#endif
MmsMapping* mmsMapping;
@@ -68,9 +71,11 @@ struct sMmsGooseControlBlock {
MmsGooseControlBlock
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);
+#endif
return self;
}
@@ -78,7 +83,9 @@ MmsGooseControlBlock_create()
void
MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->publisherMutex);
+#endif
if (self->publisher != NULL)
GoosePublisher_destroy(self->publisher);
@@ -87,13 +94,13 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
LinkedList_destroyStatic(self->dataSetValues);
if (self->goCBRef != NULL)
- free(self->goCBRef);
+ GLOBAL_FREEMEM(self->goCBRef);
if (self->goId != NULL)
- free(self->goId);
+ GLOBAL_FREEMEM(self->goId);
if (self->dataSetRef != NULL)
- free(self->dataSetRef);
+ GLOBAL_FREEMEM(self->dataSetRef);
if (self->dataSet != NULL) {
if (self->isDynamicDataSet)
@@ -102,7 +109,7 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
MmsValue_delete(self->mmsValue);
- free(self);
+ GLOBAL_FREEMEM(self);
}
MmsDomain*
@@ -150,13 +157,15 @@ MmsGooseControlBlock_isEnabled(MmsGooseControlBlock self)
void
MmsGooseControlBlock_enable(MmsGooseControlBlock self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
+#endif
if (!MmsGooseControlBlock_isEnabled(self)) {
if (self->dataSetRef != NULL) {
- free(self->dataSetRef);
+ GLOBAL_FREEMEM(self->dataSetRef);
if (self->dataSet != NULL)
if (self->isDynamicDataSet)
@@ -240,7 +249,9 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
+#endif
}
void
@@ -253,7 +264,9 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self)
self->goEna = false;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
+#endif
if (self->publisher != NULL) {
GoosePublisher_destroy(self->publisher);
@@ -262,7 +275,9 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self)
self->dataSetValues = NULL;
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
+#endif
}
}
@@ -272,7 +287,9 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
{
if (currentTime >= self->nextPublishTime) {
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
+#endif
GoosePublisher_publish(self->publisher, self->dataSetValues);
@@ -297,14 +314,18 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL;
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
+#endif
}
}
void
MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
+#endif
uint64_t currentTime = GoosePublisher_increaseStNum(self->publisher);
@@ -327,74 +348,76 @@ MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
GoosePublisher_publish(self->publisher, self->dataSetValues);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
+#endif
}
static MmsVariableSpecification*
createMmsGooseControlBlock(char* gcbName)
{
- MmsVariableSpecification* gcb = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ MmsVariableSpecification* gcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
gcb->name = copyString(gcbName);
gcb->type = MMS_STRUCTURE;
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;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GoEna");
namedVariable->type = MMS_BOOLEAN;
gcb->typeSpec.structure.elements[0] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GoID");
namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
gcb->typeSpec.structure.elements[1] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DatSet");
namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
gcb->typeSpec.structure.elements[2] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("ConfRev");
namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[3] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("NdsCom");
namedVariable->type = MMS_BOOLEAN;
gcb->typeSpec.structure.elements[4] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("DstAddress");
MmsMapping_createPhyComAddrStructure(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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[6] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("MaxTime");
namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
gcb->typeSpec.structure.elements[7] = namedVariable;
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("FixedOffs");
namedVariable->type = MMS_BOOLEAN;
@@ -439,13 +462,13 @@ MmsVariableSpecification*
GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int gseCount)
{
- MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GO");
namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = gseCount;
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(gseCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(gseCount,
sizeof(MmsVariableSpecification*));
int currentGCB = 0;
diff --git a/src/iedserver/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
similarity index 71%
rename from src/iedserver/mms_mapping/mms_mapping.c
rename to src/iec61850/server/mms_mapping/mms_mapping.c
index d70805db..9cba233e 100644
--- a/src/iedserver/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -32,8 +32,12 @@
#include "control.h"
#include "ied_server_private.h"
-#ifndef DEBUG_IDE_SERVER
-#define DEBUG_IDE_SERVER 0
+#ifndef CONFIG_IEC61850_SG_RESVTMS
+#define CONFIG_IEC61850_SG_RESVTMS 100
+#endif
+
+#ifndef DEBUG_IED_SERVER
+#define DEBUG_IED_SERVER 0
#endif
typedef struct
@@ -48,6 +52,26 @@ typedef struct
WriteAccessHandler handler;
} AttributeAccessHandler;
+
+typedef struct
+{
+ SettingGroupControlBlock* sgcb;
+ MmsValue* sgcbMmsValues;
+ MmsDomain* mmsDomain;
+
+ ActiveSettingGroupChangedHandler actSgChangedHandler;
+ void* actSgChangedHandlerParameter;
+
+ EditSettingGroupChangedHandler editSgChangedHandler;
+ void* editSgChangedHandlerParameter;
+
+ EditSettingGroupConfirmationHandler editSgConfirmedHandler;
+ void* editSgConfirmedHandlerParameter;
+
+ ClientConnection editingClient;
+ uint64_t reservationTimeout;
+} SettingGroup;
+
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
MmsValue*
Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
@@ -59,30 +83,30 @@ MmsMapping_createPhyComAddrStructure(MmsVariableSpecification* namedVariable)
{
namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = 4;
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(4,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(4,
sizeof(MmsVariableSpecification*));
MmsVariableSpecification* element;
- element = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
element->name = copyString("Addr");
element->type = MMS_OCTET_STRING;
element->typeSpec.octetString = 6;
namedVariable->typeSpec.structure.elements[0] = element;
- element = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
element->name = copyString("PRIORITY");
element->type = MMS_UNSIGNED;
element->typeSpec.unsignedInteger = 8;
namedVariable->typeSpec.structure.elements[1] = element;
- element = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
element->name = copyString("VID");
element->type = MMS_UNSIGNED;
element->typeSpec.unsignedInteger = 16;
namedVariable->typeSpec.structure.elements[2] = element;
- element = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
element->name = copyString("APPID");
element->type = MMS_UNSIGNED;
element->typeSpec.unsignedInteger = 16;
@@ -92,7 +116,7 @@ MmsMapping_createPhyComAddrStructure(MmsVariableSpecification* namedVariable)
static MmsVariableSpecification*
createNamedVariableFromDataAttribute(DataAttribute* attribute)
{
- MmsVariableSpecification* origNamedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* origNamedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
origNamedVariable->name = copyString(attribute->name);
@@ -101,7 +125,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
if (attribute->elementCount > 0) {
namedVariable->type = MMS_ARRAY;
namedVariable->typeSpec.array.elementCount = attribute->elementCount;
- namedVariable->typeSpec.array.elementTypeSpec = (MmsVariableSpecification*) calloc(1,
+ namedVariable->typeSpec.array.elementTypeSpec = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable = namedVariable->typeSpec.array.elementTypeSpec;
}
@@ -111,7 +135,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
int componentCount = ModelNode_getChildCount((ModelNode*) attribute);
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(componentCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(componentCount,
sizeof(MmsVariableSpecification*));
DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild;
@@ -244,7 +268,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
MmsMapping_createPhyComAddrStructure(namedVariable);
break;
default:
- if (DEBUG_IDE_SERVER)
+ if (DEBUG_IED_SERVER)
printf("MMS-MAPPING: type cannot be mapped %i\n", attribute->type);
break;
}
@@ -284,7 +308,7 @@ static MmsVariableSpecification*
createFCNamedVariableFromDataObject(DataObject* dataObject,
FunctionalConstraint fc)
{
- MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
MmsVariableSpecification* completeNamedVariable = namedVariable;
@@ -294,7 +318,7 @@ createFCNamedVariableFromDataObject(DataObject* dataObject,
if (dataObject->elementCount > 0) {
namedVariable->type = MMS_ARRAY;
namedVariable->typeSpec.array.elementCount = dataObject->elementCount;
- namedVariable->typeSpec.array.elementTypeSpec = (MmsVariableSpecification*) calloc(1,
+ namedVariable->typeSpec.array.elementTypeSpec = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable = namedVariable->typeSpec.array.elementTypeSpec;
}
@@ -304,7 +328,7 @@ createFCNamedVariableFromDataObject(DataObject* dataObject,
int elementCount = countChildrenWithFc(dataObject, fc);
/* Allocate memory for components */
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(elementCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(elementCount,
sizeof(MmsVariableSpecification*));
int i = 0;
@@ -342,7 +366,7 @@ createFCNamedVariableFromDataObject(DataObject* dataObject,
static MmsVariableSpecification*
createFCNamedVariable(LogicalNode* logicalNode, FunctionalConstraint fc)
{
- MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable->name = copyString(FunctionalConstraint_toString(fc));
namedVariable->type = MMS_STRUCTURE;
@@ -359,7 +383,7 @@ createFCNamedVariable(LogicalNode* logicalNode, FunctionalConstraint fc)
}
namedVariable->typeSpec.structure.elementCount = dataObjectCount;
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(dataObjectCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(dataObjectCount,
sizeof(MmsVariableSpecification*));
dataObjectCount = 0;
@@ -381,6 +405,283 @@ createFCNamedVariable(LogicalNode* logicalNode, FunctionalConstraint fc)
return namedVariable;
}
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+
+static MmsVariableSpecification*
+createSGCB(void)
+{
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
+ sizeof(MmsVariableSpecification));
+ namedVariable->name = copyString("SGCB");
+ namedVariable->type = MMS_STRUCTURE;
+
+ namedVariable->typeSpec.structure.elementCount = 6;
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(6,
+ sizeof(MmsVariableSpecification*));
+
+ MmsVariableSpecification* element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("NumOfSG");
+ element->type = MMS_UNSIGNED;
+ element->typeSpec.integer = 8;
+ namedVariable->typeSpec.structure.elements[0] = element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("ActSG");
+ element->type = MMS_UNSIGNED;
+ element->typeSpec.integer = 8;
+ namedVariable->typeSpec.structure.elements[1] = element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("EditSG");
+ element->type = MMS_UNSIGNED;
+ element->typeSpec.integer = 8;
+ namedVariable->typeSpec.structure.elements[2] = element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("CnfEdit");
+ element->type = MMS_BOOLEAN;
+ namedVariable->typeSpec.structure.elements[3] = element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("LActTm");
+ element->type = MMS_UTC_TIME;
+ namedVariable->typeSpec.structure.elements[4] = element;
+
+ element = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
+ element->name = copyString("ResvTms");
+ element->type = MMS_UNSIGNED;
+ element->typeSpec.integer = 16;
+ namedVariable->typeSpec.structure.elements[5] = element;
+
+ return namedVariable;
+}
+
+
+static MmsVariableSpecification*
+createFCNamedVariableSPWithSGCB(LogicalNode* logicalNode)
+{
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
+ sizeof(MmsVariableSpecification));
+ namedVariable->name = copyString("SP");
+ namedVariable->type = MMS_STRUCTURE;
+
+ int dataObjectCount = 1;
+
+ DataObject* dataObject = (DataObject*) logicalNode->firstChild;
+
+ while (dataObject != NULL) {
+ if (DataObject_hasFCData(dataObject, SP))
+ dataObjectCount++;
+
+ dataObject = (DataObject*) dataObject->sibling;
+ }
+
+ namedVariable->typeSpec.structure.elementCount = dataObjectCount;
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(dataObjectCount,
+ sizeof(MmsVariableSpecification*));
+
+ dataObjectCount = 1;
+
+ namedVariable->typeSpec.structure.elements[0] = createSGCB();
+
+ dataObject = (DataObject*) logicalNode->firstChild;
+
+ while (dataObject != NULL) {
+ if (DataObject_hasFCData(dataObject, SP)) {
+
+ namedVariable->typeSpec.structure.elements[dataObjectCount] =
+ createFCNamedVariableFromDataObject(dataObject, SP);
+
+ dataObjectCount++;
+ }
+
+ dataObject = (DataObject*) dataObject->sibling;
+ }
+
+ return namedVariable;
+}
+
+static bool
+isSettingGroupControlBlock(char* separator)
+{
+ if (strncmp(separator + 1, "SP$SGCB", 7) == 0)
+ return true;
+
+ return false;
+}
+
+static SettingGroup*
+getSettingGroupByMmsDomain(MmsMapping* self, MmsDomain* domain)
+{
+ LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups);
+
+ while (settingGroupElement != NULL) {
+ SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement);
+
+ if (settingGroup->mmsDomain == domain)
+ return settingGroup;
+
+ settingGroupElement = LinkedList_getNext(settingGroupElement);
+ }
+
+ return NULL;
+}
+
+static SettingGroup*
+getSettingGroupBySGCB(MmsMapping* self, SettingGroupControlBlock* sgcb)
+{
+ LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups);
+
+ while (settingGroupElement != NULL) {
+ SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement);
+
+ if (settingGroup->sgcb == sgcb)
+ return settingGroup;
+
+ settingGroupElement = LinkedList_getNext(settingGroupElement);
+ }
+
+ return NULL;
+}
+
+static void
+unselectSettingGroup(SettingGroup* settingGroup)
+{
+ printf("IED_SERVER: Unselect setting group\n");
+ settingGroup->sgcb->editSG = 0;
+ settingGroup->editingClient = NULL;
+ MmsValue* editSg = MmsValue_getElement(settingGroup->sgcbMmsValues, 2);
+ MmsValue_setUint8(editSg, 0U);
+ MmsValue* resvTms = MmsValue_getElement(settingGroup->sgcbMmsValues, 5);
+ MmsValue_setUint16(resvTms, 0U);
+}
+
+static void
+unselectAllSettingGroups(MmsMapping* self, MmsServerConnection* serverCon)
+{
+ LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups);
+
+ while (settingGroupElement != NULL) {
+ SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement);
+
+ if (settingGroup->editingClient == (ClientConnection) serverCon)
+ unselectSettingGroup(settingGroup);
+
+ settingGroupElement = LinkedList_getNext(settingGroupElement);
+ }
+}
+
+void
+MmsMapping_checkForSettingGroupReservationTimeouts(MmsMapping* self, uint64_t currentTime)
+{
+ LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups);
+
+ while (settingGroupElement != NULL) {
+ SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement);
+
+ if (settingGroup->sgcb->editSG != 0)
+ if (currentTime >= settingGroup->reservationTimeout)
+ unselectSettingGroup(settingGroup);
+
+ settingGroupElement = LinkedList_getNext(settingGroupElement);
+ }
+}
+
+void
+MmsMapping_configureSettingGroups(MmsMapping* self)
+{
+
+ LinkedList settingGroupElement = LinkedList_getNext(self->settingGroups);
+
+ while (settingGroupElement != NULL) {
+ SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement);
+
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: Configure setting group\n");
+
+ MmsValue* values =
+ MmsServer_getValueFromCache(self->mmsServer, settingGroup->mmsDomain, "LLN0$SP$SGCB");
+
+ if (values != NULL) {
+ settingGroup->sgcb->resvTms = CONFIG_IEC61850_SG_RESVTMS;
+
+ MmsValue* numOfSg = MmsValue_getElement(values, 0);
+ MmsValue* actSg = MmsValue_getElement(values, 1);
+ MmsValue* resvTms = MmsValue_getElement(values, 5);
+
+ MmsValue_setUint8(numOfSg, settingGroup->sgcb->numOfSGs);
+ MmsValue_setUint8(actSg, settingGroup->sgcb->actSG);
+ MmsValue_setUint16(resvTms, 0U);
+
+ settingGroup->sgcbMmsValues = values;
+
+ break;
+ }
+ else if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: Values for SGCB do not exist\n");
+
+ settingGroupElement = LinkedList_getNext(settingGroupElement);
+ }
+}
+
+void
+MmsMapping_setSgChangedHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
+ ActiveSettingGroupChangedHandler handler, void* parameter)
+{
+ SettingGroup* sg = getSettingGroupBySGCB(self, sgcb);
+
+ if (sg != NULL) {
+ sg->actSgChangedHandler = handler;
+ sg->actSgChangedHandlerParameter = parameter;
+ }
+}
+
+void
+MmsMapping_setEditSgChangedHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
+ EditSettingGroupChangedHandler handler, void* parameter)
+{
+ SettingGroup* sg = getSettingGroupBySGCB(self, sgcb);
+
+ if (sg != NULL) {
+ sg->editSgChangedHandler = handler;
+ sg->editSgChangedHandlerParameter = parameter;
+ }
+}
+
+void
+MmsMapping_setConfirmEditSgHandler(MmsMapping* self, SettingGroupControlBlock* sgcb,
+ EditSettingGroupConfirmationHandler handler, void* parameter)
+{
+ SettingGroup* sg = getSettingGroupBySGCB(self, sgcb);
+
+ if (sg != NULL) {
+ sg->editSgConfirmedHandler = handler;
+ sg->editSgConfirmedHandlerParameter = parameter;
+ }
+}
+
+void
+MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock* sgcb, uint8_t newActiveSg)
+{
+ SettingGroup* sg = getSettingGroupBySGCB(self, sgcb);
+
+ if (sg != NULL) {
+ if (newActiveSg > 0 && newActiveSg <= sgcb->numOfSGs) {
+ sgcb->actSG = newActiveSg;
+
+ MmsValue* actSg = MmsValue_getElement(sg->sgcbMmsValues, 1);
+ MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
+
+ MmsValue_setUint8(actSg, sgcb->actSG);
+ MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs());
+ }
+ }
+}
+
+#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
+
static int
determineLogicalNodeComponentCount(LogicalNode* logicalNode)
{
@@ -474,12 +775,27 @@ countGSEControlBlocksForLogicalNode(MmsMapping* self, LogicalNode* logicalNode)
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
+static SettingGroupControlBlock*
+checkForSgcb(MmsMapping* self, LogicalNode* logicalNode)
+{
+ SettingGroupControlBlock* sgcb = self->model->sgcbs;
+
+ while (sgcb != NULL) {
+ if (sgcb->parent == logicalNode)
+ return sgcb;
+
+ sgcb = sgcb->sibling;
+ }
+
+ return NULL;
+}
+
static MmsVariableSpecification*
createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode)
{
MmsVariableSpecification* namedVariable = (MmsVariableSpecification*)
- malloc(sizeof(MmsVariableSpecification));
+ GLOBAL_MALLOC(sizeof(MmsVariableSpecification));
namedVariable->name = copyString(logicalNode->name);
@@ -487,7 +803,27 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
int componentCount = determineLogicalNodeComponentCount(logicalNode);
- if (DEBUG_IDE_SERVER)
+
+ SettingGroupControlBlock* sgControlBlock = NULL;
+
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+
+ sgControlBlock = checkForSgcb(self, logicalNode);
+
+ if (sgControlBlock != NULL) {
+ if (LogicalNode_hasFCData(logicalNode, SP) == false)
+ componentCount++;
+
+ SettingGroup* settingGroup = (SettingGroup*) GLOBAL_CALLOC(1, sizeof(SettingGroup));
+
+ settingGroup->sgcb = sgControlBlock;
+ settingGroup->mmsDomain = domain;
+
+ LinkedList_add(self->settingGroups, (void*) settingGroup);
+ }
+#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
+
+ if (DEBUG_IED_SERVER)
printf("LogicalNode %s has %i fc components\n", logicalNode->name,
componentCount);
@@ -496,7 +832,7 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
true);
if (brcbCount > 0) {
- if (DEBUG_IDE_SERVER)
+ if (DEBUG_IED_SERVER)
printf(" and %i buffered RCBs\n", brcbCount);
componentCount++;
}
@@ -505,7 +841,7 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
false);
if (urcbCount > 0) {
- if (DEBUG_IDE_SERVER)
+ if (DEBUG_IED_SERVER)
printf(" and %i unbuffered RCBs\n", urcbCount);
componentCount++;
}
@@ -516,14 +852,14 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
int gseCount = countGSEControlBlocksForLogicalNode(self, logicalNode);
if (gseCount > 0) {
- if (DEBUG_IDE_SERVER)
+ if (DEBUG_IED_SERVER)
printf(" and %i GSE control blocks\n", gseCount);
componentCount++;
}
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(componentCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(componentCount,
sizeof(MmsVariableSpecification*));
/* Create a named variable of type structure for each functional constrained */
@@ -559,6 +895,14 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
currentComponent++;
}
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ if (sgControlBlock != NULL) {
+ namedVariable->typeSpec.structure.elements[currentComponent] =
+ createFCNamedVariableSPWithSGCB(logicalNode);
+ currentComponent++;
+ }
+ else
+#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
if (LogicalNode_hasFCData(logicalNode, SP)) {
namedVariable->typeSpec.structure.elements[currentComponent] =
createFCNamedVariable(logicalNode, SP);
@@ -600,6 +944,8 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
}
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
+
+
if (LogicalNode_hasFCData(logicalNode, SV)) {
namedVariable->typeSpec.structure.elements[currentComponent] =
createFCNamedVariable(logicalNode, SV);
@@ -644,13 +990,18 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain,
static MmsDomain*
createMmsDomainFromIedDevice(MmsMapping* self, LogicalDevice* logicalDevice)
{
- MmsDomain* domain = MmsDomain_create(logicalDevice->name);
+ char domainName[65];
+
+ strncpy(domainName, self->model->name, 64);
+ strncat(domainName, logicalDevice->name, 64);
+
+ MmsDomain* domain = MmsDomain_create(domainName);
int nodesCount = LogicalDevice_getLogicalNodeCount(logicalDevice);
/* Logical nodes are first level named variables */
domain->namedVariablesCount = nodesCount;
- domain->namedVariables = (MmsVariableSpecification**) malloc(nodesCount * sizeof(MmsVariableSpecification*));
+ domain->namedVariables = (MmsVariableSpecification**) GLOBAL_MALLOC(nodesCount * sizeof(MmsVariableSpecification*));
LogicalNode* logicalNode = (LogicalNode*) logicalDevice->firstChild;
@@ -670,7 +1021,7 @@ static void
createMmsDataModel(MmsMapping* self, int iedDeviceCount,
MmsDevice* mmsDevice, IedModel* iedModel)
{
- mmsDevice->domains = (MmsDomain**) malloc((iedDeviceCount) * sizeof(MmsDomain*));
+ mmsDevice->domains = (MmsDomain**) GLOBAL_MALLOC((iedDeviceCount) * sizeof(MmsDomain*));
mmsDevice->domainCount = iedDeviceCount;
LogicalDevice* logicalDevice = iedModel->firstChild;
@@ -689,8 +1040,13 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel)
{
DataSet* dataset = iedModel->dataSets;
+ char domainName[65];
+
while (dataset != NULL) {
- MmsDomain* dataSetDomain = MmsDevice_getDomain(mmsDevice, dataset->logicalDeviceName);
+ strncpy(domainName, iedModel->name, 64);
+ strncat(domainName, dataset->logicalDeviceName, 64);
+
+ MmsDomain* dataSetDomain = MmsDevice_getDomain(mmsDevice, domainName);
MmsNamedVariableList varList = MmsNamedVariableList_create(dataset->name, false);
@@ -700,8 +1056,10 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel)
MmsAccessSpecifier accessSpecifier;
- accessSpecifier.domain = MmsDevice_getDomain(mmsDevice,
- dataSetEntry->logicalDeviceName);
+ strncpy(domainName, iedModel->name, 64);
+ strncat(domainName, dataSetEntry->logicalDeviceName, 64);
+
+ accessSpecifier.domain = MmsDevice_getDomain(mmsDevice, domainName);
accessSpecifier.variableName = dataSetEntry->variableName;
accessSpecifier.arrayIndex = dataSetEntry->index;
@@ -743,7 +1101,7 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel)
MmsMapping*
MmsMapping_create(IedModel* model)
{
- MmsMapping* self = (MmsMapping*) calloc(1, sizeof(struct sMmsMapping));
+ MmsMapping* self = (MmsMapping*) GLOBAL_CALLOC(1, sizeof(struct sMmsMapping));
self->model = model;
@@ -759,6 +1117,10 @@ MmsMapping_create(IedModel* model)
self->controlObjects = LinkedList_create();
#endif
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ self->settingGroups = LinkedList_create();
+#endif
+
self->observedObjects = LinkedList_create();
self->attributeAccessHandlers = LinkedList_create();
@@ -772,10 +1134,12 @@ void
MmsMapping_destroy(MmsMapping* self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
if (self->reportWorkerThread != NULL) {
self->reportThreadRunning = false;
Thread_destroy(self->reportWorkerThread);
}
+#endif
if (self->mmsDevice != NULL)
MmsDevice_destroy(self->mmsDevice);
@@ -792,13 +1156,17 @@ MmsMapping_destroy(MmsMapping* self)
LinkedList_destroyDeep(self->controlObjects, (LinkedListValueDeleteFunction) ControlObject_destroy);
#endif
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ LinkedList_destroy(self->settingGroups);
+#endif
+
LinkedList_destroy(self->observedObjects);
LinkedList_destroy(self->attributeAccessHandlers);
IedModel_setAttributeValuesToNull(self->model);
- free(self);
+ GLOBAL_FREEMEM(self);
}
MmsDevice*
@@ -857,6 +1225,16 @@ isFunctionalConstraintSV(char* separator)
return false;
}
+static bool
+isFunctionalConstraintSE(char* separator)
+{
+ if (strncmp(separator + 1, "SE", 2) == 0)
+ return true;
+ else
+ return false;
+}
+
+
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
static bool
isControllable(char* separator)
@@ -1032,6 +1410,8 @@ getFunctionalConstraintForWritableNode(MmsMapping* self, char* separator)
return SP;
if (isFunctionalConstraintSV(separator))
return SV;
+ if (isFunctionalConstraintSE(separator))
+ return SE;
return NONE;
}
@@ -1067,6 +1447,13 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc)
return ACCESS_POLICY_DENY;
}
+ if (fc == SE) {
+ if (self->iedServer->writeAccessPolicies & ALLOW_WRITE_ACCESS_SE)
+ return ACCESS_POLICY_ALLOW;
+ else
+ return ACCESS_POLICY_DENY;
+ }
+
return ACCESS_POLICY_DENY;
}
@@ -1077,7 +1464,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
MmsMapping* self = (MmsMapping*) parameter;
if (DEBUG_IED_SERVER)
- printf("Write requested %s\n", variableId);
+ printf("IED_SERVER: Write requested %s\n", variableId);
/* Access control based on functional constraint */
@@ -1153,6 +1540,130 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ if (isSettingGroupControlBlock(separator)) {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: Access to SGCB\n");
+
+ char* nextSep = strchr(separator + 1, '$');
+
+ if (nextSep != NULL) {
+ nextSep = strchr(nextSep + 1, '$');
+
+ char* nameId = nextSep + 1;
+
+ if (strcmp(nameId, "ActSG") == 0) {
+ SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
+
+ if (sg != NULL) {
+ uint32_t val = MmsValue_toUint32(value);
+
+ if ((val > 0) && (val <= sg->sgcb->numOfSGs)) {
+ if (val != sg->sgcb->actSG) {
+
+ if (sg->actSgChangedHandler != NULL) {
+ if (sg->actSgChangedHandler(sg->actSgChangedHandlerParameter, sg->sgcb,
+ (uint8_t) val, (ClientConnection) connection))
+ {
+ sg->sgcb->actSG = val;
+
+ MmsValue* actSg = MmsValue_getElement(sg->sgcbMmsValues, 1);
+ MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
+
+ MmsValue_setUint8(actSg, sg->sgcb->actSG);
+ MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs());
+ }
+
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+
+ }
+
+ return DATA_ACCESS_ERROR_SUCCESS;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ }
+ }
+ else if (strcmp(nameId, "EditSG") == 0) {
+ SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
+
+ if (sg != NULL) {
+ uint32_t val = MmsValue_toUint32(value);
+
+ if ((val == 0) && (sg->editingClient == (ClientConnection) connection)) {
+ unselectSettingGroup(sg);
+ return DATA_ACCESS_ERROR_SUCCESS;
+ }
+
+ if ((val > 0) && (val <= sg->sgcb->numOfSGs)) {
+
+ if (sg->editSgChangedHandler != NULL) {
+ if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb,
+ (uint8_t) val, (ClientConnection) connection))
+ {
+ sg->sgcb->editSG = val;
+ sg->editingClient = (ClientConnection) connection;
+
+ sg->reservationTimeout = Hal_getTimeInMs() + (sg->sgcb->resvTms * 1000);
+
+ MmsValue* editSg = MmsValue_getElement(sg->sgcbMmsValues, 2);
+ MmsValue* resvTms = MmsValue_getElement(sg->sgcbMmsValues, 5);
+
+ MmsValue_setUint16(resvTms, sg->sgcb->resvTms);
+ MmsValue_setUint8(editSg, sg->sgcb->editSG);
+
+ return DATA_ACCESS_ERROR_SUCCESS;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+
+
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ }
+ }
+ else if (strcmp(nameId, "CnfEdit") == 0) {
+ SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
+
+ if (sg != NULL) {
+ bool val = MmsValue_getBoolean(value);
+
+ if (val == true) {
+ if (sg->sgcb->editSG != 0) {
+ if (sg->editingClient == (ClientConnection) connection) {
+ if (sg->editSgConfirmedHandler != NULL) {
+ sg->editSgConfirmedHandler(sg->editSgConfirmedHandlerParameter, sg->sgcb,
+ sg->sgcb->editSG);
+
+ return DATA_ACCESS_ERROR_SUCCESS;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ }
+ }
+ }
+
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
+
FunctionalConstraint fc = getFunctionalConstraintForWritableNode(self, separator);
/* writable data model elements - SP, SV, CF, DC */
@@ -1170,6 +1681,22 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
AccessPolicy nodeAccessPolicy = getAccessPolicyForFC(self, fc);
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: write to %s policy:%i\n", variableId, nodeAccessPolicy);
+
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ if (isFunctionalConstraintSE(separator)) {
+ SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
+
+ if (sg != NULL) {
+ if (sg->sgcb->editSG == 0)
+ return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ }
+#endif
+
/* Call writer access handlers */
LinkedList writeHandlerListElement = LinkedList_getNext(self->attributeAccessHandlers);
@@ -1216,6 +1743,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
if (da != NULL)
IedServer_updateAttributeValue(self->iedServer, da, value);
+ else
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
/* Call observer callback */
LinkedList observerListElement = LinkedList_getNext(self->observedObjects);
@@ -1238,17 +1767,19 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
+ printf("WRITE ACCESS DENIED!\n");
+
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
void
MmsMapping_addObservedAttribute(MmsMapping* self, DataAttribute* dataAttribute,
- void* handler)
+ AttributeChangedHandler handler)
{
- AttributeObserver* observer = (AttributeObserver*) malloc(sizeof(AttributeObserver));
+ AttributeObserver* observer = (AttributeObserver*) GLOBAL_MALLOC(sizeof(AttributeObserver));
observer->attribute = dataAttribute;
- observer->handler = (AttributeChangedHandler) handler;
+ observer->handler = handler;
LinkedList_add(self->observedObjects, observer);
}
@@ -1276,7 +1807,7 @@ MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttrib
AttributeAccessHandler* accessHandler = getAccessHandlerForAttribute(self, dataAttribute);
if (accessHandler == NULL) {
- accessHandler = (AttributeAccessHandler*) malloc(sizeof(AttributeAccessHandler));
+ accessHandler = (AttributeAccessHandler*) GLOBAL_MALLOC(sizeof(AttributeAccessHandler));
accessHandler->attribute = dataAttribute;
LinkedList_add(self->attributeAccessHandlers, (void*) accessHandler);
@@ -1337,8 +1868,8 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
{
MmsMapping* self = (MmsMapping*) parameter;
- if (DEBUG_IDE_SERVER)
- printf("mmsReadHandler: Requested %s\n", variableId);
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: mmsReadHandler: Requested %s\n", variableId);
char* separator = strchr(variableId, '$');
@@ -1459,9 +1990,11 @@ mmsConnectionHandler(void* parameter, MmsServerConnection* connection, MmsServer
private_IedServer_removeClientConnection(self->iedServer, clientConnection);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
/* wait until control threads are finished */
while (private_ClientConnection_getTasksCount(clientConnection) > 0)
Thread_sleep(10);
+#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
Reporting_deactivateReportsForConnection(self, connection);
@@ -1471,6 +2004,10 @@ mmsConnectionHandler(void* parameter, MmsServerConnection* connection, MmsServer
unselectControlsForConnection(self, connection);
#endif
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ unselectAllSettingGroups(self, connection);
+#endif
+
private_ClientConnection_destroy(clientConnection);
}
else if (event == MMS_SERVER_NEW_CONNECTION) {
@@ -1486,11 +2023,40 @@ mmsConnectionHandler(void* parameter, MmsServerConnection* connection, MmsServer
}
}
+static MmsDataAccessError
+mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection* connection)
+{
+ MmsMapping* self = (MmsMapping*) parameter;
+
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: mmsReadAccessHandler: Requested %s\n", variableId);
+
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+
+ char* separator = strchr(variableId, '$');
+
+ if (isFunctionalConstraintSE(separator)) {
+ SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
+
+ if (sg != NULL) {
+ if (sg->sgcb->editSG == 0)
+ return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
+ }
+ else
+ return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ }
+
+#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
+
+ return DATA_ACCESS_ERROR_SUCCESS;
+}
+
void
MmsMapping_installHandlers(MmsMapping* self)
{
MmsServer_installReadHandler(self->mmsServer, mmsReadHandler, (void*) self);
MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self);
+ MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self);
MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self);
}
@@ -1543,11 +2109,13 @@ DataSet_isMemberValue(DataSet* dataSet, MmsValue* value, int* index)
MmsValue* dataSetValue = dataSetEntry->value;
- if (isMemberValueRecursive(dataSetValue, value)) {
- if (index != NULL)
- *index = i;
+ if (dataSetValue != NULL) { /* prevent invalid data set members */
+ if (isMemberValueRecursive(dataSetValue, value)) {
+ if (index != NULL)
+ *index = i;
- return true;
+ return true;
+ }
}
i++;
@@ -1632,6 +2200,18 @@ MmsMapping_enableGoosePublishing(MmsMapping* self)
}
+void
+MmsMapping_disableGoosePublishing(MmsMapping* self)
+{
+ LinkedList element = self->gseControls;
+
+ while ((element = LinkedList_getNext(element)) != NULL) {
+ MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
+
+ MmsGooseControlBlock_disable(gcb);
+ }
+}
+
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
@@ -1650,11 +2230,15 @@ MmsMapping_getControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, c
char*
-MmsMapping_getMmsDomainFromObjectReference(char* objectReference, char* buffer)
+MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer)
{
int objRefLength = strlen(objectReference);
- //check if LD name is present
+ /* check for object reference size limit VISIBLESTRING129 */
+ if (objRefLength > 129)
+ return NULL;
+
+ /* check if LD name is present */
int i;
for (i = 0; i < objRefLength; i++) {
if (objectReference[i] == '/') {
@@ -1662,13 +2246,17 @@ MmsMapping_getMmsDomainFromObjectReference(char* objectReference, char* buffer)
}
}
+ /* check for LD name limit (=64 characters) */
+ if (i > 64)
+ return NULL;
+
if (i == objRefLength)
return NULL;
char* domainName;
if (buffer == NULL)
- domainName = (char*) malloc(i + 1);
+ domainName = (char*) GLOBAL_MALLOC(i + 1);
else
domainName = buffer;
@@ -1683,12 +2271,16 @@ MmsMapping_getMmsDomainFromObjectReference(char* objectReference, char* buffer)
}
char*
-MmsMapping_createMmsVariableNameFromObjectReference(char* objectReference,
+MmsMapping_createMmsVariableNameFromObjectReference(const char* objectReference,
FunctionalConstraint fc, char* buffer)
{
int objRefLength = strlen(objectReference);
- //check if LD name is present
+ /* check for object reference size limit VISIBLESTRING129 */
+ if (objRefLength > 129)
+ return NULL;
+
+ /* check if LD name is present */
int i;
for (i = 0; i < objRefLength; i++) {
if (objectReference[i] == '/') {
@@ -1696,6 +2288,10 @@ MmsMapping_createMmsVariableNameFromObjectReference(char* objectReference,
}
}
+ /* check for LD name limit (= 64 characters) */
+ if (i > 64)
+ return NULL;
+
if (i == objRefLength)
i = 0;
else
@@ -1706,10 +2302,16 @@ MmsMapping_createMmsVariableNameFromObjectReference(char* objectReference,
if (fcString == NULL)
return NULL;
+ int namePartLength = objRefLength - i - 1;
+
+ /* ensure that limit due to MMS name part length = 64 is not exceeded */
+ if (namePartLength > 61)
+ return NULL;
+
char* mmsVariableName;
if (buffer == NULL)
- mmsVariableName = (char*) malloc(objRefLength - i + 4);
+ mmsVariableName = (char*) GLOBAL_MALLOC(namePartLength + 5);
else
mmsVariableName = buffer;
@@ -1760,37 +2362,53 @@ GOOSE_processGooseEvents(MmsMapping* self, uint64_t currentTimeInMs)
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
-/* single worker thread for all enabled GOOSE and report control blocks
- *
- * TODO move GOOSE processing to other (high-priority) thread
- * */
+
+
static void
-eventWorkerThread(MmsMapping* self)
+processPeriodicTasks(MmsMapping* self)
{
- bool running = true;
- self->reportThreadFinished = false;
-
- while (running) {
- uint64_t currentTimeInMs = Hal_getTimeInMs();
+ uint64_t currentTimeInMs = Hal_getTimeInMs();
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
- GOOSE_processGooseEvents(self, currentTimeInMs);
+ GOOSE_processGooseEvents(self, currentTimeInMs);
#endif
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
- Control_processControlActions(self, currentTimeInMs);
+ Control_processControlActions(self, currentTimeInMs);
#endif
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
- Reporting_processReportEvents(self, currentTimeInMs);
+ Reporting_processReportEvents(self, currentTimeInMs);
+#endif
+
+#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
+ MmsMapping_checkForSettingGroupReservationTimeouts(self, currentTimeInMs);
#endif
+}
+
+void
+IedServer_performPeriodicTasks(IedServer self)
+{
+ processPeriodicTasks(self->mmsMapping);
+}
- Thread_sleep(1); /* hand-over control to other threads */
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
+/* single worker thread for all enabled GOOSE and report control blocks */
+static void
+eventWorkerThread(MmsMapping* self)
+{
+ bool running = true;
+ self->reportThreadFinished = false;
+
+ while (running) {
+ processPeriodicTasks(self);
+
+ Thread_sleep(10); /* hand-over control to other threads */
running = self->reportThreadRunning;
}
- if (DEBUG_IDE_SERVER)
+ if (DEBUG_IED_SERVER)
printf("IED_SERVER: event worker thread finished!\n");
self->reportThreadFinished = true;
@@ -1817,11 +2435,12 @@ MmsMapping_stopEventWorkerThread(MmsMapping* self)
Thread_sleep(1);
}
}
+#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
static DataSet*
createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableList variableList)
{
- DataSet* dataSet = (DataSet*) malloc(sizeof(DataSet));
+ DataSet* dataSet = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet));
dataSet->logicalDeviceName = NULL; /* name is not relevant for dynamically created data set */
@@ -1835,9 +2454,11 @@ createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableList variable
while (element != NULL) {
MmsAccessSpecifier* listEntry = (MmsAccessSpecifier*) element->data;
- DataSetEntry* dataSetEntry = (DataSetEntry*) malloc(sizeof(DataSetEntry));
+ DataSetEntry* dataSetEntry = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry));
+
+ /* use variable name part of domain name as logicalDeviceName */
+ dataSetEntry->logicalDeviceName = MmsDomain_getName(listEntry->domain) + strlen(self->model->name);
- dataSetEntry->logicalDeviceName = MmsDomain_getName(listEntry->domain);
dataSetEntry->variableName = listEntry->variableName;
dataSetEntry->index = listEntry->arrayIndex;
dataSetEntry->componentName = listEntry->componentName;
@@ -1907,12 +2528,12 @@ MmsMapping_freeDynamicallyCreatedDataSet(DataSet* dataSet)
while (dataSetEntry != NULL) {
DataSetEntry* nextEntry = dataSetEntry->sibling;
- free (dataSetEntry);
+ GLOBAL_FREEMEM (dataSetEntry);
dataSetEntry = nextEntry;
}
- free(dataSet);
+ GLOBAL_FREEMEM(dataSet);
}
MmsVariableAccessSpecification*
@@ -1941,7 +2562,7 @@ MmsMapping_ObjectReferenceToVariableAccessSpec(char* objectReference)
FunctionalConstraint fc = FunctionalConstraint_fromString(fcStart + 1);
MmsVariableAccessSpecification* accessSpec =
- (MmsVariableAccessSpecification*) calloc(1, sizeof(MmsVariableAccessSpecification));
+ (MmsVariableAccessSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableAccessSpecification));
accessSpec->domainId = createStringFromBuffer((uint8_t*) objectReference, domainIdLen);
@@ -2028,7 +2649,7 @@ MmsMapping_varAccessSpecToObjectReference(MmsVariableAccessSpecification* varAcc
int newStringLen = (domainIdLen + 1) + (itemIdLen - 2) + arrayIndexLen + 4 /* for FC */+ componentPartLen + 1;
- char* newString = (char*) malloc(newStringLen);
+ char* newString = (char*) GLOBAL_MALLOC(newStringLen);
char* targetPos = newString;
diff --git a/src/iedserver/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
similarity index 88%
rename from src/iedserver/mms_mapping/reporting.c
rename to src/iec61850/server/mms_mapping/reporting.c
index 9f8eee1e..d54bbfaf 100644
--- a/src/iedserver/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -26,7 +26,7 @@
#include "linked_list.h"
#include "array_list.h"
#include "stack_config.h"
-#include "thread.h"
+#include "hal_thread.h"
#include "simple_allocator.h"
#include "mem_alloc_linked_list.h"
@@ -35,6 +35,7 @@
#include "mms_mapping_internal.h"
#include "mms_value_internal.h"
#include "conversions.h"
+#include
#ifndef DEBUG_IED_SERVER
#define DEBUG_IED_SERVER 0
@@ -45,12 +46,12 @@
static ReportBuffer*
ReportBuffer_create(void)
{
- ReportBuffer* self = (ReportBuffer*) malloc(sizeof(ReportBuffer));
+ ReportBuffer* self = (ReportBuffer*) GLOBAL_MALLOC(sizeof(ReportBuffer));
self->lastEnqueuedReport = NULL;
self->oldestReport = NULL;
self->nextToTransmit = NULL;
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;
return self;
@@ -59,14 +60,14 @@ ReportBuffer_create(void)
static void
ReportBuffer_destroy(ReportBuffer* self)
{
- free(self->memoryBlock);
- free(self);
+ GLOBAL_FREEMEM(self->memoryBlock);
+ GLOBAL_FREEMEM(self);
}
ReportControl*
ReportControl_create(bool buffered, LogicalNode* parentLN)
{
- ReportControl* self = (ReportControl*) malloc(sizeof(ReportControl));
+ ReportControl* self = (ReportControl*) GLOBAL_MALLOC(sizeof(ReportControl));
self->name = NULL;
self->domain = NULL;
self->parentLN = parentLN;
@@ -90,7 +91,11 @@ ReportControl_create(bool buffered, LogicalNode* parentLN)
self->timeOfEntry = NULL;
self->reservationTimeout = 0;
self->triggerOps = 0;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->createNotificationsMutex = Semaphore_create(1);
+#endif
+
self->bufferedDataSetValues = NULL;
self->valueReferences = NULL;
self->lastEntryId = 0;
@@ -105,13 +110,17 @@ ReportControl_create(bool buffered, LogicalNode* parentLN)
static void
ReportControl_lockNotify(ReportControl* self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->createNotificationsMutex);
+#endif
}
static void
ReportControl_unlockNotify(ReportControl* self)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->createNotificationsMutex);
+#endif
}
@@ -130,11 +139,11 @@ deleteDataSetValuesShadowBuffer(ReportControl* self)
MmsValue_delete(self->bufferedDataSetValues[i]);
}
- free(self->bufferedDataSetValues);
+ GLOBAL_FREEMEM(self->bufferedDataSetValues);
}
if (self->valueReferences != NULL)
- free(self->valueReferences);
+ GLOBAL_FREEMEM(self->valueReferences);
}
void
@@ -144,7 +153,7 @@ ReportControl_destroy(ReportControl* self)
MmsValue_delete(self->rcbValues);
if (self->inclusionFlags != NULL)
- free(self->inclusionFlags);
+ GLOBAL_FREEMEM(self->inclusionFlags);
if (self->inclusionField != NULL)
MmsValue_delete(self->inclusionField);
@@ -162,11 +171,13 @@ ReportControl_destroy(ReportControl* self)
if (self->buffered)
ReportBuffer_destroy(self->reportBuffer);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->createNotificationsMutex);
+#endif
- free(self->name);
+ GLOBAL_FREEMEM(self->name);
- free(self);
+ GLOBAL_FREEMEM(self);
}
MmsValue*
@@ -255,7 +266,6 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
LinkedList_add(reportElements, optFlds);
/* delete option fields for unsupported options */
- MmsValue_setBitStringBit(optFlds, 5, false); /* data-reference */
MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */
MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */
@@ -287,6 +297,69 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
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 */
DataSetEntry* dataSetEntry = self->dataSet->fcdas;
@@ -362,11 +435,11 @@ createDataSetValuesShadowBuffer(ReportControl* rc)
{
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->valueReferences = (MmsValue**) malloc(dataSetSize * sizeof(MmsValue*));
+ rc->valueReferences = (MmsValue**) GLOBAL_MALLOC(dataSetSize * sizeof(MmsValue*));
DataSetEntry* dataSetEntry = rc->dataSet->fcdas;
@@ -424,9 +497,9 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet)
rc->inclusionField = MmsValue_newBitString(dataSet->elementCount);
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;
}
@@ -545,47 +618,88 @@ refreshBufferTime(ReportControl* rc)
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*
createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
ReportControl* reportControl)
{
- MmsVariableSpecification* rcb = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ MmsVariableSpecification* rcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
rcb->name = copyString(reportControlBlock->name);
rcb->type = MMS_STRUCTURE;
- MmsValue* mmsValue = (MmsValue*) calloc(1, sizeof(MmsValue));
+ MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
mmsValue->deleteValue = false;
mmsValue->type = MMS_STRUCTURE;
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.elements = (MmsVariableSpecification**) calloc(12,
+ rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(12,
sizeof(MmsVariableSpecification*));
MmsVariableSpecification* namedVariable =
- (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptID");
namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
rcb->typeSpec.structure.elements[0] = namedVariable;
- mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
- reportControlBlock->rptId);
+ if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0))
+ mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
+ 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->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[1] = namedVariable;
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->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[2] = namedVariable;
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->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
@@ -595,12 +709,12 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock,
reportControl);
mmsValue->value.structure.components[3] = MmsValue_newVisibleString(dataSetReference);
- free(dataSetReference);
+ GLOBAL_FREEMEM(dataSetReference);
}
else
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -610,14 +724,14 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
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->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 10;
rcb->typeSpec.structure.elements[5] = namedVariable;
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -625,21 +739,21 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[6] =
MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime);
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("SqNum");
namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 16;
rcb->typeSpec.structure.elements[7] = namedVariable;
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->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 6;
rcb->typeSpec.structure.elements[8] = namedVariable;
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -647,13 +761,13 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[9] =
MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod);
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GI");
namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[10] = namedVariable;
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->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = -128;
@@ -675,37 +789,41 @@ static MmsVariableSpecification*
createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
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->type = MMS_STRUCTURE;
- MmsValue* mmsValue = (MmsValue*) calloc(1, sizeof(MmsValue));
+ MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
mmsValue->deleteValue = false;
mmsValue->type = MMS_STRUCTURE;
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.elements = (MmsVariableSpecification**) calloc(15,
+ rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(15,
sizeof(MmsVariableSpecification*));
MmsVariableSpecification* namedVariable =
- (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptID");
namedVariable->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
rcb->typeSpec.structure.elements[0] = namedVariable;
- mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
- reportControlBlock->rptId);
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0))
+ mmsValue->value.structure.components[0] = MmsValue_newVisibleString(
+ reportControlBlock->rptId);
+ else
+ mmsValue->value.structure.components[0] = createDefaultRptId(reportControl);
+
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RptEna");
namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[1] = namedVariable;
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->typeSpec.visibleString = -129;
namedVariable->type = MMS_VISIBLE_STRING;
@@ -716,12 +834,12 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
reportControl);
mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference);
- free(dataSetReference);
+ GLOBAL_FREEMEM(dataSetReference);
}
else
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -731,14 +849,14 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
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->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 10;
rcb->typeSpec.structure.elements[4] = namedVariable;
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -746,21 +864,21 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[5] =
MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime);
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("SqNum");
namedVariable->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 16;
rcb->typeSpec.structure.elements[6] = namedVariable;
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->type = MMS_BIT_STRING;
namedVariable->typeSpec.bitString = 6;
rcb->typeSpec.structure.elements[7] = namedVariable;
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
@@ -768,26 +886,26 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
mmsValue->value.structure.components[8] =
MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod);
- namedVariable = (MmsVariableSpecification*) calloc(1, sizeof(MmsVariableSpecification));
+ namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
namedVariable->name = copyString("GI");
namedVariable->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[9] = namedVariable;
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->type = MMS_BOOLEAN;
rcb->typeSpec.structure.elements[10] = namedVariable;
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->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = 8;
rcb->typeSpec.structure.elements[11] = namedVariable;
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->type = MMS_BINARY_TIME;
rcb->typeSpec.structure.elements[12] = namedVariable;
@@ -795,14 +913,14 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock,
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->type = MMS_UNSIGNED;
namedVariable->typeSpec.unsignedInteger = 32;
rcb->typeSpec.structure.elements[13] = namedVariable;
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->type = MMS_OCTET_STRING;
namedVariable->typeSpec.octetString = -128;
@@ -849,13 +967,13 @@ MmsVariableSpecification*
Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int reportsCount)
{
- MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable->name = copyString("BR");
namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = reportsCount;
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(reportsCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount,
sizeof(MmsVariableSpecification*));
int currentReport = 0;
@@ -886,13 +1004,13 @@ MmsVariableSpecification*
Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int reportsCount)
{
- MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) calloc(1,
+ MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
sizeof(MmsVariableSpecification));
namedVariable->name = copyString("RP");
namedVariable->type = MMS_STRUCTURE;
namedVariable->typeSpec.structure.elementCount = reportsCount;
- namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) calloc(reportsCount,
+ namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount,
sizeof(MmsVariableSpecification*));
int currentReport = 0;
@@ -1057,6 +1175,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
rc->sqNum = 0;
+ MmsValue* sqNum = ReportControl_getRCBValue(rc, "SqNum");
+
+ MmsValue_setUint16(sqNum, 0U);
+
retVal = DATA_ACCESS_ERROR_SUCCESS;
goto exit_function;
}
@@ -1076,7 +1198,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
MmsServerConnection_getClientAddress(connection));
if (rc->buffered == false) {
- free(rc->inclusionFlags);
+ GLOBAL_FREEMEM(rc->inclusionFlags);
rc->inclusionFlags = NULL;
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;
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);
@@ -1653,7 +1785,7 @@ sendNextReportEntry(ReportControl* self)
if (self->reportBuffer->nextToTransmit == NULL)
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
required memory size */
@@ -1857,7 +1989,7 @@ sendNextReportEntry(ReportControl* self)
case REPORT_CONTROL_VALUE_UPDATE:
MmsValue_setBitStringBit(reason, 3, true);
break;
- case REPORT_CONTROL_NONE:
+ default:
break;
}
@@ -1905,7 +2037,7 @@ return_out_of_memory:
cleanup_and_return:
if (localStorage != NULL)
- free(localStorage);
+ GLOBAL_FREEMEM(localStorage);
}
void
diff --git a/src/iedserver/model/cdc.c b/src/iec61850/server/model/cdc.c
similarity index 99%
rename from src/iedserver/model/cdc.c
rename to src/iec61850/server/model/cdc.c
index 01061a58..05b20da4 100644
--- a/src/iedserver/model/cdc.c
+++ b/src/iec61850/server/model/cdc.c
@@ -23,8 +23,8 @@
* See COPYING file for the complete license text.
*/
-#include "dynamic_model.h"
-#include "cdc.h"
+#include "iec61850_dynamic_model.h"
+#include "iec61850_cdc.h"
/************************************************
* Constructed Attribute Classes
diff --git a/src/iedserver/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c
similarity index 94%
rename from src/iedserver/model/config_file_parser.c
rename to src/iec61850/server/model/config_file_parser.c
index 708682d7..3a1d173c 100644
--- a/src/iedserver/model/config_file_parser.c
+++ b/src/iec61850/server/model/config_file_parser.c
@@ -22,9 +22,11 @@
*/
#include "iec61850_server.h"
-#include "filesystem.h"
-#include "dynamic_model.h"
-#include "config_file_parser.h"
+#include "iec61850_dynamic_model.h"
+#include "iec61850_config_file_parser.h"
+
+#include "libiec61850_platform_includes.h"
+#include "stack_config.h"
#include "string_utilities.h"
#include
@@ -161,8 +163,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (StringUtils_startsWith((char*) lineBuffer, "LN")) {
indendation = 3;
- if (sscanf((char*) lineBuffer, "LN(%s)", nameString) < 1)
- goto exit_error;
+ if (sscanf((char*) lineBuffer, "LN(%s)", nameString) < 1)
+ goto exit_error;
terminateString(nameString, ')');
@@ -233,6 +235,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
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
goto exit_error;
diff --git a/src/iedserver/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c
similarity index 80%
rename from src/iedserver/model/dynamic_model.c
rename to src/iec61850/server/model/dynamic_model.c
index af024ad8..6e8af663 100644
--- a/src/iedserver/model/dynamic_model.c
+++ b/src/iec61850/server/model/dynamic_model.c
@@ -24,6 +24,8 @@
#include "iec61850_server.h"
#include "string_utilities.h"
+#include "libiec61850_platform_includes.h"
+#include "stack_config.h"
static void
iedModel_emptyVariableInitializer(void)
@@ -34,7 +36,7 @@ iedModel_emptyVariableInitializer(void)
IedModel*
IedModel_create(const char* name/*, MemoryAllocator allocator*/)
{
- IedModel* self = (IedModel*) calloc(1, sizeof(IedModel));
+ IedModel* self = (IedModel*) GLOBAL_CALLOC(1, sizeof(IedModel));
if (name)
self->name = copyString(name);
@@ -47,6 +49,8 @@ IedModel_create(const char* name/*, MemoryAllocator allocator*/)
self->gseCBs = NULL;
+ self->sgcbs = NULL;
+
self->initializer = iedModel_emptyVariableInitializer;
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
IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
{
@@ -119,7 +141,7 @@ IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
LogicalDevice*
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->modelType = LogicalDeviceModelType;
@@ -161,7 +183,7 @@ LogicalDevice_addLogicalNode(LogicalDevice* self, LogicalNode* lNode)
LogicalNode*
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->parent = (ModelNode*) parent;
@@ -214,7 +236,7 @@ ReportControlBlock*
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)
{
- ReportControlBlock* self = (ReportControlBlock*) malloc(sizeof(ReportControlBlock));
+ ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
self->name = copyString(name);
self->parent = parent;
@@ -243,6 +265,34 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, char* rptId, bo
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
LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
{
@@ -254,7 +304,7 @@ LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb)
GSEControlBlock*
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->parent = parent;
@@ -291,7 +341,7 @@ GSEControlBlock_addPhyComAddress(GSEControlBlock* self, PhyComAddress* phyComAdd
PhyComAddress*
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->vlanId = vlanId;
@@ -335,7 +385,7 @@ DataObject_addChild(DataObject* self, ModelNode* child)
DataObject*
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->modelType = DataObjectModelType;
@@ -383,7 +433,7 @@ DataAttribute*
DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type, FunctionalConstraint fc,
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->elementCount = arrayElements;
@@ -408,7 +458,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
DataSet*
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;
@@ -466,7 +516,7 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry)
DataSetEntry*
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);
@@ -487,7 +537,7 @@ DataSetEntry_create(DataSet* dataSet, char* variable, int index, char* component
static void
ModelNode_destroy(ModelNode* modelNode)
{
- free(modelNode->name);
+ GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild;
@@ -508,7 +558,7 @@ ModelNode_destroy(ModelNode* modelNode)
}
}
- free(modelNode);
+ GLOBAL_FREEMEM(modelNode);
}
void
@@ -521,12 +571,12 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild;
while (ld != NULL) {
- free (ld->name);
+ GLOBAL_FREEMEM (ld->name);
LogicalNode* ln = (LogicalNode*) ld->firstChild;
while (ln != NULL) {
- free(ln->name);
+ GLOBAL_FREEMEM(ln->name);
/* delete all data objects */
@@ -543,14 +593,14 @@ IedModel_destroy(IedModel* model)
LogicalNode* currentLn = ln;
ln = (LogicalNode*) ln->sibling;
- free(currentLn);
+ GLOBAL_FREEMEM(currentLn);
}
LogicalDevice* currentLd = ld;
ld = (LogicalDevice*) ld->sibling;
- free(currentLd);
+ GLOBAL_FREEMEM(currentLd);
}
/* delete all data sets */
@@ -560,7 +610,7 @@ IedModel_destroy(IedModel* model)
while (dataSet != NULL) {
DataSet* nextDataSet = dataSet->sibling;
- free(dataSet->name);
+ GLOBAL_FREEMEM(dataSet->name);
DataSetEntry* dse = dataSet->fcdas;
@@ -568,16 +618,16 @@ IedModel_destroy(IedModel* model)
DataSetEntry* nextDse = dse->sibling;
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;
}
- free(dataSet);
+ GLOBAL_FREEMEM(dataSet);
dataSet = nextDataSet;
}
@@ -589,15 +639,15 @@ IedModel_destroy(IedModel* model)
while (rcb != NULL) {
ReportControlBlock* nextRcb = rcb->sibling;
- free(rcb->name);
+ GLOBAL_FREEMEM(rcb->name);
if (rcb->rptId)
- free(rcb->rptId);
+ GLOBAL_FREEMEM(rcb->rptId);
if (rcb->dataSetName)
- free(rcb->dataSetName);
+ GLOBAL_FREEMEM(rcb->dataSetName);
- free(rcb);
+ GLOBAL_FREEMEM(rcb);
rcb = nextRcb;
}
@@ -609,22 +659,37 @@ IedModel_destroy(IedModel* model)
while (gcb != NULL) {
GSEControlBlock* nextGcb = gcb->sibling;
- free(gcb->name);
- free(gcb->appId);
- free(gcb->dataSetName);
+ GLOBAL_FREEMEM(gcb->name);
+ GLOBAL_FREEMEM(gcb->appId);
+ GLOBAL_FREEMEM(gcb->dataSetName);
if (gcb->address)
- free(gcb->address);
+ GLOBAL_FREEMEM(gcb->address);
- free(gcb);
+ GLOBAL_FREEMEM(gcb);
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)
- free(model->name);
+ GLOBAL_FREEMEM(model->name);
- free(model);
+ GLOBAL_FREEMEM(model);
}
diff --git a/src/iedserver/model/model.c b/src/iec61850/server/model/model.c
similarity index 86%
rename from src/iedserver/model/model.c
rename to src/iec61850/server/model/model.c
index 36496cf4..d01ac1a9 100644
--- a/src/iedserver/model/model.c
+++ b/src/iec61850/server/model/model.c
@@ -21,7 +21,7 @@
* See COPYING file for the complete license text.
*/
-#include "model.h"
+#include "iec61850_model.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
IedModel_setAttributeValuesToNull(IedModel* iedModel)
{
@@ -99,6 +105,9 @@ IedModel_lookupDataSet(IedModel* model, const char* dataSetReference /* e.g. ie
int ldNameLen = separator - dataSetReference;
while (dataSet != NULL) {
+
+ //TODO use domain name instead of dataSet->logicalDeviceName !?
+
if (strncmp(dataSet->logicalDeviceName, dataSetReference, ldNameLen) == 0) {
if (strcmp(dataSet->name, separator + 1) == 0) {
return dataSet;
@@ -117,7 +126,13 @@ IedModel_getDevice(IedModel* model, const char* deviceName)
LogicalDevice* device = model->firstChild;
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;
device = (LogicalDevice*) device->sibling;
@@ -352,14 +367,16 @@ createObjectReference(ModelNode* node, char* objectReference)
LogicalDevice* lDevice = (LogicalDevice*) lNode->parent;
+ IedModel* iedModel = (IedModel*) lDevice->parent;
+
bufPos = 0;
- int nameLength = strlen(lDevice->name);
+ int nameLength = strlen (iedModel->name) + strlen(lDevice->name);
- int i;
- for (i = 0; i < nameLength; i++) {
- objectReference[bufPos++] = lDevice->name[i];
- }
+ strncpy(objectReference, iedModel->name, 64);
+ strncat(objectReference, lDevice->name, 64);
+
+ bufPos += nameLength;
objectReference[bufPos++] = '/';
}
@@ -379,7 +396,7 @@ char*
ModelNode_getObjectReference(ModelNode* node, char* objectReference)
{
if (objectReference == NULL)
- objectReference = (char*) malloc(130);
+ objectReference = (char*) GLOBAL_MALLOC(130);
int bufPos = createObjectReference(node, objectReference);
@@ -443,7 +460,36 @@ ModelNode_getChild(ModelNode* self, const char* name)
inline
LogicalNode*
-LogicalDevice_getLogicalNode(LogicalDevice* device, const char* nodeName)
+LogicalDevice_getLogicalNode(LogicalDevice* self, const char* nodeName)
+{
+ return (LogicalNode*) ModelNode_getChild((ModelNode*) self, nodeName);
+}
+
+SettingGroupControlBlock*
+LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self)
{
- return (LogicalNode*) ModelNode_getChild((ModelNode*) device, nodeName);
+ 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;
}
diff --git a/src/mms/asn1/asn1_ber_primitive_value.h b/src/mms/inc/asn1_ber_primitive_value.h
similarity index 100%
rename from src/mms/asn1/asn1_ber_primitive_value.h
rename to src/mms/inc/asn1_ber_primitive_value.h
diff --git a/src/mms/asn1/ber_integer.h b/src/mms/inc/ber_integer.h
similarity index 100%
rename from src/mms/asn1/ber_integer.h
rename to src/mms/inc/ber_integer.h
diff --git a/src/mms/iso_common/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h
similarity index 91%
rename from src/mms/iso_common/iso_connection_parameters.h
rename to src/mms/inc/iso_connection_parameters.h
index 5e0655e6..27fbb82e 100644
--- a/src/mms/iso_common/iso_connection_parameters.h
+++ b/src/mms/inc/iso_connection_parameters.h
@@ -91,11 +91,21 @@ AcseAuthenticationParameter_setPassword(AcseAuthenticationParameter self, char*
typedef bool
(*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
{
AcseAuthenticationParameter acseAuthParameter;
- char* hostname;
+ const char* hostname;
int tcpPort;
uint8_t remoteApTitle[10];
@@ -103,14 +113,14 @@ struct sIsoConnectionParameters
int remoteAEQualifier;
uint32_t remotePSelector;
uint16_t remoteSSelector;
- uint16_t remoteTSelector;
+ TSelector remoteTSelector;
uint8_t localApTitle[10];
int localApTitleLen;
int localAEQualifier;
uint32_t localPSelector;
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
*/
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
@@ -176,7 +186,7 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, char* hos
* \param aeQualifier the AP-qualifier
*/
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
@@ -188,10 +198,10 @@ IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, char* apT
* \param self the IsoConnectionParameters instance
* \param pSelector the P-Selector (presentation 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
-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
@@ -218,10 +228,10 @@ IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, char* apTi
* \param self the IsoConnectionParameters instance
* \param pSelector the P-Selector (presentation 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
-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);
/**@}*/
diff --git a/src/mms/iso_server/iso_server.h b/src/mms/inc/iso_server.h
similarity index 91%
rename from src/mms/iso_server/iso_server.h
rename to src/mms/inc/iso_server.h
index 3a22fe42..8c1615f8 100644
--- a/src/mms/iso_server/iso_server.h
+++ b/src/mms/inc/iso_server.h
@@ -119,6 +119,22 @@ IsoServer_startListening(IsoServer self);
void
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
IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection);
diff --git a/src/mms/iso_mms/client/mms_client_connection.h b/src/mms/inc/mms_client_connection.h
similarity index 92%
rename from src/mms/iso_mms/client/mms_client_connection.h
rename to src/mms/inc/mms_client_connection.h
index ecb25c43..838d24d9 100644
--- a/src/mms/iso_mms/client/mms_client_connection.h
+++ b/src/mms/inc/mms_client_connection.h
@@ -38,7 +38,7 @@ extern "C" {
#include "mms_common.h"
#include "mms_type_spec.h"
#include "mms_value.h"
-#include "iso_client_connection.h"
+#include "iso_connection_parameters.h"
#include "linked_list.h"
/**
@@ -79,7 +79,7 @@ MmsConnection
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 timeoutInMs request timeout in milliseconds
@@ -87,6 +87,15 @@ MmsConnection_create(void);
void
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).
*
@@ -163,7 +172,18 @@ MmsConnection_destroy(MmsConnection self);
* \return true on success. false if the connection attempt failed.
*/
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
@@ -233,7 +253,7 @@ MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError);
* \return the of domain specific variable names or NULL if the request failed.
*/
LinkedList /* */
-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.
@@ -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.
*/
LinkedList /* */
-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.
@@ -275,7 +295,7 @@ MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsErr
* either be a simple value or a complex value or array.
*/
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.
@@ -292,7 +312,7 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, char* domainI
* array elements of numberOfElements > 0.
*/
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);
/**
@@ -308,7 +328,7 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, char* do
* in the order as they appeared in the item ID list.
*/
MmsValue*
-MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, char* domainId,
+MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
LinkedList /**/ items);
/**
@@ -322,10 +342,10 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, char
*/
void
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.
*
@@ -342,7 +362,7 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
* write.
*/
void
-MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, char* domainId,
+MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
LinkedList /**/ items, LinkedList /* */ values,
LinkedList* /* */ accessResults);
@@ -358,7 +378,7 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, cha
*/
MmsVariableSpecification*
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
@@ -374,8 +394,8 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError
* in the order as they appeared in named variable list definition.
*/
MmsValue*
-MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, char* domainId,
- char* listName, bool specWithResult);
+MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId,
+ const char* listName, bool specWithResult);
/**
@@ -392,7 +412,7 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError
*/
MmsValue*
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.
@@ -405,8 +425,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self,
* elements have to be of type MmsVariableAccessSpecification*.
*/
void
-MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, char* domainId,
- char* listName, LinkedList variableSpecs);
+MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId,
+ const char* listName, LinkedList variableSpecs);
/**
@@ -420,7 +440,7 @@ MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, ch
*/
void
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.
@@ -436,7 +456,7 @@ MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, Mms
*/
LinkedList /* */
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 /* */
MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError,
- char* listName, bool* deletable);
+ const char* listName, bool* deletable);
/**
* \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
*/
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.
@@ -473,7 +493,7 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, ch
*/
void
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.
@@ -579,7 +599,7 @@ typedef void
* \return the FRSM ID (file read state machine) handle of the opened file
*/
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);
/**
@@ -614,7 +634,7 @@ MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId);
* \param fileName name of the file to delete
*/
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
@@ -625,7 +645,7 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, char* fileName)
* \param newFileName new name of the file
*/
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.
@@ -644,7 +664,7 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, char* currentFi
* \return (more follows) true if more data is available
*/
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);
diff --git a/src/mms/iso_mms/common/mms_common.h b/src/mms/inc/mms_common.h
similarity index 98%
rename from src/mms/iso_mms/common/mms_common.h
rename to src/mms/inc/mms_common.h
index 8c515ec6..705b402f 100644
--- a/src/mms/iso_mms/common/mms_common.h
+++ b/src/mms/inc/mms_common.h
@@ -68,6 +68,7 @@ typedef enum
MMS_ERROR_DEFINITION_OBJECT_ATTRIBUTE_INCONSISTENT = 36,
MMS_ERROR_RESOURCE_OTHER = 40,
+ MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE = 41,
MMS_ERROR_SERVICE_OTHER = 50,
diff --git a/src/mms/iso_mms/server/mms_device_model.h b/src/mms/inc/mms_device_model.h
similarity index 100%
rename from src/mms/iso_mms/server/mms_device_model.h
rename to src/mms/inc/mms_device_model.h
diff --git a/src/mms/iso_mms/server/mms_named_variable_list.h b/src/mms/inc/mms_named_variable_list.h
similarity index 100%
rename from src/mms/iso_mms/server/mms_named_variable_list.h
rename to src/mms/inc/mms_named_variable_list.h
diff --git a/src/mms/iso_mms/server/mms_server.h b/src/mms/inc/mms_server.h
similarity index 77%
rename from src/mms/iso_mms/server/mms_server.h
rename to src/mms/inc/mms_server.h
index 05318d31..1a91a350 100644
--- a/src/mms/iso_mms/server/mms_server.h
+++ b/src/mms/inc/mms_server.h
@@ -1,7 +1,7 @@
/*
* mms_server.h
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* 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
#endif
-#include "filesystem.h"
+#include "hal_filesystem.h"
typedef struct {
int32_t frsmId;
@@ -78,10 +78,13 @@ typedef struct sMmsServerConnection {
} MmsServerConnection;
-typedef MmsValue* (*ReadVariableHandler)(void* parameter, MmsDomain* domain,
+typedef MmsValue* (*MmsReadVariableHandler)(void* parameter, MmsDomain* domain,
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,
MmsServerConnection* connection);
@@ -92,11 +95,17 @@ MmsServer
MmsServer_create(IsoServer isoServer, MmsDevice* device);
void
-MmsServer_installReadHandler(MmsServer self, ReadVariableHandler,
+MmsServer_destroy(MmsServer self);
+
+void
+MmsServer_installReadHandler(MmsServer self, MmsReadVariableHandler,
void* parameter);
void
-MmsServer_installWriteHandler(MmsServer self, WriteVariableHandler,
+MmsServer_installReadAccessHandler(MmsServer self, MmsReadAccessHandler, void* parameter);
+
+void
+MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler,
void* parameter);
/**
@@ -148,17 +157,68 @@ MmsServer_insertIntoCache(MmsServer self, MmsDomain* domain, char* itemId,
void
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
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
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_destroy(MmsServer self);
+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
+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);
/***************************************************
diff --git a/src/mms/iso_mms/common/mms_type_spec.h b/src/mms/inc/mms_type_spec.h
similarity index 56%
rename from src/mms/iso_mms/common/mms_type_spec.h
rename to src/mms/inc/mms_type_spec.h
index 7c594791..d9db23fb 100644
--- a/src/mms/iso_mms/common/mms_type_spec.h
+++ b/src/mms/inc/mms_type_spec.h
@@ -5,7 +5,7 @@
* Complex types are arrays or structures of simple and complex types.
* They also represent MMS NamedVariables.
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -36,31 +36,80 @@
extern "C" {
#endif
+/**
+ * \addtogroup common_api_group
+ */
+/**@{*/
+
+/**
+ * \defgroup MMS_VAR_SPEC MmsVariableSpecification data type specifications
+ */
+/**@{*/
+
/**
* \brief Delete MmsTypeSpecification object (recursive).
*
- * \param self the MmsVariableSpecification object
+ * \param self the MmsVariableSpecification instance
*/
void
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*
-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_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
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);
LinkedList /* */
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
* \return the number of elements or -1 if not applicable
@@ -77,6 +126,10 @@ MmsVariableSpecification_getArrayElementSpecification(MmsVariableSpecification*
int
MmsVariableSpecification_getExponentWidth(MmsVariableSpecification* self);
+/**@}*/
+
+/**@}*/
+
#ifdef __cplusplus
}
#endif
diff --git a/src/mms/iso_mms/common/mms_types.h b/src/mms/inc/mms_types.h
similarity index 97%
rename from src/mms/iso_mms/common/mms_types.h
rename to src/mms/inc/mms_types.h
index 768f952c..61fdab48 100644
--- a/src/mms/iso_mms/common/mms_types.h
+++ b/src/mms/inc/mms_types.h
@@ -35,11 +35,18 @@ typedef enum ATTRIBUTE_PACKED {
MMS_VALUE_OBJECT_ACCESS_UNSUPPORTED
} MmsValueIndication;
+/**
+ * \addtogroup MMS_VAR_SPEC
+ */
+/**@{*/
+
/**
* Type definition for MMS Named Variables
*/
typedef struct sMmsVariableSpecification MmsVariableSpecification;
+/**@}*/
+
struct ATTRIBUTE_PACKED sMmsVariableSpecification {
MmsType type;
char* name;
diff --git a/src/mms/iso_mms/common/mms_value.h b/src/mms/inc/mms_value.h
similarity index 95%
rename from src/mms/iso_mms/common/mms_value.h
rename to src/mms/inc/mms_value.h
index fbd7a803..cfe35633 100644
--- a/src/mms/iso_mms/common/mms_value.h
+++ b/src/mms/inc/mms_value.h
@@ -355,6 +355,9 @@ MmsValue_setAllBitStringBits(MmsValue* self);
/**
* \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.
*/
uint32_t
@@ -363,12 +366,38 @@ MmsValue_getBitStringAsInteger(MmsValue* self);
/**
* \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 intValue the integer value that is used to set the bit string
*/
void
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
*
@@ -638,7 +667,7 @@ MmsValue_clone(MmsValue* self);
* \param self the MmsValue instance that will be cloned
* \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*
MmsValue_cloneToBuffer(MmsValue* self, uint8_t* destinationAddress);
diff --git a/src/mms/iso_acse/acse.h b/src/mms/inc_private/acse.h
similarity index 100%
rename from src/mms/iso_acse/acse.h
rename to src/mms/inc_private/acse.h
diff --git a/src/mms/asn1/ber_decode.h b/src/mms/inc_private/ber_decode.h
similarity index 100%
rename from src/mms/asn1/ber_decode.h
rename to src/mms/inc_private/ber_decode.h
diff --git a/src/mms/asn1/ber_encoder.h b/src/mms/inc_private/ber_encoder.h
similarity index 91%
rename from src/mms/asn1/ber_encoder.h
rename to src/mms/inc_private/ber_encoder.h
index 85188ee7..343f54f0 100644
--- a/src/mms/asn1/ber_encoder.h
+++ b/src/mms/inc_private/ber_encoder.h
@@ -44,7 +44,7 @@ int
BerEncoder_encodeBoolean(uint8_t tag, bool value, uint8_t* buffer, int bufPos);
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
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);
int
-BerEncoder_determineEncodedStringSize(char* string);
+BerEncoder_determineEncodedStringSize(const char* string);
int
BerEncoder_determineEncodedBitStringSize(int bitStringSize);
@@ -89,7 +89,7 @@ BerEncoder_determineEncodedBitStringSize(int bitStringSize);
*/
int
-BerEncoder_encodeOIDToBuffer(char* oidString, uint8_t* buffer, int maxBufLen);
+BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufLen);
void
BerEncoder_revertByteOrder(uint8_t* octets, const int size);
diff --git a/src/mms/iso_cotp/cotp.h b/src/mms/inc_private/cotp.h
similarity index 69%
rename from src/mms/iso_cotp/cotp.h
rename to src/mms/inc_private/cotp.h
index a32f359c..273ef659 100644
--- a/src/mms/iso_cotp/cotp.h
+++ b/src/mms/inc_private/cotp.h
@@ -1,7 +1,7 @@
/*
* cotp.h
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -26,15 +26,14 @@
#include "libiec61850_platform_includes.h"
#include "byte_buffer.h"
-#include "byte_stream.h"
#include "buffer_chain.h"
-#include "socket.h"
+#include "hal_socket.h"
#include "iso_connection_parameters.h"
typedef struct {
- int32_t tsap_id_src;
- int32_t tsap_id_dst;
- uint8_t tpdu_size;
+ TSelector tSelSrc;
+ TSelector tSelDst;
+ uint8_t tpduSize;
} CotpOptions;
typedef struct {
@@ -46,13 +45,26 @@ typedef struct {
CotpOptions options;
bool isLastDataUnit;
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;
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;
+typedef enum {
+ TPKT_PACKET_COMPLETE = 0,
+ TPKT_WAITING = 1,
+ TPKT_ERROR = 2
+} TpktState;
+
int /* in byte */
CotpConnection_getTpduSize(CotpConnection* self);
@@ -60,14 +72,18 @@ void
CotpConnection_setTpduSize(CotpConnection* self, int tpduSize /* in byte */);
void
-CotpConnection_init(CotpConnection* self, Socket socket, ByteBuffer* payloadBuffer);
-
-void
-CotpConnection_destroy(CotpConnection* self);
+CotpConnection_init(CotpConnection* self, Socket socket,
+ ByteBuffer* payloadBuffer, ByteBuffer* readBuffer, ByteBuffer* writeBuffer);
CotpIndication
CotpConnection_parseIncomingMessage(CotpConnection* self);
+void
+CotpConnection_resetPayload(CotpConnection* self);
+
+TpktState
+CotpConnection_readToTpktBuffer(CotpConnection* self);
+
CotpIndication
CotpConnection_sendConnectionRequestMessage(CotpConnection* self, IsoConnectionParameters isoParameters);
diff --git a/src/mms/iso_client/iso_client_connection.h b/src/mms/inc_private/iso_client_connection.h
similarity index 97%
rename from src/mms/iso_client/iso_client_connection.h
rename to src/mms/inc_private/iso_client_connection.h
index df1babfb..d593866e 100644
--- a/src/mms/iso_client/iso_client_connection.h
+++ b/src/mms/inc_private/iso_client_connection.h
@@ -59,7 +59,7 @@ IsoClientConnection_destroy(IsoClientConnection self);
void
IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params,
- ByteBuffer* payload);
+ ByteBuffer* payload, uint32_t connectTimeoutInMs);
void
IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payload);
diff --git a/src/mms/iso_presentation/iso_presentation.h b/src/mms/inc_private/iso_presentation.h
similarity index 100%
rename from src/mms/iso_presentation/iso_presentation.h
rename to src/mms/inc_private/iso_presentation.h
diff --git a/src/mms/iso_server/iso_server_private.h b/src/mms/inc_private/iso_server_private.h
similarity index 84%
rename from src/mms/iso_server/iso_server_private.h
rename to src/mms/inc_private/iso_server_private.h
index be197010..f5c3669f 100644
--- a/src/mms/iso_server/iso_server_private.h
+++ b/src/mms/inc_private/iso_server_private.h
@@ -24,11 +24,20 @@
#ifndef ISO_SERVER_PRIVATE_H_
#define ISO_SERVER_PRIVATE_H_
-#include "socket.h"
+#include "hal_socket.h"
IsoConnection
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
private_IsoServer_increaseConnectionCounter(IsoServer self);
@@ -50,4 +59,7 @@ IsoServer_userLock(IsoServer self);
void
IsoServer_userUnlock(IsoServer self);
+bool
+IsoConnection_isRunning(IsoConnection self);
+
#endif /* ISO_SERVER_PRIVATE_H_ */
diff --git a/src/mms/iso_session/iso_session.h b/src/mms/inc_private/iso_session.h
similarity index 100%
rename from src/mms/iso_session/iso_session.h
rename to src/mms/inc_private/iso_session.h
diff --git a/src/mms/iso_mms/server/mms_access_result.h b/src/mms/inc_private/mms_access_result.h
similarity index 100%
rename from src/mms/iso_mms/server/mms_access_result.h
rename to src/mms/inc_private/mms_access_result.h
diff --git a/src/mms/iso_mms/client/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h
similarity index 80%
rename from src/mms/iso_mms/client/mms_client_internal.h
rename to src/mms/inc_private/mms_client_internal.h
index 244f6e15..fc1ec8b5 100644
--- a/src/mms/iso_mms/client/mms_client_internal.h
+++ b/src/mms/inc_private/mms_client_internal.h
@@ -27,9 +27,10 @@
#include "MmsPdu.h"
#include "linked_list.h"
#include "mms_client_connection.h"
+#include "iso_client_connection.h"
#include "ber_decode.h"
-#include "thread.h"
+#include "hal_thread.h"
#ifndef DEBUG_MMS_CLIENT
#define DEBUG_MMS_CLIENT 0
@@ -60,20 +61,20 @@ struct sMmsConnection {
uint32_t lastInvokeId;
Semaphore lastResponseLock;
- uint32_t responseInvokeId;
+ volatile uint32_t responseInvokeId;
ByteBuffer* lastResponse;
- uint32_t lastResponseBufPos;
- MmsError lastResponseError;
+ volatile uint32_t lastResponseBufPos;
+ volatile MmsError lastResponseError;
Semaphore outstandingCallsLock;
uint32_t* outstandingCalls;
uint32_t requestTimeout;
+ uint32_t connectTimeout;
IsoClientConnection isoClient;
AssociationState associationState;
ConnectionState connectionState;
- //uint8_t* buffer;
MmsConnectionParameters parameters;
IsoConnectionParameters isoParameters;
@@ -114,47 +115,47 @@ MmsPdu_t*
mmsClient_createConfirmedRequestPdu(uint32_t invokeId);
int
-mmsClient_createMmsGetNameListRequestVMDspecific(long invokeId, ByteBuffer* writeBuffer, char* continueAfter);
+mmsClient_createMmsGetNameListRequestVMDspecific(long invokeId, ByteBuffer* writeBuffer, const char* continueAfter);
bool
mmsClient_parseGetNameListResponse(LinkedList* nameList, ByteBuffer* message, uint32_t* invokeId);
int
-mmsClient_createGetNameListRequestDomainOrVMDSpecific(long invokeId, char* domainName,
- ByteBuffer* writeBuffer, MmsObjectClass objectClass, char* continueAfter);
+mmsClient_createGetNameListRequestDomainOrVMDSpecific(long invokeId, const char* domainName,
+ ByteBuffer* writeBuffer, MmsObjectClass objectClass, const char* continueAfter);
MmsValue*
mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool createArray);
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
-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);
int
-mmsClient_createReadRequestMultipleValues(uint32_t invokeId, char* domainId, LinkedList /**/ items,
+mmsClient_createReadRequestMultipleValues(uint32_t invokeId, const char* domainId, LinkedList /**/ items,
ByteBuffer* writeBuffer);
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);
int
mmsClient_createReadAssociationSpecificNamedVariableListRequest(
uint32_t invokeId,
- char* itemId,
+ const char* itemId,
ByteBuffer* writeBuffer,
bool specWithResult);
void
mmsClient_createGetNamedVariableListAttributesRequest(uint32_t invokeId, ByteBuffer* writeBuffer,
- char* domainId, char* listNameId);
+ const char* domainId, const char* listNameId);
void
mmsClient_createGetNamedVariableListAttributesRequestAssociationSpecific(uint32_t invokeId,
- ByteBuffer* writeBuffer, char* listNameId);
+ ByteBuffer* writeBuffer, const char* listNameId);
LinkedList
mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint32_t* invokeId,
@@ -163,7 +164,7 @@ mmsClient_parseGetNamedVariableListAttributesResponse(ByteBuffer* message, uint3
int
mmsClient_createGetVariableAccessAttributesRequest(
uint32_t invokeId,
- char* domainId, char* itemId,
+ const char* domainId, const char* itemId,
ByteBuffer* writeBuffer);
MmsVariableSpecification*
@@ -177,16 +178,16 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M
int itemCount, LinkedList* accessResults);
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);
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);
void
mmsClient_createDefineNamedVariableListRequest(uint32_t invokeId, ByteBuffer* writeBuffer,
- char* domainId, char* listNameId, LinkedList /**/ listOfVariables,
+ const char* domainId, const char* listNameId, LinkedList /**/ listOfVariables,
bool associationSpecific);
bool
@@ -194,7 +195,7 @@ mmsClient_parseDefineNamedVariableResponse(ByteBuffer* message, uint32_t* invoke
void
mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeBuffer,
- char* domainId, char* listNameId);
+ const char* domainId, const char* listNameId);
bool
mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId);
@@ -203,7 +204,7 @@ void
mmsClient_createDeleteAssociationSpecificNamedVariableListRequest(
long invokeId,
ByteBuffer* writeBuffer,
- char* listNameId);
+ const char* listNameId);
void
mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request);
@@ -218,7 +219,7 @@ bool
mmsClient_parseStatusResponse(MmsConnection self, int* vmdLogicalStatus, int* vmdPhysicalStatus);
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
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);
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
-mmsClient_createFileDeleteRequest(uint32_t invokeId, ByteBuffer* request, char* fileName);
+mmsClient_createFileDeleteRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName);
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
mmsClient_parseFileDirectoryResponse(MmsConnection self, MmsFileDirectoryHandler handler, void* handlerParameter,
@@ -253,6 +254,6 @@ mmsClient_createConcludeRequest(MmsConnection self, ByteBuffer* message);
int
mmsClient_createMmsGetNameListRequestAssociationSpecific(long invokeId, ByteBuffer* writeBuffer,
- char* continueAfter);
+ const char* continueAfter);
#endif /* MMS_MSG_INTERNAL_H_ */
diff --git a/src/mms/iso_mms/common/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h
similarity index 100%
rename from src/mms/iso_mms/common/mms_common_internal.h
rename to src/mms/inc_private/mms_common_internal.h
diff --git a/src/mms/iso_mms/server/mms_server_connection.h b/src/mms/inc_private/mms_server_connection.h
similarity index 97%
rename from src/mms/iso_mms/server/mms_server_connection.h
rename to src/mms/inc_private/mms_server_connection.h
index 53623e48..f5441b0d 100644
--- a/src/mms/iso_mms/server/mms_server_connection.h
+++ b/src/mms/inc_private/mms_server_connection.h
@@ -94,7 +94,8 @@ MmsServerConnection_sendInformationReportListOfVariables(
);
void
-MmsServerConnection_sendWriteResponse(MmsServerConnection* self, uint32_t invokeId, MmsDataAccessError indication);
+MmsServerConnection_sendWriteResponse(MmsServerConnection* self, uint32_t invokeId, MmsDataAccessError indication,
+ bool handlerMode);
uint32_t
diff --git a/src/mms/iso_mms/server/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h
similarity index 95%
rename from src/mms/iso_mms/server/mms_server_internal.h
rename to src/mms/inc_private/mms_server_internal.h
index 5e5384d4..69a9cef4 100644
--- a/src/mms/iso_mms/server/mms_server_internal.h
+++ b/src/mms/inc_private/mms_server_internal.h
@@ -32,11 +32,12 @@
#include "mms_device_model.h"
#include "mms_common_internal.h"
#include "stack_config.h"
+#include "mms_server.h"
#include "byte_buffer.h"
#include "string_utilities.h"
#include "map.h"
-#include "thread.h"
+#include "hal_thread.h"
#include "ber_encoder.h"
#include "ber_decode.h"
@@ -61,10 +62,13 @@ struct sMmsServer {
IsoServer isoServer;
MmsDevice* device;
- ReadVariableHandler readHandler;
+ MmsReadVariableHandler readHandler;
void* readHandlerParameter;
- WriteVariableHandler writeHandler;
+ MmsReadAccessHandler readAccessHandler;
+ void* readAccessHandlerParameter;
+
+ MmsWriteVariableHandler writeHandler;
void* writeHandlerParameter;
MmsConnectionHandler connectionHandler;
@@ -73,7 +77,10 @@ struct sMmsServer {
Map openConnections;
Map valueCaches;
bool isLocked;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore modelMutex;
+#endif
#if MMS_STATUS_SERVICE == 1
int vmdLogicalStatus;
diff --git a/src/mms/iso_mms/server/mms_value_cache.h b/src/mms/inc_private/mms_value_cache.h
similarity index 100%
rename from src/mms/iso_mms/server/mms_value_cache.h
rename to src/mms/inc_private/mms_value_cache.h
diff --git a/src/mms/iso_mms/common/mms_value_internal.h b/src/mms/inc_private/mms_value_internal.h
similarity index 100%
rename from src/mms/iso_mms/common/mms_value_internal.h
rename to src/mms/inc_private/mms_value_internal.h
diff --git a/src/mms/iso_client/impl/iso_client_connection.c b/src/mms/iso_client/impl/iso_client_connection.c
deleted file mode 100644
index 5b7187d9..00000000
--- a/src/mms/iso_client/impl/iso_client_connection.c
+++ /dev/null
@@ -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 .
- *
- * 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);
-}