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